From 2f76f9428316210347beb205821e0e39113e331d Mon Sep 17 00:00:00 2001 From: Saufi Date: Sat, 16 May 2026 20:53:43 +0800 Subject: [PATCH] feat: questionnaire management (Fasa 6) - QuestionnaireSetController: full CRUD + publish/archive - QuestionController: store, update, destroy, reorder - ProgramQuestionnaireController: attach, confirm, detach - Public/QuestionnaireController: show form, submit responses, double-submit guard - Views: admin questionnaire CRUD, program questionnaire assign, public form + thankyou/already Co-Authored-By: Claude Sonnet 4.6 --- .../Admin/ProgramQuestionnaireController.php | 79 ++++- .../Controllers/Admin/QuestionController.php | 80 ++++- .../Admin/QuestionnaireSetController.php | 115 ++++--- .../Public/QuestionnaireController.php | 112 ++++++- .../programs/questionnaire/show.blade.php | 171 ++++++++++ .../admin/questionnaires/create.blade.php | 50 +++ .../views/admin/questionnaires/edit.blade.php | 50 +++ .../admin/questionnaires/index.blade.php | 107 +++++++ .../views/admin/questionnaires/show.blade.php | 294 ++++++++++++++++++ .../public/questionnaire/already.blade.php | 35 +++ .../views/public/questionnaire/show.blade.php | 100 ++++++ .../public/questionnaire/thankyou.blade.php | 41 +++ 12 files changed, 1196 insertions(+), 38 deletions(-) create mode 100644 resources/views/admin/programs/questionnaire/show.blade.php create mode 100644 resources/views/admin/questionnaires/create.blade.php create mode 100644 resources/views/admin/questionnaires/edit.blade.php create mode 100644 resources/views/admin/questionnaires/index.blade.php create mode 100644 resources/views/admin/questionnaires/show.blade.php create mode 100644 resources/views/public/questionnaire/already.blade.php create mode 100644 resources/views/public/questionnaire/show.blade.php create mode 100644 resources/views/public/questionnaire/thankyou.blade.php diff --git a/app/Http/Controllers/Admin/ProgramQuestionnaireController.php b/app/Http/Controllers/Admin/ProgramQuestionnaireController.php index 3b7ed44..1dcaafb 100644 --- a/app/Http/Controllers/Admin/ProgramQuestionnaireController.php +++ b/app/Http/Controllers/Admin/ProgramQuestionnaireController.php @@ -3,9 +3,86 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; +use App\Models\Program; +use App\Models\ProgramQuestionnaire; +use App\Models\QuestionnaireSet; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\View\View; class ProgramQuestionnaireController extends Controller { - // + public function show(Program $program): View + { + $pq = $program->questionnaire()->with('questionnaireSet.questions')->first(); + + $availableSets = QuestionnaireSet::where('status', 'published') + ->withCount('questions') + ->orderBy('title') + ->get(); + + return view('admin.programs.questionnaire.show', compact('program', 'pq', 'availableSets')); + } + + public function attach(Request $request, Program $program): RedirectResponse + { + $data = $request->validate([ + 'questionnaire_set_id' => 'required|exists:questionnaire_sets,id', + ]); + + if ($program->questionnaire()->exists()) { + return back()->with('error', 'Program ini sudah ada soalselidik dilampirkan. Tanggalkan dahulu sebelum lampir yang baru.'); + } + + $set = QuestionnaireSet::findOrFail($data['questionnaire_set_id']); + + if ($set->status !== 'published') { + return back()->with('error', 'Hanya soalselidik yang diterbitkan boleh dilampirkan.'); + } + + ProgramQuestionnaire::create([ + 'program_id' => $program->id, + 'questionnaire_set_id' => $set->id, + 'is_confirmed' => false, + ]); + + return back()->with('success', 'Soalselidik berjaya dilampirkan. Sila sahkan sebelum program bermula.'); + } + + public function confirm(Request $request, Program $program): RedirectResponse + { + $pq = $program->questionnaire; + + if (! $pq) { + return back()->with('error', 'Tiada soalselidik untuk disahkan.'); + } + + $pq->update([ + 'is_confirmed' => true, + 'confirmed_at' => now(), + 'confirmed_by' => auth()->id(), + ]); + + return back()->with('success', 'Soalselidik telah disahkan untuk program ini.'); + } + + public function detach(Program $program): RedirectResponse + { + $pq = $program->questionnaire; + + if (! $pq) { + return back()->with('error', 'Tiada soalselidik untuk ditanggalkan.'); + } + + if ($pq->is_confirmed) { + $hasResponses = \App\Models\QuestionnaireResponse::where('program_id', $program->id)->exists(); + if ($hasResponses) { + return back()->with('error', 'Soalselidik tidak boleh ditanggalkan kerana sudah ada respons diterima.'); + } + } + + $pq->delete(); + + return back()->with('success', 'Soalselidik berjaya ditanggalkan.'); + } } diff --git a/app/Http/Controllers/Admin/QuestionController.php b/app/Http/Controllers/Admin/QuestionController.php index 1d7fa5b..c4b339c 100644 --- a/app/Http/Controllers/Admin/QuestionController.php +++ b/app/Http/Controllers/Admin/QuestionController.php @@ -3,9 +3,87 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; +use App\Models\QuestionnaireQuestion; +use App\Models\QuestionnaireSet; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class QuestionController extends Controller { - // + public function store(Request $request, QuestionnaireSet $set): RedirectResponse + { + $data = $request->validate([ + 'question_text' => 'required|string|max:1000', + 'question_type' => 'required|in:rating,single_choice,multiple_choice,short_text,long_text', + 'is_required' => 'boolean', + 'options' => 'nullable|array', + 'options.*' => 'required|string|max:255', + ]); + + $needsOptions = in_array($data['question_type'], ['single_choice', 'multiple_choice']); + if ($needsOptions && empty($data['options'])) { + return back()->withErrors(['options' => 'Pilihan jawapan diperlukan untuk jenis soalan ini.'])->withInput(); + } + + $maxOrder = $set->questions()->max('sort_order') ?? 0; + + $set->questions()->create([ + 'question_text' => $data['question_text'], + 'question_type' => $data['question_type'], + 'is_required' => $data['is_required'] ?? true, + 'options_json' => $needsOptions ? array_values(array_filter($data['options'] ?? [])) : null, + 'sort_order' => $maxOrder + 1, + ]); + + return redirect()->route('admin.questionnaires.show', $set) + ->with('success', 'Soalan berjaya ditambah.'); + } + + public function update(Request $request, QuestionnaireQuestion $question): RedirectResponse + { + $data = $request->validate([ + 'question_text' => 'required|string|max:1000', + 'question_type' => 'required|in:rating,single_choice,multiple_choice,short_text,long_text', + 'is_required' => 'boolean', + 'options' => 'nullable|array', + 'options.*' => 'required|string|max:255', + ]); + + $needsOptions = in_array($data['question_type'], ['single_choice', 'multiple_choice']); + + $question->update([ + 'question_text' => $data['question_text'], + 'question_type' => $data['question_type'], + 'is_required' => $data['is_required'] ?? true, + 'options_json' => $needsOptions ? array_values(array_filter($data['options'] ?? [])) : null, + ]); + + return redirect()->route('admin.questionnaires.show', $question->questionnaire_set_id) + ->with('success', 'Soalan berjaya dikemaskini.'); + } + + public function destroy(QuestionnaireQuestion $question): RedirectResponse + { + $setId = $question->questionnaire_set_id; + $question->delete(); + + return redirect()->route('admin.questionnaires.show', $setId) + ->with('success', 'Soalan berjaya dipadam.'); + } + + public function reorder(Request $request): JsonResponse + { + $data = $request->validate([ + 'order' => 'required|array', + 'order.*' => 'integer|exists:questionnaire_questions,id', + ]); + + foreach ($data['order'] as $sortOrder => $questionId) { + QuestionnaireQuestion::where('id', $questionId) + ->update(['sort_order' => $sortOrder + 1]); + } + + return response()->json(['ok' => true]); + } } diff --git a/app/Http/Controllers/Admin/QuestionnaireSetController.php b/app/Http/Controllers/Admin/QuestionnaireSetController.php index 3d1ea98..5703c39 100644 --- a/app/Http/Controllers/Admin/QuestionnaireSetController.php +++ b/app/Http/Controllers/Admin/QuestionnaireSetController.php @@ -3,63 +3,108 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; +use App\Models\QuestionnaireSet; use Illuminate\Http\Request; +use Illuminate\Http\RedirectResponse; +use Illuminate\View\View; class QuestionnaireSetController extends Controller { - /** - * Display a listing of the resource. - */ - public function index() + public function index(Request $request): View { - // + $query = QuestionnaireSet::withCount('questions') + ->with('creator') + ->latest(); + + if ($request->filled('q')) { + $query->where('title', 'like', '%' . $request->q . '%'); + } + + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + $sets = $query->paginate(20)->withQueryString(); + + return view('admin.questionnaires.index', compact('sets')); } - /** - * Show the form for creating a new resource. - */ - public function create() + public function create(): View { - // + return view('admin.questionnaires.create'); } - /** - * Store a newly created resource in storage. - */ - public function store(Request $request) + public function store(Request $request): RedirectResponse { - // + $data = $request->validate([ + 'title' => 'required|string|max:255', + 'description' => 'nullable|string|max:1000', + ]); + + $data['created_by'] = auth()->id(); + $data['status'] = 'draft'; + + $set = QuestionnaireSet::create($data); + + return redirect()->route('admin.questionnaires.show', $set) + ->with('success', 'Set soalselidik berjaya dicipta.'); } - /** - * Display the specified resource. - */ - public function show(string $id) + public function show(QuestionnaireSet $set): View { - // + $set->load(['questions', 'creator']); + $usedInPrograms = $set->programs()->get(); + + return view('admin.questionnaires.show', compact('set', 'usedInPrograms')); } - /** - * Show the form for editing the specified resource. - */ - public function edit(string $id) + public function edit(QuestionnaireSet $set): View { - // + return view('admin.questionnaires.edit', compact('set')); } - /** - * Update the specified resource in storage. - */ - public function update(Request $request, string $id) + public function update(Request $request, QuestionnaireSet $set): RedirectResponse { - // + $data = $request->validate([ + 'title' => 'required|string|max:255', + 'description' => 'nullable|string|max:1000', + ]); + + $set->update($data); + + return redirect()->route('admin.questionnaires.show', $set) + ->with('success', 'Set soalselidik berjaya dikemaskini.'); } - /** - * Remove the specified resource from storage. - */ - public function destroy(string $id) + public function destroy(QuestionnaireSet $set): RedirectResponse { - // + $confirmed = $set->programQuestionnaires()->where('is_confirmed', true)->exists(); + + if ($confirmed) { + return back()->with('error', 'Soalselidik ini tidak boleh dipadam kerana sudah disahkan untuk program.'); + } + + $set->delete(); + + return redirect()->route('admin.questionnaires.index') + ->with('success', 'Set soalselidik berjaya dipadam.'); + } + + public function publish(QuestionnaireSet $set): RedirectResponse + { + if ($set->questions()->count() === 0) { + return back()->with('error', 'Tambah sekurang-kurangnya satu soalan sebelum menerbitkan.'); + } + + $set->update(['status' => 'published']); + + return back()->with('success', 'Set soalselidik diterbitkan.'); + } + + public function archive(QuestionnaireSet $set): RedirectResponse + { + $set->update(['status' => 'archived']); + + return back()->with('success', 'Set soalselidik diarkibkan.'); } } diff --git a/app/Http/Controllers/Public/QuestionnaireController.php b/app/Http/Controllers/Public/QuestionnaireController.php index 40022e5..eb0d5e1 100644 --- a/app/Http/Controllers/Public/QuestionnaireController.php +++ b/app/Http/Controllers/Public/QuestionnaireController.php @@ -3,9 +3,119 @@ namespace App\Http\Controllers\Public; use App\Http\Controllers\Controller; +use App\Models\Participant; +use App\Models\ProgramQrCode; +use App\Models\QuestionnaireResponse; +use App\Models\QuestionnaireAnswer; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\View\View; class QuestionnaireController extends Controller { - // + public function show(string $qr_token, string $participant_uuid): View|RedirectResponse + { + $qrCode = ProgramQrCode::where('token', $qr_token)->where('is_active', true)->firstOrFail(); + $program = $qrCode->program; + + $participant = Participant::where('uuid', $participant_uuid)->firstOrFail(); + + // Verify participant belongs to this program + $pp = $program->programParticipants()->where('participant_id', $participant->id)->first(); + abort_if(! $pp, 404); + + $pq = $program->questionnaire()->with('questionnaireSet.questions')->first(); + + if (! $pq || ! $pq->is_confirmed) { + // No questionnaire — go straight to semak page + return redirect()->route('public.semak.show', $qr_token); + } + + // Check already submitted + $alreadySubmitted = QuestionnaireResponse::where('program_id', $program->id) + ->where('participant_id', $participant->id) + ->exists(); + + if ($alreadySubmitted) { + return view('public.questionnaire.already', compact('program', 'participant', 'qrCode')); + } + + $questions = $pq->questionnaireSet->questions; + + return view('public.questionnaire.show', compact('program', 'participant', 'qrCode', 'pq', 'questions')); + } + + public function submit(Request $request, string $qr_token, string $participant_uuid): View|RedirectResponse + { + $qrCode = ProgramQrCode::where('token', $qr_token)->where('is_active', true)->firstOrFail(); + $program = $qrCode->program; + + $participant = Participant::where('uuid', $participant_uuid)->firstOrFail(); + + $pp = $program->programParticipants()->where('participant_id', $participant->id)->first(); + abort_if(! $pp, 404); + + $pq = $program->questionnaire()->with('questionnaireSet.questions')->first(); + abort_if(! $pq || ! $pq->is_confirmed, 404); + + // Prevent double-submit + $existing = QuestionnaireResponse::where('program_id', $program->id) + ->where('participant_id', $participant->id) + ->first(); + + if ($existing) { + return view('public.questionnaire.already', compact('program', 'participant', 'qrCode')); + } + + $questions = $pq->questionnaireSet->questions; + + // Validate required questions + $rules = []; + foreach ($questions as $q) { + if ($q->is_required) { + $rules['q_' . $q->id] = 'required'; + } else { + $rules['q_' . $q->id] = 'nullable'; + } + if ($q->question_type === 'multiple_choice') { + $rules['q_' . $q->id] = ($q->is_required ? 'required|' : 'nullable|') . 'array'; + } + } + + $validated = $request->validate($rules, [ + 'q_*.required' => 'Soalan ini wajib dijawab.', + ]); + + // Save response + $response = QuestionnaireResponse::create([ + 'program_id' => $program->id, + 'participant_id' => $participant->id, + 'questionnaire_set_id' => $pq->questionnaire_set_id, + 'submitted_at' => now(), + 'ip_address' => $request->ip(), + 'user_agent' => substr($request->userAgent() ?? '', 0, 500), + ]); + + foreach ($questions as $q) { + $raw = $request->input('q_' . $q->id); + + if ($raw === null && ! $q->is_required) { + continue; + } + + $value = match ($q->question_type) { + 'multiple_choice' => (array) $raw, + 'rating' => (int) $raw, + default => $raw, + }; + + QuestionnaireAnswer::create([ + 'questionnaire_response_id' => $response->id, + 'questionnaire_question_id' => $q->id, + 'answer_value' => $value, + ]); + } + + return view('public.questionnaire.thankyou', compact('program', 'participant', 'qrCode')); + } } diff --git a/resources/views/admin/programs/questionnaire/show.blade.php b/resources/views/admin/programs/questionnaire/show.blade.php new file mode 100644 index 0000000..6935632 --- /dev/null +++ b/resources/views/admin/programs/questionnaire/show.blade.php @@ -0,0 +1,171 @@ +@extends('layouts.admin') + +@section('title', 'Soalselidik — ' . $program->title) +@section('header', 'Urus Soalselidik Program') + +@section('breadcrumb') + + + +@endsection + +@section('header-actions') + + Kembali + +@endsection + +@section('content') + +
+ {{-- Current Questionnaire --}} +
+
+
+
Soalselidik Semasa
+
+
+ + @if($pq && $pq->questionnaireSet) +
+
+
{{ $pq->questionnaireSet->title }}
+
{{ $pq->questionnaireSet->questions->count() }} soalan
+ @if($pq->questionnaireSet->description) +
{{ $pq->questionnaireSet->description }}
+ @endif +
+ @if($pq->is_confirmed) + + Disahkan + + @else + + Belum Disahkan + + @endif +
+ + @if($pq->is_confirmed) +
+ + Disahkan oleh {{ $pq->confirmedBy?->name ?? '—' }} + pada {{ $pq->confirmed_at?->format('d M Y, H:i') }}. +
+ @else +
+ + Soalselidik perlu disahkan sebelum peserta boleh menjawab. +
+
+ @csrf + +
+ @endif + + {{-- List Questions --}} +
+
Senarai Soalan:
+ @foreach($pq->questionnaireSet->questions as $q) +
+ {{ $loop->iteration }} +
+
{{ $q->question_text }}
+ + {{ match($q->question_type) { + 'rating' => 'Rating', + 'single_choice' => 'Pilihan Tunggal', + 'multiple_choice' => 'Pilihan Berganda', + 'short_text' => 'Teks Pendek', + 'long_text' => 'Teks Panjang', + } }} + + @if($q->is_required) + Wajib + @endif +
+
+ @endforeach +
+ + {{-- Detach --}} +
+
+ @csrf @method('DELETE') + +
+
+ + @else +
+ +

Belum ada soalselidik dilampirkan untuk program ini.

+
+ @endif + +
+
+
+ + {{-- Right: Attach New --}} + @if(! $pq) +
+
+
+
Lampir Soalselidik
+
+
+ + @if($availableSets->isEmpty()) +
+ + Tiada set soalselidik yang diterbitkan. + Buat set baru. +
+ @else +
+ @csrf +
+ + + @error('questionnaire_set_id')
{{ $message }}
@enderror +
+ +
+ + Selepas lampir, anda perlu sahkan soalselidik sebelum peserta boleh menjawab. +
+ + +
+ @endif + + + +
+
+
+ @endif + +
+ +@endsection diff --git a/resources/views/admin/questionnaires/create.blade.php b/resources/views/admin/questionnaires/create.blade.php new file mode 100644 index 0000000..79631f0 --- /dev/null +++ b/resources/views/admin/questionnaires/create.blade.php @@ -0,0 +1,50 @@ +@extends('layouts.admin') + +@section('title', 'Buat Set Soalselidik Baru') +@section('header', 'Buat Set Soalselidik') + +@section('breadcrumb') + + +@endsection + +@section('content') + +
+
+
+
+
+ @csrf + +
+ + + @error('title')
{{ $message }}
@enderror +
+ +
+ + + @error('description')
{{ $message }}
@enderror +
+ +
+ + + Batal + +
+
+
+
+
+
+ +@endsection diff --git a/resources/views/admin/questionnaires/edit.blade.php b/resources/views/admin/questionnaires/edit.blade.php new file mode 100644 index 0000000..d9dc666 --- /dev/null +++ b/resources/views/admin/questionnaires/edit.blade.php @@ -0,0 +1,50 @@ +@extends('layouts.admin') + +@section('title', 'Edit Set Soalselidik') +@section('header', 'Edit Set Soalselidik') + +@section('breadcrumb') + + + +@endsection + +@section('content') + +
+
+
+
+
+ @csrf + @method('PUT') + +
+ + + @error('title')
{{ $message }}
@enderror +
+ +
+ + + @error('description')
{{ $message }}
@enderror +
+ +
+ + + Batal + +
+
+
+
+
+
+ +@endsection diff --git a/resources/views/admin/questionnaires/index.blade.php b/resources/views/admin/questionnaires/index.blade.php new file mode 100644 index 0000000..4c2ab57 --- /dev/null +++ b/resources/views/admin/questionnaires/index.blade.php @@ -0,0 +1,107 @@ +@extends('layouts.admin') + +@section('title', 'Set Soalselidik') +@section('header', 'Set Soalselidik') + +@section('breadcrumb') + +@endsection + +@section('header-actions') + + Buat Set Baru + +@endsection + +@section('content') + +
+
+
+
+ +
+
+ +
+
+ + @if(request()->hasAny(['q', 'status'])) + Set Semula + @endif +
+
+
+ +
+ + + + + + + + + + + + + @forelse($sets as $set) + + + + + + + + + @empty + + + + @endforelse + +
TajukSoalanStatusDicipta OlehTarikh Cipta
+ + {{ $set->title }} + + @if($set->description) +
{{ $set->description }}
+ @endif +
+ {{ $set->questions_count }} + + @if($set->status === 'published') + Diterbitkan + @elseif($set->status === 'archived') + Diarkib + @else + Draf + @endif + {{ $set->creator?->name ?? '—' }}{{ $set->created_at->format('d/m/Y') }} + + + +
+ + Tiada set soalselidik dijumpai. +
+
+ + @if($sets->hasPages()) + + @endif +
+ +@endsection diff --git a/resources/views/admin/questionnaires/show.blade.php b/resources/views/admin/questionnaires/show.blade.php new file mode 100644 index 0000000..3d2030b --- /dev/null +++ b/resources/views/admin/questionnaires/show.blade.php @@ -0,0 +1,294 @@ +@extends('layouts.admin') + +@section('title', $set->title) +@section('header', $set->title) + +@section('breadcrumb') + + +@endsection + +@section('header-actions') +
+ + Edit + + @if($set->status === 'draft') +
+ @csrf + +
+ @elseif($set->status === 'published') +
+ @csrf + +
+ @endif +
+@endsection + +@section('content') + +
+ {{-- Left: Questions --}} +
+ + {{-- Status Banner --}} + @if($set->status === 'draft') +
+ + Set ini masih dalam status Draf. Terbitkan setelah siap untuk dilampirkan ke program. +
+ @elseif($set->status === 'archived') +
+ + Set ini telah diarkibkan dan tidak boleh dilampirkan ke program baru. +
+ @endif + + {{-- Question List --}} +
+
+
+ Senarai Soalan + {{ $set->questions->count() }} +
+
+ + @if($set->questions->isEmpty()) +
+ + Belum ada soalan. Tambah soalan menggunakan borang di sebelah kanan. +
+ @else +
    + @foreach($set->questions as $q) +
  • +
    +
    +
    + {{ $loop->iteration }} + + {{ match($q->question_type) { + 'rating' => 'Rating', + 'single_choice' => 'Pilihan Tunggal', + 'multiple_choice' => 'Pilihan Berganda', + 'short_text' => 'Teks Pendek', + 'long_text' => 'Teks Panjang', + default => $q->question_type, + } }} + + @if($q->is_required) + Wajib + @endif +
    +
    {{ $q->question_text }}
    + @if($q->options_json) +
    + @foreach($q->options_json as $opt) + {{ $opt }} + @endforeach +
    + @endif +
    +
    + +
    + @csrf @method('DELETE') + +
    +
    +
    +
  • + @endforeach +
+ @endif +
+ + {{-- Used In Programs --}} + @if($usedInPrograms->count()) +
+
+
Digunakan Dalam Program
+
+
    + @foreach($usedInPrograms as $program) +
  • + + {{ $program->title }} + + @if($program->pivot->is_confirmed ?? false) + Disahkan + @else + Belum Disahkan + @endif +
  • + @endforeach +
+
+ @endif +
+ + {{-- Right: Add / Edit Question Form --}} +
+
+
+
Tambah Soalan
+
+
+ + {{-- Add Question Form --}} +
+ @csrf + + + +
+ + + @error('question_text')
{{ $message }}
@enderror +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ +
+
+
+
+ +@endsection + +@push('scripts') + +@endpush diff --git a/resources/views/public/questionnaire/already.blade.php b/resources/views/public/questionnaire/already.blade.php new file mode 100644 index 0000000..f4df0a2 --- /dev/null +++ b/resources/views/public/questionnaire/already.blade.php @@ -0,0 +1,35 @@ +@extends('layouts.public') + +@section('title', 'Sudah Dihantar — ' . $program->title) + +@section('hero') +

{{ $program->title }}

+
+ Borang Penilaian +
+@endsection + +@section('content') + +
+
+ +
+
Sudah Dihantar
+

+ Maklum balas anda untuk program ini sudah pernah dihantar sebelum ini. + Setiap peserta hanya boleh menghantar satu maklum balas. +

+ +
+ + Sijil Digital (eCert) akan dihantar ke emel anda setelah program tamat. +
+ + + Semak Status Sijil + +
+ +@endsection diff --git a/resources/views/public/questionnaire/show.blade.php b/resources/views/public/questionnaire/show.blade.php new file mode 100644 index 0000000..c40ac95 --- /dev/null +++ b/resources/views/public/questionnaire/show.blade.php @@ -0,0 +1,100 @@ +@extends('layouts.public') + +@section('title', 'Borang Penilaian — ' . $program->title) + +@section('hero') +

{{ $program->title }}

+
+ Borang Penilaian Program +
+@endsection + +@section('content') + +
+
+
+ +
+
{{ $pq->questionnaireSet->title }}
+

+ Sila jawab semua soalan sebelum memuat turun sijil anda, {{ $participant->name }}. +

+
+ +
+ @csrf + + @foreach($questions as $q) +
+ + + @error('q_' . $q->id) +
{{ $message }}
+ @enderror + + @if($q->question_type === 'rating') +
+ @for($i = 1; $i <= 5; $i++) +
+ id) == $i ? 'checked' : '' }}> + +
+ @endfor +
+ + @elseif($q->question_type === 'single_choice') + @foreach($q->options_json ?? [] as $opt) +
+ id) === $opt ? 'checked' : '' }}> + +
+ @endforeach + + @elseif($q->question_type === 'multiple_choice') + @foreach($q->options_json ?? [] as $opt) +
+ id) ?? [])) ? 'checked' : '' }}> + +
+ @endforeach + + @elseif($q->question_type === 'short_text') + + + @elseif($q->question_type === 'long_text') + + @endif +
+ @endforeach + + +
+
+ +@endsection diff --git a/resources/views/public/questionnaire/thankyou.blade.php b/resources/views/public/questionnaire/thankyou.blade.php new file mode 100644 index 0000000..b614f80 --- /dev/null +++ b/resources/views/public/questionnaire/thankyou.blade.php @@ -0,0 +1,41 @@ +@extends('layouts.public') + +@section('title', 'Terima Kasih — ' . $program->title) + +@section('hero') +

{{ $program->title }}

+
+ Maklum Balas Diterima +
+@endsection + +@section('content') + +
+
+ +
+
Terima Kasih!
+

+ Maklum balas anda telah berjaya dihantar, {{ $participant->name }}. + Kami menghargai pandangan anda. +

+ +
+ + Sijil Digital (eCert) anda akan dihantar ke emel + @if($participant->email) + {{ $participant->email }} + @else + yang didaftarkan + @endif + setelah program tamat. +
+ + + Semak Status Sijil + +
+ +@endsection