Compare commits

...

10 Commits

Author SHA1 Message Date
yash d391440e2a first-commit 2024-07-24 15:14:06 +05:30
yash 9f113236c5 Minor UI enhancements 2024-07-24 12:03:52 +05:30
yash 128fac86ac UI Changes 2024-07-24 11:43:50 +05:30
yash 0d8c522732 Code and bug fixes 2024-07-24 11:11:55 +05:30
Yash e747614bd3 UI changes 2024-07-24 01:29:23 +05:30
yash 2f7529ce2e Fixed: Duplicating question while creating form, UI changes 2024-07-19 16:38:05 +05:30
Yash eedc2668da Minor UI changes 2024-07-19 02:45:28 +05:30
Yash 22bf15864e Added Required button, Text box question type, Added sweetalerts 2024-07-19 02:42:07 +05:30
yash 7ad8d86e7f UI Changes 2024-07-18 18:31:31 +05:30
yash 00f5ec6cf9 Added sliding add button in edit page, Added statistics in responses page 2024-07-18 17:13:20 +05:30
133 changed files with 823 additions and 1033 deletions

0
.editorconfig Normal file → Executable file
View File

0
.env.example Normal file → Executable file
View File

0
.gitattributes vendored Normal file → Executable file
View File

0
.gitignore vendored Normal file → Executable file
View File

0
README.md Normal file → Executable file
View File

0
app/Console/Kernel.php Normal file → Executable file
View File

0
app/Exceptions/Handler.php Normal file → Executable file
View File

View File

0
app/Http/Controllers/Auth/ForgotPasswordController.php Normal file → Executable file
View File

0
app/Http/Controllers/Auth/LoginController.php Normal file → Executable file
View File

0
app/Http/Controllers/Auth/RegisterController.php Normal file → Executable file
View File

0
app/Http/Controllers/Auth/ResetPasswordController.php Normal file → Executable file
View File

0
app/Http/Controllers/Auth/VerificationController.php Normal file → Executable file
View File

0
app/Http/Controllers/Controller.php Normal file → Executable file
View File

125
app/Http/Controllers/FormController.php Normal file → Executable file
View File

