Initial commit

This commit is contained in:
2026-04-12 06:24:30 +00:00
commit 007f107be6
213 changed files with 25576 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
<div
x-data="studentFormState()"
x-init="init()"
>
<script>
function studentFormState() {
return {
selectedAcademicYear: @js((string) old('academic_year_id', $student->academic_year_id ?? '')),
selectedDepartment: @js((string) old('department_id', $student->department_id ?? '')),
selectedClass: @js((string) old('class_id', $student->class_id ?? '')),
selectedProvince: @js((string) old('province_id', $student->province_id ?? '')),
selectedRegency: @js((string) old('regency_id', $student->regency_id ?? '')),
selectedDistrict: @js((string) old('district_id', $student->district_id ?? '')),
selectedVillage: @js((string) old('village_id', $student->village_id ?? '')),
classOptions: @js($classOptions),
departments: @js(collect($departments)->map(fn ($name, $id) => ['id' => (string) $id, 'name' => $name])->values()),
regencies: [],
districts: [],
villages: [],
loadingRegencies: false,
loadingDistricts: false,
loadingVillages: false,
get filteredDepartments() {
if (!this.selectedAcademicYear) {
return [];
}
const departmentIds = [...new Set(
this.classOptions
.filter(item => String(item.academic_year_id) === String(this.selectedAcademicYear))
.map(item => String(item.department_id))
)];
return this.departments.filter(item => departmentIds.includes(String(item.id)));
},
get filteredClasses() {
if (!this.selectedAcademicYear || !this.selectedDepartment) {
return [];
}
return this.classOptions.filter(item => {
const matchAcademicYear = String(item.academic_year_id) === String(this.selectedAcademicYear);
const matchDepartment = String(item.department_id) === String(this.selectedDepartment);
return matchAcademicYear && matchDepartment;
});
},
get departmentPlaceholder() {
if (!this.selectedAcademicYear) {
return 'Pilih Tahun Angkatan dulu';
}
if (this.filteredDepartments.length === 0) {
return 'Belum ada Program Keahlian untuk Tahun Angkatan ini';
}
return '-- Pilih Program Keahlian --';
},
get classPlaceholder() {
if (!this.selectedAcademicYear) {
return 'Pilih Tahun Angkatan dulu';
}
if (!this.selectedDepartment) {
return 'Pilih Program Keahlian dulu';
}
if (this.filteredClasses.length === 0) {
return 'Belum ada Kelas untuk kombinasi ini';
}
return '-- Pilih Kelas --';
},
syncDepartmentSelection() {
if (!this.selectedAcademicYear) {
this.selectedDepartment = '';
this.selectedClass = '';
return;
}
if (!this.selectedDepartment) {
this.selectedClass = '';
return;
}
const exists = this.filteredDepartments.some(item => String(item.id) === String(this.selectedDepartment));
if (!exists) {
this.selectedDepartment = '';
this.selectedClass = '';
}
},
syncClassSelection() {
this.syncDepartmentSelection();
if (!this.selectedClass) return;
const exists = this.filteredClasses.some(item => String(item.id) === String(this.selectedClass));
if (!exists) this.selectedClass = '';
},
async init() {
this.syncDepartmentSelection();
this.syncClassSelection();
if (this.selectedProvince) await this.loadRegencies(false);
if (this.selectedRegency) await this.loadDistricts(false);
if (this.selectedDistrict) await this.loadVillages(false);
},
async loadRegencies(reset = true) {
if (reset) {
this.selectedRegency = '';
this.selectedDistrict = '';
this.selectedVillage = '';
this.districts = [];
this.villages = [];
}
this.regencies = [];
if (!this.selectedProvince) return;
this.loadingRegencies = true;
const response = await fetch(`/regions/provinces/${this.selectedProvince}/regencies`);
this.regencies = await response.json();
this.loadingRegencies = false;
},
async loadDistricts(reset = true) {
if (reset) {
this.selectedDistrict = '';
this.selectedVillage = '';
this.villages = [];
}
this.districts = [];
if (!this.selectedRegency) return;
this.loadingDistricts = true;
const response = await fetch(`/regions/regencies/${this.selectedRegency}/districts`);
this.districts = await response.json();
this.loadingDistricts = false;
},
async loadVillages(reset = true) {
if (reset) this.selectedVillage = '';
this.villages = [];
if (!this.selectedDistrict) return;
this.loadingVillages = true;
const response = await fetch(`/regions/districts/${this.selectedDistrict}/villages`);
this.villages = await response.json();
this.loadingVillages = false;
}
}
}
</script>
<div class="grid grid-cols-1 gap-6 xl:grid-cols-2">
<x-master-card title="Identitas Siswa" subtitle="Data utama identitas dan akademik siswa.">
<div class="grid grid-cols-1 gap-5 md:grid-cols-2">
<x-form-input name="student_number" label="NIS" :value="$student->student_number ?? ''" placeholder="Nomor induk siswa" />
<x-form-input name="nisn" label="NISN" :value="$student->nisn ?? ''" placeholder="Nomor induk siswa nasional" />
<div class="md:col-span-2">
<x-form-input name="full_name" label="Nama Lengkap" :value="$student->full_name ?? ''" placeholder="Nama lengkap siswa" required />
</div>
<x-form-select name="gender" label="Jenis Kelamin" :options="$genders" :value="$student->gender ?? null" required />
<x-form-select name="status" label="Status Siswa" :options="$statuses" :value="$student->status ?? 'active'" x-ref="statusField" x-on:change="statusValue = $event.target.value" required />
<x-form-input name="place_of_birth" label="Tempat Lahir" :value="$student->place_of_birth ?? ''" placeholder="Kota kelahiran" />
<x-form-input name="date_of_birth" label="Tanggal Lahir" type="date" :value="isset($student?->date_of_birth) ? $student->date_of_birth?->format('Y-m-d') : ''" />
<x-form-select name="religion" label="Agama" :options="$religions" :value="$student->religion ?? null" />
<x-form-input name="nationality" label="Kewarganegaraan" :value="$student->nationality ?? 'Indonesia'" placeholder="Contoh: Indonesia" />
<x-form-input name="family_card_number" label="No. Kartu Keluarga" :value="$student->family_card_number ?? ''" placeholder="Nomor KK" />
<x-form-input name="entry_date" label="Tanggal Masuk" type="date" :value="isset($student?->entry_date) ? $student->entry_date?->format('Y-m-d') : ''" />
<x-form-input name="child_order" label="Anak Ke" type="number" :value="$student->child_order ?? ''" placeholder="Contoh: 1" />
<x-form-input name="number_of_siblings" label="Jumlah Saudara" type="number" :value="$student->number_of_siblings ?? ''" placeholder="Contoh: 2" />
</div>
</x-master-card>
<x-master-card title="Akademik & Kontak" subtitle="Relasi akademik, alamat, dan informasi kontak siswa.">
<div class="grid grid-cols-1 gap-5 md:grid-cols-2">
<x-form-select name="academic_year_id" label="Tahun Angkatan" :options="$academicYears->all()" :value="$student->academic_year_id ?? null" x-model="selectedAcademicYear" x-on:change="syncDepartmentSelection(); syncClassSelection()" required />
<div>
<label for="department_id" class="mb-2 block text-sm font-medium text-slate-700">Program Keahlian</label>
<select id="department_id" name="department_id" x-model="selectedDepartment" x-on:change="syncClassSelection()" class="h-12 w-full rounded-2xl border border-slate-200 bg-white px-4 text-sm text-slate-700 focus:border-indigo-300 focus:outline-none focus:ring-4 focus:ring-indigo-100 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:text-slate-400" :disabled="!selectedAcademicYear || filteredDepartments.length === 0" required>
<option value="" x-text="departmentPlaceholder"></option>
<template x-for="department in filteredDepartments" :key="department.id">
<option :value="department.id" x-text="department.name"></option>
</template>
</select>
<p class="mt-2 text-xs text-slate-500" x-text="!selectedAcademicYear ? 'Pilih Tahun Angkatan terlebih dahulu untuk membuka daftar Program Keahlian.' : (filteredDepartments.length === 0 ? 'Belum ada kelas yang menghubungkan Tahun Angkatan ini dengan Program Keahlian mana pun.' : 'Program Keahlian difilter otomatis berdasarkan Tahun Angkatan yang dipilih.')"></p>
<x-input-error :messages="$errors->get('department_id')" class="mt-2" />
</div>
<div>
<label for="class_id" class="mb-2 block text-sm font-medium text-slate-700">Kelas</label>
<select id="class_id" name="class_id" x-model="selectedClass" class="h-12 w-full rounded-2xl border border-slate-200 bg-white px-4 text-sm text-slate-700 focus:border-indigo-300 focus:outline-none focus:ring-4 focus:ring-indigo-100 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:text-slate-400" :disabled="!selectedAcademicYear || !selectedDepartment || filteredClasses.length === 0" required>
<option value="" x-text="classPlaceholder"></option>
<template x-for="classOption in filteredClasses" :key="classOption.id">
<option :value="classOption.id" x-text="classOption.name"></option>
</template>
</select>
<p class="mt-2 text-xs text-slate-500" x-text="!selectedAcademicYear ? 'Pilih Tahun Angkatan terlebih dahulu.' : (!selectedDepartment ? 'Pilih Program Keahlian terlebih dahulu.' : (filteredClasses.length === 0 ? 'Belum ada kelas aktif untuk kombinasi Tahun Angkatan dan Program Keahlian ini.' : 'Hanya kelas yang cocok dengan kombinasi pilihan yang ditampilkan.'))"></p>
<x-input-error :messages="$errors->get('class_id')" class="mt-2" />
</div>
<x-form-input name="phone_number" label="No. HP" :value="$student->phone_number ?? ''" placeholder="Nomor HP siswa" />
<x-form-input name="email" label="Email" type="email" :value="$student->email ?? ''" placeholder="Alamat email" />
<div>
<label for="province_id" class="mb-2 block text-sm font-medium text-slate-700">Provinsi</label>
<select id="province_id" name="province_id" x-model="selectedProvince" @change="loadRegencies()" class="h-12 w-full rounded-2xl border border-slate-200 bg-white px-4 text-sm text-slate-700 focus:border-indigo-300 focus:outline-none focus:ring-4 focus:ring-indigo-100">
<option value="">-- Pilih Provinsi --</option>
@foreach($provinces as $id => $name)
<option value="{{ $id }}">{{ $name }}</option>
@endforeach
</select>
</div>
<div>
<label for="regency_id" class="mb-2 block text-sm font-medium text-slate-700">Kota/Kabupaten</label>
<select id="regency_id" name="regency_id" x-model="selectedRegency" @change="loadDistricts()" class="h-12 w-full rounded-2xl border border-slate-200 bg-white px-4 text-sm text-slate-700 focus:border-indigo-300 focus:outline-none focus:ring-4 focus:ring-indigo-100">
<option value="" x-text="loadingRegencies ? 'Memuat...' : '-- Pilih Kota/Kabupaten --'"></option>
<template x-for="regency in regencies" :key="regency.id">
<option :value="regency.id" x-text="regency.name"></option>
</template>
</select>
</div>
<div>
<label for="district_id" class="mb-2 block text-sm font-medium text-slate-700">Kecamatan</label>
<select id="district_id" name="district_id" x-model="selectedDistrict" @change="loadVillages()" class="h-12 w-full rounded-2xl border border-slate-200 bg-white px-4 text-sm text-slate-700 focus:border-indigo-300 focus:outline-none focus:ring-4 focus:ring-indigo-100">
<option value="" x-text="loadingDistricts ? 'Memuat...' : '-- Pilih Kecamatan --'"></option>
<template x-for="district in districts" :key="district.id">
<option :value="district.id" x-text="district.name"></option>
</template>
</select>
</div>
<div>
<label for="village_id" class="mb-2 block text-sm font-medium text-slate-700">Desa/Kelurahan</label>
<select id="village_id" name="village_id" x-model="selectedVillage" class="h-12 w-full rounded-2xl border border-slate-200 bg-white px-4 text-sm text-slate-700 focus:border-indigo-300 focus:outline-none focus:ring-4 focus:ring-indigo-100">
<option value="" x-text="loadingVillages ? 'Memuat...' : '-- Pilih Desa/Kelurahan --'"></option>
<template x-for="village in villages" :key="village.id">
<option :value="village.id" x-text="village.name"></option>
</template>
</select>
</div>
<x-form-input name="postal_code" label="Kode Pos" :value="$student->postal_code ?? ''" placeholder="Kode pos" />
<div class="md:col-span-2">
<x-form-textarea name="address" label="Alamat" :value="$student->address ?? ''" placeholder="Alamat lengkap siswa" rows="4" />
</div>
<div class="md:col-span-2">
<x-form-textarea name="notes" label="Catatan" :value="$student->notes ?? ''" placeholder="Catatan tambahan terkait siswa" rows="4" />
</div>
</div>
</x-master-card>
</div>
<div class="mt-6 flex flex-wrap items-center gap-3">
<button type="submit" class="rounded-2xl bg-indigo-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-indigo-200 transition hover:bg-indigo-700">{{ $submitLabel }}</button>
<a href="{{ route('students.index') }}" 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>
</div>
</div>
@@ -0,0 +1,6 @@
<x-master-page title="Tambah Siswa" description="Tambahkan data siswa baru ke dalam sistem.">
<form action="{{ route('students.store') }}" method="POST" enctype="multipart/form-data">
@csrf
@include('students._form', ['submitLabel' => 'Simpan'])
</form>
</x-master-page>
+7
View File
@@ -0,0 +1,7 @@
<x-master-page title="Edit Siswa" description="Perbarui data siswa yang sudah tersimpan.">
<form action="{{ route('students.update', $student) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
@include('students._form', ['submitLabel' => 'Update'])
</form>
</x-master-page>
+176
View File
@@ -0,0 +1,176 @@
<x-master-page title="Import Data Siswa" description="Import data siswa esensial dari file Excel/CSV. Gunakan template agar format kolom sesuai.">
<div class="grid grid-cols-1 gap-6 xl:grid-cols-3">
<div class="xl:col-span-2 space-y-6">
<x-master-card title="Upload File Import" subtitle="Kolom yang diimport hanya data esensial siswa. Data lain bisa dilengkapi belakangan dari form detail siswa.">
<form method="POST" action="{{ route('students.import.preview') }}" enctype="multipart/form-data" class="space-y-6">
@csrf
<div>
<label for="import_file" class="mb-2 block text-sm font-medium text-slate-700">File Excel / CSV</label>
<input id="import_file" name="import_file" type="file" accept=".xlsx,.csv,.txt" class="block w-full rounded-2xl border border-slate-200 bg-white px-4 py-3 text-sm text-slate-700 file:mr-4 file:rounded-xl file:border-0 file:bg-indigo-50 file:px-4 file:py-2 file:font-semibold file:text-indigo-700 hover:file:bg-indigo-100">
<p class="mt-2 text-xs text-slate-500">Format didukung: <strong>.xlsx</strong> dan <strong>.csv</strong>. Untuk paling aman, pakai template yang disediakan lalu isi di Excel.</p>
</div>
<div class="flex flex-wrap items-center gap-3">
<button type="submit" class="inline-flex items-center rounded-2xl bg-indigo-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-indigo-200 transition hover:bg-indigo-700">
Preview Import
</button>
<a href="{{ route('students.import.template') }}" class="inline-flex items-center rounded-2xl border border-emerald-200 bg-emerald-50 px-5 py-3 text-sm font-semibold text-emerald-700 transition hover:bg-emerald-100">
Download Template
</a>
<a href="{{ route('students.index') }}" class="inline-flex items-center rounded-2xl border border-slate-200 px-5 py-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-50">
Kembali ke Data Siswa
</a>
</div>
</form>
</x-master-card>
@if ($preview)
<x-master-card title="Preview Import" subtitle="Periksa dulu data yang akan diproses sebelum disimpan ke database.">
<div class="mb-6 grid grid-cols-1 gap-4 md:grid-cols-4">
<div class="rounded-3xl border border-slate-200 bg-slate-50 px-5 py-4">
<div class="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Total Baris</div>
<div class="mt-2 text-3xl font-bold text-slate-800">{{ $preview['summary']['total_rows'] }}</div>
</div>
<div class="rounded-3xl border border-emerald-200 bg-emerald-50 px-5 py-4">
<div class="text-xs font-semibold uppercase tracking-[0.18em] text-emerald-600">Valid</div>
<div class="mt-2 text-3xl font-bold text-emerald-700">{{ $preview['summary']['valid_rows'] }}</div>
</div>
<div class="rounded-3xl border border-sky-200 bg-sky-50 px-5 py-4">
<div class="text-xs font-semibold uppercase tracking-[0.18em] text-sky-600">Insert Baru</div>
<div class="mt-2 text-3xl font-bold text-sky-700">{{ $preview['summary']['insert_rows'] }}</div>
</div>
<div class="rounded-3xl border border-amber-200 bg-amber-50 px-5 py-4">
<div class="text-xs font-semibold uppercase tracking-[0.18em] text-amber-600">Update Data</div>
<div class="mt-2 text-3xl font-bold text-amber-700">{{ $preview['summary']['update_rows'] }}</div>
</div>
</div>
@if (!empty($preview['errors']))
<div class="mb-6 rounded-2xl border border-rose-200 bg-rose-50 px-5 py-4 text-sm text-rose-700">
<div class="font-semibold">Ada baris yang belum valid.</div>
<ul class="mt-2 list-disc space-y-1 pl-5">
@foreach (array_slice($preview['errors'], 0, 10) as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="overflow-x-auto rounded-3xl border border-slate-200">
<table class="min-w-full divide-y divide-slate-200 text-sm">
<thead class="bg-slate-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Baris</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">NIS / NISN</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Nama</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Akademik</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Aksi</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Status Validasi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 bg-white">
@foreach ($preview['rows'] as $row)
<tr class="align-top {{ $row['is_valid'] ? '' : 'bg-rose-50/50' }}">
<td class="px-4 py-4 font-semibold text-slate-700">{{ $row['row_number'] }}</td>
<td class="px-4 py-4 text-slate-600">
<div>{{ $row['data']['student_number'] ?: '-' }}</div>
<div class="text-xs text-slate-400">NISN: {{ $row['data']['nisn'] ?: '-' }}</div>
</td>
<td class="px-4 py-4 text-slate-700">
<div class="font-medium">{{ $row['data']['full_name'] ?: '-' }}</div>
<div class="text-xs text-slate-400">{{ $row['data']['gender'] ?: '-' }} · {{ $row['data']['phone_number'] ?: '-' }}</div>
</td>
<td class="px-4 py-4 text-slate-600">
<div>Tahun: {{ $row['data']['academic_year_id'] ?: '-' }}</div>
<div>Jurusan: {{ $row['data']['department_id'] ?: '-' }}</div>
<div>Kelas: {{ $row['data']['class_id'] ?: '-' }}</div>
</td>
<td class="px-4 py-4">
<span class="rounded-full px-3 py-1 text-xs font-semibold {{ $row['action'] === 'update' ? 'bg-amber-100 text-amber-700' : 'bg-sky-100 text-sky-700' }}">
{{ $row['action'] === 'update' ? 'Update' : 'Insert' }}
</span>
</td>
<td class="px-4 py-4 text-sm">
@if ($row['is_valid'])
<span class="rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-700">Valid</span>
@else
<div class="space-y-1 text-rose-700">
<div class="inline-flex rounded-full bg-rose-100 px-3 py-1 text-xs font-semibold">Perlu perbaikan</div>
<ul class="list-disc pl-4 text-xs">
@foreach ($row['errors'] as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@if ($preview['summary']['valid_rows'] > 0)
<form method="POST" action="{{ route('students.import') }}" class="mt-6 flex flex-wrap items-center gap-3">
@csrf
<input type="hidden" name="preview_token" value="{{ $preview['token'] }}">
<button type="submit" class="inline-flex items-center rounded-2xl bg-emerald-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-emerald-200 transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-200">
Konfirmasi Import ke Database
</button>
<span class="text-sm text-slate-500">Hanya baris valid yang akan diproses.</span>
</form>
@endif
</x-master-card>
@endif
</div>
<div class="space-y-6">
<x-master-card title="Kolom Esensial" subtitle="Kolom inti yang harus ada di file import.">
<ul class="space-y-2 text-sm text-slate-600">
<li><strong>student_number</strong> — NIS</li>
<li><strong>nisn</strong> — NISN</li>
<li><strong>full_name</strong> — nama lengkap</li>
<li><strong>gender</strong> — L / P</li>
<li><strong>academic_year_id</strong> — ID tahun ajaran</li>
<li><strong>department_id</strong> — ID program keahlian</li>
<li><strong>class_id</strong> — ID kelas</li>
<li><strong>entry_date</strong> — format YYYY-MM-DD</li>
<li><strong>status</strong> — default paling aman: active</li>
<li><strong>phone_number</strong> — opsional</li>
</ul>
</x-master-card>
<x-master-card title="Referensi ID Master" subtitle="Pakai ID ini saat mengisi template import.">
<div class="space-y-4 text-sm text-slate-600">
<div>
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.14em] text-slate-500">Tahun Ajaran</div>
<div class="space-y-1">
@foreach ($academicYears as $id => $label)
<div>• <strong>{{ $id }}</strong> — {{ $label }}</div>
@endforeach
</div>
</div>
<div>
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.14em] text-slate-500">Program Keahlian</div>
<div class="space-y-1">
@foreach ($departments as $id => $label)
<div>• <strong>{{ $id }}</strong> — {{ $label }}</div>
@endforeach
</div>
</div>
<div>
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.14em] text-slate-500">Kelas</div>
<div class="max-h-72 space-y-1 overflow-y-auto pr-2">
@foreach ($classes as $classRoom)
<div>• <strong>{{ $classRoom->id }}</strong> — {{ $classRoom->name }} <span class="text-slate-400">({{ $classRoom->academicYear?->name }} · {{ $classRoom->department?->name }})</span></div>
@endforeach
</div>
</div>
</div>
</x-master-card>
</div>
</div>
</x-master-page>
+210
View File
@@ -0,0 +1,210 @@
<x-master-page
:title="$pageTitle ?? 'Data Siswa'"
:description="$pageDescription ?? 'Kelola data siswa lengkap berdasarkan master akademik yang tersedia.'"
>
@php
$isAdmin = auth()->user()?->isAdmin() ?? false;
$indexRoute = match ($viewMode ?? 'all') {
'graduated' => route('students.graduated'),
'transferred' => route('students.transferred'),
default => route('students.index'),
};
$sortUrl = function (string $column) use ($sort, $direction, $indexRoute) {
$nextDirection = $sort === $column && $direction === 'asc' ? 'desc' : 'asc';
return $indexRoute . '?' . http_build_query(array_merge(request()->query(), ['sort' => $column, 'direction' => $nextDirection]));
};
$sortIcon = function (string $column) use ($sort, $direction) {
if ($sort !== $column) {
return '↕';
}
return $direction === 'asc' ? '↑' : '↓';
};
@endphp
<div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
<div></div>
<div class="flex flex-wrap items-center gap-3">
@if ($isAdmin)
<a href="{{ route('students.import.form') }}" class="inline-flex items-center justify-center rounded-2xl border border-emerald-200 bg-emerald-50 px-5 py-3 text-sm font-semibold text-emerald-700 transition hover:bg-emerald-100">
Import Data Siswa
</a>
@endif
<a href="{{ route('students.create') }}" class="inline-flex items-center justify-center rounded-2xl bg-indigo-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-indigo-200 transition hover:bg-indigo-700">
Tambah Siswa
</a>
</div>
</div>
<x-master-card title="Filter Data Siswa" subtitle="Cari berdasarkan nama, NIS, NISN, kontak, atau filter akademik dasar.">
<form method="GET" action="{{ $indexRoute }}" class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-5">
<x-form-input name="search" label="Pencarian" :value="$search" placeholder="Nama / NIS / NISN / HP" />
<x-form-select name="academic_year_id" label="Tahun Angkatan" :options="$academicYears->all()" :value="$filters['academic_year_id']" />
<x-form-select name="department_id" label="Program Keahlian" :options="$departments->all()" :value="$filters['department_id']" />
<x-form-select name="class_id" label="Kelas" :options="$classes->all()" :value="$filters['class_id']" />
<x-form-select name="status" label="Status" :options="$statuses" :value="$filters['status']" />
<input type="hidden" name="sort" value="{{ $sort }}">
<input type="hidden" name="direction" value="{{ $direction }}">
<div class="flex items-end gap-3 md:col-span-2 xl:col-span-5">
<button type="submit" class="rounded-2xl bg-slate-900 px-5 py-3 text-sm font-semibold text-white transition hover:bg-slate-800">Cari</button>
<a href="{{ $indexRoute }}" class="rounded-2xl border border-slate-200 px-5 py-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-50">Reset</a>
</div>
</form>
</x-master-card>
<form method="POST" action="{{ route('students.bulk-destroy') }}" class="space-y-4" id="bulk-delete-students-form">
@csrf
@method('DELETE')
<div class="flex flex-col gap-3 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm lg:flex-row lg:items-center lg:justify-between">
<div class="text-sm text-slate-600">
<div><span class="font-semibold text-slate-800">Total data:</span> {{ $students->total() }} siswa</div>
<div class="text-xs text-slate-400">Halaman {{ $students->currentPage() }} dari {{ $students->lastPage() }}</div>
</div>
@if ($isAdmin)
<div class="flex flex-wrap items-center gap-3">
<label class="inline-flex items-center gap-2 text-sm text-slate-600">
<input type="checkbox" id="select-all-students" class="rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
<span>Pilih semua di halaman ini</span>
</label>
<button type="submit" class="inline-flex items-center rounded-2xl border border-rose-200 bg-rose-50 px-5 py-3 text-sm font-semibold text-rose-700 transition hover:bg-rose-100" id="bulk-delete-students-button" disabled>
Hapus Banyak Data Siswa
</button>
</div>
@endif
</div>
<div class="flex justify-end">
{{ $students->onEachSide(1)->links() }}
</div>
<div class="overflow-hidden rounded-3xl border border-slate-200 bg-white shadow-sm shadow-slate-200/50">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200">
<thead class="bg-slate-50">
<tr>
@if ($isAdmin)
<th class="px-6 py-4 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Pilih</th>
@endif
<th class="px-6 py-4 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">
<a href="{{ $sortUrl('full_name') }}" class="inline-flex items-center gap-2 hover:text-slate-700">Siswa <span>{{ $sortIcon('full_name') }}</span></a>
</th>
<th class="px-6 py-4 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">
<a href="{{ $sortUrl('academic_year') }}" class="inline-flex items-center gap-2 hover:text-slate-700">Akademik <span>{{ $sortIcon('academic_year') }}</span></a>
</th>
<th class="px-6 py-4 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">
<a href="{{ $sortUrl('phone_number') }}" class="inline-flex items-center gap-2 hover:text-slate-700">Kontak <span>{{ $sortIcon('phone_number') }}</span></a>
</th>
<th class="px-6 py-4 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Kelengkapan Dokumen</th>
<th class="px-6 py-4 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">
<a href="{{ $sortUrl('status') }}" class="inline-flex items-center gap-2 hover:text-slate-700">Status <span>{{ $sortIcon('status') }}</span></a>
</th>
<th class="px-6 py-4 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Aksi</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 bg-white">
@forelse ($students as $student)
<tr>
@if ($isAdmin)
<td class="px-6 py-4 align-top">
<input type="checkbox" name="student_ids[]" value="{{ $student->id }}" class="student-row-checkbox rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
</td>
@endif
<td class="px-6 py-4">
<button
type="button"
class="student-hover-trigger cursor-default font-semibold text-slate-800 underline decoration-dotted underline-offset-4"
data-photo-url="{{ $student->hover_photo_url }}"
data-student-name="{{ $student->full_name }}"
data-student-nis="{{ $student->student_number ?: '-' }}"
data-student-class="{{ $student->classRoom?->name ?: '-' }}"
data-student-department="{{ $student->department?->name ?: '-' }}"
>
{{ $student->full_name }}
</button>
<div class="mt-1 text-xs text-slate-500">NIS: {{ $student->student_number ?: '-' }} · NISN: {{ $student->nisn ?: '-' }}</div>
</td>
<td class="px-6 py-4 text-sm text-slate-600">
<div>{{ $student->academicYear?->name ?: '-' }}</div>
<div>{{ $student->department?->name ?: '-' }}</div>
<div>{{ $student->classRoom?->name ?: '-' }}</div>
</td>
<td class="px-6 py-4 text-sm text-slate-600">
<div>{{ $student->phone_number ?: '-' }}</div>
<div>{{ $student->email ?: '-' }}</div>
</td>
<td class="px-6 py-4">
<div class="min-w-[180px]">
<div class="mb-2 flex items-center justify-between text-xs font-medium text-slate-500">
<span>Upload: {{ $student->required_documents_present }}/{{ $student->required_documents_total }}</span>
<span>{{ $student->document_completion_percentage }}%</span>
</div>
<div class="h-2.5 overflow-hidden rounded-full bg-slate-200">
<div class="h-full rounded-full {{ $student->required_documents_missing === 0 ? 'bg-emerald-500' : 'bg-amber-500' }}" style="width: {{ $student->document_completion_percentage }}%"></div>
</div>
<div class="mt-2 flex items-center justify-between text-xs font-medium text-slate-500">
<span>Terverifikasi: {{ $student->required_documents_verified }}/{{ $student->required_documents_total }}</span>
<span>{{ $student->document_verification_percentage }}%</span>
</div>
<div class="h-2.5 overflow-hidden rounded-full bg-slate-200">
<div class="h-full rounded-full {{ $student->required_documents_unverified === 0 && $student->required_documents_missing === 0 ? 'bg-sky-500' : 'bg-slate-500' }}" style="width: {{ $student->document_verification_percentage }}%"></div>
</div>
<div class="mt-2 text-xs {{ $student->required_documents_missing > 0 ? 'text-amber-600' : ($student->required_documents_unverified > 0 ? 'text-sky-600' : 'text-emerald-600') }}">
{{ $student->required_documents_missing > 0 ? 'Kurang ' . $student->required_documents_missing . ' dokumen wajib' : ($student->required_documents_unverified > 0 ? $student->required_documents_unverified . ' dokumen menunggu validasi' : 'Lengkap & tervalidasi') }}
</div>
</div>
</td>
<td class="px-6 py-4">
@php
$statusMap = [
'active' => 'bg-emerald-100 text-emerald-700',
'graduated' => 'bg-sky-100 text-sky-700',
'transferred' => 'bg-amber-100 text-amber-700',
'dropped' => 'bg-rose-100 text-rose-700',
'inactive' => 'bg-slate-100 text-slate-600',
];
@endphp
<span class="rounded-full px-3 py-1 text-xs font-semibold {{ $statusMap[$student->status] ?? 'bg-slate-100 text-slate-600' }}">
{{ $studentStatusLabels[$student->status] ?? $student->status }}
</span>
</td>
<td class="px-6 py-4">
<div class="flex flex-wrap items-center gap-2">
<a href="{{ route('students.show', $student) }}" class="rounded-xl border border-sky-200 px-3 py-2 text-xs font-semibold text-sky-700 transition hover:bg-sky-50">Detail</a>
<a href="{{ route('students.edit', $student) }}" class="rounded-xl border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-50">Edit</a>
@if ($isAdmin)
<form action="{{ route('students.destroy', $student) }}" method="POST" class="delete-form" data-entity="data siswa {{ $student->full_name }}">
@csrf
@method('DELETE')
<button type="submit" class="rounded-xl border border-rose-200 px-3 py-2 text-xs font-semibold text-rose-600 transition hover:bg-rose-50">Hapus</button>
</form>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="{{ $isAdmin ? 7 : 6 }}" class="px-6 py-10 text-center text-sm text-slate-500">Belum ada data siswa.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="flex flex-col gap-3 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm lg:flex-row lg:items-center lg:justify-between">
<div class="text-sm text-slate-600">
Menampilkan <span class="font-semibold text-slate-800">{{ $students->firstItem() ?? 0 }}</span> sampai <span class="font-semibold text-slate-800">{{ $students->lastItem() ?? 0 }}</span> dari <span class="font-semibold text-slate-800">{{ $students->total() }}</span> siswa.
</div>
<div>
{{ $students->onEachSide(1)->links() }}
</div>
</div>
</form>
</x-master-page>
+264
View File
@@ -0,0 +1,264 @@
<x-master-page title="Detail Siswa" description="Ringkasan lengkap data siswa yang tersimpan di sistem.">
@php
$isAdmin = auth()->user()?->isAdmin() ?? false;
@endphp
<div class="mb-6 rounded-3xl border border-slate-200 bg-white p-6 shadow-sm shadow-slate-200/50">
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div>
<h3 class="text-lg font-semibold text-slate-900">Kelengkapan Dokumen Wajib</h3>
<p class="mt-1 text-sm text-slate-500">Ringkasan upload dan verifikasi dokumen wajib siswa.</p>
</div>
<div class="grid grid-cols-2 gap-4 text-right">
<div>
<div class="text-3xl font-bold text-slate-900">{{ $requiredDocumentSummary['percentage'] }}%</div>
<div class="text-sm {{ $requiredDocumentSummary['missing'] === 0 ? 'text-emerald-600' : 'text-amber-600' }}">
{{ $requiredDocumentSummary['missing'] === 0 ? 'Upload lengkap' : 'Kurang ' . $requiredDocumentSummary['missing'] . ' dokumen' }}
</div>
</div>
<div>
<div class="text-3xl font-bold text-slate-900">{{ $requiredDocumentSummary['verification_percentage'] }}%</div>
<div class="text-sm {{ $requiredDocumentSummary['unverified'] === 0 && $requiredDocumentSummary['missing'] === 0 ? 'text-emerald-600' : 'text-sky-600' }}">
{{ $requiredDocumentSummary['unverified'] === 0 && $requiredDocumentSummary['missing'] === 0 ? 'Terverifikasi penuh' : $requiredDocumentSummary['unverified'] . ' belum valid' }}
</div>
</div>
</div>
</div>
<div class="mt-4 space-y-4">
<div>
<div class="mb-2 flex items-center justify-between text-xs font-semibold uppercase tracking-[0.16em] text-slate-400">
<span>Upload Dokumen Wajib</span>
<span>{{ $requiredDocumentSummary['uploaded'] }}/{{ $requiredDocumentSummary['total'] }}</span>
</div>
<div class="h-3 overflow-hidden rounded-full bg-slate-200">
<div class="h-full rounded-full {{ $requiredDocumentSummary['missing'] === 0 ? 'bg-emerald-500' : 'bg-amber-500' }}" style="width: {{ $requiredDocumentSummary['percentage'] }}%"></div>
</div>
</div>
<div>
<div class="mb-2 flex items-center justify-between text-xs font-semibold uppercase tracking-[0.16em] text-slate-400">
<span>Verifikasi Dokumen Wajib</span>
<span>{{ $requiredDocumentSummary['verified'] }}/{{ $requiredDocumentSummary['total'] }}</span>
</div>
<div class="h-3 overflow-hidden rounded-full bg-slate-200">
<div class="h-full rounded-full {{ $requiredDocumentSummary['unverified'] === 0 && $requiredDocumentSummary['missing'] === 0 ? 'bg-sky-500' : 'bg-slate-500' }}" style="width: {{ $requiredDocumentSummary['verification_percentage'] }}%"></div>
</div>
</div>
</div>
@if ($requiredDocumentSummary['missing_items']->isNotEmpty() || $requiredDocumentSummary['unverified_items']->isNotEmpty())
<div class="mt-4 flex flex-wrap gap-2">
@foreach ($requiredDocumentSummary['missing_items'] as $missingDocument)
<span class="rounded-full bg-rose-100 px-3 py-1 text-xs font-semibold text-rose-700">Belum upload: {{ $missingDocument }}</span>
@endforeach
@foreach ($requiredDocumentSummary['unverified_items'] as $unverifiedDocument)
<span class="rounded-full bg-sky-100 px-3 py-1 text-xs font-semibold text-sky-700">Belum valid: {{ $unverifiedDocument }}</span>
@endforeach
</div>
@endif
</div>
<div class="grid grid-cols-1 gap-6 xl:grid-cols-3">
<x-master-card title="Identitas Utama" subtitle="Informasi inti siswa.">
<div class="space-y-4 text-sm text-slate-600">
<div><span class="font-semibold text-slate-800">Nama:</span> {{ $student->full_name }}</div>
<div><span class="font-semibold text-slate-800">NIS:</span> {{ $student->student_number ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">NISN:</span> {{ $student->nisn ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Jenis Kelamin:</span> {{ $student->gender === 'L' ? 'Laki-laki' : 'Perempuan' }}</div>
<div><span class="font-semibold text-slate-800">TTL:</span> {{ $student->place_of_birth ?: '-' }}, {{ $student->date_of_birth?->format('d-m-Y') ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Agama:</span> {{ $student->religion ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Kewarganegaraan:</span> {{ $student->nationality ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Status:</span> {{ ucfirst($student->status) }}</div>
</div>
</x-master-card>
<x-master-card title="Akademik" subtitle="Relasi akademik siswa.">
<div class="space-y-4 text-sm text-slate-600">
<div><span class="font-semibold text-slate-800">Tahun Angkatan:</span> {{ $student->academicYear?->name ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Program Keahlian:</span> {{ $student->department?->name ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Kelas:</span> {{ $student->classRoom?->name ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Tanggal Masuk:</span> {{ $student->entry_date?->format('d-m-Y') ?: '-' }}</div>
</div>
</x-master-card>
<x-master-card title="Kontak & Alamat" subtitle="Informasi komunikasi siswa.">
<div class="space-y-4 text-sm text-slate-600">
<div><span class="font-semibold text-slate-800">No. HP:</span> {{ $student->phone_number ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Email:</span> {{ $student->email ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Alamat:</span> {{ $student->address ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Wilayah:</span> {{ collect([$student->village, $student->district, $student->city, $student->province])->filter()->join(', ') ?: '-' }}</div>
<div><span class="font-semibold text-slate-800">Kode Pos:</span> {{ $student->postal_code ?: '-' }}</div>
</div>
</x-master-card>
</div>
<div class="mt-6">
<x-master-card title="Histori Akademik Siswa" subtitle="Riwayat penempatan siswa per tahun angkatan, jurusan, dan kelas.">
@if ($student->academicHistories->isEmpty())
<div class="text-sm text-slate-500">Belum ada histori akademik untuk siswa ini.</div>
@else
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200">
<thead class="bg-slate-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Tahun</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Jurusan</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Kelas</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Status</th>
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-[0.16em] text-slate-500">Periode</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 bg-white">
@foreach ($student->academicHistories as $history)
<tr>
<td class="px-4 py-3 text-sm text-slate-700">{{ $history->academicYear?->name ?: '-' }}</td>
<td class="px-4 py-3 text-sm text-slate-600">{{ $history->department?->name ?: '-' }}</td>
<td class="px-4 py-3 text-sm text-slate-600">{{ $history->classRoom?->name ?: '-' }}</td>
<td class="px-4 py-3 text-sm text-slate-600">
<span class="rounded-full {{ $history->is_current ? 'bg-emerald-100 text-emerald-700' : 'bg-slate-100 text-slate-600' }} px-3 py-1 text-xs font-semibold">
{{ $history->is_current ? 'Aktif Sekarang' : ucfirst($history->status) }}
</span>
</td>
<td class="px-4 py-3 text-sm text-slate-500">
{{ $history->start_date?->format('d-m-Y') ?: '-' }} s/d {{ $history->end_date?->format('d-m-Y') ?: 'Sekarang' }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</x-master-card>
</div>
<div class="mt-6 grid grid-cols-1 gap-6 xl:grid-cols-2">
<x-master-card title="Data Orang Tua / Wali" subtitle="Kelola data ayah, ibu, atau wali siswa.">
<div class="mb-4">
<a href="{{ route('students.parents.create', $student) }}" class="inline-flex items-center rounded-2xl bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white shadow-lg shadow-indigo-200 transition hover:bg-indigo-700">
Tambah Orang Tua / Wali
</a>
</div>
@if ($student->parents->isEmpty())
<div class="text-sm text-slate-500">Belum ada data orang tua/wali untuk siswa ini.</div>
@else
<div class="space-y-4">
@foreach ($student->parents as $parent)
<div class="rounded-2xl border border-slate-200 bg-slate-50 p-4 text-sm text-slate-600">
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
<div>
<div class="font-semibold text-slate-800">{{ ucfirst($parent->relation_type === 'father' ? 'Ayah' : ($parent->relation_type === 'mother' ? 'Ibu' : 'Wali')) }} - {{ $parent->name }}</div>
<div class="mt-1">Pekerjaan: {{ $parent->occupation ?: '-' }}</div>
<div>No. HP: {{ $parent->phone_number ?: '-' }}</div>
<div>Pendidikan: {{ $parent->education ?: '-' }}</div>
</div>
<div class="flex flex-wrap gap-2">
<a href="{{ route('students.parents.edit', [$student, $parent]) }}" class="rounded-xl border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-100">Edit</a>
@if ($isAdmin)
<form action="{{ route('students.parents.destroy', [$student, $parent]) }}" method="POST" class="delete-form" data-entity="data {{ $parent->name }}">
@csrf
@method('DELETE')
<button type="submit" class="rounded-xl border border-rose-200 px-3 py-2 text-xs font-semibold text-rose-600 transition hover:bg-rose-50">Hapus</button>
</form>
@endif
</div>
</div>
</div>
@endforeach
</div>
@endif
</x-master-card>
<x-master-card title="Dokumen Siswa" subtitle="Kelola file dan status verifikasi dokumen siswa.">
<div class="mb-4">
<a href="{{ route('students.documents.create', $student) }}" class="inline-flex items-center rounded-2xl bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white shadow-lg shadow-indigo-200 transition hover:bg-indigo-700">
Tambah Dokumen
</a>
</div>
@if ($student->documents->isEmpty())
<div class="text-sm text-slate-500">Belum ada dokumen siswa yang diunggah.</div>
@else
<div class="space-y-4">
@foreach ($student->documents as $document)
@php
$verificationMap = [
'pending' => 'bg-slate-100 text-slate-600',
'valid' => 'bg-emerald-100 text-emerald-700',
'invalid' => 'bg-rose-100 text-rose-700',
'revision' => 'bg-amber-100 text-amber-700',
];
@endphp
<div class="rounded-3xl border border-slate-200 bg-slate-50 p-5 text-sm text-slate-600 shadow-sm shadow-slate-200/40">
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
<div>
<div class="font-semibold text-slate-800">{{ $document->documentType?->name ?: '-' }}</div>
<div class="mt-1">File: {{ $document->file_name }}</div>
<div>Format: {{ strtoupper($document->file_extension ?: '-') }}</div>
<div>Ukuran: {{ $document->file_size ? number_format($document->file_size / 1024, 1) . ' KB' : '-' }}</div>
@if ($document->verifiedBy)
<div>Diverifikasi oleh: {{ $document->verifiedBy->name }}</div>
@endif
<div class="mt-2 flex flex-wrap gap-2">
<span class="rounded-full px-3 py-1 text-xs font-semibold {{ $verificationMap[$document->verification_status] ?? 'bg-slate-100 text-slate-600' }}">
{{ ucfirst($document->verification_status) }}
</span>
@if ($document->verified_at)
<span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold text-slate-600">
Diverifikasi {{ $document->verified_at->format('d-m-Y H:i') }}
</span>
@endif
</div>
@if ($document->verification_note)
<div class="mt-2 rounded-xl bg-white px-3 py-2 text-xs text-slate-500">
Catatan: {{ $document->verification_note }}
</div>
@endif
</div>
<div class="flex flex-wrap gap-2">
<a href="{{ route('students.documents.preview', $document) }}" class="rounded-xl border border-indigo-200 px-3 py-2 text-xs font-semibold text-indigo-700 transition hover:bg-indigo-50">Lihat</a>
<a href="{{ route('students.documents.download', $document) }}" class="rounded-xl border border-sky-200 px-3 py-2 text-xs font-semibold text-sky-700 transition hover:bg-sky-50">Download</a>
<a href="{{ route('students.documents.edit', [$student, $document]) }}" class="rounded-xl border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-100">Edit</a>
@if ($isAdmin)
<form action="{{ route('students.documents.quick-verify', [$student, $document]) }}" method="POST" class="inline-flex gap-2">
@csrf
<input type="hidden" name="verification_status" value="valid">
<input type="hidden" name="verification_note" value="Dokumen dinyatakan valid.">
<button type="submit" @disabled($document->verification_status === 'valid') class="rounded-xl border border-emerald-200 px-3 py-2 text-xs font-semibold text-emerald-700 transition hover:bg-emerald-50 disabled:cursor-not-allowed disabled:bg-emerald-100 disabled:text-emerald-500">Valid</button>
</form>
<form action="{{ route('students.documents.quick-verify', [$student, $document]) }}" method="POST" class="inline-flex gap-2">
@csrf
<input type="hidden" name="verification_status" value="revision">
<input type="hidden" name="verification_note" value="Dokumen perlu revisi.">
<button type="submit" @disabled($document->verification_status === 'revision') class="rounded-xl border border-amber-200 px-3 py-2 text-xs font-semibold text-amber-700 transition hover:bg-amber-50 disabled:cursor-not-allowed disabled:bg-amber-100 disabled:text-amber-500">Revisi</button>
</form>
<form action="{{ route('students.documents.quick-verify', [$student, $document]) }}" method="POST" class="inline-flex gap-2">
@csrf
<input type="hidden" name="verification_status" value="invalid">
<input type="hidden" name="verification_note" value="Dokumen dinyatakan tidak valid.">
<button type="submit" @disabled($document->verification_status === 'invalid') class="rounded-xl border border-rose-200 px-3 py-2 text-xs font-semibold text-rose-700 transition hover:bg-rose-50 disabled:cursor-not-allowed disabled:bg-rose-100 disabled:text-rose-500">Invalid</button>
</form>
<form action="{{ route('students.documents.destroy', [$student, $document]) }}" method="POST" class="delete-form" data-entity="dokumen {{ $document->file_name }}">
@csrf
@method('DELETE')
<button type="submit" class="rounded-xl border border-rose-200 px-3 py-2 text-xs font-semibold text-rose-600 transition hover:bg-rose-50">Hapus</button>
</form>
@endif
</div>
</div>
</div>
@endforeach
</div>
@endif
</x-master-card>
</div>
<div class="mt-6 flex flex-wrap items-center gap-3">
<a href="{{ route('students.edit', $student) }}" class="rounded-2xl bg-indigo-600 px-5 py-3 text-sm font-semibold text-white shadow-lg shadow-indigo-200 transition hover:bg-indigo-700">Edit Siswa</a>
<a href="{{ route('students.index') }}" class="rounded-2xl border border-slate-200 px-5 py-3 text-sm font-semibold text-slate-700 transition hover:bg-slate-50">Kembali</a>
</div>
</x-master-page>