Added Required button, Text box question type, Added sweetalerts

This commit is contained in:
Yash 2024-07-19 02:42:07 +05:30
parent 7ad8d86e7f
commit 22bf15864e
10 changed files with 487 additions and 482 deletions

View File

@ -25,6 +25,15 @@ class FormController extends Controller
return view('forms.create');
}
public function togglePublish(Form $form)
{
$form->is_published = !$form->is_published;
$form->save();
return redirect()->route('forms.show', $form->id)->with('success', 'Form publish status updated.');
}
public function edit(Form $form)
{
// Questions are already fetched with their options cast to array due to the casts property
@ -40,20 +49,22 @@ class FormController extends Controller
public function store(Request $request)
{
{
try {
$validatedData = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'questions' => 'required|array',
'questions.*.type' => 'required|string|in:multiple_choice,checkbox,dropdown,short_answer,long_answer',
'questions.*.text' => 'required|string', // This should match the key used in the JavaScript
'questions.*.type' => 'required|string|in:multiple_choice,checkbox,dropdown,text',
'questions.*.text' => 'required|string',
'questions.*.options' => 'nullable|array',
'questions.*.required' => 'nullable|boolean',
]);
$form = new Form();
$form->title = $validatedData['title'];
$form->description = $validatedData['description'];
$form->is_published = $request->input('is_published', false); // Default to false if not provided
$form->is_published = $request->input('is_published', false);
$form->user_id = Auth::id();
$form->save();
@ -61,15 +72,18 @@ class FormController extends Controller
$question = new Question();
$question->form_id = $form->id;
$question->type = $questionData['type'];
$question->question_text = $questionData['text']; // Ensure this matches the key in the validated data
$question->question_text = $questionData['text'];
$question->options = isset($questionData['options']) ? json_encode($questionData['options']) : null;
$question->required = isset($questionData['required']) ? $questionData['required'] : false;
$question->save();
}
Session::flash('success', 'Form created successfully!');
return response()->json(['success' => true, 'form_id' => $form->id]);
}
return response()->json(['success' => true, 'form_id' => $form->id]);
} catch (\Exception $e) {
Log::error('Error saving form: ' . $e->getMessage(), ['exception' => $e]);
return response()->json(['success' => false, 'message' => 'Error saving form'], 500);
}
}
public function show(Form $form)
{
@ -99,7 +113,7 @@ class FormController extends Controller
'description' => 'nullable|string|max:255',
'questions' => 'required|array',
'questions.*.id' => 'nullable|exists:questions,id',
'questions.*.type' => 'required|string|in:multiple_choice,checkbox,dropdown,short_answer,long_answer',
'questions.*.type' => 'required|string|in:multiple_choice,checkbox,dropdown,text',
'questions.*.text' => 'required|string|max:255',
'questions.*.options' => 'nullable|array',
'questions.*.options.*' => 'nullable|string|max:255',
@ -149,6 +163,8 @@ class FormController extends Controller
public function destroy(Form $form)
{
// This will also delete all related questions and responses due to foreign key constraints

View File

@ -103,12 +103,27 @@ public function viewResponses(Form $form)
{
Log::info($request->all()); // Log the entire request data for debugging
// Fetch all questions for the form
$questions = $form->questions;
// Extract IDs of required questions
$requiredQuestionIds = $questions->where('required', true)->pluck('id')->toArray();
// Validate and process form submission
$validatedData = $request->validate([
'answers' => 'required|array',
'answers.*' => 'required',
'answers.*' => 'required', // Ensure all answers are provided
]);
// Ensure all required questions are answered
foreach ($requiredQuestionIds as $requiredQuestionId) {
if (!array_key_exists($requiredQuestionId, $validatedData['answers'])) {
return redirect()->back()
->withErrors(['errors' => 'Please answer all required questions.'])
->withInput();
}
}
Log::info($validatedData); // Log the validated data for debugging
// Generate a UUID for response_id
@ -129,4 +144,9 @@ public function viewResponses(Form $form)
return redirect()->route('responses.showForm', $form)
->with('success', 'Response submitted successfully.');
}
}

View File

@ -14,8 +14,9 @@ return new class extends Migration
Schema::create('questions', function (Blueprint $table) {
$table->id();
$table->foreignId('form_id')->constrained()->onDelete('cascade');
$table->enum('type', ['multiple_choice', 'checkbox', 'dropdown', 'short_answer', 'long_answer']);
$table->enum('type', ['multiple_choice', 'checkbox', 'dropdown', 'text']);
$table->text('question_text');
$table->boolean('required')->default(false);
$table->json('options')->nullable(); // Store options as JSON if applicable
$table->timestamps();
});

View File

@ -1,216 +1,3 @@
// document.addEventListener("DOMContentLoaded", function () {
// const questionsSection = document.getElementById("questions_section");
// function addOption(button) {
// const optionContainer = button.previousElementSibling;
// const optionDiv = document.createElement("div");
// optionDiv.className = "option";
// optionDiv.innerHTML = `
// <input type="text" class="form-control option-input" placeholder="New Option" />
// <span class="delete-option" onclick="deleteOption(this)">&#10005;</span>
// `;
// optionContainer.appendChild(optionDiv);
// updateAddButtonPosition();
// }
// function deleteOption(span) {
// const optionDiv = span.parentElement;
// optionDiv.remove();
// updateAddButtonPosition();
// }
// function changeQuestionType(select) {
// const questionInput = select.nextElementSibling;
// questionInput.style.display = "block";
// const optionsContainer = select.nextElementSibling.nextElementSibling;
// optionsContainer.innerHTML =
// '<input type="text" class="form-control option-input" placeholder="Option 1">';
// }
// let questionCount = document.querySelectorAll(".question").length;
// function addNewQuestion() {
// const newQuestionDiv = document.createElement("div");
// newQuestionDiv.className = "question";
// newQuestionDiv.innerHTML = `
// <select class="form-control question_type" onchange="changeQuestionType(this)">
// <option value="">Select Question Type</option>
// <option value="multiple_choice">Multiple Choice</option>
// <option value="checkbox">Checkbox</option>
// <option value="dropdown">Dropdown</option>
// </select>
// <input type="text" class="form-control question-input" placeholder="Type your question here" />
// <div class="options-container">
// <div class="option">
// <input type="text" class="form-control option-input" placeholder="Option 1" />
// <span class="delete-option" onclick="deleteOption(this)">&#10005;</span>
// </div>
// </div>
// <button class="btn btn-secondary" onclick="addOption(this)">Add Option</button>
// <button class="btnn" onclick="deleteQuestion(this)">
// <img src="public/images/bin.png" alt="" width="20px" height="20px" />
// </button>
// `;
// questionsSection.appendChild(newQuestionDiv);
// questionCount++;
// updateAddButtonPosition();
// }
// function saveForm() {
// const formTitle = document.getElementById("form-title").value;
// const formDescription =
// document.getElementById("form-description").value;
// const questions = document.querySelectorAll(".question");
// let formData = [];
// questions.forEach((question, index) => {
// const questionType = question.querySelector("select").value;
// const questionText =
// question.querySelector(".question-input").value;
// const options = Array.from(
// question.querySelectorAll(".option-input")
// ).map((input) => input.value);
// formData.push({
// type: questionType,
// text: questionText, // Ensure this matches the key in the PHP validation
// options: options,
// });
// });
// // Get CSRF token
// const csrfTokenMeta = document.querySelector('meta[name="csrf-token"]');
// let csrfToken = "";
// if (csrfTokenMeta) {
// csrfToken = csrfTokenMeta.getAttribute("content");
// } else {
// console.error("CSRF token meta tag not found.");
// // Handle the error condition gracefully or abort further execution
// return;
// }
// const data = {
// title: formTitle,
// description: formDescription,
// questions: formData,
// };
// console.log("Form Data:", data); // Log form data
// console.log("CSRF Token:", csrfToken); // Log CSRF token
// // Send AJAX request to save the form data
// fetch("/forms", {
// method: "POST",
// headers: {
// "Content-Type": "application/json",
// "X-CSRF-TOKEN": csrfToken,
// },
// body: JSON.stringify(data),
// })
// .then((response) => {
// if (!response.ok) {
// throw new Error("Network response was not ok");
// }
// return response.json();
// })
// .then((result) => {
// console.log("Server Response:", result); // Log server response
// if (result.success) {
// alert("Form saved successfully!");
// window.location.href = "/forms"; // Redirect to forms index page
// } else {
// alert("Failed to save form.");
// }
// })
// .catch((error) => {
// console.error("Error saving form:", error);
// alert("An error occurred while saving the form.");
// });
// }
// window.addNewQuestion = addNewQuestion;
// window.deleteQuestion = deleteQuestion;
// window.addOption = addOption;
// window.changeQuestionType = changeQuestionType;
// window.saveForm = saveForm;
// window.previewForm = function (formId) {
// const formTitle = document.getElementById("form-title").value;
// const formDescription =
// document.getElementById("form-description").value;
// const questions = document.querySelectorAll(".question");
// let formData = [];
// questions.forEach((question, index) => {
// const questionType = question.querySelector("select").value;
// const questionText =
// question.querySelector(".question-input").value;
// const options = Array.from(
// question.querySelectorAll(".option-input")
// ).map((input) => input.value);
// formData.push({
// type: questionType,
// text: questionText,
// options: options,
// });
// });
// const formParams = new URLSearchParams({
// title: formTitle,
// description: formDescription,
// data: JSON.stringify(formData),
// });
// window.location.href = '/forms/' + formId + '/preview';
// };
// window.addNewQuestion = addNewQuestion;
// window.deleteQuestion = deleteQuestion;
// window.addOption = addOption;
// window.changeQuestionType = changeQuestionType;
// });
// function deleteQuestion(element) {
// let questionContainer = element.closest(".question");
// if (questionContainer) {
// questionContainer.remove();
// updateAddButtonPosition();
// }
// }
// function deleteOption(span) {
// const optionDiv = span.parentElement;
// optionDiv.remove();
// updateAddButtonPosition();
// }
// function updateAddButtonPosition() {
// const questionsSection = document.getElementById("questions_section");
// const lastQuestion = questionsSection.lastElementChild;
// // Check if lastQuestion exists before accessing its properties
// if (lastQuestion) {
// const selectQuestionType = lastQuestion.querySelector(".question_type");
// // Ensure selectQuestionType is not null before accessing offsetTop
// if (selectQuestionType) {
// const sidebar = document.getElementById("moveableDiv");
// const offset = selectQuestionType.offsetTop - sidebar.offsetHeight;
// sidebar.style.transform = `translateY(${offset}px)`;
// console.log(`Moving sidebar to: ${offset}px`);
// } else {
// console.warn("No .question_type found in last question.");
// }
// } else {
// const sidebar = document.getElementById("moveableDiv");
// sidebar.style.transform = `translateY(0px)`;
// console.log(`Moving sidebar to: 0px`);
// }
// }
document.addEventListener("DOMContentLoaded", function () {
const questionsSection = document.getElementById("questions_section");
@ -233,12 +20,27 @@ document.addEventListener("DOMContentLoaded", function () {
updateAddButtonPosition();
}
function changeQuestionType(select) {
const questionInput = select.nextElementSibling;
questionInput.style.display = "block";
const optionsContainer = select.nextElementSibling.nextElementSibling;
optionsContainer.innerHTML =
'<input type="text" class="form-control option-input" placeholder="Option 1">';
function changeQuestionType(selectElement) {
const questionContainer = selectElement.closest('.question');
const optionsContainer = questionContainer.querySelector('.options-container');
const addOptionButton = questionContainer.querySelector('.btn-secondary');
const questionType = selectElement.value;
// Clear the options container
optionsContainer.innerHTML = '';
if (questionType === 'multiple_choice' || questionType === 'checkbox' || questionType === 'dropdown') {
const optionDiv = document.createElement('div');
optionDiv.className = 'option d-flex align-items-center mb-2';
optionDiv.innerHTML = `
<input type="text" name="option" class="form-control option-input" placeholder="Option 1" />
<span class="delete-option ml-2 text-danger" onclick="deleteOption(this)" style="cursor: pointer;">&#10005;</span>
`;
optionsContainer.appendChild(optionDiv);
addOptionButton.style.display = 'inline-block'; // Show the "Add Option" button
} else if (questionType === 'text') {
addOptionButton.style.display = 'none'; // Hide the "Add Option" button
}
}
let questionCount = document.querySelectorAll(".question").length;
@ -247,26 +49,27 @@ document.addEventListener("DOMContentLoaded", function () {
const newQuestionDiv = document.createElement("div");
newQuestionDiv.className = "question";
newQuestionDiv.innerHTML = `
<div class="question mb-4 p-3 border rounded bg-white">
<div class="question mb-4 p-4 border rounded bg-white">
<select class="form-control question_type mb-1" onchange="changeQuestionType(this)">
<option value="">Select Question Type</option>
<option value="multiple_choice">Multiple Choice</option>
<option value="checkbox">Checkbox</option>
<option value="dropdown">Dropdown</option>
<option value="text">Text</option>
</select>
<input type="text" name="question" class="form-control question-input mb-3" placeholder="Type your question here" />
<div class="options-container mb-3">
<div class="option d-flex align-items-center">
<input type="text" name="option" class="form-control option-input" placeholder="Option 1" />
<span class="delete-option ml-2 text-danger" onclick="deleteOption(this)" style="cursor: pointer;">&#10005;</span>
</div>
<!-- Options or text input will be dynamically added here -->
</div>
<button class="btn btn-secondary" onclick="addOption(this)">
<button class="btn btn-secondary add-option-btn" onclick="addOption(this)">
Add Option
</button>
<button class="btn btn-md" id="moveUpButton" onclick="deleteQuestion(this);">
<img src="/images/bin.png" alt="" width="20px" height="20px" />
</button>
<label class="ml-3">
<input type="checkbox" class="required-checkbox"> Required
</label>
</div>
`;
questionsSection.appendChild(newQuestionDiv);
@ -316,14 +119,21 @@ document.addEventListener("DOMContentLoaded", function () {
const questions = document.querySelectorAll(".question");
let formData = [];
questions.forEach((question, index) => {
questions.forEach((question) => {
const questionType = question.querySelector("select").value;
const questionText = question.querySelector(".question-input").value;
const options = Array.from(question.querySelectorAll(".option-input")).map((input) => input.value);
const isRequired = question.querySelector(".required-checkbox").checked;
let options = [];
if (questionType === 'multiple_choice' || questionType === 'checkbox' || questionType === 'dropdown') {
options = Array.from(question.querySelectorAll(".option-input")).map((input) => input.value);
}
formData.push({
type: questionType,
text: questionText,
options: options,
required: isRequired
});
});
@ -339,41 +149,52 @@ document.addEventListener("DOMContentLoaded", function () {
const data = {
title: formTitle,
description: formDescription,
questions: formData,
questions: formData
};
console.log("Form Data:", data);
console.log("CSRF Token:", csrfToken);
fetch("/forms", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": csrfToken,
"X-CSRF-TOKEN": csrfToken
},
body: JSON.stringify(data),
})
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((result) => {
console.log("Server Response:", result);
if (result.success) {
alert("Form saved successfully!");
window.location.href = "/forms";
} else {
alert("Failed to save form.");
}
})
.catch((error) => {
console.error("Error saving form:", error);
alert("An error occurred while saving the form.");
});
.then((response) => response.json())
.then((result) => {
if (result.success) {
Swal.fire({
title: 'Success!',
text: 'Form saved successfully.',
icon: 'success',
confirmButtonText: 'OK'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = "/forms";
}
});
} else {
Swal.fire({
title: 'Error!',
text: 'Failed to save form.',
icon: 'error',
confirmButtonText: 'OK'
});
}
})
.catch((error) => {
console.error("Error saving form:", error);
alert("An error occurred while saving the form.");
});
}
window.addNewQuestion = addNewQuestion;
window.deleteQuestion = deleteQuestion;
window.addOption = addOption;

View File

@ -1,4 +1,4 @@
<!DOCTYPE html>
{{-- <!DOCTYPE html>
<html lang="en">
<head>
@ -98,4 +98,121 @@
<script src={{ asset('js/script.js') }}></script>
</body>
</html> --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Google-Form-Clone</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href={{ asset('css/app.css') }} />
<script src="https://cdn.jsdelivr.net/npm/alpinejs@2.8.2/dist/alpine.min.js" defer></script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"
rel="stylesheet" />
</head>
<body style="background-color: #f4f4f9;" >
<nav class="bg-white p-4 shadow-sm">
<div class="mx-auto flex justify-between items-center">
<a href="{{ url('/') }}" style="color: rgb(103,58,183)"
class="text-3xl font-bold font-sans">LaraForms</a>
<div class="relative dropdown">
<button id="profileMenuButton" class="flex items-center focus:outline-none">
<img src="{{ asset('images/user.png') }}" alt="Profile"
class="w-10 h-10 rounded-full border-2 border-white">
</button>
<div id="profileMenu"
class="dropdown-menu hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-2">
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="block px-4 py-2 text-gray-700 hover:bg-gray-200 w-full text-left">
Logout
</button>
</form>
</div>
</div>
</div>
</nav>
<br><br>
<div style="background-color: #f4f4f9; max-width: 100%" class="question_form p-4 rounded">
<div class="section">
<div class="question_title_section mb-1">
<div class="question_form_top">
<input type="text" id="form-title" name="title" class="form-control form-control-lg p-2 mb-2" placeholder="Untitled Form" />
<input type="text" name="description" id="form-description" class="form-control form-control-sm" placeholder="Form Description" />
</div>
</div>
</div>
<div class="section" id="questions_section">
<div class="question mb-4 p-4 border rounded bg-white">
<select class="form-control question_type mb-1" onchange="changeQuestionType(this)">
<option value="">Select Question Type</option>
<option value="multiple_choice">Multiple Choice</option>
<option value="checkbox">Checkbox</option>
<option value="dropdown">Dropdown</option>
<option value="text">Text</option>
</select>
<input type="text" name="question" class="form-control question-input mb-3" placeholder="Type your question here" />
<div class="options-container mb-3">
<!-- Options or text input will be dynamically added here -->
</div>
<button class="btn btn-secondary add-option-btn" onclick="addOption(this)">
Add Option
</button>
<button class="btn btn-md" id="moveUpButton" onclick="deleteQuestion(this);">
<img src="{{ asset('images/bin.png') }}" alt="" width="20px" height="20px" />
</button>
<label class="ml-3">
<input type="checkbox" class="required-checkbox"> Required
</label>
</div>
<div class="sidebar">
<div id="moveableDiv">
<button class="btn btn-light shadow-sm" onclick="addNewQuestion();">
<img src="{{ asset('images/add.png') }}" alt="" width="20px" height="20px" />
</button>
</div>
</div>
</div>
</div>
<div style="background-color: #f4f4f9;" class="btnsub">
<span>
<button type="submit" name="save" value="save" onclick="saveForm()"
class="btnsave btn btn-secondary">
Save
</button>
</span>
&nbsp;
&nbsp;
&nbsp;
<span>
<button type="submit" name="publish" value="publish" onclick="saveForm()"
class="btnsave btn btn-secondary">
Publish
</button>
</span>
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
</div>
<script src={{ asset('js/script.js') }}></script>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@2.8.2/dist/alpine.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</body>
</html>

View File

@ -60,15 +60,11 @@
@foreach ($questions as $index => $question)
<div class="question mb-4 p-3 border rounded bg-light" data-index="{{ $index }}">
<div class="form-group">
<select class="form-control question-type" id="question-type-{{ $index }}"
name="questions[{{ $index }}][type]">
<option value="multiple_choice"
{{ $question->type === 'multiple_choice' ? 'selected' : '' }}>Multiple Choice
</option>
<option value="checkbox" {{ $question->type === 'checkbox' ? 'selected' : '' }}>
Checkbox</option>
<option value="dropdown" {{ $question->type === 'dropdown' ? 'selected' : '' }}>
Dropdown</option>
<select class="form-control question-type" id="question-type-{{ $index }}" name="questions[{{ $index }}][type]">
<option value="multiple_choice" {{ $question->type === 'multiple_choice' ? 'selected' : '' }}>Multiple Choice</option>
<option value="checkbox" {{ $question->type === 'checkbox' ? 'selected' : '' }}>Checkbox</option>
<option value="dropdown" {{ $question->type === 'dropdown' ? 'selected' : '' }}>Dropdown</option>
<option value="text" {{ $question->type === 'text' ? 'selected' : '' }}>Text</option>
</select>
</div>
<div class="form-group">
@ -76,24 +72,25 @@
name="questions[{{ $index }}][text]" class="form-control question-input"
value="{{ $question->question_text }}" required>
</div>
<div class="form-group options-container">
<div class="form-group form-check">
<input type="checkbox" id="question-required-{{ $index }}"
name="questions[{{ $index }}][required]" class="form-check-input"
{{ $question->required ? 'checked' : '' }}>
<label for="question-required-{{ $index }}" class="form-check-label">Required</label>
</div>
<div class="form-group options-container" style="{{ $question->type === 'text' ? 'display:none;' : '' }}">
<label>Options</label>
@if (is_array($question->options))
@foreach ($question->options as $optionIndex => $option)
<div class="option d-flex align-items-center mb-2">
<input type="text"
name="questions[{{ $index }}][options][{{ $optionIndex }}]"
class="form-control option-input" value="{{ $option }}">
<span class="delete-option ml-2 text-danger" onclick="deleteOption(this)"
style="cursor: pointer;">&#10005;</span>
<input type="text" name="questions[{{ $index }}][options][{{ $optionIndex }}]" class="form-control option-input" value="{{ $option }}">
<span class="delete-option ml-2 text-danger" onclick="deleteOption(this)" style="cursor: pointer;">&#10005;</span>
</div>
@endforeach
@endif
<button type="button" class="btn btn-secondary" onclick="addOption(this)">Add
Option</button>
<button type="button" class="btn btn-secondary" onclick="addOption(this)">Add Option</button>
<button class="btn btn-md" id="moveUpButton" onclick="deleteQuestion(this);">
<img src="{{ asset('images/bin.png') }}" alt="" width="20px"
height="20px" />
<img src="{{ asset('images/bin.png') }}" alt="" width="20px" height="20px" />
</button>
</div>
</div>
@ -106,7 +103,6 @@
</div>
</div>
</div>
<button type="button" class="btn btn-primary mb-4" onclick="addNewQuestion()">Add New Question</button>
<button type="submit" class="btn btn-success mb-4">Save</button>
</form>
</div>
@ -142,22 +138,29 @@
const questionHtml = `
<div class="question mb-4 p-3 border rounded bg-light" data-index="${questionIndex}">
<div class="form-group">
<select class="form-control question-type" id="question-type-${questionIndex}" name="questions[${questionIndex}][type]">
<select class="form-control question-type" id="question-type-${questionIndex}" name="questions[${questionIndex}][type]" onchange="handleQuestionTypeChange(this)">
<option value="multiple_choice">Multiple Choice</option>
<option value="checkbox">Checkbox</option>
<option value="dropdown">Dropdown</option>
<option value="text">Text</option>
</select>
</div>
<div class="form-group">
<input type="text" id="question-text-${questionIndex}" name="questions[${questionIndex}][text]" class="form-control question-input" placeholder="Type your question here" required>
</div>
<div class="form-group form-check">
<input type="checkbox" id="question-required-{{ $index }}"
name="questions[{{ $index }}][required]" class="form-check-input"
{{ $question->required ? 'checked' : '' }}>
<label for="question-required-{{ $index }}" class="form-check-label">Required</label>
</div>
<div class="form-group options-container">
<label>Options</label>
<button type="button" class="btn btn-secondary" onclick="addOption(this)">Add Option</button>
<button class="btn btn-md" id="moveUpButton" onclick="deleteQuestion(this);">
<img src="{{ asset('images/bin.png') }}" alt="" width="20px" height="20px" />
</button>
</div>
<button class="btn btn-md" id="moveUpButton" onclick="deleteQuestion(this);">
<img src="{{ asset('images/bin.png') }}" alt="" width="20px" height="20px" />
</button>
</div>
`;
@ -167,9 +170,35 @@
function deleteQuestion(button) {
$(button).closest('.question').remove();
updateQuestionIndices();
updateAddButtonPosition();
}
function updateQuestionIndices() {
$('#questions-section .question').each((index, element) => {
$(element).attr('data-index', index);
$(element).find('.question-type').attr('name', `questions[${index}][type]`);
$(element).find('.question-input').attr('name', `questions[${index}][text]`);
$(element).find('.question-input').attr('id', `question-text-${index}`);
$(element).find('.form-check-input').attr('name', `questions[${index}][required]`);
$(element).find('.form-check-input').attr('id', `question-required-${index}`);
$(element).find('.options-container').find('.option-input').each((optionIndex, optionElement) => {
$(optionElement).attr('name', `questions[${index}][options][${optionIndex}]`);
});
});
}
function handleQuestionTypeChange(selectElement) {
const selectedType = $(selectElement).val();
const optionsContainer = $(selectElement).closest('.question').find('.options-container');
if (selectedType === 'text') {
optionsContainer.hide();
} else {
optionsContainer.show();
}
}
function updateAddButtonPosition() {
const questions = document.querySelectorAll("#questions-section .question");
const sidebar = document.getElementById("moveableDiv");
@ -194,21 +223,32 @@
}
}
$(document).ready(function() {
$('#profileMenuButton').on('click', function() {
$(document).ready(function () {
$('#profileMenuButton').click(function () {
$('#profileMenu').toggleClass('hidden');
});
$(document).on('click', function(e) {
if (!$(e.target).closest('#profileMenuButton').length && !$(e.target).closest(
'#profileMenu').length) {
$(document).click(function (event) {
if (!$(event.target).closest('#profileMenuButton, #profileMenu').length) {
$('#profileMenu').addClass('hidden');
}
});
$('.question-type').each((index, element) => {
handleQuestionTypeChange(element);
});
updateAddButtonPosition();
});
</script>
</body>
</html>

View File

@ -74,9 +74,11 @@
<th
class="py-4 px-6 border-b border-gray-200 text-left text-sm font-semibold text-gray-600">
Responses</th>
<th
class="py-4 px-6 border-b border-gray-200 text-left text-sm font-semibold text-gray-600">
Actions</th>
<th class="py-4 px-6 border-b border-gray-200 text-left text-sm font-semibold text-gray-600">
Status</th>
<th></th>
<th>
</th>
</tr>
</thead>
<tbody>
@ -93,11 +95,20 @@
<a href="{{ route('responses.viewResponses', $form) }}"
class="text-blue-500 hover:underline">View Responses</a>
</td>
<td class="py-4 px-6 border-b border-gray-200">
@if ($form->is_published)
Published
@else
Unpublished
@endif
</td>
<td class="py-8 px-6 border-b border-gray-200 flex items-center space-x-10">
@if (!$form->is_published)
<a href="{{ route('forms.edit', $form) }}"
class="text-green-500 hover:underline">Edit</a>
@endif
</td>
<td>
<form action="{{ route('forms.destroy', $form) }}" method="POST"
class="inline-block">
@csrf

View File

@ -1,134 +1,4 @@
{{-- <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Show Form - {{ $form->title }}</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap" rel="stylesheet">
</head>
<body>
<div class="form_header">
<div class="form_header_left">
<a href="/forms"><img src="{{ asset('images/google-form.png') }}" class="form_header_icon" height="45px" width="40px" /></a>
<input type="text" name="title" id="form-title-nav" placeholder="Untitled Form" class="form_name" value="{{ $form->title }}" readonly />
<img src="{{ asset('images/folder.png') }}" alt="" class="form_header_icon" height="20px" width="20px" />
<img src="{{ asset('images/star.svg') }}" alt="" class="form_header_icon" />
</div>
<div class="form_header_right">
<span><img src="{{ asset('images/palette-svgrepo-com.svg') }}" alt="pallette" height="20px" width="20px" /></span>
<span><img src="{{ asset('images/view.png') }}" alt="eye" height="20px" width="20px" onclick="previewForm()" /></span>
<span><img src="{{ asset('images/undo.png') }}" alt="" height="20px" width="20px" /></span>
<span><img src="{{ asset('images/forward.png') }}" alt="" height="20px" width="20px" /></span>
<button class="btn">Send</button>
<span><img src="{{ asset('images/menu.png') }}" alt="menu" height="30px" width="30px" /></span>
<span><img src="{{ asset('images/user.png') }}" alt="" height="30px" width="30px" /></span>
</div>
</div>
<div class="container">
<div class="box">
<input type="radio" class="tab-toggle" name="tab-toggle" id="tab1" checked />
<input type="radio" class="tab-toggle" name="tab-toggle" id="tab2" />
<input type="radio" class="tab-toggle" name="tab-toggle" id="tab3" />
<ul class="tab-list">
<li class="tab-item">
<label class="tab-trigger" for="tab1"><b>Questions</b></label>
</li>
<li class="tab-item">
<label class="tab-trigger" for="tab2"><b>Responses</b></label>
</li>
<li class="tab-item">
<label class="tab-trigger" for="tab3"><b>Settings</b></label>
</li>
</ul>
</div>
</div>
<form id="publish-form" method="POST" action="{{ route('forms.update', $form) }}">
@csrf
@method('PUT')
<div class="question_form">
<br />
<div class="section">
<div class="question_title_section">
<div class="question_form_top">
<input type="text" id="form-title" name="title" class="question_form_top_name form-control" style="color: black" placeholder="Untitled Form" value="{{ $form->title }}" readonly />
<input type="text" name="description" id="form-description" class="question_form_top_desc" style="color: black" placeholder="Form Description" value="{{ $form->description }}" readonly />
</div>
</div>
</div>
<div class="section" id="questions_section">
@foreach ($form->questions as $index => $question)
<div class="question">
<select class="form-control question_type" name="questions[{{ $index }}][type]" onchange="changeQuestionType(this)" disabled>
<option value="multiple_choice" {{ $question->type === 'multiple_choice' ? 'selected' : '' }}>Multiple Choice</option>
<option value="checkbox" {{ $question->type === 'checkbox' ? 'selected' : '' }}>Checkbox</option>
<option value="dropdown" {{ $question->type === 'dropdown' ? 'selected' : '' }}>Dropdown</option>
</select>
<input type="text" name="questions[{{ $index }}][text]" class="form-control question-input" placeholder="Type your question here" value="{{ $question->question_text }}" readonly />
@if ($question->options)
<div class="options-container">
@foreach (json_decode($question->options) as $optionIndex => $option)
<div class="option">
<input type="text" name="questions[{{ $index }}][options][{{ $optionIndex }}]" class="form-control option-input" placeholder="Option" value="{{ $option }}" readonly />
</div>
@endforeach
</div>
@endif
</div>
@endforeach
</div>
</div>
<div class="btnsub">
<span>
<a href="{{ route('forms.edit', $form) }}" class="btnsave btn btn-secondary">Edit</a>
</span>
&nbsp;
&nbsp;
&nbsp;
<span>
<button type="submit" name="publish" value="publish" class="btnsave btn btn-secondary">
{{ $form->is_published ? 'Unpublish' : 'Publish' }}
</button>
</span>
&nbsp;
&nbsp;
&nbsp;
<span>
<a href="/forms" class="btnsave btn btn-secondary">Return</a>
</span>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('button[name="publish"]').addEventListener('click', function(event) {
event.preventDefault();
document.getElementById('publish-form').action = "{{ route('forms.update', $form) }}";
document.getElementById('publish-form').submit();
});
});
</script>
<script src="{{ asset('js/script.js') }}"></script>
</body>
</html>
--}}
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
@ -194,6 +64,7 @@
<option value="multiple_choice" {{ $question->type === 'multiple_choice' ? 'selected' : '' }}>Multiple Choice</option>
<option value="checkbox" {{ $question->type === 'checkbox' ? 'selected' : '' }}>Checkbox</option>
<option value="dropdown" {{ $question->type === 'dropdown' ? 'selected' : '' }}>Dropdown</option>
<option value="text" {{ $question->type === 'text' ? 'selected' : '' }}>Text</option>
</select>
<input type="text" name="questions[{{ $index }}][text]" class="form-control question-input mb-3" placeholder="Type your question here" value="{{ $question->question_text }}" readonly />
@if ($question->options)
@ -210,9 +81,16 @@
</div>
</div>
<div class="btnsub text-center mt-4">
<span>
<a href="{{ route('forms.index') }}" class="btnsave btn btn-secondary">Return to Forms</a>
</span>
<form action="{{ route('forms.publish', $form->id) }}" method="POST">
@csrf
@method('PATCH')
<span><button type="submit" name="publish" value="publish" class="btnsave btn btn-secondary">
{{ $form->is_published ? 'Unpublish' : 'Publish' }}
</button></span>
</form>
&nbsp;
&nbsp;
<span><a href="{{ route('forms.index') }}" class="btnsave btn btn-secondary">Return to Forms</a></span>
</div>
<script src="{{ asset('js/script.js') }}"></script>
</body>

View File

@ -1,54 +1,7 @@
{{-- @extends('layouts.app')
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
@section('content')
<div class="container mx-auto py-8">
<h1 class="text-3xl font-semibold">{{ $form->title }}</h1>
<p class="text-gray-700 mt-2">{{ $form->description }}</p>
<form action="{{ route('responses.submitForm', $form) }}" method="POST" class="mt-8">
@csrf
@foreach ($questions as $question)
<div class="mt-6">
<label class="block font-medium text-gray-800">{{ $question->question_text }}</label>
@if ($question->type == 'multiple_choice')
@foreach (json_decode($question->options) as $option)
<label class="inline-flex items-center mt-2">
<input class="form-radio text-purple-600" type="radio" name="answers[{{ $question->id }}]" value="{{ $option }}">
<span class="ml-2 text-gray-700">{{ $option }}</span>
</label>
@endforeach
@elseif($question->type == 'checkbox')
@foreach (json_decode($question->options) as $option)
<label class="inline-flex items-center mt-2">
<input class="form-checkbox text-purple-600" type="checkbox" name="answers[{{ $question->id }}][]" value="{{ $option }}">
<span class="ml-2 text-gray-700">{{ $option }}</span>
</label>
@endforeach
@elseif($question->type == 'dropdown')
<select class="form-select mt-2 block w-full p-2 border border-gray-300 rounded-md bg-white shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" name="answers[{{ $question->id }}]">
@foreach (json_decode($question->options) as $option)
<option value="{{ $option }}">{{ $option }}</option>
@endforeach
</select>
@elseif($question->type == 'short_answer')
<input type="text" name="answers[{{ $question->id }}]" class="form-input mt-2 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm">
@elseif($question->type == 'long_answer')
<textarea name="answers[{{ $question->id }}]" class="form-textarea mt-2 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm"></textarea>
@endif
</div>
@endforeach
<button type="submit" class="mt-6 inline-flex items-center px-4 py-2 bg-purple-700 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-purple-800 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 focus:ring-offset-purple-200">
Submit
</button>
</form>
</div>
@endsection
--}}
@extends('layouts.app')
@section('content')
<div class="container mx-auto py-8 px-4 md:px-0">
<div class="bg-white shadow-md rounded-lg p-6">
@ -80,9 +33,7 @@
<option value="{{ $option }}">{{ $option }}</option>
@endforeach
</select>
@elseif($question->type == 'short_answer')
<input type="text" name="answers[{{ $question->id }}]" class="form-input mt-2 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm">
@elseif($question->type == 'long_answer')
@elseif($question->type == 'text')
<textarea name="answers[{{ $question->id }}]" class="form-textarea mt-2 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm"></textarea>
@endif
</div>
@ -94,4 +45,154 @@
</form>
</div>
</div>
<script>
document.getElementById('responseForm').addEventListener('submit', function(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
fetch(form.action, {
method: form.method,
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
title: 'Success!',
text: 'Form submitted successfully.',
icon: 'success',
confirmButtonText: 'OK'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = '/forms';
}
});
} else {
Swal.fire({
title: 'Error!',
text: 'Failed to submit form.',
icon: 'error',
confirmButtonText: 'OK'
});
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
title: 'Error!',
text: 'An error occurred while submitting the form.',
icon: 'error',
confirmButtonText: 'OK'
});
});
});
</script>
@endsection --}}
@extends('layouts.app')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
@section('content')
<div class="container mx-auto py-8 px-4 md:px-0">
<div class="bg-white shadow-md rounded-lg p-6">
<h1 class="text-3xl font-semibold text-gray-900">{{ $form->title }}</h1>
<p class="text-gray-600 mt-2">{{ $form->description }}</p>
<form id="responseForm" action="{{ route('responses.submitForm', $form) }}" method="POST" class="mt-8">
@csrf
@foreach ($questions as $question)
<div class="mt-6">
<label class="block font-medium text-base text-gray-800 mb-2">{{ $question->question_text }}</label>
@if ($question->type == 'multiple_choice')
@foreach (json_decode($question->options) as $option)
<label class="flex items-center mt-2">
<input class="form-radio text-base text-purple-600 h-4 w-4" type="radio" name="answers[{{ $question->id }}]" value="{{ $option }}">
<span class="ml-2 text-gray-700">{{ $option }}</span>
</label>
@endforeach
@elseif($question->type == 'checkbox')
@foreach (json_decode($question->options) as $option)
<label class="flex items-center mt-2">
<input class="form-checkbox text-purple-600 h-4 w-4" type="checkbox" name="answers[{{ $question->id }}][]" value="{{ $option }}">
<span class="ml-2 text-gray-700">{{ $option }}</span>
</label>
@endforeach
@elseif($question->type == 'dropdown')
<select class="form-select mt-2 block w-full p-2 border border-gray-300 rounded-md bg-white shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" name="answers[{{ $question->id }}]">
@foreach (json_decode($question->options) as $option)
<option value="{{ $option }}">{{ $option }}</option>
@endforeach
</select>
@elseif($question->type == 'text')
<textarea name="answers[{{ $question->id }}]" class="form-textarea mt-2 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm"></textarea>
@endif
</div>
@endforeach
<button type="submit" class="mt-8 w-full md:w-auto inline-flex justify-center items-center px-6 py-3 bg-purple-700 border border-transparent rounded-md font-semibold text-white text-lg uppercase tracking-widest hover:bg-purple-800 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2">
Submit
</button>
</form>
</div>
</div>
<script>
document.getElementById('responseForm').addEventListener('submit', function(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
fetch(form.action, {
method: form.method,
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
title: 'Success!',
text: 'Form submitted successfully.',
icon: 'success',
confirmButtonText: 'OK'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = '/forms';
}
});
} else {
Swal.fire({
title: 'Error!',
text: 'Failed to submit form.',
icon: 'error',
confirmButtonText: 'OK'
});
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
title: 'Success!',
text: 'Form Submitted Successfully',
icon: 'success',
confirmButtonText: 'OK'
});
});
});
</script>
@endsection

View File

@ -22,7 +22,7 @@ Route::middleware(['auth'])->group(function () {
Route::put('/forms/{form}', [FormController::class, 'update'])->name('forms.update');
Route::delete('/forms/{form}', [FormController::class, 'destroy'])->name('forms.destroy');
Route::get('/forms/{form}/preview', [FormController::class, 'preview'])->name('forms.preview');
Route::patch('/forms/{form}/publish', [FormController::class, 'togglePublish'])->name('forms.publish');
});
// Response routes