Files
myPostMortem/resources/views/admin/surveys/edit.blade.php

381 lines
16 KiB
PHP

@extends('layouts.app')
@push('styles')
<link rel="stylesheet" href="{{ asset('css/adminHeader.css') }}">
@endpush
@section('content')
<div class="container">
<div class="admin-header-box mb-4">
<div>
<h4>Edit Soal Selidik</h4>
<p>Kemaskini maklumat borang di bawah.</p>
</div>
</div>
@if ($errors->any())
<div class="alert alert-danger">
<strong>Ralat! Sila semak input anda:</strong>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@if (session('error'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>Ralat Sistem:</strong> {{ session('error') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
<form method="POST" action="{{ route('admin.surveys.update', $survey->id) }}" id="survey-builder-form">
@csrf
@method('PUT')
<div class="card shadow-sm mb-4">
<div class="card-body p-4">
<div class="row">
<div class="col-md-8 mb-3">
<label for="title" class="form-label fw-bold">Tajuk Soal Selidik</label>
<input type="text" name="title" id="title" class="form-control" placeholder="eg: Sambutan Maulidur Rasul" value="{{ old('title', $survey->title) }}" required>
</div>
<div class="col-md-4 mb-3">
<label for="date" class="form-label fw-bold">Tarikh</label>
<input type="date" name="date" id="date" class="form-control" value="{{ old('date', $survey->date) }}" required>
</div>
<div class="col-md-12 mb-3">
<label for="perincian" class="form-label fw-bold">Perincian</label>
<textarea name="perincian" id="perincian" class="form-control" rows="3" placeholder="Terangkan tujuan borang ini (Pilihan)">{{ old('perincian', $survey->perincian) }}</textarea>
</div>
</div>
</div>
</div>
<div id="sections-container"></div>
<button type="button" class="btn btn-outline-primary" id="add-section-btn">+ Tambah Seksyen</button>
<div class="text-end mt-4">
<a href="{{ route('admin.surveys.index') }}" class="btn btn-secondary">Batal</a>
<button type="submit" class="btn btn-success">Simpan Perubahan</button>
</div>
</form>
</div>
{{-- TEMPLATES --}}
<template id="section-template">
<div class="card shadow-sm mb-4 section-block">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<h5 class="mb-0 section-title">Bahagian</h5>
<button type="button" class="btn btn-sm btn-outline-danger remove-section-btn">Buang Seksyen</button>
</div>
<div class="card-body p-4">
<div class="row">
<div class="col-md-6 mb-3">
<input type="text" name="sections[0][title]" class="form-control section-title-input" placeholder="Tajuk Bahagian (cth: Maklumat Peribadi)" required>
</div>
<div class="col-md-6 mb-3">
<input type="text" name="sections[0][description]" class="form-control" placeholder="Optional description">
</div>
</div>
<div class="questions-container"></div>
<button type="button" class="btn btn-sm btn-info add-question-btn">+ Tambah soalan</button>
</div>
</div>
</template>
<template id="question-template">
<div class="border rounded p-3 mb-3 question-block bg-white">
<div class="d-flex justify-content-between align-items-center mb-2">
<label class="form-label fw-bold question-title">Soalan 1</label>
<div>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-secondary type-selector" data-type="radio">Radio</button>
<button type="button" class="btn btn-outline-secondary type-selector" data-type="text">Text</button>
<button type="button" class="btn btn-outline-secondary type-selector" data-type="checkbox">Checkbox</button>
</div>
<input type="hidden" name="sections[0][questions][0][type]" class="question-type-input" value="radio">
<button type="button" class="btn btn-sm btn-outline-danger ms-2 remove-question-btn">Buang</button>
</div>
</div>
<input type="text" name="sections[0][questions][0][text]" class="form-control mb-3 question-text-input" placeholder="Isi soalan di sini" required>
<div class="form-check allow-other-container mb-3">
<input class="form-check-input allow-other-option-checkbox" type="checkbox" value="1" name="sections[0][questions][0][allow_other_option]">
<label class="form-check-label">
Lain-lain
</label>
</div>
<div class="options-container">
<label class="form-label">Jawapan Pilihan</label>
<button type="button" class="btn btn-sm btn-link add-option-btn p-0">+ Tambah pilihan</button>
</div>
<div class="text-placeholder-container" style="display:none;">
<input type="text" class="form-control bg-light" placeholder="Ruang responden menjawab" disabled>
</div>
</div>
</template>
<template id="option-template">
<div class="input-group mb-2 option-group">
<input type="text" name="sections[0][questions][0][options][]" class="form-control option-input" placeholder="Pilihan jawapan" required>
<button type="button" class="btn btn-outline-danger remove-option-btn">Buang</button>
</div>
</template>
@endsection
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
const sectionsContainer = document.getElementById('sections-container');
const addSectionBtn = document.getElementById('add-section-btn');
const sectionTemplate = document.getElementById('section-template');
const questionTemplate = document.getElementById('question-template');
const optionTemplate = document.getElementById('option-template');
let sectionCounter = 0;
function addSection(data = null) {
const sectionIndex = sectionCounter++;
const newSection = sectionTemplate.content.cloneNode(true).firstElementChild;
newSection.dataset.sectionIndex = sectionIndex;
const titleInput = newSection.querySelector('.section-title-input');
const descInput = newSection.querySelector('input[name$="[description]"]');
titleInput.value = data?.title ?? '';
descInput.value = data?.description ?? '';
newSection.querySelector('.section-title').textContent = data?.title ?? 'Bahagian';
// Adjust names
newSection.querySelectorAll('[name]').forEach(input => {
input.name = input.name.replace('sections[0]', `sections[${sectionIndex}]`);
});
const questionsContainer = newSection.querySelector('.questions-container');
if (data?.questions && data.questions.length) {
data.questions.forEach(q => addQuestion(questionsContainer, sectionIndex, q));
} else {
addQuestion(questionsContainer, sectionIndex);
}
sectionsContainer.appendChild(newSection);
reindexQuestions();
}
function addQuestion(questionsContainer, sectionIndex, qdata = null) {
const questionIndex = questionsContainer.querySelectorAll('.question-block').length;
const newQuestion = questionTemplate.content.cloneNode(true).firstElementChild;
newQuestion.dataset.questionIndex = questionIndex;
newQuestion.querySelector('.question-title').textContent = `Soalan ${questionIndex + 1}`;
const qtext = newQuestion.querySelector('.question-text-input');
const qtype = newQuestion.querySelector('.question-type-input');
const otherCheckbox = newQuestion.querySelector('.allow-other-option-checkbox');
qtext.value = qdata?.text ?? '';
qtype.value = qdata?.type ?? 'radio';
// BARU: Set status checkbox Lain-lain based on DB data
// Check if qdata.allow_other_option is true or '1'
if (qdata && (qdata.allow_other_option == 1 || qdata.allow_other_option === true)) {
otherCheckbox.checked = true;
} else {
otherCheckbox.checked = false;
}
newQuestion.querySelectorAll('.type-selector').forEach(btn => {
btn.classList.remove('active','btn-primary');
if (btn.dataset.type === qtype.value) btn.classList.add('active','btn-primary');
});
newQuestion.querySelectorAll('[name]').forEach(input => {
input.name = input.name
.replace('sections[0]', `sections[${sectionIndex}]`)
.replace('[questions][0]', `[questions][${questionIndex}]`);
});
const optionsContainer = newQuestion.querySelector('.options-container');
if (qdata?.options && qdata.options.length && qtype.value !== 'text') {
qdata.options.forEach(opt => addOption(optionsContainer, sectionIndex, questionIndex, opt.text));
} else if (qtype.value !== 'text') {
addOption(optionsContainer, sectionIndex, questionIndex);
addOption(optionsContainer, sectionIndex, questionIndex);
}
// Initialize display state (options vs text vs other checkbox)
toggleQuestionType(newQuestion, qtype.value);
questionsContainer.appendChild(newQuestion);
reindexQuestions();
}
function addOption(optionsContainer, sectionIndex, questionIndex, value = '') {
const newOption = optionTemplate.content.cloneNode(true).firstElementChild;
const optInput = newOption.querySelector('.option-input');
optInput.name = `sections[${sectionIndex}][questions][${questionIndex}][options][]`;
optInput.value = value ?? '';
const addBtn = optionsContainer.querySelector('.add-option-btn');
optionsContainer.insertBefore(newOption, addBtn);
}
function reindexQuestions() {
sectionsContainer.querySelectorAll('.section-block').forEach((section, sIdx) => {
section.dataset.sectionIndex = sIdx;
section.querySelectorAll('.section-title-input').forEach(input => {
input.name = input.name.replace(/sections\[\d+\]/, `sections[${sIdx}]`);
});
section.querySelectorAll('.question-block').forEach((q, qIdx) => {
q.dataset.questionIndex = qIdx;
q.querySelector('.question-title').textContent = `Soalan ${qIdx + 1}`;
// Reindex all inputs including the new checkbox
q.querySelectorAll('[name]').forEach(input => {
input.name = input.name
.replace(/sections\[\d+\]/, `sections[${sIdx}]`)
.replace(/questions\[\d+\]/, `questions[${qIdx}]`);
});
// Reindex dynamic options
q.querySelectorAll('.option-input').forEach((opt, optIdx) => {
opt.name = `sections[${sIdx}][questions][${qIdx}][options][${optIdx}]`;
});
});
});
}
function toggleQuestionType(questionBlock, type) {
const optionsContainer = questionBlock.querySelector('.options-container');
const textPlaceholder = questionBlock.querySelector('.text-placeholder-container');
const otherContainer = questionBlock.querySelector('.allow-other-container');
const otherCheckbox = questionBlock.querySelector('.allow-other-option-checkbox');
if (type === 'text') {
// Text Mode
optionsContainer.style.display = 'none';
textPlaceholder.style.display = 'block';
// Hide 'Other' checkbox and uncheck it
otherContainer.style.display = 'none';
otherCheckbox.checked = false;
// Remove options inputs
optionsContainer.querySelectorAll('.option-input').forEach(i=>i.remove());
} else {
// Radio/Checkbox Mode
optionsContainer.style.display = 'block';
textPlaceholder.style.display = 'none';
// Show 'Other' checkbox
otherContainer.style.display = 'block';
}
}
addSectionBtn.addEventListener('click', () => addSection());
sectionsContainer.addEventListener('click', function (e) {
if (e.target.classList.contains('add-question-btn')) {
const questionsContainer = e.target.closest('.card-body').querySelector('.questions-container');
const sIdx = parseInt(e.target.closest('.section-block').dataset.sectionIndex);
addQuestion(questionsContainer, sIdx);
}
if (e.target.classList.contains('add-option-btn')) {
const optionsContainer = e.target.closest('.options-container');
const qBlock = e.target.closest('.question-block');
const sIdx = parseInt(e.target.closest('.section-block').dataset.sectionIndex);
const qIdx = parseInt(qBlock.dataset.questionIndex);
addOption(optionsContainer, sIdx, qIdx);
}
if (e.target.classList.contains('type-selector')) {
const type = e.target.dataset.type;
const qBlock = e.target.closest('.question-block');
qBlock.querySelectorAll('.type-selector').forEach(btn => btn.classList.remove('active','btn-primary'));
e.target.classList.add('active','btn-primary');
qBlock.querySelector('.question-type-input').value = type;
// Check if we need to add default options when switching from Text to Radio/Checkbox
const optionsContainer = qBlock.querySelector('.options-container');
if (type !== 'text' && optionsContainer.querySelectorAll('.option-group').length === 0) {
const sIdx = parseInt(qBlock.closest('.section-block').dataset.sectionIndex);
const qIdx = parseInt(qBlock.dataset.questionIndex);
addOption(optionsContainer, sIdx, qIdx);
addOption(optionsContainer, sIdx, qIdx);
}
toggleQuestionType(qBlock, type);
reindexQuestions();
}
if (e.target.closest('.remove-section-btn')) {
e.target.closest('.section-block').remove();
reindexQuestions();
}
if (e.target.closest('.remove-question-btn')) {
e.target.closest('.question-block').remove();
reindexQuestions();
}
if (e.target.closest('.remove-option-btn')) {
e.target.closest('.option-group').remove();
reindexQuestions();
}
});
// Form Submission Logic
document.getElementById('survey-builder-form').addEventListener('submit', function () {
sectionsContainer.querySelectorAll('.question-block').forEach(q=>{
const type = q.querySelector('.question-type-input').value;
// Handle Options Clean up
if (type === 'text') {
q.querySelectorAll('.option-input').forEach(i=>i.remove());
} else {
q.querySelectorAll('.option-input').forEach(i => {
if (typeof i.value === 'undefined') i.value = '';
});
}
// Handle Other Option Checkbox
const otherCheckbox = q.querySelector('.allow-other-option-checkbox');
if (otherCheckbox) {
if (otherCheckbox.checked) {
otherCheckbox.value = '1';
} else {
// Remove name attribute so it doesn't send "on" or any value if unchecked
// This relies on the backend treating missing boolean field as false/0
otherCheckbox.removeAttribute('name');
}
}
});
});
// Initial Data Loading
const oldSections = @json(old('sections') ?? null);
const serverSections = @json($sections_with_questions ?? []);
const initialSections = oldSections ?? serverSections;
if (Array.isArray(initialSections) && initialSections.length) {
initialSections.forEach(sec => addSection(sec));
} else {
addSection();
}
});
</script>
@endpush