@ -1,6 +1,7 @@
<?php <?php
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\Form; use App\Models\Form;
use App\Models\User; use App\Models\User;
@ -9,13 +10,23 @@ 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; use Illuminate\Support\Facades\Session;
use Illuminate\Validation\Rules\Unique;
class FormController extends Controller class FormController extends Controller
{ {
public function index() public function index()
{ {
// Get forms belonging to the authenticated user $totalForms = Form::count();
$forms = Form::where('user_id', Auth::id())->get(); $publishedForms = Form::where('is_published', true)->count();
return view('forms.index', compact('forms')); $totalResponses = Response::count();
$forms = Form::where('user_id', Auth::id())->orderBy('created_at', 'desc')->get();
return view('forms.index', [
'forms' => $forms,
'totalForms' => $totalForms,
'publishedForms' => $publishedForms,
'totalResponses' => $totalResponses,
]);
} }
public function create() public function create()
@ -23,35 +34,92 @@ class FormController extends Controller
return view('forms.create'); return view('forms.create');
} }
public function edit(Form $form) public function togglePublish(Form $form)
{ {
// Questions are already fetched with their options cast to array due to the casts property $form->is_published = !$form->is_published;
$questions = $form->questions; $form->save();
foreach ($questions as $question) {
$question->options = json_decode($question->options, true); return redirect()->route('forms.show', $form->id)->with('success', 'Form publish status updated.');
} }
public function edit(Form $form)
{
$questions = $form->questions;
foreach ($questions as $question) {
$question->options = json_decode($question->options, true);
}
return view('forms.edit', compact('form', 'questions'));
}
public function createWithTemplate($template)
{
$data = [];
switch ($template) {
case 'contact':
$data = [
'title' => 'Contact Information',
'description' => 'Template for collecting contact information.',
'questions' => [
['type' => 'text', 'question_text' => 'Name'],
['type' => 'text', 'question_text' => 'Email'],
// Add more questions as needed
],
];
break;
case 'rsvp':
$data = [
'title' => 'RSVP',
'description' => 'Event Address: 123 Your Street Your City, ST 12345
Contact us at (123) 456-7890 or no_reply@example.com
',
'questions' => [
['type' => 'text', 'question_text' => 'Can you attend?'],
['type' => 'text', 'question_text' => 'Number of Guests'],
],
];
break;
case 'party':
$data = [
'title' => 'Party Invite',
'description' => 'Template for party invitations.',
'questions' => [
['type' => 'text', 'question_text' => 'Name'],
['type' => 'text', 'question_text' => 'RSVP Status'],
],
];
break;
}
return view('forms.create', ['data' => $data]);
}
// Pass the questions to the view
return view('forms.edit', compact('form', 'questions'));
}
public function store(Request $request) public function store(Request $request)
{ {
try {
$validatedData = $request->validate([ $validatedData = $request->validate([
'title' => 'required|string|max:255', 'title' => 'required|string|max:255',
'description' => 'nullable|string', 'description' => 'nullable|string',
'questions' => 'required|array', 'questions' => 'required|array',
'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', // This should match the key used in the JavaScript 'questions.*.text' => 'required|string',
'questions.*.options' => 'nullable|array', 'questions.*.options' => 'nullable|array',
'questions.*.required' => 'nullable|boolean',
]); ]);
$form = new Form(); $form = new Form();
$form->title = $validatedData['title']; $form->title = $validatedData['title'];
$form->description = $validatedData['description']; $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->user_id = Auth::id();
$form->save(); $form->save();
@ -59,28 +127,32 @@ return view('forms.edit', compact('form', 'questions'));
$question = new Question(); $question = new Question();
$question->form_id = $form->id; $question->form_id = $form->id;
$question->type = $questionData['type']; $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->options = isset($questionData['options']) ? json_encode($questionData['options']) : null;
$question->required = isset($questionData['required']) ? $questionData['required'] : false;
$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]);
} 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) public function show(Form $form)
{ {
$form->load('questions.responses'); $form->load('questions.responses');
return view('forms.show', compact('form')); return view('forms.show', compact('form'));
} }
public function preview($id) public function preview($id)
{ {
$form = Form::findOrFail($id); $form = Form::findOrFail($id);
return view('forms.previewForm', compact('form')); return view('forms.previewForm', compact('form'));
} }
public function update(Request $request, Form $form) public function update(Request $request, Form $form)
@ -98,7 +170,7 @@ return view('forms.edit', compact('form', 'questions'));
'description' => 'nullable|string|max:255', 'description' => 'nullable|string|max:255',
'questions' => 'required|array', 'questions' => 'required|array',
'questions.*.id' => 'nullable|exists:questions,id', '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.*.text' => 'required|string|max:255',
'questions.*.options' => 'nullable|array', 'questions.*.options' => 'nullable|array',
'questions.*.options.*' => 'nullable|string|max:255', 'questions.*.options.*' => 'nullable|string|max:255',
@ -130,7 +202,6 @@ return view('forms.edit', compact('form', 'questions'));
$existingQuestionIds[] = $question->id; $existingQuestionIds[] = $question->id;
} }
// Delete questions that were removed
$form->questions()->whereNotIn('id', $existingQuestionIds)->delete(); $form->questions()->whereNotIn('id', $existingQuestionIds)->delete();
Log::info('Remaining questions: ', $form->questions()->get()->toArray()); Log::info('Remaining questions: ', $form->questions()->get()->toArray());
@ -148,6 +219,8 @@ return view('forms.edit', compact('form', 'questions'));
public function destroy(Form $form) public function destroy(Form $form)
{ {
// 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

12
app/Http/Controllers/HomeController.php Normal file → Executable file
View File

@ -6,21 +6,13 @@ use Illuminate\Http\Request;
class HomeController extends Controller class HomeController extends Controller
{ {
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct() public function __construct()
{ {
$this->middleware('auth'); $this->middleware('auth');
} }
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index() public function index()
{ {
return view('forms.index'); return view('forms.index');

12
app/Http/Controllers/QuestionController.php Normal file → Executable file
View File

@ -17,14 +17,14 @@ class QuestionController extends Controller
public function store(Form $form, Request $request) public function store(Form $form, Request $request)
{ {
// Validate question data
$validatedData = $request->validate([ $validatedData = $request->validate([
'type' => 'required|string|in:multiple_choice,checkbox,dropdown,short_answer,long_answer', 'type' => 'required|string|in:multiple_choice,checkbox,dropdown,short_answer,long_answer',
'question_text' => 'required|string', 'question_text' => 'required|string',
'options' => 'nullable|array', // If needed based on question type 'options' => 'nullable|array',
]); ]);
// Create new question for the form
$question = new Question(); $question = new Question();
$question->form_id = $form->id; $question->form_id = $form->id;
$question->type = $validatedData['type']; $question->type = $validatedData['type'];
@ -42,14 +42,14 @@ class QuestionController extends Controller
public function update(Form $form, Question $question, Request $request) public function update(Form $form, Question $question, Request $request)
{ {
// Validate updated question data
$validatedData = $request->validate([ $validatedData = $request->validate([
'type' => 'required|string|in:multiple_choice,checkbox,dropdown,short_answer,long_answer', 'type' => 'required|string|in:multiple_choice,checkbox,dropdown,short_answer,long_answer',
'question_text' => 'required|string', 'question_text' => 'required|string',
'options' => 'nullable|array', // If needed based on question type 'options' => 'nullable|array',
]); ]);
// Update question details
$question->type = $validatedData['type']; $question->type = $validatedData['type'];
$question->question_text = $validatedData['question_text']; $question->question_text = $validatedData['question_text'];
$question->options = $validatedData['options'] ?? null; $question->options = $validatedData['options'] ?? null;

103
app/Http/Controllers/ResponseController.php Normal file → Executable file
View File

@ -1,6 +1,7 @@
<?php <?php
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
use App\Models\Form; use App\Models\Form;
@ -21,30 +22,79 @@ class ResponseController extends Controller
return view('responses.index', compact('form', 'responses')); return view('responses.index', compact('form', 'responses'));
} }
// Display a specific response public function showSuccess(Form $form)
{
return view('responses.success', compact('form'));
}
public function viewResponse(Form $form, $responseId) public function viewResponse(Form $form, $responseId)
{ {
// Get all responses with the same response_id
$responses = Response::where('response_id', $responseId) $responses = Response::where('response_id', $responseId)
->where('form_id', $form->id) ->where('form_id', $form->id)
->get(); ->get();
// Get all questions for the form
$questions = Question::where('form_id', $form->id)->get()->keyBy('id'); $questions = Question::where('form_id', $form->id)->get()->keyBy('id');
return view('responses.viewResponse', compact('form', 'responses', 'questions'));
}
public function viewResponses(Form $form) $statistics = [];
{ foreach ($questions as $question) {
// Get all responses for the form, grouped by response_id $statistics[$question->id] = [
'question_text' => $question->question_text,
'type' => $question->type,
'options' => json_decode($question->options),
'responses' => []
];
foreach ($responses as $response) {
$decodedAnswers = json_decode($response->answers, true);
if (isset($decodedAnswers[$question->id])) {
$statistics[$question->id]['responses'][] = $decodedAnswers[$question->id];
}
}
}
return view('responses.viewResponse', compact('form', 'responses', 'questions', 'statistics'));
}
public function viewResponses(Form $form)
{
$responses = Response::where('form_id', $form->id) $responses = Response::where('form_id', $form->id)
->orderBy('submitted_at', 'desc') ->orderBy('submitted_at', 'desc')
->get() ->get()
->groupBy('response_id'); ->groupBy('response_id');
return view('responses.viewResponses', compact('form', 'responses'));
} $questions = Question::where('form_id', $form->id)->get()->keyBy('id');
$statistics = [];
foreach ($questions as $question) {
$statistics[$question->id] = [
'question_text' => $question->question_text,
'type' => $question->type,
'options' => json_decode($question->options, true),
'responses' => [],
];
foreach ($responses as $responseGroup) {
foreach ($responseGroup as $response) {
$decodedAnswers = json_decode($response->answers, true);
if (isset($decodedAnswers[$question->id])) {
$statistics[$question->id]['responses'][] = $decodedAnswers[$question->id];
}
}
}
}
return view('responses.viewResponses', compact('form', 'responses', 'statistics', 'questions'));
}
public function showForm(Form $form) public function showForm(Form $form)
{ {
@ -57,24 +107,39 @@ public function viewResponses(Form $form)
} }
public function submitForm(Request $request, Form $form) public function submitForm(Request $request, Form $form)
{ {
Log::info($request->all()); // Log the entire request data for debugging Log::info($request->all());
$questions = $form->questions;
$requiredQuestionIds = $questions->where('required', true)->pluck('id')->toArray();
// Validate and process form submission
$validatedData = $request->validate([ $validatedData = $request->validate([
'answers' => 'required|array', 'answers' => 'required|array',
'answers.*' => 'required', 'answers.*' => 'required',
]); ]);
Log::info($validatedData); // Log the validated data for debugging
// Generate a UUID for response_id foreach ($requiredQuestionIds as $requiredQuestionId) {
if (!isset($validatedData['answers'][$requiredQuestionId]) || empty($validatedData['answers'][$requiredQuestionId])) {
return redirect()->back()
->withErrors(['errors' => 'Please answer all required questions.'])
->withInput();
}
}
Log::info($validatedData);
$responseId = Uuid::uuid4()->toString(); $responseId = Uuid::uuid4()->toString();
// Save each question's response
foreach ($validatedData['answers'] as $questionId => $answer) { foreach ($validatedData['answers'] as $questionId => $answer) {
$response = new Response(); $response = new Response();
$response->response_id = $responseId; // Assign the generated UUID $response->response_id = $responseId;
$response->question_id = $questionId; $response->question_id = $questionId;
$response->form_id = $form->id; $response->form_id = $form->id;
$response->user_id = auth()->id(); $response->user_id = auth()->id();
@ -85,5 +150,5 @@ public function viewResponses(Form $form)
return redirect()->route('responses.showForm', $form) return redirect()->route('responses.showForm', $form)
->with('success', 'Response submitted successfully.'); ->with('success', 'Response submitted successfully.');
} }
} }

0
app/Http/Kernel.php Normal file → Executable file
View File

0
app/Http/Middleware/Authenticate.php Normal file → Executable file
View File

0
app/Http/Middleware/EncryptCookies.php Normal file → Executable file
View File

View File

0
app/Http/Middleware/RedirectIfAuthenticated.php Normal file → Executable file
View File

0
app/Http/Middleware/TrimStrings.php Normal file → Executable file
View File

0
app/Http/Middleware/TrustHosts.php Normal file → Executable file
View File

0
app/Http/Middleware/TrustProxies.php Normal file → Executable file
View File

0
app/Http/Middleware/ValidateSignature.php Normal file → Executable file
View File

0
app/Http/Middleware/VerifyCsrfToken.php Normal file → Executable file
View File

0
app/Models/Form.php Normal file → Executable file
View File

0
app/Models/Question.php Normal file → Executable file
View File

0
app/Models/Response.php Normal file → Executable file
View File

0
app/Models/User.php Normal file → Executable file
View File

0
app/Providers/AppServiceProvider.php Normal file → Executable file
View File

0
app/Providers/AuthServiceProvider.php Normal file → Executable file
View File

0
app/Providers/BroadcastServiceProvider.php Normal file → Executable file
View File

0
app/Providers/EventServiceProvider.php Normal file → Executable file
View File

0
app/Providers/RouteServiceProvider.php Normal file → Executable file
View File

0
bin.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

0
bootstrap/app.php Normal file → Executable file
View File

0
bootstrap/cache/.gitignore vendored Normal file → Executable file
View File

0
composer.json Normal file → Executable file
View File

0
composer.lock generated Normal file → Executable file
View File

0
config/app.php Normal file → Executable file
View File

0
config/auth.php Normal file → Executable file
View File

0
config/broadcasting.php Normal file → Executable file
View File

0
config/cache.php Normal file → Executable file
View File

0
config/cors.php Normal file → Executable file
View File

0
config/database.php Normal file → Executable file
View File

0
config/filesystems.php Normal file → Executable file
View File

0
config/hashing.php Normal file → Executable file
View File

0
config/logging.php Normal file → Executable file
View File

0
config/mail.php Normal file → Executable file
View File

0
config/queue.php Normal file → Executable file
View File

0
config/sanctum.php Normal file → Executable file
View File

0
config/services.php Normal file → Executable file
View File

0
config/session.php Normal file → Executable file
View File

0
config/view.php Normal file → Executable file
View File

0
database/.gitignore vendored Normal file → Executable file
View File

0
database/factories/UserFactory.php Normal file → Executable file
View File

View File

View File

View File

View File

View File

@ -14,8 +14,9 @@ return new class extends Migration
Schema::create('questions', function (Blueprint $table) { Schema::create('questions', function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('form_id')->constrained()->onDelete('cascade'); $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->text('question_text');
$table->boolean('required')->default(false);
$table->json('options')->nullable(); // Store options as JSON if applicable $table->json('options')->nullable(); // Store options as JSON if applicable
$table->timestamps(); $table->timestamps();
}); });

View File

0
database/seeders/DatabaseSeeder.php Normal file → Executable file
View File

16
package-lock.json generated Normal file → Executable file
View File

@ -5,7 +5,8 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"sweetalert": "^2.1.2" "sweetalert": "^2.1.2",
"toastr": "^2.1.4"
}, },
"devDependencies": { "devDependencies": {
"@popperjs/core": "^2.11.6", "@popperjs/core": "^2.11.6",
@ -889,6 +890,11 @@
"node": ">=0.12.0" "node": ">=0.12.0"
} }
}, },
"node_modules/jquery": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
},
"node_modules/laravel-vite-plugin": { "node_modules/laravel-vite-plugin": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.5.tgz", "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.5.tgz",
@ -1107,6 +1113,14 @@
"node": ">=8.0" "node": ">=8.0"
} }
}, },
"node_modules/toastr": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz",
"integrity": "sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA==",
"dependencies": {
"jquery": ">=1.12.0"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "5.3.3", "version": "5.3.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz",

3
package.json Normal file → Executable file
View File

@ -14,6 +14,7 @@
"vite": "^5.0.0" "vite": "^5.0.0"
}, },
"dependencies": { "dependencies": {
"sweetalert": "^2.1.2" "sweetalert": "^2.1.2",
"toastr": "^2.1.4"
} }
} }

