Added flash messages, minor UI changes

This commit is contained in:
yash 2024-07-16 17:14:26 +05:30
parent 03da2ae482
commit 6bc022b0f3
16 changed files with 538 additions and 265 deletions

View File

@ -8,7 +8,7 @@ use App\Models\Question;
use App\Models\Response; use App\Models\Response;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
class FormController extends Controller class FormController extends Controller
{ {
public function index() public function index()
@ -64,7 +64,7 @@ return view('forms.edit', compact('form', 'questions'));
$question->save(); $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]);
} }
@ -76,6 +76,12 @@ return view('forms.edit', compact('form', 'questions'));
return view('forms.show', compact('form')); return view('forms.show', compact('form'));
} }
public function preview($id)
{
$form = Form::findOrFail($id);
return view('forms.previewForm', compact('form'));
}
public function update(Request $request, Form $form) public function update(Request $request, Form $form)
{ {
@ -147,6 +153,6 @@ return view('forms.edit', compact('form', 'questions'));
// This will also delete all related questions and responses due to foreign key constraints // This will also delete all related questions and responses due to foreign key constraints
$form->delete(); $form->delete();
return redirect()->route('forms.index')->with('success', 'Form deleted successfully.'); return redirect()->route('forms.index')->with('delete', 'Form deleted successfully.');
} }
} }

View File

@ -49,6 +49,9 @@ public function viewResponses(Form $form)
public function showForm(Form $form) public function showForm(Form $form)
{ {
$questions = $form->questions; $questions = $form->questions;
if (!$form->is_published) {
return redirect('/forms')->with('delete', 'This form is not published.');
}
return view('responses.showForm', compact('form', 'questions')); return view('responses.showForm', compact('form', 'questions'));
} }

22
package-lock.json generated
View File

@ -4,6 +4,9 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"dependencies": {
"sweetalert": "^2.1.2"
},
"devDependencies": { "devDependencies": {
"@popperjs/core": "^2.11.6", "@popperjs/core": "^2.11.6",
"axios": "^1.6.4", "axios": "^1.6.4",
@ -723,6 +726,11 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/es6-object-assign": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
"integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw=="
},
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@ -994,6 +1002,11 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/promise-polyfill": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz",
"integrity": "sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ=="
},
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@ -1073,6 +1086,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/sweetalert": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/sweetalert/-/sweetalert-2.1.2.tgz",
"integrity": "sha512-iWx7X4anRBNDa/a+AdTmvAzQtkN1+s4j/JJRWlHpYE8Qimkohs8/XnFcWeYHH2lMA8LRCa5tj2d244If3S/hzA==",
"dependencies": {
"es6-object-assign": "^1.1.0",
"promise-polyfill": "^6.0.2"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",

View File

@ -12,5 +12,8 @@
"laravel-vite-plugin": "^1.0.0", "laravel-vite-plugin": "^1.0.0",
"sass": "^1.56.1", "sass": "^1.56.1",
"vite": "^5.0.0" "vite": "^5.0.0"
},
"dependencies": {
"sweetalert": "^2.1.2"
} }
} }

View File

