Compare commits

..

7 Commits

Author SHA1 Message Date
yash 2ce4691231 Made index page responsive 2024-07-25 11:28:48 +05:30
yash c4c52ac0af Bug fixes 2024-07-25 10:46:43 +05:30
Yash b6b8642496 Fixed profile button bug 2024-07-25 03:01:25 +05:30
Yash f83295fdfd Changed UI of viewResponse Page 2024-07-25 02:51:51 +05:30
Yash d4ad6e9a47 Fixed bug on the form responding page 2024-07-25 02:46:46 +05:30
Yash 87e0486962 Fixed bugs in edit form, Added softDelete, Added form_snapshot to save the older responses even after editing the form 2024-07-25 01:11:19 +05:30
yash 93916d6cde Flash message and UI changes 2024-07-24 17:24:47 +05:30
135 changed files with 447 additions and 216 deletions

0
.editorconfig Executable file → Normal file
View File

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

0
.gitattributes vendored Executable file → Normal file
View File

0
.gitignore vendored Executable file → Normal file
View File

0
README.md Executable file → Normal file
View File

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

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

View File

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

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

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

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

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

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

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

@ -1,7 +1,7 @@
<?php <?php
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\Form; use App\Models\Form;
use App\Models\User; use App\Models\User;
@ -104,7 +104,6 @@ Contact us at (123) 456-7890 or no_reply@example.com
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',
@ -112,33 +111,35 @@ Contact us at (123) 456-7890 or no_reply@example.com
'questions.*.type' => 'required|string|in:multiple_choice,checkbox,dropdown,text', 'questions.*.type' => 'required|string|in:multiple_choice,checkbox,dropdown,text',
'questions.*.text' => 'required|string', 'questions.*.text' => 'required|string',
'questions.*.options' => 'nullable|array', 'questions.*.options' => 'nullable|array',
'questions.*.required' => 'nullable|boolean', 'questions.*.required' => 'boolean',
]); ]);
DB::beginTransaction();
$form = new Form(); try {
$form->title = $validatedData['title']; $form = Form::create([
$form->description = $validatedData['description']; 'title' => $validatedData['title'],
$form->is_published = $request->input('is_published', false); 'description' => $validatedData['description'],
$form->user_id = Auth::id(); 'user_id' => Auth::id(),
$form->save(); ]);
foreach ($validatedData['questions'] as $questionData) { foreach ($validatedData['questions'] as $questionData) {
$question = new Question(); $question = new Question([
$question->form_id = $form->id; 'form_id' => $form->id,
$question->type = $questionData['type']; 'type' => $questionData['type'],
$question->question_text = $questionData['text']; 'question_text' => $questionData['text'],
$question->options = isset($questionData['options']) ? json_encode($questionData['options']) : null; 'options' => json_encode($questionData['options'] ?? []),
$question->required = isset($questionData['required']) ? $questionData['required'] : false; 'required' => $questionData['required'],
]);
$question->save(); $question->save();
} }
DB::commit();
return response()->json(['success' => true, 'message' => 'Form saved successfully.']);
return response()->json(['success' => true, 'form_id' => $form->id]);
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error('Error saving form: ' . $e->getMessage(), ['exception' => $e]); DB::rollBack();
return response()->json(['success' => false, 'message' => 'Error saving form'], 500); Log::error('Error saving form: ' . $e->getMessage());
return response()->json(['success' => false, 'message' => 'Failed to save form.']);
} }
} }
@ -156,15 +157,22 @@ Contact us at (123) 456-7890 or no_reply@example.com
public function update(Request $request, Form $form) public function update(Request $request, Form $form)
{ {
if ($request->has('publish')) { try {
$form->is_published = !$form->is_published; // Normalize the 'required' field to boolean
$form->save(); if ($request->has('questions')) {
$questions = $request->input('questions');
return redirect()->route('forms.show', $form); foreach ($questions as $index => $question) {
if (isset($question['required']) && $question['required'] === 'on') {
$questions[$index]['required'] = true;
} else {
$questions[$index]['required'] = false;
}
}
$request->merge(['questions' => $questions]);
} }
Log::info('Incoming request data: ', $request->all());
// Validate the request
$validatedData = $request->validate([ $validatedData = $request->validate([
'title' => 'required|string|max:255', 'title' => 'required|string|max:255',
'description' => 'nullable|string|max:255', 'description' => 'nullable|string|max:255',
@ -174,50 +182,38 @@ Contact us at (123) 456-7890 or no_reply@example.com
'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',
'questions.*.required' => 'boolean',
]); ]);
Log::info('Validated data: ', $validatedData); // Update form
$form->update([ $form->update([
'title' => $validatedData['title'], 'title' => $validatedData['title'],
'description' => $validatedData['description'], 'description' => $validatedData['description'],
]); ]);
$existingQuestionIds = []; // Clear existing questions
$form->questions()->delete();
// Create or update questions
foreach ($validatedData['questions'] as $questionData) { foreach ($validatedData['questions'] as $questionData) {
if (isset($questionData['id'])) { $question = new Question([
$question = Question::find($questionData['id']); 'form_id' => $form->id,
} else { 'type' => $questionData['type'],
$question = new Question(); 'question_text' => $questionData['text'],
$question->form_id = $form->id; 'options' => json_encode($questionData['options'] ?? []),
} 'required' => $questionData['required'],
]);
$question->type = $questionData['type'];
$question->question_text = $questionData['text'];
$question->options = isset($questionData['options']) ? json_encode($questionData['options']) : json_encode([]);
$question->save(); $question->save();
Log::info('Saved question: ', $question->toArray());
$existingQuestionIds[] = $question->id;
} }
$form->questions()->whereNotIn('id', $existingQuestionIds)->delete(); DB::commit();
Log::info('Remaining questions: ', $form->questions()->get()->toArray());
return redirect()->route('forms.show', $form)->with('success', 'Form updated successfully.'); return redirect()->route('forms.show', $form)->with('success', 'Form updated successfully.');
} catch (\Exception $e) {
DB::rollBack();
Log::error('Error updating form: ' . $e->getMessage());
return back()->withErrors(['error' => 'An error occurred while updating the form. Please try again.'])->withInput();
} }
}

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

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

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