0
phpunit.xml Normal file → Executable file
View File

0
public/.htaccess Normal file → Executable file
View File

4
public/css/app.css Normal file → Executable file
View File

@ -6,6 +6,10 @@
background-color: #fff; background-color: #fff;
} }
input:focus, textarea:focus, select:focus{
outline: none;
}
#moveableDiv { #moveableDiv {
transition: transform 1s ease; transition: transform 1s ease;
} }

0
public/css/index.css Normal file → Executable file
View File

0
public/css/preview.css Normal file → Executable file
View File

0
public/favicon.ico Normal file → Executable file
View File

BIN
public/images/add.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 959 B

BIN
public/images/bin.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

0
public/images/folder.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

0
public/images/forward.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

0
public/images/google-form.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

0
public/images/menu.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

0
public/images/palette-svgrepo-com.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

0
public/images/star.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 623 B

After

Width:  |  Height:  |  Size: 623 B

0
public/images/undo.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

0
public/images/user.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

0
public/images/view.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

0
public/index.php Normal file → Executable file
View File

0
public/js/form.js Normal file → Executable file
View File

368
public/js/script.js Normal file → Executable file
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 () { document.addEventListener("DOMContentLoaded", function () {
const questionsSection = document.getElementById("questions_section"); const questionsSection = document.getElementById("questions_section");
@ -233,40 +20,62 @@ document.addEventListener("DOMContentLoaded", function () {
updateAddButtonPosition(); updateAddButtonPosition();
} }
function changeQuestionType(select) { function changeQuestionType(selectElement) {
const questionInput = select.nextElementSibling; const questionContainer = selectElement.closest(".question");
questionInput.style.display = "block"; const optionsContainer =
const optionsContainer = select.nextElementSibling.nextElementSibling; questionContainer.querySelector(".options-container");
optionsContainer.innerHTML = const addOptionButton =
'<input type="text" class="form-control option-input" placeholder="Option 1">'; 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; let questionCount = document.querySelectorAll(".question").length;
function addNewQuestion() { function addNewQuestion() {
const newQuestionDiv = document.createElement("div"); const newQuestionDiv = document.createElement("div");
newQuestionDiv.className = "question"; // newQuestionDiv.className = "question";
newQuestionDiv.innerHTML = ` newQuestionDiv.innerHTML = `
<div class="question mb-4 p-3 border rounded bg-white"> <div class="question mb-4 p-4 border rounded bg-white shadow-sm">
<select class="form-control question_type mb-1" onchange="changeQuestionType(this)"> <select class="form-control question_type mb-1" onchange="changeQuestionType(this)">
<option value="">Select Question Type</option> <option style="border:1px solid rgb(103,58,183);" value="">Select Question Type</option>
<option value="multiple_choice">Multiple Choice</option> <option value="multiple_choice">Multiple Choice</option>
<option value="checkbox">Checkbox</option> <option value="checkbox">Checkbox</option>
<option value="dropdown">Dropdown</option> <option value="dropdown">Dropdown</option>
<option value="text">Text</option>
</select> </select>
<input type="text" name="question" class="form-control question-input mb-3" placeholder="Type your question here" /> <input style="border:none; border-bottom: 2px solid rgb(103,58,183); border-radius:0" type="text" name="question" class="form-control question-input mb-3" placeholder="Type your question here" />
<div class="options-container mb-3"> <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> </div>
</div> <button class="btn btn-secondary add-option-btn" onclick="addOption(this)">
<button class="btn btn-secondary" onclick="addOption(this)">
Add Option Add Option
</button> </button>
<button class="btn btn-md" id="moveUpButton" onclick="deleteQuestion(this);"> <button class="btn btn-md" id="moveUpButton" onclick="deleteQuestion(this);">
<img src="/images/bin.png" alt="" width="20px" height="20px" /> <img src="/images/bin.png" alt="" width="20px" height="20px" />
</button> </button>
<label class="ml-3">
<input type="checkbox" class="required-checkbox"> Required
</label>
</div> </div>
`; `;
questionsSection.appendChild(newQuestionDiv); questionsSection.appendChild(newQuestionDiv);
@ -301,7 +110,9 @@ document.addEventListener("DOMContentLoaded", function () {
sidebar.style.transform = `translateY(${newPosition}px)`; sidebar.style.transform = `translateY(${newPosition}px)`;
console.log(`Moving sidebar to: ${newPosition}px`); console.log(`Moving sidebar to: ${newPosition}px`);
} else { } else {
sidebar.style.transform = `translateY(${containerHeight - sidebarHeight}px)`; sidebar.style.transform = `translateY(${
containerHeight - sidebarHeight
}px)`;
console.log(`Moving sidebar to bottom of container`); console.log(`Moving sidebar to bottom of container`);
} }
} else { } else {
@ -312,18 +123,43 @@ document.addEventListener("DOMContentLoaded", function () {
function saveForm() { function saveForm() {
const formTitle = document.getElementById("form-title").value; const formTitle = document.getElementById("form-title").value;
const formDescription = document.getElementById("form-description").value; const formDescription =
document.getElementById("form-description").value;
const questions = document.querySelectorAll(".question"); const questions = document.querySelectorAll(".question");
let formData = []; let formData = [];
questions.forEach((question, index) => { console.log(questions);
questions.forEach((question) => {
const questionType = question.querySelector("select").value; const questionType = question.querySelector("select").value;
const questionText = question.querySelector(".question-input").value; const questionText =
const options = Array.from(question.querySelectorAll(".option-input")).map((input) => input.value); question.querySelector(".question-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({ formData.push({
type: questionType, type: questionType,
text: questionText, text: questionText,
options: options, options: options,
required: isRequired,
});
console.log({
type: questionType,
text: questionText,
options: options,
required: isRequired,
}); });
}); });
@ -342,8 +178,7 @@ document.addEventListener("DOMContentLoaded", function () {
questions: formData, questions: formData,
}; };
console.log("Form Data:", data); console.log(data);
console.log("CSRF Token:", csrfToken);
fetch("/forms", { fetch("/forms", {
method: "POST", method: "POST",
@ -353,19 +188,26 @@ document.addEventListener("DOMContentLoaded", function () {
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}) })
.then((response) => { .then((response) => response.json())
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((result) => { .then((result) => {
console.log("Server Response:", result);
if (result.success) { if (result.success) {
alert("Form saved successfully!"); Swal.fire({
title: "Success!",
text: "Form saved successfully.",
icon: "success",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.href = "/forms"; window.location.href = "/forms";
}
});
} else { } else {
alert("Failed to save form."); Swal.fire({
title: "Error!",
text: "Failed to save form.",
icon: "error",
confirmButtonText: "OK",
});
} }
}) })
.catch((error) => { .catch((error) => {
@ -381,36 +223,12 @@ document.addEventListener("DOMContentLoaded", function () {
window.changeQuestionType = changeQuestionType; window.changeQuestionType = changeQuestionType;
window.saveForm = saveForm; window.saveForm = saveForm;
window.previewForm = function (formId) { // document.getElementById("add-question-button").addEventListener("click", addNewQuestion);
const formTitle = document.getElementById("form-title").value;
const formDescription = document.getElementById("form-description").value;
const questions = document.querySelectorAll(".question");
let formData = [];
questions.forEach((question) => { document
const questionType = question.querySelector("select").value; .getElementById("questions_section")
const questionText = question.querySelector(".question-input").value; .addEventListener("DOMNodeInserted", updateAddButtonPosition);
const options = Array.from(question.querySelectorAll(".option-input")).map((input) => input.value); document
formData.push({ .getElementById("questions_section")
type: questionType, .addEventListener("DOMNodeRemoved", updateAddButtonPosition);
text: questionText,
options: options,
});
});
const formParams = new URLSearchParams({
title: formTitle,
description: formDescription,
data: JSON.stringify(formData),
});
window.location.href = '/forms/' + formId + '/preview';
};
// Assuming there's a button with id "add-question-button"
document.getElementById("add-question-button").addEventListener("click", addNewQuestion);
document.getElementById("questions_section").addEventListener("DOMNodeInserted", updateAddButtonPosition);
document.getElementById("questions_section").addEventListener("DOMNodeRemoved", updateAddButtonPosition);
}); });

0
public/robots.txt Normal file → Executable file
View File

0
resources/css/app.css Normal file → Executable file
View File

0
resources/js/app.js Normal file → Executable file
View File

0
resources/js/bootstrap.js vendored Normal file → Executable file
View File

0
resources/sass/_variables.scss Normal file → Executable file
View File

0
resources/sass/app.scss Normal file → Executable file
View File

0
resources/views/auth/login.blade.php Normal file → Executable file
View File

0
resources/views/auth/passwords/confirm.blade.php Normal file → Executable file
View File

0
resources/views/auth/passwords/email.blade.php Normal file → Executable file
View File

0
resources/views/auth/passwords/reset.blade.php Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More