@ -32,6 +32,7 @@
top: 0; top: 0;
left: 75%; left: 75%;
top: 10%; top: 10%;
border-radius: 6px;
} }
.btnp { .btnp {

View File

@ -1,33 +1,33 @@
body { body {
font-family: "Open Sans", sans-serif; font-family: "Open Sans", sans-serif;
background-color: #f2f2f2; background-color: #f2f2f2;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.form_header { .form_header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
background-color: white; background-color: white;
color: white; color: white;
padding: 10px 20px; padding: 10px 20px;
} }
.form_header_left { .form_header_left {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.form_header_icon { .form_header_icon {
margin-right: 10px; margin-right: 10px;
} }
.form_header_right { .form_header_right {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.form_header_right img { .form_header_right img {
margin-right: 10px; margin-right: 10px;
cursor: pointer; cursor: pointer;
} }
.container { .container {
max-width: 800px; max-width: 800px;
margin: 20px auto; margin: 20px auto;
background-color: white; background-color: white;
@ -36,39 +36,39 @@ body {
border-left: 4px #4285f4 solid; border-left: 4px #4285f4 solid;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
} }
.start_form { .start_form {
margin-bottom: 10px; margin-bottom: 10px;
background-color: #f5f5f5; background-color: #f5f5f5;
padding: 10px; padding: 10px;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
} }
.start_form a { .start_form a {
text-decoration: none; text-decoration: none;
color: rgb(103, 58, 183); color: rgb(103, 58, 183);
} }
.start_form a:hover { .start_form a:hover {
text-decoration: underline; text-decoration: underline;
} }
.recent_forms { .recent_forms {
background-color: #f4f4f9; background-color: #f4f4f9;
padding: 20px; padding: 20px;
border-radius: 5px; border-radius: 5px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
} }
.recent_forms h2 { .recent_forms h2 {
color: rgb(103, 58, 183); color: rgb(103, 58, 183);
margin-bottom: 10px; margin-bottom: 10px;
} }
.recent_forms ul { .recent_forms ul {
list-style-type: none; list-style-type: none;
padding: 0; padding: 0;
} }
.recent_forms ul li { .recent_forms ul li {
margin-bottom: 10px; margin-bottom: 10px;
} }
.recent_forms ul li a { .recent_forms ul li a {
color: #333; color: #333;
text-decoration: none; text-decoration: none;
display: block; display: block;
@ -76,15 +76,29 @@ body {
border-radius: 4px; border-radius: 4px;
background-color: #e0e0e0; background-color: #e0e0e0;
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
} }
.recent_forms ul li a:hover { .recent_forms ul li a:hover {
background-color: #c5c5c5; background-color: #c5c5c5;
} }
.form_name { .form_name {
color: #5f6368; color: black;
} font-weight: 500;
.roboto-light { }
.roboto-light {
font-family: "Roboto", sans-serif; font-family: "Roboto", sans-serif;
font-weight: 150; font-weight: 150;
font-style: normal; font-style: normal;
} }
#shareLink{
border: 2px solid black;
}
.btn-primary{
background-color: rgb(103, 58, 183);
}
:hover.btn-primary{
background-color: rgb(96, 4, 96);
color: white;
}

56
public/css/preview.css Normal file
View File

@ -0,0 +1,56 @@
.form_preview {
padding: 20px;
max-width: 600px;
margin: auto;
background-color: #f4f4f9;
border-radius: 8px;
}
#form_name {
font-family: Arial, Helvetica, sans-serif;
font-size: 32px;
font-weight: 400;
line-height: 135%;
border-bottom: 1px solid #f4f4f9;
color: black;
}
#form_desc {
font-family: Arial, Helvetica, sans-serif;
font-size: 13px;
font-weight: 400;
line-height: 100%;
border-bottom: 1px solid #f4f4f9;
color: black;
margin-top: 10px;
}
.question {
margin-bottom: 20px;
background-color: #fff;
border-radius: 8px;
padding: 20px;
border-top: 8px solid rgb(103, 58, 183);
}
.question_title {
font-weight: bold;
font-size: 18px;
margin-bottom: 10px;
}
.options {
margin-top: 10px;
}
.option {
margin-bottom: 10px;
display: flex;
align-items: center;
}
.option input[type="radio"],
.option input[type="checkbox"] {
margin-right: 10px;
}

View File

@ -48,7 +48,7 @@ document.addEventListener("DOMContentLoaded", function () {
</div> </div>
<button class="btn btn-secondary" onclick="addOption(this)">Add Option</button> <button class="btn btn-secondary" onclick="addOption(this)">Add Option</button>
<button class="btnn" onclick="deleteQuestion(this)"> <button class="btnn" 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> </button>
`; `;
questionsSection.appendChild(newQuestionDiv); questionsSection.appendChild(newQuestionDiv);
@ -133,7 +133,7 @@ document.addEventListener("DOMContentLoaded", function () {
window.changeQuestionType = changeQuestionType; window.changeQuestionType = changeQuestionType;
window.saveForm = saveForm; window.saveForm = saveForm;
window.previewForm = function () { window.previewForm = function (formId) {
const formTitle = document.getElementById("form-title").value; const formTitle = document.getElementById("form-title").value;
const formDescription = const formDescription =
document.getElementById("form-description").value; document.getElementById("form-description").value;
@ -160,7 +160,7 @@ document.addEventListener("DOMContentLoaded", function () {
data: JSON.stringify(formData), data: JSON.stringify(formData),
}); });
window.open(`preview.html?${formParams.toString()}`, "_blank"); window.location.href = '/forms/' + formId + '/preview';
}; };
window.addNewQuestion = addNewQuestion; window.addNewQuestion = addNewQuestion;

View File

@ -54,7 +54,7 @@
</ul> </ul>
</div> </div>
</div> </div>
<div class="question_form"> <div style="background-color: #f0ebf8" class="question_form">
<br /> <br />
<div class="section"> <div class="section">
<div class="question_title_section"> <div class="question_title_section">
@ -92,14 +92,14 @@
</div> </div>
<div class="sidebar"> <div class="sidebar">
<div id="moveableDiv"> <div id="moveableDiv">
<button class="btnp" onclick="addNewQuestion(); moveDown()"> <button style="background-color: white"class="btnp" onclick="addNewQuestion(); moveDown()">
<img src={{ asset('images/add.png') }} alt="" width="20px" height="20px" /> <img src={{ asset('images/add.png') }} alt="" width="20px" height="20px" />
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="btnsub"> <div style="background-color: #f0ebf8"class="btnsub">
<span> <span>
<button type="submit" name="save" value="save" onclick="saveForm()" <button type="submit" name="save" value="save" onclick="saveForm()"
class="btnsave btn btn-secondary"> class="btnsave btn btn-secondary">

View File

@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -9,20 +10,25 @@
.dropdown:hover .dropdown-menu { .dropdown:hover .dropdown-menu {
display: block; display: block;
} }
.shadow-custom { .shadow-custom {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
} }
</style> </style>
</head> </head>
<body class="bg-gray-50">
<body style="bg-gray-100">
<nav class="bg-white p-4 shadow-lg"> <nav class="bg-white p-4 shadow-lg">
<div class="container mx-auto flex justify-between items-center"> <div class="container 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> <a href="{{ url('/') }}" style="color: rgb(103,58,183)"
class="text-3xl font-bold font-sans">LaraForms</a>
<div class="relative dropdown"> <div class="relative dropdown">
<button id="profileMenuButton" class="flex items-center focus:outline-none"> <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"> <img src="{{ asset('images/user.png') }}" alt="Profile"
class="w-10 h-10 rounded-full border-2 border-white">
</button> </button>
<div id="profileMenu" class="dropdown-menu hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-2"> <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') }}"> <form method="POST" action="{{ route('logout') }}">
@csrf @csrf
<button type="submit" class="block px-4 py-2 text-gray-700 hover:bg-gray-200 w-full text-left"> <button type="submit" class="block px-4 py-2 text-gray-700 hover:bg-gray-200 w-full text-left">
@ -33,10 +39,21 @@
</div> </div>
</div> </div>
</nav> </nav>
@if (session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded relative mt-4" role="alert">
<span class="block sm:inline">{{ session('success') }}</span>
</div>
@endif
@if (session('delete'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded relative mt-4" role="alert">
<span class="block sm:inline">{{ session('delete') }}</span>
</div>
@endif
<div class="container mx-auto mt-10"> <div class="container mx-auto mt-10">
<div class="flex justify-between mb-6 items-center"> <div class="flex justify-between mb-6 items-center">
<a href="{{ route('forms.create') }}" class="inline-block px-6 py-3 text-white font-semibold rounded-md shadow bg-purple-700 hover:bg-purple-900 transition duration-200">Start a new form</a> <a href="{{ route('forms.create') }}"
class="inline-block px-6 py-3 text-white font-semibold rounded-md shadow bg-purple-700 hover:bg-purple-900 transition duration-200">Start
a new form</a>
</div> </div>
<h2 class="text-3xl font-semibold text-gray-800 font-sans">Recent Forms</h2> <h2 class="text-3xl font-semibold text-gray-800 font-sans">Recent Forms</h2>
@ -48,30 +65,41 @@
<table class="min-w-full bg-white rounded-md overflow-hidden"> <table class="min-w-full bg-white rounded-md overflow-hidden">
<thead class="bg-gray-100"> <thead class="bg-gray-100">
<tr> <tr>
<th class="py-4 px-6 border-b border-gray-200 text-left text-sm font-semibold text-gray-600">Form Title</th> <th
<th class="py-4 px-6 border-b border-gray-200 text-left text-sm font-semibold text-gray-600">Created At</th> class="py-4 px-6 border-b border-gray-200 text-left text-sm font-semibold text-gray-600">
<th class="py-4 px-6 border-b border-gray-200 text-left text-sm font-semibold text-gray-600">Responses</th> Form Title</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">
Created At</th>
<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>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach($forms as $form) @foreach ($forms as $form)
<tr class="hover:bg-gray-50 transition duration-150"> <tr class="hover:bg-gray-50 transition duration-150">
<td class="py-4 px-6 border-b border-gray-200"> <td class="py-4 px-6 border-b border-gray-200">
<a href="{{ route('forms.show', $form) }}" class="text-blue-600 font-semibold hover:underline">{{ $form->title }}</a> <a href="{{ route('forms.show', $form) }}"
class="text-blue-600 font-semibold hover:underline">{{ $form->title }}</a>
<p class="text-gray-600">{{ $form->description }}</p> <p class="text-gray-600">{{ $form->description }}</p>
</td> </td>
<td class="py-4 px-6 border-b border-gray-200">{{ $form->created_at->format('M d, Y') }}</td> <td class="py-4 px-6 border-b border-gray-200">{{ $form->created_at->format('M d, Y') }}
</td>
<td class="py-4 px-6 border-b border-gray-200"> <td class="py-4 px-6 border-b border-gray-200">
@if ($form->is_published) <a href="{{ route('responses.viewResponses', $form) }}"
<a href="{{ route('responses.viewResponses', $form) }}" class="text-blue-500 hover:underline">View Responses</a> class="text-blue-500 hover:underline">View Responses</a>
@else
<span class="text-gray-600">Not Published</span>
@endif
</td> </td>
<td class="py-8 px-6 border-b border-gray-200 flex items-center space-x-10"> <td class="py-8 px-6 border-b border-gray-200 flex items-center space-x-10">
<a href="{{ route('forms.edit', $form) }}" class="text-green-500 hover:underline">Edit</a> @if (!$form->is_published)
<form action="{{ route('forms.destroy', $form) }}" method="POST" class="inline-block"> <a href="{{ route('forms.edit', $form) }}"
class="text-green-500 hover:underline">Edit</a>
@endif
<form action="{{ route('forms.destroy', $form) }}" method="POST"
class="inline-block">
@csrf @csrf
@method('DELETE') @method('DELETE')
<button type="submit" class="text-red-500 hover:underline">Delete</button> <button type="submit" class="text-red-500 hover:underline">Delete</button>
@ -89,13 +117,13 @@
document.getElementById('profileMenuButton').addEventListener('click', function() { document.getElementById('profileMenuButton').addEventListener('click', function() {
document.getElementById('profileMenu').classList.toggle('hidden'); document.getElementById('profileMenu').classList.toggle('hidden');
}); });
setTimeout(function() {
var successMessage = document.getElementById('successMessage');
if (successMessage) {
successMessage.remove();
}
}, 3000);
</script> </script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Preview</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{asset("css/preview.css")}}">
</head>
<body>
<div class="form_preview">
<h1 id="form_name"></h1>
<p id="form_desc"></p>
<div id="questions_container"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const formName = document.getElementById('form_name');
const formDesc = document.getElementById('form_desc');
const questionsContainer = document.getElementById('questions_container');
function getParameterByName(name, url = window.location.href) {
name = name.replace(/[\[\]]/g, '\\$&');
const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
const formTitle = getParameterByName('title');
const formDescription = getParameterByName('description');
const formData = JSON.parse(getParameterByName('data'));
formName.textContent = formTitle;
formDesc.textContent = formDescription;
formData.forEach((question, index) => {
const questionType = question.type;
const questionText = question.text;
const options = question.options;
let questionHtml = `<div class="question"><h3 class="question_title">${questionText}</h3><div class="options">`;
options.forEach(option => {
if (questionType === 'multiple_choice') {
questionHtml += `<div class="option"><input type="radio" name="q${index}">${option}</div>`;
} else if (questionType === 'checkbox') {
questionHtml += `<div class="option"><input type="checkbox" name="q${index}">${option}</div>`;
} else if (questionType === 'dropdown') {
if (option === options[0]) questionHtml += `<select class="form-control">`;
questionHtml += `<option>${option}</option>`;
if (option === options[options.length - 1]) questionHtml += `</select>`;
}
});
questionHtml += `</div></div>`;
questionsContainer.innerHTML += questionHtml;
});
});
</script>
</body>
</html>

View File

@ -17,9 +17,9 @@
<!-- Scripts --> <!-- Scripts -->
@vite(['resources/sass/app.scss', 'resources/js/app.js']) @vite(['resources/sass/app.scss', 'resources/js/app.js'])
</head> </head>
<body> <body style="background-color: #f0ebf8">
<div id="app"> <div id="app">
<nav class="navbar navbar-expand-md navbar-light shadow-sm" style="background-color: rgb(103,58,183)"> <nav class="navbar navbar-expand-md navbar-light shadow-md px-4 py-3 flex justify-between items-center" style="background-color: rgb(103,58,183)">
<div class="container"> <div class="container">
<a class="navbar-brand text-white" href="{{ url('/') }}"> <a class="navbar-brand text-white" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }} {{ config('app.name', 'Laravel') }}

View File

@ -1,51 +1,5 @@
{{-- @extends('layouts.app') {{-- @extends('layouts.app')
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
@section('content')
<div class="container">
<h1>{{ $form->title }}</h1>
<p>{{ $form->description }}</p>
<form action="{{ route('responses.submitForm', $form) }}" method="POST">
@csrf
@foreach ($questions as $question)
<div class="form-group">
<label>{{ $question->question_text }}</label>
@if ($question->type == 'multiple_choice')
@foreach (json_decode($question->options) as $option)
<div class="form-check">
<input class="form-check-input" type="radio" name="answers[{{ $question->id }}]"
value="{{ $option }}">
<label class="form-check-label">{{ $option }}</label>
</div>
@endforeach
@elseif($question->type == 'checkbox')
@foreach (json_decode($question->options) as $option)
<div class="form-check">
<input class="form-check-input" type="checkbox" name="answers[{{ $question->id }}][]"
value="{{ $option }}">
<label class="form-check-label">{{ $option }}</label>
</div>
@endforeach
@elseif($question->type == 'dropdown')
<select class="form-control" name="answers[{{ $question->id }}]">
@foreach (json_decode($question->options) as $option)
<option value="{{ $option }}">{{ $option }}</option>
@endforeach
</select>
@endif
</div>
@endforeach
<br>
<br>
<br>
<button type="submit" class="focus:outline-none text-white bg-purple-700 hover:bg-purple-800 focus:ring-4 focus:ring-purple-300 font-medium rounded-lg text-sm px-5 py-2.5 mb-2 dark:bg-purple-600 dark:hover:bg-purple-700 dark:focus:ring-purple-900"">Submit</button>
</form>
</div>
@endsection --}}
@extends('layouts.app')
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
@section('content') @section('content')
<div class="container mx-auto py-8"> <div class="container mx-auto py-8">
@ -91,4 +45,53 @@
</form> </form>
</div> </div>
@endsection @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">
<h1 class="text-3xl font-semibold text-gray-900">{{ $form->title }}</h1>
<p class="text-gray-600 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-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 == '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-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>
@endsection

View File

@ -4,45 +4,61 @@
<head> <head>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap" rel="stylesheet">
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900&display=swap"
rel="stylesheet">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Response Detail</title> <title>Response Detail</title>
<link rel="stylesheet" href="{{ asset('css/index.css') }}"> <script src="https://cdn.tailwindcss.com"></script>
</head> </head>
<body> <body class="bg-gray-50 font-roboto">
<div class="form_header roboto-light"> <div class="bg-white shadow-md p-4 flex justify-between items-center">
<div class="form_header_left"> <div class="flex items-center">
<a href="/forms"><img src="{{ asset('images/google-form.png') }}" class="form_header_icon" height="45px" <a href="/forms">
width="40px" /></a> <img src="{{ asset('images/google-form.png') }}" class="h-12 w-12 mr-4" alt="Google Form Icon" />
<h1 class="form_name">{{ $form->title }} - Response Detail</h1> </a>
<h1 class="text-xl font-semibold">{{ $form->title }} - Response Detail</h1>
</div> </div>
</div> </div>
<div class="container">
<div class="response_detail"> <div class="container mx-auto mt-8">
<h2>Response from {{ $responses->first()->user->name ?? 'Anonymous' }} - {{ $responses->first()->submitted_at }}</h2> <div class="bg-white p-6 shadow-md rounded-lg">
<h2 class="text-2xl font-bold mb-4">Response from {{ $responses->first()->user->name ?? 'Anonymous' }} - {{ $responses->first()->submitted_at }}</h2>
@foreach ($responses as $response) @foreach ($responses as $response)
@php @php
$question = $questions[$response->question_id]; $question = $questions[$response->question_id] ?? null;
$decodedAnswers = json_decode($response->answers, true); $decodedAnswers = json_decode($response->answers, true);
@endphp @endphp
<div class="question">
<h3>{{ $question->question_text }}</h3> @if ($question)
@if ($question->type == 'multiple_choice' || $question->type == 'checkbox' || $question->type == 'dropdown') <div class="mb-6">
<h3 class="text-lg font-medium">{{ $question->question_text }}</h3>
@if ($question->type == 'dropdown')
<select disabled class="w-full p-2 mt-2 border rounded">
@foreach (json_decode($question->options) as $option) @foreach (json_decode($question->options) as $option)
<p> <option {{ ($option == $decodedAnswers) ? 'selected' : '' }}>
<input type="radio" disabled {{ in_array($option, (array)$decodedAnswers) ? 'checked' : '' }}>
{{ $option }} {{ $option }}
</p> </option>
@endforeach @endforeach
</select>
@elseif (in_array($question->type, ['multiple_choice', 'checkbox']))
<div class="mt-2">
@foreach (json_decode($question->options) as $option)
<label class="block">
<input type="{{ $question->type == 'checkbox' ? 'checkbox' : 'radio' }}" disabled {{ in_array($option, (array)$decodedAnswers) ? 'checked' : '' }} class="mr-2">
{{ $option }}
</label>
@endforeach
</div>
@else @else
<p>{{ is_array($decodedAnswers) ? implode(', ', $decodedAnswers) : $decodedAnswers }}</p> <p class="mt-2 p-2 bg-gray-100 rounded">{{ is_array($decodedAnswers) ? implode(', ', $decodedAnswers) : $decodedAnswers }}</p>
@endif @endif
</div> </div>
@else
<p class="text-red-500">Question not found for ID: {{ $response->question_id }}</p>
@endif
@endforeach @endforeach
</div> </div>
</div> </div>
@ -59,28 +75,29 @@
<head> <head>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap" rel="stylesheet">
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900&display=swap"
rel="stylesheet">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Response Detail</title> <title>Response Detail</title>
<link rel="stylesheet" href="{{ asset('css/index.css') }}"> <script src="https://cdn.tailwindcss.com"></script>
</head> </head>
<body> <body style="background-color: #f0ebf8" class="font-roboto">
<div class="form_header roboto-light"> <header class="bg-white shadow-md py-4">
<div class="form_header_left"> <div class="container mx-auto flex justify-between items-center">
<a href="/forms"><img src="{{ asset('images/google-form.png') }}" class="form_header_icon" height="45px" <div class="flex items-center">
width="40px" /></a> <a href="/forms">
<h1 class="form_name">{{ $form->title }} - Response Detail</h1> <img src="{{ asset('images/google-form.png') }}" class="h-12 w-12 mr-4" alt="Google Form Icon" />
</a>
<h1 style="color: rgb(103,58,183)" class="text-xl font-semibold">{{ $form->title }} - Response Detail</h1>
</div> </div>
</div> </div>
<div class="container"> </header>
<div class="response_detail">
<h2>Response from {{ $responses->first()->user->name ?? 'Anonymous' }} - {{ $responses->first()->submitted_at }}</h2>
<main class="container mx-auto mt-8">
<div class="bg-white p-8 shadow-lg rounded-lg">
<h2 class="text-2xl font-bold mb-6">Response from {{ $responses->first()->user->name ?? 'Anonymous' }} - {{ $responses->first()->submitted_at }}</h2>
@foreach ($responses as $response) @foreach ($responses as $response)
@php @php
@ -89,10 +106,10 @@
@endphp @endphp
@if ($question) @if ($question)
<div class="question"> <div class="mb-8">
<h3>{{ $question->question_text }}</h3> <h3 class="text-lg font-medium mb-2">{{ $question->question_text }}</h3>
@if ($question->type == 'dropdown') @if ($question->type == 'dropdown')
<select disabled> <select disabled class="w-full p-3 border border-gray-600 rounded-lg">
@foreach (json_decode($question->options) as $option) @foreach (json_decode($question->options) as $option)
<option {{ ($option == $decodedAnswers) ? 'selected' : '' }}> <option {{ ($option == $decodedAnswers) ? 'selected' : '' }}>
{{ $option }} {{ $option }}
@ -100,25 +117,26 @@
@endforeach @endforeach
</select> </select>
@elseif (in_array($question->type, ['multiple_choice', 'checkbox'])) @elseif (in_array($question->type, ['multiple_choice', 'checkbox']))
<div class="space-y-2">
@foreach (json_decode($question->options) as $option) @foreach (json_decode($question->options) as $option)
<p> <label class="block">
<input type="{{ $question->type == 'checkbox' ? 'checkbox' : 'radio' }}" disabled {{ in_array($option, (array)$decodedAnswers) ? 'checked' : '' }}> <input type="{{ $question->type == 'checkbox' ? 'checkbox' : 'radio' }}" disabled {{ in_array($option, (array)$decodedAnswers) ? 'checked' : '' }} class="mr-2">
{{ $option }} {{ $option }}
</p> </label>
@endforeach @endforeach
</div>
@else @else
<p>{{ is_array($decodedAnswers) ? implode(', ', $decodedAnswers) : $decodedAnswers }}</p> <p class="mt-2 p-3 bg-gray-100 rounded-lg">{{ is_array($decodedAnswers) ? implode(', ', $decodedAnswers) : $decodedAnswers }}</p>
@endif @endif
</div> </div>
@else @else
<p>Question not found for ID: {{ $response->question_id }}</p> <p class="text-red-500">Question not found for ID: {{ $response->question_id }}</p>
@endif @endif
@endforeach @endforeach
</div> </div>
</div> </main>
<script src="{{ asset('js/script.js') }}"></script> <script src="{{ asset('js/script.js') }}"></script>
</body> </body>
</html> </html>

View File

@ -1,57 +1,108 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<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=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900&display=swap" rel="stylesheet">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Form Responses</title> <title>Form Responses</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ asset('css/index.css') }}"> <link rel="stylesheet" href="{{ asset('css/index.css') }}">
</head> </head>
<body>
<div class="form_header roboto-light">
<div class="form_header_left">
<a href="/forms"><img src="{{ asset('images/google-form.png') }}" class="form_header_icon" height="45px" width="40px" /></a>
<h1 class="form_name">{{ $form->title }} - Responses</h1>
</div>
<div class="form_header_right">
<img src="{{ asset('images/menu.png') }}" alt="menu" height="30px" width="30px" />
<img src="{{ asset('images/user.png') }}" alt="" height="30px" width="30px" />
</div>
</div>
<div class="container">
<h2>Responses</h2>
<div class="share-link"> <body style="background-color: #f0ebf8" class="font-roboto text-gray-800">
<input type="text" value="{{ route('responses.showForm', $form) }}" id="shareLink" readonly>
<button onclick="copyLink()">Copy Link</button> <!-- Header -->
<div class="bg-white shadow-md px-6 py-4 flex justify-between items-center">
<div class="flex items-center">
<a href="/forms" class="mr-4">
<img src="{{ asset('images/google-form.png') }}" alt="Google Forms" class="h-12 w-auto">
</a>
<h1 class="text-2xl font-semibold text-purple-900">{{ $form->title }} - Responses</h1>
</div>
<div class="flex items-center">
<img src="{{ asset('images/menu.png') }}" alt="Menu" class="h-8 w-8 mr-4">
<img src="{{ asset('images/user.png') }}" alt="User" class="h-8 w-8">
</div>
</div> </div>
<!-- Main Content -->
<div class="mx-auto max-w-7xl px-6 py-8">
<!-- Share Link -->
<div class="flex items-center mb-6">
<h2 class="text-xl font-semibold mr-4">Responses</h2>
<div class="flex items-center">
<input type="text" value="{{ route('responses.showForm', $form) }}" id="shareLink"
class="bg-white border border-gray-300 px-3 py-1 rounded-l sm:w-auto focus:outline-none"
readonly>
&nbsp;
<button onclick="copyLink()"
class="bg-purple-600 text-white px-4 py-1.5 rounded-r hover:bg-purple-700 focus:outline-none ml-2 sm:ml-0 mt-2 sm:mt-0">
Copy Link
</button>
<!-- Copy Link Notification -->
<div id="copyNotification"
class="hidden bg-green-100 border border-green-500 text-green-700 px-3 py-2 rounded ml-2">
Link copied!
</div>
</div>
</div>
<!-- Responses Table -->
@if ($responses->isEmpty())
<p class="text-gray-600">No responses available.</p>
@else
<div class="overflow-x-auto">
<table class="min-w-full bg-white shadow-md rounded-lg overflow-hidden">
<thead class="bg-gray-200 text-gray-600 text-sm leading-normal">
<tr>
<th class="py-3 px-6 text-left">User</th>
<th class="py-3 px-6 text-left">Submitted At</th>
<th class="py-3 px-6 text-left">Actions</th>
</tr>
</thead>
<tbody class="text-gray-600 text-sm font-light">
@foreach ($responses as $responseGroup)
<tr class="border-b border-gray-200 hover:bg-gray-100">
<td class="py-3 px-6 text-left">
{{ $responseGroup->first()->user->name ?? 'Anonymous' }}
</td>
<td class="py-3 px-6 text-left">
{{ $responseGroup->first()->created_at->diffForHumans()}}
</td>
<td class="py-3 px-6 text-left">
<a href="{{ route('responses.viewResponse', ['form' => $form, 'responseId' => $responseGroup->first()->response_id]) }}"
target="_blank"
class="text-blue-600 hover:text-blue-700 focus:outline-none">View Response</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
<!-- Script for Copy Link Functionality -->
<script> <script>
function copyLink() { function copyLink() {
var copyText = document.getElementById("shareLink"); var copyText = document.getElementById("shareLink");
copyText.select(); copyText.select();
copyText.setSelectionRange(0, 99999); /* For mobile devices */ copyText.setSelectionRange(0, 99999); /* For mobile devices */
document.execCommand("copy"); document.execCommand("copy");
alert("Link copied: " + copyText.value);
// Show copy notification next to the copy button
var copyNotification = document.getElementById("copyNotification");
copyNotification.classList.remove("hidden");
setTimeout(function () {
copyNotification.classList.add("hidden");
}, 2000);
} }
</script> </script>
@if ($responses->isEmpty()) <!-- Custom Scripts -->
<p>No responses available.</p>
@else
<ul>
@foreach ($responses as $responseGroup)
<li>
User: {{ $responseGroup->first()->user->name ?? 'Anonymous' }} - Submitted at: {{ $responseGroup->first()->submitted_at }}
<a href="{{ route('responses.viewResponse', ['form' => $form, 'responseId' => $responseGroup->first()->response_id]) }}" target="_blank">View Response</a>
</li>
@endforeach
</ul>
@endif
</div>
<script src="{{ asset('js/script.js') }}"></script> <script src="{{ asset('js/script.js') }}"></script>
</body> </body>
</html> </html>

View File

@ -21,6 +21,8 @@ Route::middleware(['auth'])->group(function () {
Route::get('/forms/{form}/edit', [FormController::class, 'edit'])->name('forms.edit'); Route::get('/forms/{form}/edit', [FormController::class, 'edit'])->name('forms.edit');
Route::put('/forms/{form}', [FormController::class, 'update'])->name('forms.update'); Route::put('/forms/{form}', [FormController::class, 'update'])->name('forms.update');
Route::delete('/forms/{form}', [FormController::class, 'destroy'])->name('forms.destroy'); Route::delete('/forms/{form}', [FormController::class, 'destroy'])->name('forms.destroy');
Route::get('/forms/{form}/preview', [FormController::class, 'preview'])->name('forms.preview');
}); });
// Response routes // Response routes