Improve reset data module safety and UI
This commit is contained in:
@@ -7,11 +7,14 @@ use App\Models\Department;
|
||||
use App\Models\DocumentType;
|
||||
use App\Models\SchoolClass;
|
||||
use App\Models\Student;
|
||||
use App\Models\StudentAchievement;
|
||||
use App\Models\StudentDocument;
|
||||
use App\Models\StudentExit;
|
||||
use App\Models\StudentGraduation;
|
||||
use App\Models\StudentTransfer;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\View\View;
|
||||
@@ -28,6 +31,10 @@ class MaintenanceController extends Controller
|
||||
'student_parents' => 'Orang Tua / Wali',
|
||||
'student_documents' => 'Dokumen Siswa + file storage',
|
||||
'student_academic_histories' => 'Histori Akademik Siswa',
|
||||
'student_achievements' => 'Prestasi Siswa + file storage',
|
||||
'student_exits' => 'Arsip Siswa Keluar + file storage',
|
||||
'student_transfers' => 'Arsip Mutasi Siswa + file storage',
|
||||
'student_graduations' => 'Arsip Kelulusan Siswa + file storage',
|
||||
];
|
||||
|
||||
public function index(): View
|
||||
@@ -42,8 +49,13 @@ class MaintenanceController extends Controller
|
||||
'studentParents' => DB::table('student_parents')->count(),
|
||||
'studentDocuments' => StudentDocument::withTrashed()->count(),
|
||||
'studentAcademicHistories' => DB::table('student_academic_histories')->count(),
|
||||
'studentAchievements' => StudentAchievement::count(),
|
||||
'studentExits' => StudentExit::count(),
|
||||
'studentTransfers' => StudentTransfer::count(),
|
||||
'studentGraduations' => StudentGraduation::count(),
|
||||
],
|
||||
'targets' => $this->availableTargets,
|
||||
'resolvedTargetsPreview' => $this->buildResolvedTargetPreview(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -76,6 +88,52 @@ class MaintenanceController extends Controller
|
||||
$resolvedTargets = $this->resolveTargets($targets->all());
|
||||
|
||||
DB::transaction(function () use ($resolvedTargets) {
|
||||
if (in_array('student_achievements', $resolvedTargets, true)) {
|
||||
StudentAchievement::query()->get()->each(function (StudentAchievement $achievement) {
|
||||
foreach ([$achievement->certificate_file, $achievement->activity_photo] as $path) {
|
||||
if ($path && Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
}
|
||||
});
|
||||
DB::table('student_achievements')->delete();
|
||||
}
|
||||
|
||||
if (in_array('student_exits', $resolvedTargets, true)) {
|
||||
StudentExit::query()->get()->each(function (StudentExit $studentExit) {
|
||||
if ($studentExit->exit_letter_file_path && Storage::disk('public')->exists($studentExit->exit_letter_file_path)) {
|
||||
Storage::disk('public')->delete($studentExit->exit_letter_file_path);
|
||||
}
|
||||
});
|
||||
DB::table('student_exits')->delete();
|
||||
}
|
||||
|
||||
if (in_array('student_transfers', $resolvedTargets, true)) {
|
||||
StudentTransfer::query()->get()->each(function (StudentTransfer $studentTransfer) {
|
||||
if ($studentTransfer->transfer_letter_file_path && Storage::disk('public')->exists($studentTransfer->transfer_letter_file_path)) {
|
||||
Storage::disk('public')->delete($studentTransfer->transfer_letter_file_path);
|
||||
}
|
||||
});
|
||||
DB::table('student_transfers')->delete();
|
||||
}
|
||||
|
||||
if (in_array('student_graduations', $resolvedTargets, true)) {
|
||||
StudentGraduation::query()->get()->each(function (StudentGraduation $graduation) {
|
||||
foreach ([
|
||||
$graduation->skl_file_path,
|
||||
$graduation->ijazah_file_path,
|
||||
$graduation->transcript_file_path,
|
||||
$graduation->ukk_certificate_file_path,
|
||||
$graduation->lsp_certificate_file_path,
|
||||
] as $path) {
|
||||
if ($path && Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
}
|
||||
});
|
||||
DB::table('student_graduations')->delete();
|
||||
}
|
||||
|
||||
if (in_array('students', $resolvedTargets, true)) {
|
||||
Student::withTrashed()->get()->each->forceDelete();
|
||||
}
|
||||
@@ -122,6 +180,22 @@ class MaintenanceController extends Controller
|
||||
Storage::disk('public')->deleteDirectory('student-documents');
|
||||
}
|
||||
|
||||
if (in_array('student_achievements', $resolvedTargets, true)) {
|
||||
Storage::disk('public')->deleteDirectory('student-achievements');
|
||||
}
|
||||
|
||||
if (in_array('student_exits', $resolvedTargets, true)) {
|
||||
Storage::disk('public')->deleteDirectory('student-exit-letters');
|
||||
}
|
||||
|
||||
if (in_array('student_transfers', $resolvedTargets, true)) {
|
||||
Storage::disk('public')->deleteDirectory('student-transfer-letters');
|
||||
}
|
||||
|
||||
if (in_array('student_graduations', $resolvedTargets, true)) {
|
||||
Storage::disk('public')->deleteDirectory('student-graduation-documents');
|
||||
}
|
||||
|
||||
$labels = collect($resolvedTargets)
|
||||
->map(fn ($target) => $this->availableTargets[$target] ?? $target)
|
||||
->implode(', ');
|
||||
@@ -136,11 +210,26 @@ class MaintenanceController extends Controller
|
||||
$resolved = collect($targets);
|
||||
|
||||
if ($resolved->contains('students')) {
|
||||
$resolved = $resolved->merge(['student_documents', 'student_parents', 'student_academic_histories']);
|
||||
$resolved = $resolved->merge([
|
||||
'student_documents',
|
||||
'student_parents',
|
||||
'student_academic_histories',
|
||||
'student_achievements',
|
||||
'student_exits',
|
||||
'student_transfers',
|
||||
'student_graduations',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($resolved->contains('classes')) {
|
||||
$resolved = $resolved->merge(['students', 'student_academic_histories']);
|
||||
$resolved = $resolved->merge([
|
||||
'students',
|
||||
'student_academic_histories',
|
||||
'student_achievements',
|
||||
'student_exits',
|
||||
'student_transfers',
|
||||
'student_graduations',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($resolved->contains('document_types')) {
|
||||
@@ -148,14 +237,46 @@ class MaintenanceController extends Controller
|
||||
}
|
||||
|
||||
if ($resolved->contains('departments')) {
|
||||
$resolved = $resolved->merge(['classes', 'students', 'student_academic_histories']);
|
||||
$resolved = $resolved->merge([
|
||||
'classes',
|
||||
'students',
|
||||
'student_academic_histories',
|
||||
'student_achievements',
|
||||
'student_exits',
|
||||
'student_transfers',
|
||||
'student_graduations',
|
||||
]);
|
||||
}
|
||||
|
||||
if ($resolved->contains('academic_years')) {
|
||||
$resolved = $resolved->merge(['classes', 'students', 'student_academic_histories']);
|
||||
$resolved = $resolved->merge([
|
||||
'classes',
|
||||
'students',
|
||||
'student_academic_histories',
|
||||
'student_achievements',
|
||||
'student_exits',
|
||||
'student_transfers',
|
||||
'student_graduations',
|
||||
]);
|
||||
}
|
||||
|
||||
return $resolved->unique()->values()->all();
|
||||
}
|
||||
|
||||
protected function buildResolvedTargetPreview(): array
|
||||
{
|
||||
return collect(array_keys($this->availableTargets))
|
||||
->mapWithKeys(function (string $target) {
|
||||
$resolved = $this->resolveTargets([$target]);
|
||||
|
||||
return [
|
||||
$target => collect($resolved)
|
||||
->map(fn (string $resolvedTarget) => $this->availableTargets[$resolvedTarget] ?? $resolvedTarget)
|
||||
->values()
|
||||
->all(),
|
||||
];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,78 +1,245 @@
|
||||
<x-master-page title="Kosongkan Data Aplikasi" description="Pilih kelompok data yang ingin dikosongkan. Sistem akan menyesuaikan dependensi relasi agar proses aman dari konflik foreign key.">
|
||||
<x-master-card title="Reset Data Selektif" subtitle="Gunakan fitur ini dengan hati-hati. Data yang dikosongkan dari menu ini tidak bisa dipulihkan dari UI.">
|
||||
<div class="rounded-3xl border border-amber-200 bg-amber-50 px-5 py-4 text-sm text-amber-700">
|
||||
<div class="font-semibold">Catatan penting:</div>
|
||||
<ul class="mt-3 list-disc space-y-1 pl-5">
|
||||
<li>Mas bisa memilih tabel/kelompok data tertentu saja.</li>
|
||||
<li>Backend akan menambahkan dependensi yang wajib ikut dibersihkan supaya tidak bentrok foreign key.</li>
|
||||
<li>Untuk dokumen siswa, file fisik di storage juga ikut dibersihkan.</li>
|
||||
<li>Konfirmasi memakai password admin yang sedang login.</li>
|
||||
</ul>
|
||||
@php
|
||||
$statCards = [
|
||||
['label' => 'Tahun', 'value' => $stats['academicYears'], 'icon' => 'fa-solid fa-calendar-days', 'accent' => 'slate'],
|
||||
['label' => 'Jurusan', 'value' => $stats['departments'], 'icon' => 'fa-solid fa-layer-group', 'accent' => 'indigo'],
|
||||
['label' => 'Kelas', 'value' => $stats['classes'], 'icon' => 'fa-solid fa-school', 'accent' => 'sky'],
|
||||
['label' => 'Jenis Dokumen', 'value' => $stats['documentTypes'], 'icon' => 'fa-solid fa-file-lines', 'accent' => 'violet'],
|
||||
['label' => 'Siswa', 'value' => $stats['students'], 'icon' => 'fa-solid fa-user-graduate', 'accent' => 'rose'],
|
||||
['label' => 'Ortu/Wali', 'value' => $stats['studentParents'], 'icon' => 'fa-solid fa-people-roof', 'accent' => 'amber'],
|
||||
['label' => 'Dokumen', 'value' => $stats['studentDocuments'], 'icon' => 'fa-solid fa-folder-open', 'accent' => 'emerald'],
|
||||
['label' => 'Histori', 'value' => $stats['studentAcademicHistories'], 'icon' => 'fa-solid fa-clock-rotate-left', 'accent' => 'cyan'],
|
||||
['label' => 'Prestasi', 'value' => $stats['studentAchievements'], 'icon' => 'fa-solid fa-trophy', 'accent' => 'yellow'],
|
||||
['label' => 'Keluar', 'value' => $stats['studentExits'], 'icon' => 'fa-solid fa-right-from-bracket', 'accent' => 'orange'],
|
||||
['label' => 'Mutasi', 'value' => $stats['studentTransfers'], 'icon' => 'fa-solid fa-arrow-right-arrow-left', 'accent' => 'fuchsia'],
|
||||
['label' => 'Kelulusan', 'value' => $stats['studentGraduations'], 'icon' => 'fa-solid fa-award', 'accent' => 'teal'],
|
||||
];
|
||||
|
||||
$accentClasses = [
|
||||
'slate' => 'border-slate-200 bg-gradient-to-br from-white via-slate-50 to-slate-100/70 text-slate-700 shadow-slate-200/50 ring-slate-100',
|
||||
'indigo' => 'border-indigo-200 bg-gradient-to-br from-white via-indigo-50/60 to-indigo-100/60 text-indigo-700 shadow-indigo-100/50 ring-indigo-100',
|
||||
'sky' => 'border-sky-200 bg-gradient-to-br from-white via-sky-50/60 to-sky-100/60 text-sky-700 shadow-sky-100/50 ring-sky-100',
|
||||
'violet' => 'border-violet-200 bg-gradient-to-br from-white via-violet-50/60 to-violet-100/60 text-violet-700 shadow-violet-100/50 ring-violet-100',
|
||||
'rose' => 'border-rose-200 bg-gradient-to-br from-white via-rose-50/60 to-rose-100/60 text-rose-700 shadow-rose-100/50 ring-rose-100',
|
||||
'amber' => 'border-amber-200 bg-gradient-to-br from-white via-amber-50/60 to-amber-100/60 text-amber-700 shadow-amber-100/50 ring-amber-100',
|
||||
'emerald' => 'border-emerald-200 bg-gradient-to-br from-white via-emerald-50/60 to-emerald-100/60 text-emerald-700 shadow-emerald-100/50 ring-emerald-100',
|
||||
'cyan' => 'border-cyan-200 bg-gradient-to-br from-white via-cyan-50/60 to-cyan-100/60 text-cyan-700 shadow-cyan-100/50 ring-cyan-100',
|
||||
'yellow' => 'border-yellow-200 bg-gradient-to-br from-white via-yellow-50/60 to-yellow-100/60 text-yellow-700 shadow-yellow-100/50 ring-yellow-100',
|
||||
'orange' => 'border-orange-200 bg-gradient-to-br from-white via-orange-50/60 to-orange-100/60 text-orange-700 shadow-orange-100/50 ring-orange-100',
|
||||
'fuchsia' => 'border-fuchsia-200 bg-gradient-to-br from-white via-fuchsia-50/60 to-fuchsia-100/60 text-fuchsia-700 shadow-fuchsia-100/50 ring-fuchsia-100',
|
||||
'teal' => 'border-teal-200 bg-gradient-to-br from-white via-teal-50/60 to-teal-100/60 text-teal-700 shadow-teal-100/50 ring-teal-100',
|
||||
];
|
||||
|
||||
$selectedTargets = old('targets', []);
|
||||
@endphp
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="rounded-3xl border border-rose-200 bg-gradient-to-r from-rose-50 via-white to-amber-50 p-6 shadow-sm shadow-rose-100/40 ring-1 ring-white/70">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div class="max-w-3xl">
|
||||
<div class="inline-flex items-center gap-2 rounded-full bg-rose-100 px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-rose-700">
|
||||
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||
Zona Sensitif
|
||||
</div>
|
||||
<h2 class="mt-3 text-2xl font-bold text-slate-900">Reset data selektif untuk bersih-bersih aplikasi</h2>
|
||||
<p class="mt-2 text-sm leading-6 text-slate-600">Gunakan halaman ini saat Mas memang ingin mengosongkan data tertentu tanpa mengutak-atik struktur aplikasi. Sistem akan menambahkan dependensi penting agar proses hapus tidak mentok foreign key dan file storage terkait ikut dibersihkan.</p>
|
||||
</div>
|
||||
<div class="grid min-w-[220px] grid-cols-1 gap-3 sm:grid-cols-2 lg:w-[280px] lg:grid-cols-1">
|
||||
<div class="rounded-2xl border border-white/70 bg-white/90 px-4 py-3 shadow-sm ring-1 ring-rose-100/70 backdrop-blur-sm">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Target Tersedia</div>
|
||||
<div class="mt-2 text-2xl font-bold text-slate-900">{{ count($targets) }}</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-white/70 bg-white/90 px-4 py-3 shadow-sm ring-1 ring-amber-100/70 backdrop-blur-sm">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Perlu Konfirmasi</div>
|
||||
<div class="mt-2 text-sm font-semibold text-amber-700">Password admin aktif</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid grid-cols-1 gap-4 md:grid-cols-4 xl:grid-cols-8">
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600"><div class="text-xs uppercase tracking-[0.14em] text-slate-400">Tahun</div><div class="mt-1 text-2xl font-bold text-slate-800">{{ $stats['academicYears'] }}</div></div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600"><div class="text-xs uppercase tracking-[0.14em] text-slate-400">Jurusan</div><div class="mt-1 text-2xl font-bold text-slate-800">{{ $stats['departments'] }}</div></div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600"><div class="text-xs uppercase tracking-[0.14em] text-slate-400">Kelas</div><div class="mt-1 text-2xl font-bold text-slate-800">{{ $stats['classes'] }}</div></div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600"><div class="text-xs uppercase tracking-[0.14em] text-slate-400">Jenis Dokumen</div><div class="mt-1 text-2xl font-bold text-slate-800">{{ $stats['documentTypes'] }}</div></div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600"><div class="text-xs uppercase tracking-[0.14em] text-slate-400">Siswa</div><div class="mt-1 text-2xl font-bold text-slate-800">{{ $stats['students'] }}</div></div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600"><div class="text-xs uppercase tracking-[0.14em] text-slate-400">Ortu/Wali</div><div class="mt-1 text-2xl font-bold text-slate-800">{{ $stats['studentParents'] }}</div></div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600"><div class="text-xs uppercase tracking-[0.14em] text-slate-400">Dokumen</div><div class="mt-1 text-2xl font-bold text-slate-800">{{ $stats['studentDocuments'] }}</div></div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600"><div class="text-xs uppercase tracking-[0.14em] text-slate-400">Histori</div><div class="mt-1 text-2xl font-bold text-slate-800">{{ $stats['studentAcademicHistories'] }}</div></div>
|
||||
<div class="mt-5 grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4">
|
||||
<div class="rounded-2xl border border-amber-200 bg-amber-50/90 px-4 py-4 text-sm text-amber-800 shadow-sm shadow-amber-100/40">
|
||||
<div class="font-semibold">Aman dari relasi putus</div>
|
||||
<div class="mt-1 text-xs leading-5 text-amber-700">Dependensi wajib dibersihkan otomatis supaya data yatim tidak tertinggal.</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-rose-200 bg-rose-50/90 px-4 py-4 text-sm text-rose-800 shadow-sm shadow-rose-100/40">
|
||||
<div class="font-semibold">Tidak ada undo dari UI</div>
|
||||
<div class="mt-1 text-xs leading-5 text-rose-700">Kalau sudah diproses, pemulihan harus lewat backup atau input ulang data.</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-emerald-200 bg-emerald-50/90 px-4 py-4 text-sm text-emerald-800 shadow-sm shadow-emerald-100/40">
|
||||
<div class="font-semibold">Storage ikut dibersihkan</div>
|
||||
<div class="mt-1 text-xs leading-5 text-emerald-700">Dokumen dan lampiran terkait target reset akan ikut dihapus dari disk publik.</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-sky-200 bg-sky-50/90 px-4 py-4 text-sm text-sky-800 shadow-sm shadow-sky-100/40">
|
||||
<div class="font-semibold">Pilih seperlunya</div>
|
||||
<div class="mt-1 text-xs leading-5 text-sky-700">Mas bisa kosongkan satu area saja tanpa harus reset semuanya sekaligus.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('settings.reset-data.execute') }}" class="mt-6 space-y-6">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-3 xl:grid-cols-6">
|
||||
@foreach ($statCards as $card)
|
||||
@php
|
||||
$cardClasses = $accentClasses[$card['accent']] ?? $accentClasses['slate'];
|
||||
@endphp
|
||||
<div class="group rounded-3xl border p-5 shadow-sm ring-1 ring-white/70 transition hover:-translate-y-0.5 hover:shadow-lg {{ $cardClasses }}">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-xs font-semibold uppercase tracking-[0.16em] opacity-75">{{ $card['label'] }}</div>
|
||||
<div class="mt-3 text-3xl font-bold text-slate-900">{{ $card['value'] }}</div>
|
||||
</div>
|
||||
<div class="rounded-2xl bg-white/85 p-3 text-base shadow-sm ring-1 backdrop-blur-sm {{ $accentClasses[$card['accent']] ?? '' }}">
|
||||
<i class="{{ $card['icon'] }}"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<x-master-card title="Pilih Kelompok Data" subtitle="Centang area yang ingin dikosongkan. Keterangan di tiap kartu menjelaskan dampak dan dependensi yang ikut dibersihkan.">
|
||||
<form method="POST" action="{{ route('settings.reset-data.execute') }}" class="space-y-6" x-data="{ selected: @js($selectedTargets) }">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<div class="mb-3 text-sm font-medium text-slate-700">Pilih data yang ingin dikosongkan</div>
|
||||
<div class="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div class="flex flex-col gap-4 rounded-3xl border border-slate-200 bg-gradient-to-br from-white via-slate-50 to-rose-50/20 p-4 shadow-sm ring-1 ring-white/70 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div class="max-w-2xl">
|
||||
<div class="text-sm font-semibold text-slate-800">Pilih target reset</div>
|
||||
<div class="mt-1 text-xs leading-5 text-slate-500">Gunakan pilihan ini dengan hati-hati. Beberapa target akan menarik dependensi tambahan secara otomatis.</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2 lg:justify-end">
|
||||
<button
|
||||
type="button"
|
||||
@click="selected = @js(array_keys($targets))"
|
||||
class="inline-flex min-h-[44px] items-center gap-2 rounded-2xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 transition hover:bg-slate-50"
|
||||
>
|
||||
<i class="fa-solid fa-check-double"></i>
|
||||
<span>Pilih Semua</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="selected = []"
|
||||
class="inline-flex min-h-[44px] items-center gap-2 rounded-2xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 transition hover:bg-slate-50"
|
||||
>
|
||||
<i class="fa-solid fa-eraser"></i>
|
||||
<span>Kosongkan Pilihan</span>
|
||||
</button>
|
||||
<div class="inline-flex min-h-[44px] items-center gap-2 rounded-2xl bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white shadow-sm">
|
||||
<i class="fa-solid fa-list-check"></i>
|
||||
<span x-text="selected.length + ' dipilih'"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
@foreach ($targets as $key => $label)
|
||||
<label class="flex items-start gap-3 rounded-2xl border border-slate-200 bg-white px-4 py-3 transition hover:border-rose-200 hover:bg-rose-50/40">
|
||||
<input type="checkbox" name="targets[]" value="{{ $key }}" class="mt-1 rounded border-slate-300 text-rose-600 focus:ring-rose-500" @checked(in_array($key, old('targets', []), true))>
|
||||
@php
|
||||
$isSelected = in_array($key, $selectedTargets, true);
|
||||
@endphp
|
||||
<label
|
||||
class="group relative flex cursor-pointer items-start gap-4 rounded-3xl border bg-white p-5 shadow-sm shadow-slate-200/40 ring-1 ring-white/70 transition hover:-translate-y-0.5 hover:shadow-lg"
|
||||
:class="selected.includes('{{ $key }}') ? 'border-rose-300 bg-rose-50/40 shadow-rose-100/60 ring-rose-100' : 'border-slate-200 hover:border-rose-200'"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="targets[]"
|
||||
value="{{ $key }}"
|
||||
x-model="selected"
|
||||
class="mt-1 rounded border-slate-300 text-rose-600 focus:ring-rose-500"
|
||||
@checked($isSelected)
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-sm font-semibold text-slate-800">{{ $label }}</div>
|
||||
<div class="mt-1 text-xs text-slate-500">
|
||||
@switch($key)
|
||||
@case('students')
|
||||
Menghapus data siswa; sistem otomatis ikut membersihkan dokumen, orang tua/wali, dan histori akademik.
|
||||
Menghapus data siswa; sistem otomatis ikut membersihkan dokumen, orang tua/wali, histori akademik, prestasi, arsip keluar, mutasi, dan kelulusan.
|
||||
@break
|
||||
@case('classes')
|
||||
Menghapus master kelas; sistem akan ikut memaksa pembersihan data siswa dan histori yang bergantung.
|
||||
Menghapus master kelas; sistem akan ikut memaksa pembersihan data siswa dan seluruh data turunan yang bergantung.
|
||||
@break
|
||||
@case('document_types')
|
||||
Menghapus master jenis dokumen; sistem akan ikut membersihkan dokumen siswa yang masih memakai jenis dokumen.
|
||||
@break
|
||||
@case('student_achievements')
|
||||
Menghapus data prestasi siswa beserta file sertifikat dan foto kegiatan yang tersimpan.
|
||||
@break
|
||||
@case('student_exits')
|
||||
Menghapus arsip siswa keluar beserta file surat keluar yang tersimpan.
|
||||
@break
|
||||
@case('student_transfers')
|
||||
Menghapus arsip mutasi siswa beserta file surat mutasi yang tersimpan.
|
||||
@break
|
||||
@case('student_graduations')
|
||||
Menghapus arsip kelulusan siswa beserta dokumen kelulusan yang tersimpan.
|
||||
@break
|
||||
@default
|
||||
Data pada kelompok ini akan dikosongkan sesuai relasi yang diperlukan.
|
||||
@endswitch
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<span
|
||||
class="inline-flex items-center rounded-full px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.14em]"
|
||||
:class="selected.includes('{{ $key }}') ? 'bg-rose-100 text-rose-700' : 'bg-slate-100 text-slate-500'"
|
||||
>
|
||||
<span x-text="selected.includes('{{ $key }}') ? 'Dipilih' : 'Opsional'"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if (!empty($resolvedTargetsPreview[$key]))
|
||||
<div class="mt-3 rounded-2xl border border-slate-200 bg-slate-50/90 px-4 py-3 text-[11px] text-slate-600">
|
||||
<div class="font-semibold uppercase tracking-[0.14em] text-slate-500">Ikut dibersihkan</div>
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
@foreach ($resolvedTargetsPreview[$key] as $resolvedLabel)
|
||||
<span class="rounded-full bg-white px-3 py-1 font-medium text-slate-600 ring-1 ring-slate-200">{{ $resolvedLabel }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="admin_password" class="mb-2 block text-sm font-medium text-slate-700">Password Admin</label>
|
||||
<div class="grid grid-cols-1 gap-6 xl:grid-cols-[1.15fr_0.85fr]">
|
||||
<div class="rounded-3xl border border-slate-200 bg-gradient-to-br from-white via-slate-50 to-indigo-50/20 p-5 shadow-sm ring-1 ring-white/70">
|
||||
<label for="admin_password" class="mb-2 block text-sm font-semibold text-slate-800">Password Admin</label>
|
||||
<div class="text-xs text-slate-500">Masukkan password akun admin yang sedang login untuk konfirmasi akhir sebelum proses reset dijalankan.</div>
|
||||
<div class="mt-4 relative">
|
||||
<span class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-4 text-slate-400">
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
</span>
|
||||
<input
|
||||
id="admin_password"
|
||||
name="admin_password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
class="w-full rounded-2xl border border-slate-200 bg-white px-4 py-3 text-sm text-slate-700 placeholder:text-slate-400 focus:border-rose-300 focus:outline-none focus:ring-2 focus:ring-rose-100"
|
||||
class="w-full min-h-[48px] rounded-2xl border border-slate-200 bg-white py-3 pl-12 pr-4 text-sm text-slate-700 placeholder:text-slate-400 focus:border-rose-300 focus:outline-none focus:ring-2 focus:ring-rose-100"
|
||||
placeholder="Masukkan password admin untuk konfirmasi"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<button type="submit" class="inline-flex items-center rounded-2xl bg-rose-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-rose-200 transition hover:bg-rose-700">
|
||||
Proses Kosongkan Data
|
||||
<div class="rounded-3xl border border-rose-200 bg-gradient-to-br from-rose-50 via-white to-rose-100/60 p-5 shadow-sm shadow-rose-100/40 ring-1 ring-white/70">
|
||||
<div class="text-sm font-semibold text-slate-800">Konfirmasi sebelum eksekusi</div>
|
||||
<ul class="mt-3 space-y-2 text-xs leading-5 text-slate-600">
|
||||
<li class="flex gap-2"><i class="fa-solid fa-check mt-0.5 text-rose-500"></i><span>Pastikan target yang dipilih memang ingin dikosongkan sekarang.</span></li>
|
||||
<li class="flex gap-2"><i class="fa-solid fa-check mt-0.5 text-rose-500"></i><span>Kalau perlu rollback, siapkan backup database dan storage lebih dulu.</span></li>
|
||||
<li class="flex gap-2"><i class="fa-solid fa-check mt-0.5 text-rose-500"></i><span>File dokumen/lampiran yang terkait juga akan hilang dari storage publik.</span></li>
|
||||
</ul>
|
||||
|
||||
<div class="mt-5 flex flex-wrap items-center gap-3">
|
||||
<button type="submit" onclick="this.disabled=true; this.innerText='Memproses...'; this.form.submit();" class="inline-flex min-h-[48px] items-center gap-2 rounded-2xl bg-rose-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-rose-200 transition hover:bg-rose-700 disabled:cursor-not-allowed disabled:opacity-70">
|
||||
<i class="fa-solid fa-trash-can"></i>
|
||||
<span>Proses Kosongkan Data</span>
|
||||
</button>
|
||||
<a href="{{ route('dashboard') }}" class="rounded-2xl border border-slate-200 px-5 py-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-50">
|
||||
Batal
|
||||
<a href="{{ route('dashboard') }}" class="inline-flex min-h-[48px] items-center gap-2 rounded-2xl border border-slate-200 bg-white px-5 py-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-50">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
<span>Batal</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</x-master-card>
|
||||
</div>
|
||||
</x-master-page>
|
||||
|
||||
Reference in New Issue
Block a user