@ -31,33 +31,27 @@ class ResponseController extends Controller
public function viewResponse(Form $form, $responseId) public function viewResponse(Form $form, $responseId)
{ {
$responses = Response::where('response_id', $responseId) $responses = Response::where('response_id', $responseId)
->where('form_id', $form->id) ->where('form_id', $form->id)
->get(); ->get();
if ($responses->isEmpty()) {
$questions = Question::where('form_id', $form->id)->get()->keyBy('id'); abort(404, 'Response not found');
$statistics = [];
foreach ($questions as $question) {
$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')); $formSnapshot = json_decode($responses->first()->form_snapshot, true);
if (is_null($formSnapshot) || !isset($formSnapshot['questions'])) {
Log::error('Form snapshot is null or does not contain questions', [
'response_id' => $responseId,
'form_snapshot' => $responses->first()->form_snapshot
]);
abort(500, 'Form snapshot is invalid');
}
$questions = collect($formSnapshot['questions'])->keyBy('id');
return view('responses.viewResponse', compact('form', 'responses', 'questions'));
} }
@ -108,34 +102,39 @@ class ResponseController extends Controller
public function submitForm(Request $request, Form $form) public function submitForm(Request $request, Form $form)
{ {
Log::info($request->all()); Log::info('Form submission started', $request->all());
$questions = $form->questions; $questions = $form->questions;
$requiredQuestionIds = $questions->where('required', true)->pluck('id')->toArray(); $requiredQuestionIds = $questions->where('required', true)->pluck('id')->toArray();
$validatedData = $request->validate([ $validatedData = $request->validate([
'answers' => 'required|array', 'answers' => 'array',
'answers.*' => 'required', 'answers.*' => '',
]); ]);
foreach ($requiredQuestionIds as $requiredQuestionId) { foreach ($requiredQuestionIds as $requiredQuestionId) {
if (!isset($validatedData['answers'][$requiredQuestionId]) || empty($validatedData['answers'][$requiredQuestionId])) { if (!isset($validatedData['answers'][$requiredQuestionId]) || empty($validatedData['answers'][$requiredQuestionId])) {
return redirect()->back() return response()->json(['success' => false, 'message' => 'Please answer all required questions.']);
->withErrors(['errors' => 'Please answer all required questions.'])
->withInput();
} }
} }
Log::info($validatedData); Log::info('Validation passed', $validatedData);
$responseId = Uuid::uuid4()->toString(); $responseId = Uuid::uuid4()->toString();
$formSnapshot = [
'title' => $form->title,
'description' => $form->description,
'questions' => $questions->map(function ($question) {
return [
'id' => $question->id,
'question_text' => $question->question_text,
'type' => $question->type,
'options' => $question->options,
];
})->toArray(),
];
foreach ($validatedData['answers'] as $questionId => $answer) { foreach ($validatedData['answers'] as $questionId => $answer) {
$response = new Response(); $response = new Response();
@ -145,10 +144,12 @@ class ResponseController extends Controller
$response->user_id = auth()->id(); $response->user_id = auth()->id();
$response->answers = json_encode($answer); $response->answers = json_encode($answer);
$response->submitted_at = now(); $response->submitted_at = now();
$response->form_snapshot = json_encode($formSnapshot);
$response->save(); $response->save();
Log::info('Response saved', $response->toArray());
} }
return redirect()->route('responses.showForm', $form) return response()->json(['success' => true, 'message' => 'Response submitted successfully.']);
->with('success', 'Response submitted successfully.');
} }
} }

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

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

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

View File

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

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

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

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

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

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

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

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

@ -1,25 +1,21 @@
<?php <?php
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class Question extends Model class Question extends Model
{ {
use SoftDeletes;
use HasFactory; use HasFactory;
protected $fillable = ['form_id', 'user_id', 'submitted_at', 'answers']; protected $fillable = ['form_id', 'type', 'question_text', 'options', 'required'];
protected $casts = [ protected $casts = [
'answers' => 'array',
'options' => 'array', 'options' => 'array',
'required' => 'boolean',
]; ];
public function getOptionsAttribute($value)
{
return json_decode($value, true);
}
public function form() public function form()
{ {
return $this->belongsTo(Form::class); return $this->belongsTo(Form::class);

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

@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Model;
class Response extends Model class Response extends Model
{ {
use HasFactory; use HasFactory;
protected $fillable = ['form_id', 'user_id', 'answers']; protected $fillable = ['form_id', 'user_id', 'response_id', 'question_id', 'answers', 'submitted_at', 'form_snapshot'];
// Define relationships // Define relationships
public function form() public function form()

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

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

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

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

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

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

0
bin.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

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

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

0
composer.json Executable file → Normal file
View File

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

View File

View File

View File

View File

View File

View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::table('questions', function (Blueprint $table) {
$table->softDeletes();
});
}
public function down()
{
Schema::table('questions', function (Blueprint $table) {
$table->dropSoftDeletes();
});
}
};

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('responses', function (Blueprint $table) {
$table->json('form_snapshot')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('responses', function (Blueprint $table) {
if (Schema::hasColumn('responses', 'form_snapshot')) {
$table->dropColumn('form_snapshot');
}
});
}
};

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

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

0
package.json Executable file → Normal file
View File

0
phpunit.xml Executable file → Normal file
View File

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

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

@ -255,3 +255,7 @@ input:focus, textarea:focus, select:focus{
color: black; color: black;
height: 20px; height: 20px;
} }
.active-question {
border-left: 4px solid blue;
}

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

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

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

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

Before

Width:  |  Height:  |  Size: 959 B

After

Width:  |  Height:  |  Size: 959 B

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

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

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

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

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

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

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

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

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

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

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

Before

Width:  |  Height:  |  Size: 623 B

After

Width:  |  Height:  |  Size: 623 B

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

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

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

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

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

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

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

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

2
public/js/script.js Executable file → Normal file
View File

@ -6,7 +6,7 @@ document.addEventListener("DOMContentLoaded", function () {
const optionDiv = document.createElement("div"); const optionDiv = document.createElement("div");
optionDiv.className = "option"; optionDiv.className = "option";
optionDiv.innerHTML = ` optionDiv.innerHTML = `
<input type="text" class="form-control option-input" placeholder="New Option" /> <input type="text" class="form-control option-input mb-1" placeholder="New Option" />
<span class="delete-option" onclick="deleteOption(this)">&#10005;</span> <span class="delete-option" onclick="deleteOption(this)">&#10005;</span>
`; `;
optionContainer.appendChild(optionDiv); optionContainer.appendChild(optionDiv);

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

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

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

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

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

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

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

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

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