upload project TA SIM Santri

This commit is contained in:
HelgaFaisa 2026-05-13 11:22:50 +07:00
parent 1332db7373
commit ff81f4cbf6
40 changed files with 4832 additions and 5449 deletions

View File

@ -12,6 +12,21 @@
class UangSakuController extends Controller
{
// ────────────────────────────────────────────────────────────────
// PRIVATE: cek apakah user aktif adalah pamong
// ────────────────────────────────────────────────────────────────
private function isPamong(): bool
{
return auth()->user()->role === 'pamong';
}
private function requirePamong(): void
{
if (! $this->isPamong()) {
abort(403, 'Akses ditolak. Hanya Pamong yang dapat melakukan aksi ini.');
}
}
/**
* Tampilkan daftar uang saku Grouped per Santri
* Default: bulan ini
@ -20,12 +35,11 @@ public function index(Request $request)
{
$search = $request->get('search');
// ── Default: bulan ini ──────────────────────────────────────
$dari = $request->get('dari', now()->startOfMonth()->format('Y-m-d'));
$sampai = $request->get('sampai', now()->endOfMonth()->format('Y-m-d'));
$sort = $request->get('sort', 'nama'); // nama | saldo_asc | saldo_desc | transaksi_desc | terakhir
$sort = $request->get('sort', 'nama');
// ── KPI ringkasan periode (dipengaruhi filter tanggal) ──────
// ── KPI ringkasan periode ───────────────────────────────────
$kpiQuery = UangSaku::whereBetween('tanggal_transaksi', [$dari, $sampai]);
$kpi = [
'total_transaksi' => (clone $kpiQuery)->count(),
@ -33,23 +47,22 @@ public function index(Request $request)
'total_pengeluaran' => (float)(clone $kpiQuery)->where('jenis_transaksi', 'pengeluaran')->sum('nominal'),
'total_santri' => (clone $kpiQuery)->distinct('id_santri')->count('id_santri'),
];
// Selisih periode: apakah dalam rentang ini uang yang masuk lebih besar dari yang keluar
$kpi['selisih'] = $kpi['total_pemasukan'] - $kpi['total_pengeluaran'];
// ── KPI Real-time: Total saldo aktual seluruh santri (tidak dipengaruhi filter) ──
// Ambil saldo_sesudah dari transaksi TERAKHIR masing-masing santri
$totalSaldoSemua = DB::table('uang_saku as u1')
->join(DB::raw('(
SELECT id_santri, MAX(id) as max_id
FROM uang_saku
GROUP BY id_santri
) as latest'), function ($join) {
$join->on('u1.id_santri', '=', 'latest.id_santri')
->on('u1.id', '=', 'latest.max_id');
// ── KPI Real-time: total saldo semua santri ─────────────────
$kpi['total_saldo_realtime'] = (float) DB::table('uang_saku as a')
->whereNotExists(function ($q) {
$q->from('uang_saku as b')
->whereColumn('b.id_santri', 'a.id_santri')
->where(function ($inner) {
$inner->whereColumn('b.tanggal_transaksi', '>', 'a.tanggal_transaksi')
->orWhere(function ($tie) {
$tie->whereColumn('b.tanggal_transaksi', '=', 'a.tanggal_transaksi')
->whereColumn('b.id', '>', 'a.id');
});
});
})
->sum('u1.saldo_sesudah');
$kpi['total_saldo_realtime'] = (float) $totalSaldoSemua;
->sum('saldo_sesudah');
// ── Query santri ────────────────────────────────────────────
$santriQuery = Santri::aktif()
@ -64,71 +77,79 @@ public function index(Request $request)
}
$santriQuery->orderBy('nama_lengkap');
$santriList = $santriQuery->paginate(20)->appends(request()->query());
$ids = $santriList->pluck('id_santri');
// ── Saldo terakhir per santri (efisien: subquery per-id) ────
// Ambil id transaksi terakhir per santri lalu join, hindari get()->unique() yang boros
$latestIds = DB::table('uang_saku')
->whereIn('id_santri', $ids)
->select('id_santri', DB::raw('MAX(id) as max_id'))
->groupBy('id_santri')
->pluck('max_id', 'id_santri');
// ── Saldo terakhir per santri (NOT EXISTS) ──────────────────
$latestIds = DB::table('uang_saku as a')
->whereIn('a.id_santri', $ids)
->whereNotExists(function ($q) {
$q->from('uang_saku as b')
->whereColumn('b.id_santri', 'a.id_santri')
->where(function ($inner) {
$inner->whereColumn('b.tanggal_transaksi', '>', 'a.tanggal_transaksi')
->orWhere(function ($tie) {
$tie->whereColumn('b.tanggal_transaksi', '=', 'a.tanggal_transaksi')
->whereColumn('b.id', '>', 'a.id');
});
});
})
->select('a.id_santri', 'a.id')
->pluck('a.id', 'a.id_santri');
$saldoMap = UangSaku::whereIn('id', $latestIds->values())
->get()
->keyBy('id_santri');
// ── Pemasukan & pengeluaran bulan ini per santri ────────────
$bulanIniStats = UangSaku::whereIn('id_santri', $ids)
->whereMonth('tanggal_transaksi', now()->month)
->whereYear('tanggal_transaksi', now()->year)
// ── Statistik per santri mengikuti PERIODE filter ───────────
$periodeStats = UangSaku::whereIn('id_santri', $ids)
->whereBetween('tanggal_transaksi', [$dari, $sampai])
->select(
'id_santri',
DB::raw('SUM(CASE WHEN jenis_transaksi="pemasukan" THEN nominal ELSE 0 END) as pemasukan_bulan'),
DB::raw('SUM(CASE WHEN jenis_transaksi="pengeluaran" THEN nominal ELSE 0 END) as pengeluaran_bulan'),
DB::raw('COUNT(*) as total_bulan')
DB::raw('SUM(CASE WHEN jenis_transaksi="pemasukan" THEN nominal ELSE 0 END) as pemasukan_periode'),
DB::raw('SUM(CASE WHEN jenis_transaksi="pengeluaran" THEN nominal ELSE 0 END) as pengeluaran_periode'),
DB::raw('COUNT(*) as total_periode')
)
->groupBy('id_santri')
->get()
->keyBy('id_santri');
// ── Transaksi terbaru per santri (max 5, untuk collapsed detail) ──
// ── Transaksi terbaru per santri (max 5) ────────────────────
$transaksiMap = UangSaku::whereIn('id_santri', $ids)
->orderByDesc('tanggal_transaksi')
->orderByDesc('created_at')
->orderByDesc('id')
->get()
->groupBy('id_santri')
->map(fn($g) => $g->take(5));
// ── Attach semua data ke santri objects ─────────────────────
$collection = $santriList->getCollection()->map(function ($santri) use ($saldoMap, $bulanIniStats, $transaksiMap) {
$saldoRow = $saldoMap[$santri->id_santri] ?? null;
$bulan = $bulanIniStats[$santri->id_santri] ?? null;
// ── Attach data ke santri objects ───────────────────────────
$collection = $santriList->getCollection()->map(function ($santri) use ($saldoMap, $periodeStats, $transaksiMap) {
$saldoRow = $saldoMap[$santri->id_santri] ?? null;
$periode = $periodeStats[$santri->id_santri] ?? null;
$santri->saldo_terakhir = $saldoRow ? (float)$saldoRow->saldo_sesudah : 0;
$santri->transaksi_terakhir_tgl = $saldoRow ? $saldoRow->tanggal_transaksi : null;
$santri->pemasukan_bulan = $bulan ? (float)$bulan->pemasukan_bulan : 0;
$santri->pengeluaran_bulan = $bulan ? (float)$bulan->pengeluaran_bulan : 0;
$santri->transaksi_bulan_ini = $bulan ? (int)$bulan->total_bulan : 0;
$santri->transaksi_terbaru = $transaksiMap[$santri->id_santri] ?? collect();
$santri->saldo_terakhir = $saldoRow ? (float)$saldoRow->saldo_sesudah : 0;
$santri->transaksi_terakhir_tgl = $saldoRow ? $saldoRow->tanggal_transaksi : null;
$santri->pemasukan_periode = $periode ? (float)$periode->pemasukan_periode : 0;
$santri->pengeluaran_periode = $periode ? (float)$periode->pengeluaran_periode : 0;
$santri->transaksi_periode = $periode ? (int)$periode->total_periode : 0;
$santri->transaksi_terbaru = $transaksiMap[$santri->id_santri] ?? collect();
return $santri;
});
// ── Re-sort collection setelah attach ───────────────────────
$sorted = match($sort) {
'saldo_asc' => $collection->sortBy('saldo_terakhir'),
'saldo_desc' => $collection->sortByDesc('saldo_terakhir'),
'transaksi_desc' => $collection->sortByDesc('transaksi_bulan_ini'),
'terakhir' => $collection->sortByDesc('transaksi_terakhir_tgl'),
default => $collection->sortBy('nama_lengkap'),
'saldo_asc' => $collection->sortBy('saldo_terakhir'),
'saldo_desc' => $collection->sortByDesc('saldo_terakhir'),
'transaksi_desc' => $collection->sortByDesc('transaksi_periode'),
'terakhir' => $collection->sortByDesc('transaksi_terakhir_tgl'),
default => $collection->sortBy('nama_lengkap'),
};
$santriList->setCollection($sorted->values());
return view('admin.uang-saku.index', compact('santriList', 'kpi', 'dari', 'sampai', 'sort'));
// Kirim flag ke view agar tampilan bisa menyesuaikan
$canCrud = $this->isPamong();
return view('admin.uang-saku.index', compact('santriList', 'kpi', 'dari', 'sampai', 'sort', 'canCrud'));
}
/**
@ -136,13 +157,12 @@ public function index(Request $request)
*/
public function santriInfo($id_santri)
{
$santri = Santri::where('id_santri', $id_santri)->firstOrFail();
$santri = Santri::where('id_santri', $id_santri)->firstOrFail();
$bulanIni = now();
$lastTx = UangSaku::where('id_santri', $id_santri)
->orderByDesc('tanggal_transaksi')
->orderByDesc('created_at')
->orderByDesc('id')
->first();
$saldo = $lastTx ? (float)$lastTx->saldo_sesudah : 0;
@ -161,7 +181,7 @@ public function santriInfo($id_santri)
$transaksiTerakhir = UangSaku::where('id_santri', $id_santri)
->orderByDesc('tanggal_transaksi')
->orderByDesc('created_at')
->orderByDesc('id')
->limit(3)
->get()
->map(fn($t) => [
@ -183,6 +203,8 @@ public function santriInfo($id_santri)
public function create()
{
$this->requirePamong();
$santriList = Santri::where('status', 'Aktif')
->select('id_santri', 'nama_lengkap')
->orderBy('nama_lengkap')
@ -193,6 +215,8 @@ public function create()
public function store(Request $request)
{
$this->requirePamong();
$validated = $request->validate([
'id_santri' => 'required|exists:santris,id_santri',
'jenis_transaksi' => 'required|in:pemasukan,pengeluaran',
@ -205,6 +229,17 @@ public function store(Request $request)
try {
UangSaku::create($validated);
$this->recalculateSaldoAfter($validated['id_santri'], $validated['tanggal_transaksi']);
if ($validated['jenis_transaksi'] === 'pengeluaran'
&& $this->hasSaldoNegatif($validated['id_santri'], $validated['tanggal_transaksi'])) {
DB::rollBack();
return back()->withInput()->with(
'error',
'Transaksi gagal: Saldo tidak mencukupi. ' .
'Jumlah pengeluaran melebihi saldo yang tersedia pada tanggal tersebut.'
);
}
DB::commit();
Cache::forget('santri_aktif_uang_saku');
return redirect()->route('admin.uang-saku.index')
@ -218,11 +253,14 @@ public function store(Request $request)
public function show($id)
{
$transaksi = UangSaku::with('santri')->findOrFail($id);
return view('admin.uang-saku.show', compact('transaksi'));
$canCrud = $this->isPamong();
return view('admin.uang-saku.show', compact('transaksi', 'canCrud'));
}
public function edit($id)
{
$this->requirePamong();
$transaksi = UangSaku::with('santri')->findOrFail($id);
$santriList = Santri::where('status', 'Aktif')
->select('id_santri', 'nama_lengkap')
@ -233,6 +271,8 @@ public function edit($id)
public function update(Request $request, $id)
{
$this->requirePamong();
$transaksi = UangSaku::findOrFail($id);
$validated = $request->validate([
'jenis_transaksi' => 'required|in:pemasukan,pengeluaran',
@ -241,20 +281,24 @@ public function update(Request $request, $id)
'tanggal_transaksi' => 'required|date',
]);
// Simpan tanggal lama sebelum update, agar recalculate dimulai dari yang paling awal
$tanggalLama = $transaksi->tanggal_transaksi->format('Y-m-d');
$tanggalLama = $transaksi->tanggal_transaksi->format('Y-m-d');
$tanggalBaru = $validated['tanggal_transaksi'];
$tanggalMulai = min($tanggalLama, $tanggalBaru);
DB::beginTransaction();
try {
// Gunakan saveQuietly agar model boot (updating) tidak ikut menghitung ulang saldo
// — recalculate akan dikerjakan secara menyeluruh oleh recalculateSaldoAfter()
$transaksi->fill($validated)->saveQuietly();
// Recalculate dari tanggal yang paling awal antara tanggal lama dan baru
$tanggalBaru = $validated['tanggal_transaksi'];
$tanggalMulai = min($tanggalLama, $tanggalBaru);
$this->recalculateSaldoAfter($transaksi->id_santri, $tanggalMulai);
if ($this->hasSaldoNegatif($transaksi->id_santri, $tanggalMulai)) {
DB::rollBack();
return back()->withInput()->with(
'error',
'Perubahan gagal: Perubahan ini menyebabkan saldo menjadi negatif. ' .
'Pengeluaran tidak boleh melebihi saldo yang tersedia.'
);
}
DB::commit();
Cache::forget('santri_aktif_uang_saku');
return redirect()->route('admin.uang-saku.index')
@ -267,6 +311,8 @@ public function update(Request $request, $id)
public function destroy($id)
{
$this->requirePamong();
$transaksi = UangSaku::findOrFail($id);
$idSantri = $transaksi->id_santri;
$tanggal = $transaksi->tanggal_transaksi->format('Y-m-d');
@ -289,17 +335,22 @@ public function riwayat(Request $request, $id_santri)
{
$santri = Santri::where('id_santri', $id_santri)->firstOrFail();
$tanggalDari = $request->filled('tanggal_dari') ? $request->tanggal_dari : now()->startOfMonth()->format('Y-m-d');
$tanggalSampai = $request->filled('tanggal_sampai') ? $request->tanggal_sampai : now()->endOfMonth()->format('Y-m-d');
$tanggalDari = $request->filled('tanggal_dari')
? $request->tanggal_dari
: now()->startOfMonth()->format('Y-m-d');
$tanggalSampai = $request->filled('tanggal_sampai')
? $request->tanggal_sampai
: now()->endOfMonth()->format('Y-m-d');
$query = UangSaku::where('id_santri', $id_santri)
->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai]);
$transaksi = $query->orderBy('tanggal_transaksi', 'desc')
->orderBy('created_at', 'desc')
// Transaksi dalam periode (paginated)
$transaksi = UangSaku::where('id_santri', $id_santri)
->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai])
->orderBy('tanggal_transaksi', 'desc')
->orderBy('id', 'desc')
->paginate(20)
->appends($request->query());
// Total pemasukan & pengeluaran periode
$totalPemasukan = UangSaku::where('id_santri', $id_santri)
->where('jenis_transaksi', 'pemasukan')
->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai])
@ -310,50 +361,102 @@ public function riwayat(Request $request, $id_santri)
->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai])
->sum('nominal');
// Ambil saldo aktual dari transaksi TERAKHIR santri ini (real-time, bukan dari filter)
// Saldo aktual real-time (kumulatif semua waktu)
$lastTx = UangSaku::where('id_santri', $id_santri)
->orderByDesc('tanggal_transaksi')
->orderByDesc('created_at')
->orderByDesc('id')
->first();
$saldoTerakhir = $lastTx ? (float)$lastTx->saldo_sesudah : 0;
$dataGrafik = UangSaku::where('id_santri', $id_santri)
// Saldo awal periode = saldo_sesudah transaksi terakhir SEBELUM tanggalDari
$txSebelumPeriode = UangSaku::where('id_santri', $id_santri)
->where('tanggal_transaksi', '<', $tanggalDari)
->orderByDesc('tanggal_transaksi')
->orderByDesc('id')
->first();
$saldoAwalPeriode = $txSebelumPeriode ? (float)$txSebelumPeriode->saldo_sesudah : 0;
// Saldo akhir periode = saldo_sesudah transaksi terakhir s.d. tanggalSampai
$txAkhirPeriode = UangSaku::where('id_santri', $id_santri)
->where('tanggal_transaksi', '<=', $tanggalSampai)
->orderByDesc('tanggal_transaksi')
->orderByDesc('id')
->first();
$saldoAkhirPeriode = $txAkhirPeriode ? (float)$txAkhirPeriode->saldo_sesudah : 0;
// ── DATA GRAFIK: PERJALANAN SALDO ────────────────────────────
$saldoPerHari = UangSaku::where('id_santri', $id_santri)
->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai])
->select(
DB::raw('DATE(tanggal_transaksi) as tanggal'),
DB::raw('SUM(CASE WHEN jenis_transaksi="pemasukan" THEN nominal ELSE 0 END) as pemasukan'),
DB::raw('SUM(CASE WHEN jenis_transaksi="pengeluaran" THEN nominal ELSE 0 END) as pengeluaran')
DB::raw('DATE(tanggal_transaksi) as tgl'),
DB::raw('SUBSTRING_INDEX(GROUP_CONCAT(saldo_sesudah ORDER BY id DESC), ",", 1) as saldo_akhir_hari')
)
->groupBy('tanggal')
->orderBy('tanggal')
->get();
if ($dataGrafik->isEmpty()) {
$dataGrafik = collect([(object)['tanggal' => $tanggalDari, 'pemasukan' => 0, 'pengeluaran' => 0]]);
}
->groupBy('tgl')
->orderBy('tgl')
->get()
->keyBy('tgl');
$periodeDari = Carbon::parse($tanggalDari);
$periodeSampai = Carbon::parse($tanggalSampai);
$dataGrafikSaldo = [];
$saldoBerjalan = $saldoAwalPeriode;
$current = $periodeDari->copy();
$dataGrafikSaldo[] = [
'tanggal' => $periodeDari->format('Y-m-d'),
'saldo' => $saldoAwalPeriode,
'is_awal' => true,
];
while ($current->lte($periodeSampai)) {
$tgl = $current->format('Y-m-d');
if (isset($saldoPerHari[$tgl])) {
$saldoBerjalan = (float)$saldoPerHari[$tgl]->saldo_akhir_hari;
}
if ($current->eq($periodeDari)) {
if (isset($saldoPerHari[$tgl])) {
$dataGrafikSaldo[0]['saldo'] = (float)$saldoPerHari[$tgl]->saldo_akhir_hari;
$dataGrafikSaldo[0]['is_awal'] = false;
}
} else {
$dataGrafikSaldo[] = [
'tanggal' => $tgl,
'saldo' => $saldoBerjalan,
'is_awal' => false,
];
}
$current->addDay();
}
$canCrud = $this->isPamong();
return view('admin.uang-saku.riwayat', compact(
'santri', 'transaksi',
'totalPemasukan', 'totalPengeluaran', 'saldoTerakhir',
'dataGrafik', 'tanggalDari', 'tanggalSampai',
'periodeDari', 'periodeSampai'
'totalPemasukan', 'totalPengeluaran',
'saldoAwalPeriode', 'saldoAkhirPeriode', 'saldoTerakhir',
'dataGrafikSaldo',
'tanggalDari', 'tanggalSampai',
'periodeDari', 'periodeSampai',
'canCrud'
));
}
/**
* Hitung ulang saldo_sebelum & saldo_sesudah untuk semua transaksi
* milik $idSantri yang tanggalnya >= $tanggal.
*
* Dipanggil setelah store / update / destroy agar urutan saldo
* tetap konsisten meski transaksi disisipkan di tengah.
*/
// ────────────────────────────────────────────────────────────────
// PRIVATE HELPERS
// ────────────────────────────────────────────────────────────────
private function hasSaldoNegatif(string $idSantri, string $tanggal): bool
{
return UangSaku::where('id_santri', $idSantri)
->where('tanggal_transaksi', '>=', $tanggal)
->where('saldo_sesudah', '<', 0)
->exists();
}
private function recalculateSaldoAfter($idSantri, $tanggal)
{
// Pastikan format tanggal string (bukan Carbon object)
$tanggal = $tanggal instanceof \Carbon\Carbon
$tanggal = $tanggal instanceof Carbon
? $tanggal->format('Y-m-d')
: $tanggal;
@ -366,7 +469,6 @@ private function recalculateSaldoAfter($idSantri, $tanggal)
foreach ($transaksiSetelah as $index => $trans) {
if ($index === 0) {
// Cari saldo_sesudah transaksi tepat sebelum batch ini
$prev = UangSaku::where('id_santri', $idSantri)
->where('tanggal_transaksi', '<', $tanggal)
->orderByDesc('tanggal_transaksi')

View File

@ -4,6 +4,240 @@
{{-- Select2 CSS --}}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">
<style>
/* ═══════════════════════════════════════════
OVERRIDE SELECT2 agar sesuai tema
═══════════════════════════════════════════ */
.select2-container--default .select2-selection--single {
height: 44px;
border: 1.5px solid #d1d9e0;
border-radius: 10px;
padding: 6px 12px;
display: flex;
align-items: center;
transition: border-color .2s, box-shadow .2s;
background: #fff;
}
.select2-container--default .select2-selection--single:hover {
border-color: var(--primary-color, #6FBA9D);
}
.select2-container--default.select2-container--focus .select2-selection--single,
.select2-container--default.select2-container--open .select2-selection--single {
border-color: var(--primary-color, #6FBA9D);
box-shadow: 0 0 0 3px rgba(111,186,157,.18);
outline: none;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 1.4;
color: #333;
padding: 0;
font-size: .93rem;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #aaa;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 42px;
right: 10px;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: var(--primary-color, #6FBA9D);
}
.select2-dropdown {
border: 1.5px solid var(--primary-color, #6FBA9D);
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0,0,0,.12);
overflow: hidden;
}
.select2-search--dropdown .select2-search__field {
border: 1.5px solid #d1d9e0;
border-radius: 7px;
padding: 7px 10px;
font-size: .88rem;
outline: none;
}
.select2-search--dropdown .select2-search__field:focus {
border-color: var(--primary-color, #6FBA9D);
}
.select2-results__option {
padding: 9px 14px;
font-size: .88rem;
}
/* ═══════════════════════════════════════════
LABEL FIELD
═══════════════════════════════════════════ */
.field-label {
display: block;
font-weight: 600;
font-size: .8rem;
text-transform: uppercase;
letter-spacing: .5px;
color: var(--text-light, #888);
margin-bottom: 8px;
}
.field-label .fi { margin-right: 5px; color: var(--primary-color, #6FBA9D); }
/* ═══════════════════════════════════════════
CARD PILIH JENIS TRANSAKSI
═══════════════════════════════════════════ */
.jenis-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
.jenis-card {
position: relative;
cursor: pointer;
border-radius: 14px;
border: 2.5px solid #e0e7ef;
padding: 18px 16px 16px;
transition: border-color .2s, box-shadow .2s, transform .15s, background .2s;
background: #fff;
user-select: none;
-webkit-user-select: none;
}
.jenis-card:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,.09); }
.jenis-card input[type="radio"] { position:absolute; opacity:0; width:0; height:0; }
.jenis-card .ji-wrap {
width: 46px; height: 46px;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 1.25rem;
margin-bottom: 11px;
transition: background .2s;
}
.jenis-card .ji-title { font-weight: 700; font-size: .93rem; margin-bottom: 4px; transition: color .2s; }
.jenis-card .ji-desc { font-size: .75rem; color: #b0b8c4; line-height: 1.4; }
.jenis-card .ji-check {
position: absolute; top: 12px; right: 12px;
width: 22px; height: 22px; border-radius: 50%;
border: 2px solid #d1d9e0;
display: flex; align-items: center; justify-content: center;
font-size: .68rem; color: transparent;
transition: all .2s;
}
/* Pemasukan aktif */
.jenis-card.pemasukan.active {
border-color: #6FBA9D;
background: linear-gradient(135deg, #f0fdf7 0%, #e6f9f2 100%);
box-shadow: 0 4px 18px rgba(111,186,157,.22);
}
.jenis-card.pemasukan .ji-wrap { background: #edf9f4; color: #6FBA9D; }
.jenis-card.pemasukan.active .ji-wrap { background: rgba(111,186,157,.2); color: #38a169; }
.jenis-card.pemasukan.active .ji-title { color: #38a169; }
.jenis-card.pemasukan.active .ji-check { background: #6FBA9D; border-color: #6FBA9D; color: #fff; }
/* Pengeluaran aktif */
.jenis-card.pengeluaran.active {
border-color: #FF8B94;
background: linear-gradient(135deg, #fff5f6 0%, #ffe9eb 100%);
box-shadow: 0 4px 18px rgba(255,139,148,.22);
}
.jenis-card.pengeluaran .ji-wrap { background: #fff0f1; color: #FF8B94; }
.jenis-card.pengeluaran.active .ji-wrap { background: rgba(255,139,148,.2); color: #e53e3e; }
.jenis-card.pengeluaran.active .ji-title { color: #e53e3e; }
.jenis-card.pengeluaran.active .ji-check { background: #FF8B94; border-color: #FF8B94; color: #fff; }
/* ═══════════════════════════════════════════
BOX PERINGATAN SALDO
═══════════════════════════════════════════ */
#warning-saldo {
display: none;
border-radius: 10px;
border: 1.5px solid #FC8181;
background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
padding: 13px 16px;
margin-top: 10px;
animation: warnIn .22s ease;
}
@keyframes warnIn {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
}
#warning-saldo .wt { font-weight: 700; color: #c53030; font-size: .87rem; margin-bottom: 5px; }
#warning-saldo .wb { font-size: .8rem; color: #742a2a; line-height: 1.55; }
#warning-saldo .ws-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 10px;
}
#warning-saldo .ws-cell {
background: rgba(255,255,255,.75); border-radius: 8px;
padding: 8px 10px; text-align: center;
}
#warning-saldo .ws-cell small { display:block; font-size:.68rem; color:#aaa; margin-bottom:3px; }
#warning-saldo .ws-cell strong { font-size:.86rem; font-weight:700; }
/* ═══════════════════════════════════════════
INPUT NOMINAL
═══════════════════════════════════════════ */
.nominal-wrap { position: relative; display: flex; align-items: center; }
.nominal-prefix {
position: absolute; left: 14px;
font-weight: 700; color: #aaa;
pointer-events: none; font-size: .93rem; z-index: 1;
}
.nominal-wrap input {
padding-left: 44px !important;
font-size: 1rem; font-weight: 600; letter-spacing: .3px;
height: 44px; border-radius: 10px;
border: 1.5px solid #d1d9e0;
transition: border-color .2s, box-shadow .2s;
width: 100%;
}
.nominal-wrap input:focus {
border-color: var(--primary-color, #6FBA9D);
box-shadow: 0 0 0 3px rgba(111,186,157,.18);
outline: none;
}
.nominal-wrap.danger input {
border-color: #FC8181 !important;
box-shadow: 0 0 0 3px rgba(255,139,148,.18) !important;
}
/* form-control radius konsisten */
.form-control { border-radius: 10px !important; border: 1.5px solid #d1d9e0; transition: border-color .2s, box-shadow .2s; }
.form-control:focus {
border-color: var(--primary-color, #6FBA9D) !important;
box-shadow: 0 0 0 3px rgba(111,186,157,.18) !important;
outline: none;
}
textarea.form-control { height: auto; }
/* ═══════════════════════════════════════════
INFO CARD SANTRI
═══════════════════════════════════════════ */
#santri-info {
border-radius: 12px; overflow: hidden;
margin-bottom: 20px;
border: 1.5px solid var(--primary-color, #6FBA9D);
box-shadow: 0 4px 14px rgba(111,186,157,.13);
animation: fadeUp .22s ease;
}
@keyframes fadeUp {
from { opacity:0; transform:translateY(6px); }
to { opacity:1; transform:translateY(0); }
}
.si-header {
background: linear-gradient(90deg, #6FBA9D 0%, #38a169 100%);
padding: 10px 16px;
display: flex; align-items: center; gap: 8px;
color: #fff; font-weight: 700;
font-size: .8rem; text-transform: uppercase; letter-spacing: .45px;
}
.si-body { padding: 14px 16px; background: #f9fdfb; }
.si-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 12px; }
.si-stat {
background: #fff; border-radius: 9px;
padding: 10px 12px; text-align: center;
border: 1px solid #e8f5ef;
}
.si-stat small { display:block; font-size:.68rem; color:#aaa; text-transform:uppercase; letter-spacing:.3px; margin-bottom:4px; }
.si-val { font-size: .93rem; font-weight: 800; }
.form-group { margin-bottom: 20px; }
</style>
<div class="page-header">
<h2><i class="fas fa-plus-circle"></i> Tambah Transaksi Uang Saku</h2>
</div>
@ -12,17 +246,21 @@
<form action="{{ route('admin.uang-saku.store') }}" method="POST" id="transaksiForm">
@csrf
{{-- ══════════════════════════════════════
1. PILIH SANTRI
══════════════════════════════════════ --}}
<div class="form-group">
<label for="id_santri">
<i class="fas fa-user form-icon"></i>
Pilih Santri <span style="color: red;">*</span>
<label class="field-label" for="id_santri">
<i class="fas fa-user fi"></i> Pilih Santri
<span style="color:#e53e3e;font-weight:700;">*</span>
</label>
<select name="id_santri" id="id_santri" class="form-control @error('id_santri') is-invalid @enderror" required>
<option value="">-- Pilih Santri --</option>
<select name="id_santri" id="id_santri"
class="form-control @error('id_santri') is-invalid @enderror" required>
<option value="">Cari ID atau nama santri...</option>
@foreach($santriList as $santri)
<option value="{{ $santri->id_santri }}"
<option value="{{ $santri->id_santri }}"
{{ (old('id_santri', request('id_santri')) == $santri->id_santri) ? 'selected' : '' }}>
{{ $santri->id_santri }} - {{ $santri->nama_lengkap }}
{{ $santri->id_santri }} {{ $santri->nama_lengkap }}
</option>
@endforeach
</select>
@ -31,87 +269,170 @@
@enderror
</div>
{{-- Info Card Santri (AJAX) --}}
<div id="santri-info" style="display:none; margin-bottom: 14px;">
<div class="content-box" style="padding:16px; background:var(--primary-light);">
<div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:12px; margin-bottom:12px;">
<div>
<small class="text-muted">Saldo Terakhir</small>
<div id="info-saldo" style="font-weight:700; font-size:1.1rem;"></div>
{{-- Info Card Santri --}}
<div id="santri-info" style="display:none;">
<div class="si-header">
<i class="fas fa-id-card"></i>
<span id="info-nama"></span>
</div>
<div class="si-body">
<div class="si-stats">
<div class="si-stat">
<small>Saldo Saat Ini</small>
<div class="si-val" id="info-saldo"></div>
</div>
<div>
<small class="text-muted">Pemasukan Bln Ini</small>
<div id="info-masuk" style="font-weight:600; color:#6FBA9D;"></div>
<div class="si-stat">
<small>Masuk Bln Ini</small>
<div class="si-val" id="info-masuk" style="color:#6FBA9D;"></div>
</div>
<div>
<small class="text-muted">Pengeluaran Bln Ini</small>
<div id="info-keluar" style="font-weight:600; color:#FF8B94;"></div>
<div class="si-stat">
<small>Keluar Bln Ini</small>
<div class="si-val" id="info-keluar" style="color:#FF8B94;"></div>
</div>
</div>
<div id="info-riwayat"></div>
</div>
</div>
{{-- ══════════════════════════════════════
2. JENIS TRANSAKSI Card pilihan
══════════════════════════════════════ --}}
<div class="form-group">
<label for="jenis_transaksi">
<i class="fas fa-exchange-alt form-icon"></i>
Jenis Transaksi <span style="color: red;">*</span>
<label class="field-label">
<i class="fas fa-exchange-alt fi"></i> Jenis Transaksi
<span style="color:#e53e3e;font-weight:700;">*</span>
</label>
<select name="jenis_transaksi" id="jenis_transaksi" class="form-control @error('jenis_transaksi') is-invalid @enderror" required>
<option value="">-- Pilih Jenis --</option>
<option value="pemasukan" {{ old('jenis_transaksi') == 'pemasukan' ? 'selected' : '' }}>
Pemasukan (Terima Uang Saku)
</option>
<option value="pengeluaran" {{ old('jenis_transaksi') == 'pengeluaran' ? 'selected' : '' }}>
Pengeluaran (Gunakan Uang Saku)
</option>
{{-- Hidden select dikirim ke server & divalidasi Laravel --}}
<select name="jenis_transaksi" id="jenis_transaksi" style="display:none;"
class="@error('jenis_transaksi') is-invalid @enderror" required>
<option value=""></option>
<option value="pemasukan" {{ old('jenis_transaksi') == 'pemasukan' ? 'selected' : '' }}>pemasukan</option>
<option value="pengeluaran" {{ old('jenis_transaksi') == 'pengeluaran' ? 'selected' : '' }}>pengeluaran</option>
</select>
<div class="jenis-cards">
<div class="jenis-card pemasukan {{ old('jenis_transaksi') == 'pemasukan' ? 'active' : '' }}"
id="card-pemasukan" onclick="pilihJenis('pemasukan')" role="button" tabindex="0"
onkeydown="if(event.key==='Enter'||event.key===' ')pilihJenis('pemasukan')">
<input type="radio" name="_jenis_ui" value="pemasukan"
{{ old('jenis_transaksi') == 'pemasukan' ? 'checked' : '' }}>
<div class="ji-wrap"><i class="fas fa-arrow-circle-down"></i></div>
<div class="ji-title">Pemasukan</div>
<div class="ji-desc">Santri menerima uang saku dari wali / pesantren</div>
<div class="ji-check"><i class="fas fa-check"></i></div>
</div>
<div class="jenis-card pengeluaran {{ old('jenis_transaksi') == 'pengeluaran' ? 'active' : '' }}"
id="card-pengeluaran" onclick="pilihJenis('pengeluaran')" role="button" tabindex="0"
onkeydown="if(event.key==='Enter'||event.key===' ')pilihJenis('pengeluaran')">
<input type="radio" name="_jenis_ui" value="pengeluaran"
{{ old('jenis_transaksi') == 'pengeluaran' ? 'checked' : '' }}>
<div class="ji-wrap"><i class="fas fa-arrow-circle-up"></i></div>
<div class="ji-title">Pengeluaran</div>
<div class="ji-desc">Santri menggunakan uang saku untuk keperluan</div>
<div class="ji-check"><i class="fas fa-check"></i></div>
</div>
</div>
@error('jenis_transaksi')
<div class="invalid-feedback">{{ $message }}</div>
<div style="color:#e53e3e;font-size:.82rem;margin-top:6px;">
<i class="fas fa-exclamation-circle"></i> {{ $message }}
</div>
@enderror
</div>
{{-- ══════════════════════════════════════
3. NOMINAL
══════════════════════════════════════ --}}
<div class="form-group">
<label for="nominal">
<i class="fas fa-money-bill-wave form-icon"></i>
Nominal (Rp) <span style="color: red;">*</span>
<label class="field-label" for="nominal_display">
<i class="fas fa-money-bill-wave fi"></i> Nominal (Rp)
<span style="color:#e53e3e;font-weight:700;">*</span>
</label>
<input type="number" name="nominal" id="nominal" class="form-control @error('nominal') is-invalid @enderror"
value="{{ old('nominal') }}" placeholder="Contoh: 50000" min="1" step="1" required>
<div class="nominal-wrap" id="nominalWrap">
<span class="nominal-prefix">Rp</span>
<input type="text"
id="nominal_display"
class="@error('nominal') is-invalid @enderror"
placeholder="0"
autocomplete="off"
inputmode="numeric"
value="{{ old('nominal') ? number_format((int)old('nominal'), 0, ',', '.') : '' }}">
</div>
<input type="hidden" name="nominal" id="nominal" value="{{ old('nominal') }}">
@error('nominal')
<div class="invalid-feedback">{{ $message }}</div>
<div style="color:#e53e3e;font-size:.82rem;margin-top:5px;">
<i class="fas fa-exclamation-circle"></i> {{ $message }}
</div>
@enderror
<small class="form-text">Masukkan nominal tanpa titik atau koma</small>
{{-- ── KOTAK PERINGATAN SALDO TIDAK CUKUP ── --}}
<div id="warning-saldo">
<div class="wt">
<i class="fas fa-exclamation-triangle"></i>
Peringatan: Saldo Tidak Mencukupi!
</div>
<div class="wb">
Nominal pengeluaran yang dimasukkan melebihi saldo santri saat ini.
Sistem akan menolak transaksi ini. Silakan kurangi nominal atau pastikan
data saldo sudah benar.
</div>
<div class="ws-grid">
<div class="ws-cell">
<small>Saldo Tersedia</small>
<strong id="warn-saldo" style="color:#38a169;"></strong>
</div>
<div class="ws-cell">
<small>Nominal Diinput</small>
<strong id="warn-nominal" style="color:#c53030;"></strong>
</div>
<div class="ws-cell">
<small>Kekurangan</small>
<strong id="warn-kurang" style="color:#c53030;"></strong>
</div>
</div>
</div>
</div>
{{-- ══════════════════════════════════════
4. TANGGAL
══════════════════════════════════════ --}}
<div class="form-group">
<label for="tanggal_transaksi">
<i class="fas fa-calendar form-icon"></i>
Tanggal Transaksi <span style="color: red;">*</span>
<label class="field-label" for="tanggal_transaksi">
<i class="fas fa-calendar fi"></i> Tanggal Transaksi
<span style="color:#e53e3e;font-weight:700;">*</span>
</label>
<input type="date" name="tanggal_transaksi" id="tanggal_transaksi"
class="form-control @error('tanggal_transaksi') is-invalid @enderror"
<input type="date" name="tanggal_transaksi" id="tanggal_transaksi"
class="form-control @error('tanggal_transaksi') is-invalid @enderror"
value="{{ old('tanggal_transaksi', date('Y-m-d')) }}" required>
@error('tanggal_transaksi')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
{{-- ══════════════════════════════════════
5. KETERANGAN
══════════════════════════════════════ --}}
<div class="form-group">
<label for="keterangan">
<i class="fas fa-sticky-note form-icon"></i>
Keterangan
<label class="field-label" for="keterangan">
<i class="fas fa-sticky-note fi"></i> Keterangan
<span style="font-weight:400;text-transform:none;letter-spacing:0;color:#bbb;">(opsional)</span>
</label>
<textarea name="keterangan" id="keterangan" class="form-control @error('keterangan') is-invalid @enderror"
rows="4" placeholder="Tuliskan Nama Petugas disini">{{ old('keterangan') }}</textarea>
<textarea name="keterangan" id="keterangan"
class="form-control @error('keterangan') is-invalid @enderror"
rows="3"
placeholder="Contoh: Uang saku januari">{{ old('keterangan') }}</textarea>
@error('keterangan')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<small class="form-text">Opsional - Jelaskan detail transaksi</small>
</div>
<div class="btn-group">
<button type="submit" class="btn btn-success hover-lift">
<button type="submit" class="btn btn-success hover-lift" id="btnSubmit">
<i class="fas fa-save"></i> Simpan Transaksi
</button>
<a href="{{ route('admin.uang-saku.index') }}" class="btn btn-secondary">
@ -124,54 +445,174 @@ class="form-control @error('tanggal_transaksi') is-invalid @enderror"
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
$(document).ready(function() {
// Inisialisasi Select2
// ═══════════════════════════════════════════
// STATE GLOBAL
// ═══════════════════════════════════════════
var saldoSantri = 0; // saldo raw santri terpilih
var jenisAktif = ''; // 'pemasukan' | 'pengeluaran' | ''
var nominalBersih = 0; // nominal tanpa titik
// ═══════════════════════════════════════════
// FORMAT HELPER
// ═══════════════════════════════════════════
function formatRibuan(val) {
var b = String(val).replace(/\D/g, '');
return b ? b.replace(/\B(?=(\d{3})+(?!\d))/g, '.') : '';
}
function stripRibuan(val) { return String(val).replace(/\./g, ''); }
function rpStr(angka) { return 'Rp\u00a0' + formatRibuan(angka); }
// ═══════════════════════════════════════════
// PILIH JENIS (kartu)
// ═══════════════════════════════════════════
function pilihJenis(jenis) {
jenisAktif = jenis;
document.getElementById('jenis_transaksi').value = jenis;
document.getElementById('card-pemasukan').classList.toggle('active', jenis === 'pemasukan');
document.getElementById('card-pengeluaran').classList.toggle('active', jenis === 'pengeluaran');
cekPeringatan();
}
// ═══════════════════════════════════════════
// CEK PERINGATAN SALDO
// ═══════════════════════════════════════════
function cekPeringatan() {
var box = document.getElementById('warning-saldo');
var wrap = document.getElementById('nominalWrap');
var btn = document.getElementById('btnSubmit');
var kurang = nominalBersih - saldoSantri;
var tampil = jenisAktif === 'pengeluaran' && nominalBersih > 0 && kurang > 0;
if (tampil) {
document.getElementById('warn-saldo').textContent = rpStr(saldoSantri);
document.getElementById('warn-nominal').textContent = rpStr(nominalBersih);
document.getElementById('warn-kurang').textContent = rpStr(kurang);
box.style.display = 'block';
wrap.classList.add('danger');
btn.style.opacity = '.6';
btn.title = 'Saldo tidak mencukupi — transaksi akan ditolak!';
} else {
box.style.display = 'none';
wrap.classList.remove('danger');
btn.style.opacity = '1';
btn.title = '';
}
}
// ═══════════════════════════════════════════
// INPUT NOMINAL — auto titik ribuan
// ═══════════════════════════════════════════
var dispEl = document.getElementById('nominal_display');
var hiddenEl = document.getElementById('nominal');
dispEl.addEventListener('input', function () {
var bersih = stripRibuan(this.value);
var diformat = formatRibuan(bersih);
// Jaga posisi kursor agar tidak melompat
var pos = this.selectionStart;
var dotBef = (this.value.substring(0, pos).match(/\./g) || []).length;
this.value = diformat;
var dotAft = (diformat.substring(0, pos).match(/\./g) || []).length;
try { this.setSelectionRange(pos + dotAft - dotBef, pos + dotAft - dotBef); } catch(e) {}
nominalBersih = parseInt(bersih) || 0;
hiddenEl.value = nominalBersih || '';
cekPeringatan();
});
// ═══════════════════════════════════════════
// SUBMIT — konfirmasi ekstra jika saldo kurang
// ═══════════════════════════════════════════
document.getElementById('transaksiForm').addEventListener('submit', function (e) {
hiddenEl.value = stripRibuan(dispEl.value) || '';
if (jenisAktif === 'pengeluaran' && nominalBersih > saldoSantri) {
var lanjut = confirm(
'\u26a0\ufe0f PERINGATAN: Saldo Tidak Mencukupi!\n\n' +
'Saldo santri : ' + rpStr(saldoSantri) + '\n' +
'Nominal input : ' + rpStr(nominalBersih) + '\n' +
'Kekurangan : ' + rpStr(nominalBersih - saldoSantri) + '\n\n' +
'Transaksi ini akan DITOLAK oleh sistem.\n' +
'Yakin tetap ingin melanjutkan?'
);
if (!lanjut) { e.preventDefault(); return false; }
}
});
// ═══════════════════════════════════════════
// SELECT2 + AJAX INFO SANTRI
// ═══════════════════════════════════════════
$(document).ready(function () {
$('#id_santri').select2({
placeholder: '-- Pilih Santri --',
placeholder: 'Cari ID atau nama santri...',
allowClear: true,
width: '100%'
});
$('#jenis_transaksi').select2({
placeholder: '-- Pilih Jenis --',
allowClear: true,
width: '100%'
width: '100%',
language: {
noResults: function () { return 'Santri tidak ditemukan'; },
searching: function () { return 'Mencari...'; }
}
});
// Info santri via AJAX pakai jQuery .on('change') agar kompatibel dengan Select2
$('#id_santri').on('change', function() {
$('#id_santri').on('change', function () {
var infoBox = document.getElementById('santri-info');
var val = this.value;
var val = this.value;
saldoSantri = 0;
cekPeringatan();
if (!val) { infoBox.style.display = 'none'; return; }
fetch('{{ url("admin/uang-saku/santri-info") }}/' + val)
.then(function(r) { return r.json(); })
.then(function(d) {
var saldoColor = d.saldo_raw >= 0 ? '#6FBA9D' : '#FF8B94';
document.getElementById('info-saldo').innerHTML = '<span style="color:' + saldoColor + '">Rp ' + d.saldo_terakhir + '</span>';
document.getElementById('info-masuk').textContent = 'Rp ' + d.total_pemasukan_bulan_ini;
document.getElementById('info-keluar').textContent = 'Rp ' + d.total_pengeluaran_bulan_ini;
.then(function (r) { return r.json(); })
.then(function (d) {
saldoSantri = d.saldo_raw;
document.getElementById('info-nama').textContent = d.nama;
var sc = d.saldo_raw >= 100000 ? '#38a169'
: d.saldo_raw >= 20000 ? '#f5a623' : '#e53e3e';
document.getElementById('info-saldo').innerHTML =
'<span style="color:' + sc + '">Rp\u00a0' + d.saldo_terakhir + '</span>';
document.getElementById('info-masuk').textContent = 'Rp\u00a0' + d.total_pemasukan_bulan_ini;
document.getElementById('info-keluar').textContent = 'Rp\u00a0' + d.total_pengeluaran_bulan_ini;
var html = '';
if (d.transaksi_terakhir.length > 0) {
html = '<small class="text-muted">3 Transaksi Terakhir:</small><table class="data-table" style="margin-top:6px;font-size:.85rem;"><thead><tr><th>Tanggal</th><th>Jenis</th><th>Nominal</th><th>Ket</th></tr></thead><tbody>';
d.transaksi_terakhir.forEach(function(t) {
html = '<div style="font-size:.7rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:7px;">3 Transaksi Terakhir</div>';
html += '<table class="data-table" style="font-size:.81rem;"><thead><tr>';
html += '<th>Tanggal</th><th>Jenis</th><th>Nominal</th><th>Keterangan</th>';
html += '</tr></thead><tbody>';
d.transaksi_terakhir.forEach(function (t) {
var badge = t.jenis === 'pemasukan'
? '<span class="badge badge-success">Masuk</span>'
: '<span class="badge badge-danger">Keluar</span>';
html += '<tr><td>' + t.tanggal + '</td><td>' + badge + '</td><td>Rp ' + t.nominal + '</td><td>' + t.keterangan + '</td></tr>';
html += '<tr><td>' + t.tanggal + '</td><td>' + badge +
'</td><td>Rp\u00a0' + t.nominal + '</td><td>' + t.keterangan + '</td></tr>';
});
html += '</tbody></table>';
} else {
html = '<p style="font-size:.8rem;color:#aaa;margin:0;">Belum ada riwayat transaksi.</p>';
}
document.getElementById('info-riwayat').innerHTML = html;
infoBox.style.display = 'block';
cekPeringatan();
})
.catch(function() { infoBox.style.display = 'none'; });
.catch(function () {
document.getElementById('santri-info').style.display = 'none';
saldoSantri = 0;
});
});
// Trigger on page load if santri pre-selected
if ($('#id_santri').val()) {
$('#id_santri').trigger('change');
}
// Trigger jika sudah ada pilihan (dari ?id_santri=xxx atau old())
if ($('#id_santri').val()) $('#id_santri').trigger('change');
// Restore jenis dari old() setelah validasi server gagal
var oldJenis = '{{ old("jenis_transaksi", "") }}';
if (oldJenis) pilihJenis(oldJenis);
});
</script>
@endsection

View File

@ -1,6 +1,28 @@
@extends('layouts.app')
@section('content')
<style>
/* Styling input nominal dengan prefix Rp */
.nominal-wrap {
position: relative;
display: flex;
align-items: center;
}
.nominal-wrap .nominal-prefix {
position: absolute;
left: 12px;
font-weight: 600;
color: var(--text-light);
pointer-events: none;
font-size: .95rem;
z-index: 1;
}
.nominal-wrap input {
padding-left: 38px !important;
letter-spacing: .3px;
}
</style>
<div class="page-header">
<h2><i class="fas fa-edit"></i> Edit Transaksi Uang Saku</h2>
</div>
@ -10,12 +32,15 @@
@csrf
@method('PUT')
{{-- Santri (readonly) --}}
<div class="form-group">
<label for="id_santri">
<i class="fas fa-user form-icon"></i>
Santri
</label>
<input type="text" class="form-control" value="{{ $transaksi->santri->id_santri }} - {{ $transaksi->santri->nama_lengkap }}" readonly disabled>
<input type="text" class="form-control"
value="{{ $transaksi->santri->id_santri }} - {{ $transaksi->santri->nama_lengkap }}"
readonly disabled>
<small class="form-text">Santri tidak dapat diubah</small>
</div>
@ -40,17 +65,21 @@
</div>
</div>
{{-- Jenis Transaksi --}}
<div class="form-group">
<label for="jenis_transaksi">
<i class="fas fa-exchange-alt form-icon"></i>
Jenis Transaksi <span style="color: red;">*</span>
</label>
<select name="jenis_transaksi" id="jenis_transaksi" class="form-control @error('jenis_transaksi') is-invalid @enderror" required>
<select name="jenis_transaksi" id="jenis_transaksi"
class="form-control @error('jenis_transaksi') is-invalid @enderror" required>
<option value="">-- Pilih Jenis --</option>
<option value="pemasukan" {{ old('jenis_transaksi', $transaksi->jenis_transaksi) == 'pemasukan' ? 'selected' : '' }}>
<option value="pemasukan"
{{ old('jenis_transaksi', $transaksi->jenis_transaksi) == 'pemasukan' ? 'selected' : '' }}>
Pemasukan (Terima Uang Saku)
</option>
<option value="pengeluaran" {{ old('jenis_transaksi', $transaksi->jenis_transaksi) == 'pengeluaran' ? 'selected' : '' }}>
<option value="pengeluaran"
{{ old('jenis_transaksi', $transaksi->jenis_transaksi) == 'pengeluaran' ? 'selected' : '' }}>
Pengeluaran (Gunakan Uang Saku)
</option>
</select>
@ -59,38 +88,66 @@
@enderror
</div>
{{--
NOMINAL Auto-format dengan titik sebagai pemisah ribuan.
Cara kerja:
#nominal_display → input text yang ditampilkan ke user (format: 10.000)
#nominal → hidden input yang dikirim ke server (raw: 10000)
--}}
<div class="form-group">
<label for="nominal">
<label for="nominal_display">
<i class="fas fa-money-bill-wave form-icon"></i>
Nominal (Rp) <span style="color: red;">*</span>
</label>
<input type="number" name="nominal" id="nominal" class="form-control @error('nominal') is-invalid @enderror"
value="{{ old('nominal', $transaksi->nominal) }}" placeholder="Contoh: 50000" min="1" step="1" required>
<div class="nominal-wrap">
<span class="nominal-prefix">Rp</span>
@php
$nominalLama = old('nominal', $transaksi->nominal);
$nominalDisplay = $nominalLama ? number_format((int)$nominalLama, 0, ',', '.') : '';
@endphp
<input type="text"
id="nominal_display"
class="form-control @error('nominal') is-invalid @enderror"
placeholder="0"
autocomplete="off"
inputmode="numeric"
value="{{ $nominalDisplay }}">
</div>
{{-- Input tersembunyi yang benar-benar dikirim --}}
<input type="hidden" name="nominal" id="nominal" value="{{ $nominalLama }}">
@error('nominal')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback" style="display:block;">{{ $message }}</div>
@enderror
<small class="form-text">Ketik angka, titik akan muncul otomatis. Contoh: <strong>50.000</strong></small>
</div>
{{-- Tanggal Transaksi --}}
<div class="form-group">
<label for="tanggal_transaksi">
<i class="fas fa-calendar form-icon"></i>
Tanggal Transaksi <span style="color: red;">*</span>
</label>
<input type="date" name="tanggal_transaksi" id="tanggal_transaksi"
class="form-control @error('tanggal_transaksi') is-invalid @enderror"
<input type="date" name="tanggal_transaksi" id="tanggal_transaksi"
class="form-control @error('tanggal_transaksi') is-invalid @enderror"
value="{{ old('tanggal_transaksi', $transaksi->tanggal_transaksi->format('Y-m-d')) }}" required>
@error('tanggal_transaksi')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
{{-- Keterangan --}}
<div class="form-group">
<label for="keterangan">
<i class="fas fa-sticky-note form-icon"></i>
Keterangan
</label>
<textarea name="keterangan" id="keterangan" class="form-control @error('keterangan') is-invalid @enderror"
rows="4" placeholder="Contoh: Uang saku bulan Januari 2025">{{ old('keterangan', $transaksi->keterangan) }}</textarea>
<textarea name="keterangan" id="keterangan"
class="form-control @error('keterangan') is-invalid @enderror"
rows="4"
placeholder="Contoh: Uang saku bulan Januari 2025">{{ old('keterangan', $transaksi->keterangan) }}</textarea>
@error('keterangan')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
@ -108,31 +165,72 @@ class="form-control @error('tanggal_transaksi') is-invalid @enderror"
</div>
<script>
(function() {
// ── Helpers format angka ────────────────────────────────────────────
function formatRibuan(nilai) {
var bersih = String(nilai).replace(/\D/g, '');
if (!bersih) return '';
return bersih.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
}
function stripRibuan(nilai) {
return String(nilai).replace(/\./g, '');
}
// ── Inisialisasi input nominal ──────────────────────────────────────
var displayInput = document.getElementById('nominal_display');
var hiddenInput = document.getElementById('nominal');
displayInput.addEventListener('input', function () {
var bersih = stripRibuan(this.value);
var diformat = formatRibuan(bersih);
var selStart = this.selectionStart;
var selEnd = this.selectionEnd;
var dotSebelum = (this.value.substring(0, selStart).match(/\./g) || []).length;
this.value = diformat;
var dotSesudah = (diformat.substring(0, selStart).match(/\./g) || []).length;
var offset = dotSesudah - dotSebelum;
try { this.setSelectionRange(selStart + offset, selEnd + offset); } catch (e) {}
hiddenInput.value = bersih;
});
// Saat form submit: pastikan hidden input sudah terisi dari display
document.getElementById('transaksiForm').addEventListener('submit', function () {
hiddenInput.value = stripRibuan(displayInput.value);
});
// ── Info santri via AJAX saat halaman load ──────────────────────────
(function () {
var idSantri = '{{ $transaksi->santri->id_santri }}';
fetch('{{ url("admin/uang-saku/santri-info") }}/' + idSantri)
.then(function(r) { return r.json(); })
.then(function(d) {
.then(function (r) { return r.json(); })
.then(function (d) {
var saldoColor = d.saldo_raw >= 0 ? '#6FBA9D' : '#FF8B94';
document.getElementById('info-saldo').innerHTML = '<span style="color:' + saldoColor + '">Rp ' + d.saldo_terakhir + '</span>';
document.getElementById('info-masuk').textContent = 'Rp ' + d.total_pemasukan_bulan_ini;
document.getElementById('info-saldo').innerHTML =
'<span style="color:' + saldoColor + '">Rp ' + d.saldo_terakhir + '</span>';
document.getElementById('info-masuk').textContent = 'Rp ' + d.total_pemasukan_bulan_ini;
document.getElementById('info-keluar').textContent = 'Rp ' + d.total_pengeluaran_bulan_ini;
var html = '';
if (d.transaksi_terakhir.length > 0) {
html = '<small class="text-muted">3 Transaksi Terakhir:</small><table class="data-table" style="margin-top:6px;font-size:.85rem;"><thead><tr><th>Tanggal</th><th>Jenis</th><th>Nominal</th><th>Ket</th></tr></thead><tbody>';
d.transaksi_terakhir.forEach(function(t) {
html = '<small class="text-muted">3 Transaksi Terakhir:</small>';
html += '<table class="data-table" style="margin-top:6px;font-size:.85rem;">';
html += '<thead><tr><th>Tanggal</th><th>Jenis</th><th>Nominal</th><th>Ket</th></tr></thead><tbody>';
d.transaksi_terakhir.forEach(function (t) {
var badge = t.jenis === 'pemasukan'
? '<span class="badge badge-success">Masuk</span>'
: '<span class="badge badge-danger">Keluar</span>';
html += '<tr><td>' + t.tanggal + '</td><td>' + badge + '</td><td>Rp ' + t.nominal + '</td><td>' + t.keterangan + '</td></tr>';
html += '<tr><td>' + t.tanggal + '</td><td>' + badge +
'</td><td>Rp ' + t.nominal + '</td><td>' + t.keterangan + '</td></tr>';
});
html += '</tbody></table>';
}
document.getElementById('info-riwayat').innerHTML = html;
document.getElementById('santri-info').style.display = 'block';
})
.catch(function() {});
.catch(function () {});
})();
</script>
@endsection

View File

@ -12,10 +12,25 @@
<div class="alert alert-danger"><i class="fas fa-exclamation-circle"></i> {{ session('error') }}</div>
@endif
{{-- ── FILTER + KPI ── --}}
<style>
/* ── Row utama santri ── */
.us-row-header { transition: background .15s; }
.us-row-header:hover { background: var(--primary-light, #f0fdf7); }
/* ── Divider vertikal ── */
.vdiv {
width: 1px;
height: 48px;
background: #e2e8f0;
flex-shrink: 0;
align-self: center;
}
</style>
{{-- ── FILTER + KPI ── --}}
<div class="content-box" style="margin-bottom:16px;">
<form method="GET" action="{{ route('admin.uang-saku.index') }}" id="filterForm"
style="display:flex; flex-wrap:wrap; gap:10px; align-items:flex-end; margin-bottom:18px;">
style="display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin-bottom:18px;">
@if(request('search')) <input type="hidden" name="search" value="{{ request('search') }}"> @endif
@if(request('sort')) <input type="hidden" name="sort" value="{{ request('sort') }}"> @endif
@ -28,7 +43,6 @@
<input type="date" name="sampai" class="form-control" value="{{ $sampai }}" style="width:155px;">
</div>
{{-- Preset cepat --}}
<div style="display:flex;gap:5px;flex-wrap:wrap;align-self:flex-end;">
@php
$bulanIniDari = now()->startOfMonth()->format('Y-m-d');
@ -37,136 +51,107 @@
$isHariIni = $dari === now()->format('Y-m-d') && $sampai === now()->format('Y-m-d');
@endphp
<button type="button" onclick="setPreset('today')"
class="btn btn-sm {{ $isHariIni ? 'btn-primary' : 'btn-secondary' }}">
Hari Ini
</button>
class="btn btn-sm {{ $isHariIni ? 'btn-primary' : 'btn-secondary' }}">Hari Ini</button>
<button type="button" onclick="setPreset('month')"
class="btn btn-sm {{ $isBulanIni ? 'btn-primary' : 'btn-secondary' }}">
Bulan Ini
</button>
class="btn btn-sm {{ $isBulanIni ? 'btn-primary' : 'btn-secondary' }}">Bulan Ini</button>
<button type="submit" class="btn btn-primary btn-sm">
<i class="fas fa-filter"></i> Terapkan
</button>
</div>
</form>
{{--
┌─────────────────────────────────────────────────────────────────────┐
│ KPI Cards │
│ │
│ Baris 1 (dipengaruhi filter tanggal): │
│ • Total Transaksi — jumlah baris transaksi di periode ini │
│ • Total Pemasukan — uang masuk ke santri di periode ini │
│ • Total Pengeluaran — uang keluar dari santri di periode ini │
│ • Selisih (Net) — pemasukan minus pengeluaran di periode ini │
│ positif = lebih banyak uang masuk (surplus) │
│ negatif = lebih banyak uang keluar (defisit)│
│ │
│ Baris 2 (REAL-TIME, tidak terpengaruh filter): │
│ • Total Saldo Seluruh Santri — total uang yang sedang dipegang │
│ oleh seluruh santri aktif saat ini │
└─────────────────────────────────────────────────────────────────────┘
--}}
{{-- KPI Baris 1: berdasarkan filter periode --}}
<div class="row-cards row-cards-4" style="margin-bottom:10px;">
<div class="card card-info">
{{-- KPI Baris 1 --}}
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 15px; margin-bottom:10px;">
<div class="card card-info" style="margin: 0;">
<h3>Total Transaksi</h3>
<p class="card-value">{{ $kpi['total_transaksi'] }}</p>
<p class="card-value">{{ $kpi['total_transaksi'] }} Transaksi</p>
<span class="card-sub">dari {{ $kpi['total_santri'] }} santri</span>
<i class="fas fa-exchange-alt card-icon"></i>
</div>
<div class="card card-success">
<h3>Total Pemasukan</h3>
<div class="card card-success" style="margin: 0;">
<h3>Total Setoran (Masuk)</h3>
<p class="card-value" style="font-size:1.05rem;">Rp {{ number_format($kpi['total_pemasukan'], 0, ',', '.') }}</p>
<span class="card-sub">
{{ \Carbon\Carbon::parse($dari)->format('d M') }} &ndash; {{ \Carbon\Carbon::parse($sampai)->format('d M Y') }}
</span>
<i class="fas fa-arrow-circle-down card-icon"></i>
</div>
<div class="card card-warning">
<h3>Total Pengeluaran</h3>
<div class="card card-warning" style="margin: 0;">
<h3>Total Penarikan (Keluar)</h3>
<p class="card-value" style="font-size:1.05rem;">Rp {{ number_format($kpi['total_pengeluaran'], 0, ',', '.') }}</p>
<span class="card-sub">
{{ \Carbon\Carbon::parse($dari)->format('d M') }} &ndash; {{ \Carbon\Carbon::parse($sampai)->format('d M Y') }}
</span>
<i class="fas fa-arrow-circle-up card-icon"></i>
</div>
<div class="card {{ $kpi['selisih'] >= 0 ? 'card-success' : 'card-danger' }}">
<h3>
Selisih Periode
<span title="Selisih = Total Pemasukan dikurangi Total Pengeluaran pada periode yang dipilih. Surplus berarti lebih banyak uang masuk; Defisit berarti lebih banyak uang keluar."
style="cursor:help;font-size:.75rem;color:var(--text-light);">
<i class="fas fa-question-circle"></i>
</span>
</h3>
<p class="card-value" style="font-size:1.05rem;">
{{ $kpi['selisih'] >= 0 ? '+' : '-' }} Rp {{ number_format(abs($kpi['selisih']), 0, ',', '.') }}
</p>
<span class="card-sub">{{ $kpi['selisih'] >= 0 ? '✓ Surplus periode ini' : '✗ Defisit periode ini' }}</span>
<i class="fas fa-balance-scale card-icon"></i>
</div>
</div>
{{-- KPI Baris 2: real-time, tidak berubah meski filter diganti --}}
{{-- KPI Baris 2: saldo real-time --}}
<div class="row-cards row-cards-1">
<div class="card card-primary" style="border-left: 4px solid var(--primary-color); position:relative;">
<div class="card card-primary" style="border-left:4px solid var(--primary-color);">
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:space-between;">
<div>
<h3 style="margin:0 0 4px;">
Total Saldo Seluruh Santri
</h3>
<h3 style="margin:0 0 4px;">Total Saldo Seluruh Santri</h3>
<p class="card-value" style="font-size:1.4rem;margin:0;color:var(--primary-color);">
Rp {{ number_format($kpi['total_saldo_realtime'], 0, ',', '.') }}
</p>
</div>
<div style="text-align:right;">
<span class="badge badge-info" style="font-size:.8rem;padding:5px 10px;">
<i class="fas fa-clock"></i> Real-time — tidak terpengaruh filter tanggal
</span>
</div>
<span class="badge badge-info" style="font-size:.8rem;padding:5px 10px;">
<i class="fas fa-clock"></i> Akumulasi semua waktu tidak terpengaruh filter
</span>
</div>
<i class="fas fa-piggy-bank card-icon"></i>
</div>
</div>
</div>
{{-- ── DAFTAR SANTRI ── --}}
{{-- ── DAFTAR SANTRI ── --}}
<div class="content-box">
{{-- Toolbar: Tambah + Search + Sort --}}
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:14px; flex-wrap:wrap; gap:10px;">
<a href="{{ route('admin.uang-saku.create') }}" class="btn btn-primary">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
{{-- Toolbar --}}
<div style="display:flex;justify-content:space-between;align-items:center;
margin-bottom:14px;flex-wrap:wrap;gap:10px;">
<div style="display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
{{-- Tombol Tambah hanya untuk pamong --}}
@if($canCrud)
<a href="{{ route('admin.uang-saku.create') }}" class="btn btn-primary">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
@else
<div></div>{{-- spacer agar sort tetap rata kanan --}}
@endif
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
{{-- Sort --}}
<form method="GET" action="{{ route('admin.uang-saku.index') }}" id="sortForm" style="display:flex;gap:6px;align-items:center;">
<form method="GET" action="{{ route('admin.uang-saku.index') }}"
style="display:flex;gap:6px;align-items:center;">
<input type="hidden" name="dari" value="{{ $dari }}">
<input type="hidden" name="sampai" value="{{ $sampai }}">
@if(request('search')) <input type="hidden" name="search" value="{{ request('search') }}"> @endif
<label style="font-size:.79rem;color:var(--text-light);white-space:nowrap;">
<i class="fas fa-sort"></i> Urut:
</label>
<select name="sort" class="form-control form-control-sm" onchange="this.form.submit()" style="width:auto;">
<option value="nama" {{ $sort==='nama' ? 'selected' : '' }}>Nama A–Z</option>
<option value="saldo_asc" {{ $sort==='saldo_asc' ? 'selected' : '' }}>Saldo Terendah</option>
<option value="saldo_desc" {{ $sort==='saldo_desc' ? 'selected' : '' }}>Saldo Tertinggi</option>
<option value="transaksi_desc" {{ $sort==='transaksi_desc'? 'selected' : '' }}>Transaksi Terbanyak</option>
<option value="terakhir" {{ $sort==='terakhir' ? 'selected' : '' }}>Transaksi Terbaru</option>
<select name="sort" class="form-control form-control-sm"
onchange="this.form.submit()" style="width:auto;">
<option value="nama" {{ $sort==='nama' ? 'selected':'' }}>Nama</option>
<option value="saldo_asc" {{ $sort==='saldo_asc' ? 'selected':'' }}>Saldo Terendah</option>
<option value="saldo_desc" {{ $sort==='saldo_desc' ? 'selected':'' }}>Saldo Tertinggi</option>
<option value="transaksi_desc" {{ $sort==='transaksi_desc'? 'selected':'' }}>Transaksi Terbanyak</option>
<option value="terakhir" {{ $sort==='terakhir' ? 'selected':'' }}>Transaksi Terbaru</option>
</select>
</form>
{{-- Search --}}
<form method="GET" action="{{ route('admin.uang-saku.index') }}" style="display:flex;gap:6px;">
<form method="GET" action="{{ route('admin.uang-saku.index') }}"
style="display:flex;gap:6px;">
<input type="hidden" name="dari" value="{{ $dari }}">
<input type="hidden" name="sampai" value="{{ $sampai }}">
<input type="hidden" name="sort" value="{{ $sort }}">
<input type="text" name="search" class="form-control form-control-sm"
placeholder="Cari nama / ID santri..."
value="{{ request('search') }}" style="width:210px;">
<button type="submit" class="btn btn-primary btn-sm"><i class="fas fa-search"></i></button>
<button type="submit" class="btn btn-primary btn-sm">
<i class="fas fa-search"></i>
</button>
@if(request('search'))
<a href="{{ route('admin.uang-saku.index', ['dari'=>$dari,'sampai'=>$sampai,'sort'=>$sort]) }}"
class="btn btn-secondary btn-sm"><i class="fas fa-times"></i></a>
@ -175,75 +160,76 @@ class="btn btn-secondary btn-sm"><i class="fas fa-times"></i></a>
</div>
</div>
{{-- Legend saldo --}}
<div style="display:flex;gap:14px;margin-bottom:12px;flex-wrap:wrap;font-size:.78rem;color:var(--text-light);">
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#6FBA9D;margin-right:4px;"></span>Saldo ≥ Rp 100rb</span>
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#f5a623;margin-right:4px;"></span>Saldo Rp 20rb – 99rb</span>
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#FF8B94;margin-right:4px;"></span>Saldo &lt; Rp 20rb</span>
{{-- Legend warna saldo --}}
<div style="display:flex;gap:14px;margin-bottom:12px;flex-wrap:wrap;
font-size:.78rem;color:var(--text-light);">
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;
background:#6FBA9D;margin-right:4px;"></span>Saldo ≥ Rp 100.000</span>
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;
background:#f5a623;margin-right:4px;"></span>Rp 20.000 99.999</span>
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;
background:#FF8B94;margin-right:4px;"></span>&lt; Rp 20.000</span>
</div>
{{-- List santri --}}
{{-- ── LIST SANTRI ── --}}
@if($santriList->count() > 0)
@foreach($santriList as $santri)
@php
$saldoColor = $santri->saldo_terakhir >= 100000 ? '#6FBA9D'
: ($santri->saldo_terakhir >= 20000 ? '#f5a623' : '#FF8B94');
$saldoDot = $saldoColor;
$sc = $santri->saldo_terakhir >= 100000 ? '#6FBA9D'
: ($santri->saldo_terakhir >= 20000 ? '#f5a623' : '#FF8B94');
@endphp
<div class="content-box us-row" style="margin-bottom:10px;padding:0;overflow:hidden;">
{{-- Baris utama — klik untuk expand --}}
<div class="content-box us-row"
style="margin-bottom:10px;padding:0;overflow:hidden;">
{{-- ── Baris utama ── --}}
<div class="us-row-header"
onclick="toggleDetail('detail-{{ $santri->id_santri }}', this)"
style="display:flex;align-items:center;gap:0;cursor:pointer;padding:13px 16px;flex-wrap:wrap;gap:10px;">
style="display:flex;align-items:center;cursor:pointer;
padding:12px 16px;flex-wrap:wrap;gap:10px;">
{{-- Chevron + Nama --}}
<div style="display:flex;align-items:center;gap:10px;flex:1;min-width:160px;">
<i class="fas fa-chevron-right toggle-arrow"
style="transition:transform .2s;color:var(--text-light);font-size:.8rem;flex-shrink:0;"></i>
style="transition:transform .2s;color:var(--text-light);
font-size:.8rem;flex-shrink:0;"></i>
<div>
<div style="font-weight:700;font-size:.93rem;">{{ $santri->nama_lengkap }}</div>
<div style="font-size:.76rem;color:var(--text-light);">{{ $santri->id_santri }}</div>
<div style="font-weight:700;font-size:.93rem;">
{{ $santri->nama_lengkap }}
</div>
<div style="font-size:.74rem;color:var(--text-light);">
{{ $santri->id_santri }}
</div>
</div>
</div>
{{-- Saldo sekarang (prominent) --}}
<div style="display:flex;flex-direction:column;align-items:center;min-width:120px;">
<div style="font-size:.7rem;color:var(--text-light);margin-bottom:2px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;">Saldo</div>
<div style="font-size:1.05rem;font-weight:800;color:{{ $saldoColor }};display:flex;align-items:center;gap:5px;">
<span style="width:8px;height:8px;border-radius:50%;background:{{ $saldoDot }};flex-shrink:0;display:inline-block;"></span>
{{-- Saldo saat ini (kumulatif) --}}
<div style="display:flex;flex-direction:column;align-items:center;min-width:130px;">
<div style="font-size:.62rem;color:var(--text-light);margin-bottom:2px;
font-weight:600;text-transform:uppercase;letter-spacing:.4px;">
Saldo Saat Ini
</div>
<div style="font-size:1.05rem;font-weight:800;color:{{ $sc }};
display:flex;align-items:center;gap:5px;">
<span style="width:8px;height:8px;border-radius:50%;
background:{{ $sc }};flex-shrink:0;display:inline-block;"></span>
Rp {{ number_format($santri->saldo_terakhir, 0, ',', '.') }}
</div>
<div style="font-size:.6rem;color:#ccc;margin-top:2px;">akumulasi semua waktu</div>
</div>
{{-- Divider --}}
<div style="width:1px;height:36px;background:var(--primary-light);flex-shrink:0;"></div>
<div class="vdiv"></div>
{{-- Pemasukan bulan ini --}}
<div style="display:flex;flex-direction:column;align-items:center;min-width:100px;">
<div style="font-size:.7rem;color:var(--text-light);margin-bottom:2px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;">Masuk Bln Ini</div>
<div style="font-size:.85rem;font-weight:700;color:#6FBA9D;">
+ Rp {{ number_format($santri->pemasukan_bulan, 0, ',', '.') }}
</div>
</div>
{{-- Pengeluaran bulan ini --}}
<div style="display:flex;flex-direction:column;align-items:center;min-width:100px;">
<div style="font-size:.7rem;color:var(--text-light);margin-bottom:2px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;">Keluar Bln Ini</div>
<div style="font-size:.85rem;font-weight:700;color:#FF8B94;">
- Rp {{ number_format($santri->pengeluaran_bulan, 0, ',', '.') }}
</div>
</div>
{{-- Divider --}}
<div style="width:1px;height:36px;background:var(--primary-light);flex-shrink:0;"></div>
{{-- Transaksi + tanggal terakhir --}}
{{-- Jumlah transaksi + tanggal terakhir --}}
<div style="display:flex;flex-direction:column;align-items:center;min-width:90px;">
<div style="font-size:.7rem;color:var(--text-light);margin-bottom:2px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;">Transaksi</div>
<span class="badge badge-info" style="font-size:.74rem;">{{ $santri->transaksi_bulan_ini }}x bln ini</span>
<div style="font-size:.62rem;color:var(--text-light);margin-bottom:2px;
font-weight:600;text-transform:uppercase;letter-spacing:.4px;">Transaksi</div>
<span class="badge badge-info" style="font-size:.74rem;">
{{ $santri->transaksi_periode }}x di periode
</span>
@if($santri->transaksi_terakhir_tgl)
<div style="font-size:.7rem;color:var(--text-light);margin-top:3px;">
<div style="font-size:.66rem;color:var(--text-light);margin-top:3px;">
terakhir {{ \Carbon\Carbon::parse($santri->transaksi_terakhir_tgl)->format('d/m/Y') }}
</div>
@endif
@ -251,10 +237,13 @@ class="btn btn-secondary btn-sm"><i class="fas fa-times"></i></a>
{{-- Aksi --}}
<div style="display:flex;gap:5px;flex-shrink:0;" onclick="event.stopPropagation()">
<a href="{{ route('admin.uang-saku.create') }}?id_santri={{ $santri->id_santri }}"
class="btn btn-success btn-sm" title="Tambah Transaksi">
<i class="fas fa-plus"></i>
</a>
{{-- Tombol tambah transaksi: hanya pamong --}}
@if($canCrud)
<a href="{{ route('admin.uang-saku.create') }}?id_santri={{ $santri->id_santri }}"
class="btn btn-success btn-sm" title="Tambah Transaksi">
<i class="fas fa-plus"></i>
</a>
@endif
<a href="{{ route('admin.uang-saku.riwayat', $santri->id_santri) }}"
class="btn btn-primary btn-sm" title="Riwayat Lengkap">
<i class="fas fa-history"></i>
@ -262,7 +251,7 @@ class="btn btn-primary btn-sm" title="Riwayat Lengkap">
</div>
</div>
{{-- Detail transaksi (collapsed) --}}
{{-- ── Detail transaksi (collapsed) ── --}}
<div id="detail-{{ $santri->id_santri }}"
style="display:none;border-top:1px solid var(--primary-light);padding:12px 16px;">
@if($santri->transaksi_terbaru->isNotEmpty())
@ -274,7 +263,7 @@ class="btn btn-primary btn-sm" title="Riwayat Lengkap">
<th>Jenis</th>
<th>Nominal</th>
<th>Keterangan</th>
<th>Saldo</th>
<th>Saldo Sesudah</th>
<th class="text-center">Aksi</th>
</tr>
</thead>
@ -284,25 +273,38 @@ class="btn btn-primary btn-sm" title="Riwayat Lengkap">
<td>{{ $tx->tanggal_transaksi->format('d/m/Y') }}</td>
<td>
@if($tx->jenis_transaksi === 'pemasukan')
<span class="badge badge-success"><i class="fas fa-arrow-down"></i> Masuk</span>
<span class="badge badge-success">
<i class="fas fa-arrow-down"></i> Masuk
</span>
@else
<span class="badge badge-danger"><i class="fas fa-arrow-up"></i> Keluar</span>
<span class="badge badge-danger">
<i class="fas fa-arrow-up"></i> Keluar
</span>
@endif
</td>
<td class="nominal-highlight">{{ $tx->nominal_format }}</td>
<td class="nominal-highlight">Rp {{ number_format($tx->nominal, 0, ',', '.') }}</td>
<td><div class="content-preview">{{ $tx->keterangan ?? '-' }}</div></td>
<td style="font-weight:600;color:{{ $tx->saldo_sesudah >= 0 ? '#6FBA9D' : '#FF8B94' }};">
<td style="font-weight:600;
color:{{ $tx->saldo_sesudah >= 0 ? '#6FBA9D' : '#FF8B94' }};">
Rp {{ number_format($tx->saldo_sesudah, 0, ',', '.') }}
</td>
<td class="text-center">
<div style="display:flex;gap:4px;justify-content:center;">
<a href="{{ route('admin.uang-saku.show', $tx->id) }}" class="btn btn-primary btn-sm"><i class="fas fa-eye"></i></a>
<a href="{{ route('admin.uang-saku.edit', $tx->id) }}" class="btn btn-warning btn-sm"><i class="fas fa-edit"></i></a>
<form action="{{ route('admin.uang-saku.destroy', $tx->id) }}" method="POST"
style="display:inline;" onsubmit="return confirm('Yakin hapus transaksi ini?')">
@csrf @method('DELETE')
<button class="btn btn-danger btn-sm"><i class="fas fa-trash"></i></button>
</form>
<a href="{{ route('admin.uang-saku.show', $tx->id) }}"
class="btn btn-primary btn-sm"><i class="fas fa-eye"></i></a>
{{-- Edit & Hapus: hanya pamong --}}
@if($canCrud)
<a href="{{ route('admin.uang-saku.edit', $tx->id) }}"
class="btn btn-warning btn-sm"><i class="fas fa-edit"></i></a>
<form action="{{ route('admin.uang-saku.destroy', $tx->id) }}"
method="POST" style="display:inline;"
onsubmit="return confirm('Yakin hapus transaksi ini?')">
@csrf @method('DELETE')
<button class="btn btn-danger btn-sm">
<i class="fas fa-trash"></i>
</button>
</form>
@endif
</div>
</td>
</tr>
@ -319,52 +321,66 @@ class="btn btn-secondary btn-sm">
</div>
@endif
@else
<p class="text-muted" style="margin:0;">Belum ada transaksi.</p>
<p class="text-muted" style="margin:0;font-size:.85rem;">
<i class="fas fa-info-circle"></i>
Tidak ada transaksi pada periode ini.
<a href="{{ route('admin.uang-saku.riwayat', $santri->id_santri) }}">
Lihat semua riwayat
</a>
</p>
@endif
</div>
</div>
@endforeach
<div style="margin-top:14px;">{{ $santriList->links() }}</div>
@else
<div class="empty-state">
<i class="fas fa-wallet"></i>
<h3>Belum Ada Data</h3>
<p>Belum ada santri dengan transaksi uang saku.</p>
<a href="{{ route('admin.uang-saku.create') }}" class="btn btn-success">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
@if($canCrud)
<a href="{{ route('admin.uang-saku.create') }}" class="btn btn-success">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
@endif
</div>
@endif
</div>
<script>
// ═══════════════════════════════════════════════════════════════════
// TOGGLE DETAIL ROW
// ═══════════════════════════════════════════════════════════════════
function toggleDetail(id, el) {
var detail = document.getElementById(id);
var arrow = el.querySelector('.toggle-arrow');
var open = detail.style.display !== 'none';
detail.style.display = open ? 'none' : 'block';
detail.style.display = open ? 'none' : 'block';
arrow.style.transform = open ? 'rotate(0deg)' : 'rotate(90deg)';
}
// ═══════════════════════════════════════════════════════════════════
// PRESET FILTER TANGGAL
// ═══════════════════════════════════════════════════════════════════
function setPreset(type) {
var form = document.getElementById('filterForm');
var dari = form.querySelector('[name=dari]');
var sampai = form.querySelector('[name=sampai]');
var today = new Date();
var pad = n => String(n).padStart(2, '0');
var ymd = d => d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
var pad = function (n) { return String(n).padStart(2, '0'); };
var ymd = function (d) {
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
};
if (type === 'today') {
dari.value = ymd(today);
sampai.value = ymd(today);
} else if (type === 'month') {
var first = new Date(today.getFullYear(), today.getMonth(), 1);
var last = new Date(today.getFullYear(), today.getMonth() + 1, 0);
dari.value = ymd(first);
sampai.value = ymd(last);
dari.value = sampai.value = ymd(today);
} else {
dari.value = ymd(new Date(today.getFullYear(), today.getMonth(), 1));
sampai.value = ymd(new Date(today.getFullYear(), today.getMonth() + 1, 0));
}
form.submit();
}
</script>
@endsection

View File

@ -2,46 +2,34 @@
@section('content')
<div class="page-header">
<h2><i class="fas fa-history"></i> Riwayat Uang Saku - {{ $santri->nama_lengkap }}</h2>
<h2><i class="fas fa-history"></i> Riwayat Uang Saku &mdash; {{ $santri->nama_lengkap }}</h2>
</div>
{{-- Filter Periode --}}
<div class="content-box" style="margin-bottom: 14px;">
<div class="content-box" style="margin-bottom:14px;">
<form method="GET" action="{{ route('admin.uang-saku.riwayat', $santri->id_santri) }}" id="filterPeriode">
<div style="display: flex; align-items: end; gap: 11px; flex-wrap: wrap;">
<div class="form-group" style="margin-bottom: 0; flex: 1; min-width: 200px;">
<label for="tanggal_dari" style="display: block; margin-bottom: 5px; font-weight: 600;">
<div style="display:flex;align-items:flex-end;gap:11px;flex-wrap:wrap;">
<div class="form-group" style="margin-bottom:0;flex:1;min-width:200px;">
<label for="tanggal_dari" style="display:block;margin-bottom:5px;font-weight:600;">
<i class="fas fa-calendar-alt"></i> Dari Tanggal
</label>
<input type="date"
name="tanggal_dari"
id="tanggal_dari"
class="form-control"
value="{{ $tanggalDari }}"
max="{{ date('Y-m-d') }}">
<input type="date" name="tanggal_dari" id="tanggal_dari"
class="form-control" value="{{ $tanggalDari }}" max="{{ date('Y-m-d') }}">
</div>
<div class="form-group" style="margin-bottom: 0; flex: 1; min-width: 200px;">
<label for="tanggal_sampai" style="display: block; margin-bottom: 5px; font-weight: 600;">
<div class="form-group" style="margin-bottom:0;flex:1;min-width:200px;">
<label for="tanggal_sampai" style="display:block;margin-bottom:5px;font-weight:600;">
<i class="fas fa-calendar-check"></i> Sampai Tanggal
</label>
<input type="date"
name="tanggal_sampai"
id="tanggal_sampai"
class="form-control"
value="{{ $tanggalSampai }}"
max="{{ date('Y-m-d') }}">
<input type="date" name="tanggal_sampai" id="tanggal_sampai"
class="form-control" value="{{ $tanggalSampai }}" max="{{ date('Y-m-d') }}">
</div>
<div style="display: flex; gap: 10px;">
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary">
<i class="fas fa-filter"></i> Terapkan
</button>
<button type="button" class="btn btn-success" onclick="setBulanIni()">
<i class="fas fa-calendar-day"></i> Bulan Ini
</button>
<a href="{{ route('admin.uang-saku.riwayat', $santri->id_santri) }}" class="btn btn-secondary">
<i class="fas fa-redo"></i> Reset
</a>
@ -50,336 +38,467 @@ class="form-control"
</form>
</div>
{{-- Info Periode --}}
<div class="info-box" style="margin-bottom: 14px;">
<p style="margin: 0;">
<i class="fas fa-info-circle"></i>
<strong>Periode:</strong>
{{ $periodeDari->format('d F Y') }} - {{ $periodeSampai->format('d F Y') }}
({{ $periodeDari->diffInDays($periodeSampai) + 1 }} hari)
</p>
</div>
{{-- ═══════════════════════════════════════════════════
PERSAMAAN SALDO PERIODE
════════════════════════════════════════════════════ --}}
<!-- Summary Cards -->
<div class="row-cards">
<div class="card card-success hover-lift">
<h3>Total Pemasukan</h3>
<div class="card-value">Rp {{ number_format($totalPemasukan, 0, ',', '.') }}</div>
<p style="margin: 10px 0 0 0; font-size: 0.85rem; color: var(--text-light);">
Periode yang dipilih
</p>
<i class="fas fa-arrow-down card-icon"></i>
<div style="margin-bottom:10px;">
<div style="font-size:.78rem;font-weight:600;color:var(--text-light);
text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;">
<i class="fas fa-calculator" style="color:var(--primary-color);"></i>
Ringkasan Periode:
<strong style="color:var(--text-color);">
{{ $periodeDari->format('d M Y') }} {{ $periodeSampai->format('d M Y') }}
</strong>
</div>
<div class="card card-danger hover-lift">
<h3>Total Pengeluaran</h3>
<div class="card-value">Rp {{ number_format($totalPengeluaran, 0, ',', '.') }}</div>
<p style="margin: 10px 0 0 0; font-size: 0.85rem; color: var(--text-light);">
Periode yang dipilih
</p>
<i class="fas fa-arrow-up card-icon"></i>
</div>
<div class="card card-info hover-lift">
<h3>Selisih</h3>
<div class="card-value" style="color: {{ ($totalPemasukan - $totalPengeluaran) >= 0 ? '#6FBA9D' : '#FF8B94' }}">
Rp {{ number_format($totalPemasukan - $totalPengeluaran, 0, ',', '.') }}
{{-- Persamaan saldo --}}
<div style="display:grid;
grid-template-columns: 1fr 32px 1fr 32px 1fr 32px 1fr;
gap:0;align-items:stretch;
border-radius:12px;overflow:hidden;
border:1.5px solid #e2e8f0;background:#fff;
margin-bottom:10px;">
{{-- Saldo Awal --}}
<div style="padding:14px 16px;border-right:1px solid #e2e8f0;">
<div style="font-size:.66rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:5px;">
Saldo Awal Periode
</div>
<div style="font-size:1rem;font-weight:800;color:#4a5568;">
Rp {{ number_format($saldoAwalPeriode, 0, ',', '.') }}
</div>
<div style="font-size:.66rem;color:#bbb;margin-top:3px;">
per {{ $periodeDari->format('d M Y') }}
@if($saldoAwalPeriode == 0)
<span style="color:#f5a623;">(belum ada saldo sebelumnya)</span>
@endif
</div>
</div>
<p style="margin: 10px 0 0 0; font-size: 0.85rem; color: var(--text-light);">
Pemasukan - Pengeluaran
</p>
<i class="fas fa-chart-line card-icon"></i>
</div>
<div class="card card-primary hover-lift">
<h3>Saldo Saat Ini</h3>
<div class="card-value" style="color: {{ $saldoTerakhir >= 0 ? '#6FBA9D' : '#FF8B94' }}">
Rp {{ number_format($saldoTerakhir, 0, ',', '.') }}
{{-- + --}}
<div style="display:flex;align-items:center;justify-content:center;
font-size:1.3rem;font-weight:300;color:#6FBA9D;
border-right:1px solid #e2e8f0;">+</div>
{{-- Pemasukan --}}
<div style="padding:14px 16px;border-right:1px solid #e2e8f0;">
<div style="font-size:.66rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:5px;">
Pemasukan Periode
</div>
<div style="font-size:1rem;font-weight:800;color:#6FBA9D;">
Rp {{ number_format($totalPemasukan, 0, ',', '.') }}
</div>
<div style="font-size:.66rem;color:#bbb;margin-top:3px;">uang masuk di periode ini</div>
</div>
{{-- --}}
<div style="display:flex;align-items:center;justify-content:center;
font-size:1.3rem;font-weight:300;color:#FF8B94;
border-right:1px solid #e2e8f0;">&minus;</div>
{{-- Pengeluaran --}}
<div style="padding:14px 16px;border-right:1px solid #e2e8f0;">
<div style="font-size:.66rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:5px;">
Pengeluaran Periode
</div>
<div style="font-size:1rem;font-weight:800;color:#FF8B94;">
Rp {{ number_format($totalPengeluaran, 0, ',', '.') }}
</div>
<div style="font-size:.66rem;color:#bbb;margin-top:3px;">uang keluar di periode ini</div>
</div>
{{-- = --}}
<div style="display:flex;align-items:center;justify-content:center;
font-size:1.3rem;font-weight:300;color:#718096;
border-right:1px solid #e2e8f0;">=</div>
{{-- Saldo Akhir --}}
<div style="padding:14px 16px;background:linear-gradient(135deg,#f0fdf7 0%,#e6f9f2 100%);">
<div style="font-size:.66rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:5px;">
Saldo Akhir Periode
</div>
<div style="font-size:1rem;font-weight:800;
color:{{ $saldoAkhirPeriode >= 0 ? '#38a169' : '#e53e3e' }};">
Rp {{ number_format($saldoAkhirPeriode, 0, ',', '.') }}
</div>
<div style="font-size:.66rem;color:#bbb;margin-top:3px;">
per {{ $periodeSampai->format('d M Y') }}
</div>
</div>
</div>
{{-- Notif jika saldo akhir periode saldo saat ini --}}
@if(round($saldoAkhirPeriode) !== round($saldoTerakhir))
<div style="background:#fffbeb;border:1px solid #fbd38d;border-radius:9px;
padding:10px 14px;font-size:.8rem;color:#744210;margin-bottom:10px;
display:flex;align-items:flex-start;gap:8px;">
<i class="fas fa-exclamation-triangle" style="color:#f5a623;margin-top:2px;flex-shrink:0;"></i>
<div>
<strong>Catatan:</strong>
Saldo akhir periode (Rp {{ number_format($saldoAkhirPeriode, 0, ',', '.') }})
berbeda dengan <strong>Saldo Saat Ini (Rp {{ number_format($saldoTerakhir, 0, ',', '.') }})</strong>
karena ada transaksi di luar rentang
{{ $periodeDari->format('d M') }}{{ $periodeSampai->format('d M Y') }}.
</div>
</div>
@endif
{{-- Saldo saat ini (real-time) --}}
<div style="background:linear-gradient(135deg,#ebf8ff 0%,#bee3f8 40%,#ebf8ff 100%);
border:1.5px solid #90cdf4;border-radius:12px;
padding:14px 18px;display:flex;justify-content:space-between;
align-items:center;flex-wrap:wrap;gap:10px;">
<div>
<div style="font-size:.66rem;color:#2b6cb0;text-transform:uppercase;
letter-spacing:.5px;margin-bottom:4px;font-weight:600;">
<i class="fas fa-wallet"></i> Saldo Saat Ini (Akumulasi Semua Waktu)
</div>
<div style="font-size:1.4rem;font-weight:900;
color:{{ $saldoTerakhir >= 0 ? '#2b6cb0' : '#e53e3e' }};">
Rp {{ number_format($saldoTerakhir, 0, ',', '.') }}
</div>
</div>
<div style="font-size:.78rem;color:#2c5282;text-align:right;max-width:300px;line-height:1.5;">
<i class="fas fa-info-circle"></i>
Total uang santri saat ini, dihitung dari <strong>seluruh transaksi sejak awal</strong>,
bukan hanya periode yang ditampilkan.
</div>
<p style="margin: 10px 0 0 0; font-size: 0.85rem; color: var(--text-light);">
Total keseluruhan
</p>
<i class="fas fa-wallet card-icon"></i>
</div>
</div>
<!-- Grafik -->
<div class="content-box" style="margin-bottom: 22px;">
<h3 style="margin-bottom: 14px; color: var(--primary-color);">
<i class="fas fa-chart-line"></i> Grafik Arus Uang Saku
</h3>
<canvas id="chartUangSaku" style="max-height: 400px;"></canvas>
{{-- ═══════════════════════════════════════════════════
GRAFIK PERJALANAN SALDO
════════════════════════════════════════════════════ --}}
<div class="content-box" style="margin-bottom:22px;">
<div style="display:flex;justify-content:space-between;align-items:flex-start;
flex-wrap:wrap;gap:8px;margin-bottom:16px;">
<div>
<h3 style="margin:0 0 4px;color:var(--primary-color);">
<i class="fas fa-chart-area"></i> Perjalanan Saldo
</h3>
<div style="font-size:.78rem;color:var(--text-light);">
{{ $periodeDari->format('d M Y') }} {{ $periodeSampai->format('d M Y') }}
&bull; Grafik menunjukkan nilai saldo aktual setiap hari
</div>
</div>
{{-- Mini legend --}}
<div style="display:flex;gap:14px;align-items:center;font-size:.78rem;color:var(--text-light);">
<span>
<span style="display:inline-block;width:24px;height:3px;background:#6FBA9D;
vertical-align:middle;border-radius:2px;margin-right:5px;"></span>
Saldo naik (pemasukan)
</span>
<span>
<span style="display:inline-block;width:24px;height:3px;background:#FF8B94;
vertical-align:middle;border-radius:2px;margin-right:5px;"></span>
Saldo turun (pengeluaran)
</span>
</div>
</div>
<canvas id="chartSaldo" style="max-height:380px;"></canvas>
</div>
<!-- Action Buttons -->
<div class="content-box" style="margin-bottom: 14px;">
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 11px;">
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
{{-- Action Buttons --}}
<div class="content-box" style="margin-bottom:14px;">
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:11px;">
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<a href="{{ route('admin.uang-saku.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali ke Daftar
<i class="fas fa-arrow-left"></i> Kembali
</a>
<a href="{{ route('admin.santri.show', $santri->id) }}" class="btn btn-primary">
<i class="fas fa-user"></i> Profil Santri
</a>
</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="{{ route('admin.uang-saku.create') }}?id_santri={{ $santri->id_santri }}" class="btn btn-success">
{{-- Tambah Transaksi: hanya pamong --}}
@if($canCrud)
<a href="{{ route('admin.uang-saku.create') }}?id_santri={{ $santri->id_santri }}"
class="btn btn-success">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
</div>
@endif
</div>
</div>
<!-- Tabel Riwayat -->
{{-- Tabel Riwayat --}}
<div class="content-box">
<h3 style="margin-bottom: 15px; color: var(--text-color);">
<i class="fas fa-list"></i> Daftar Transaksi
<h3 style="margin-bottom:12px;color:var(--text-color);">
<i class="fas fa-list"></i> Daftar Transaksi
@if($transaksi->total() > 0)
<span style="color: var(--text-light);">({{ $transaksi->total() }} transaksi)</span>
<span style="color:var(--text-light);font-weight:400;">
({{ $transaksi->total() }} transaksi)
</span>
@endif
</h3>
@if($transaksi->count() > 0)
<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;
padding:9px 14px;margin-bottom:12px;font-size:.78rem;color:var(--text-light);">
<i class="fas fa-info-circle" style="color:var(--primary-color);"></i>
<strong>Saldo Sebelum</strong> &amp; <strong>Saldo Sesudah</strong>
menunjukkan saldo kumulatif santri sebelum dan setelah tiap transaksi.
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th style="width: 5%;">No</th>
<th style="width: 12%;">ID Transaksi</th>
<th style="width: 12%;">Tanggal</th>
<th style="width: 12%;">Jenis</th>
<th style="width: 15%;">Nominal</th>
<th style="width: 13%;">Saldo Sebelum</th>
<th style="width: 13%;">Saldo Sesudah</th>
<th style="width: 12%;">Keterangan</th>
<th style="width: 6%;" class="text-center">Aksi</th>
<th style="width:4%;">No</th>
<th style="width:11%;">ID</th>
<th style="width:10%;">Tanggal</th>
<th style="width:10%;">Jenis</th>
<th style="width:13%;">Nominal</th>
<th style="width:13%;">Saldo Sebelum</th>
<th style="width:13%;">Saldo Sesudah</th>
<th style="width:20%;">Keterangan</th>
<th style="width:6%;" class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
@foreach($transaksi as $index => $item)
<tr>
<td>{{ $transaksi->firstItem() + $index }}</td>
<td><strong>{{ $item->id_uang_saku }}</strong></td>
<td>{{ $item->tanggal_transaksi->format('d/m/Y') }}</td>
<td>
@if($item->jenis_transaksi === 'pemasukan')
<span class="badge badge-success">
<i class="fas fa-arrow-down"></i> Pemasukan
</span>
@else
<span class="badge badge-danger">
<i class="fas fa-arrow-up"></i> Pengeluaran
</span>
@endif
</td>
<td class="nominal-highlight">
{{ $item->nominal_format }}
</td>
<td>
Rp {{ number_format($item->saldo_sebelum, 0, ',', '.') }}
</td>
<td>
<strong style="color: {{ $item->saldo_sesudah >= 0 ? '#6FBA9D' : '#FF8B94' }}">
{{ $item->saldo_sesudah_format }}
</strong>
</td>
<td>
<div class="content-preview">
{{ $item->keterangan ?? '-' }}
</div>
</td>
<td class="text-center">
<div style="display: flex; gap: 5px; justify-content: center;">
<a href="{{ route('admin.uang-saku.show', $item->id) }}"
class="btn btn-primary btn-sm"
title="Detail">
<i class="fas fa-eye"></i>
</a>
<a href="{{ route('admin.uang-saku.edit', $item->id) }}"
class="btn btn-warning btn-sm"
title="Edit">
<tr>
<td>{{ $transaksi->firstItem() + $index }}</td>
<td><strong>{{ $item->id_uang_saku }}</strong></td>
<td>{{ $item->tanggal_transaksi->format('d/m/Y') }}</td>
<td>
@if($item->jenis_transaksi === 'pemasukan')
<span class="badge badge-success">
<i class="fas fa-arrow-down"></i> Masuk
</span>
@else
<span class="badge badge-danger">
<i class="fas fa-arrow-up"></i> Keluar
</span>
@endif
</td>
<td class="nominal-highlight">{{ $item->nominal_format }}</td>
<td style="color:#718096;">
Rp {{ number_format($item->saldo_sebelum, 0, ',', '.') }}
</td>
<td>
<strong style="color:{{ $item->saldo_sesudah >= 0 ? '#38a169' : '#e53e3e' }};">
{{ $item->saldo_sesudah_format }}
</strong>
</td>
<td><div class="content-preview">{{ $item->keterangan ?? '-' }}</div></td>
<td class="text-center">
<div style="display:flex;gap:4px;justify-content:center;">
<a href="{{ route('admin.uang-saku.show', $item->id) }}"
class="btn btn-primary btn-sm" title="Detail">
<i class="fas fa-eye"></i>
</a>
{{-- Edit: hanya pamong --}}
@if($canCrud)
<a href="{{ route('admin.uang-saku.edit', $item->id) }}"
class="btn btn-warning btn-sm" title="Edit">
<i class="fas fa-edit"></i>
</a>
</div>
</td>
</tr>
@endif
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div style="margin-top:14px;">{{ $transaksi->links() }}</div>
<div style="margin-top: 14px;">
{{ $transaksi->links() }}
</div>
@else
<div class="empty-state">
<i class="fas fa-calendar-times"></i>
<h3>Tidak Ada Transaksi</h3>
<p>Tidak ada transaksi pada periode {{ $periodeDari->format('d F Y') }} - {{ $periodeSampai->format('d F Y') }}</p>
<a href="{{ route('admin.uang-saku.create') }}?id_santri={{ $santri->id_santri }}" class="btn btn-success">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
<p>
Tidak ada transaksi pada periode
{{ $periodeDari->format('d F Y') }} &ndash; {{ $periodeSampai->format('d F Y') }}.
</p>
@if(round($saldoTerakhir) > 0)
<p style="font-size:.85rem;color:var(--text-light);">
Santri memiliki saldo
<strong>Rp {{ number_format($saldoTerakhir, 0, ',', '.') }}</strong>
dari transaksi di periode lain.
</p>
@endif
@if($canCrud)
<a href="{{ route('admin.uang-saku.create') }}?id_santri={{ $santri->id_santri }}"
class="btn btn-success">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
@endif
</div>
@endif
</div>
<!-- Chart.js Library -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
// Data untuk grafik dari Laravel
const dataGrafik = @json($dataGrafik);
// Format data untuk Chart.js (per hari)
const labels = dataGrafik.map(item => {
const date = new Date(item.tanggal);
return date.toLocaleDateString('id-ID', { day: 'numeric', month: 'short' });
});
const pemasukan = dataGrafik.map(item => parseFloat(item.pemasukan));
const pengeluaran = dataGrafik.map(item => parseFloat(item.pengeluaran));
// Konfigurasi Chart
const ctx = document.getElementById('chartUangSaku').getContext('2d');
const chartUangSaku = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Pemasukan',
data: pemasukan,
borderColor: '#6FBA9D',
backgroundColor: 'rgba(111, 186, 157, 0.1)',
borderWidth: 3,
fill: true,
tension: 0.4,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: '#6FBA9D',
pointBorderColor: '#fff',
pointBorderWidth: 2
},
{
label: 'Pengeluaran',
data: pengeluaran,
borderColor: '#FF8B94',
backgroundColor: 'rgba(255, 139, 148, 0.1)',
borderWidth: 3,
fill: true,
tension: 0.4,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: '#FF8B94',
pointBorderColor: '#fff',
pointBorderWidth: 2
}
]
},
options: {
responsive: true,
maintainAspectRatio: true,
interaction: {
intersect: false,
mode: 'index'
// ═══════════════════════════════════════════════════════════════
// DATA dari Laravel
// ═══════════════════════════════════════════════════════════════
const rawData = @json($dataGrafikSaldo);
// ═══════════════════════════════════════════════════════════════
// Bangun dataset perjalanan saldo
// ═══════════════════════════════════════════════════════════════
const labels = [];
const saldoArr = [];
rawData.forEach((d, i) => {
const dt = new Date(d.tanggal + 'T00:00:00');
const opt = { day: 'numeric', month: 'short' };
labels.push(
d.is_awal
? dt.toLocaleDateString('id-ID', opt) + ' (Awal)'
: dt.toLocaleDateString('id-ID', opt)
);
saldoArr.push(d.saldo);
});
function segmentColor(ctx, naik, turun, flat) {
const i = ctx.p1DataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
if (cur > prv) return naik;
if (cur < prv) return turun;
return flat;
}
function buildGradient(ctx) {
const chart = ctx.chart;
const { chartArea } = chart;
if (!chartArea) return 'rgba(111,186,157,0.15)';
const gradient = chart.ctx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);
gradient.addColorStop(0, 'rgba(111,186,157,0.25)');
gradient.addColorStop(0.6, 'rgba(111,186,157,0.06)');
gradient.addColorStop(1, 'rgba(111,186,157,0.0)');
return gradient;
}
const ctx = document.getElementById('chartSaldo').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels,
datasets: [{
label: 'Saldo',
data: saldoArr,
borderWidth: 3,
tension: 0.35,
fill: true,
backgroundColor: buildGradient,
pointRadius: function(ctx) {
const i = ctx.dataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
return cur !== prv || i === 0 || i === saldoArr.length - 1 ? 6 : 3;
},
plugins: {
legend: {
display: true,
position: 'top',
labels: {
padding: 15,
font: {
size: 13,
family: "'Inter', sans-serif"
},
usePointStyle: true,
pointStyle: 'circle'
}
},
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
padding: 12,
titleFont: {
size: 14,
weight: 'bold'
},
bodyFont: {
size: 13
},
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
label += 'Rp ' + new Intl.NumberFormat('id-ID').format(context.parsed.y);
return label;
pointHoverRadius: 8,
pointBackgroundColor: function(ctx) {
const i = ctx.dataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
if (i === 0) return '#718096';
if (cur > prv) return '#6FBA9D';
if (cur < prv) return '#FF8B94';
return '#d1d9e0';
},
pointBorderColor: '#fff',
pointBorderWidth: 2,
segment: {
borderColor: ctx => segmentColor(ctx, '#6FBA9D', '#FF8B94', '#d1d9e0'),
},
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
interaction: { intersect: false, mode: 'index' },
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(30,41,59,0.92)',
padding: 13,
cornerRadius: 10,
titleFont: { size: 13, weight: 'bold' },
bodyFont: { size: 13 },
callbacks: {
title: items => items[0].label,
label: item => {
const i = item.dataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
const diff = cur - prv;
const fmt = v => 'Rp\u00a0' + new Intl.NumberFormat('id-ID').format(v);
let lines = [' Saldo: ' + fmt(cur)];
if (i > 0 && diff !== 0) {
const sign = diff > 0 ? '▲ +' : '▼ ';
lines.push(' ' + sign + fmt(Math.abs(diff))
+ (diff > 0 ? ' (pemasukan)' : ' (pengeluaran)'));
} else if (i > 0) {
lines.push(' — tidak ada transaksi');
}
return lines;
},
labelColor: item => {
const i = item.dataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
const c = i === 0 ? '#718096'
: cur > prv ? '#6FBA9D'
: cur < prv ? '#FF8B94' : '#d1d9e0';
return { borderColor: c, backgroundColor: c, borderRadius: 3 };
}
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return 'Rp ' + new Intl.NumberFormat('id-ID', {
notation: 'compact',
compactDisplay: 'short'
}).format(value);
},
font: {
size: 12
}
},
grid: {
color: 'rgba(111, 186, 157, 0.1)',
drawBorder: false
}
},
x: {
grid: {
display: false,
drawBorder: false
},
ticks: {
font: {
size: 11
},
maxRotation: 45,
minRotation: 45
}
}
},
animation: {
duration: 1500,
easing: 'easeInOutQuart'
}
}
});
// Function untuk set bulan ini
function setBulanIni() {
const today = new Date();
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
const lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
document.getElementById('tanggal_dari').value = firstDay.toISOString().split('T')[0];
document.getElementById('tanggal_sampai').value = lastDay.toISOString().split('T')[0];
document.getElementById('filterPeriode').submit();
},
scales: {
y: {
min: function() {
const minVal = Math.min(...saldoArr);
return Math.max(0, Math.floor(minVal * 0.8 / 10000) * 10000);
}(),
ticks: {
callback: v => 'Rp\u00a0' + new Intl.NumberFormat('id-ID', {
notation: 'compact', compactDisplay: 'short'
}).format(v),
font: { size: 12 },
maxTicksLimit: 8,
},
grid: { color: 'rgba(0,0,0,.05)', drawBorder: false }
},
x: {
grid: { display: false, drawBorder: false },
ticks: {
font: { size: 11 },
maxRotation: 45,
minRotation: 45,
maxTicksLimit: rawData.length > 20 ? 12 : rawData.length,
autoSkip: true,
}
}
},
animation: { duration: 1200, easing: 'easeInOutQuart' }
}
});
// Validasi tanggal
document.getElementById('tanggal_sampai').addEventListener('change', function() {
const dari = document.getElementById('tanggal_dari').value;
const sampai = this.value;
if (dari && sampai && sampai < dari) {
alert('Tanggal sampai tidak boleh lebih kecil dari tanggal dari!');
this.value = dari;
}
});
// ═══════════════════════════════════════════════════════════════
// Helpers
// ═══════════════════════════════════════════════════════════════
function setBulanIni() {
const today = new Date();
const first = new Date(today.getFullYear(), today.getMonth(), 1);
const last = new Date(today.getFullYear(), today.getMonth() + 1, 0);
document.getElementById('tanggal_dari').value = first.toISOString().split('T')[0];
document.getElementById('tanggal_sampai').value = last.toISOString().split('T')[0];
document.getElementById('filterPeriode').submit();
}
document.getElementById('tanggal_sampai').addEventListener('change', function () {
const dari = document.getElementById('tanggal_dari').value;
if (dari && this.value && this.value < dari) {
alert('Tanggal sampai tidak boleh lebih kecil dari tanggal dari!');
this.value = dari;
}
});
</script>
@endsection

View File

@ -12,9 +12,12 @@
<a href="{{ route('admin.uang-saku.riwayat', $transaksi->id_santri) }}" class="btn btn-primary">
<i class="fas fa-history"></i> Lihat Riwayat
</a>
<a href="{{ route('admin.uang-saku.edit', $transaksi->id) }}" class="btn btn-warning">
<i class="fas fa-edit"></i> Edit
</a>
{{-- Edit: hanya pamong --}}
@if($canCrud)
<a href="{{ route('admin.uang-saku.edit', $transaksi->id) }}" class="btn btn-warning">
<i class="fas fa-edit"></i> Edit
</a>
@endif
<a href="{{ route('admin.uang-saku.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
@ -114,19 +117,22 @@
<a href="{{ route('admin.uang-saku.riwayat', $transaksi->id_santri) }}" class="btn btn-primary">
<i class="fas fa-history"></i> Lihat Riwayat
</a>
<a href="{{ route('admin.uang-saku.edit', $transaksi->id) }}" class="btn btn-warning">
<i class="fas fa-edit"></i> Edit Transaksi
</a>
<form action="{{ route('admin.uang-saku.destroy', $transaksi->id) }}"
method="POST"
style="display: inline;"
onsubmit="return confirm('Yakin ingin menghapus transaksi ini?\n\nPerhatian: Saldo transaksi setelahnya akan di-recalculate otomatis.')">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> Hapus
</button>
</form>
{{-- Edit & Hapus: hanya pamong --}}
@if($canCrud)
<a href="{{ route('admin.uang-saku.edit', $transaksi->id) }}" class="btn btn-warning">
<i class="fas fa-edit"></i> Edit Transaksi
</a>
<form action="{{ route('admin.uang-saku.destroy', $transaksi->id) }}"
method="POST"
style="display: inline;"
onsubmit="return confirm('Yakin ingin menghapus transaksi ini?\n\nPerhatian: Saldo transaksi setelahnya akan di-recalculate otomatis.')">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> Hapus
</button>
</form>
@endif
</div>
</div>
@endsection

View File

@ -441,10 +441,18 @@
Route::post('/', [UangSakuController::class, 'store'])->name('store');
Route::get('/santri-info/{id_santri}',[UangSakuController::class, 'santriInfo'])->name('santri-info');
Route::get('/riwayat/{id_santri}', [UangSakuController::class, 'riwayat'])->name('riwayat');
Route::get('/{uangSaku}', [UangSakuController::class, 'show'])->name('show');
Route::get('/{uangSaku}/edit', [UangSakuController::class, 'edit'])->name('edit');
Route::put('/{uangSaku}', [UangSakuController::class, 'update'])->name('update');
Route::delete('/{uangSaku}', [UangSakuController::class, 'destroy'])->name('destroy');
Route::get('/{uangSaku}', [UangSakuController::class, 'show'])
->whereNumber('uangSaku')
->name('show');
Route::get('/{uangSaku}/edit', [UangSakuController::class, 'edit'])
->whereNumber('uangSaku')
->name('edit');
Route::put('/{uangSaku}', [UangSakuController::class, 'update'])
->whereNumber('uangSaku')
->name('update');
Route::delete('/{uangSaku}', [UangSakuController::class, 'destroy'])
->whereNumber('uangSaku')
->name('destroy');
});
});
});

View File

@ -1 +0,0 @@
a:4:{s:6:"_token";s:40:"Rogr1HsMSukvmYR2m3b7pPraVOehwZvcU2GYYNaA";s:9:"_previous";a:1:{s:3:"url";s:44:"http://127.0.0.1:8000/admin/laporan-kegiatan";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:50:"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d";i:1;}

View File

@ -1 +0,0 @@
a:3:{s:6:"_token";s:40:"z2Q3wuWmkF9oCYl0WHpsIaXywzR0FjwyltHaEHA1";s:9:"_previous";a:1:{s:3:"url";s:34:"http://127.0.0.1:8000/santri/login";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}

View File

@ -1 +0,0 @@
a:4:{s:6:"_token";s:40:"TiMKyzlAzRLRL9vdvNjuuT4zMOD7QGCBKyJKG7lC";s:3:"url";a:1:{s:8:"intended";s:58:"http://localhost/admin/laporan-kegiatan?periode=minggu_ini";}s:9:"_previous";a:1:{s:3:"url";s:58:"http://localhost/admin/laporan-kegiatan?periode=minggu_ini";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}

View File

@ -1 +0,0 @@
a:3:{s:6:"_token";s:40:"38E76quLNOyMUAX08jBlb63KKtrUj910NO6H7reC";s:6:"_flash";a:2:{s:3:"new";a:0:{}s:3:"old";a:0:{}}s:9:"_previous";a:1:{s:3:"url";s:33:"http://127.0.0.1:8000/admin/login";}}

View File

@ -0,0 +1 @@
a:4:{s:6:"_token";s:40:"6coVi3IEjegOzvk6b3puL9vN7Cj70VOUTHaB1oEN";s:9:"_previous";a:1:{s:3:"url";s:36:"http://127.0.0.1:8000/admin/kegiatan";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:50:"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d";i:5;}

View File

@ -1 +0,0 @@
a:4:{s:6:"_token";s:40:"JkoSzSFooBHlizoZQ1QgxXKBUyzQsoduDjvLAUrc";s:9:"_previous";a:1:{s:3:"url";s:38:"http://127.0.0.1:8000/admin/kepulangan";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:50:"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d";i:1;}

View File

@ -0,0 +1 @@
a:4:{s:6:"_token";s:40:"J6ggUD8C2cAW8afPm7ZlmET0SBcvC47D9khAiGHB";s:9:"_previous";a:1:{s:3:"url";s:37:"http://127.0.0.1:8000/admin/uang-saku";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}s:50:"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d";i:1;}

View File

@ -1,5 +0,0 @@
<?php $__env->startSection('title', __('Not Found')); ?>
<?php $__env->startSection('code', '404'); ?>
<?php $__env->startSection('message', __('Not Found')); ?>
<?php echo $__env->make('errors::minimal', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\vendor\laravel\framework\src\Illuminate\Foundation\Exceptions/views/404.blade.php ENDPATH**/ ?>

View File

@ -1,417 +0,0 @@

<?php $__env->startSection('content'); ?>
<style>
/* =====================================================
DETAIL SANTRI Riwayat Kehadiran
===================================================== */
.ds-header { display:flex; justify-content:space-between; align-items:flex-start; gap:12px;
margin-bottom:18px; flex-wrap:wrap; }
.ds-header h2 { margin:0; font-size:1.3rem; color:var(--primary-dark); display:flex; align-items:center; gap:9px; }
/* Info santri */
.ds-info-box { background:#fff; border-radius:12px; padding:16px 20px; margin-bottom:14px;
box-shadow:0 2px 10px rgba(0,0,0,0.06); display:flex; justify-content:space-between;
align-items:center; flex-wrap:wrap; gap:12px; }
.ds-info-box h3 { margin:0 0 5px; color:var(--primary-color); font-size:1.1rem; }
.ds-info-meta { font-size:0.83rem; color:#64748b; display:flex; flex-wrap:wrap; gap:10px; }
.ds-info-meta span { display:inline-flex; align-items:center; gap:4px; }
/* KPI cards */
.ds-kpi-grid { display:grid; grid-template-columns:repeat(5,1fr); gap:10px; margin-bottom:14px; }
@media(max-width:900px) { .ds-kpi-grid { grid-template-columns:repeat(3,1fr); } }
@media(max-width:540px) { .ds-kpi-grid { grid-template-columns:repeat(2,1fr); } }
.ds-kpi { background:#fff; border-radius:10px; padding:12px 14px;
box-shadow:0 2px 8px rgba(0,0,0,0.06); display:flex; align-items:center; gap:10px; }
.ds-kpi .ico { width:36px; height:36px; border-radius:9px; display:flex; align-items:center;
justify-content:center; font-size:1.1rem; flex-shrink:0; }
.ds-kpi.hadir .ico { background:#d1fae5; color:#065f46; }
.ds-kpi.terlambat .ico { background:#fff3e0; color:#e65100; }
.ds-kpi.izin .ico { background:#fef3c7; color:#92400e; }
.ds-kpi.sakit .ico { background:#dbeafe; color:#1e40af; }
.ds-kpi.alpa .ico { background:#fee2e2; color:#991b1b; }
.ds-kpi .cnt { font-size:1.4rem; font-weight:800; color:var(--primary-dark); line-height:1; }
.ds-kpi .lbl { font-size:0.74rem; color:#94a3b8; margin-top:2px; }
/* Terlambat note */
.ds-terlambat-note { font-size:0.76rem; color:#6b7280; background:#fff7ed; border:1px solid #fed7aa;
border-radius:8px; padding:6px 12px; margin-bottom:14px;
display:flex; align-items:center; gap:6px; }
.ds-terlambat-note i { color:#ea580c; }
/* Tren chart box */
.ds-chart-box { background:#fff; border-radius:12px; padding:16px 18px; margin-bottom:14px;
box-shadow:0 2px 10px rgba(0,0,0,0.06); }
.ds-chart-box h4 { margin:0 0 14px; color:var(--primary-color); font-size:0.95rem;
display:flex; align-items:center; gap:7px; }
/* Riwayat lengkap dengan tabs */
.ds-riwayat-box { background:#fff; border-radius:12px; overflow:hidden; margin-bottom:14px;
box-shadow:0 2px 10px rgba(0,0,0,0.06); }
.ds-riwayat-head { padding:14px 18px 0; border-bottom:2px solid #f1f5f9; }
.ds-riwayat-head h4 { margin:0 0 12px; color:var(--primary-dark); font-size:0.95rem;
display:flex; align-items:center; gap:7px; }
/* Tabs */
.ds-tabs { display:flex; gap:0; overflow-x:auto; -webkit-overflow-scrolling:touch; }
.ds-tabs::-webkit-scrollbar { height:0; }
.ds-tab { padding:9px 16px; font-size:0.82rem; font-weight:600; border:none; background:transparent;
cursor:pointer; white-space:nowrap; color:#64748b; border-bottom:3px solid transparent;
transition:all 0.15s; text-decoration:none; display:inline-flex; align-items:center; gap:6px; }
.ds-tab:hover { color:var(--primary-color); background:#f0fdf4; }
.ds-tab.active { color:var(--primary-color); border-bottom-color:var(--primary-color); background:#f0fdf4; }
.ds-tab .tab-cnt { background:#e8f7f2; color:var(--primary-color); padding:1px 7px; border-radius:10px;
font-size:0.72rem; font-weight:700; }
.ds-tab.active .tab-cnt { background:var(--primary-color); color:#fff; }
.ds-tab .tab-hadir { background:#d1fae5; color:#065f46; padding:1px 7px; border-radius:10px;
font-size:0.7rem; font-weight:700; }
/* Per-page toolbar */
.ds-toolbar { display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap;
gap:8px; padding:10px 18px; background:#fafafa; border-bottom:1px solid #f1f5f9; }
.ds-toolbar-left { font-size:0.8rem; color:#64748b; }
.ds-toolbar-right { display:flex; align-items:center; gap:8px; font-size:0.8rem; color:#64748b; }
.ds-perpage-group { display:flex; gap:4px; }
.ds-pp-btn { padding:4px 10px; border:1.5px solid #e2e8f0; border-radius:6px; background:#fff;
font-size:0.78rem; font-weight:600; color:#64748b; cursor:pointer;
text-decoration:none; transition:all 0.15s; }
.ds-pp-btn:hover { border-color:var(--primary-color); color:var(--primary-color); }
.ds-pp-btn.active { background:var(--primary-color); color:#fff; border-color:var(--primary-color); }
/* Table */
.ds-table-wrap { overflow-x:auto; }
.ds-table { width:100%; border-collapse:collapse; min-width:540px; }
.ds-table thead th { padding:9px 14px; text-align:left; font-size:0.78rem; font-weight:600;
color:#64748b; background:#f8fafc; border-bottom:1px solid #e2e8f0; }
.ds-table tbody td { padding:9px 14px; font-size:0.83rem; border-bottom:1px solid #f8fafc; vertical-align:middle; }
.ds-table tbody tr:last-child td { border-bottom:none; }
.ds-table tbody tr:hover { background:#f8fafc; }
/* Status badges (inline fallback jika status_badge tidak ada) */
.s-hadir { background:#d1fae5; color:#065f46; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-terlambat { background:#fff3e0; color:#e65100; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-izin { background:#fef3c7; color:#92400e; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-sakit { background:#dbeafe; color:#1e40af; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-alpa { background:#fee2e2; color:#991b1b; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
.s-pulang { background:#f3e8ff; color:#6b21a8; padding:3px 9px; border-radius:9px; font-size:0.74rem; font-weight:700; }
/* Button back */
.btn-ds-back { padding:8px 16px; background:#6b7280; color:#fff; border-radius:8px; font-size:0.82rem;
font-weight:600; text-decoration:none; display:inline-flex; align-items:center; gap:6px;
transition:background 0.18s; }
.btn-ds-back:hover { background:#4b5563; color:#fff; }
/* Empty */
.ds-empty { text-align:center; padding:30px; color:#94a3b8; }
.ds-empty i { font-size:2.5rem; display:block; margin-bottom:10px; opacity:0.35; }
</style>
<?php
$totalHadirEfektif = $stats['_hadir_efektif'] ?? 0;
$totalHadir = $stats['Hadir'] ?? 0;
$totalTerlambat = $stats['Terlambat'] ?? 0;
$totalIzin = $stats['Izin'] ?? 0;
$totalSakit = $stats['Sakit'] ?? 0;
$totalAlpa = $stats['Alpa'] ?? 0;
$persenHadir = ($totalHadirEfektif + $totalIzin + $totalSakit + $totalAlpa) > 0
? round($totalHadirEfektif / ($totalHadirEfektif + $totalIzin + $totalSakit + $totalAlpa) * 100, 1)
: 0;
?>
<div class="ds-header">
<h2><i class="fas fa-user-clock" style="color:var(--primary-color);"></i>
Riwayat Kehadiran: <?php echo e($santri->nama_lengkap); ?>
</h2>
<a href="<?php echo e(route('admin.riwayat-kegiatan.index')); ?>" class="btn-ds-back">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
<div class="ds-info-box">
<div>
<h3><?php echo e($santri->nama_lengkap); ?></h3>
<div class="ds-info-meta">
<span><i class="fas fa-id-card"></i> <strong><?php echo e($santri->id_santri); ?></strong></span>
<span><i class="fas fa-school"></i> <?php echo e($santri->kelas); ?></span>
<span><i class="fas fa-circle" style="font-size:0.5rem; color:#22c55e;"></i>
<span class="badge badge-success" style="font-size:0.76rem;"><?php echo e($santri->status); ?></span>
</span>
<?php if($persenHadir > 0): ?>
<span style="font-weight:700; color:<?php echo e($persenHadir >= 85 ? '#059669' : ($persenHadir >= 70 ? '#d97706' : '#dc2626')); ?>;">
<i class="fas fa-chart-pie"></i> Kehadiran <?php echo e($persenHadir); ?>%
</span>
<?php endif; ?>
</div>
</div>
</div>
<?php if($totalTerlambat > 0): ?>
<div class="ds-terlambat-note">
<i class="fas fa-info-circle"></i>
<strong><?php echo e($totalTerlambat); ?>x Terlambat</strong> dihitung sebagai <strong>Hadir</strong> (bukan Alpa).
Total hadir efektif: <strong><?php echo e($totalHadirEfektif); ?></strong> kali
(<?php echo e($totalHadir); ?> hadir tepat waktu + <?php echo e($totalTerlambat); ?> terlambat).
</div>
<?php endif; ?>
<div class="ds-kpi-grid">
<div class="ds-kpi hadir">
<div class="ico"><i class="fas fa-check-circle"></i></div>
<div>
<div class="cnt"><?php echo e($totalHadirEfektif); ?></div>
<div class="lbl">Hadir Efektif</div>
<?php if($totalTerlambat > 0): ?>
<div style="font-size:0.68rem; color:#94a3b8;"><?php echo e($totalHadir); ?>+<?php echo e($totalTerlambat); ?>tl</div>
<?php endif; ?>
</div>
</div>
<div class="ds-kpi terlambat">
<div class="ico"><i class="fas fa-clock"></i></div>
<div><div class="cnt"><?php echo e($totalTerlambat); ?></div><div class="lbl">Terlambat</div></div>
</div>
<div class="ds-kpi izin">
<div class="ico"><i class="fas fa-envelope"></i></div>
<div><div class="cnt"><?php echo e($totalIzin); ?></div><div class="lbl">Izin</div></div>
</div>
<div class="ds-kpi sakit">
<div class="ico"><i class="fas fa-heartbeat"></i></div>
<div><div class="cnt"><?php echo e($totalSakit); ?></div><div class="lbl">Sakit</div></div>
</div>
<div class="ds-kpi alpa">
<div class="ico"><i class="fas fa-times-circle"></i></div>
<div><div class="cnt"><?php echo e($totalAlpa); ?></div><div class="lbl">Alpa</div></div>
</div>
</div>
<?php if($riwayat30Hari->count() > 0): ?>
<div class="ds-chart-box">
<h4><i class="fas fa-chart-line"></i> Tren Kehadiran 30 Hari Terakhir
<span style="font-size:0.76rem; font-weight:400; color:#94a3b8; margin-left:4px;">
(Hadir + Terlambat)
</span>
</h4>
<canvas id="chartTren" style="max-height:220px;"></canvas>
</div>
<?php endif; ?>
<div class="ds-riwayat-box">
<div class="ds-riwayat-head">
<h4><i class="fas fa-list-alt" style="color:var(--primary-color);"></i> Riwayat Lengkap per Kategori</h4>
<?php if($kategoriList->count() > 0): ?>
<div class="ds-tabs" role="tablist">
<?php $__currentLoopData = $kategoriList; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kat): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$tabParams = array_merge(request()->query(), [
'tab_kat' => $kat->kategori_id,
'per_page' => $perPage,
'page' => 1,
]);
$isActive = ($activeKategori == $kat->kategori_id);
$pctKat = $kat->total > 0 ? round($kat->hadir_efektif / $kat->total * 100) : 0;
?>
<a href="<?php echo e(route('admin.riwayat-kegiatan.detail-santri', $santri->id_santri)); ?>?<?php echo e(http_build_query($tabParams)); ?>"
class="ds-tab <?php echo e($isActive ? 'active' : ''); ?>"
role="tab">
<i class="fas fa-tag" style="font-size:0.72rem;"></i>
<?php echo e($kat->nama_kategori); ?>
<span class="tab-cnt"><?php echo e($kat->total); ?></span>
<?php if($kat->hadir_efektif > 0): ?>
<span class="tab-hadir" title="Hadir efektif"><?php echo e($pctKat); ?>%</span>
<?php endif; ?>
</a>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<?php endif; ?>
</div>
<?php if($riwayats->count() > 0): ?>
<div class="ds-toolbar">
<div class="ds-toolbar-left">
<?php if(!$showAll): ?>
Menampilkan
<strong><?php echo e($riwayats->firstItem()); ?><?php echo e($riwayats->lastItem()); ?></strong>
dari <strong><?php echo e($riwayats->total()); ?></strong> riwayat
<?php else: ?>
Menampilkan <strong>semua <?php echo e($riwayats->total()); ?></strong> riwayat
<?php endif; ?>
<?php $activeKat = $kategoriList->firstWhere('kategori_id', $activeKategori); ?>
<?php if($activeKat): ?>
&mdash; <span style="color:var(--primary-color); font-weight:600;"><?php echo e($activeKat->nama_kategori); ?></span>
<?php endif; ?>
</div>
<div class="ds-toolbar-right">
Tampilkan:
<div class="ds-perpage-group">
<?php $__currentLoopData = [15, 50, 100]; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $pp): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php $ppParams = array_merge(request()->query(), ['per_page' => $pp, 'page' => 1]); ?>
<a href="<?php echo e(route('admin.riwayat-kegiatan.detail-santri', $santri->id_santri)); ?>?<?php echo e(http_build_query($ppParams)); ?>"
class="ds-pp-btn <?php echo e((!$showAll && $perPage == $pp) ? 'active' : ''); ?>">
<?php echo e($pp); ?>
</a>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<?php $allParams = array_merge(request()->query(), ['per_page' => 'all', 'page' => 1]); ?>
<a href="<?php echo e(route('admin.riwayat-kegiatan.detail-santri', $santri->id_santri)); ?>?<?php echo e(http_build_query($allParams)); ?>"
class="ds-pp-btn <?php echo e($showAll ? 'active' : ''); ?>"
title="Tampilkan semua data sekaligus (bisa lambat jika data banyak)">
Semua
</a>
</div>
</div>
</div>
<div class="ds-table-wrap">
<table class="ds-table">
<thead>
<tr>
<th style="width:50px;">No</th>
<th style="width:110px;">Tanggal</th>
<th>Nama Kegiatan</th>
<th style="width:115px; text-align:center;">Status</th>
<th style="width:90px; text-align:center;">Waktu</th>
</tr>
</thead>
<tbody>
<?php $__currentLoopData = $riwayats; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $idx => $riwayat): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<tr>
<td style="color:#94a3b8; font-size:0.78rem;"><?php echo e($riwayats->firstItem() + $idx); ?></td>
<td style="font-size:0.81rem; color:#64748b;">
<?php echo e($riwayat->tanggal->format('d/m/Y')); ?>
<div style="font-size:0.71rem; color:#94a3b8;">
<?php echo e($riwayat->tanggal->locale('id')->isoFormat('ddd')); ?>
</div>
</td>
<td>
<strong style="font-size:0.85rem;"><?php echo e($riwayat->kegiatan->nama_kegiatan); ?></strong>
<div style="font-size:0.74rem; color:#94a3b8; margin-top:1px;">
<i class="fas fa-clock"></i>
<?php echo e(date('H:i', strtotime($riwayat->kegiatan->waktu_mulai))); ?><?php echo e(date('H:i', strtotime($riwayat->kegiatan->waktu_selesai))); ?>
</div>
</td>
<td style="text-align:center;">
<?php if(method_exists($riwayat, 'getStatusBadgeAttribute') || isset($riwayat->status_badge)): ?>
<?php echo $riwayat->status_badge; ?>
<?php else: ?>
<?php $st = $riwayat->status; ?>
<span class="s-<?php echo e(strtolower($st)); ?>"><?php echo e($st); ?></span>
<?php endif; ?>
</td>
<td style="text-align:center; font-size:0.81rem; color:#64748b;">
<?php echo e($riwayat->waktu_absen ? date('H:i', strtotime($riwayat->waktu_absen)) : ''); ?>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</tbody>
</table>
</div>
<?php if(!$showAll && $riwayats->lastPage() > 1): ?>
<div style="padding:12px 16px;">
<?php echo e($riwayats->links()); ?>
</div>
<?php elseif($showAll): ?>
<div style="padding:10px 16px; font-size:0.78rem; color:#94a3b8; background:#fafafa; border-top:1px solid #f1f5f9;">
<i class="fas fa-info-circle"></i> Semua <strong><?php echo e($riwayats->total()); ?></strong> data ditampilkan.
&nbsp;
<?php $backPaged = array_merge(request()->query(), ['per_page' => 15, 'page' => 1]); ?>
<a href="<?php echo e(route('admin.riwayat-kegiatan.detail-santri', $santri->id_santri)); ?>?<?php echo e(http_build_query($backPaged)); ?>"
style="color:var(--primary-color);">Kembali ke paginasi</a>
</div>
<?php endif; ?>
<?php elseif($kategoriList->count() > 0): ?>
<div class="ds-empty">
<i class="fas fa-inbox"></i>
<p>Belum ada riwayat untuk kategori ini.</p>
</div>
<?php else: ?>
<div class="ds-empty">
<i class="fas fa-inbox"></i>
<p>Santri ini belum memiliki riwayat kehadiran apapun.</p>
</div>
<?php endif; ?>
</div>
<?php if($riwayat30Hari->count() > 0): ?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
(function() {
var ctx = document.getElementById('chartTren');
if (!ctx) return;
var labels = <?php echo json_encode($riwayat30Hari->pluck('tanggal')->map(fn($d) => date('d/m', strtotime($d)))); ?>;
var hadir = <?php echo json_encode($riwayat30Hari->pluck('hadir')); ?>;
var total = <?php echo json_encode($riwayat30Hari->pluck('total')); ?>;
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Hadir (termasuk terlambat)',
data: hadir,
borderColor: '#6FBA9D',
backgroundColor: 'rgba(111,186,157,0.12)',
tension: 0.4,
fill: true,
pointRadius: 4,
pointBackgroundColor: '#6FBA9D',
},
{
label: 'Total Kegiatan',
data: total,
borderColor: '#cbd5e1',
backgroundColor: 'transparent',
borderDash: [5, 5],
tension: 0.4,
fill: false,
pointRadius: 0,
}
]
},
options: {
responsive: true,
maintainAspectRatio: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { position: 'bottom', labels: { boxWidth: 12, font: { size: 11 } } }
},
scales: {
y: { beginAtZero: true, ticks: { stepSize: 1 }, grid: { color: '#f1f5f9' } },
x: { grid: { display: false }, ticks: { font: { size: 10 } } }
}
}
});
})();
</script>
<?php endif; ?>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kegiatan/riwayat/detail-santri.blade.php ENDPATH**/ ?>

View File

@ -0,0 +1,704 @@

<?php $__env->startSection('content'); ?>
<?php
// -- Day-tab calculations --
$isoDay = $selectedDate->dayOfWeekIso;
$monOfWeek = $selectedDate->copy()->subDays($isoDay - 1);
$hariMapTab = [
'Senin' => 'Senin','Selasa' => 'Selasa','Rabu' => 'Rabu',
'Kamis' => 'Kamis','Jumat' => 'Jumat','Sabtu' => 'Sabtu','Minggu' => 'Ahad'
];
$activeTab = $hariMapTab[$selectedDate->locale('id')->isoFormat('dddd')] ?? 'Senin';
$todayHari = $hariMapTab[now()->locale('id')->isoFormat('dddd')] ?? 'Senin';
$tabHari = [
['nama' => 'Senin', 'offset' => 0],
['nama' => 'Selasa', 'offset' => 1],
['nama' => 'Rabu', 'offset' => 2],
['nama' => 'Kamis', 'offset' => 3],
['nama' => 'Jumat', 'offset' => 4],
['nama' => 'Sabtu', 'offset' => 5],
['nama' => 'Minggu', 'offset' => 6],
];
$kelompokGroups = $kelasList->groupBy('kelompok.nama_kelompok');
?>
<div class="page-header" style="display:flex; align-items:center; justify-content:space-between;">
<h2><i class="fas fa-tachometer-alt"></i> Dashboard Absensi</h2>
<div style="display:flex; gap:8px;">
<a href="<?php echo e(route('admin.mesin.mapping-santri.index')); ?>"
class="btn btn-sm btn-secondary">
<i class="fas fa-link"></i> Mapping Fingerprint
</a>
<a href="<?php echo e(route('admin.mesin.import.index')); ?>"
class="btn btn-sm btn-success"
style="background:#0F7B6C; border-color:#0F7B6C;">
<i class="fas fa-file-import"></i> Import
</a>
</div>
</div>
<p style="color: var(--text-light); margin-top: 5px; margin-bottom: 14px;">
<?php echo e($selectedDate->locale('id')->isoFormat('dddd, D MMMM Y')); ?>
</p>
<?php if(session('success')): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> <?php echo e(session('success')); ?>
</div>
<?php endif; ?>
<div class="kpi-grid-kegiatan">
<div class="kpi-kegiatan bg-primary">
<i class="fas fa-calendar-day kpi-icon"></i>
<div class="kpi-value"><?php echo e($totalKegiatanHariIni); ?></div>
<div class="kpi-label">Total Kegiatan</div>
<div class="kpi-sub">
<i class="fas fa-<?php echo e($comparisonTotal >= 0 ? 'arrow-up' : 'arrow-down'); ?>"></i>
<?php echo e(abs($comparisonTotal)); ?> vs minggu lalu
</div>
</div>
<div class="kpi-kegiatan bg-success">
<i class="fas fa-check-circle kpi-icon"></i>
<div class="kpi-value"><?php echo e($kegiatanSelesai); ?></div>
<div class="kpi-label">Kegiatan Selesai</div>
<div class="kpi-sub">dari <?php echo e($totalKegiatanHariIni); ?> kegiatan</div>
</div>
<div class="kpi-kegiatan bg-warning">
<i class="fas fa-chart-line kpi-icon"></i>
<div class="kpi-value"><?php echo e($avgKehadiran); ?>%</div>
<div class="kpi-label">Rata-rata Kehadiran</div>
<div class="kpi-sub">
<i class="fas fa-<?php echo e($comparisonAvg >= 0 ? 'arrow-up' : 'arrow-down'); ?>"></i>
<?php echo e(abs($comparisonAvg)); ?>% vs minggu lalu
</div>
</div>
<div class="kpi-kegiatan bg-info">
<i class="fas fa-clock kpi-icon"></i>
<div class="kpi-value"><?php echo e($kegiatanBerlangsung); ?></div>
<div class="kpi-label">Sedang Berlangsung</div>
<?php if($kegiatanBerlangsung > 0): ?>
<div class="kpi-sub"><i class="fas fa-circle" style="color: #90ee90;"></i> Live Now</div>
<?php else: ?>
<div class="kpi-sub">Tidak ada kegiatan</div>
<?php endif; ?>
</div>
</div>
<div class="content-box">
<div class="day-tabs">
<?php $__currentLoopData = $tabHari; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $tab): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$tabDate = $monOfWeek->copy()->addDays($tab['offset']);
$tabDateStr = $tabDate->format('Y-m-d');
$isActive = ($activeTab === $tab['nama']);
$isToday = ($todayHari === $tab['nama'] && now()->format('W') === $selectedDate->format('W'));
$params = array_merge(
request()->only(['kelas', 'kategori_id']),
['tanggal' => $tabDateStr]
);
?>
<a href="<?php echo e(route('admin.kegiatan.index', $params)); ?>"
class="day-tab <?php echo e($isActive ? 'active' : ''); ?> <?php echo e($isToday ? 'today-tab' : ''); ?>">
<span class="day-name"><?php echo e($tab['nama']); ?></span>
<span class="day-date"><?php echo e($tabDate->format('d/m')); ?></span>
</a>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<form method="GET" action="<?php echo e(route('admin.kegiatan.index')); ?>" id="filterForm" class="filter-form-inline" style="margin-top: 10px;">
<input type="hidden" name="kelas" id="kelasInput" value="<?php echo e($selectedKelasId); ?>">
<div class="filter-form-inline" style="gap: 8px;">
<label style="font-weight: 600; white-space: nowrap; margin: 0;">
<i class="fas fa-calendar"></i> Tanggal:
</label>
<input type="date" name="tanggal" class="form-control"
value="<?php echo e($selectedDate->format('Y-m-d')); ?>"
onchange="this.form.submit()" style="max-width: 170px;">
</div>
<div class="filter-form-inline" style="gap: 8px;">
<label style="font-weight: 600; white-space: nowrap; margin: 0;">
<i class="fas fa-tags"></i> Kategori:
</label>
<select name="kategori_id" class="form-control" onchange="this.form.submit()" style="max-width: 180px;">
<option value="">Semua Kategori</option>
<?php $__currentLoopData = $kategoris; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kat): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($kat->kategori_id); ?>" <?php echo e(request('kategori_id') == $kat->kategori_id ? 'selected' : ''); ?>>
<?php echo e($kat->nama_kategori); ?>
</option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
</div>
<div class="filter-form-inline" style="gap: 8px;">
<label style="font-weight: 600; white-space: nowrap; margin: 0;">
<i class="fas fa-school"></i> Kelas:
</label>
<select name="kelas" class="form-control" onchange="this.form.submit()" style="max-width: 200px;">
<option value="">Semua Kelas</option>
<option value="umum" <?php echo e($selectedKelasId === 'umum' ? 'selected' : ''); ?>>Kegiatan Umum</option>
<?php $__currentLoopData = $kelompokGroups; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kelompokNama => $kelasGroup): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<optgroup label="<?php echo e($kelompokNama); ?>">
<?php $__currentLoopData = $kelasGroup; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kelas): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($kelas->id); ?>" <?php echo e($selectedKelasId == $kelas->id ? 'selected' : ''); ?>>
<?php echo e($kelas->nama_kelas); ?>
</option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</optgroup>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
</div>
<button type="button" class="btn btn-sm btn-primary" onclick="setToday()">
<i class="fas fa-calendar-day"></i> Hari Ini
</button>
<button type="button" class="btn btn-sm btn-secondary" onclick="prevDay()">
<i class="fas fa-chevron-left"></i>
</button>
<button type="button" class="btn btn-sm btn-secondary" onclick="nextDay()">
<i class="fas fa-chevron-right"></i>
</button>
</form>
</div>
<?php if(count($insights) > 0): ?>
<div class="content-box" style="margin-top: 14px;">
<h4 style="margin: 0 0 12px; color: var(--primary-color);">
<i class="fas fa-lightbulb"></i> Insight Hari Ini
</h4>
<?php $__currentLoopData = $insights; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $insight): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<div class="insight-item <?php echo e($insight['type']); ?>">
<div class="insight-content">
<div class="insight-message">
<i class="fas fa-<?php echo e($insight['icon']); ?>"></i> <?php echo e($insight['message']); ?>
</div>
<div class="insight-detail"><?php echo e($insight['detail']); ?></div>
</div>
<?php if($insight['action_url']): ?>
<a href="<?php echo e($insight['action_url']); ?>" class="btn btn-sm btn-<?php echo e($insight['type']); ?>">
<?php echo e($insight['action_text']); ?>
</a>
<?php endif; ?>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<?php endif; ?>
<div class="layout-kegiatan" style="margin-top: 14px;">
<div>
<?php if($kegiatanHariIni->count() > 0): ?>
<div class="kegiatan-list">
<?php $__currentLoopData = $kegiatanHariIni; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kegiatan): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$persen = $kegiatan->persen_kehadiran;
$totalSantri = $kegiatan->total_santri_kegiatan;
$totalHadir = $kegiatan->total_hadir; // hadir + terlambat
$totalInput = $kegiatan->total_absensi; // sudah diinput (semua status)
$belumAbsen = $kegiatan->belum_absen; // belum diinput sama sekali
$terlambat = $kegiatan->total_terlambat;
$isUmum = $kegiatan->kelasKegiatan->isEmpty();
// Warna progress bar
$pClass = $persen >= 85 ? 'p-success' : ($persen >= 70 ? 'p-warning' : ($persen >= 50 ? 'p-orange' : 'p-danger'));
// Hitung per kelas: sudah input berapa
$infoPerKelas = $kegiatan->info_per_kelas ?? collect();
?>
<div class="kegiatan-card">
<div class="kegiatan-card-header">
<div class="kegiatan-info">
<h3 class="kegiatan-title">
<i class="fas fa-calendar" style="color: var(--primary-color);"></i>
<?php echo e($kegiatan->nama_kegiatan); ?>
</h3>
<div class="kegiatan-meta">
<span>
<i class="fas fa-clock"></i>
<?php echo e(date('H:i', strtotime($kegiatan->waktu_mulai))); ?> &ndash;
<?php echo e(date('H:i', strtotime($kegiatan->waktu_selesai))); ?>
</span>
<span class="badge badge-info">
<i class="fas fa-tag"></i>
<?php echo e($kegiatan->kategori->nama_kategori); ?>
</span>
<?php if($kegiatan->materi): ?>
<span>
<i class="fas fa-book"></i>
<?php echo e(Str::limit($kegiatan->materi, 40)); ?>
</span>
<?php endif; ?>
<span>
<i class="fas fa-layer-group"></i>
<?php if($isUmum): ?>
<span class="badge badge-secondary">Kegiatan Umum</span>
<?php else: ?>
<?php echo e($kegiatan->kelasKegiatan->pluck('nama_kelas')->implode(', ')); ?>
<?php endif; ?>
</span>
</div>
</div>
<span class="status-badge status-<?php echo e($kegiatan->status_kegiatan); ?>">
<?php if($kegiatan->status_kegiatan == 'belum'): ?>
<i class="fas fa-hourglass-start"></i> Belum Dimulai
<?php elseif($kegiatan->status_kegiatan == 'berlangsung'): ?>
<i class="fas fa-play-circle"></i> Berlangsung
<?php else: ?>
<i class="fas fa-check-circle"></i> Selesai
<?php endif; ?>
</span>
</div>
<div class="kegiatan-progress">
<div class="kegiatan-progress-header" style="margin-bottom: 4px;">
<span style="font-weight: 500;">
<i class="fas fa-users"></i> Kehadiran
<?php if($terlambat > 0): ?>
<span style="font-size: 0.78rem; color: #FF9800; font-weight: 400; margin-left: 4px;">
(termasuk <?php echo e($terlambat); ?> terlambat)
</span>
<?php endif; ?>
</span>
<span style="font-weight: 700; font-size: 1rem; color: <?php echo e($persen >= 85 ? '#059669' : ($persen >= 70 ? '#d97706' : '#dc2626')); ?>;">
<?php echo e($totalHadir); ?>/<?php echo e($totalSantri); ?> santri
&nbsp;<span style="font-size: 0.85rem;">(<?php echo e($persen); ?>%)</span>
</span>
</div>
<div style="display: flex; gap: 12px; margin-bottom: 6px; font-size: 0.78rem; flex-wrap: wrap;">
<span style="color: #059669;">
<i class="fas fa-check"></i>
Sudah diinput: <strong><?php echo e($totalInput); ?></strong> santri
</span>
<?php if($belumAbsen > 0): ?>
<span style="color: #dc2626;">
<i class="fas fa-hourglass-half"></i>
Belum absen: <strong><?php echo e($belumAbsen); ?></strong> santri
</span>
<?php else: ?>
<span style="color: #059669;">
<i class="fas fa-check-double"></i>
Semua sudah diinput
</span>
<?php endif; ?>
</div>
<?php
$hadirMurni = $kegiatan->total_hadir_murni ?? ($totalHadir - $terlambat);
$alpa = $kegiatan->total_alpa ?? 0;
$izin = $kegiatan->total_izin ?? 0;
$sakit = $kegiatan->total_sakit ?? 0;
$pctHadir = $totalSantri > 0 ? round($hadirMurni / $totalSantri * 100) : 0;
$pctTlmbat = $totalSantri > 0 ? round($terlambat / $totalSantri * 100) : 0;
$pctIzin = $totalSantri > 0 ? round($izin / $totalSantri * 100) : 0;
$pctSakit = $totalSantri > 0 ? round($sakit / $totalSantri * 100) : 0;
$pctAlpa = $totalSantri > 0 ? round($alpa / $totalSantri * 100) : 0;
$pctBelum = $totalSantri > 0 ? round($belumAbsen / $totalSantri * 100) : 0;
?>
<div style="height: 22px; background: #f3f4f6; border-radius: 11px; overflow: hidden; display: flex; margin-bottom: 6px;">
<?php if($pctHadir > 0): ?>
<div style="width:<?php echo e($pctHadir); ?>%; background:#22c55e; display:flex; align-items:center; justify-content:center; color:white; font-size:0.7rem; font-weight:700;"
title="Hadir: <?php echo e($hadirMurni); ?>"><?php echo e($hadirMurni > 0 && $pctHadir > 5 ? $hadirMurni : ''); ?></div>
<?php endif; ?>
<?php if($pctTlmbat > 0): ?>
<div style="width:<?php echo e($pctTlmbat); ?>%; background:#FF9800; display:flex; align-items:center; justify-content:center; color:white; font-size:0.7rem; font-weight:700;"
title="Terlambat : <?php echo e($terlambat); ?>"><?php echo e($terlambat > 0 && $pctTlmbat > 5 ? $terlambat : ''); ?></div>
<?php endif; ?>
<?php if($pctIzin > 0): ?>
<div style="width:<?php echo e($pctIzin); ?>%; background:#f59e0b; display:flex; align-items:center; justify-content:center; color:white; font-size:0.7rem; font-weight:700;"
title="Izin: <?php echo e($izin); ?>"><?php echo e($izin > 0 && $pctIzin > 5 ? $izin : ''); ?></div>
<?php endif; ?>
<?php if($pctSakit > 0): ?>
<div style="width:<?php echo e($pctSakit); ?>%; background:#3b82f6; display:flex; align-items:center; justify-content:center; color:white; font-size:0.7rem; font-weight:700;"
title="Sakit: <?php echo e($sakit); ?>"><?php echo e($sakit > 0 && $pctSakit > 5 ? $sakit : ''); ?></div>
<?php endif; ?>
<?php if($pctAlpa > 0): ?>
<div style="width:<?php echo e($pctAlpa); ?>%; background:#ef4444; display:flex; align-items:center; justify-content:center; color:white; font-size:0.7rem; font-weight:700;"
title="Alpa: <?php echo e($alpa); ?>"><?php echo e($alpa > 0 && $pctAlpa > 5 ? $alpa : ''); ?></div>
<?php endif; ?>
<?php if($pctBelum > 0): ?>
<div style="width:<?php echo e($pctBelum); ?>%; background:#d1d5db; display:flex; align-items:center; justify-content:center; color:#6b7280; font-size:0.7rem; font-weight:700;"
title="Belum absen: <?php echo e($belumAbsen); ?>"><?php echo e($belumAbsen > 0 && $pctBelum > 5 ? $belumAbsen : ''); ?></div>
<?php endif; ?>
</div>
<div style="display:flex; gap:10px; flex-wrap:wrap; font-size:0.72rem; color:#6b7280; margin-bottom: 6px;">
<?php if($hadirMurni > 0): ?>
<span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#22c55e;margin-right:2px;"></span>Hadir</span>
<?php endif; ?>
<?php if($terlambat > 0): ?>
<span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#FF9800;margin-right:2px;"></span>Terlambat</span>
<?php endif; ?>
<?php if($izin > 0): ?>
<span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#f59e0b;margin-right:2px;"></span>Izin</span>
<?php endif; ?>
<?php if($sakit > 0): ?>
<span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#3b82f6;margin-right:2px;"></span>Sakit</span>
<?php endif; ?>
<?php if($alpa > 0): ?>
<span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#ef4444;margin-right:2px;"></span>Alpa</span>
<?php endif; ?>
<?php if($belumAbsen > 0): ?>
<span><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#d1d5db;margin-right:2px;"></span>Belum absen</span>
<?php endif; ?>
</div>
<?php if(!$isUmum && $infoPerKelas->count() > 1): ?>
<div style="margin-top: 8px; padding: 8px 10px; background: #f8fafc; border-radius: 8px; border: 1px solid #e2e8f0;">
<div style="font-size: 0.75rem; font-weight: 600; color: #475569; margin-bottom: 5px;">
<i class="fas fa-school"></i> Status per Kelas:
</div>
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
<?php $__currentLoopData = $infoPerKelas; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $infoKls): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
// Hitung santri kelas ini yg sudah diinput dari data absensis
$sudahKelas = $kegiatan->absensis->filter(function($ab) use ($infoKls) {
return ($ab->santri->kelas_name ?? '') === $infoKls['nama'];
})->count();
$totalKls = $infoKls['total_santri'];
$belumKls = max(0, $totalKls - $sudahKelas);
$sudahSemua = $belumKls === 0 && $totalKls > 0;
?>
<div style="display: flex; align-items: center; gap: 4px; padding: 3px 8px;
background: <?php echo e($sudahSemua ? '#dcfce7' : ($sudahKelas > 0 ? '#fef9c3' : '#f1f5f9')); ?>;
border: 1px solid <?php echo e($sudahSemua ? '#86efac' : ($sudahKelas > 0 ? '#fde68a' : '#e2e8f0')); ?>;
border-radius: 6px; font-size: 0.72rem;">
<i class="fas fa-<?php echo e($sudahSemua ? 'check-circle' : ($sudahKelas > 0 ? 'minus-circle' : 'circle')); ?>"
style="color: <?php echo e($sudahSemua ? '#16a34a' : ($sudahKelas > 0 ? '#ca8a04' : '#94a3b8')); ?>;"></i>
<span style="font-weight: 600;"><?php echo e($infoKls['nama']); ?></span>
<span style="color: #64748b;">
<?php echo e($sudahKelas); ?>/<?php echo e($totalKls); ?>
<?php if(!$sudahSemua && $belumKls > 0): ?>
<span style="color: #dc2626;">(<?php echo e($belumKls); ?> belum)</span>
<?php endif; ?>
</span>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
</div>
<?php endif; ?>
</div>
<div class="kegiatan-actions">
<a href="<?php echo e(route('admin.absensi-kegiatan.input', $kegiatan->kegiatan_id)); ?>?tanggal=<?php echo e($selectedDate->format('Y-m-d')); ?>"
class="btn btn-sm btn-primary">
<i class="fas fa-clipboard-check"></i> Input Absensi
</a>
<button type="button" class="btn btn-sm btn-info"
data-id="<?php echo e($kegiatan->kegiatan_id); ?>"
data-tanggal="<?php echo e($selectedDate->format('Y-m-d')); ?>"
onclick="showDetailModal(this.getAttribute('data-id'), this.getAttribute('data-tanggal'))">
<i class="fas fa-eye"></i> Lihat Detail
</button>
<a href="<?php echo e(route('admin.absensi-kegiatan.rekap', $kegiatan->kegiatan_id)); ?>?tanggal=<?php echo e($selectedDate->format('Y-m-d')); ?>"
class="btn btn-sm btn-secondary">
<i class="fas fa-chart-bar"></i> Rekap
</a>
</div>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<?php else: ?>
<div class="empty-state">
<i class="fas fa-calendar-times"></i>
<h3>Tidak Ada Kegiatan Dijadwalkan</h3>
<p>Tidak ada kegiatan untuk
<?php echo e($selectedKelasId ? 'kelas ini' : 'hari ini'); ?>
pada <?php echo e($selectedDate->locale('id')->isoFormat('dddd, D MMMM Y')); ?>
</p>
<a href="<?php echo e(route('admin.kegiatan.create')); ?>" class="btn btn-success">
<i class="fas fa-plus"></i> Buat Kegiatan Baru
</a>
</div>
<?php endif; ?>
</div>
<div>
<div class="heatmap-calendar">
<div class="heatmap-header">
<i class="fas fa-calendar-alt"></i>
<span>Kalender Kehadiran</span>
</div>
<div class="filter-form-inline" style="margin-bottom: 12px;">
<button type="button" class="btn btn-sm btn-secondary" style="padding: 4px 8px;"
onclick="changeHeatmapMonth(-1)">
<i class="fas fa-chevron-left"></i>
</button>
<select id="heatmapMonth" onchange="updateHeatmap()" class="form-control" style="flex: 1;">
<?php for($m = 1; $m <= 12; $m++): ?>
<option value="<?php echo e($m); ?>" <?php echo e($m == now()->month ? 'selected' : ''); ?>>
<?php echo e(\Carbon\Carbon::create(null, $m, 1)->locale('id')->isoFormat('MMMM')); ?>
</option>
<?php endfor; ?>
</select>
<select id="heatmapYear" onchange="updateHeatmap()" class="form-control" style="width: 80px;">
<?php for($y = now()->year - 2; $y <= now()->year + 1; $y++): ?>
<option value="<?php echo e($y); ?>" <?php echo e($y == now()->year ? 'selected' : ''); ?>><?php echo e($y); ?></option>
<?php endfor; ?>
</select>
<button type="button" class="btn btn-sm btn-secondary" style="padding: 4px 8px;"
onclick="changeHeatmapMonth(1)">
<i class="fas fa-chevron-right"></i>
</button>
</div>
<div style="text-align: center; font-weight: 600; color: var(--primary-color); margin-bottom: 10px;"
id="heatmapMonthName">
<?php echo e(now()->locale('id')->isoFormat('MMMM Y')); ?>
</div>
<div class="heatmap-days">
<div class="heatmap-day-label">Sen</div>
<div class="heatmap-day-label">Sel</div>
<div class="heatmap-day-label">Rab</div>
<div class="heatmap-day-label">Kam</div>
<div class="heatmap-day-label">Jum</div>
<div class="heatmap-day-label">Sab</div>
<div class="heatmap-day-label">Ahd</div>
</div>
<div class="heatmap-grid" id="heatmapGrid">
<?php $__currentLoopData = $heatmapData; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $day): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<div class="heatmap-cell heatmap-level-<?php echo e($day['level']); ?> <?php echo e($day['is_today'] ? 'today' : ''); ?>"
data-date="<?php echo e($day['date']); ?>"
data-percentage="<?php echo e($day['percentage']); ?>"
title="<?php echo e(\Carbon\Carbon::parse($day['date'])->locale('id')->isoFormat('dddd, D MMM Y')); ?>: <?php echo e($day['percentage']); ?>%">
<?php echo e(\Carbon\Carbon::parse($day['date'])->format('j')); ?>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<div class="heatmap-legend">
<div class="heatmap-legend-title">Legend:</div>
<div class="heatmap-legend-items">
<div class="heatmap-legend-item">
<div class="heatmap-legend-box heatmap-level-4"></div>
<span>&gt;90%</span>
</div>
<div class="heatmap-legend-item">
<div class="heatmap-legend-box heatmap-level-3"></div>
<span>80-90%</span>
</div>
<div class="heatmap-legend-item">
<div class="heatmap-legend-box heatmap-level-2"></div>
<span>70-80%</span>
</div>
<div class="heatmap-legend-item">
<div class="heatmap-legend-box heatmap-level-1"></div>
<span>&lt;70%</span>
</div>
<div class="heatmap-legend-item">
<div class="heatmap-legend-box heatmap-level-0"></div>
<span>No data</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="detailModal" class="modal-kegiatan">
<div class="modal-kegiatan-panel">
<div class="modal-kegiatan-head">
<h3><i class="fas fa-info-circle"></i> Detail Absensi Kegiatan</h3>
<button class="modal-kegiatan-close" onclick="closeModal()">&times;</button>
</div>
<div class="modal-kegiatan-body" id="modalBody">
<div style="text-align: center; padding: 22px;">
<i class="fas fa-spinner fa-spin" style="font-size: 2rem; color: var(--primary-color);"></i>
<p style="margin-top: 10px;">Memuat data...</p>
</div>
</div>
</div>
</div>
<script>
function setToday() {
var now = new Date();
var y = now.getFullYear();
var m = ('0' + (now.getMonth() + 1)).slice(-2);
var d = ('0' + now.getDate()).slice(-2);
document.querySelector('input[name="tanggal"]').value = y + '-' + m + '-' + d;
document.getElementById('filterForm').submit();
}
function prevDay() {
var currentDate = new Date('<?php echo e($selectedDate->format("Y-m-d")); ?>');
currentDate.setDate(currentDate.getDate() - 1);
document.querySelector('input[name="tanggal"]').value = currentDate.toISOString().split('T')[0];
document.getElementById('filterForm').submit();
}
function nextDay() {
var currentDate = new Date('<?php echo e($selectedDate->format("Y-m-d")); ?>');
currentDate.setDate(currentDate.getDate() + 1);
document.querySelector('input[name="tanggal"]').value = currentDate.toISOString().split('T')[0];
document.getElementById('filterForm').submit();
}
function goToDate(date) {
document.querySelector('input[name="tanggal"]').value = date;
document.getElementById('filterForm').submit();
}
function showDetailModal(kegiatanId, tanggal) {
var modal = document.getElementById('detailModal');
var modalBody = document.getElementById('modalBody');
modal.classList.add('active');
var baseUrl = '<?php echo e(route("admin.kegiatan.detail-modal", ":id")); ?>';
var url = baseUrl.replace(':id', kegiatanId) + '?tanggal=' + tanggal;
fetch(url)
.then(function(r) { return r.text(); })
.then(function(html) { modalBody.innerHTML = html; })
.catch(function() {
modalBody.innerHTML =
'<div style="text-align:center;padding:22px;">' +
'<i class="fas fa-exclamation-circle" style="font-size:2.2rem;color:var(--danger-color);"></i>' +
'<h4 style="margin:20px 0 10px;color:var(--danger-color);">Gagal Memuat Data</h4>' +
'<p style="color:var(--text-light);">Terjadi kesalahan saat memuat detail absensi.</p>' +
'<button class="btn btn-primary" onclick="closeModal()">Tutup</button>' +
'</div>';
});
}
function closeModal() {
document.getElementById('detailModal').classList.remove('active');
}
window.onclick = function(event) {
var modal = document.getElementById('detailModal');
if (event.target === modal) { closeModal(); }
};
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') { closeModal(); }
});
document.getElementById('heatmapGrid').addEventListener('click', function(e) {
var cell = e.target.closest('.heatmap-cell');
if (cell && cell.dataset.date) { goToDate(cell.dataset.date); }
});
function changeHeatmapMonth(delta) {
var monthSelect = document.getElementById('heatmapMonth');
var yearSelect = document.getElementById('heatmapYear');
var month = parseInt(monthSelect.value) + delta;
var year = parseInt(yearSelect.value);
if (month > 12) { month = 1; year++; }
else if (month < 1) { month = 12; year--; }
monthSelect.value = month;
yearSelect.value = year;
updateHeatmap();
}
function updateHeatmap() {
var month = document.getElementById('heatmapMonth').value;
var year = document.getElementById('heatmapYear').value;
var kelasId = document.getElementById('kelasInput').value;
var monthNames = ['Januari','Februari','Maret','April','Mei','Juni',
'Juli','Agustus','September','Oktober','November','Desember'];
document.getElementById('heatmapMonthName').textContent = monthNames[month - 1] + ' ' + year;
var url = '<?php echo e(route("admin.kegiatan.index")); ?>' +
'?heatmap=1&month=' + month + '&year=' + year + '&kelas=' + kelasId;
fetch(url)
.then(function(r) { return r.json(); })
.then(function(data) {
var grid = document.getElementById('heatmapGrid');
grid.innerHTML = '';
var firstDay = new Date(year, month - 1, 1).getDay();
var startDay = firstDay === 0 ? 6 : firstDay - 1;
for (var i = 0; i < startDay; i++) {
var empty = document.createElement('div');
empty.className = 'heatmap-cell';
empty.style.visibility = 'hidden';
grid.appendChild(empty);
}
data.heatmapData.forEach(function(day) {
var cell = document.createElement('div');
var date = new Date(day.date);
var today = new Date();
var isToday = date.toDateString() === today.toDateString();
cell.className = 'heatmap-cell heatmap-level-' + day.level + (isToday ? ' today' : '');
cell.setAttribute('data-date', day.date);
cell.setAttribute('data-percentage', day.percentage);
cell.setAttribute('title', day.title);
cell.onclick = function() { goToDate(day.date); };
cell.textContent = date.getDate();
grid.appendChild(cell);
});
})
.catch(function(err) { console.error('Error loading heatmap:', err); });
}
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kegiatan/data/dashboard.blade.php ENDPATH**/ ?>

View File

@ -1,107 +0,0 @@
<?php
$totalSantri = $kegiatan->total_absensi;
$hdr = $kegiatan->hadir ?? 0;
$tlb = $kegiatan->terlambat ?? 0;
$izn = $kegiatan->izin ?? 0;
$skt = $kegiatan->sakit ?? 0;
$alp = $kegiatan->alpa ?? 0;
$plg = $kegiatan->pulang ?? 0;
$tot = $kegiatan->total_absensi ?? 0;
$pct = fn($n) => $tot > 0 ? round($n / $tot * 100) : 0;
$isUmum = $kegiatan->kelasKegiatan->isEmpty();
?>
<div class="rw-card">
<div class="rw-card-head">
<div style="flex: 1; min-width: 0;">
<div class="rw-card-title">
<i class="fas fa-clipboard-list"></i>
<?php echo e($kegiatan->nama_kegiatan); ?>
</div>
<div class="rw-card-meta">
<span><i class="fas fa-clock"></i>
<?php echo e(date('H:i', strtotime($kegiatan->waktu_mulai))); ?><?php echo e(date('H:i', strtotime($kegiatan->waktu_selesai))); ?>
</span>
<span class="rw-kat-tag"><i class="fas fa-tag"></i> <?php echo e($kegiatan->kategori->nama_kategori); ?></span>
<?php if($isUmum): ?>
<span class="rw-umum-tag"><i class="fas fa-globe"></i> Umum</span>
<?php else: ?>
<?php $__currentLoopData = $kegiatan->kelasKegiatan->take(3); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kls): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<span class="rw-kelas-tag"><?php echo e($kls->nama_kelas); ?></span>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<?php if($kegiatan->kelasKegiatan->count() > 3): ?>
<span class="rw-umum-tag">+<?php echo e($kegiatan->kelasKegiatan->count() - 3); ?> kelas</span>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<?php if(isset($passParams) && $passParams['mode'] === 'hari_ini'): ?>
<div style="font-size: 0.75rem; color: var(--primary-color); font-weight: 700;
background: #f0fdf4; padding: 4px 10px; border-radius: 20px; border: 1px solid #bbf7d0;
white-space: nowrap; flex-shrink: 0;">
<i class="fas fa-calendar-day"></i> <?php echo e($kegiatan->hari); ?>
</div>
<?php endif; ?>
</div>
<div class="rw-card-body">
<?php if($tot > 0): ?>
<div class="rw-stats-row">
<?php if($hdr > 0): ?> <span class="rw-chip hadir"><i class="fas fa-check"></i> <?php echo e($hdr); ?> Hadir</span> <?php endif; ?>
<?php if($tlb > 0): ?> <span class="rw-chip terlambat"><i class="fas fa-clock"></i> <?php echo e($tlb); ?> Terlambat</span> <?php endif; ?>
<?php if($izn > 0): ?> <span class="rw-chip izin"><i class="fas fa-envelope"></i> <?php echo e($izn); ?> Izin</span> <?php endif; ?>
<?php if($skt > 0): ?> <span class="rw-chip sakit"><i class="fas fa-heartbeat"></i> <?php echo e($skt); ?> Sakit</span> <?php endif; ?>
<?php if($alp > 0): ?> <span class="rw-chip alpa"><i class="fas fa-times"></i> <?php echo e($alp); ?> Alpa</span> <?php endif; ?>
<?php if($plg > 0): ?> <span class="rw-chip pulang"><i class="fas fa-home"></i> <?php echo e($plg); ?> Pulang</span> <?php endif; ?>
</div>
<div class="rw-progress-wrap">
<?php if($hdr > 0): ?> <div class="rw-prog-hadir" style="width:<?php echo e($pct($hdr)); ?>%;" title="Hadir <?php echo e($hdr); ?>"></div> <?php endif; ?>
<?php if($tlb > 0): ?> <div class="rw-prog-terlambat" style="width:<?php echo e($pct($tlb)); ?>%;" title="Terlambat <?php echo e($tlb); ?>"></div> <?php endif; ?>
<?php if($izn > 0): ?> <div class="rw-prog-izin" style="width:<?php echo e($pct($izn)); ?>%;" title="Izin <?php echo e($izn); ?>"></div> <?php endif; ?>
<?php if($skt > 0): ?> <div class="rw-prog-sakit" style="width:<?php echo e($pct($skt)); ?>%;" title="Sakit <?php echo e($skt); ?>"></div> <?php endif; ?>
<?php if($alp > 0): ?> <div class="rw-prog-alpa" style="width:<?php echo e($pct($alp)); ?>%;" title="Alpa <?php echo e($alp); ?>"></div> <?php endif; ?>
<?php if($plg > 0): ?> <div class="rw-prog-pulang" style="width:<?php echo e($pct($plg)); ?>%;" title="Pulang <?php echo e($plg); ?>"></div> <?php endif; ?>
</div>
<div style="font-size: 0.74rem; color: #94a3b8;">
Total tercatat: <strong style="color:#374151;"><?php echo e($tot); ?></strong> santri
</div>
<?php else: ?>
<span class="rw-chip none"><i class="fas fa-inbox"></i> Belum ada data absensi</span>
<?php endif; ?>
</div>
<div class="rw-card-foot">
<div class="rw-total-txt">
<?php if($tot > 0): ?>
<i class="fas fa-users" style="color:var(--primary-color);"></i>
<strong><?php echo e($hdr + $tlb); ?></strong> hadir dari <strong><?php echo e($tot); ?></strong> tercatat
<?php else: ?>
<span style="color:#cbd5e1;"><i class="fas fa-info-circle"></i> Belum ada absensi diinput</span>
<?php endif; ?>
</div>
<?php
$detailParams = array_merge($passParams ?? [], ['kategori_id' => request('kategori_id')]);
?>
<a href="<?php echo e(route('admin.riwayat-kegiatan.show', $kegiatan->id)); ?>?<?php echo e(http_build_query($detailParams)); ?>"
class="btn-rw-detail">
<i class="fas fa-users"></i> Lihat Santri
</a>
</div>
</div><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kegiatan/riwayat/_card.blade.php ENDPATH**/ ?>

View File

@ -0,0 +1,395 @@

<?php $__env->startSection('content'); ?>
<div class="page-header">
<h2><i class="fas fa-wallet"></i> Manajemen Uang Saku Santri</h2>
</div>
<?php if(session('success')): ?>
<div class="alert alert-success"><i class="fas fa-check-circle"></i> <?php echo e(session('success')); ?></div>
<?php endif; ?>
<?php if(session('error')): ?>
<div class="alert alert-danger"><i class="fas fa-exclamation-circle"></i> <?php echo e(session('error')); ?></div>
<?php endif; ?>
<style>
/* ── Row utama santri ── */
.us-row-header { transition: background .15s; }
.us-row-header:hover { background: var(--primary-light, #f0fdf7); }
/* ── Divider vertikal ── */
.vdiv {
width: 1px;
height: 48px;
background: #e2e8f0;
flex-shrink: 0;
align-self: center;
}
</style>
<div class="content-box" style="margin-bottom:16px;">
<form method="GET" action="<?php echo e(route('admin.uang-saku.index')); ?>" id="filterForm"
style="display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin-bottom:18px;">
<?php if(request('search')): ?> <input type="hidden" name="search" value="<?php echo e(request('search')); ?>"> <?php endif; ?>
<?php if(request('sort')): ?> <input type="hidden" name="sort" value="<?php echo e(request('sort')); ?>"> <?php endif; ?>
<div>
<label style="font-size:.78rem;color:var(--text-light);display:block;margin-bottom:3px;">Dari Tanggal</label>
<input type="date" name="dari" class="form-control" value="<?php echo e($dari); ?>" style="width:155px;">
</div>
<div>
<label style="font-size:.78rem;color:var(--text-light);display:block;margin-bottom:3px;">Sampai Tanggal</label>
<input type="date" name="sampai" class="form-control" value="<?php echo e($sampai); ?>" style="width:155px;">
</div>
<div style="display:flex;gap:5px;flex-wrap:wrap;align-self:flex-end;">
<?php
$bulanIniDari = now()->startOfMonth()->format('Y-m-d');
$bulanIniSampai = now()->endOfMonth()->format('Y-m-d');
$isBulanIni = $dari === $bulanIniDari && $sampai === $bulanIniSampai;
$isHariIni = $dari === now()->format('Y-m-d') && $sampai === now()->format('Y-m-d');
?>
<button type="button" onclick="setPreset('today')"
class="btn btn-sm <?php echo e($isHariIni ? 'btn-primary' : 'btn-secondary'); ?>">Hari Ini</button>
<button type="button" onclick="setPreset('month')"
class="btn btn-sm <?php echo e($isBulanIni ? 'btn-primary' : 'btn-secondary'); ?>">Bulan Ini</button>
<button type="submit" class="btn btn-primary btn-sm">
<i class="fas fa-filter"></i> Terapkan
</button>
</div>
</form>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 15px; margin-bottom:10px;">
<div class="card card-info" style="margin: 0;">
<h3>Total Transaksi</h3>
<p class="card-value"><?php echo e($kpi['total_transaksi']); ?> Transaksi</p>
<span class="card-sub">dari <?php echo e($kpi['total_santri']); ?> santri</span>
<i class="fas fa-exchange-alt card-icon"></i>
</div>
<div class="card card-success" style="margin: 0;">
<h3>Total Setoran (Masuk)</h3>
<p class="card-value" style="font-size:1.05rem;">Rp <?php echo e(number_format($kpi['total_pemasukan'], 0, ',', '.')); ?></p>
<span class="card-sub">
<?php echo e(\Carbon\Carbon::parse($dari)->format('d M')); ?> &ndash; <?php echo e(\Carbon\Carbon::parse($sampai)->format('d M Y')); ?>
</span>
<i class="fas fa-arrow-circle-down card-icon"></i>
</div>
<div class="card card-warning" style="margin: 0;">
<h3>Total Penarikan (Keluar)</h3>
<p class="card-value" style="font-size:1.05rem;">Rp <?php echo e(number_format($kpi['total_pengeluaran'], 0, ',', '.')); ?></p>
<span class="card-sub">
<?php echo e(\Carbon\Carbon::parse($dari)->format('d M')); ?> &ndash; <?php echo e(\Carbon\Carbon::parse($sampai)->format('d M Y')); ?>
</span>
<i class="fas fa-arrow-circle-up card-icon"></i>
</div>
</div>
<div class="row-cards row-cards-1">
<div class="card card-primary" style="border-left:4px solid var(--primary-color);">
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:space-between;">
<div>
<h3 style="margin:0 0 4px;">Total Saldo Seluruh Santri</h3>
<p class="card-value" style="font-size:1.4rem;margin:0;color:var(--primary-color);">
Rp <?php echo e(number_format($kpi['total_saldo_realtime'], 0, ',', '.')); ?>
</p>
</div>
<span class="badge badge-info" style="font-size:.8rem;padding:5px 10px;">
<i class="fas fa-clock"></i> Akumulasi semua waktu tidak terpengaruh filter
</span>
</div>
<i class="fas fa-piggy-bank card-icon"></i>
</div>
</div>
</div>
<div class="content-box">
<div style="display:flex;justify-content:space-between;align-items:center;
margin-bottom:14px;flex-wrap:wrap;gap:10px;">
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.create')); ?>" class="btn btn-primary">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
<?php else: ?>
<div></div>
<?php endif; ?>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<form method="GET" action="<?php echo e(route('admin.uang-saku.index')); ?>"
style="display:flex;gap:6px;align-items:center;">
<input type="hidden" name="dari" value="<?php echo e($dari); ?>">
<input type="hidden" name="sampai" value="<?php echo e($sampai); ?>">
<?php if(request('search')): ?> <input type="hidden" name="search" value="<?php echo e(request('search')); ?>"> <?php endif; ?>
<label style="font-size:.79rem;color:var(--text-light);white-space:nowrap;">
<i class="fas fa-sort"></i> Urut:
</label>
<select name="sort" class="form-control form-control-sm"
onchange="this.form.submit()" style="width:auto;">
<option value="nama" <?php echo e($sort==='nama' ? 'selected':''); ?>>Nama</option>
<option value="saldo_asc" <?php echo e($sort==='saldo_asc' ? 'selected':''); ?>>Saldo Terendah</option>
<option value="saldo_desc" <?php echo e($sort==='saldo_desc' ? 'selected':''); ?>>Saldo Tertinggi</option>
<option value="transaksi_desc" <?php echo e($sort==='transaksi_desc'? 'selected':''); ?>>Transaksi Terbanyak</option>
<option value="terakhir" <?php echo e($sort==='terakhir' ? 'selected':''); ?>>Transaksi Terbaru</option>
</select>
</form>
<form method="GET" action="<?php echo e(route('admin.uang-saku.index')); ?>"
style="display:flex;gap:6px;">
<input type="hidden" name="dari" value="<?php echo e($dari); ?>">
<input type="hidden" name="sampai" value="<?php echo e($sampai); ?>">
<input type="hidden" name="sort" value="<?php echo e($sort); ?>">
<input type="text" name="search" class="form-control form-control-sm"
placeholder="Cari nama / ID santri..."
value="<?php echo e(request('search')); ?>" style="width:210px;">
<button type="submit" class="btn btn-primary btn-sm">
<i class="fas fa-search"></i>
</button>
<?php if(request('search')): ?>
<a href="<?php echo e(route('admin.uang-saku.index', ['dari'=>$dari,'sampai'=>$sampai,'sort'=>$sort])); ?>"
class="btn btn-secondary btn-sm"><i class="fas fa-times"></i></a>
<?php endif; ?>
</form>
</div>
</div>
<div style="display:flex;gap:14px;margin-bottom:12px;flex-wrap:wrap;
font-size:.78rem;color:var(--text-light);">
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;
background:#6FBA9D;margin-right:4px;"></span>Saldo ≥ Rp 100.000</span>
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;
background:#f5a623;margin-right:4px;"></span>Rp 20.000 99.999</span>
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;
background:#FF8B94;margin-right:4px;"></span>&lt; Rp 20.000</span>
</div>
<?php if($santriList->count() > 0): ?>
<?php $__currentLoopData = $santriList; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $santri): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$sc = $santri->saldo_terakhir >= 100000 ? '#6FBA9D'
: ($santri->saldo_terakhir >= 20000 ? '#f5a623' : '#FF8B94');
?>
<div class="content-box us-row"
style="margin-bottom:10px;padding:0;overflow:hidden;">
<div class="us-row-header"
onclick="toggleDetail('detail-<?php echo e($santri->id_santri); ?>', this)"
style="display:flex;align-items:center;cursor:pointer;
padding:12px 16px;flex-wrap:wrap;gap:10px;">
<div style="display:flex;align-items:center;gap:10px;flex:1;min-width:160px;">
<i class="fas fa-chevron-right toggle-arrow"
style="transition:transform .2s;color:var(--text-light);
font-size:.8rem;flex-shrink:0;"></i>
<div>
<div style="font-weight:700;font-size:.93rem;">
<?php echo e($santri->nama_lengkap); ?>
</div>
<div style="font-size:.74rem;color:var(--text-light);">
<?php echo e($santri->id_santri); ?>
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;align-items:center;min-width:130px;">
<div style="font-size:.62rem;color:var(--text-light);margin-bottom:2px;
font-weight:600;text-transform:uppercase;letter-spacing:.4px;">
Saldo Saat Ini
</div>
<div style="font-size:1.05rem;font-weight:800;color:<?php echo e($sc); ?>;
display:flex;align-items:center;gap:5px;">
<span style="width:8px;height:8px;border-radius:50%;
background:<?php echo e($sc); ?>;flex-shrink:0;display:inline-block;"></span>
Rp <?php echo e(number_format($santri->saldo_terakhir, 0, ',', '.')); ?>
</div>
<div style="font-size:.6rem;color:#ccc;margin-top:2px;">akumulasi semua waktu</div>
</div>
<div class="vdiv"></div>
<div style="display:flex;flex-direction:column;align-items:center;min-width:90px;">
<div style="font-size:.62rem;color:var(--text-light);margin-bottom:2px;
font-weight:600;text-transform:uppercase;letter-spacing:.4px;">Transaksi</div>
<span class="badge badge-info" style="font-size:.74rem;">
<?php echo e($santri->transaksi_periode); ?>x di periode
</span>
<?php if($santri->transaksi_terakhir_tgl): ?>
<div style="font-size:.66rem;color:var(--text-light);margin-top:3px;">
terakhir <?php echo e(\Carbon\Carbon::parse($santri->transaksi_terakhir_tgl)->format('d/m/Y')); ?>
</div>
<?php endif; ?>
</div>
<div style="display:flex;gap:5px;flex-shrink:0;" onclick="event.stopPropagation()">
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.create')); ?>?id_santri=<?php echo e($santri->id_santri); ?>"
class="btn btn-success btn-sm" title="Tambah Transaksi">
<i class="fas fa-plus"></i>
</a>
<?php endif; ?>
<a href="<?php echo e(route('admin.uang-saku.riwayat', $santri->id_santri)); ?>"
class="btn btn-primary btn-sm" title="Riwayat Lengkap">
<i class="fas fa-history"></i>
</a>
</div>
</div>
<div id="detail-<?php echo e($santri->id_santri); ?>"
style="display:none;border-top:1px solid var(--primary-light);padding:12px 16px;">
<?php if($santri->transaksi_terbaru->isNotEmpty()): ?>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Tanggal</th>
<th>Jenis</th>
<th>Nominal</th>
<th>Keterangan</th>
<th>Saldo Sesudah</th>
<th class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
<?php $__currentLoopData = $santri->transaksi_terbaru; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $tx): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<tr>
<td><?php echo e($tx->tanggal_transaksi->format('d/m/Y')); ?></td>
<td>
<?php if($tx->jenis_transaksi === 'pemasukan'): ?>
<span class="badge badge-success">
<i class="fas fa-arrow-down"></i> Masuk
</span>
<?php else: ?>
<span class="badge badge-danger">
<i class="fas fa-arrow-up"></i> Keluar
</span>
<?php endif; ?>
</td>
<td class="nominal-highlight">Rp <?php echo e(number_format($tx->nominal, 0, ',', '.')); ?></td>
<td><div class="content-preview"><?php echo e($tx->keterangan ?? '-'); ?></div></td>
<td style="font-weight:600;
color:<?php echo e($tx->saldo_sesudah >= 0 ? '#6FBA9D' : '#FF8B94'); ?>;">
Rp <?php echo e(number_format($tx->saldo_sesudah, 0, ',', '.')); ?>
</td>
<td class="text-center">
<div style="display:flex;gap:4px;justify-content:center;">
<a href="<?php echo e(route('admin.uang-saku.show', $tx->id)); ?>"
class="btn btn-primary btn-sm"><i class="fas fa-eye"></i></a>
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.edit', $tx->id)); ?>"
class="btn btn-warning btn-sm"><i class="fas fa-edit"></i></a>
<form action="<?php echo e(route('admin.uang-saku.destroy', $tx->id)); ?>"
method="POST" style="display:inline;"
onsubmit="return confirm('Yakin hapus transaksi ini?')">
<?php echo csrf_field(); ?> <?php echo method_field('DELETE'); ?>
<button class="btn btn-danger btn-sm">
<i class="fas fa-trash"></i>
</button>
</form>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</tbody>
</table>
</div>
<?php if($santri->transaksi_terbaru->count() >= 5): ?>
<div style="text-align:center;margin-top:10px;">
<a href="<?php echo e(route('admin.uang-saku.riwayat', $santri->id_santri)); ?>"
class="btn btn-secondary btn-sm">
<i class="fas fa-arrow-right"></i> Lihat Semua Riwayat
</a>
</div>
<?php endif; ?>
<?php else: ?>
<p class="text-muted" style="margin:0;font-size:.85rem;">
<i class="fas fa-info-circle"></i>
Tidak ada transaksi pada periode ini.
<a href="<?php echo e(route('admin.uang-saku.riwayat', $santri->id_santri)); ?>">
Lihat semua riwayat
</a>
</p>
<?php endif; ?>
</div>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<div style="margin-top:14px;"><?php echo e($santriList->links()); ?></div>
<?php else: ?>
<div class="empty-state">
<i class="fas fa-wallet"></i>
<h3>Belum Ada Data</h3>
<p>Belum ada santri dengan transaksi uang saku.</p>
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.create')); ?>" class="btn btn-success">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<script>
// ═══════════════════════════════════════════════════════════════════
// TOGGLE DETAIL ROW
// ═══════════════════════════════════════════════════════════════════
function toggleDetail(id, el) {
var detail = document.getElementById(id);
var arrow = el.querySelector('.toggle-arrow');
var open = detail.style.display !== 'none';
detail.style.display = open ? 'none' : 'block';
arrow.style.transform = open ? 'rotate(0deg)' : 'rotate(90deg)';
}
// ═══════════════════════════════════════════════════════════════════
// PRESET FILTER TANGGAL
// ═══════════════════════════════════════════════════════════════════
function setPreset(type) {
var form = document.getElementById('filterForm');
var dari = form.querySelector('[name=dari]');
var sampai = form.querySelector('[name=sampai]');
var today = new Date();
var pad = function (n) { return String(n).padStart(2, '0'); };
var ymd = function (d) {
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
};
if (type === 'today') {
dari.value = sampai.value = ymd(today);
} else {
dari.value = ymd(new Date(today.getFullYear(), today.getMonth(), 1));
sampai.value = ymd(new Date(today.getFullYear(), today.getMonth() + 1, 0));
}
form.submit();
}
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/uang-saku/index.blade.php ENDPATH**/ ?>

View File

@ -1,777 +0,0 @@

<?php $__env->startSection('content'); ?>
<style>
/* ── Filter Periode ── */
.period-bar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.period-btn {
padding:6px 14px; border-radius:20px; border:1px solid #e2e8f0;
background:#fff; cursor:pointer; font-size:0.85rem; transition:all .2s;
}
.period-btn:hover, .period-btn.active {
background:var(--primary-color); color:#fff; border-color:var(--primary-color);
}
.custom-range { display:none; align-items:center; gap:8px; }
.custom-range.show { display:flex; }
.custom-range input[type=date] {
padding:5px 10px; border:1px solid #e2e8f0; border-radius:6px; font-size:0.85rem;
}
/* ── KPI Strip ── */
.kpi-strip {
display:grid;
grid-template-columns: repeat(5, 1fr);
gap:12px;
margin-bottom:18px;
}
.kpi-card {
background:#fff; border-radius:10px; padding:14px 16px;
box-shadow:0 2px 8px rgba(0,0,0,.06);
border-top:3px solid transparent;
}
.kpi-card.kpi-hadir { border-color:#10B981; }
.kpi-card.kpi-alpa { border-color:#EF4444; }
.kpi-card.kpi-avg { border-color:#3B82F6; }
.kpi-card.kpi-warning{ border-color:#F59E0B; }
.kpi-card.kpi-info { border-color:#6366F1; }
.kpi-label { font-size:0.78rem; color:#6B7280; margin-bottom:4px; }
.kpi-value { font-size:1.45rem; font-weight:700; color:var(--primary-dark); line-height:1.1; }
.kpi-context { font-size:0.75rem; color:#9CA3AF; margin-top:4px; }
.kpi-trend { font-size:0.78rem; margin-top:5px; display:flex; align-items:center; gap:4px; }
.kpi-trend.up { color:#10B981; }
.kpi-trend.down { color:#EF4444; }
.kpi-trend.neutral { color:#6B7280; }
/* ── Dua kolom ── */
.two-col { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-bottom:16px; }
.three-col { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-bottom:16px; }
@media(max-width:900px){
.kpi-strip { grid-template-columns:repeat(2,1fr); }
.two-col { grid-template-columns:1fr; }
.three-col { grid-template-columns:1fr; }
}
/* ── Cards ── */
.panel {
background:#fff; border-radius:10px; padding:16px;
box-shadow:0 2px 8px rgba(0,0,0,.06);
}
.panel-title {
font-size:.93rem; font-weight:600; margin:0 0 12px;
display:flex; align-items:center; gap:8px;
}
.panel-title a { font-size:.78rem; margin-left:auto; color:var(--primary-color); font-weight:400; }
/* ── List item kegiatan ── */
.kg-item {
display:flex; align-items:center; gap:10px;
padding:8px 0; border-bottom:1px solid #f1f5f9; font-size:.84rem;
}
.kg-item:last-child { border-bottom:none; }
.kg-name { flex:1; font-weight:500; }
.kg-context { font-size:.76rem; color:#6B7280; }
.kg-persen {
font-weight:700; font-size:.9rem; min-width:52px; text-align:right;
}
/* ── Anomali ── */
.alert-row {
display:flex; align-items:flex-start; gap:10px;
padding:10px 12px; border-radius:8px; margin-bottom:6px;
font-size:.85rem; border-left:4px solid transparent;
}
.alert-row:last-child { margin-bottom:0; }
.alert-row.a-danger { background:#FEF2F2; border-color:#EF4444; color:#991B1B; }
.alert-row.a-warning { background:#FFFBEB; border-color:#F59E0B; color:#92400E; }
.alert-row.a-info { background:#EFF6FF; border-color:#3B82F6; color:#1E40AF; }
.alert-row.a-success { background:#ECFDF5; border-color:#10B981; color:#065F46; }
.alert-text { flex:1; }
.alert-title { font-weight:600; font-size:.86rem; }
.alert-desc { font-size:.8rem; opacity:.85; margin-top:2px; }
/* ── Kelas chart legend ── */
.kelas-legend { display:flex; gap:16px; flex-wrap:wrap; margin-top:10px; font-size:.78rem; }
.kelas-legend-item { display:flex; align-items:center; gap:5px; color:#6B7280; }
.kelas-legend-dot { width:10px; height:10px; border-radius:50%; flex-shrink:0; }
/* ── Santri perlu perhatian ── */
.spp-item {
display:flex; align-items:center; gap:10px;
padding:7px 0; border-bottom:1px solid #f1f5f9; font-size:.84rem;
}
.spp-item:last-child { border-bottom:none; }
.spp-rank { width:22px; text-align:center; color:#9CA3AF; font-size:.78rem; }
.spp-name { flex:1; font-weight:500; }
.spp-pct { font-weight:700; color:#EF4444; }
.spp-ctx { font-size:.75rem; color:#9CA3AF; }
/* ── Trend chart ── */
.chart-wrap { background:#fff; border-radius:10px; padding:16px; box-shadow:0 2px 8px rgba(0,0,0,.06); margin-bottom:16px; }
.chart-wrap h4 { margin:0 0 10px; font-size:.93rem; color:var(--primary-dark); }
</style>
<div class="page-header">
<h2><i class="fas fa-chart-line"></i> Laporan Kegiatan</h2>
</div>
<div class="content-box" style="margin-bottom:16px;">
<form method="GET" id="periodForm" action="<?php echo e(route('admin.laporan-kegiatan.index')); ?>">
<div class="period-bar">
<span style="font-size:.85rem;color:var(--text-light);">
<i class="fas fa-calendar-alt"></i> Periode:
</span>
<?php $__currentLoopData = ['hari_ini'=>'Hari Ini','minggu_ini'=>'Minggu Ini','bulan_ini'=>'Bulan Ini','semester_ini'=>'Semester','custom'=>'Custom']; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $key=>$label): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<button type="button"
class="period-btn <?php echo e($periode===$key?'active':''); ?>"
onclick="setPeriode('<?php echo e($key); ?>')"><?php echo e($label); ?></button>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<div class="custom-range <?php echo e($periode==='custom'?'show':''); ?>" id="customRange">
<input type="date" name="tanggal_dari"
value="<?php echo e(request('tanggal_dari', $startDate->format('Y-m-d'))); ?>">
<span></span>
<input type="date" name="tanggal_sampai"
value="<?php echo e(request('tanggal_sampai', $endDate->format('Y-m-d'))); ?>">
</div>
<input type="hidden" name="periode" id="periodeInput" value="<?php echo e($periode); ?>">
<button type="submit" class="btn btn-primary btn-sm">
<i class="fas fa-sync-alt"></i> Terapkan
</button>
</div>
</form>
<p style="margin:8px 0 0;font-size:.82rem;color:var(--text-light);">
<i class="fas fa-info-circle"></i> Data: <strong><?php echo e($periodeLabel); ?></strong>
</p>
</div>
<div class="kpi-strip">
<div class="kpi-card kpi-avg">
<div class="kpi-label">Rata-rata Kehadiran</div>
<div class="kpi-value"><?php echo e($kpi['avg_kehadiran']); ?>%</div>
<div class="kpi-context">
<?php echo e(number_format($kpi['total_hadir'])); ?> hadir dari
<?php echo e(number_format($kpi['total_absensi'])); ?> tercatat
</div>
<?php $diffAvg = $kpiComparison['avg_kehadiran']; ?>
<div class="kpi-trend <?php echo e($diffAvg > 0 ? 'up' : ($diffAvg < 0 ? 'down' : 'neutral')); ?>">
<i class="fas fa-arrow-<?php echo e($diffAvg >= 0 ? 'up' : 'down'); ?>"></i>
<?php echo e(abs($diffAvg)); ?>% vs periode sebelumnya
</div>
</div>
<div class="kpi-card kpi-hadir">
<div class="kpi-label">Hadir Efektif</div>
<div class="kpi-value"><?php echo e(number_format($kpi['total_hadir'])); ?></div>
<div class="kpi-context">
termasuk <?php echo e($kpi['total_terlambat']); ?> terlambat
dari <?php echo e(number_format($kpi['total_absensi'])); ?> sesi tercatat
</div>
</div>
<div class="kpi-card kpi-alpa">
<div class="kpi-label">Total Alpa</div>
<div class="kpi-value"><?php echo e(number_format($kpi['total_alpa'])); ?></div>
<div class="kpi-context">
<?php $alpaRate = $kpi['total_absensi'] > 0 ? round($kpi['total_alpa']/$kpi['total_absensi']*100,1) : 0; ?>
<?php echo e($alpaRate); ?>% dari <?php echo e(number_format($kpi['total_absensi'])); ?> tercatat
</div>
</div>
<div class="kpi-card kpi-warning">
<div class="kpi-label">Santri Perlu Perhatian</div>
<div class="kpi-value"><?php echo e($kpi['santri_perlu_perhatian']); ?></div>
<div class="kpi-context">
dari <?php echo e($kpi['total_santri_aktif']); ?> santri aktif
(kehadiran &lt;70%)
</div>
<?php $diffSpp = $kpiComparison['santri_perlu_perhatian']; ?>
<div class="kpi-trend <?php echo e($diffSpp <= 0 ? 'up' : 'down'); ?>">
<i class="fas fa-arrow-<?php echo e($diffSpp <= 0 ? 'down' : 'up'); ?>"></i>
<?php echo e(abs($diffSpp)); ?> vs periode sebelumnya
</div>
</div>
<div class="kpi-card kpi-info">
<div class="kpi-label">Kehadiran Terbaik</div>
<div class="kpi-value" style="font-size:1rem;">
<?php echo e($kpi['kegiatan_terbaik']['nama']); ?>
</div>
<div class="kpi-context">
<?php echo e($kpi['kegiatan_terbaik']['persen']); ?>%
(<?php echo e($kpi['kegiatan_terbaik']['hadir']); ?> hadir dari <?php echo e($kpi['kegiatan_terbaik']['total']); ?> tercatat)
</div>
</div>
</div>
<?php
$buckets = $distribusiSantri['buckets'];
$totalDengan = $distribusiSantri['total'];
?>
<div class="chart-wrap" style="margin-bottom:16px;">
<div style="display:flex;align-items:flex-start;gap:24px;flex-wrap:wrap;">
<div style="position:relative;width:200px;height:200px;flex-shrink:0;">
<canvas id="distribusiChart"></canvas>
<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;">
<div style="font-size:1.6rem;font-weight:700;color:var(--primary-dark);line-height:1;">
<?php echo e($totalDengan); ?>
</div>
<div style="font-size:.72rem;color:#6B7280;margin-top:2px;">santri<br>tercatat</div>
</div>
</div>
<div style="flex:1;min-width:220px;">
<h4 style="margin:0 0 6px;font-size:.93rem;color:var(--primary-dark);">
<i class="fas fa-users"></i> Kondisi Kehadiran Santri
</h4>
<?php $__currentLoopData = $buckets; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $b): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;">
<div style="width:12px;height:12px;border-radius:50%;background:<?php echo e($b['color']); ?>;flex-shrink:0;"></div>
<div style="flex:1;min-width:0;">
<div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:3px;">
<span style="font-size:.83rem;font-weight:600;color:var(--primary-dark);">
<?php echo e($b['label']); ?>
<?php if($b['label']==='Sangat Baik'): ?> <span style="font-weight:400;color:#9CA3AF;font-size:.75rem;">(≥95%)</span>
<?php elseif($b['label']==='Baik'): ?> <span style="font-weight:400;color:#9CA3AF;font-size:.75rem;">(8594%)</span>
<?php elseif($b['label']==='Cukup'): ?> <span style="font-weight:400;color:#9CA3AF;font-size:.75rem;">(7084%)</span>
<?php else: ?> <span style="font-weight:400;color:#9CA3AF;font-size:.75rem;">(&lt;70%)</span>
<?php endif; ?>
</span>
<span style="font-size:.83rem;font-weight:700;color:<?php echo e($b['color']); ?>;">
<?php echo e($b['count']); ?> santri
<span style="font-weight:400;color:#9CA3AF;">(<?php echo e($b['persen']); ?>%)</span>
</span>
</div>
<div style="background:#f1f5f9;border-radius:4px;height:6px;overflow:hidden;">
<div style="width:<?php echo e($b['persen']); ?>%;height:100%;background:<?php echo e($b['color']); ?>;border-radius:4px;"></div>
</div>
</div>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<?php $tanpaData = $kpi['total_santri_aktif'] - $totalDengan; ?>
<?php if($tanpaData > 0): ?>
<p style="margin:6px 0 0;font-size:.76rem;color:#9CA3AF;">
<i class="fas fa-info-circle"></i>
<?php echo e($tanpaData); ?> santri aktif belum punya data absensi di periode ini.
</p>
<?php endif; ?>
</div>
</div>
</div>
<div class="panel" style="margin-bottom:16px;">
<div class="panel-title" style="color:#EF4444;">
<i class="fas fa-bell"></i> Anomali & Perlu Tindak Lanjut
</div>
<?php if(!empty($patterns) && count($patterns) > 0): ?>
<?php $__currentLoopData = $patterns; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $p): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<div class="alert-row a-<?php echo e($p['type']); ?>">
<?php if($p['type']==='danger'): ?>
<i class="fas fa-exclamation-circle" style="margin-top:2px;"></i>
<?php elseif($p['type']==='warning'): ?>
<i class="fas fa-exclamation-triangle" style="margin-top:2px;"></i>
<?php else: ?>
<i class="fas fa-info-circle" style="margin-top:2px;"></i>
<?php endif; ?>
<div class="alert-text">
<div class="alert-title"><?php echo e($p['title']); ?></div>
<div class="alert-desc"><?php echo e($p['description']); ?></div>
</div>
<?php if(!empty($p['action_url'])): ?>
<a href="<?php echo e($p['action_url']); ?>"
class="btn btn-sm btn-<?php echo e($p['type']==='danger'?'danger':($p['type']==='warning'?'warning':'info')); ?>"
style="white-space:nowrap;">
<?php echo e($p['action_text'] ?? 'Lihat'); ?>
</a>
<?php endif; ?>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<?php else: ?>
<div class="alert-row a-success">
<i class="fas fa-check-circle"></i>
<div class="alert-text">
<div class="alert-title">Tidak ada anomali</div>
<div class="alert-desc">Pola kehadiran dalam kondisi normal.</div>
</div>
</div>
<?php endif; ?>
</div>
<?php
$trendLabels = $trendData['labels'] ?? [];
$trendDs = $trendData['datasets'] ?? [];
$avgLine = [];
foreach ($trendLabels as $i => $_) {
$sum=0; $cnt=0;
foreach ($trendDs as $ds) {
if (isset($ds['data'][$i]) && $ds['data'][$i] !== null) { $sum+=$ds['data'][$i]; $cnt++; }
}
$avgLine[] = $cnt > 0 ? round($sum/$cnt,1) : null;
}
?>
<div class="chart-wrap">
<h4><i class="fas fa-chart-line"></i> Trend Kehadiran Rata-rata</h4>
<p style="margin:0 0 10px;font-size:.8rem;color:#6B7280;">
Angka = % hadir efektif (Terlambat dihitung Hadir). Hover untuk detail.
</p>
<div style="position:relative;height:200px;">
<canvas id="trendChart"></canvas>
</div>
</div>
<div class="three-col">
<div class="panel">
<div class="panel-title" style="color:#10B981;">
<i class="fas fa-trophy"></i> Kehadiran Terbaik
</div>
<?php $__empty_1 = true; $__currentLoopData = $topKegiatan; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $i => $kg): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
<div class="kg-item">
<span style="color:#9CA3AF;font-size:.75rem;width:16px;flex-shrink:0;text-align:center;">
<?php echo e($i + 1); ?>
</span>
<div style="flex:1;min-width:0;">
<div style="display:flex;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:2px;">
<div class="kg-name"
style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:.84rem;font-weight:600;">
<?php echo e($kg['nama_kegiatan']); ?>
</div>
<span style="font-weight:700;color:#10B981;font-size:.88rem;flex-shrink:0;">
<?php echo e($kg['persen']); ?>%
</span>
</div>
<div style="background:#f1f5f9;border-radius:4px;height:6px;overflow:hidden;margin-bottom:4px;">
<div style="width:<?php echo e($kg['persen']); ?>%;height:100%;background:#10B981;border-radius:4px;transition:width .5s;"></div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;">
<span class="kg-context">
<?php echo e($kg['nama_kategori'] ?? '-'); ?> · <?php echo e($kg['hari'] ?? '-'); ?>
</span>
<span style="font-size:.74rem;color:#10B981;">
<?php echo e($kg['hadir']); ?>/<?php echo e($kg['total']); ?> sesi
</span>
</div>
</div>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
<p style="color:var(--text-light);font-size:.85rem;text-align:center;padding:16px 0;">
Belum ada data absensi di periode ini.
</p>
<?php endif; ?>
</div>
<div class="panel">
<div class="panel-title" style="color:#EF4444;">
<i class="fas fa-arrow-trend-down"></i> Kehadiran Terendah
</div>
<?php $__empty_1 = true; $__currentLoopData = $bottomKegiatan; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $i => $kg): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
<?php
$warna = $kg['persen'] < 70 ? '#EF4444' : '#F59E0B';
$bgBar = $kg['persen'] < 70 ? '#FEF2F2' : '#FFFBEB';
$kgId = $kg['id'];
?>
<div class="kg-item">
<span style="color:#9CA3AF;font-size:.75rem;width:16px;flex-shrink:0;text-align:center;">
<?php echo e($i + 1); ?>
</span>
<div style="flex:1;min-width:0;">
<div style="display:flex;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:2px;">
<a href="<?php echo e(route('admin.riwayat-kegiatan.show', $kgId)); ?>"
style="text-decoration:none;color:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:.84rem;font-weight:600;flex:1;min-width:0;">
<?php echo e($kg['nama_kegiatan']); ?>
</a>
<span style="font-weight:700;color:<?php echo e($warna); ?>;font-size:.88rem;flex-shrink:0;">
<?php echo e($kg['persen']); ?>%
</span>
</div>
<div style="background:<?php echo e($bgBar); ?>;border-radius:4px;height:6px;overflow:hidden;margin-bottom:4px;">
<div style="width:<?php echo e($kg['persen']); ?>%;height:100%;background:<?php echo e($warna); ?>;border-radius:4px;transition:width .5s;"></div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;">
<span class="kg-context">
<?php echo e($kg['nama_kategori'] ?? '-'); ?> · <?php echo e($kg['hari'] ?? '-'); ?>
</span>
<span style="font-size:.74rem;color:<?php echo e($warna); ?>;">
<?php echo e($kg['hadir']); ?>/<?php echo e($kg['total']); ?> sesi
<?php if(($kg['alpa'] ?? 0) > 0): ?>
· <strong><?php echo e($kg['alpa']); ?>× alpa</strong>
<?php endif; ?>
</span>
</div>
</div>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
<p style="color:var(--text-light);font-size:.85rem;text-align:center;padding:16px 0;">
Belum ada data absensi di periode ini.
</p>
<?php endif; ?>
</div>
<div class="panel">
<div class="panel-title" style="color:#F59E0B;">
<i class="fas fa-user-clock"></i> Santri Perlu Perhatian
<a href="<?php echo e(route('admin.laporan-kegiatan.santri-perlu-perhatian', request()->query())); ?>">
Lihat Semua
</a>
</div>
<p style="font-size:.78rem;color:#6B7280;margin:0 0 10px;">
<?php echo e($kpi['santri_perlu_perhatian']); ?> dari <?php echo e($kpi['total_santri_aktif']); ?> santri aktif
dengan kehadiran &lt;70%
</p>
<?php $__empty_1 = true; $__currentLoopData = $santriPerluPerhatianList; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $i => $s): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
<div class="spp-item">
<span class="spp-rank"><?php echo e($i + 1); ?></span>
<div style="flex:1;min-width:0;">
<div style="display:flex;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:2px;">
<a href="<?php echo e(route('admin.riwayat-kegiatan.detail-santri', $s->id_santri)); ?>"
class="spp-name"
style="text-decoration:none;color:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;">
<?php echo e($s->nama_lengkap); ?>
</a>
<span class="spp-pct" style="flex-shrink:0;"><?php echo e($s->persen); ?>%</span>
</div>
<div style="background:#FEF2F2;border-radius:4px;height:6px;overflow:hidden;margin-bottom:3px;">
<div style="width:<?php echo e($s->persen); ?>%;height:100%;background:#EF4444;border-radius:4px;transition:width .5s;"></div>
</div>
<div class="spp-ctx">
<?php echo e($s->hadir); ?>/<?php echo e($s->total); ?> sesi · <?php echo e($s->alpa); ?>× alpa
</div>
</div>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
<div style="padding:12px;background:#ECFDF5;border-radius:8px;color:#065F46;
font-size:.85rem;display:flex;align-items:center;gap:8px;margin-top:4px;">
<i class="fas fa-check-circle"></i> Semua santri kehadiran ≥70%. Alhamdulillah!
</div>
<?php endif; ?>
</div>
</div>
<?php
/* Flatten semua kelas dari semua kelompok untuk chart */
$chartKelasLabels = [];
$chartKelasData = [];
$chartKelasColors = [];
$chartKelasContext = []; // untuk tooltip: "hadir / total"
$chartKelasGroups = []; // label kelompok per kelas
foreach ($kelasRingkasan as $kelompok) {
foreach ($kelompok['kelas'] as $k) {
$chartKelasLabels[] = $k['nama_kelas'];
$chartKelasData[] = $k['persen'];
$chartKelasColors[] = $k['persen'] >= 85 ? '#10B981' : ($k['persen'] >= 70 ? '#F59E0B' : '#EF4444');
$chartKelasContext[] = $k['hadir'] . ' hadir dari ' . $k['total'] . ' tercatat';
$chartKelasGroups[] = $kelompok['nama_kelompok'];
}
}
$chartHeight = max(180, count($chartKelasLabels) * 34 + 40);
?>
<div class="chart-wrap" style="margin-bottom:16px;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px;flex-wrap:wrap;gap:8px;">
<h4 style="margin:0;">
<i class="fas fa-school"></i> Kehadiran Per Kelas
</h4>
<a href="<?php echo e(route('admin.riwayat-kegiatan.index')); ?>"
style="font-size:.78rem;color:var(--primary-color);">
Detail di Riwayat Kegiatan
</a>
</div>
<p style="margin:0 0 12px;font-size:.78rem;color:#6B7280;">
% hadir efektif Terlambat dihitung Hadir. Hover bar untuk melihat angka lengkap.
</p>
<?php if(count($chartKelasLabels) > 0): ?>
<div style="position:relative;height:<?php echo e($chartHeight); ?>px;">
<canvas id="kelasChart"></canvas>
</div>
<div class="kelas-legend">
<div class="kelas-legend-item">
<div class="kelas-legend-dot" style="background:#10B981;"></div> ≥85% Baik
</div>
<div class="kelas-legend-item">
<div class="kelas-legend-dot" style="background:#F59E0B;"></div> 7084% Cukup
</div>
<div class="kelas-legend-item">
<div class="kelas-legend-dot" style="background:#EF4444;"></div> &lt;70% Perlu Perhatian
</div>
</div>
<?php else: ?>
<p style="color:var(--text-light);font-size:.85rem;text-align:center;padding:20px 0;">
Belum ada data kelas untuk periode ini.
</p>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script>
function setPeriode(p) {
document.getElementById('periodeInput').value = p;
document.querySelectorAll('.period-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
const cr = document.getElementById('customRange');
if (p === 'custom') { cr.classList.add('show'); }
else { cr.classList.remove('show'); document.getElementById('periodForm').submit(); }
}
// ── Distribusi Santri Donut Chart ──
const distribusiBuckets = <?php echo json_encode($buckets, 15, 512) ?>;
new Chart(document.getElementById('distribusiChart'), {
type: 'doughnut',
data: {
labels: distribusiBuckets.map(b => b.label),
datasets: [{
data: distribusiBuckets.map(b => b.count),
backgroundColor: distribusiBuckets.map(b => b.color),
borderWidth: 2,
borderColor: '#fff',
hoverOffset: 8,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '68%',
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: (ctx) => {
const b = distribusiBuckets[ctx.dataIndex];
return [
` ${b.count} santri (${b.persen}%)`,
` dari <?php echo e($totalDengan); ?> santri tercatat`,
];
}
},
backgroundColor: 'rgba(17,24,39,.9)',
titleFont: { size: 13, weight: '600' },
bodyFont: { size: 12 },
padding: 10,
cornerRadius: 8,
}
}
}
});
const labels = <?php echo json_encode($trendLabels, 15, 512) ?>;
const avgLine = <?php echo json_encode($avgLine, 15, 512) ?>;
new Chart(document.getElementById('trendChart'), {
type: 'line',
data: {
labels,
datasets: [{
label: 'Rata-rata Kehadiran',
data: avgLine,
borderColor: '#10B981',
backgroundColor: 'rgba(16,185,129,.1)',
tension: 0.4, fill: true,
pointRadius: 4, pointHoverRadius: 6,
spanGaps: true,
}]
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: ctx => {
const v = ctx.parsed.y;
return v !== null ? `${v}% kehadiran rata-rata` : 'Tidak ada data';
}
}
}
},
scales: {
y: {
min: 0, max: 100,
ticks: { callback: v => v + '%' },
grid: { color: '#f1f5f9' }
},
x: { grid: { display: false } }
}
}
});
// ── Horizontal Bar Chart: Kehadiran Per Kelas ──
<?php if(count($chartKelasLabels) > 0): ?>
const kelasLabels = <?php echo json_encode($chartKelasLabels, 15, 512) ?>;
const kelasData = <?php echo json_encode($chartKelasData, 15, 512) ?>;
const kelasColors = <?php echo json_encode($chartKelasColors, 15, 512) ?>;
const kelasContext = <?php echo json_encode($chartKelasContext, 15, 512) ?>;
const kelasGroups = <?php echo json_encode($chartKelasGroups, 15, 512) ?>;
new Chart(document.getElementById('kelasChart'), {
type: 'bar',
data: {
labels: kelasLabels,
datasets: [{
label: '% Kehadiran',
data: kelasData,
backgroundColor: kelasColors,
borderRadius: 5,
borderSkipped: false,
barThickness: 20,
}]
},
options: {
indexAxis: 'y', // horizontal
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
title: (items) => {
const i = items[0].dataIndex;
return `${kelasLabels[i]} — ${kelasGroups[i]}`;
},
label: (ctx) => {
const i = ctx.dataIndex;
return [
`Kehadiran: ${ctx.parsed.x}%`,
kelasContext[i],
];
},
labelColor: (ctx) => ({
borderColor: kelasColors[ctx.dataIndex],
backgroundColor: kelasColors[ctx.dataIndex],
borderRadius: 3,
}),
},
backgroundColor: 'rgba(17,24,39,.9)',
titleFont: { size: 13, weight: '600' },
bodyFont: { size: 12 },
padding: 10,
cornerRadius: 8,
}
},
scales: {
x: {
min: 0, max: 100,
ticks: { callback: v => v + '%', font: { size: 11 } },
grid: { color: '#f1f5f9' },
// Garis referensi 70% dan 85%
},
y: {
ticks: { font: { size: 11 } },
grid: { display: false },
}
},
// Plugin untuk garis referensi 70% dan 85%
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
title: (items) => {
const i = items[0].dataIndex;
return `${kelasLabels[i]} — ${kelasGroups[i]}`;
},
label: (ctx) => {
const i = ctx.dataIndex;
return [
`Kehadiran: ${ctx.parsed.x}%`,
kelasContext[i],
];
},
},
backgroundColor: 'rgba(17,24,39,.9)',
titleFont: { size: 13, weight: '600' },
bodyFont: { size: 12 },
padding: 10,
cornerRadius: 8,
},
annotation: undefined,
},
},
plugins: [{
// Plugin custom: garis vertikal referensi 70% dan 85%
id: 'refLines',
afterDraw(chart) {
const { ctx, chartArea: { top, bottom }, scales: { x } } = chart;
[{ v: 70, color: '#F59E0B', label: '70%' }, { v: 85, color: '#10B981', label: '85%' }].forEach(ref => {
const xPx = x.getPixelForValue(ref.v);
ctx.save();
ctx.beginPath();
ctx.setLineDash([4, 4]);
ctx.moveTo(xPx, top);
ctx.lineTo(xPx, bottom);
ctx.strokeStyle = ref.color;
ctx.lineWidth = 1.5;
ctx.globalAlpha = 0.6;
ctx.stroke();
ctx.globalAlpha = 1;
ctx.fillStyle = ref.color;
ctx.font = '10px sans-serif';
ctx.fillText(ref.label, xPx + 3, top + 10);
ctx.restore();
});
}
}]
});
<?php endif; ?>
</script>
<style>
@media print {
.period-bar, .btn, button { display:none !important; }
.panel, .chart-wrap { box-shadow:none !important; border:1px solid #e2e8f0; }
}
</style>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kegiatan/laporan/index.blade.php ENDPATH**/ ?>

File diff suppressed because one or more lines are too long

View File

@ -1,164 +0,0 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bukti Pembayaran SPP - <?php echo e($pembayaranSpp->id_pembayaran); ?></title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; padding: 30px; font-size: 13px; }
.bukti-container { max-width: 600px; margin: 0 auto; border: 3px solid #6FBA9D; border-radius: 10px; padding: 25px; }
/* ── KOP SURAT ─────────────────────────────────── */
.kop { display: flex; align-items: center; gap: 16px; padding-bottom: 12px; border-bottom: 3px double #6FBA9D; margin-bottom: 8px; }
.kop img { width: 72px; height: 72px; object-fit: contain; flex-shrink: 0; }
.kop-text { flex: 1; text-align: center; }
.kop-text .nama-lembaga { font-size: 17px; font-weight: bold; color: #1a1a1a; letter-spacing: .4px; text-transform: uppercase; }
.kop-text .nama-singkat { font-size: 13px; color: #555; margin: 2px 0; }
.kop-text .alamat { font-size: 10.5px; color: #555; line-height: 16px; margin-top: 4px; }
/* ── JUDUL DOKUMEN ─────────────────────────────── */
.header { text-align: center; margin: 14px 0 20px; }
.header h2 { font-size: 15px; color: #6FBA9D; letter-spacing: 2px; text-transform: uppercase; }
.header p { font-size: 12px; color: #666; margin-top: 4px; }
.bukti-info { background: #f9f9f9; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
.bukti-info table { width: 100%; }
.bukti-info tr { line-height: 28px; }
.bukti-info td:first-child { font-weight: bold; width: 160px; color: #555; }
.bukti-info td:nth-child(2) { width: 20px; }
.bukti-info td:last-child { color: #333; }
.nominal-box { background: linear-gradient(135deg, #6FBA9D 0%, #8FCAAE 100%); color: white; padding: 20px; border-radius: 8px; text-align: center; margin: 25px 0; }
.nominal-box h3 { font-size: 14px; margin-bottom: 10px; opacity: .9; }
.nominal-box .amount { font-size: 32px; font-weight: bold; letter-spacing: 1px; }
.status-box { text-align: center; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
.status-lunas { background: #d4edda; color: #155724; border: 2px solid #c3e6cb; }
.status-belum { background: #fff3cd; color: #856404; border: 2px solid #ffeaa7; }
.footer { text-align: center; margin-top: 30px; padding-top: 20px; border-top: 2px dashed #ddd; }
.footer p { font-size: 11px; color: #888; line-height: 18px; }
.ttd-section { margin-top: 40px; display: flex; justify-content: flex-end; }
.ttd-box { text-align: center; width: 45%; }
.ttd-box p { margin-bottom: 60px; font-size: 12px; }
.ttd-box strong { font-size: 13px; border-top: 1px solid #333; padding-top: 5px; display: inline-block; }
@media print {
body { padding: 0; }
.no-print { display: none; }
@page { margin: 1.5cm; }
}
</style>
</head>
<body>
<!-- Tombol Print -->
<div class="no-print" style="text-align:center; margin-bottom:20px;">
<button onclick="window.print()" style="padding:12px 30px; background:#6FBA9D; color:white; border:none; border-radius:5px; cursor:pointer; font-size:14px;">
Cetak Bukti
</button>
</div>
<div class="bukti-container">
<!-- ══ KOP SURAT ══ -->
<div class="kop">
<img src="<?php echo e(asset('images/logo.png')); ?>" alt="Logo PKPPS Riyadlul Jannah">
<div class="kop-text">
<div class="nama-lembaga">PKPPS Riyadlul Jannah Mojokerto</div>
<div class="alamat">
Jl. Raya Brangkal No. 42 RT. 02 RW. 01, Desa Brangkal<br>
Kec. Sooko, Kab. Mojokerto, Prov. Jawa Timur
</div>
</div>
</div>
<!-- ══ JUDUL DOKUMEN ══ -->
<div class="header">
<h2>Bukti Pembayaran SPP</h2>
<p>No. Bukti: <strong><?php echo e($pembayaranSpp->id_pembayaran); ?></strong></p>
</div>
<!-- Status -->
<?php if($pembayaranSpp->status === 'Lunas'): ?>
<div class="status-box status-lunas">
<strong style="font-size:16px;">&#10003; LUNAS</strong>
</div>
<?php else: ?>
<div class="status-box status-belum">
<strong style="font-size:16px;">&#9888; BELUM LUNAS</strong>
</div>
<?php endif; ?>
<!-- Info Santri & Pembayaran -->
<div class="bukti-info">
<table>
<tr>
<td>ID Santri</td>
<td>:</td>
<td><?php echo e($pembayaranSpp->santri->id_santri); ?></td>
</tr>
<tr>
<td>Nama Santri</td>
<td>:</td>
<td><strong><?php echo e($pembayaranSpp->santri->nama_lengkap); ?></strong></td>
</tr>
<tr>
<td>Periode Pembayaran</td>
<td>:</td>
<td><strong><?php echo e($pembayaranSpp->periode_lengkap); ?></strong></td>
</tr>
<tr>
<td>Tanggal Bayar</td>
<td>:</td>
<td>
<?php if($pembayaranSpp->tanggal_bayar): ?>
<?php echo e($pembayaranSpp->tanggal_bayar->format('d F Y')); ?>
<?php else: ?>
<span style="color:#999;">Belum dibayar</span>
<?php endif; ?>
</td>
</tr>
<?php if($pembayaranSpp->keterangan): ?>
<tr>
<td>Keterangan</td>
<td>:</td>
<td><?php echo e($pembayaranSpp->keterangan); ?></td>
</tr>
<?php endif; ?>
</table>
</div>
<!-- Nominal -->
<div class="nominal-box">
<h3>JUMLAH PEMBAYARAN</h3>
<div class="amount"><?php echo e($pembayaranSpp->nominal_format); ?></div>
</div>
<!-- Tanda Tangan -->
<div class="ttd-section">
<div class="ttd-box">
<p>Petugas</p>
<strong>(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)</strong>
</div>
</div>
<!-- Footer -->
<div class="footer">
<p>
<strong>PERHATIAN:</strong><br>
Bukti pembayaran ini sah dan merupakan tanda terima yang diakui secara resmi.<br>
Simpan bukti ini sebagai arsip pembayaran SPP.<br>
Dicetak pada: <?php echo e(date('d F Y, H:i')); ?> WIB
</p>
</div>
</div>
</body>
</html><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/pembayaran-spp/cetak-bukti.blade.php ENDPATH**/ ?>

View File

@ -0,0 +1,303 @@
<?php $__env->startSection('title', 'Generate SPP Massal'); ?>
<?php $__env->startSection('content'); ?>
<div class="page-header">
<h2><i class="fas fa-cogs"></i> Generate SPP Massal</h2>
</div>
<div class="content-box">
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
<strong>Informasi:</strong> Fitur ini akan membuat data pembayaran SPP untuk <strong>semua santri aktif</strong> dalam periode yang ditentukan.
Data yang sudah ada akan dilewati (tidak duplikat).
</div>
<form action="<?php echo e(route('admin.pembayaran-spp.generate')); ?>" method="POST" onsubmit="return confirmGenerate()">
<?php echo csrf_field(); ?>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<!-- Bulan -->
<div class="form-group">
<label><i class="fas fa-calendar form-icon"></i> Bulan <span style="color: red;">*</span></label>
<select name="bulan" class="form-control <?php $__errorArgs = ['bulan'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>" required>
<option value="">-- Pilih Bulan --</option>
<?php for($i = 1; $i <= 12; $i++): ?>
<option value="<?php echo e($i); ?>" <?php echo e(old('bulan', date('n')) == $i ? 'selected' : ''); ?>>
<?php echo e(DateTime::createFromFormat('!m', $i)->format('F')); ?>
</option>
<?php endfor; ?>
</select>
<?php $__errorArgs = ['bulan'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div class="invalid-feedback"><?php echo e($message); ?></div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<!-- Tahun -->
<div class="form-group">
<label><i class="fas fa-calendar-alt form-icon"></i> Tahun <span style="color: red;">*</span></label>
<input type="number"
name="tahun"
class="form-control <?php $__errorArgs = ['tahun'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>"
value="<?php echo e(old('tahun', date('Y'))); ?>"
min="2020"
max="2100"
required>
<?php $__errorArgs = ['tahun'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div class="invalid-feedback"><?php echo e($message); ?></div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<!-- Nominal -->
<div class="form-group">
<label><i class="fas fa-money-bill-wave form-icon"></i> Nominal per Santri (Rp) <span style="color: red;">*</span></label>
<input type="number"
name="nominal"
id="input-nominal"
class="form-control <?php $__errorArgs = ['nominal'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>"
value="<?php echo e(old('nominal')); ?>"
placeholder="Contoh: 250000"
min="0"
step="1000"
required>
<?php $__errorArgs = ['nominal'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div class="invalid-feedback"><?php echo e($message); ?></div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
<small class="form-text" id="nominal-helper">
<span id="nominal-display" style="color: var(--primary-color); font-weight: 600;">Masukkan nominal pembayaran</span>
</small>
</div>
<!-- Batas Bayar -->
<div class="form-group">
<label><i class="fas fa-clock form-icon"></i> Batas Bayar <span style="color: red;">*</span></label>
<input type="date"
name="batas_bayar"
class="form-control <?php $__errorArgs = ['batas_bayar'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>"
value="<?php echo e(old('batas_bayar', date('Y-m-10'))); ?>"
required>
<?php $__errorArgs = ['batas_bayar'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div class="invalid-feedback"><?php echo e($message); ?></div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
</div>
<!-- Preview Info Santri -->
<div class="info-box" style="background: linear-gradient(135deg, #E8F7F2 0%, #D4F1E3 100%); border-color: var(--success-color);">
<p style="margin: 0; line-height: 1.8;">
<i class="fas fa-users"></i>
Data SPP akan dibuat untuk <strong><?php echo e(\App\Models\Santri::where('status', 'Aktif')->count()); ?> santri</strong> dengan status Aktif.<br>
<i class="fas fa-exclamation-triangle"></i>
Pastikan data sudah benar sebelum melanjutkan. Proses ini tidak dapat dibatalkan.
</p>
</div>
<!-- Periode Yang Sudah Di-Generate -->
<?php
$periodeGenerated = \App\Models\PembayaranSpp::selectRaw('DISTINCT bulan, tahun')
->orderBy('tahun', 'desc')
->orderBy('bulan', 'desc')
->get();
?>
<?php if($periodeGenerated->count() > 0): ?>
<div class="info-box" style="background: linear-gradient(135deg, #FFF8E1 0%, #FFF3CD 100%); border-color: var(--warning-color);">
<p style="margin: 0 0 10px 0; font-weight: 600; color: var(--text-color);">
<i class="fas fa-info-circle"></i> Periode Yang Sudah Di-Generate:
</p>
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
<?php $__currentLoopData = $periodeGenerated; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $periode): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$bulanNama = DateTime::createFromFormat('!m', $periode->bulan)->format('F');
?>
<span style="background: white; padding: 6px 12px; border-radius: 5px; font-size: 0.9rem; border: 1px solid #ddd;">
<?php echo e($bulanNama); ?> <?php echo e($periode->tahun); ?>
</span>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<p style="margin: 10px 0 0 0; font-size: 0.85rem; color: var(--text-light);">
<i class="fas fa-lightbulb"></i> Jika Anda generate periode yang sudah ada, data akan di-skip (tidak duplikat).
</p>
</div>
<?php endif; ?>
<!-- Buttons -->
<div style="display: flex; gap: 10px; margin-top: 25px;">
<button type="submit" class="btn btn-success hover-shadow" id="btn-generate">
<i class="fas fa-cogs"></i> Generate SPP
</button>
<a href="<?php echo e(route('admin.pembayaran-spp.index')); ?>" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</form>
</div>
<!-- Loading Overlay -->
<div id="loading-overlay" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 9999; justify-content: center; align-items: center;">
<div style="background: white; padding: 22px; border-radius: 15px; text-align: center; max-width: 400px;">
<div style="border: 4px solid var(--primary-light); border-top: 4px solid var(--primary-color); border-radius: 50%; width: 60px; height: 60px; animation: spin 1s linear infinite; margin: 0 auto 20px;"></div>
<h3 style="color: var(--primary-color); margin-bottom: 10px;">Generating SPP...</h3>
<p style="color: var(--text-light); margin: 0;">Mohon tunggu, proses sedang berjalan.</p>
</div>
</div>
<?php $__env->startPush('styles'); ?>
<style>
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<?php $__env->stopPush(); ?>
<?php $__env->startPush('scripts'); ?>
<script>
// Daftar periode yang sudah di-generate
const periodeGenerated = <?php echo json_encode($periodeGenerated->map(function($p) {
return ['bulan' => $p->bulan, 'tahun' => $p->tahun];
}), 512) ?>;
// Format nominal display saat user mengetik
document.getElementById('input-nominal').addEventListener('input', function() {
const value = parseInt(this.value) || 0;
const displayElement = document.getElementById('nominal-display');
if (value > 0) {
const formatted = new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(value);
displayElement.textContent = formatted;
displayElement.style.color = 'var(--primary-color)';
} else {
displayElement.textContent = 'Masukkan nominal pembayaran';
displayElement.style.color = 'var(--text-light)';
}
});
// Check apakah periode sudah pernah di-generate
function checkPeriode(bulan, tahun) {
return periodeGenerated.some(p => p.bulan == bulan && p.tahun == tahun);
}
// Confirm before generate
function confirmGenerate() {
const bulan = document.querySelector('select[name="bulan"]');
const tahun = document.querySelector('input[name="tahun"]');
const nominal = document.querySelector('input[name="nominal"]');
const batasBayar = document.querySelector('input[name="batas_bayar"]');
// Validasi nominal harus diisi
if (!nominal.value || parseInt(nominal.value) <= 0) {
alert('Nominal pembayaran harus diisi!');
nominal.focus();
return false;
}
const bulanValue = parseInt(bulan.value);
const tahunValue = parseInt(tahun.value);
const bulanText = bulan.options[bulan.selectedIndex].text;
const nominalFormatted = new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0
}).format(nominal.value);
const jumlahSantri = <?php echo e(\App\Models\Santri::where('status', 'Aktif')->count()); ?>;
const totalNominal = new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0
}).format(nominal.value * jumlahSantri);
// Cek apakah periode sudah pernah di-generate
const sudahDiGenerate = checkPeriode(bulanValue, tahunValue);
let warningText = '';
if (sudahDiGenerate) {
warningText = `\n⚠️ PERINGATAN: Periode ${bulanText} ${tahunValue} sudah pernah di-generate!\nData yang sudah ada akan di-skip (tidak akan duplikat).\n`;
}
const message = `Anda akan generate SPP untuk semua santri aktif dengan detail:\n\n` +
`Periode: ${bulanText} ${tahunValue}\n` +
`Nominal per Santri: ${nominalFormatted}\n` +
`Batas Bayar: ${new Date(batasBayar.value).toLocaleDateString('id-ID')}\n` +
`Jumlah Santri: ${jumlahSantri} santri\n` +
`Total Nominal: ${totalNominal}\n` +
warningText +
`\nLanjutkan?`;
if (confirm(message)) {
// Show loading overlay
document.getElementById('loading-overlay').style.display = 'flex';
document.getElementById('btn-generate').disabled = true;
return true;
}
return false;
}
</script>
<?php $__env->stopPush(); ?>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/pembayaran-spp/generate.blade.php ENDPATH**/ ?>

View File

@ -1,168 +0,0 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Laporan Pembayaran SPP</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; padding: 20px; font-size: 12px; }
.header { text-align: center; margin-bottom: 30px; border-bottom: 3px solid #333; padding-bottom: 15px; }
.header h1 { font-size: 20px; margin-bottom: 5px; }
.header h2 { font-size: 16px; color: #666; margin-bottom: 10px; }
.header p { font-size: 11px; color: #888; }
.info-box { background: #f5f5f5; padding: 15px; margin-bottom: 20px; border-radius: 5px; }
.info-box table { width: 100%; }
.info-box td { padding: 5px; }
.info-box td:first-child { font-weight: bold; width: 150px; }
.stats { display: flex; justify-content: space-around; margin-bottom: 20px; }
.stat-card { text-align: center; flex: 1; padding: 15px; background: #f9f9f9; border-radius: 5px; margin: 0 5px; }
.stat-card h3 { font-size: 11px; color: #666; margin-bottom: 8px; }
.stat-card .value { font-size: 18px; font-weight: bold; color: #333; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 10px; text-align: left; border: 1px solid #ddd; }
th { background: #6FBA9D; color: white; font-weight: bold; }
tr:nth-child(even) { background: #f9f9f9; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.badge { padding: 4px 8px; border-radius: 3px; font-size: 10px; font-weight: bold; }
.badge-success { background: #d4edda; color: #155724; }
.badge-warning { background: #fff3cd; color: #856404; }
.badge-danger { background: #f8d7da; color: #721c24; }
.footer { margin-top: 30px; text-align: center; font-size: 10px; color: #888; border-top: 1px solid #ddd; padding-top: 10px; }
@media print {
body { padding: 0; }
.no-print { display: none; }
@page { margin: 1cm; }
}
</style>
</head>
<body>
<!-- Tombol Print -->
<div class="no-print" style="margin-bottom: 20px; text-align: right;">
<button onclick="window.print()" style="padding: 10px 20px; background: #6FBA9D; color: white; border: none; border-radius: 5px; cursor: pointer;">
<i class="fas fa-print"></i> Cetak Laporan
</button>
</div>
<!-- Header -->
<div class="header">
<h1>PKPPS RIYADLUL JANNAH</h1>
<h2>LAPORAN PEMBAYARAN SPP</h2>
<p>Tanggal Cetak: <?php echo e(date('d F Y')); ?> WIB</p>
</div>
<!-- Info Filter -->
<?php if(request()->has('bulan') || request()->has('tahun') || request()->has('status')): ?>
<div class="info-box">
<table>
<?php if(request()->filled('bulan')): ?>
<tr>
<td>Bulan</td>
<td>: <?php echo e(DateTime::createFromFormat('!m', request('bulan'))->format('F')); ?></td>
</tr>
<?php endif; ?>
<?php if(request()->filled('tahun')): ?>
<tr>
<td>Tahun</td>
<td>: <?php echo e(request('tahun')); ?></td>
</tr>
<?php endif; ?>
<?php if(request()->filled('status')): ?>
<tr>
<td>Status</td>
<td>: <?php echo e(request('status')); ?></td>
</tr>
<?php endif; ?>
</table>
</div>
<?php endif; ?>
<!-- Statistik -->
<div class="stats">
<div class="stat-card">
<h3>Total Terbayar</h3>
<div class="value" style="color: #28a745;">Rp <?php echo e(number_format($totalLunas, 0, ',', '.')); ?></div>
</div>
<div class="stat-card">
<h3>Total Tunggakan</h3>
<div class="value" style="color: #dc3545;">Rp <?php echo e(number_format($totalTunggakan, 0, ',', '.')); ?></div>
</div>
<div class="stat-card">
<h3>Pembayaran Telat</h3>
<div class="value" style="color: #ffc107;"><?php echo e($jumlahTelat); ?></div>
</div>
<div class="stat-card">
<h3>Total Data</h3>
<div class="value" style="color: #17a2b8;"><?php echo e($pembayaranSpp->count()); ?></div>
</div>
</div>
<!-- Tabel Data -->
<table>
<thead>
<tr>
<th width="5%">No</th>
<th width="10%">ID</th>
<th width="20%">Santri</th>
<th width="15%">Periode</th>
<th width="15%">Nominal</th>
<th width="12%">Batas Bayar</th>
<th width="12%">Tgl Bayar</th>
<th width="11%">Status</th>
</tr>
</thead>
<tbody>
<?php $__empty_1 = true; $__currentLoopData = $pembayaranSpp; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $index => $spp): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
<tr>
<td class="text-center"><?php echo e($index + 1); ?></td>
<td><?php echo e($spp->id_pembayaran); ?></td>
<td>
<strong><?php echo e($spp->santri->nama_lengkap); ?></strong><br>
<small style="color: #666;"><?php echo e($spp->santri->id_santri); ?></small>
</td>
<td><?php echo e($spp->periode_lengkap); ?></td>
<td class="text-right"><strong><?php echo e($spp->nominal_format); ?></strong></td>
<td class="text-center"><?php echo e($spp->batas_bayar->format('d/m/Y')); ?></td>
<td class="text-center">
<?php if($spp->tanggal_bayar): ?>
<?php echo e($spp->tanggal_bayar->format('d/m/Y')); ?>
<?php else: ?>
<span style="color: #999;">-</span>
<?php endif; ?>
</td>
<td class="text-center">
<?php if($spp->status === 'Lunas'): ?>
<span class="badge badge-success">Lunas</span>
<?php elseif($spp->isTelat()): ?>
<span class="badge badge-danger">Telat</span>
<?php else: ?>
<span class="badge badge-warning">Belum Lunas</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
<tr>
<td colspan="8" class="text-center" style="padding: 30px; color: #999;">
Tidak ada data pembayaran SPP
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
<script>
// Auto print saat load (opsional)
// window.onload = function() { window.print(); }
</script>
</body>
</html><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/pembayaran-spp/cetak-laporan.blade.php ENDPATH**/ ?>

View File

@ -1,144 +0,0 @@

<?php $__env->startSection('content'); ?>
<style>
.text-center { text-align:center; }
.progress-inline { display:flex; align-items:center; gap:8px; }
.progress-bar-mini { flex:1; background:#e9ecef; border-radius:8px; height:8px; overflow:hidden; max-width:120px; }
.progress-bar-mini .fill { height:100%; border-radius:8px; }
</style>
<div class="page-header">
<h2><i class="fas fa-user-clock"></i> Santri Perlu Perhatian</h2>
<a href="<?php echo e(route('admin.laporan-kegiatan.index', request()->only('periode','tanggal_dari','tanggal_sampai'))); ?>"
class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
<div class="content-box" style="margin-bottom:14px;">
<p style="margin:0;font-size:.88rem;color:var(--text-light);">
<i class="fas fa-info-circle"></i>
Menampilkan <strong><?php echo e($santris->total()); ?> santri</strong>
dari <strong><?php echo e($totalSantriAktif); ?> santri aktif</strong>
yang kehadiran efektifnya &lt;70%
dalam periode <strong><?php echo e($periodeLabel); ?></strong>.
<br>
<span style="font-size:.8rem;opacity:.8;">
<i class="fas fa-clock"></i> Terlambat dihitung sebagai Hadir efektif.
</span>
</p>
</div>
<div class="content-box" style="margin-bottom:14px;">
<form method="GET" style="display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;">
<input type="hidden" name="periode" value="<?php echo e(request('periode','bulan_ini')); ?>">
<input type="hidden" name="tanggal_dari" value="<?php echo e(request('tanggal_dari')); ?>">
<input type="hidden" name="tanggal_sampai" value="<?php echo e(request('tanggal_sampai')); ?>">
<div class="form-group" style="margin:0;">
<label style="font-size:.82rem;">Filter Kelas</label>
<select name="id_kelas" class="form-control" style="min-width:180px;">
<option value="">-- Semua Kelas --</option>
<?php $__currentLoopData = $kelasList; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $k): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($k->id); ?>" <?php echo e(request('id_kelas')==$k->id?'selected':''); ?>>
<?php echo e($k->kelompok->nama_kelompok ?? ''); ?> <?php echo e($k->nama_kelas); ?>
</option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm"><i class="fas fa-filter"></i> Filter</button>
</form>
</div>
<div class="content-box">
<?php if($santris->count() > 0): ?>
<div class="table-wrapper">
<table class="data-table" style="font-size:.84rem;">
<thead>
<tr>
<th>No</th>
<th>Nama</th>
<th class="text-center">Total Sesi</th>
<th class="text-center">
Hadir Efektif
<span style="display:block;font-weight:400;font-size:.73rem;color:#9CA3AF;">(Hadir+Terlambat)</span>
</th>
<th class="text-center">Terlambat</th>
<th class="text-center">Alpa</th>
<th class="text-center">Izin</th>
<th class="text-center">Sakit</th>
<th class="text-center" style="min-width:180px;">
% Kehadiran
<span style="display:block;font-weight:400;font-size:.73rem;color:#9CA3AF;">(hadir / total sesi)</span>
</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
<?php $__currentLoopData = $santris; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $i => $s): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<tr>
<td><?php echo e($santris->firstItem() + $i); ?></td>
<td>
<strong><?php echo e($s->nama_lengkap); ?></strong>
<div style="font-size:.75rem;color:#9CA3AF;"><code><?php echo e($s->id_santri); ?></code></div>
</td>
<td class="text-center"><?php echo e($s->total); ?></td>
<td class="text-center">
<span class="badge badge-success"><?php echo e($s->hadir); ?></span>
</td>
<td class="text-center">
<?php if(($s->terlambat ?? 0) > 0): ?>
<span class="badge badge-warning"><?php echo e($s->terlambat); ?></span>
<?php else: ?>
<span style="color:#9CA3AF;">0</span>
<?php endif; ?>
</td>
<td class="text-center">
<span class="badge badge-danger"><?php echo e($s->alpa); ?></span>
</td>
<td class="text-center">
<span class="badge badge-warning"><?php echo e($s->izin); ?></span>
</td>
<td class="text-center">
<span class="badge badge-info"><?php echo e($s->sakit); ?></span>
</td>
<td class="text-center">
<div class="progress-inline" style="justify-content:center;">
<div class="progress-bar-mini">
<div class="fill" style="width:<?php echo e($s->persen); ?>%;background:#EF4444;"></div>
</div>
<div>
<strong style="color:#EF4444;"><?php echo e($s->persen); ?>%</strong>
<div style="font-size:.72rem;color:#9CA3AF;">
<?php echo e($s->hadir); ?> dari <?php echo e($s->total); ?>
</div>
</div>
</div>
</td>
<td>
<a href="<?php echo e(route('admin.riwayat-kegiatan.detail-santri', $s->id_santri)); ?>"
class="btn btn-sm btn-info" title="Lihat Riwayat Lengkap">
<i class="fas fa-history"></i> Riwayat
</a>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</tbody>
</table>
</div>
<div style="margin-top:16px;"><?php echo e($santris->links()); ?></div>
<?php else: ?>
<div class="empty-state">
<i class="fas fa-check-circle" style="color:#10B981;font-size:2rem;"></i>
<h3>Alhamdulillah!</h3>
<p>Tidak ada santri dengan kehadiran di bawah 70%.</p>
</div>
<?php endif; ?>
</div>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kegiatan/laporan/santri-perlu-perhatian.blade.php ENDPATH**/ ?>

View File

@ -1,50 +0,0 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $__env->yieldContent('title', 'Login'); ?> | SIM Santri</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="<?php echo e(asset('css/app.css')); ?>">
<style>
/* Gaya dasar untuk halaman auth/login */
body.auth-page {
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.auth-container {
width: 100%;
max-width: 400px;
background: white;
padding: 22px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.auth-header {
text-align: center;
margin-bottom: 25px;
color: var(--text-color);
}
.auth-header h2 {
margin: 0;
font-size: 1.8rem;
}
.auth-header p {
color: #777;
margin-top: 5px;
font-size: 0.9rem;
}
.btn-full {
width: 100%;
}
</style>
</head>
<body class="auth-page">
<div class="auth-container">
<?php echo $__env->yieldContent('auth-content'); ?>
</div>
</body>
</html><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/auth/auth_layout.blade.php ENDPATH**/ ?>

View File

@ -1,592 +0,0 @@

<?php $__env->startSection('title', 'Data Kepulangan Santri'); ?>
<?php $__env->startSection('content'); ?>
<div class="page-header">
<h2><i class="fas fa-home"></i> Data Kepulangan Santri</h2>
</div>
<?php
$pendingPengajuan = \App\Models\PengajuanKepulangan::where('status', 'Menunggu')->count();
?>
<?php if($pendingPengajuan > 0): ?>
<div class="alert alert-warning" style="display: flex; align-items: center; gap: 11px; margin-bottom: 14px; background: linear-gradient(135deg, #ffc107 0%, #ff9800 100%); border: none; color: #000;">
<i class="fas fa-bell" style="font-size: 2rem;"></i>
<div style="flex: 1;">
<strong style="font-size: 1.1rem;">Ada <?php echo e($pendingPengajuan); ?> pengajuan kepulangan dari mobile yang menunggu review!</strong>
<p style="margin: 5px 0 0 0; opacity: 0.8;">Klik tombol di bawah untuk melihat dan meninjau pengajuan.</p>
</div>
<a href="<?php echo e(route('admin.kepulangan.pengajuan')); ?>" class="btn btn-dark" style="white-space: nowrap;">
<i class="fas fa-mobile-alt"></i> Lihat Pengajuan
</a>
</div>
<?php endif; ?>
<div style="background: linear-gradient(135deg, #6FBA9D 0%, #89cdb3 100%); color: white; padding: 14px; border-radius: 12px; margin-bottom: 14px;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 20px; align-items: center;">
<div>
<h4 style="margin: 0 0 5px 0; opacity: 0.9;"> Periode Kuota</h4>
<p style="margin: 0; font-size: 1.1rem; font-weight: 600;">
<?php echo e($settings->periode_mulai->format('d M Y')); ?> - <?php echo e($settings->periode_akhir->format('d M Y')); ?>
</p>
</div>
<div>
<h4 style="margin: 0 0 5px 0; opacity: 0.9;"> Kuota Maksimal</h4>
<p style="margin: 0; font-size: 1.1rem; font-weight: 600;"><?php echo e($settings->kuota_maksimal); ?> Hari / Tahun</p>
</div>
<div>
<h4 style="margin: 0 0 5px 0; opacity: 0.9;"> Terakhir Reset</h4>
<p style="margin: 0; font-size: 1.1rem; font-weight: 600;">
<?php echo e($settings->terakhir_reset ? $settings->terakhir_reset->format('d M Y') : 'Belum Pernah'); ?>
</p>
</div>
<div style="text-align: right;">
<a href="<?php echo e(route('admin.kepulangan.settings')); ?>" class="btn btn-light" style="background: white; color: #667eea; font-weight: 600;">
<i class="fas fa-cog"></i> Kelola Pengaturan
</a>
</div>
</div>
</div>
<div class="row-cards">
<div class="card card-info">
<h3>Total Data</h3>
<div class="card-value"><?php echo e($stats['total_data']); ?></div>
<i class="fas fa-clipboard-list card-icon"></i>
</div>
<div class="card card-warning">
<h3>Menunggu Approval</h3>
<div class="card-value"><?php echo e($stats['menunggu_approval']); ?></div>
<i class="fas fa-clock card-icon"></i>
</div>
<div class="card card-success">
<h3>Sedang Izin</h3>
<div class="card-value"><?php echo e($stats['sedang_izin']); ?></div>
<i class="fas fa-home card-icon"></i>
</div>
<div class="card card-danger">
<h3>Over Limit (><?php echo e($settings->kuota_maksimal); ?> Hari)</h3>
<div class="card-value"><?php echo e($stats['over_limit_santri']); ?></div>
<i class="fas fa-exclamation-triangle card-icon"></i>
<?php if($stats['over_limit_santri'] > 0): ?>
<a href="<?php echo e(route('admin.kepulangan.over-limit')); ?>" style="font-size: 0.85rem; color: #dc3545; text-decoration: underline; margin-top: 5px; display: block;">
Lihat Detail
</a>
<?php endif; ?>
</div>
</div>
<?php if(session('success')): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> <?php echo e(session('success')); ?>
</div>
<?php endif; ?>
<?php if(session('error')): ?>
<div class="alert alert-danger">
<i class="fas fa-exclamation-circle"></i> <?php echo e(session('error')); ?>
</div>
<?php endif; ?>
<div class="content-box">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; flex-wrap: wrap; gap: 11px;">
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="<?php echo e(route('admin.kepulangan.create')); ?>" class="btn btn-primary">
<i class="fas fa-plus"></i> Tambah Izin Kepulangan
</a>
<a href="<?php echo e(route('admin.kepulangan.pengajuan')); ?>" class="btn btn-warning">
<i class="fas fa-mobile-alt"></i> Pengajuan izin
<?php if($pendingPengajuan > 0): ?>
<span class="badge" style="background: #dc3545; color: white; margin-left: 5px; padding: 3px 8px; border-radius: 10px; font-size: 0.75rem;">
<?php echo e($pendingPengajuan); ?>
</span>
<?php endif; ?>
</a>
</div>
</div>
<form method="GET" action="<?php echo e(route('admin.kepulangan.index')); ?>" id="filterForm" style="margin-bottom: 14px;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 11px; align-items: end;">
<div class="form-group" style="margin-bottom: 0;">
<input type="text" name="search" class="form-control" placeholder="Cari nama, ID, atau alasan..." value="<?php echo e(request('search')); ?>" id="searchInput">
</div>
<div class="form-group" style="margin-bottom: 0;">
<select name="status" class="form-control" onchange="document.getElementById('filterForm').submit();">
<option value="">Semua Status</option>
<option value="Menunggu" <?php echo e(request('status') == 'Menunggu' ? 'selected' : ''); ?>>Menunggu</option>
<option value="Disetujui" <?php echo e(request('status') == 'Disetujui' ? 'selected' : ''); ?>>Disetujui</option>
<option value="Ditolak" <?php echo e(request('status') == 'Ditolak' ? 'selected' : ''); ?>>Ditolak</option>
<option value="Selesai" <?php echo e(request('status') == 'Selesai' ? 'selected' : ''); ?>>Selesai</option>
</select>
</div>
<div class="form-group" style="margin-bottom: 0;">
<select name="tahun" class="form-control" onchange="document.getElementById('filterForm').submit();">
<option value="">Semua Tahun</option>
<?php $__currentLoopData = $tahunList; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $tahun): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($tahun); ?>" <?php echo e(request('tahun') == $tahun ? 'selected' : ''); ?>><?php echo e($tahun); ?></option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
</div>
<div class="form-group" style="margin-bottom: 0;">
<select name="bulan" class="form-control" onchange="document.getElementById('filterForm').submit();">
<option value="">Semua Bulan</option>
<?php for($i = 1; $i <= 12; $i++): ?>
<option value="<?php echo e($i); ?>" <?php echo e(request('bulan') == $i ? 'selected' : ''); ?>>
<?php echo e(\Carbon\Carbon::create()->month($i)->format('F')); ?>
</option>
<?php endfor; ?>
</select>
</div>
<div style="display: flex; gap: 10px;">
<button type="submit" class="btn btn-primary"><i class="fas fa-search"></i> Filter</button>
<a href="<?php echo e(route('admin.kepulangan.index')); ?>" class="btn btn-secondary"><i class="fas fa-redo"></i> Reset</a>
</div>
</div>
</form>
<div style="overflow-x: auto;">
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Santri</th>
<th>Tanggal Pulang</th>
<th>Tanggal Kembali</th>
<th>Durasi</th>
<th>Total Kuota Terpakai</th>
<th>Alasan</th>
<th>Status</th>
<th class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
<?php $__empty_1 = true; $__currentLoopData = $kepulangan; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
<?php
$isOverLimit = isset($santriOverLimit[$item->id_santri]);
$totalHariTerpakai = $isOverLimit ? $santriOverLimit[$item->id_santri] : 0;
?>
<tr style="<?php echo e($isOverLimit ? 'background-color: rgba(220, 53, 69, 0.1); border-left: 4px solid #dc3545;' : ''); ?>">
<td>
<strong><?php echo e($item->id_kepulangan); ?></strong>
<?php if($isOverLimit): ?>
<span style="display: inline-block; background: #dc3545; color: white; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; margin-left: 5px; animation: pulse 2s infinite;" title="Over Limit: <?php echo e($totalHariTerpakai); ?> hari">
<i class="fas fa-exclamation-triangle"></i>
</span>
<?php endif; ?>
</td>
<td>
<div>
<strong><?php echo e($item->santri->nama_lengkap ?? 'N/A'); ?></strong><br>
<small style="color: #7F8C8D;"><?php echo e($item->santri->id_santri ?? ''); ?> | <?php echo e($item->santri->kelas ?? ''); ?></small>
</div>
</td>
<td><?php echo e($item->tanggal_pulang_formatted); ?></td>
<td><?php echo e($item->tanggal_kembali_formatted); ?></td>
<td>
<span style="display: inline-block; background: <?php echo e($item->durasi_izin > 7 ? '#ffc107' : '#6c757d'); ?>; color: <?php echo e($item->durasi_izin > 7 ? '#000' : '#fff'); ?>; padding: 4px 8px; border-radius: 4px; font-size: 0.85rem; font-weight: 600;">
<?php echo e($item->durasi_izin); ?> hari
</span>
</td>
<td>
<?php
$kuotaSantri = \App\Models\Kepulangan::getSisaKuotaSantri($item->id_santri);
$badgeColor = $kuotaSantri['badge_color'];
$badgeColors = ['success' => '#28a745', 'warning' => '#ffc107', 'danger' => '#dc3545'];
$bgColor = $badgeColors[$badgeColor] ?? '#6c757d';
$textColor = $badgeColor == 'warning' ? '#000' : '#fff';
?>
<div style="text-align: center;">
<span style="display: inline-block; background: <?php echo e($bgColor); ?>; color: <?php echo e($textColor); ?>; padding: 4px 10px; border-radius: 4px; font-size: 0.85rem; font-weight: 600;">
<?php echo e($kuotaSantri['total_terpakai']); ?> / <?php echo e($kuotaSantri['kuota_maksimal']); ?> hari
</span>
<div style="margin-top: 5px; font-size: 0.75rem; color: #7F8C8D;">
<?php if($kuotaSantri['status'] === 'melebihi'): ?>
<strong style="color: #dc3545;">OVER LIMIT</strong>
<?php else: ?>
Sisa: <?php echo e($kuotaSantri['sisa_kuota']); ?> hari (<?php echo e($kuotaSantri['persentase']); ?>%)
<?php endif; ?>
</div>
</div>
</td>
<td>
<span style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block;" title="<?php echo e($item->alasan); ?>">
<?php echo e($item->alasan); ?>
</span>
</td>
<td>
<span style="display: inline-block; padding: 4px 10px; border-radius: 4px; font-size: 0.85rem; font-weight: 600;
<?php if($item->status == 'Menunggu'): ?> background: #ffc107; color: #000;
<?php elseif($item->status == 'Disetujui'): ?> background: #28a745; color: white;
<?php elseif($item->status == 'Ditolak'): ?> background: #dc3545; color: white;
<?php else: ?> background: #6c757d; color: white;
<?php endif; ?>">
<?php echo e($item->status); ?>
</span>
<?php if($item->is_aktif): ?>
<br><small style="color: #28a745; font-weight: 600;">Sedang Izin</small>
<?php elseif($item->is_terlambat): ?>
<br><small style="color: #dc3545; font-weight: 600;">Terlambat</small>
<?php endif; ?>
</td>
<td class="text-center">
<div style="display: flex; gap: 5px; justify-content: center; flex-wrap: wrap;">
<a href="<?php echo e(route('admin.kepulangan.show', $item->id_kepulangan)); ?>" class="btn btn-sm btn-primary" title="Detail">
<i class="fas fa-eye"></i>
</a>
<?php if($item->status == 'Menunggu'): ?>
<a href="<?php echo e(route('admin.kepulangan.edit', $item->id_kepulangan)); ?>" class="btn btn-sm btn-warning" title="Edit">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-sm btn-success" onclick="approveKepulangan('<?php echo e($item->id_kepulangan); ?>')" title="Setujui">
<i class="fas fa-check"></i>
</button>
<button type="button" class="btn btn-sm btn-danger" onclick="rejectKepulangan('<?php echo e($item->id_kepulangan); ?>')" title="Tolak">
<i class="fas fa-times"></i>
</button>
<?php endif; ?>
<?php if($item->status == 'Disetujui'): ?>
<a href="<?php echo e(route('admin.kepulangan.print', $item->id_kepulangan)); ?>" class="btn btn-sm btn-secondary" target="_blank" title="Cetak Surat">
<i class="fas fa-print"></i>
</a>
<button type="button" class="btn btn-sm btn-success"
onclick="completeKepulangan('<?php echo e($item->id_kepulangan); ?>', '<?php echo e($item->santri->nama_lengkap); ?>', '<?php echo e($item->tanggal_pulang->format('Y-m-d')); ?>', '<?php echo e($item->tanggal_kembali->format('Y-m-d')); ?>', <?php echo e($item->durasi_izin); ?>)"
title="Selesaikan">
<i class="fas fa-check-double"></i>
</button>
<?php endif; ?>
<?php if(in_array($item->status, ['Menunggu', 'Ditolak', 'Selesai'])): ?>
<button type="button" class="btn btn-sm btn-danger" onclick="deleteKepulangan('<?php echo e($item->id_kepulangan); ?>')" title="Hapus">
<i class="fas fa-trash"></i>
</button>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
<tr>
<td colspan="9" style="text-align: center; padding: 22px;">
<i class="fas fa-inbox" style="font-size: 2.2rem; color: #ccc; margin-bottom: 15px;"></i>
<p style="color: #7F8C8D;">Tidak ada data kepulangan ditemukan</p>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php if($kepulangan->hasPages()): ?>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 14px; flex-wrap: wrap; gap: 11px;">
<div>Menampilkan <?php echo e($kepulangan->firstItem() ?? 0); ?> - <?php echo e($kepulangan->lastItem() ?? 0); ?> dari <?php echo e($kepulangan->total()); ?> data</div>
<div><?php echo e($kepulangan->appends(request()->query())->links()); ?></div>
</div>
<?php endif; ?>
</div>
<div class="modal fade" id="approveModal" tabindex="-1" style="display: none;">
<div class="modal-dialog">
<div class="modal-content" style="background: white; border-radius: 12px; padding: 14px;">
<form id="approveForm">
<?php echo csrf_field(); ?>
<div style="margin-bottom: 14px;"><h3 style="margin: 0; color: #2C3E50;">Setujui Izin Kepulangan</h3></div>
<div class="form-group">
<label>Catatan (Opsional):</label>
<textarea name="catatan" class="form-control" rows="3" placeholder="Tambahkan catatan untuk persetujuan ini..."></textarea>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px;">
<button type="button" class="btn btn-secondary" onclick="closeModal('approveModal')">Batal</button>
<button type="submit" class="btn btn-success"><i class="fas fa-check"></i> Setujui</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" id="rejectModal" tabindex="-1" style="display: none;">
<div class="modal-dialog">
<div class="modal-content" style="background: white; border-radius: 12px; padding: 14px;">
<form id="rejectForm">
<?php echo csrf_field(); ?>
<div style="margin-bottom: 14px;"><h3 style="margin: 0; color: #2C3E50;">Tolak Izin Kepulangan</h3></div>
<div class="form-group">
<label>Alasan Penolakan: <span style="color: #dc3545;">*</span></label>
<textarea name="alasan_penolakan" class="form-control" rows="3" placeholder="Jelaskan alasan penolakan..." required></textarea>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px;">
<button type="button" class="btn btn-secondary" onclick="closeModal('rejectModal')">Batal</button>
<button type="submit" class="btn btn-danger"><i class="fas fa-times"></i> Tolak</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" id="deleteModal" tabindex="-1" style="display: none;">
<div class="modal-dialog">
<div class="modal-content" style="background: white; border-radius: 12px; padding: 14px;">
<div style="margin-bottom: 14px;"><h3 style="margin: 0; color: #2C3E50;">Konfirmasi Hapus</h3></div>
<p>Apakah Anda yakin ingin menghapus data kepulangan ini?</p>
<p style="color: #dc3545; font-size: 0.9rem;">Data yang sudah dihapus tidak dapat dikembalikan.</p>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px;">
<button type="button" class="btn btn-secondary" onclick="closeModal('deleteModal')">Batal</button>
<button type="button" class="btn btn-danger" id="confirmDeleteBtn"><i class="fas fa-trash"></i> Hapus</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="completeModal" tabindex="-1" style="display: none;">
<div class="modal-dialog">
<div class="modal-content" style="background: white; border-radius: 12px; padding: 14px;">
<form id="completeForm">
<?php echo csrf_field(); ?>
<div style="margin-bottom: 14px;">
<h3 style="margin: 0; color: #2C3E50;"><i class="fas fa-check-circle" style="color: #28a745;"></i> Selesaikan Kepulangan</h3>
</div>
<div style="background: #E8F7F2; padding: 15px; border-radius: 8px; margin-bottom: 14px; border-left: 4px solid #6FBA9D;">
<p style="margin: 5px 0;"><strong>ID Kepulangan:</strong> <span id="completeIdKepulangan"></span></p>
<p style="margin: 5px 0;"><strong>Santri:</strong> <span id="completeNamaSantri"></span></p>
<p style="margin: 5px 0;"><strong>Tanggal Pulang:</strong> <span id="completeTanggalPulang"></span></p>
<p style="margin: 5px 0;"><strong>Rencana Kembali:</strong> <span id="completeTanggalKembaliRencana"></span></p>
<p style="margin: 5px 0;"><strong>Durasi Rencana:</strong> <span id="completeDurasiRencana"></span> hari</p>
</div>
<div class="form-group">
<label for="tanggal_kembali_aktual">
<i class="fas fa-calendar-check"></i> Tanggal Kembali Aktual <span style="color: #dc3545;">*</span>
</label>
<input type="date" name="tanggal_kembali_aktual" id="tanggal_kembali_aktual" class="form-control" required>
<small style="color: #7F8C8D; margin-top: 5px; display: block;">Masukkan tanggal santri kembali ke pesantren.</small>
</div>
<div id="durasiAktualInfo" style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 14px; border-left: 4px solid #007bff; display: none;">
<p style="margin: 0;"><strong>Durasi Aktual:</strong> <span id="durasiAktual" style="font-weight: 600; color: #007bff;">-</span> hari</p>
<p style="margin: 5px 0 0 0; font-size: 0.9rem; color: #7F8C8D;" id="selisihInfo"></p>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px;">
<button type="button" class="btn btn-secondary" onclick="closeModal('completeModal')">Batal</button>
<button type="submit" class="btn btn-success"><i class="fas fa-check"></i> Selesaikan</button>
</div>
</form>
</div>
</div>
</div>
<style>
.modal.fade { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center; }
.modal-dialog { max-width: 500px; width: 90%; margin: auto; }
.modal-content { max-height: 90vh; overflow-y: auto; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
</style>
<script>
const KEPULANGAN_BASE_URL = "<?php echo e(url('admin/kepulangan')); ?>";
const CSRF_TOKEN = "<?php echo e(csrf_token()); ?>";
</script>
<script>
let currentActionId = null;
// Auto submit search
let searchTimeout;
document.getElementById('searchInput')?.addEventListener('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => document.getElementById('filterForm').submit(), 500);
});
// ===== APPROVE =====
function approveKepulangan(id) {
currentActionId = id;
document.getElementById('approveModal').style.display = 'flex';
}
document.getElementById('approveForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const submitBtn = this.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Memproses...';
// ✅ FIX: pakai KEPULANGAN_BASE_URL
fetch(`${KEPULANGAN_BASE_URL}/${currentActionId}/approve`, {
method: 'POST',
body: formData,
headers: { 'X-CSRF-TOKEN': CSRF_TOKEN }
})
.then(r => r.json())
.then(data => {
if (data.success) { closeModal('approveModal'); showAlert('success', data.message); setTimeout(() => location.reload(), 1000); }
else showAlert('danger', data.message);
})
.catch(e => showAlert('danger', 'Error: ' + e.message))
.finally(() => { submitBtn.disabled = false; submitBtn.innerHTML = originalText; });
});
// ===== REJECT =====
function rejectKepulangan(id) {
currentActionId = id;
document.getElementById('rejectModal').style.display = 'flex';
}
document.getElementById('rejectForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const submitBtn = this.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Memproses...';
// ✅ FIX: pakai KEPULANGAN_BASE_URL
fetch(`${KEPULANGAN_BASE_URL}/${currentActionId}/reject`, {
method: 'POST',
body: formData,
headers: { 'X-CSRF-TOKEN': CSRF_TOKEN }
})
.then(r => r.json())
.then(data => {
if (data.success) { closeModal('rejectModal'); showAlert('success', data.message); setTimeout(() => location.reload(), 1000); }
else showAlert('danger', data.message);
})
.catch(e => showAlert('danger', 'Error: ' + e.message))
.finally(() => { submitBtn.disabled = false; submitBtn.innerHTML = originalText; });
});
// ===== COMPLETE =====
let currentCompleteData = {};
function completeKepulangan(id, namaSantri, tanggalPulang, tanggalKembaliRencana, durasiRencana) {
currentCompleteData = { id, namaSantri, tanggalPulang, tanggalKembaliRencana, durasiRencana };
document.getElementById('completeIdKepulangan').textContent = id;
document.getElementById('completeNamaSantri').textContent = namaSantri;
document.getElementById('completeTanggalPulang').textContent = formatTanggal(tanggalPulang);
document.getElementById('completeTanggalKembaliRencana').textContent = formatTanggal(tanggalKembaliRencana);
document.getElementById('completeDurasiRencana').textContent = durasiRencana;
const today = new Date().toISOString().split('T')[0];
document.getElementById('tanggal_kembali_aktual').value = today;
document.getElementById('tanggal_kembali_aktual').min = tanggalPulang;
calculateDurasiAktual();
document.getElementById('completeModal').style.display = 'flex';
}
function calculateDurasiAktual() {
const tanggalKembaliAktual = document.getElementById('tanggal_kembali_aktual').value;
if (!tanggalKembaliAktual || !currentCompleteData.tanggalPulang) return;
const startDate = new Date(currentCompleteData.tanggalPulang);
const endDate = new Date(tanggalKembaliAktual);
if (endDate < startDate) { document.getElementById('durasiAktualInfo').style.display = 'none'; return; }
const durasiAktual = Math.ceil(Math.abs(endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
const durasiRencana = currentCompleteData.durasiRencana;
document.getElementById('durasiAktual').textContent = durasiAktual;
document.getElementById('durasiAktualInfo').style.display = 'block';
let selisihText = '', selisihColor = '#007bff';
if (durasiAktual < durasiRencana) {
selisihText = `Santri pulang ${durasiRencana - durasiAktual} hari lebih cepat. Kuota akan berkurang ${durasiAktual} hari.`;
selisihColor = '#28a745';
} else if (durasiAktual > durasiRencana) {
selisihText = `Santri pulang ${durasiAktual - durasiRencana} hari lebih lambat. Kuota akan bertambah.`;
selisihColor = '#ffc107';
} else {
selisihText = `Sesuai rencana (${durasiAktual} hari).`;
}
document.getElementById('selisihInfo').textContent = selisihText;
document.getElementById('selisihInfo').style.color = selisihColor;
document.getElementById('durasiAktual').style.color = selisihColor;
}
document.getElementById('tanggal_kembali_aktual')?.addEventListener('change', calculateDurasiAktual);
document.getElementById('completeForm')?.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const submitBtn = this.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Memproses...';
// ✅ FIX: pakai KEPULANGAN_BASE_URL
fetch(`${KEPULANGAN_BASE_URL}/${currentCompleteData.id}/complete`, {
method: 'POST',
body: formData,
headers: { 'X-CSRF-TOKEN': CSRF_TOKEN }
})
.then(r => r.json())
.then(data => {
if (data.success) { closeModal('completeModal'); showAlert('success', data.message); setTimeout(() => location.reload(), 1500); }
else showAlert('danger', data.message);
})
.catch(e => showAlert('danger', 'Error: ' + e.message))
.finally(() => { submitBtn.disabled = false; submitBtn.innerHTML = originalText; });
});
// ===== DELETE =====
function deleteKepulangan(id) {
currentActionId = id;
document.getElementById('deleteModal').style.display = 'flex';
}
document.getElementById('confirmDeleteBtn').addEventListener('click', function() {
const btn = this;
const originalText = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Menghapus...';
// ✅ FIX: pakai KEPULANGAN_BASE_URL
fetch(`${KEPULANGAN_BASE_URL}/${currentActionId}`, {
method: 'DELETE',
headers: { 'X-CSRF-TOKEN': CSRF_TOKEN }
})
.then(r => r.json())
.then(data => {
if (data.success) { closeModal('deleteModal'); showAlert('success', data.message); setTimeout(() => location.reload(), 1000); }
else showAlert('danger', data.message);
})
.catch(e => showAlert('danger', 'Error: ' + e.message))
.finally(() => { btn.disabled = false; btn.innerHTML = originalText; });
});
// ===== HELPERS =====
function formatTanggal(dateString) {
return new Date(dateString).toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' });
}
function closeModal(modalId) { document.getElementById(modalId).style.display = 'none'; }
function showAlert(type, message) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type}`;
alertDiv.innerHTML = `<i class="fas fa-${type === 'success' ? 'check' : 'exclamation'}-circle"></i> ${message}`;
document.querySelector('.page-header').insertAdjacentElement('afterend', alertDiv);
setTimeout(() => alertDiv.remove(), 5000);
}
document.addEventListener('keydown', e => { if (e.key === 'Escape') document.querySelectorAll('.modal.fade').forEach(m => m.style.display = 'none'); });
document.querySelectorAll('.modal.fade').forEach(modal => {
modal.addEventListener('click', function(e) { if (e.target === this) this.style.display = 'none'; });
});
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kepulangan/index.blade.php ENDPATH**/ ?>

View File

@ -1,456 +0,0 @@
<?php $__env->startSection('content'); ?>
<style>
/* ============================================================
RIWAYAT KEGIATAN INDEX (v3)
============================================================ */
.rw-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; flex-wrap:wrap; gap:10px; }
.rw-header h2 { margin:0; font-size:1.35rem; color:var(--primary-dark); display:flex; align-items:center; gap:9px; }
.rw-header h2 i { color:var(--primary-color); }
/* Filter */
.rw-filter { background:#fff; border-radius:12px; box-shadow:0 2px 10px rgba(0,0,0,.06); padding:14px 18px; margin-bottom:14px; }
.rw-mode-tabs { display:flex; gap:6px; margin-bottom:14px; flex-wrap:wrap; }
.rw-tab { padding:7px 18px; border-radius:22px; border:1.5px solid #e2e8f0; background:#fff;
font-size:0.82rem; font-weight:600; cursor:pointer; text-decoration:none;
color:#64748b; transition:all .18s; display:inline-flex; align-items:center; gap:6px; }
.rw-tab:hover { border-color:var(--primary-color); color:var(--primary-color); background:#f0fdf4; }
.rw-tab.active { background:var(--primary-color); color:#fff; border-color:var(--primary-color); box-shadow:0 2px 8px rgba(16,185,129,.25); }
.rw-filter-row { display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap; }
.rw-fg { display:flex; flex-direction:column; gap:4px; }
.rw-fg label { font-size:0.78rem; font-weight:600; color:#64748b; }
.rw-fg .form-control { padding:7px 11px; font-size:0.84rem; border:1.5px solid #e2e8f0; border-radius:8px; }
.rw-fg .form-control:focus { border-color:var(--primary-color); outline:none; }
.btn-rw-apply { background:var(--primary-color); color:#fff; border:none; padding:8px 18px; border-radius:8px;
font-size:0.84rem; font-weight:600; cursor:pointer; display:inline-flex; align-items:center; gap:6px;
align-self:flex-end; white-space:nowrap; }
.btn-rw-apply:hover { background:#059669; }
.btn-rw-reset { background:#f1f5f9; color:#64748b; border:1.5px solid #e2e8f0; padding:8px 12px; border-radius:8px;
font-size:0.84rem; font-weight:600; text-decoration:none;
display:inline-flex; align-items:center; gap:6px; align-self:flex-end; }
.btn-rw-reset:hover { background:#e2e8f0; }
/* Periode label */
.rw-periode { background:linear-gradient(90deg,#f0fdf4,#e8f7f2); border-left:3px solid var(--primary-color);
border-radius:8px; padding:9px 14px; margin-bottom:14px; font-size:0.84rem; color:#374151;
display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.rw-periode i { color:var(--primary-color); }
/* KPI strip */
.rw-kpi-strip { display:grid; grid-template-columns:repeat(auto-fit,minmax(105px,1fr)); gap:8px; margin-bottom:14px; }
.rw-kpi { background:#fff; border-radius:10px; padding:11px 14px; box-shadow:0 2px 8px rgba(0,0,0,.05); text-align:center; }
.rw-kpi-val { font-size:1.45rem; font-weight:800; line-height:1; }
.rw-kpi-lbl { font-size:0.73rem; color:#94a3b8; margin-top:4px; font-weight:500; }
.rw-kpi-sub { font-size:0.68rem; color:#cbd5e1; margin-top:2px; }
/* Per-date block */
.rw-date-block { margin-bottom:20px; }
.rw-date-header { display:flex; align-items:center; gap:10px; padding:10px 16px; flex-wrap:wrap;
background:linear-gradient(90deg,var(--primary-color),#0ea574);
border-radius:10px 10px 0 0; color:#fff; }
.rw-date-today { background:linear-gradient(90deg,#0ea574,#059669); }
.dh-date { font-size:1rem; font-weight:700; }
.dh-hari { background:rgba(255,255,255,.22); padding:2px 10px; border-radius:12px; font-size:0.78rem; font-weight:600; }
.dh-count { margin-left:auto; font-size:0.78rem; background:rgba(255,255,255,.2); padding:3px 10px; border-radius:12px; }
/* Table */
.rw-tbl-wrap { background:#fff; border-radius:0 0 10px 10px; box-shadow:0 3px 12px rgba(0,0,0,.07); overflow-x:auto; }
.rw-table { width:100%; border-collapse:collapse; min-width:680px; }
.rw-table thead th { padding:9px 13px; text-align:left; font-size:0.78rem; font-weight:700;
color:#475569; background:#f8fafc; border-bottom:1.5px solid #e2e8f0; white-space:nowrap; }
.rw-table tbody td { padding:9px 13px; font-size:0.83rem; border-bottom:1px solid #f8fafc; vertical-align:middle; }
.rw-table tbody tr:last-child td { border-bottom:none; }
.rw-table tbody tr:hover { background:#f8fafc; }
.rw-table tfoot td { padding:8px 13px; }
/* Tags */
.rw-kelas-tag { background:#e8f7f2; color:var(--primary-dark); padding:2px 7px; border-radius:6px; font-size:.72rem; font-weight:600; margin-right:2px; display:inline-block; }
.rw-umum-tag { background:#f1f5f9; color:#64748b; padding:2px 7px; border-radius:6px; font-size:.72rem; font-weight:600; display:inline-block; }
.rw-kat-tag { background:#dbeafe; color:#1e40af; padding:2px 7px; border-radius:6px; font-size:.72rem; font-weight:600; display:inline-block; }
/* Mini progress */
.rw-mini-prog { height:6px; background:#f1f5f9; border-radius:3px; overflow:hidden; display:flex; min-width:70px; }
/* Button table */
.btn-tbl-detail { padding:5px 11px; background:var(--primary-color); color:#fff; border:none; border-radius:6px;
font-size:0.76rem; font-weight:600; text-decoration:none; display:inline-flex; align-items:center; gap:4px; white-space:nowrap; }
.btn-tbl-detail:hover { background:#059669; color:#fff; }
/* Cards (hari_ini) */
.rw-card { background:#fff; border-radius:12px; box-shadow:0 2px 10px rgba(0,0,0,.06); border:1px solid #f0f0f0; overflow:hidden; margin-bottom:10px; }
.rw-card:hover { box-shadow:0 4px 18px rgba(0,0,0,.1); }
.rw-card-head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; padding:14px 18px 10px; border-bottom:1px solid #f1f5f9; flex-wrap:wrap; }
.rw-card-title { font-size:1rem; font-weight:700; color:var(--primary-dark); margin:0 0 5px; display:flex; align-items:center; gap:8px; }
.rw-card-meta { display:flex; gap:10px; flex-wrap:wrap; font-size:.8rem; color:#64748b; align-items:center; }
.rw-card-meta span{ display:inline-flex; align-items:center; gap:4px; }
.rw-status { padding:4px 12px; border-radius:20px; font-size:.76rem; font-weight:700; white-space:nowrap; flex-shrink:0; display:inline-flex; align-items:center; gap:5px; }
.rw-status.belum { background:#f1f5f9; color:#64748b; }
.rw-status.selesai{ background:#d1fae5; color:#065f46; }
.rw-status.brlgs { background:#fef9c3; color:#854d0e; }
.rw-card-body { padding:10px 18px 14px; }
.rw-stats-row { display:flex; gap:6px; flex-wrap:wrap; align-items:center; margin-bottom:10px; }
.rw-chip { padding:4px 10px; border-radius:10px; font-size:.76rem; font-weight:700; display:inline-flex; align-items:center; gap:4px; }
.rw-chip.hadir { background:#d1fae5; color:#065f46; }
.rw-chip.terlambat{ background:#fff3e0; color:#e65100; }
.rw-chip.izin { background:#fef3c7; color:#92400e; }
.rw-chip.sakit { background:#dbeafe; color:#1e40af; }
.rw-chip.alpa { background:#fee2e2; color:#991b1b; }
.rw-chip.pulang { background:#f3e8ff; color:#6b21a8; }
.rw-chip.none { background:#f1f5f9; color:#94a3b8; }
.rw-progress-wrap { height:8px; background:#f1f5f9; border-radius:4px; overflow:hidden; display:flex; margin-bottom:6px; }
.rw-prog-hadir { background:#22c55e; }
.rw-prog-terlambat{ background:#FF9800; }
.rw-prog-izin { background:#f59e0b; }
.rw-prog-sakit { background:#3b82f6; }
.rw-prog-alpa { background:#ef4444; }
.rw-prog-pulang { background:#a855f7; }
.rw-prog-belum { background:#e2e8f0; }
.rw-card-foot { display:flex; align-items:center; justify-content:space-between; padding:8px 18px; background:#fafafa; border-top:1px solid #f1f5f9; flex-wrap:wrap; gap:8px; }
.rw-total-txt { font-size:.78rem; color:#94a3b8; }
.rw-total-txt strong { color:#374151; }
.btn-rw-detail { padding:6px 16px; background:var(--primary-color); color:#fff; border:none; border-radius:7px; font-size:.82rem; font-weight:600; text-decoration:none; display:inline-flex; align-items:center; gap:5px; }
.btn-rw-detail:hover { background:#059669; color:#fff; }
/* Empty */
.rw-empty { text-align:center; padding:48px 20px; background:#fff; border-radius:12px; box-shadow:0 2px 10px rgba(0,0,0,.06); }
.rw-empty i { font-size:3rem; color:#cbd5e1; display:block; margin-bottom:12px; }
.rw-empty h3 { margin:0 0 6px; color:var(--primary-dark); }
.rw-empty p { margin:0; color:var(--text-light); font-size:.88rem; }
@media(max-width:640px) {
.rw-filter-row { flex-direction:column; }
.rw-fg { width:100%; }
.rw-fg .form-control { width:100%; }
.btn-rw-apply,.btn-rw-reset { width:100%; justify-content:center; }
.rw-kpi-strip { grid-template-columns:repeat(3,1fr); }
.dh-count { display:none; }
}
</style>
<?php
$mode = $mode ?? 'hari_ini';
$dari = $dari ?? now()->format('Y-m-d');
$sampai = $sampai ?? now()->format('Y-m-d');
$tanggal = $tanggal ?? now()->format('Y-m-d');
$kategoriId = $kategoriId ?? '';
$passParams = ['mode' => $mode, 'dari' => $dari, 'sampai' => $sampai, 'tanggal' => $tanggal];
$totalAbsensi = $summary['total_absensi'] ?? 0;
$totalHadir = $summary['hadir'] ?? 0;
$pctHadirGlbl = $totalAbsensi > 0 ? round($totalHadir / $totalAbsensi * 100, 1) : 0;
?>
<div class="rw-header">
<h2><i class="fas fa-history"></i> Riwayat Kegiatan &amp; Absensi</h2>
</div>
<div class="rw-filter">
<div class="rw-mode-tabs">
<a href="<?php echo e(route('admin.riwayat-kegiatan.index', ['mode'=>'hari_ini','tanggal'=>$tanggal])); ?>"
class="rw-tab <?php echo e($mode==='hari_ini'?'active':''); ?>">
<i class="fas fa-calendar-day"></i> Hari Ini
</a>
<a href="<?php echo e(route('admin.riwayat-kegiatan.index', ['mode'=>'minggu_ini'])); ?>"
class="rw-tab <?php echo e($mode==='minggu_ini'?'active':''); ?>">
<i class="fas fa-calendar-week"></i> Minggu Ini
</a>
<a href="<?php echo e(route('admin.riwayat-kegiatan.index', ['mode'=>'custom','dari'=>$dari,'sampai'=>$sampai])); ?>"
class="rw-tab <?php echo e($mode==='custom'?'active':''); ?>">
<i class="fas fa-sliders-h"></i> Rentang Tanggal
</a>
</div>
<form method="GET" action="<?php echo e(route('admin.riwayat-kegiatan.index')); ?>">
<?php if($mode === 'hari_ini'): ?>
<input type="hidden" name="mode" value="hari_ini">
<div class="rw-filter-row">
<div class="rw-fg">
<label><i class="fas fa-calendar-alt"></i> Tanggal</label>
<input type="date" name="tanggal" class="form-control" value="<?php echo e($tanggal); ?>" style="max-width:180px;">
</div>
<div class="rw-fg">
<label><i class="fas fa-tag"></i> Kategori</label>
<select name="kategori_id" class="form-control" style="max-width:200px;">
<option value="">Semua Kategori</option>
<?php $__currentLoopData = $kategoris; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $k): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($k->kategori_id); ?>" <?php echo e($kategoriId==$k->kategori_id?'selected':''); ?>><?php echo e($k->nama_kategori); ?></option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
</div>
<button type="submit" class="btn-rw-apply"><i class="fas fa-search"></i> Tampilkan</button>
<a href="<?php echo e(route('admin.riwayat-kegiatan.index')); ?>" class="btn-rw-reset"><i class="fas fa-undo"></i> Reset</a>
</div>
<?php elseif($mode === 'minggu_ini'): ?>
<input type="hidden" name="mode" value="minggu_ini">
<div class="rw-filter-row">
<div class="rw-fg">
<label><i class="fas fa-tag"></i> Kategori</label>
<select name="kategori_id" class="form-control" style="max-width:200px;">
<option value="">Semua Kategori</option>
<?php $__currentLoopData = $kategoris; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $k): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($k->kategori_id); ?>" <?php echo e($kategoriId==$k->kategori_id?'selected':''); ?>><?php echo e($k->nama_kategori); ?></option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
</div>
<button type="submit" class="btn-rw-apply"><i class="fas fa-search"></i> Tampilkan</button>
<a href="<?php echo e(route('admin.riwayat-kegiatan.index', ['mode'=>'minggu_ini'])); ?>" class="btn-rw-reset"><i class="fas fa-undo"></i> Reset</a>
</div>
<?php else: ?>
<input type="hidden" name="mode" value="custom">
<div class="rw-filter-row">
<div class="rw-fg">
<label><i class="fas fa-calendar-alt"></i> Dari Tanggal</label>
<input type="date" name="dari" class="form-control" value="<?php echo e($dari); ?>" style="max-width:180px;" required>
</div>
<div class="rw-fg">
<label><i class="fas fa-calendar-alt"></i> Sampai Tanggal</label>
<input type="date" name="sampai" class="form-control" value="<?php echo e($sampai); ?>" style="max-width:180px;" required>
</div>
<div class="rw-fg">
<label><i class="fas fa-tag"></i> Kategori</label>
<select name="kategori_id" class="form-control" style="max-width:200px;">
<option value="">Semua Kategori</option>
<?php $__currentLoopData = $kategoris; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $k): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($k->kategori_id); ?>" <?php echo e($kategoriId==$k->kategori_id?'selected':''); ?>><?php echo e($k->nama_kategori); ?></option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
</div>
<button type="submit" class="btn-rw-apply"><i class="fas fa-search"></i> Tampilkan</button>
<a href="<?php echo e(route('admin.riwayat-kegiatan.index', ['mode'=>'custom'])); ?>" class="btn-rw-reset"><i class="fas fa-undo"></i> Reset</a>
</div>
<?php endif; ?>
</form>
</div>
<div class="rw-periode">
<i class="fas fa-calendar-check"></i>
Menampilkan: <strong><?php echo e($periodeLabel); ?></strong>
<?php if($mode !== 'hari_ini' && ($summary['jumlah_hari'] ?? 0) > 0): ?>
<span style="color:#94a3b8;">·</span>
<strong style="color:var(--primary-color);"><?php echo e($summary['jumlah_hari']); ?></strong> hari aktif
<span style="color:#94a3b8;">·</span>
<strong style="color:var(--primary-color);"><?php echo e($summary['jumlah_kegiatan']); ?></strong> sesi
<?php endif; ?>
</div>
<?php if($totalAbsensi > 0): ?>
<div class="rw-kpi-strip">
<div class="rw-kpi">
<div class="rw-kpi-val" style="color:#065f46;"><?php echo e($summary['hadir']); ?></div>
<div class="rw-kpi-lbl"><i class="fas fa-check" style="color:#22c55e;"></i> Hadir Efektif</div>
<?php if($summary['terlambat'] > 0): ?>
<div class="rw-kpi-sub">+<?php echo e($summary['terlambat']); ?> terlambat</div>
<?php endif; ?>
</div>
<div class="rw-kpi">
<div class="rw-kpi-val" style="color:#e65100;"><?php echo e($summary['terlambat']); ?></div>
<div class="rw-kpi-lbl"><i class="fas fa-clock" style="color:#FF9800;"></i> Terlambat</div>
</div>
<div class="rw-kpi">
<div class="rw-kpi-val" style="color:#92400e;"><?php echo e($summary['izin']); ?></div>
<div class="rw-kpi-lbl"><i class="fas fa-envelope" style="color:#f59e0b;"></i> Izin</div>
</div>
<div class="rw-kpi">
<div class="rw-kpi-val" style="color:#1e40af;"><?php echo e($summary['sakit']); ?></div>
<div class="rw-kpi-lbl"><i class="fas fa-heartbeat" style="color:#3b82f6;"></i> Sakit</div>
</div>
<div class="rw-kpi">
<div class="rw-kpi-val" style="color:#991b1b;"><?php echo e($summary['alpa']); ?></div>
<div class="rw-kpi-lbl"><i class="fas fa-times-circle" style="color:#ef4444;"></i> Alpa</div>
</div>
<div class="rw-kpi" style="border-top:2px solid var(--primary-color);">
<div class="rw-kpi-val" style="color:<?php echo e($pctHadirGlbl>=85?'#059669':($pctHadirGlbl>=70?'#d97706':'#dc2626')); ?>;"><?php echo e($pctHadirGlbl); ?>%</div>
<div class="rw-kpi-lbl"><i class="fas fa-chart-pie" style="color:var(--primary-color);"></i> Rata-rata</div>
<div class="rw-kpi-sub">dari <?php echo e($totalAbsensi); ?> tercatat
<?php if($mode !== 'hari_ini' && ($summary['jumlah_kegiatan']??0) > 0): ?>
· <?php echo e($summary['jumlah_kegiatan']); ?> sesi
<?php endif; ?>
</div>
</div>
</div>
<?php endif; ?>
<?php if($mode === 'hari_ini'): ?>
<?php if($kegiatans && $kegiatans->count() > 0): ?>
<?php $__currentLoopData = $kegiatans; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kegiatan): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php echo $__env->make('admin.kegiatan.riwayat._card', ['kegiatan' => $kegiatan, 'passParams' => $passParams], \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<div style="margin-top:16px;"><?php echo $kegiatans->appends(request()->query())->links('pagination::simple-bootstrap-4'); ?></div>
<?php else: ?>
<div class="rw-empty">
<i class="fas fa-calendar-times"></i>
<h3>Tidak Ada Kegiatan</h3>
<p>Tidak ada kegiatan pada <strong><?php echo e($periodeLabel); ?></strong>.</p>
<p style="margin-top:8px; font-size:.82rem;">
Coba tanggal lain atau lihat <a href="<?php echo e(route('admin.riwayat-kegiatan.index', ['mode'=>'minggu_ini'])); ?>" style="color:var(--primary-color);">riwayat minggu ini</a>.
</p>
</div>
<?php endif; ?>
<?php else: ?>
<?php if($kegiatanPerTanggal && $kegiatanPerTanggal->count() > 0): ?>
<?php $__currentLoopData = $kegiatanPerTanggal; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $tgl => $items): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$tglCarbon = \Carbon\Carbon::parse($tgl);
$isHariIni = ($tgl === now()->format('Y-m-d'));
$hariLabel = $tglCarbon->locale('id')->isoFormat('dddd');
$tglLabel = $tglCarbon->locale('id')->isoFormat('D MMMM Y');
$tglHadir = $items->sum('hadir') + $items->sum('terlambat');
$tglTotal = $items->sum('total_absensi');
$tglPct = $tglTotal > 0 ? round($tglHadir / $tglTotal * 100) : 0;
?>
<div class="rw-date-block">
<div class="rw-date-header <?php echo e($isHariIni ? 'rw-date-today' : ''); ?>">
<span class="dh-date"><?php echo e($tglLabel); ?></span>
<span class="dh-hari"><?php echo e($hariLabel); ?><?php echo e($isHariIni ? ' · Hari Ini' : ''); ?></span>
<span class="dh-count">
<?php echo e($items->count()); ?> kegiatan
&nbsp;·&nbsp;
<strong><?php echo e($tglHadir); ?></strong>/<?php echo e($tglTotal); ?> hadir
<?php if($tglTotal > 0): ?>
<span style="background:rgba(255,255,255,.15); padding:1px 7px; border-radius:8px; margin-left:4px;"><?php echo e($tglPct); ?>%</span>
<?php endif; ?>
</span>
</div>
<div class="rw-tbl-wrap">
<table class="rw-table">
<thead>
<tr>
<th style="width:36px; text-align:center;">No</th>
<th>Nama Kegiatan</th>
<th style="width:95px;">Waktu</th>
<th style="width:110px;">Kategori</th>
<th style="width:140px;">Kelas</th>
<th style="width:58px; text-align:center; color:#22c55e;" title="Hadir efektif (termasuk terlambat)">Hadir</th>
<th style="width:52px; text-align:center; color:#FF9800;" title="Terlambat">Tlbt</th>
<th style="width:48px; text-align:center; color:#f59e0b;" title="Izin">Izin</th>
<th style="width:48px; text-align:center; color:#3b82f6;" title="Sakit">Skt</th>
<th style="width:48px; text-align:center; color:#ef4444;" title="Alpa">Alpa</th>
<th style="width:90px;">Progress</th>
<th style="width:88px; text-align:center;">Aksi</th>
</tr>
</thead>
<tbody>
<?php $__currentLoopData = $items; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $idx => $kegiatan): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$hadirEfektif = $kegiatan->hadir + $kegiatan->terlambat;
$totalKeg = $kegiatan->total_absensi;
$kelasNames = $kegiatan->kelasKegiatan->pluck('nama_kelas');
$isUmum = $kelasNames->isEmpty();
?>
<tr>
<td style="color:#94a3b8; font-size:.76rem; text-align:center;"><?php echo e($idx + 1); ?></td>
<td>
<div style="font-weight:700; font-size:.87rem; color:var(--primary-dark);"><?php echo e($kegiatan->nama_kegiatan); ?></div>
</td>
<td style="font-size:.79rem; color:#64748b; white-space:nowrap;">
<?php echo e(date('H:i', strtotime($kegiatan->waktu_mulai))); ?><br>
<span style="color:#94a3b8; font-size:.7rem;">s/d <?php echo e(date('H:i', strtotime($kegiatan->waktu_selesai))); ?></span>
</td>
<td>
<?php if($kegiatan->kategori): ?>
<span class="rw-kat-tag"><?php echo e($kegiatan->kategori->nama_kategori); ?></span>
<?php endif; ?>
</td>
<td>
<?php if($isUmum): ?>
<span class="rw-umum-tag"><i class="fas fa-globe-asia"></i> Umum</span>
<?php else: ?>
<?php $__currentLoopData = $kelasNames->take(2); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kn): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<span class="rw-kelas-tag"><?php echo e($kn); ?></span>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<?php if($kelasNames->count() > 2): ?>
<span class="rw-umum-tag">+<?php echo e($kelasNames->count()-2); ?></span>
<?php endif; ?>
<?php endif; ?>
</td>
<td style="text-align:center; font-weight:700; color:#065f46;"><?php echo e($hadirEfektif ?: ''); ?></td>
<td style="text-align:center; color:#e65100;"><?php echo e($kegiatan->terlambat ?: ''); ?></td>
<td style="text-align:center; color:#92400e;"><?php echo e($kegiatan->izin ?: ''); ?></td>
<td style="text-align:center; color:#1e40af;"><?php echo e($kegiatan->sakit ?: ''); ?></td>
<td style="text-align:center; color:#991b1b; font-weight:700;"><?php echo e($kegiatan->alpa ?: ''); ?></td>
<td>
<?php if($totalKeg > 0): ?>
<?php
$segs = [
['w'=>round($kegiatan->hadir/$totalKeg*100), 'c'=>'#22c55e'],
['w'=>round($kegiatan->terlambat/$totalKeg*100), 'c'=>'#FF9800'],
['w'=>round($kegiatan->izin/$totalKeg*100), 'c'=>'#f59e0b'],
['w'=>round($kegiatan->sakit/$totalKeg*100), 'c'=>'#3b82f6'],
['w'=>round($kegiatan->alpa/$totalKeg*100), 'c'=>'#ef4444'],
];
?>
<div class="rw-mini-prog">
<?php $__currentLoopData = $segs; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $s): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php if($s['w'] > 0): ?><div style="width:<?php echo e($s['w']); ?>%;background:<?php echo e($s['c']); ?>;"></div><?php endif; ?>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<div style="font-size:.67rem; color:#94a3b8; margin-top:2px;"><?php echo e($hadirEfektif); ?>/<?php echo e($totalKeg); ?></div>
<?php else: ?>
<span style="color:#cbd5e1; font-size:.76rem;"></span>
<?php endif; ?>
</td>
<td style="text-align:center;">
<a href="<?php echo e(route('admin.riwayat-kegiatan.show', $kegiatan->id)); ?>?mode=hari_ini&tanggal=<?php echo e($tgl); ?>&dari=<?php echo e($tgl); ?>&sampai=<?php echo e($tgl); ?>"
class="btn-tbl-detail">
<i class="fas fa-users"></i> Santri
</a>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</tbody>
<?php if($items->count() > 1): ?>
<tfoot>
<tr style="background:#f0fdf4; border-top:1.5px solid #bbf7d0;">
<td colspan="5" style="font-size:.78rem; font-weight:700; color:#065f46; padding:8px 13px;">
<i class="fas fa-layer-group"></i> Total <?php echo e($tglLabel); ?>
</td>
<td style="text-align:center; font-weight:800; color:#065f46;"><?php echo e($tglHadir); ?></td>
<td style="text-align:center; font-weight:700; color:#e65100;"><?php echo e($items->sum('terlambat')); ?></td>
<td style="text-align:center; font-weight:700; color:#92400e;"><?php echo e($items->sum('izin')); ?></td>
<td style="text-align:center; font-weight:700; color:#1e40af;"><?php echo e($items->sum('sakit')); ?></td>
<td style="text-align:center; font-weight:700; color:#991b1b;"><?php echo e($items->sum('alpa')); ?></td>
<td colspan="2" style="font-size:.75rem; color:#94a3b8; padding:8px 13px;">
<?php echo e($tglTotal); ?> tercatat &nbsp;·&nbsp; <?php echo e($tglPct); ?>% hadir
</td>
</tr>
</tfoot>
<?php endif; ?>
</table>
</div>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<?php else: ?>
<div class="rw-empty">
<i class="fas fa-calendar-times"></i>
<h3>Tidak Ada Data Absensi</h3>
<p>Tidak ada absensi yang tercatat pada periode <strong><?php echo e($periodeLabel); ?></strong>.</p>
<?php if($mode === 'custom'): ?>
<p style="margin-top:8px; font-size:.82rem; color:#6b7280;">
Pastikan rentang tanggal sudah benar dan ada data absensi di periode tersebut.
</p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kegiatan/riwayat/index.blade.php ENDPATH**/ ?>

View File

@ -1,74 +0,0 @@
<?php $__env->startSection('title', 'Laporan Pembayaran SPP'); ?>
<?php $__env->startSection('content'); ?>
<div class="page-header">
<h2><i class="fas fa-file-pdf"></i> Laporan Pembayaran SPP</h2>
</div>
<div class="row-cards">
<!-- Laporan Semua Data -->
<div class="card card-primary hover-lift">
<h3>Laporan Semua Data</h3>
<p style="margin: 10px 0; color: var(--text-light);">
Cetak laporan semua pembayaran SPP atau dengan filter tertentu
</p>
<form action="<?php echo e(route('admin.pembayaran-spp.cetak-laporan')); ?>" method="GET" target="_blank">
<div class="form-group">
<label><i class="fas fa-calendar form-icon"></i> Bulan (Opsional)</label>
<select name="bulan" class="form-control">
<option value="">Semua Bulan</option>
<?php for($i = 1; $i <= 12; $i++): ?>
<option value="<?php echo e($i); ?>"><?php echo e(DateTime::createFromFormat('!m', $i)->format('F')); ?></option>
<?php endfor; ?>
</select>
</div>
<div class="form-group">
<label><i class="fas fa-calendar-alt form-icon"></i> Tahun (Opsional)</label>
<select name="tahun" class="form-control">
<option value="">Semua Tahun</option>
<?php for($year = date('Y'); $year >= 2020; $year--): ?>
<option value="<?php echo e($year); ?>"><?php echo e($year); ?></option>
<?php endfor; ?>
</select>
</div>
<div class="form-group">
<label><i class="fas fa-info-circle form-icon"></i> Status (Opsional)</label>
<select name="status" class="form-control">
<option value="">Semua Status</option>
<option value="Lunas">Lunas</option>
<option value="Belum Lunas">Belum Lunas</option>
<option value="Telat">Telat</option>
</select>
</div>
<button type="submit" class="btn btn-primary hover-shadow" style="width: 100%;">
<i class="fas fa-print"></i> Cetak Laporan
</button>
</form>
<div class="content-box" style="margin-top: 25px;">
<a href="<?php echo e(route('admin.pembayaran-spp.index')); ?>" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</div>
<?php $__env->startPush('scripts'); ?>
<script>
// Update form action saat santri dipilih
document.getElementById('select-santri').addEventListener('change', function() {
const form = document.getElementById('form-santri');
const selectedId = this.value;
if (selectedId) {
form.action = "<?php echo e(route('admin.pembayaran-spp.cetak-laporan-santri', ':id')); ?>".replace(':id', selectedId);
}
});
</script>
<?php $__env->stopPush(); ?>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/pembayaran-spp/laporan.blade.php ENDPATH**/ ?>

View File

@ -0,0 +1,173 @@

<?php $__env->startSection('title', 'Data Santri'); ?>
<?php $__env->startSection('content'); ?>
<div class="page-header">
<h2><i class="fas fa-users"></i> Data Santri</h2>
</div>
<?php if(session('success')): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> <?php echo e(session('success')); ?>
</div>
<?php endif; ?>
<div class="content-box">
<!-- Header Actions -->
<div style="display: flex; justify-content: space-between; align-items: flex-start; gap: 11px; margin-bottom: 14px; flex-wrap: wrap;">
<!-- Tombol Tambah -->
<a href="<?php echo e(route('admin.santri.create')); ?>" class="btn btn-primary btn-sm">
<i class="fas fa-plus"></i> Tambah Santri
</a>
<!-- Form Search & Filter -->
<form action="<?php echo e(route('admin.santri.index')); ?>" method="GET" style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center;">
<input type="text" name="search" class="form-control" placeholder="Cari nama, NIS, atau ID..." value="<?php echo e(request('search')); ?>" style="width: 220px; height: 38px;">
<select name="status" class="form-control" style="width: 150px; height: 38px;">
<option value=""> Semua Status</option>
<option value="Aktif" <?php echo e(request('status') == 'Aktif' ? 'selected' : ''); ?>>Aktif</option>
<option value="Lulus" <?php echo e(request('status') == 'Lulus' ? 'selected' : ''); ?>>Lulus</option>
<option value="Tidak Aktif" <?php echo e(request('status') == 'Tidak Aktif' ? 'selected' : ''); ?>>Tidak Aktif</option>
</select>
<select name="id_kelas" class="form-control" style="width: 180px; height: 38px;">
<option value=""> Semua Kelas</option>
<?php $__currentLoopData = $kelompokKelas; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kelompok): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php if($kelompok->kelas && $kelompok->kelas->count() > 0): ?>
<optgroup label="<?php echo e($kelompok->nama_kelompok); ?>">
<?php $__currentLoopData = $kelompok->kelas; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kelas): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($kelas->id); ?>" <?php echo e(request('id_kelas') == $kelas->id ? 'selected' : ''); ?>>
<?php echo e($kelas->nama_kelas); ?>
</option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</optgroup>
<?php endif; ?>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
<button type="submit" class="btn btn-primary btn-sm" style="height: 38px; padding: 0 16px;">
<i class="fas fa-search"></i> Cari
</button>
<?php if(request('search') || request('status') || request('id_kelas')): ?>
<a href="<?php echo e(route('admin.santri.index')); ?>" class="btn btn-secondary btn-sm" style="height: 38px; padding: 0 16px; display: inline-flex; align-items: center;">
<i class="fas fa-redo"></i> Reset
</a>
<?php endif; ?>
</form>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>No</th>
<th>Foto</th>
<th>ID Santri</th>
<th>NIS</th>
<th>Nama Lengkap</th>
<th>Jenis Kelamin</th>
<th>Status</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
<?php $__empty_1 = true; $__currentLoopData = $santris; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $index => $santri): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?>
<tr>
<td><?php echo e($santris->firstItem() + $index); ?></td>
<td>
<?php if($santri->foto): ?>
<img src="<?php echo e(asset('storage/' . $santri->foto)); ?>"
alt="Foto <?php echo e($santri->nama_lengkap); ?>"
style="width: 40px; height: 40px; border-radius: 50%; object-fit: cover; border: 2px solid var(--primary-color);"
loading="lazy">
<?php else: ?>
<div class="santri-avatar-initial" style="width: 40px; height: 40px; border-radius: 50%; background: var(--primary-color); display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 0.9rem;">
<?php echo e(strtoupper(substr($santri->nama_lengkap, 0, 1))); ?>
</div>
<?php endif; ?>
</td>
<td><strong><?php echo e($santri->id_santri); ?></strong></td>
<td><?php echo e($santri->nis ?? '-'); ?></td>
<td><?php echo e($santri->nama_lengkap); ?></td>
<td><?php echo e($santri->jenis_kelamin); ?></td>
<td>
<?php if($santri->status == 'Aktif'): ?>
<span style="padding: 6px 12px; border-radius: 6px; font-size: 0.85rem; font-weight: 600; background: linear-gradient(135deg, #E8F7F2 0%, #D4F1E3 100%); color: #2C5F4F; display: inline-block;">
<i class="fas fa-check-circle"></i> <?php echo e($santri->status); ?>
</span>
<?php elseif($santri->status == 'Lulus'): ?>
<span style="padding: 6px 12px; border-radius: 6px; font-size: 0.85rem; font-weight: 600; background: linear-gradient(135deg, #E3F2FD 0%, #D1E9F9 100%); color: #2D4A7C; display: inline-block;">
<i class="fas fa-graduation-cap"></i> <?php echo e($santri->status); ?>
</span>
<?php else: ?>
<span style="padding: 6px 12px; border-radius: 6px; font-size: 0.85rem; font-weight: 600; background: linear-gradient(135deg, #E8ECF0 0%, #D1D8E0 100%); color: #555; display: inline-block;">
<i class="fas fa-times-circle"></i> <?php echo e($santri->status); ?>
</span>
<?php endif; ?>
</td>
<td>
<a href="<?php echo e(route('admin.santri.show', $santri)); ?>" class="btn btn-sm btn-primary">
<i class="fas fa-eye"></i></a>
<a href="<?php echo e(route('admin.santri.edit', $santri)); ?>" class="btn btn-sm btn-warning">
<i class="fas fa-edit"></i></a>
<form action="<?php echo e(route('admin.santri.destroy', $santri)); ?>" method="POST" style="display:inline;">
<?php echo csrf_field(); ?>
<?php echo method_field('DELETE'); ?>
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Yakin ingin menghapus data santri <?php echo e($santri->nama_lengkap); ?>?')">
<i class="fas fa-trash"></i>
</button>
</form>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?>
<tr>
<td colspan="8" class="text-center" style="padding: 22px;">
<i class="fas fa-inbox" style="font-size: 2.2rem; color: #ccc; margin-bottom: 15px; display: block;"></i>
<?php if(request('search') || request('status') || request('id_kelas')): ?>
<strong>Data tidak ditemukan.</strong><br>
<small>Coba ubah kata kunci pencarian atau filter yang digunakan.</small>
<?php else: ?>
<strong>Belum ada data santri.</strong><br>
<small>Klik tombol "Tambah Santri" untuk menambahkan data baru.</small>
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if($santris->count() > 0): ?>
<div style="margin-top: 14px; padding-top: 20px; border-top: 1px solid #E8F7F2;">
<p style="color: #7F8C8D; font-size: 0.9rem;">
<i class="fas fa-info-circle"></i>
Menampilkan <strong><?php echo e($santris->count()); ?></strong> dari <strong><?php echo e($santris->total()); ?></strong> data santri
<?php if(request('search') || request('status') || request('id_kelas')): ?>
(hasil pencarian/filter)
<?php endif; ?>
</p>
</div>
<?php endif; ?>
<!-- Pagination -->
<?php if(method_exists($santris, 'links')): ?>
<div style="margin-top: 14px;">
<?php echo e($santris->links()); ?>
</div>
<?php endif; ?>
</div>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', ['isAdmin' => true], \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/santri/index.blade.php ENDPATH**/ ?>

View File

@ -0,0 +1,465 @@

<?php $__env->startSection('content'); ?>
<div class="page-header">
<h2><i class="fas fa-clipboard-check"></i> Input Absensi: <?php echo e($kegiatan->nama_kegiatan); ?></h2>
</div>
<div class="content-box" style="margin-bottom: 14px;">
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 11px;">
<div>
<h3 style="margin: 0; color: var(--primary-color);"><?php echo e($kegiatan->nama_kegiatan); ?></h3>
<p style="margin: 5px 0 0 0; color: var(--text-light);">
<i class="fas fa-calendar-day"></i> <?php echo e($kegiatan->hari); ?> |
<i class="fas fa-clock"></i> <?php echo e(date('H:i', strtotime($kegiatan->waktu_mulai))); ?> - <?php echo e(date('H:i', strtotime($kegiatan->waktu_selesai))); ?> |
<i class="fas fa-list-alt"></i> <?php echo e($kegiatan->kategori->nama_kategori); ?>
</p>
</div>
<div style="display: flex; gap: 10px;">
<button type="button" id="btnModeManual" class="btn btn-primary" onclick="setMode('manual')">
<i class="fas fa-hand-pointer"></i> Mode Manual
</button>
<button type="button" id="btnModeRfid" class="btn btn-secondary" onclick="setMode('rfid')">
<i class="fas fa-id-card"></i> Mode RFID
</button>
</div>
</div>
</div>
<div class="info-box" style="margin-bottom: 14px; border-left: 4px solid var(--primary-color);">
<i class="fas fa-info-circle"></i>
<?php if($kegiatanInfo['is_umum']): ?>
<strong>Kegiatan Umum</strong> - Diikuti oleh semua santri aktif (<?php echo e($santris->count()); ?> santri)
<?php else: ?>
<strong>Kegiatan Khusus</strong> - Untuk kelas:
<strong style="color: var(--primary-color);"><?php echo e($kegiatanInfo['kelas_list']); ?></strong>
(<?php echo e($kegiatanInfo['jumlah_kelas']); ?> kelas)
&nbsp;|&nbsp; Total semua santri aktif: <strong><?php echo e($santris->count()); ?> santri</strong>
<?php endif; ?>
</div>
<?php
$sudahAdaData = count($absensiData) > 0;
?>
<?php if($sudahAdaData): ?>
<div class="alert alert-info" style="margin-bottom: 14px;">
<i class="fas fa-edit"></i>
<strong>Mode Edit</strong> - Data absensi untuk tanggal ini sudah ada
(<strong><?php echo e(count($absensiData)); ?></strong> dari <strong><?php echo e($santris->count()); ?></strong> santri sudah diinput,
<strong style="color: <?php echo e(($santris->count() - count($absensiData)) > 0 ? '#dc2626' : '#059669'); ?>;"><?php echo e($santris->count() - count($absensiData)); ?> belum absen</strong>).
Anda dapat mengubah status absensi lalu klik Simpan.
</div>
<?php endif; ?>
<!-- MODE MANUAL -->
<div id="modeManual" class="content-box">
<form action="<?php echo e(route('admin.absensi-kegiatan.simpan')); ?>" method="POST">
<?php echo csrf_field(); ?>
<input type="hidden" name="kegiatan_id" value="<?php echo e($kegiatan->kegiatan_id); ?>">
<div class="filter-form-inline" style="margin-bottom: 14px; gap: 12px;">
<div class="filter-form-inline" style="gap: 8px;">
<label style="font-weight: 600; white-space: nowrap; margin: 0;">
<i class="fas fa-calendar"></i> Tanggal:
</label>
<input type="date" name="tanggal" id="tanggal" class="form-control"
value="<?php echo e($tanggal); ?>" required style="max-width: 170px;">
</div>
<div class="filter-form-inline" style="gap: 8px;">
<label style="font-weight: 600; white-space: nowrap; margin: 0;">
<i class="fas fa-school"></i> Pilih Kelas:
</label>
<select id="kelasFilter" class="form-control" onchange="filterKelas(this.value)" style="max-width: 220px;">
<option value="semua">-- Tampilkan Semua Kelas --</option>
<?php $__currentLoopData = $santriGrouped; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kelasNama => $santriKelas): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($kelasNama); ?>"><?php echo e($kelasNama); ?> (<?php echo e($santriKelas->count()); ?> santri)</option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
</div>
<div style="margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap;">
<button type="button" class="btn btn-sm btn-info" onclick="setAllStatus('Hadir')">
<i class="fas fa-check-double"></i> Semua Hadir
</button>
<button type="button" class="btn btn-sm" style="background: #FF9800; color: white;" onclick="setAllStatus('Terlambat')">
<i class="fas fa-clock"></i> Semua Terlambat
</button>
<button type="button" class="btn btn-sm btn-danger" onclick="setAllStatus('Alpa')">
<i class="fas fa-times"></i> Semua Alpa
</button>
<button type="button" class="btn btn-sm btn-secondary" onclick="clearAllStatus()">
<i class="fas fa-eraser"></i> Kosongkan
</button>
</div>
</div>
<div class="info-box" style="margin-bottom: 14px;">
<p style="margin: 0;"><i class="fas fa-info-circle"></i> Pilih kelas terlebih dahulu untuk menampilkan daftar santri. Santri tanpa pilihan status akan <strong>dilewati</strong>. Santri yang <strong>sedang pulang</strong> otomatis ditandai.</p>
</div>
<?php $__currentLoopData = $santriGrouped; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $kelasNama => $santriKelas): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$hadirCount = 0;
$totalKelas = $santriKelas->count();
foreach ($santriKelas as $s) {
$st = $absensiData[$s->id_santri] ?? null;
if ($st === 'Hadir') $hadirCount++;
}
$sudahInputKelas = false;
foreach ($santriKelas as $s) {
if (isset($absensiData[$s->id_santri])) { $sudahInputKelas = true; break; }
}
?>
<div class="kelas-group" data-kelas="<?php echo e($kelasNama); ?>" style="margin-bottom: 20px; display: none;">
<div style="background: var(--primary-light); padding: 10px 14px; border-radius: 8px 8px 0 0; display: flex; justify-content: space-between; align-items: center;">
<h4 style="margin: 0; color: var(--primary-color); font-size: 0.95rem;">
<i class="fas fa-users"></i> <?php echo e($kelasNama); ?>
</h4>
<div style="display: flex; gap: 8px; align-items: center;">
<?php if($sudahInputKelas): ?>
<span class="badge badge-info"><i class="fas fa-edit"></i> <?php echo e($hadirCount); ?>/<?php echo e($totalKelas); ?> hadir</span>
<?php endif; ?>
<span class="badge badge-primary"><?php echo e($totalKelas); ?> santri</span>
</div>
</div>
<div class="table-wrapper">
<table class="data-table" style="margin-top: 0; border-top-left-radius: 0; border-top-right-radius: 0;">
<thead>
<tr>
<th style="width: 50px;">No</th>
<th style="width: 100px;">ID Santri</th>
<th>Nama Santri</th>
<th style="width: 420px; text-align: center;">Status</th>
</tr>
</thead>
<tbody>
<?php $__currentLoopData = $santriKelas; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $santri): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<?php
$isPulang = in_array($santri->id_santri, $santriSedangPulang ?? []);
$currentStatus = $absensiData[$santri->id_santri] ?? ($isPulang ? 'Pulang' : '');
?>
<tr <?php if($isPulang): ?> style="background: #FFF8E1; opacity: 0.85;" <?php endif; ?>>
<td><?php echo e($loop->iteration); ?></td>
<td><strong><?php echo e($santri->id_santri); ?></strong></td>
<td>
<?php echo e($santri->nama_lengkap); ?>
<?php if($isPulang): ?>
<span class="badge" style="background: #FFF3E0; color: #E65100; font-size: 0.75rem; margin-left: 6px;">
<i class="fas fa-home"></i> Sedang Pulang
</span>
<?php endif; ?>
<?php if(isset($absensiData[$santri->id_santri])): ?>
<span class="badge badge-secondary" style="font-size: 0.7rem; margin-left: 4px;">
<i class="fas fa-edit"></i> Sudah input
</span>
<?php endif; ?>
</td>
<td class="text-center">
<?php if($isPulang): ?>
<input type="hidden" name="absensi[<?php echo e($santri->id_santri); ?>]" value="Pulang" class="absensi-input">
<span class="badge" style="background: #FFF3E0; color: #E65100; padding: 6px 14px; font-size: 0.85rem;">
<i class="fas fa-home"></i> Pulang
</span>
<?php else: ?>
<div style="display: flex; gap: 5px; justify-content: center; flex-wrap: wrap;">
<label style="margin: 0; cursor: pointer;">
<input type="radio" name="absensi[<?php echo e($santri->id_santri); ?>]" value="Hadir"
<?php echo e($currentStatus == 'Hadir' ? 'checked' : ''); ?> class="absensi-radio absensi-input">
<span class="badge badge-success">Hadir</span>
</label>
<label style="margin: 0; cursor: pointer;">
<input type="radio" name="absensi[<?php echo e($santri->id_santri); ?>]" value="Terlambat"
<?php echo e($currentStatus == 'Terlambat' ? 'checked' : ''); ?> class="absensi-radio absensi-input">
<span class="badge" style="background: #FF9800; color: white;">Terlambat</span>
</label>
<label style="margin: 0; cursor: pointer;">
<input type="radio" name="absensi[<?php echo e($santri->id_santri); ?>]" value="Izin"
<?php echo e($currentStatus == 'Izin' ? 'checked' : ''); ?> class="absensi-radio absensi-input">
<span class="badge badge-warning">Izin</span>
</label>
<label style="margin: 0; cursor: pointer;">
<input type="radio" name="absensi[<?php echo e($santri->id_santri); ?>]" value="Sakit"
<?php echo e($currentStatus == 'Sakit' ? 'checked' : ''); ?> class="absensi-radio absensi-input">
<span class="badge badge-info">Sakit</span>
</label>
<label style="margin: 0; cursor: pointer;">
<input type="radio" name="absensi[<?php echo e($santri->id_santri); ?>]" value="Alpa"
<?php echo e($currentStatus == 'Alpa' ? 'checked' : ''); ?> class="absensi-radio absensi-input">
<span class="badge badge-danger">Alpa</span>
</label>
<?php if($currentStatus): ?>
<label style="margin: 0; cursor: pointer;" title="Hapus pilihan">
<button type="button" class="btn btn-sm" style="padding: 2px 8px; font-size: 0.75rem; background: #f1f1f1;" onclick="clearRadio('<?php echo e($santri->id_santri); ?>')">
<i class="fas fa-undo"></i>
</button>
</label>
<?php endif; ?>
</div>
<?php endif; ?>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</tbody>
</table>
</div>
</div>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
<div id="noKelasSelected" class="empty-state" style="padding: 40px 20px;">
<i class="fas fa-hand-pointer"></i>
<h3>Pilih Kelas Terlebih Dahulu</h3>
<p>Silakan pilih kelas pada dropdown di atas untuk menampilkan daftar santri yang akan diabsen.</p>
</div>
<div class="btn-group" style="margin-top: 14px;">
<button type="submit" class="btn btn-success">
<i class="fas fa-save"></i> <?php echo e($sudahAdaData ? 'Update Absensi' : 'Simpan Absensi'); ?>
</button>
<a href="<?php echo e(route('admin.kegiatan.index')); ?>" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</form>
</div>
<!-- MODE RFID -->
<div id="modeRfid" class="content-box" style="display: none;">
<div class="form-group">
<label for="tanggalRfid">
<i class="fas fa-calendar form-icon"></i>
Tanggal Absensi
</label>
<input type="date" id="tanggalRfid" class="form-control" value="<?php echo e($tanggal); ?>">
</div>
<div class="info-box">
<p><i class="fas fa-id-card"></i> Tempelkan kartu RFID santri ke reader. Absensi akan otomatis tersimpan sebagai <strong>Hadir</strong>.</p>
</div>
<div style="background: linear-gradient(135deg, var(--primary-light) 0%, #D4F1E3 100%); padding: 22px; border-radius: var(--border-radius); text-align: center; margin-bottom: 14px;">
<div id="rfidStatus" style="font-size: 1.5rem; font-weight: 600; color: var(--primary-color); margin-bottom: 15px;">
<i class="fas fa-wifi"></i> Siap Scan RFID
</div>
<input type="text" id="rfidInput" placeholder="Fokus di sini untuk scan RFID..."
style="width: 100%; padding: 15px; font-size: 1.2rem; border: 3px solid var(--primary-color); border-radius: var(--border-radius-sm); text-align: center;"
autofocus>
</div>
<div id="rfidLog" style="max-height: 400px; overflow-y: auto; background: white; border-radius: var(--border-radius-sm); padding: 15px; border: 1px solid var(--primary-light);">
<h4 style="margin: 0 0 15px 0; color: var(--primary-color);"><i class="fas fa-history"></i> Log Absensi</h4>
<div id="rfidLogContent">
<p style="text-align: center; color: var(--text-light);">Belum ada absensi...</p>
</div>
</div>
<div class="btn-group" style="margin-top: 14px;">
<button type="button" class="btn btn-warning" onclick="clearLog()">
<i class="fas fa-trash"></i> Bersihkan Log
</button>
<a href="<?php echo e(route('admin.kegiatan.index')); ?>" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</div>
<script>
// -- Kelas Filter --
function filterKelas(value) {
var groups = document.querySelectorAll('.kelas-group');
var emptyMsg = document.getElementById('noKelasSelected');
if (value === 'semua') {
for (var i = 0; i < groups.length; i++) {
groups[i].style.display = 'block';
toggleGroupInputs(groups[i], true);
}
emptyMsg.style.display = 'none';
} else {
var found = false;
for (var i = 0; i < groups.length; i++) {
if (groups[i].getAttribute('data-kelas') === value) {
groups[i].style.display = 'block';
toggleGroupInputs(groups[i], true);
found = true;
} else {
groups[i].style.display = 'none';
toggleGroupInputs(groups[i], false);
}
}
emptyMsg.style.display = found ? 'none' : 'block';
}
}
// Enable/disable all inputs in a kelas group so hidden groups don't submit
function toggleGroupInputs(group, enabled) {
var inputs = group.querySelectorAll('.absensi-input');
for (var i = 0; i < inputs.length; i++) {
inputs[i].disabled = !enabled;
}
}
// -- Set All Status (for visible groups only) --
function setAllStatus(status) {
var groups = document.querySelectorAll('.kelas-group');
for (var i = 0; i < groups.length; i++) {
if (groups[i].style.display !== 'none') {
var radios = groups[i].querySelectorAll('input.absensi-radio[value="' + status + '"]');
for (var j = 0; j < radios.length; j++) {
radios[j].checked = true;
}
}
}
}
// -- Clear radio selection for a specific santri --
function clearRadio(santriId) {
var radios = document.querySelectorAll('input[name="absensi[' + santriId + ']"]');
for (var i = 0; i < radios.length; i++) {
radios[i].checked = false;
}
}
// -- Clear all selections in visible groups --
function clearAllStatus() {
var groups = document.querySelectorAll('.kelas-group');
for (var i = 0; i < groups.length; i++) {
if (groups[i].style.display !== 'none') {
var radios = groups[i].querySelectorAll('input.absensi-radio');
for (var j = 0; j < radios.length; j++) {
radios[j].checked = false;
}
}
}
}
// -- Mode Switch --
var currentMode = 'manual';
var kegiatanId = '<?php echo e($kegiatan->kegiatan_id); ?>';
function setMode(mode) {
currentMode = mode;
var modeManualEl = document.getElementById('modeManual');
var modeRfidEl = document.getElementById('modeRfid');
var btnManual = document.getElementById('btnModeManual');
var btnRfid = document.getElementById('btnModeRfid');
if (mode === 'manual') {
modeManualEl.style.display = 'block';
modeRfidEl.style.display = 'none';
btnManual.className = 'btn btn-primary';
btnRfid.className = 'btn btn-secondary';
} else {
modeManualEl.style.display = 'none';
modeRfidEl.style.display = 'block';
btnManual.className = 'btn btn-secondary';
btnRfid.className = 'btn btn-success';
document.getElementById('rfidInput').focus();
}
}
// -- RFID Scanner --
document.getElementById('rfidInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
var rfidUid = this.value.trim();
if (rfidUid) {
scanRfid(rfidUid);
this.value = '';
}
}
});
function scanRfid(rfidUid) {
var tanggal = document.getElementById('tanggalRfid').value;
var statusEl = document.getElementById('rfidStatus');
statusEl.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Memproses...';
statusEl.style.color = 'var(--warning-color)';
fetch('<?php echo e(route("admin.absensi-kegiatan.scan-rfid")); ?>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '<?php echo e(csrf_token()); ?>'
},
body: JSON.stringify({
rfid_uid: rfidUid,
kegiatan_id: kegiatanId,
tanggal: tanggal
})
})
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.success) {
statusEl.innerHTML = '<i class="fas fa-check-circle"></i> ' + data.message;
statusEl.style.color = 'var(--success-color)';
addLogEntry(data.data, 'success');
} else {
statusEl.innerHTML = '<i class="fas fa-exclamation-circle"></i> ' + data.message;
statusEl.style.color = 'var(--danger-color)';
}
setTimeout(function() {
statusEl.innerHTML = '<i class="fas fa-wifi"></i> Siap Scan RFID';
statusEl.style.color = 'var(--primary-color)';
}, 2000);
})
.catch(function(error) {
statusEl.innerHTML = '<i class="fas fa-times-circle"></i> Koneksi error';
statusEl.style.color = 'var(--danger-color)';
console.error('Error:', error);
setTimeout(function() {
statusEl.innerHTML = '<i class="fas fa-wifi"></i> Siap Scan RFID';
statusEl.style.color = 'var(--primary-color)';
}, 2000);
});
}
function addLogEntry(data, type) {
var logContent = document.getElementById('rfidLogContent');
if (logContent.querySelector('p')) {
logContent.innerHTML = '';
}
var entry = document.createElement('div');
entry.style.cssText = 'padding: 12px; margin-bottom: 10px; border-radius: 8px; background: ' +
(type === 'success' ? 'linear-gradient(135deg, #E8F7F2 0%, #D4F1E3 100%)' : 'linear-gradient(135deg, #FFE8EA 0%, #FFD5D8 100%)') +
'; border-left: 4px solid ' + (type === 'success' ? 'var(--success-color)' : 'var(--danger-color)');
entry.innerHTML = '<div style="display: flex; justify-content: space-between; align-items: center;">' +
'<div><strong>' + data.nama + '</strong> (' + data.id_santri + ')' +
'<div style="font-size: 0.85rem; color: var(--text-light); margin-top: 3px;">Kelas: ' + data.kelas + ' | Waktu: ' + data.waktu + '</div></div>' +
'<span class="badge badge-success"><i class="fas fa-check"></i> Hadir</span></div>';
logContent.insertBefore(entry, logContent.firstChild);
}
function clearLog() {
if (confirm('Yakin ingin membersihkan log?')) {
document.getElementById('rfidLogContent').innerHTML = '<p style="text-align: center; color: var(--text-light);">Belum ada absensi...</p>';
}
}
// -- Auto-focus RFID input --
setInterval(function() {
if (currentMode === 'rfid') {
var rfidInput = document.getElementById('rfidInput');
if (document.activeElement !== rfidInput) {
rfidInput.focus();
}
}
}, 1000);
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/kegiatan/absensi/input.blade.php ENDPATH**/ ?>

View File

@ -1,373 +0,0 @@
<?php $__env->startSection('title', 'Register Admin'); ?>
<?php $__env->startSection('auth-content'); ?>
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:opsz,wght@9..40,300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body.auth-page {
background: #F8FDFB !important;
font-family: 'DM Sans', sans-serif !important;
padding: 0 !important;
align-items: stretch !important;
min-height: 100vh !important;
}
.auth-container {
width: 100vw !important; max-width: 100vw !important;
min-height: 100vh !important; background: transparent !important;
padding: 0 !important; border-radius: 0 !important; box-shadow: none !important;
}
.rv2-wrap {
position: relative; width: 100%; min-height: 100vh;
display: flex; align-items: center; justify-content: center;
overflow: hidden; font-family: 'DM Sans', sans-serif;
}
.rv2-bg {
position: absolute; inset: 0; z-index: 0;
background:
radial-gradient(ellipse 80% 60% at 30% 50%, rgba(111,186,157,.12) 0%, transparent 60%),
radial-gradient(ellipse 50% 80% at 90% 20%, rgba(111,186,157,.08) 0%, transparent 55%),
#F8FDFB;
}
.rv2-bg::before {
content: ''; position: absolute; inset: 0;
background-image:
linear-gradient(rgba(111,186,157,.055) 1px, transparent 1px),
linear-gradient(90deg, rgba(111,186,157,.055) 1px, transparent 1px);
background-size: 48px 48px;
}
.rv2-ring {
position: absolute; border-radius: 50%;
border: 1.5px solid rgba(111,186,157,.18); pointer-events: none;
}
.rv2-ring.r1 { width:420px; height:420px; top:-110px; left:-110px; }
.rv2-ring.r2 { width:270px; height:270px; top:50px; left:60px; border-color:rgba(111,186,157,.10); }
.rv2-ring.r3 { width:200px; height:200px; bottom:-50px; right:60px; border-color:rgba(111,186,157,.14); }
.rv2-dot { position:absolute; border-radius:50%; background:#6FBA9D; pointer-events:none; }
.rv2-dot.d1 { width:8px; height:8px; top:22%; left:18%; opacity:.14; }
.rv2-dot.d2 { width:5px; height:5px; bottom:40%; left:30%; opacity:.09; }
.rv2-dot.d3 { width:11px; height:11px; top:55%; right:15%; opacity:.08; }
.rv2-line { position:absolute; height:1px; background:linear-gradient(90deg,transparent,rgba(111,186,157,.14),transparent); pointer-events:none; }
.rv2-line.l1 { width:280px; top:28%; right:-60px; transform:rotate(15deg); }
.rv2-line.l2 { width:220px; bottom:30%; left:-40px; transform:rotate(-18deg); }
.rv2-layout {
position: relative; z-index: 2;
display: flex; align-items: center;
width: 100%; max-width: 1100px;
padding: 40px 60px; gap: 80px;
animation: rv2In .6s ease both;
}
@keyframes rv2In {
from { opacity:0; transform:translateY(20px); }
to { opacity:1; transform:translateY(0); }
}
/* Form kiri dulu di register */
.rv2-form-panel { flex: 1; max-width: 430px; order: 1; }
.rv2-brand { flex: 0 0 320px; order: 2; }
/* CARD */
.rv2-card {
background: #fff; border-radius: 24px;
padding: 42px 38px;
box-shadow:
0 0 0 1px rgba(111,186,157,.1),
0 4px 6px rgba(15,33,24,.03),
0 20px 44px rgba(15,33,24,.08);
position: relative; overflow: hidden;
}
.rv2-card::before {
content: ''; position:absolute; top:0; left:0; right:0; height:3px;
background: linear-gradient(90deg, #6FBA9D, #A8D8C6, #6FBA9D);
}
.rv2-card::after {
content: ''; position:absolute; bottom:-50px; left:-50px;
width:140px; height:140px; border-radius:50%;
background: radial-gradient(circle, rgba(111,186,157,.06) 0%, transparent 70%);
}
.rv2-card-lbl {
font-size:.67rem; font-weight:700; letter-spacing:2px;
text-transform:uppercase; color:#6FBA9D; margin-bottom:7px;
}
.rv2-card-title {
font-family:'DM Serif Display',serif;
font-size:1.85rem; color:#0F2118; line-height:1.1; margin-bottom:5px;
}
.rv2-card-desc { font-size:.79rem; color:#8AADA0; line-height:1.6; margin-bottom:26px; }
/* Alert */
.rv2-alert {
border-radius:10px; padding:10px 13px; font-size:.79rem;
margin-bottom:18px; background:#FFF3F3; color:#c62828; border-left:3px solid #e53935;
}
.rv2-alert p { display:flex; align-items:center; gap:7px; margin:2px 0; }
/* Fields */
.rv2-field { margin-bottom:15px; }
.rv2-lbl {
display:block; font-size:.7rem; font-weight:700;
letter-spacing:.8px; text-transform:uppercase; color:#2A4235; margin-bottom:7px;
}
.rv2-shell { position:relative; display:flex; align-items:center; }
.rv2-shell .fi { position:absolute; left:15px; color:#A8D8C6; font-size:.8rem; pointer-events:none; transition:color .2s; }
.rv2-shell input {
width:100%; padding:12px 15px 12px 40px;
background:#EBF7F2; border:1.5px solid transparent;
border-radius:11px; font-family:inherit; font-size:.87rem; color:#0F2118; outline:none;
transition:all .2s;
}
.rv2-shell input::placeholder { color:#8AADA0; font-size:.83rem; }
.rv2-shell input:focus {
background:#fff; border-color:#6FBA9D;
box-shadow:0 0 0 4px rgba(111,186,157,.12);
}
.rv2-show {
position:absolute; right:13px;
background:none; border:none; font-size:.68rem; font-weight:800;
letter-spacing:.8px; color:#5EA98C; cursor:pointer; font-family:inherit;
}
.rv2-show:hover { color:#3D8A6E; }
.rv2-ferr { font-size:.72rem; color:#e53935; margin-top:4px; padding-left:3px; }
/* Strength */
.rv2-strength { display:flex; gap:4px; margin-top:7px; }
.rv2-bar { height:3px; flex:1; border-radius:3px; background:#D6EDE5; transition:background .3s; }
.rv2-bar.w { background:#e53935; }
.rv2-bar.m { background:#FB8C00; }
.rv2-bar.s { background:#6FBA9D; }
/* Buttons */
.rv2-btn {
width:100%; padding:13px;
background:linear-gradient(135deg, #6FBA9D, #5EA98C);
color:#fff; border:none; border-radius:12px;
font-family:inherit; font-size:.89rem; font-weight:700;
cursor:pointer; letter-spacing:.3px; margin-top:6px;
display:flex; align-items:center; justify-content:center; gap:8px;
box-shadow:0 4px 18px rgba(94,169,140,.35);
transition:all .25s;
}
.rv2-btn:hover { transform:translateY(-2px); box-shadow:0 8px 26px rgba(94,169,140,.45); }
.rv2-btn:active { transform:none; }
.rv2-foot { text-align:center; font-size:.77rem; color:#8AADA0; margin-top:20px; }
.rv2-foot a { color:#5EA98C; font-weight:700; text-decoration:none; }
.rv2-foot a:hover { text-decoration:underline; }
/* Brand */
.rv2-logo { width:72px; height:72px; margin-bottom:20px; border-radius:16px; box-shadow:0 4px 20px rgba(111,186,157,.2); object-fit:contain; background:#fff; }
.rv2-eyebrow {
display:inline-flex; align-items:center; gap:8px;
font-size:.68rem; font-weight:700; letter-spacing:2px;
text-transform:uppercase; color:#6FBA9D; margin-bottom:18px;
}
.rv2-eyebrow::before {
content:''; display:inline-block; width:22px; height:2px;
background:#6FBA9D; border-radius:2px;
}
.rv2-title {
font-family:'DM Serif Display',serif;
font-size:3.2rem; line-height:1.05; color:#0F2118; margin-bottom:6px;
}
.rv2-title em { font-style:italic; color:#5EA98C; }
.rv2-sub { font-size:.9rem; font-weight:500; color:#8AADA0; margin-bottom:32px; line-height:1.6; }
.rv2-divider { width:44px; height:3px; background:linear-gradient(90deg,#6FBA9D,#A8D8C6); border-radius:3px; margin-bottom:24px; }
.rv2-desc { font-size:.81rem; color:#8AADA0; line-height:1.8; max-width:270px; margin-bottom:32px; }
.rv2-features { display:flex; flex-direction:column; gap:11px; }
.rv2-feat { display:flex; align-items:center; gap:11px; font-size:.79rem; color:#2A4235; font-weight:500; }
.rv2-feat-ico {
width:30px; height:30px; border-radius:8px; background:#EBF7F2;
display:flex; align-items:center; justify-content:center;
color:#3D8A6E; font-size:.73rem; flex-shrink:0;
}
/* Responsive */
@media (max-width: 900px) {
.rv2-layout { gap:48px; padding:32px 36px; }
.rv2-brand { flex:0 0 260px; }
.rv2-title { font-size:2.7rem; }
}
@media (max-width: 720px) {
body.auth-page { align-items:flex-start !important; overflow-y:auto !important; }
.rv2-wrap { align-items:flex-start; min-height:auto; padding:24px 0 40px; }
.rv2-layout { flex-direction:column; padding:0 20px; gap:28px; }
.rv2-form-panel { order:2; max-width:100%; }
.rv2-brand { order:1; flex:none; }
.rv2-title { font-size:2.2rem; }
.rv2-logo { width:56px; height:56px; margin:0 auto 14px; display:block; }
.rv2-features, .rv2-desc, .rv2-divider { display:none; }
.rv2-sub { margin-bottom:0; }
.rv2-card { padding:28px 20px; }
.rv2-ring.r1 { width:260px; height:260px; top:-70px; left:-70px; }
.rv2-ring.r2 { display:none; }
}
@media (max-width: 420px) {
.rv2-title { font-size:1.85rem; }
.rv2-card { padding:24px 16px; border-radius:16px; }
.rv2-card-title { font-size:1.5rem; }
}
@media (min-width: 1280px) {
.rv2-layout { max-width:1160px; padding:40px 80px; }
.rv2-brand { flex:0 0 360px; }
.rv2-title { font-size:3.6rem; }
}
</style>
<div class="rv2-wrap">
<div class="rv2-bg"></div>
<div class="rv2-ring r1"></div>
<div class="rv2-ring r2"></div>
<div class="rv2-ring r3"></div>
<div class="rv2-dot d1"></div>
<div class="rv2-dot d2"></div>
<div class="rv2-dot d3"></div>
<div class="rv2-line l1"></div>
<div class="rv2-line l2"></div>
<div class="rv2-layout">
<!-- Form (kiri) -->
<div class="rv2-form-panel">
<div class="rv2-card">
<div class="rv2-card-lbl">Pendaftaran Admin</div>
<div class="rv2-card-title">Buat Akun Baru</div>
<div class="rv2-card-desc">Isi data berikut untuk mendaftarkan akun admin Anda.</div>
<?php if($errors->any()): ?>
<div class="rv2-alert">
<?php $__currentLoopData = $errors->all(); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $error): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<p><i class="fas fa-circle-exclamation"></i> <?php echo e($error); ?></p>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<?php endif; ?>
<form method="POST" action="<?php echo e(route('admin.register')); ?>">
<?php echo csrf_field(); ?>
<div class="rv2-field">
<label class="rv2-lbl">Email Admin</label>
<div class="rv2-shell">
<i class="fas fa-envelope fi" id="ico-e"></i>
<input type="email" id="email" name="email"
value="<?php echo e(old('email')); ?>"
placeholder="nama@institusi.com"
autocomplete="email" required autofocus
onfocus="document.getElementById('ico-e').style.color='#6FBA9D'"
onblur="document.getElementById('ico-e').style.color=''">
</div>
<?php $__errorArgs = ['email'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?><div class="rv2-ferr"><?php echo e($message); ?></div><?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<div class="rv2-field">
<label class="rv2-lbl">Password</label>
<div class="rv2-shell">
<i class="fas fa-lock fi" id="ico-pw"></i>
<input type="password" id="password" name="password"
placeholder="Buat password yang kuat"
oninput="rv2Str(this.value)" required
onfocus="document.getElementById('ico-pw').style.color='#6FBA9D'"
onblur="document.getElementById('ico-pw').style.color=''">
<button type="button" class="rv2-show" id="rv2TglBtn">SHOW</button>
</div>
<div class="rv2-strength">
<div class="rv2-bar" id="rv2b1"></div>
<div class="rv2-bar" id="rv2b2"></div>
<div class="rv2-bar" id="rv2b3"></div>
<div class="rv2-bar" id="rv2b4"></div>
</div>
<?php $__errorArgs = ['password'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?><div class="rv2-ferr"><?php echo e($message); ?></div><?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<div class="rv2-field" style="margin-bottom:22px;">
<label class="rv2-lbl">Konfirmasi Password</label>
<div class="rv2-shell">
<i class="fas fa-lock-open fi" id="ico-c"></i>
<input type="password" id="password_confirmation"
name="password_confirmation"
placeholder="Ulangi password Anda" required
onfocus="document.getElementById('ico-c').style.color='#6FBA9D'"
onblur="document.getElementById('ico-c').style.color=''">
</div>
</div>
<button type="submit" class="rv2-btn">
<i class="fas fa-user-plus"></i>
Daftarkan Akun Admin
</button>
<div class="rv2-foot">
Sudah punya akun? <a href="<?php echo e(route('admin.login')); ?>">Login di sini</a>
</div>
</form>
</div>
</div>
<!-- Brand (kanan) -->
<div class="rv2-brand">
<img src="<?php echo e(asset('images/logo.png')); ?>" alt="Logo PKPPS" class="rv2-logo">
<div class="rv2-eyebrow">Bergabung Sekarang</div>
<h1 class="rv2-title">Bergabung<br>Bersama<br><em>Kami.</em></h1>
<p class="rv2-sub">PKPPS Riyadlul Jannah</p>
<div class="rv2-divider"></div>
<p class="rv2-desc">Daftarkan akun admin baru dengan aman. Gunakan email dan password kuat untuk menjaga keamanan sistem pesantren.</p>
<div class="rv2-features">
<div class="rv2-feat">
<div class="rv2-feat-ico"><i class="fas fa-envelope"></i></div>
<span>Gunakan email institusi yang valid</span>
</div>
<div class="rv2-feat">
<div class="rv2-feat-ico"><i class="fas fa-key"></i></div>
<span>Password minimal 8 karakter campuran</span>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const btn = document.getElementById('rv2TglBtn');
const pw = document.getElementById('password');
if (btn && pw) {
btn.addEventListener('click', () => {
const isP = pw.type === 'password';
pw.type = isP ? 'text' : 'password';
btn.textContent = isP ? 'HIDE' : 'SHOW';
});
}
});
function rv2Str(v) {
const bars = ['rv2b1','rv2b2','rv2b3','rv2b4'].map(id => document.getElementById(id));
bars.forEach(b => b.className = 'rv2-bar');
let s = 0;
if (v.length >= 6) s++;
if (v.length >= 10) s++;
if (/[A-Z]/.test(v) && /[0-9]/.test(v)) s++;
if (/[^A-Za-z0-9]/.test(v)) s++;
const cls = s <= 1 ? 'w' : s <= 2 ? 'm' : 's';
for (let i = 0; i < s; i++) bars[i].classList.add(cls);
}
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('auth.auth_layout', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/auth/register.blade.php ENDPATH**/ ?>

View File

@ -1,28 +0,0 @@
<?php if($paginator->hasPages()): ?>
<nav>
<ul class="pagination">
<?php if($paginator->onFirstPage()): ?>
<li class="page-item disabled" aria-disabled="true">
<span class="page-link"><?php echo app('translator')->get('pagination.previous'); ?></span>
</li>
<?php else: ?>
<li class="page-item">
<a class="page-link" href="<?php echo e($paginator->previousPageUrl()); ?>" rel="prev"><?php echo app('translator')->get('pagination.previous'); ?></a>
</li>
<?php endif; ?>
<?php if($paginator->hasMorePages()): ?>
<li class="page-item">
<a class="page-link" href="<?php echo e($paginator->nextPageUrl()); ?>" rel="next"><?php echo app('translator')->get('pagination.next'); ?></a>
</li>
<?php else: ?>
<li class="page-item disabled" aria-disabled="true">
<span class="page-link"><?php echo app('translator')->get('pagination.next'); ?></span>
</li>
<?php endif; ?>
</ul>
</nav>
<?php endif; ?>
<?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/vendor/pagination/simple-bootstrap-4.blade.php ENDPATH**/ ?>

View File

@ -0,0 +1,680 @@
<?php $__env->startSection('content'); ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">
<style>
/* ═══════════════════════════════════════════
OVERRIDE SELECT2 agar sesuai tema
═══════════════════════════════════════════ */
.select2-container--default .select2-selection--single {
height: 44px;
border: 1.5px solid #d1d9e0;
border-radius: 10px;
padding: 6px 12px;
display: flex;
align-items: center;
transition: border-color .2s, box-shadow .2s;
background: #fff;
}
.select2-container--default .select2-selection--single:hover {
border-color: var(--primary-color, #6FBA9D);
}
.select2-container--default.select2-container--focus .select2-selection--single,
.select2-container--default.select2-container--open .select2-selection--single {
border-color: var(--primary-color, #6FBA9D);
box-shadow: 0 0 0 3px rgba(111,186,157,.18);
outline: none;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 1.4;
color: #333;
padding: 0;
font-size: .93rem;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #aaa;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 42px;
right: 10px;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: var(--primary-color, #6FBA9D);
}
.select2-dropdown {
border: 1.5px solid var(--primary-color, #6FBA9D);
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0,0,0,.12);
overflow: hidden;
}
.select2-search--dropdown .select2-search__field {
border: 1.5px solid #d1d9e0;
border-radius: 7px;
padding: 7px 10px;
font-size: .88rem;
outline: none;
}
.select2-search--dropdown .select2-search__field:focus {
border-color: var(--primary-color, #6FBA9D);
}
.select2-results__option {
padding: 9px 14px;
font-size: .88rem;
}
/* ═══════════════════════════════════════════
LABEL FIELD
═══════════════════════════════════════════ */
.field-label {
display: block;
font-weight: 600;
font-size: .8rem;
text-transform: uppercase;
letter-spacing: .5px;
color: var(--text-light, #888);
margin-bottom: 8px;
}
.field-label .fi { margin-right: 5px; color: var(--primary-color, #6FBA9D); }
/* ═══════════════════════════════════════════
CARD PILIH JENIS TRANSAKSI
═══════════════════════════════════════════ */
.jenis-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
.jenis-card {
position: relative;
cursor: pointer;
border-radius: 14px;
border: 2.5px solid #e0e7ef;
padding: 18px 16px 16px;
transition: border-color .2s, box-shadow .2s, transform .15s, background .2s;
background: #fff;
user-select: none;
-webkit-user-select: none;
}
.jenis-card:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,.09); }
.jenis-card input[type="radio"] { position:absolute; opacity:0; width:0; height:0; }
.jenis-card .ji-wrap {
width: 46px; height: 46px;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 1.25rem;
margin-bottom: 11px;
transition: background .2s;
}
.jenis-card .ji-title { font-weight: 700; font-size: .93rem; margin-bottom: 4px; transition: color .2s; }
.jenis-card .ji-desc { font-size: .75rem; color: #b0b8c4; line-height: 1.4; }
.jenis-card .ji-check {
position: absolute; top: 12px; right: 12px;
width: 22px; height: 22px; border-radius: 50%;
border: 2px solid #d1d9e0;
display: flex; align-items: center; justify-content: center;
font-size: .68rem; color: transparent;
transition: all .2s;
}
/* Pemasukan aktif */
.jenis-card.pemasukan.active {
border-color: #6FBA9D;
background: linear-gradient(135deg, #f0fdf7 0%, #e6f9f2 100%);
box-shadow: 0 4px 18px rgba(111,186,157,.22);
}
.jenis-card.pemasukan .ji-wrap { background: #edf9f4; color: #6FBA9D; }
.jenis-card.pemasukan.active .ji-wrap { background: rgba(111,186,157,.2); color: #38a169; }
.jenis-card.pemasukan.active .ji-title { color: #38a169; }
.jenis-card.pemasukan.active .ji-check { background: #6FBA9D; border-color: #6FBA9D; color: #fff; }
/* Pengeluaran aktif */
.jenis-card.pengeluaran.active {
border-color: #FF8B94;
background: linear-gradient(135deg, #fff5f6 0%, #ffe9eb 100%);
box-shadow: 0 4px 18px rgba(255,139,148,.22);
}
.jenis-card.pengeluaran .ji-wrap { background: #fff0f1; color: #FF8B94; }
.jenis-card.pengeluaran.active .ji-wrap { background: rgba(255,139,148,.2); color: #e53e3e; }
.jenis-card.pengeluaran.active .ji-title { color: #e53e3e; }
.jenis-card.pengeluaran.active .ji-check { background: #FF8B94; border-color: #FF8B94; color: #fff; }
/* ═══════════════════════════════════════════
BOX PERINGATAN SALDO
═══════════════════════════════════════════ */
#warning-saldo {
display: none;
border-radius: 10px;
border: 1.5px solid #FC8181;
background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
padding: 13px 16px;
margin-top: 10px;
animation: warnIn .22s ease;
}
@keyframes warnIn {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
}
#warning-saldo .wt { font-weight: 700; color: #c53030; font-size: .87rem; margin-bottom: 5px; }
#warning-saldo .wb { font-size: .8rem; color: #742a2a; line-height: 1.55; }
#warning-saldo .ws-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 10px;
}
#warning-saldo .ws-cell {
background: rgba(255,255,255,.75); border-radius: 8px;
padding: 8px 10px; text-align: center;
}
#warning-saldo .ws-cell small { display:block; font-size:.68rem; color:#aaa; margin-bottom:3px; }
#warning-saldo .ws-cell strong { font-size:.86rem; font-weight:700; }
/* ═══════════════════════════════════════════
INPUT NOMINAL
═══════════════════════════════════════════ */
.nominal-wrap { position: relative; display: flex; align-items: center; }
.nominal-prefix {
position: absolute; left: 14px;
font-weight: 700; color: #aaa;
pointer-events: none; font-size: .93rem; z-index: 1;
}
.nominal-wrap input {
padding-left: 44px !important;
font-size: 1rem; font-weight: 600; letter-spacing: .3px;
height: 44px; border-radius: 10px;
border: 1.5px solid #d1d9e0;
transition: border-color .2s, box-shadow .2s;
width: 100%;
}
.nominal-wrap input:focus {
border-color: var(--primary-color, #6FBA9D);
box-shadow: 0 0 0 3px rgba(111,186,157,.18);
outline: none;
}
.nominal-wrap.danger input {
border-color: #FC8181 !important;
box-shadow: 0 0 0 3px rgba(255,139,148,.18) !important;
}
/* form-control radius konsisten */
.form-control { border-radius: 10px !important; border: 1.5px solid #d1d9e0; transition: border-color .2s, box-shadow .2s; }
.form-control:focus {
border-color: var(--primary-color, #6FBA9D) !important;
box-shadow: 0 0 0 3px rgba(111,186,157,.18) !important;
outline: none;
}
textarea.form-control { height: auto; }
/* ═══════════════════════════════════════════
INFO CARD SANTRI
═══════════════════════════════════════════ */
#santri-info {
border-radius: 12px; overflow: hidden;
margin-bottom: 20px;
border: 1.5px solid var(--primary-color, #6FBA9D);
box-shadow: 0 4px 14px rgba(111,186,157,.13);
animation: fadeUp .22s ease;
}
@keyframes fadeUp {
from { opacity:0; transform:translateY(6px); }
to { opacity:1; transform:translateY(0); }
}
.si-header {
background: linear-gradient(90deg, #6FBA9D 0%, #38a169 100%);
padding: 10px 16px;
display: flex; align-items: center; gap: 8px;
color: #fff; font-weight: 700;
font-size: .8rem; text-transform: uppercase; letter-spacing: .45px;
}
.si-body { padding: 14px 16px; background: #f9fdfb; }
.si-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 12px; }
.si-stat {
background: #fff; border-radius: 9px;
padding: 10px 12px; text-align: center;
border: 1px solid #e8f5ef;
}
.si-stat small { display:block; font-size:.68rem; color:#aaa; text-transform:uppercase; letter-spacing:.3px; margin-bottom:4px; }
.si-val { font-size: .93rem; font-weight: 800; }
.form-group { margin-bottom: 20px; }
</style>
<div class="page-header">
<h2><i class="fas fa-plus-circle"></i> Tambah Transaksi Uang Saku</h2>
</div>
<div class="form-container">
<form action="<?php echo e(route('admin.uang-saku.store')); ?>" method="POST" id="transaksiForm">
<?php echo csrf_field(); ?>
<div class="form-group">
<label class="field-label" for="id_santri">
<i class="fas fa-user fi"></i> Pilih Santri
<span style="color:#e53e3e;font-weight:700;">*</span>
</label>
<select name="id_santri" id="id_santri"
class="form-control <?php $__errorArgs = ['id_santri'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>" required>
<option value="">Cari ID atau nama santri...</option>
<?php $__currentLoopData = $santriList; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $santri): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<option value="<?php echo e($santri->id_santri); ?>"
<?php echo e((old('id_santri', request('id_santri')) == $santri->id_santri) ? 'selected' : ''); ?>>
<?php echo e($santri->id_santri); ?> — <?php echo e($santri->nama_lengkap); ?>
</option>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</select>
<?php $__errorArgs = ['id_santri'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div class="invalid-feedback"><?php echo e($message); ?></div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<div id="santri-info" style="display:none;">
<div class="si-header">
<i class="fas fa-id-card"></i>
<span id="info-nama"></span>
</div>
<div class="si-body">
<div class="si-stats">
<div class="si-stat">
<small>Saldo Saat Ini</small>
<div class="si-val" id="info-saldo"></div>
</div>
<div class="si-stat">
<small>Masuk Bln Ini</small>
<div class="si-val" id="info-masuk" style="color:#6FBA9D;"></div>
</div>
<div class="si-stat">
<small>Keluar Bln Ini</small>
<div class="si-val" id="info-keluar" style="color:#FF8B94;"></div>
</div>
</div>
<div id="info-riwayat"></div>
</div>
</div>
<div class="form-group">
<label class="field-label">
<i class="fas fa-exchange-alt fi"></i> Jenis Transaksi
<span style="color:#e53e3e;font-weight:700;">*</span>
</label>
<select name="jenis_transaksi" id="jenis_transaksi" style="display:none;"
class="<?php $__errorArgs = ['jenis_transaksi'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>" required>
<option value=""></option>
<option value="pemasukan" <?php echo e(old('jenis_transaksi') == 'pemasukan' ? 'selected' : ''); ?>>pemasukan</option>
<option value="pengeluaran" <?php echo e(old('jenis_transaksi') == 'pengeluaran' ? 'selected' : ''); ?>>pengeluaran</option>
</select>
<div class="jenis-cards">
<div class="jenis-card pemasukan <?php echo e(old('jenis_transaksi') == 'pemasukan' ? 'active' : ''); ?>"
id="card-pemasukan" onclick="pilihJenis('pemasukan')" role="button" tabindex="0"
onkeydown="if(event.key==='Enter'||event.key===' ')pilihJenis('pemasukan')">
<input type="radio" name="_jenis_ui" value="pemasukan"
<?php echo e(old('jenis_transaksi') == 'pemasukan' ? 'checked' : ''); ?>>
<div class="ji-wrap"><i class="fas fa-arrow-circle-down"></i></div>
<div class="ji-title">Pemasukan</div>
<div class="ji-desc">Santri menerima uang saku dari wali / pesantren</div>
<div class="ji-check"><i class="fas fa-check"></i></div>
</div>
<div class="jenis-card pengeluaran <?php echo e(old('jenis_transaksi') == 'pengeluaran' ? 'active' : ''); ?>"
id="card-pengeluaran" onclick="pilihJenis('pengeluaran')" role="button" tabindex="0"
onkeydown="if(event.key==='Enter'||event.key===' ')pilihJenis('pengeluaran')">
<input type="radio" name="_jenis_ui" value="pengeluaran"
<?php echo e(old('jenis_transaksi') == 'pengeluaran' ? 'checked' : ''); ?>>
<div class="ji-wrap"><i class="fas fa-arrow-circle-up"></i></div>
<div class="ji-title">Pengeluaran</div>
<div class="ji-desc">Santri menggunakan uang saku untuk keperluan</div>
<div class="ji-check"><i class="fas fa-check"></i></div>
</div>
</div>
<?php $__errorArgs = ['jenis_transaksi'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div style="color:#e53e3e;font-size:.82rem;margin-top:6px;">
<i class="fas fa-exclamation-circle"></i> <?php echo e($message); ?>
</div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<div class="form-group">
<label class="field-label" for="nominal_display">
<i class="fas fa-money-bill-wave fi"></i> Nominal (Rp)
<span style="color:#e53e3e;font-weight:700;">*</span>
</label>
<div class="nominal-wrap" id="nominalWrap">
<span class="nominal-prefix">Rp</span>
<input type="text"
id="nominal_display"
class="<?php $__errorArgs = ['nominal'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>"
placeholder="0"
autocomplete="off"
inputmode="numeric"
value="<?php echo e(old('nominal') ? number_format((int)old('nominal'), 0, ',', '.') : ''); ?>">
</div>
<input type="hidden" name="nominal" id="nominal" value="<?php echo e(old('nominal')); ?>">
<?php $__errorArgs = ['nominal'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div style="color:#e53e3e;font-size:.82rem;margin-top:5px;">
<i class="fas fa-exclamation-circle"></i> <?php echo e($message); ?>
</div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
<div id="warning-saldo">
<div class="wt">
<i class="fas fa-exclamation-triangle"></i>
Peringatan: Saldo Tidak Mencukupi!
</div>
<div class="wb">
Nominal pengeluaran yang dimasukkan melebihi saldo santri saat ini.
Sistem akan menolak transaksi ini. Silakan kurangi nominal atau pastikan
data saldo sudah benar.
</div>
<div class="ws-grid">
<div class="ws-cell">
<small>Saldo Tersedia</small>
<strong id="warn-saldo" style="color:#38a169;"></strong>
</div>
<div class="ws-cell">
<small>Nominal Diinput</small>
<strong id="warn-nominal" style="color:#c53030;"></strong>
</div>
<div class="ws-cell">
<small>Kekurangan</small>
<strong id="warn-kurang" style="color:#c53030;"></strong>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="field-label" for="tanggal_transaksi">
<i class="fas fa-calendar fi"></i> Tanggal Transaksi
<span style="color:#e53e3e;font-weight:700;">*</span>
</label>
<input type="date" name="tanggal_transaksi" id="tanggal_transaksi"
class="form-control <?php $__errorArgs = ['tanggal_transaksi'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>"
value="<?php echo e(old('tanggal_transaksi', date('Y-m-d'))); ?>" required>
<?php $__errorArgs = ['tanggal_transaksi'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div class="invalid-feedback"><?php echo e($message); ?></div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<div class="form-group">
<label class="field-label" for="keterangan">
<i class="fas fa-sticky-note fi"></i> Keterangan
<span style="font-weight:400;text-transform:none;letter-spacing:0;color:#bbb;">(opsional)</span>
</label>
<textarea name="keterangan" id="keterangan"
class="form-control <?php $__errorArgs = ['keterangan'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>"
rows="3"
placeholder="Contoh: Uang saku januari"><?php echo e(old('keterangan')); ?></textarea>
<?php $__errorArgs = ['keterangan'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?>
<div class="invalid-feedback"><?php echo e($message); ?></div>
<?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<div class="btn-group">
<button type="submit" class="btn btn-success hover-lift" id="btnSubmit">
<i class="fas fa-save"></i> Simpan Transaksi
</button>
<a href="<?php echo e(route('admin.uang-saku.index')); ?>" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</form>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
// ═══════════════════════════════════════════
// STATE GLOBAL
// ═══════════════════════════════════════════
var saldoSantri = 0; // saldo raw santri terpilih
var jenisAktif = ''; // 'pemasukan' | 'pengeluaran' | ''
var nominalBersih = 0; // nominal tanpa titik
// ═══════════════════════════════════════════
// FORMAT HELPER
// ═══════════════════════════════════════════
function formatRibuan(val) {
var b = String(val).replace(/\D/g, '');
return b ? b.replace(/\B(?=(\d{3})+(?!\d))/g, '.') : '';
}
function stripRibuan(val) { return String(val).replace(/\./g, ''); }
function rpStr(angka) { return 'Rp\u00a0' + formatRibuan(angka); }
// ═══════════════════════════════════════════
// PILIH JENIS (kartu)
// ═══════════════════════════════════════════
function pilihJenis(jenis) {
jenisAktif = jenis;
document.getElementById('jenis_transaksi').value = jenis;
document.getElementById('card-pemasukan').classList.toggle('active', jenis === 'pemasukan');
document.getElementById('card-pengeluaran').classList.toggle('active', jenis === 'pengeluaran');
cekPeringatan();
}
// ═══════════════════════════════════════════
// CEK PERINGATAN SALDO
// ═══════════════════════════════════════════
function cekPeringatan() {
var box = document.getElementById('warning-saldo');
var wrap = document.getElementById('nominalWrap');
var btn = document.getElementById('btnSubmit');
var kurang = nominalBersih - saldoSantri;
var tampil = jenisAktif === 'pengeluaran' && nominalBersih > 0 && kurang > 0;
if (tampil) {
document.getElementById('warn-saldo').textContent = rpStr(saldoSantri);
document.getElementById('warn-nominal').textContent = rpStr(nominalBersih);
document.getElementById('warn-kurang').textContent = rpStr(kurang);
box.style.display = 'block';
wrap.classList.add('danger');
btn.style.opacity = '.6';
btn.title = 'Saldo tidak mencukupi — transaksi akan ditolak!';
} else {
box.style.display = 'none';
wrap.classList.remove('danger');
btn.style.opacity = '1';
btn.title = '';
}
}
// ═══════════════════════════════════════════
// INPUT NOMINAL — auto titik ribuan
// ═══════════════════════════════════════════
var dispEl = document.getElementById('nominal_display');
var hiddenEl = document.getElementById('nominal');
dispEl.addEventListener('input', function () {
var bersih = stripRibuan(this.value);
var diformat = formatRibuan(bersih);
// Jaga posisi kursor agar tidak melompat
var pos = this.selectionStart;
var dotBef = (this.value.substring(0, pos).match(/\./g) || []).length;
this.value = diformat;
var dotAft = (diformat.substring(0, pos).match(/\./g) || []).length;
try { this.setSelectionRange(pos + dotAft - dotBef, pos + dotAft - dotBef); } catch(e) {}
nominalBersih = parseInt(bersih) || 0;
hiddenEl.value = nominalBersih || '';
cekPeringatan();
});
// ═══════════════════════════════════════════
// SUBMIT — konfirmasi ekstra jika saldo kurang
// ═══════════════════════════════════════════
document.getElementById('transaksiForm').addEventListener('submit', function (e) {
hiddenEl.value = stripRibuan(dispEl.value) || '';
if (jenisAktif === 'pengeluaran' && nominalBersih > saldoSantri) {
var lanjut = confirm(
'\u26a0\ufe0f PERINGATAN: Saldo Tidak Mencukupi!\n\n' +
'Saldo santri : ' + rpStr(saldoSantri) + '\n' +
'Nominal input : ' + rpStr(nominalBersih) + '\n' +
'Kekurangan : ' + rpStr(nominalBersih - saldoSantri) + '\n\n' +
'Transaksi ini akan DITOLAK oleh sistem.\n' +
'Yakin tetap ingin melanjutkan?'
);
if (!lanjut) { e.preventDefault(); return false; }
}
});
// ═══════════════════════════════════════════
// SELECT2 + AJAX INFO SANTRI
// ═══════════════════════════════════════════
$(document).ready(function () {
$('#id_santri').select2({
placeholder: 'Cari ID atau nama santri...',
allowClear: true,
width: '100%',
language: {
noResults: function () { return 'Santri tidak ditemukan'; },
searching: function () { return 'Mencari...'; }
}
});
$('#id_santri').on('change', function () {
var infoBox = document.getElementById('santri-info');
var val = this.value;
saldoSantri = 0;
cekPeringatan();
if (!val) { infoBox.style.display = 'none'; return; }
fetch('<?php echo e(url("admin/uang-saku/santri-info")); ?>/' + val)
.then(function (r) { return r.json(); })
.then(function (d) {
saldoSantri = d.saldo_raw;
document.getElementById('info-nama').textContent = d.nama;
var sc = d.saldo_raw >= 100000 ? '#38a169'
: d.saldo_raw >= 20000 ? '#f5a623' : '#e53e3e';
document.getElementById('info-saldo').innerHTML =
'<span style="color:' + sc + '">Rp\u00a0' + d.saldo_terakhir + '</span>';
document.getElementById('info-masuk').textContent = 'Rp\u00a0' + d.total_pemasukan_bulan_ini;
document.getElementById('info-keluar').textContent = 'Rp\u00a0' + d.total_pengeluaran_bulan_ini;
var html = '';
if (d.transaksi_terakhir.length > 0) {
html = '<div style="font-size:.7rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:7px;">3 Transaksi Terakhir</div>';
html += '<table class="data-table" style="font-size:.81rem;"><thead><tr>';
html += '<th>Tanggal</th><th>Jenis</th><th>Nominal</th><th>Keterangan</th>';
html += '</tr></thead><tbody>';
d.transaksi_terakhir.forEach(function (t) {
var badge = t.jenis === 'pemasukan'
? '<span class="badge badge-success">Masuk</span>'
: '<span class="badge badge-danger">Keluar</span>';
html += '<tr><td>' + t.tanggal + '</td><td>' + badge +
'</td><td>Rp\u00a0' + t.nominal + '</td><td>' + t.keterangan + '</td></tr>';
});
html += '</tbody></table>';
} else {
html = '<p style="font-size:.8rem;color:#aaa;margin:0;">Belum ada riwayat transaksi.</p>';
}
document.getElementById('info-riwayat').innerHTML = html;
infoBox.style.display = 'block';
cekPeringatan();
})
.catch(function () {
document.getElementById('santri-info').style.display = 'none';
saldoSantri = 0;
});
});
// Trigger jika sudah ada pilihan (dari ?id_santri=xxx atau old())
if ($('#id_santri').val()) $('#id_santri').trigger('change');
// Restore jenis dari old() setelah validasi server gagal
var oldJenis = '<?php echo e(old("jenis_transaksi", "")); ?>';
if (oldJenis) pilihJenis(oldJenis);
});
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/uang-saku/create.blade.php ENDPATH**/ ?>

View File

@ -1,258 +0,0 @@
<?php
$total = ($spp['lunas'] ?? 0) + ($spp['belum'] ?? 0);
$persenLunas = $total > 0 ? round(($spp['lunas'] / $total) * 100) : 0;
$terkumpul = (float) ($spp['terkumpul'] ?? 0);
$totalTagihan = (float) ($spp['totalTagihan'] ?? 0);
$persenNominal = $totalTagihan > 0 ? min(100, round($terkumpul / $totalTagihan * 100)) : 0;
$pemasukanLain = (float) ($spp['pemasukanLain'] ?? 0);
$pengeluaran = (float) ($spp['pengeluaran'] ?? 0);
$totalPemasukan = $terkumpul + $pemasukanLain;
$sisaKas = $totalPemasukan - $pengeluaran;
$kasMax = max($totalPemasukan, $pengeluaran, 1);
$pBarMasuk = min(100, round($totalPemasukan / $kasMax * 100));
$pBarKeluar = min(100, round($pengeluaran / $kasMax * 100));
?>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
<span style="display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;
background:linear-gradient(135deg,var(--primary-color),var(--secondary-color));border-radius:6px;flex-shrink:0;">
<i class="fas fa-wallet" style="font-size:.7rem;color:#fff;"></i>
</span>
<span style="font-size:.88rem;font-weight:700;color:var(--text-color);">Keuangan Bulan Ini</span>
<a href="<?php echo e(route('admin.keuangan.laporan', ['bulan'=>date('n'),'tahun'=>date('Y')])); ?>"
style="margin-left:auto;font-size:.72rem;color:var(--primary-color);font-weight:600;text-decoration:none;display:flex;align-items:center;gap:4px;">
Lihat Neraca <i class="fas fa-arrow-right" style="font-size:.6rem;"></i>
</a>
</div>
<div class="dash-fin-grid">
<div class="content-box" style="display:flex;flex-direction:column;gap:0;">
<h4 style="margin:0 0 14px;font-size:.8rem;font-weight:700;color:var(--primary-dark);
display:flex;align-items:center;gap:6px;">
<i class="fas fa-money-check-alt" style="color:var(--primary-color);"></i>
Status Pembayaran SPP
</h4>
<div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:14px;">
<div style="position:relative;width:110px;height:110px;flex-shrink:0;">
<canvas id="sppRingChart"></canvas>
<div style="position:absolute;inset:0;display:flex;flex-direction:column;
align-items:center;justify-content:center;pointer-events:none;">
<span style="font-size:1.5rem;font-weight:800;color:var(--text-color);line-height:1;"><?php echo e($persenLunas); ?>%</span>
<span style="font-size:.6rem;color:var(--text-light);font-weight:500;">lunas</span>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:8px;flex:1;min-width:100px;">
<div style="display:flex;align-items:center;gap:8px;">
<span style="width:10px;height:10px;border-radius:50%;background:var(--primary-color);flex-shrink:0;display:inline-block;"></span>
<span style="font-size:.72rem;color:var(--text-light);flex:1;">Lunas</span>
<strong style="font-size:.82rem;color:var(--primary-color);"><?php echo e($spp['lunas'] ?? 0); ?></strong>
</div>
<div style="display:flex;align-items:center;gap:8px;">
<span style="width:10px;height:10px;border-radius:50%;background:var(--danger-color);flex-shrink:0;display:inline-block;"></span>
<span style="font-size:.72rem;color:var(--text-light);flex:1;">Belum Lunas</span>
<strong style="font-size:.82rem;color:var(--danger-color);"><?php echo e($spp['belum'] ?? 0); ?></strong>
</div>
<div style="height:1px;background:var(--primary-light);margin:2px 0;"></div>
<div style="display:flex;justify-content:space-between;align-items:center;">
<span style="font-size:.68rem;color:var(--text-light);">Terkumpul</span>
<span style="font-size:.72rem;font-weight:700;color:var(--text-color);">Rp <?php echo e(number_format($terkumpul/1000000,1)); ?>jt</span>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;">
<span style="font-size:.68rem;color:var(--text-light);">Target</span>
<span style="font-size:.72rem;font-weight:600;color:var(--text-light);">Rp <?php echo e(number_format($totalTagihan/1000000,1)); ?>jt</span>
</div>
</div>
</div>
<div style="margin-bottom:14px;">
<div style="display:flex;justify-content:space-between;font-size:.68rem;color:var(--text-light);margin-bottom:4px;">
<span>Nominal terkumpul</span>
<span style="font-weight:700;color:var(--text-color);"><?php echo e($persenNominal); ?>%</span>
</div>
<div class="progress-bar-wrap">
<div class="progress-bar-fill" style="width:<?php echo e($persenNominal); ?>%;
background:linear-gradient(90deg,var(--primary-color),var(--primary-dark));"></div>
</div>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:auto;">
<a href="<?php echo e(route('admin.pembayaran-spp.index', ['tab'=>'belum-bayar','bulan'=>date('n'),'tahun'=>date('Y')])); ?>"
class="btn btn-danger btn-sm" style="flex:1 1 0;min-width:0;justify-content:center;">
<i class="fas fa-exclamation-circle"></i> Belum (<?php echo e($spp['belum'] ?? 0); ?>)
</a>
<a href="<?php echo e(route('admin.pembayaran-spp.generate')); ?>"
class="btn btn-warning btn-sm" style="flex:1 1 0;min-width:0;justify-content:center;">
<i class="fas fa-cogs"></i> Generate
</a>
<a href="<?php echo e(route('admin.pembayaran-spp.index', ['tab'=>'sudah-bayar','bulan'=>date('n'),'tahun'=>date('Y')])); ?>"
class="btn btn-success btn-sm" style="flex:1 1 0;min-width:0;justify-content:center;">
<i class="fas fa-check-circle"></i> Lunas (<?php echo e($spp['lunas'] ?? 0); ?>)
</a>
</div>
</div>
<div class="content-box" style="display:flex;flex-direction:column;gap:0;">
<h4 style="margin:0 0 14px;font-size:.8rem;font-weight:700;color:var(--primary-dark);
display:flex;align-items:center;gap:6px;">
<i class="fas fa-landmark" style="color:var(--info-color);"></i>
Neraca Kas Pondok
</h4>
<div style="display:flex;flex-direction:column;gap:14px;margin-bottom:14px;">
<div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:5px;">
<span style="font-size:.72rem;color:var(--text-light);font-weight:600;display:flex;align-items:center;gap:6px;">
<span style="width:10px;height:10px;border-radius:50%;background:var(--success-color);display:inline-block;flex-shrink:0;"></span>
SPP + Pemasukan
</span>
<strong style="font-size:.78rem;color:var(--success-color);">
Rp <?php echo e(number_format($totalPemasukan,0,',','.')); ?>
</strong>
</div>
<div class="progress-bar-wrap" style="height:10px;">
<div class="progress-bar-fill" style="width:<?php echo e($pBarMasuk); ?>%;height:100%;
background:linear-gradient(90deg,var(--primary-color),#38ef7d);"></div>
</div>
</div>
<div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:5px;">
<span style="font-size:.72rem;color:var(--text-light);font-weight:600;display:flex;align-items:center;gap:6px;">
<span style="width:10px;height:10px;border-radius:50%;background:var(--danger-color);display:inline-block;flex-shrink:0;"></span>
Pengeluaran
</span>
<strong style="font-size:.78rem;color:var(--danger-color);">
Rp <?php echo e(number_format($pengeluaran,0,',','.')); ?>
</strong>
</div>
<div class="progress-bar-wrap" style="height:10px;">
<div class="progress-bar-fill" style="width:<?php echo e($pBarKeluar); ?>%;height:100%;
background:linear-gradient(90deg,var(--danger-color),#FF6B7A);"></div>
</div>
</div>
<?php if($pemasukanLain > 0): ?>
<div style="opacity:.8;">
<?php $pBarSpp = min(100, round($terkumpul / $kasMax * 100)); ?>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:5px;">
<span style="font-size:.68rem;color:var(--text-light);display:flex;align-items:center;gap:6px;">
<span style="width:8px;height:8px;border-radius:50%;background:var(--info-color);display:inline-block;flex-shrink:0;"></span>
dari SPP saja
</span>
<span style="font-size:.7rem;color:var(--info-color);font-weight:600;">
Rp <?php echo e(number_format($terkumpul,0,',','.')); ?>
</span>
</div>
<div class="progress-bar-wrap" style="height:7px;">
<div class="progress-bar-fill" style="width:<?php echo e($pBarSpp); ?>%;height:100%;
background:linear-gradient(90deg,var(--info-color),#5FAFE0);"></div>
</div>
</div>
<?php endif; ?>
</div>
<div style="padding:11px 14px;border-radius:var(--border-radius-sm);margin-bottom:14px;
background:<?php echo e($sisaKas >= 0 ? 'linear-gradient(135deg,#E8F7F2,#D4F1E3)' : 'linear-gradient(135deg,#FFE8EA,#FFD5D8)'); ?>;
border-left:4px solid <?php echo e($sisaKas >= 0 ? 'var(--success-color)' : 'var(--danger-color)'); ?>;
display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:6px;">
<span style="font-size:.75rem;font-weight:700;color:var(--text-color);display:flex;align-items:center;gap:6px;">
<i class="fas fa-<?php echo e($sisaKas >= 0 ? 'piggy-bank' : 'exclamation-triangle'); ?>"
style="color:<?php echo e($sisaKas >= 0 ? 'var(--success-color)' : 'var(--danger-color)'); ?>;"></i>
Sisa Kas Bulan Ini
</span>
<strong style="font-size:1rem;font-weight:800;color:<?php echo e($sisaKas >= 0 ? 'var(--success-color)' : 'var(--danger-color)'); ?>;word-break:break-all;">
<?php echo e($sisaKas >= 0 ? '+' : ''); ?>Rp <?php echo e(number_format($sisaKas,0,',','.')); ?>
</strong>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:auto;">
<a href="<?php echo e(route('admin.keuangan.index')); ?>"
class="btn btn-info btn-sm" style="flex:1 1 0;min-width:0;justify-content:center;">
<i class="fas fa-book-open"></i> Buku Kas
</a>
<a href="<?php echo e(route('admin.keuangan.laporan', ['bulan'=>date('n'),'tahun'=>date('Y')])); ?>"
class="btn btn-warning btn-sm" style="flex:1 1 0;min-width:0;justify-content:center;">
<i class="fas fa-chart-bar"></i> Laporan
</a>
</div>
</div>
</div>
<script>
(function () {
var SPP_LUNAS = <?php echo e((int)($spp['lunas'] ?? 0)); ?>;
var SPP_BELUM = <?php echo e((int)($spp['belum'] ?? 0)); ?>;
function initRing() {
if (typeof Chart === 'undefined') { setTimeout(initRing, 50); return; }
var el = document.getElementById('sppRingChart');
if (!el) return;
if (el._ci) { el._ci.destroy(); }
var allZero = (SPP_LUNAS === 0 && SPP_BELUM === 0);
el._ci = new Chart(el, {
type: 'doughnut',
data: {
labels : allZero ? ['Belum ada data'] : ['Lunas', 'Belum Lunas'],
datasets: [{
data : allZero ? [1] : [SPP_LUNAS, SPP_BELUM],
backgroundColor: allZero ? ['#E8F7F2'] : ['#6FBA9D', '#FF8B94'],
borderWidth : allZero ? 0 : 3,
borderColor : '#fff',
hoverOffset : allZero ? 0 : 6
}]
},
options: {
responsive: true, maintainAspectRatio: false,
cutout: '70%',
plugins: {
legend: { display: false },
tooltip: {
enabled: !allZero,
callbacks: {
label: function (ctx) {
var total = ctx.dataset.data.reduce(function(a,b){return a+b;},0);
var pct = total > 0 ? Math.round(ctx.parsed/total*100) : 0;
return ' '+ctx.label+': '+ctx.formattedValue+' ('+pct+'%)';
}
}
}
}
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initRing);
} else { initRing(); }
})();
</script><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/dashboard/_ringkasan-spp.blade.php ENDPATH**/ ?>

View File

@ -0,0 +1,512 @@

<?php $__env->startSection('content'); ?>
<div class="page-header">
<h2><i class="fas fa-history"></i> Riwayat Uang Saku &mdash; <?php echo e($santri->nama_lengkap); ?></h2>
</div>
<div class="content-box" style="margin-bottom:14px;">
<form method="GET" action="<?php echo e(route('admin.uang-saku.riwayat', $santri->id_santri)); ?>" id="filterPeriode">
<div style="display:flex;align-items:flex-end;gap:11px;flex-wrap:wrap;">
<div class="form-group" style="margin-bottom:0;flex:1;min-width:200px;">
<label for="tanggal_dari" style="display:block;margin-bottom:5px;font-weight:600;">
<i class="fas fa-calendar-alt"></i> Dari Tanggal
</label>
<input type="date" name="tanggal_dari" id="tanggal_dari"
class="form-control" value="<?php echo e($tanggalDari); ?>" max="<?php echo e(date('Y-m-d')); ?>">
</div>
<div class="form-group" style="margin-bottom:0;flex:1;min-width:200px;">
<label for="tanggal_sampai" style="display:block;margin-bottom:5px;font-weight:600;">
<i class="fas fa-calendar-check"></i> Sampai Tanggal
</label>
<input type="date" name="tanggal_sampai" id="tanggal_sampai"
class="form-control" value="<?php echo e($tanggalSampai); ?>" max="<?php echo e(date('Y-m-d')); ?>">
</div>
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary">
<i class="fas fa-filter"></i> Terapkan
</button>
<button type="button" class="btn btn-success" onclick="setBulanIni()">
<i class="fas fa-calendar-day"></i> Bulan Ini
</button>
<a href="<?php echo e(route('admin.uang-saku.riwayat', $santri->id_santri)); ?>" class="btn btn-secondary">
<i class="fas fa-redo"></i> Reset
</a>
</div>
</div>
</form>
</div>
<div style="margin-bottom:10px;">
<div style="font-size:.78rem;font-weight:600;color:var(--text-light);
text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;">
<i class="fas fa-calculator" style="color:var(--primary-color);"></i>
Ringkasan Periode:
<strong style="color:var(--text-color);">
<?php echo e($periodeDari->format('d M Y')); ?> <?php echo e($periodeSampai->format('d M Y')); ?>
</strong>
</div>
<div style="display:grid;
grid-template-columns: 1fr 32px 1fr 32px 1fr 32px 1fr;
gap:0;align-items:stretch;
border-radius:12px;overflow:hidden;
border:1.5px solid #e2e8f0;background:#fff;
margin-bottom:10px;">
<div style="padding:14px 16px;border-right:1px solid #e2e8f0;">
<div style="font-size:.66rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:5px;">
Saldo Awal Periode
</div>
<div style="font-size:1rem;font-weight:800;color:#4a5568;">
Rp <?php echo e(number_format($saldoAwalPeriode, 0, ',', '.')); ?>
</div>
<div style="font-size:.66rem;color:#bbb;margin-top:3px;">
per <?php echo e($periodeDari->format('d M Y')); ?>
<?php if($saldoAwalPeriode == 0): ?>
<span style="color:#f5a623;">(belum ada saldo sebelumnya)</span>
<?php endif; ?>
</div>
</div>
<div style="display:flex;align-items:center;justify-content:center;
font-size:1.3rem;font-weight:300;color:#6FBA9D;
border-right:1px solid #e2e8f0;">+</div>
<div style="padding:14px 16px;border-right:1px solid #e2e8f0;">
<div style="font-size:.66rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:5px;">
Pemasukan Periode
</div>
<div style="font-size:1rem;font-weight:800;color:#6FBA9D;">
Rp <?php echo e(number_format($totalPemasukan, 0, ',', '.')); ?>
</div>
<div style="font-size:.66rem;color:#bbb;margin-top:3px;">uang masuk di periode ini</div>
</div>
<div style="display:flex;align-items:center;justify-content:center;
font-size:1.3rem;font-weight:300;color:#FF8B94;
border-right:1px solid #e2e8f0;">&minus;</div>
<div style="padding:14px 16px;border-right:1px solid #e2e8f0;">
<div style="font-size:.66rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:5px;">
Pengeluaran Periode
</div>
<div style="font-size:1rem;font-weight:800;color:#FF8B94;">
Rp <?php echo e(number_format($totalPengeluaran, 0, ',', '.')); ?>
</div>
<div style="font-size:.66rem;color:#bbb;margin-top:3px;">uang keluar di periode ini</div>
</div>
<div style="display:flex;align-items:center;justify-content:center;
font-size:1.3rem;font-weight:300;color:#718096;
border-right:1px solid #e2e8f0;">=</div>
<div style="padding:14px 16px;background:linear-gradient(135deg,#f0fdf7 0%,#e6f9f2 100%);">
<div style="font-size:.66rem;color:#aaa;text-transform:uppercase;letter-spacing:.4px;margin-bottom:5px;">
Saldo Akhir Periode
</div>
<div style="font-size:1rem;font-weight:800;
color:<?php echo e($saldoAkhirPeriode >= 0 ? '#38a169' : '#e53e3e'); ?>;">
Rp <?php echo e(number_format($saldoAkhirPeriode, 0, ',', '.')); ?>
</div>
<div style="font-size:.66rem;color:#bbb;margin-top:3px;">
per <?php echo e($periodeSampai->format('d M Y')); ?>
</div>
</div>
</div>
<?php if(round($saldoAkhirPeriode) !== round($saldoTerakhir)): ?>
<div style="background:#fffbeb;border:1px solid #fbd38d;border-radius:9px;
padding:10px 14px;font-size:.8rem;color:#744210;margin-bottom:10px;
display:flex;align-items:flex-start;gap:8px;">
<i class="fas fa-exclamation-triangle" style="color:#f5a623;margin-top:2px;flex-shrink:0;"></i>
<div>
<strong>Catatan:</strong>
Saldo akhir periode (Rp <?php echo e(number_format($saldoAkhirPeriode, 0, ',', '.')); ?>)
berbeda dengan <strong>Saldo Saat Ini (Rp <?php echo e(number_format($saldoTerakhir, 0, ',', '.')); ?>)</strong>
karena ada transaksi di luar rentang
<?php echo e($periodeDari->format('d M')); ?><?php echo e($periodeSampai->format('d M Y')); ?>.
</div>
</div>
<?php endif; ?>
<div style="background:linear-gradient(135deg,#ebf8ff 0%,#bee3f8 40%,#ebf8ff 100%);
border:1.5px solid #90cdf4;border-radius:12px;
padding:14px 18px;display:flex;justify-content:space-between;
align-items:center;flex-wrap:wrap;gap:10px;">
<div>
<div style="font-size:.66rem;color:#2b6cb0;text-transform:uppercase;
letter-spacing:.5px;margin-bottom:4px;font-weight:600;">
<i class="fas fa-wallet"></i> Saldo Saat Ini (Akumulasi Semua Waktu)
</div>
<div style="font-size:1.4rem;font-weight:900;
color:<?php echo e($saldoTerakhir >= 0 ? '#2b6cb0' : '#e53e3e'); ?>;">
Rp <?php echo e(number_format($saldoTerakhir, 0, ',', '.')); ?>
</div>
</div>
<div style="font-size:.78rem;color:#2c5282;text-align:right;max-width:300px;line-height:1.5;">
<i class="fas fa-info-circle"></i>
Total uang santri saat ini, dihitung dari <strong>seluruh transaksi sejak awal</strong>,
bukan hanya periode yang ditampilkan.
</div>
</div>
</div>
<div class="content-box" style="margin-bottom:22px;">
<div style="display:flex;justify-content:space-between;align-items:flex-start;
flex-wrap:wrap;gap:8px;margin-bottom:16px;">
<div>
<h3 style="margin:0 0 4px;color:var(--primary-color);">
<i class="fas fa-chart-area"></i> Perjalanan Saldo
</h3>
<div style="font-size:.78rem;color:var(--text-light);">
<?php echo e($periodeDari->format('d M Y')); ?> <?php echo e($periodeSampai->format('d M Y')); ?>
&bull; Grafik menunjukkan nilai saldo aktual setiap hari
</div>
</div>
<div style="display:flex;gap:14px;align-items:center;font-size:.78rem;color:var(--text-light);">
<span>
<span style="display:inline-block;width:24px;height:3px;background:#6FBA9D;
vertical-align:middle;border-radius:2px;margin-right:5px;"></span>
Saldo naik (pemasukan)
</span>
<span>
<span style="display:inline-block;width:24px;height:3px;background:#FF8B94;
vertical-align:middle;border-radius:2px;margin-right:5px;"></span>
Saldo turun (pengeluaran)
</span>
</div>
</div>
<canvas id="chartSaldo" style="max-height:380px;"></canvas>
</div>
<div class="content-box" style="margin-bottom:14px;">
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:11px;">
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<a href="<?php echo e(route('admin.uang-saku.index')); ?>" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
<a href="<?php echo e(route('admin.santri.show', $santri->id)); ?>" class="btn btn-primary">
<i class="fas fa-user"></i> Profil Santri
</a>
</div>
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.create')); ?>?id_santri=<?php echo e($santri->id_santri); ?>"
class="btn btn-success">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
<?php endif; ?>
</div>
</div>
<div class="content-box">
<h3 style="margin-bottom:12px;color:var(--text-color);">
<i class="fas fa-list"></i> Daftar Transaksi
<?php if($transaksi->total() > 0): ?>
<span style="color:var(--text-light);font-weight:400;">
(<?php echo e($transaksi->total()); ?> transaksi)
</span>
<?php endif; ?>
</h3>
<?php if($transaksi->count() > 0): ?>
<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;
padding:9px 14px;margin-bottom:12px;font-size:.78rem;color:var(--text-light);">
<i class="fas fa-info-circle" style="color:var(--primary-color);"></i>
<strong>Saldo Sebelum</strong> &amp; <strong>Saldo Sesudah</strong>
menunjukkan saldo kumulatif santri sebelum dan setelah tiap transaksi.
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th style="width:4%;">No</th>
<th style="width:11%;">ID</th>
<th style="width:10%;">Tanggal</th>
<th style="width:10%;">Jenis</th>
<th style="width:13%;">Nominal</th>
<th style="width:13%;">Saldo Sebelum</th>
<th style="width:13%;">Saldo Sesudah</th>
<th style="width:20%;">Keterangan</th>
<th style="width:6%;" class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
<?php $__currentLoopData = $transaksi; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $index => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<tr>
<td><?php echo e($transaksi->firstItem() + $index); ?></td>
<td><strong><?php echo e($item->id_uang_saku); ?></strong></td>
<td><?php echo e($item->tanggal_transaksi->format('d/m/Y')); ?></td>
<td>
<?php if($item->jenis_transaksi === 'pemasukan'): ?>
<span class="badge badge-success">
<i class="fas fa-arrow-down"></i> Masuk
</span>
<?php else: ?>
<span class="badge badge-danger">
<i class="fas fa-arrow-up"></i> Keluar
</span>
<?php endif; ?>
</td>
<td class="nominal-highlight"><?php echo e($item->nominal_format); ?></td>
<td style="color:#718096;">
Rp <?php echo e(number_format($item->saldo_sebelum, 0, ',', '.')); ?>
</td>
<td>
<strong style="color:<?php echo e($item->saldo_sesudah >= 0 ? '#38a169' : '#e53e3e'); ?>;">
<?php echo e($item->saldo_sesudah_format); ?>
</strong>
</td>
<td><div class="content-preview"><?php echo e($item->keterangan ?? '-'); ?></div></td>
<td class="text-center">
<div style="display:flex;gap:4px;justify-content:center;">
<a href="<?php echo e(route('admin.uang-saku.show', $item->id)); ?>"
class="btn btn-primary btn-sm" title="Detail">
<i class="fas fa-eye"></i>
</a>
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.edit', $item->id)); ?>"
class="btn btn-warning btn-sm" title="Edit">
<i class="fas fa-edit"></i>
</a>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</tbody>
</table>
</div>
<div style="margin-top:14px;"><?php echo e($transaksi->links()); ?></div>
<?php else: ?>
<div class="empty-state">
<i class="fas fa-calendar-times"></i>
<h3>Tidak Ada Transaksi</h3>
<p>
Tidak ada transaksi pada periode
<?php echo e($periodeDari->format('d F Y')); ?> &ndash; <?php echo e($periodeSampai->format('d F Y')); ?>.
</p>
<?php if(round($saldoTerakhir) > 0): ?>
<p style="font-size:.85rem;color:var(--text-light);">
Santri memiliki saldo
<strong>Rp <?php echo e(number_format($saldoTerakhir, 0, ',', '.')); ?></strong>
dari transaksi di periode lain.
</p>
<?php endif; ?>
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.create')); ?>?id_santri=<?php echo e($santri->id_santri); ?>"
class="btn btn-success">
<i class="fas fa-plus"></i> Tambah Transaksi
</a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
// ═══════════════════════════════════════════════════════════════
// DATA dari Laravel
// ═══════════════════════════════════════════════════════════════
const rawData = <?php echo json_encode($dataGrafikSaldo, 15, 512) ?>;
// ═══════════════════════════════════════════════════════════════
// Bangun dataset perjalanan saldo
// ═══════════════════════════════════════════════════════════════
const labels = [];
const saldoArr = [];
rawData.forEach((d, i) => {
const dt = new Date(d.tanggal + 'T00:00:00');
const opt = { day: 'numeric', month: 'short' };
labels.push(
d.is_awal
? dt.toLocaleDateString('id-ID', opt) + ' (Awal)'
: dt.toLocaleDateString('id-ID', opt)
);
saldoArr.push(d.saldo);
});
function segmentColor(ctx, naik, turun, flat) {
const i = ctx.p1DataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
if (cur > prv) return naik;
if (cur < prv) return turun;
return flat;
}
function buildGradient(ctx) {
const chart = ctx.chart;
const { chartArea } = chart;
if (!chartArea) return 'rgba(111,186,157,0.15)';
const gradient = chart.ctx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);
gradient.addColorStop(0, 'rgba(111,186,157,0.25)');
gradient.addColorStop(0.6, 'rgba(111,186,157,0.06)');
gradient.addColorStop(1, 'rgba(111,186,157,0.0)');
return gradient;
}
const ctx = document.getElementById('chartSaldo').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels,
datasets: [{
label: 'Saldo',
data: saldoArr,
borderWidth: 3,
tension: 0.35,
fill: true,
backgroundColor: buildGradient,
pointRadius: function(ctx) {
const i = ctx.dataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
return cur !== prv || i === 0 || i === saldoArr.length - 1 ? 6 : 3;
},
pointHoverRadius: 8,
pointBackgroundColor: function(ctx) {
const i = ctx.dataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
if (i === 0) return '#718096';
if (cur > prv) return '#6FBA9D';
if (cur < prv) return '#FF8B94';
return '#d1d9e0';
},
pointBorderColor: '#fff',
pointBorderWidth: 2,
segment: {
borderColor: ctx => segmentColor(ctx, '#6FBA9D', '#FF8B94', '#d1d9e0'),
},
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
interaction: { intersect: false, mode: 'index' },
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(30,41,59,0.92)',
padding: 13,
cornerRadius: 10,
titleFont: { size: 13, weight: 'bold' },
bodyFont: { size: 13 },
callbacks: {
title: items => items[0].label,
label: item => {
const i = item.dataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
const diff = cur - prv;
const fmt = v => 'Rp\u00a0' + new Intl.NumberFormat('id-ID').format(v);
let lines = [' Saldo: ' + fmt(cur)];
if (i > 0 && diff !== 0) {
const sign = diff > 0 ? '▲ +' : '▼ ';
lines.push(' ' + sign + fmt(Math.abs(diff))
+ (diff > 0 ? ' (pemasukan)' : ' (pengeluaran)'));
} else if (i > 0) {
lines.push(' — tidak ada transaksi');
}
return lines;
},
labelColor: item => {
const i = item.dataIndex;
const cur = saldoArr[i];
const prv = saldoArr[i - 1] ?? cur;
const c = i === 0 ? '#718096'
: cur > prv ? '#6FBA9D'
: cur < prv ? '#FF8B94' : '#d1d9e0';
return { borderColor: c, backgroundColor: c, borderRadius: 3 };
}
}
}
},
scales: {
y: {
min: function() {
const minVal = Math.min(...saldoArr);
return Math.max(0, Math.floor(minVal * 0.8 / 10000) * 10000);
}(),
ticks: {
callback: v => 'Rp\u00a0' + new Intl.NumberFormat('id-ID', {
notation: 'compact', compactDisplay: 'short'
}).format(v),
font: { size: 12 },
maxTicksLimit: 8,
},
grid: { color: 'rgba(0,0,0,.05)', drawBorder: false }
},
x: {
grid: { display: false, drawBorder: false },
ticks: {
font: { size: 11 },
maxRotation: 45,
minRotation: 45,
maxTicksLimit: rawData.length > 20 ? 12 : rawData.length,
autoSkip: true,
}
}
},
animation: { duration: 1200, easing: 'easeInOutQuart' }
}
});
// ═══════════════════════════════════════════════════════════════
// Helpers
// ═══════════════════════════════════════════════════════════════
function setBulanIni() {
const today = new Date();
const first = new Date(today.getFullYear(), today.getMonth(), 1);
const last = new Date(today.getFullYear(), today.getMonth() + 1, 0);
document.getElementById('tanggal_dari').value = first.toISOString().split('T')[0];
document.getElementById('tanggal_sampai').value = last.toISOString().split('T')[0];
document.getElementById('filterPeriode').submit();
}
document.getElementById('tanggal_sampai').addEventListener('change', function () {
const dari = document.getElementById('tanggal_dari').value;
if (dari && this.value && this.value < dari) {
alert('Tanggal sampai tidak boleh lebih kecil dari tanggal dari!');
this.value = dari;
}
});
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/uang-saku/riwayat.blade.php ENDPATH**/ ?>

View File

@ -1,387 +0,0 @@
<?php $__env->startSection('title', 'Login Santri'); ?>
<?php $__env->startSection('auth-content'); ?>
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:opsz,wght@9..40,300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body.auth-page {
background: #F8FDFB !important;
font-family: 'DM Sans', sans-serif !important;
padding: 0 !important;
align-items: stretch !important;
min-height: 100vh !important;
}
.auth-container {
width: 100vw !important; max-width: 100vw !important;
min-height: 100vh !important; background: transparent !important;
padding: 0 !important; border-radius: 0 !important; box-shadow: none !important;
}
/* ── Wrapper & Background ── */
.sl-wrap {
position: relative; width: 100%; min-height: 100vh;
display: flex; align-items: center; justify-content: center;
overflow: hidden; font-family: 'DM Sans', sans-serif;
}
.sl-bg {
position: absolute; inset: 0; z-index: 0;
background:
radial-gradient(ellipse 80% 60% at 70% 50%, rgba(111,186,157,.12) 0%, transparent 60%),
radial-gradient(ellipse 50% 80% at 10% 80%, rgba(111,186,157,.08) 0%, transparent 55%),
#F8FDFB;
}
.sl-bg::before {
content: ''; position: absolute; inset: 0;
background-image:
linear-gradient(rgba(111,186,157,.055) 1px, transparent 1px),
linear-gradient(90deg, rgba(111,186,157,.055) 1px, transparent 1px);
background-size: 48px 48px;
}
/* Decorations */
.sl-ring { position: absolute; border-radius: 50%; border: 1.5px solid rgba(111,186,157,.18); pointer-events: none; }
.sl-ring.r1 { width:420px; height:420px; top:-110px; right:-110px; }
.sl-ring.r2 { width:270px; height:270px; bottom:50px; right:60px; border-color:rgba(111,186,157,.10); }
.sl-ring.r3 { width:200px; height:200px; bottom:-50px; left:60px; border-color:rgba(111,186,157,.14); }
.sl-dot { position:absolute; border-radius:50%; background:#6FBA9D; pointer-events:none; }
.sl-dot.d1 { width:8px; height:8px; top:22%; right:18%; opacity:.14; }
.sl-dot.d2 { width:5px; height:5px; bottom:40%; right:30%; opacity:.09; }
.sl-dot.d3 { width:11px; height:11px; top:55%; left:15%; opacity:.08; }
.sl-line { position:absolute; height:1px; background:linear-gradient(90deg,transparent,rgba(111,186,157,.14),transparent); pointer-events:none; }
.sl-line.l1 { width:280px; top:28%; left:-60px; transform:rotate(-15deg); }
.sl-line.l2 { width:220px; bottom:30%; right:-40px; transform:rotate(18deg); }
/* Layout */
.sl-layout {
position: relative; z-index: 2;
display: flex; align-items: center;
width: 100%; max-width: 1100px;
padding: 40px 60px; gap: 80px;
animation: slIn .6s ease both;
}
@keyframes slIn {
from { opacity:0; transform:translateY(20px); }
to { opacity:1; transform:translateY(0); }
}
/* Brand left, Form right */
.sl-brand { flex: 0 0 340px; order: 1; }
.sl-form-panel { flex: 1; max-width: 430px; order: 2; }
/* ── Brand section ── */
.sl-logo { width:72px; height:72px; margin-bottom:20px; border-radius:16px; box-shadow:0 4px 20px rgba(111,186,157,.2); object-fit:contain; background:#fff; }
.sl-eyebrow {
display:inline-flex; align-items:center; gap:8px;
font-size:.68rem; font-weight:700; letter-spacing:2px;
text-transform:uppercase; color:#6FBA9D; margin-bottom:18px;
}
.sl-eyebrow::before {
content:''; display:inline-block; width:22px; height:2px;
background:#6FBA9D; border-radius:2px;
}
.sl-title {
font-family:'DM Serif Display',serif;
font-size:3.2rem; line-height:1.05; color:#0F2118; margin-bottom:6px;
}
.sl-title em { font-style:italic; color:#5EA98C; }
.sl-sub { font-size:.9rem; font-weight:500; color:#8AADA0; margin-bottom:32px; line-height:1.6; }
.sl-divider { width:44px; height:3px; background:linear-gradient(90deg,#6FBA9D,#A8D8C6); border-radius:3px; margin-bottom:24px; }
.sl-desc { font-size:.81rem; color:#8AADA0; line-height:1.8; max-width:290px; margin-bottom:32px; }
.sl-features { display:flex; flex-direction:column; gap:11px; }
.sl-feat { display:flex; align-items:center; gap:11px; font-size:.79rem; color:#2A4235; font-weight:500; }
.sl-feat-ico {
width:30px; height:30px; border-radius:8px; background:#EBF7F2;
display:flex; align-items:center; justify-content:center;
color:#3D8A6E; font-size:.73rem; flex-shrink:0;
}
/* ── Card ── */
.sl-card {
background: #fff; border-radius: 24px;
padding: 42px 38px;
box-shadow:
0 0 0 1px rgba(111,186,157,.1),
0 4px 6px rgba(15,33,24,.03),
0 20px 44px rgba(15,33,24,.08);
position: relative; overflow: hidden;
}
.sl-card::before {
content: ''; position:absolute; top:0; left:0; right:0; height:3px;
background: linear-gradient(90deg, #6FBA9D, #A8D8C6, #6FBA9D);
}
.sl-card::after {
content: ''; position:absolute; bottom:-50px; right:-50px;
width:140px; height:140px; border-radius:50%;
background: radial-gradient(circle, rgba(111,186,157,.06) 0%, transparent 70%);
}
.sl-card-lbl {
font-size:.67rem; font-weight:700; letter-spacing:2px;
text-transform:uppercase; color:#6FBA9D; margin-bottom:7px;
}
.sl-card-title {
font-family:'DM Serif Display',serif;
font-size:1.85rem; color:#0F2118; line-height:1.1; margin-bottom:5px;
}
.sl-card-desc { font-size:.79rem; color:#8AADA0; line-height:1.6; margin-bottom:26px; }
/* Alert */
.sl-alert {
border-radius:10px; padding:10px 13px; font-size:.79rem;
margin-bottom:18px; border-left:3px solid;
}
.sl-alert.danger { background:#FFF3F3; color:#c62828; border-color:#e53935; }
.sl-alert.success { background:#F0FFF4; color:#2E7D32; border-color:#43A047; }
.sl-alert p { display:flex; align-items:center; gap:7px; margin:2px 0; }
/* Fields */
.sl-field { margin-bottom:18px; }
.sl-lbl {
display:block; font-size:.7rem; font-weight:700;
letter-spacing:.8px; text-transform:uppercase; color:#2A4235; margin-bottom:7px;
}
.sl-shell { position:relative; display:flex; align-items:center; }
.sl-shell .fi { position:absolute; left:15px; color:#A8D8C6; font-size:.8rem; pointer-events:none; transition:color .2s; }
.sl-shell input {
width:100%; padding:12px 15px 12px 40px;
background:#EBF7F2; border:1.5px solid transparent;
border-radius:11px; font-family:inherit; font-size:.87rem; color:#0F2118; outline:none;
transition:all .2s;
}
.sl-shell input::placeholder { color:#8AADA0; font-size:.83rem; }
.sl-shell input:focus {
background:#fff; border-color:#6FBA9D;
box-shadow:0 0 0 4px rgba(111,186,157,.12);
}
.sl-show {
position:absolute; right:13px;
background:none; border:none; font-size:.68rem; font-weight:800;
letter-spacing:.8px; color:#5EA98C; cursor:pointer; font-family:inherit;
}
.sl-show:hover { color:#3D8A6E; }
.sl-ferr { font-size:.72rem; color:#e53935; margin-top:4px; padding-left:3px; }
/* Remember */
.sl-remember {
display:flex; align-items:center; gap:9px;
margin-bottom:20px; cursor:pointer;
}
.sl-remember input[type="checkbox"] {
width:16px; height:16px; accent-color:#6FBA9D; cursor:pointer;
border-radius:4px;
}
.sl-remember span {
font-size:.8rem; color:#5A7E6E; font-weight:500; user-select:none;
}
/* Button */
.sl-btn {
width:100%; padding:13px;
background:linear-gradient(135deg, #6FBA9D, #5EA98C);
color:#fff; border:none; border-radius:12px;
font-family:inherit; font-size:.89rem; font-weight:700;
cursor:pointer; letter-spacing:.3px; margin-top:4px;
display:flex; align-items:center; justify-content:center; gap:8px;
box-shadow:0 4px 18px rgba(94,169,140,.35);
transition:all .25s;
}
.sl-btn:hover { transform:translateY(-2px); box-shadow:0 8px 26px rgba(94,169,140,.45); }
.sl-btn:active { transform:none; }
.sl-foot { text-align:center; font-size:.77rem; color:#8AADA0; margin-top:20px; }
.sl-foot a { color:#5EA98C; font-weight:700; text-decoration:none; }
.sl-foot a:hover { text-decoration:underline; }
.sl-note {
text-align:center; font-size:.73rem; color:#A8C4B8; margin-top:14px;
line-height:1.5;
}
.sl-note i { margin-right:4px; }
/* ── Responsive ── */
@media (max-width: 900px) {
.sl-layout { gap:48px; padding:32px 36px; }
.sl-brand { flex:0 0 260px; }
.sl-title { font-size:2.7rem; }
}
@media (max-width: 720px) {
body.auth-page { align-items:flex-start !important; overflow-y:auto !important; }
.sl-wrap { align-items:flex-start; min-height:auto; padding:24px 0 40px; }
.sl-layout { flex-direction:column; padding:0 20px; gap:28px; }
.sl-form-panel { order:2; max-width:100%; }
.sl-brand { order:1; flex:none; text-align:center; }
.sl-title { font-size:2.2rem; }
.sl-logo { width:56px; height:56px; margin:0 auto 14px; display:block; }
.sl-features, .sl-desc, .sl-divider { display:none; }
.sl-sub { margin-bottom:0; }
.sl-card { padding:28px 20px; }
.sl-ring.r1 { width:260px; height:260px; top:-70px; right:-70px; }
.sl-ring.r2 { display:none; }
}
@media (max-width: 420px) {
.sl-title { font-size:1.85rem; }
.sl-card { padding:24px 16px; border-radius:16px; }
.sl-card-title { font-size:1.5rem; }
}
@media (min-width: 1280px) {
.sl-layout { max-width:1160px; padding:40px 80px; }
.sl-brand { flex:0 0 360px; }
.sl-title { font-size:3.6rem; }
}
</style>
<div class="sl-wrap">
<div class="sl-bg"></div>
<div class="sl-ring r1"></div>
<div class="sl-ring r2"></div>
<div class="sl-ring r3"></div>
<div class="sl-dot d1"></div>
<div class="sl-dot d2"></div>
<div class="sl-dot d3"></div>
<div class="sl-line l1"></div>
<div class="sl-line l2"></div>
<div class="sl-layout">
<!-- Brand (kiri) -->
<div class="sl-brand">
<img src="<?php echo e(asset('images/logo.png')); ?>" alt="Logo PKPPS" class="sl-logo">
<div class="sl-eyebrow">Portal Santri</div>
<h1 class="sl-title">Welcome Back<br><em>SIM Santri</em></h1>
<p class="sl-sub">PKPPS Riyadlul Jannah</p>
<div class="sl-divider"></div>
<p class="sl-desc">Akses nilai, absensi, jadwal, dan seluruh progres pembelajaran santri secara mudah dan aman.</p>
<div class="sl-features">
<div class="sl-feat">
<div class="sl-feat-ico"><i class="fas fa-user-graduate"></i></div>
<span>Pantau progres akademik santri</span>
</div>
<div class="sl-feat">
<div class="sl-feat-ico"><i class="fas fa-calendar-check"></i></div>
<span>Lihat jadwal & absensi harian</span>
</div>
<div class="sl-feat">
<div class="sl-feat-ico"><i class="fas fa-chart-line"></i></div>
<span>Laporan capaian & perkembangan</span>
</div>
</div>
</div>
<!-- Form (kanan) -->
<div class="sl-form-panel">
<div class="sl-card">
<div class="sl-card-lbl">Login Santri</div>
<div class="sl-card-title">Masuk ke Akun</div>
<div class="sl-card-desc">Gunakan username dan password yang diberikan oleh admin pesantren.</div>
<?php if($errors->any()): ?>
<div class="sl-alert danger">
<?php $__currentLoopData = $errors->all(); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $error): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
<p><i class="fas fa-circle-exclamation"></i> <?php echo e($error); ?></p>
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
</div>
<?php endif; ?>
<?php if(session('success')): ?>
<div class="sl-alert success">
<p><i class="fas fa-check-circle"></i> <?php echo e(session('success')); ?></p>
</div>
<?php endif; ?>
<form method="POST" action="<?php echo e(route('santri.login')); ?>">
<?php echo csrf_field(); ?>
<div class="sl-field">
<label class="sl-lbl">Username / ID Santri</label>
<div class="sl-shell">
<i class="fas fa-user fi" id="sl-ico-u"></i>
<input type="text" id="username" name="username"
value="<?php echo e(old('username')); ?>"
placeholder="Masukkan username Anda"
autocomplete="username" required autofocus
onfocus="document.getElementById('sl-ico-u').style.color='#6FBA9D'"
onblur="document.getElementById('sl-ico-u').style.color=''">
</div>
<?php $__errorArgs = ['username'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?><div class="sl-ferr"><?php echo e($message); ?></div><?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<div class="sl-field">
<label class="sl-lbl">Password</label>
<div class="sl-shell">
<i class="fas fa-lock fi" id="sl-ico-pw"></i>
<input type="password" id="password" name="password"
placeholder="Masukkan password Anda"
autocomplete="current-password" required
onfocus="document.getElementById('sl-ico-pw').style.color='#6FBA9D'"
onblur="document.getElementById('sl-ico-pw').style.color=''">
<button type="button" class="sl-show" id="slTglBtn">SHOW</button>
</div>
<?php $__errorArgs = ['password'];
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
if ($__bag->has($__errorArgs[0])) :
if (isset($message)) { $__messageOriginal = $message; }
$message = $__bag->first($__errorArgs[0]); ?><div class="sl-ferr"><?php echo e($message); ?></div><?php unset($message);
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
endif;
unset($__errorArgs, $__bag); ?>
</div>
<label class="sl-remember">
<input type="checkbox" name="remember" id="remember" <?php echo e(old('remember') ? 'checked' : ''); ?>>
<span>Ingat Saya</span>
</label>
<button type="submit" class="sl-btn">
<i class="fas fa-sign-in-alt"></i>
Masuk
</button>
<div class="sl-note">
<i class="fas fa-info-circle"></i>
Lupa akun? Silakan hubungi admin pesantren untuk bantuan.
</div>
</form>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Toggle password
const btn = document.getElementById('slTglBtn');
const pw = document.getElementById('password');
if (btn && pw) {
btn.addEventListener('click', () => {
const isP = pw.type === 'password';
pw.type = isP ? 'text' : 'password';
btn.textContent = isP ? 'HIDE' : 'SHOW';
});
}
// Auto-hide success alert
const successAlert = document.querySelector('.sl-alert.success');
if (successAlert) {
setTimeout(() => {
successAlert.style.transition = 'opacity .5s ease';
successAlert.style.opacity = '0';
setTimeout(() => successAlert.remove(), 500);
}, 4000);
}
});
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('auth.auth_layout', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/santri/auth/login.blade.php ENDPATH**/ ?>

View File

@ -0,0 +1,140 @@
<?php $__env->startSection('content'); ?>
<div class="page-header">
<h2><i class="fas fa-info-circle"></i> Detail Transaksi Uang Saku</h2>
</div>
<div class="content-box">
<div class="detail-header">
<h3><?php echo e($transaksi->id_uang_saku); ?></h3>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="<?php echo e(route('admin.uang-saku.riwayat', $transaksi->id_santri)); ?>" class="btn btn-primary">
<i class="fas fa-history"></i> Lihat Riwayat
</a>
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.edit', $transaksi->id)); ?>" class="btn btn-warning">
<i class="fas fa-edit"></i> Edit
</a>
<?php endif; ?>
<a href="<?php echo e(route('admin.uang-saku.index')); ?>" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</div>
<div class="detail-section">
<h4><i class="fas fa-file-alt"></i> Informasi Transaksi</h4>
<table class="detail-table">
<tr>
<th>ID Transaksi</th>
<td><strong><?php echo e($transaksi->id_uang_saku); ?></strong></td>
</tr>
<tr>
<th>Santri</th>
<td>
<strong><?php echo e($transaksi->santri->nama_lengkap); ?></strong><br>
<small class="text-muted"><?php echo e($transaksi->santri->id_santri); ?> - <?php echo e($transaksi->santri->kelas); ?></small>
</td>
</tr>
<tr>
<th>Jenis Transaksi</th>
<td>
<?php if($transaksi->jenis_transaksi === 'pemasukan'): ?>
<span class="badge badge-success badge-lg">
<i class="fas fa-arrow-down"></i> Pemasukan
</span>
<?php else: ?>
<span class="badge badge-danger badge-lg">
<i class="fas fa-arrow-up"></i> Pengeluaran
</span>
<?php endif; ?>
</td>
</tr>
<tr>
<th>Nominal</th>
<td class="nominal-highlight" style="font-size: 1.3rem;">
<?php echo e($transaksi->nominal_format); ?>
</td>
</tr>
<tr>
<th>Tanggal Transaksi</th>
<td><?php echo e($transaksi->tanggal_transaksi->format('d F Y')); ?></td>
</tr>
<tr>
<th>Keterangan</th>
<td><?php echo e($transaksi->keterangan ?? '-'); ?></td>
</tr>
</table>
</div>
<div class="detail-section">
<h4><i class="fas fa-calculator"></i> Rincian Saldo</h4>
<table class="detail-table">
<tr>
<th>Saldo Sebelum</th>
<td>
<strong>Rp <?php echo e(number_format($transaksi->saldo_sebelum, 0, ',', '.')); ?></strong>
</td>
</tr>
<tr>
<th><?php echo e($transaksi->jenis_transaksi === 'pemasukan' ? 'Pemasukan' : 'Pengeluaran'); ?></th>
<td style="color: <?php echo e($transaksi->jenis_transaksi === 'pemasukan' ? '#6FBA9D' : '#FF8B94'); ?>;">
<strong>
<?php echo e($transaksi->jenis_transaksi === 'pemasukan' ? '+' : '-'); ?>
<?php echo e($transaksi->nominal_format); ?>
</strong>
</td>
</tr>
<tr style="background: linear-gradient(135deg, #E8F7F2 0%, #D4F1E3 100%);">
<th>Saldo Sesudah</th>
<td>
<strong style="font-size: 1.2rem; color: <?php echo e($transaksi->saldo_sesudah >= 0 ? '#6FBA9D' : '#FF8B94'); ?>">
<?php echo e($transaksi->saldo_sesudah_format); ?>
</strong>
</td>
</tr>
</table>
</div>
<div class="detail-section">
<h4><i class="fas fa-clock"></i> Informasi Waktu</h4>
<table class="detail-table">
<tr>
<th>Dibuat Pada</th>
<td><?php echo e($transaksi->created_at->format('d F Y, H:i')); ?> WIB</td>
</tr>
<tr>
<th>Terakhir Diubah</th>
<td><?php echo e($transaksi->updated_at->format('d F Y, H:i')); ?> WIB</td>
</tr>
</table>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px; flex-wrap: wrap;">
<a href="<?php echo e(route('admin.uang-saku.riwayat', $transaksi->id_santri)); ?>" class="btn btn-primary">
<i class="fas fa-history"></i> Lihat Riwayat
</a>
<?php if($canCrud): ?>
<a href="<?php echo e(route('admin.uang-saku.edit', $transaksi->id)); ?>" class="btn btn-warning">
<i class="fas fa-edit"></i> Edit Transaksi
</a>
<form action="<?php echo e(route('admin.uang-saku.destroy', $transaksi->id)); ?>"
method="POST"
style="display: inline;"
onsubmit="return confirm('Yakin ingin menghapus transaksi ini?\n\nPerhatian: Saldo transaksi setelahnya akan di-recalculate otomatis.')">
<?php echo csrf_field(); ?>
<?php echo method_field('DELETE'); ?>
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> Hapus
</button>
</form>
<?php endif; ?>
</div>
</div>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/uang-saku/show.blade.php ENDPATH**/ ?>

View File

@ -1,315 +0,0 @@
<?php $__env->startSection('title', 'Lupa Password'); ?>
<?php $__env->startSection('auth-content'); ?>
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:opsz,wght@9..40,300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body.auth-page {
background: #F8FDFB !important;
font-family: 'DM Sans', sans-serif !important;
padding: 0 !important;
align-items: stretch !important;
min-height: 100vh !important;
}
.auth-container {
width: 100vw !important; max-width: 100vw !important;
min-height: 100vh !important; background: transparent !important;
padding: 0 !important; border-radius: 0 !important; box-shadow: none !important;
}
.fp-wrap {
position: relative; width: 100%; min-height: 100vh;
display: flex; align-items: center; justify-content: center;
overflow: hidden; font-family: 'DM Sans', sans-serif;
}
.fp-bg {
position: absolute; inset: 0; z-index: 0;
background:
radial-gradient(ellipse 80% 60% at 70% 50%, rgba(111,186,157,.12) 0%, transparent 60%),
radial-gradient(ellipse 50% 80% at 10% 80%, rgba(111,186,157,.08) 0%, transparent 55%),
#F8FDFB;
}
.fp-bg::before {
content: ''; position: absolute; inset: 0;
background-image:
linear-gradient(rgba(111,186,157,.055) 1px, transparent 1px),
linear-gradient(90deg, rgba(111,186,157,.055) 1px, transparent 1px);
background-size: 48px 48px;
}
.fp-ring { position: absolute; border-radius: 50%; border: 1.5px solid rgba(111,186,157,.18); pointer-events: none; }
.fp-ring.r1 { width:420px; height:420px; top:-110px; right:-110px; }
.fp-ring.r2 { width:270px; height:270px; bottom:50px; left:60px; border-color:rgba(111,186,157,.10); }
.fp-dot { position:absolute; border-radius:50%; background:#6FBA9D; pointer-events:none; }
.fp-dot.d1 { width:8px; height:8px; top:22%; right:18%; opacity:.14; }
.fp-dot.d2 { width:11px; height:11px; top:55%; left:15%; opacity:.08; }
.fp-line { position:absolute; height:1px; background:linear-gradient(90deg,transparent,rgba(111,186,157,.14),transparent); pointer-events:none; }
.fp-line.l1 { width:280px; top:28%; left:-60px; transform:rotate(-15deg); }
.fp-layout {
position: relative; z-index: 2;
display: flex; align-items: center;
width: 100%; max-width: 1100px;
padding: 40px 60px; gap: 80px;
animation: fpIn .6s ease both;
}
@keyframes fpIn {
from { opacity:0; transform:translateY(20px); }
to { opacity:1; transform:translateY(0); }
}
.fp-brand { flex: 0 0 340px; order: 1; }
.fp-form-panel { flex: 1; max-width: 430px; order: 2; }
/* Brand */
.fp-logo { width:72px; height:72px; margin-bottom:20px; border-radius:16px; box-shadow:0 4px 20px rgba(111,186,157,.2); object-fit:contain; background:#fff; }
.fp-eyebrow {
display:inline-flex; align-items:center; gap:8px;
font-size:.68rem; font-weight:700; letter-spacing:2px;
text-transform:uppercase; color:#6FBA9D; margin-bottom:18px;
}
.fp-eyebrow::before { content:''; display:inline-block; width:22px; height:2px; background:#6FBA9D; border-radius:2px; }
.fp-title {
font-family:'DM Serif Display',serif;
font-size:3.2rem; line-height:1.05; color:#0F2118; margin-bottom:6px;
}
.fp-title em { font-style:italic; color:#5EA98C; }
.fp-sub { font-size:.9rem; font-weight:500; color:#8AADA0; margin-bottom:32px; line-height:1.6; }
.fp-divider { width:44px; height:3px; background:linear-gradient(90deg,#6FBA9D,#A8D8C6); border-radius:3px; margin-bottom:24px; }
.fp-desc { font-size:.81rem; color:#8AADA0; line-height:1.8; max-width:290px; margin-bottom:32px; }
/* Steps */
.fp-steps { display:flex; flex-direction:column; gap:14px; }
.fp-step { display:flex; align-items:flex-start; gap:12px; }
.fp-step-num {
width:28px; height:28px; border-radius:50%;
background:linear-gradient(135deg,#6FBA9D,#5EA98C);
color:#fff; font-size:.72rem; font-weight:800;
display:flex; align-items:center; justify-content:center; flex-shrink:0;
}
.fp-step-num.active { box-shadow:0 0 0 4px rgba(111,186,157,.2); }
.fp-step-text { font-size:.78rem; color:#2A4235; font-weight:500; line-height:1.5; padding-top:3px; }
.fp-step-text small { display:block; color:#8AADA0; font-weight:400; font-size:.72rem; }
/* Card */
.fp-card {
background: #fff; border-radius: 24px;
padding: 42px 38px;
box-shadow:
0 0 0 1px rgba(111,186,157,.1),
0 4px 6px rgba(15,33,24,.03),
0 20px 44px rgba(15,33,24,.08);
position: relative; overflow: hidden;
}
.fp-card::before {
content: ''; position:absolute; top:0; left:0; right:0; height:3px;
background: linear-gradient(90deg, #e57373, #ef9a9a, #e57373);
}
.fp-card::after {
content: ''; position:absolute; bottom:-50px; right:-50px;
width:140px; height:140px; border-radius:50%;
background: radial-gradient(circle, rgba(229,115,115,.06) 0%, transparent 70%);
}
.fp-card-icon {
width:54px; height:54px; border-radius:14px;
background:linear-gradient(135deg,#FFEBEE,#FFCDD2);
display:flex; align-items:center; justify-content:center;
font-size:1.3rem; color:#e53935; margin-bottom:16px;
}
.fp-card-lbl {
font-size:.67rem; font-weight:700; letter-spacing:2px;
text-transform:uppercase; color:#e57373; margin-bottom:7px;
}
.fp-card-title {
font-family:'DM Serif Display',serif;
font-size:1.85rem; color:#0F2118; line-height:1.1; margin-bottom:5px;
}
.fp-card-desc { font-size:.79rem; color:#8AADA0; line-height:1.6; margin-bottom:26px; }
/* Alert */
.fp-alert-danger {
border-radius:10px; padding:10px 13px; font-size:.79rem;
margin-bottom:18px; background:#FFF3F3; color:#c62828; border-left:3px solid #e53935;
display:flex; align-items:center; gap:7px;
}
.fp-alert-success {
border-radius:10px; padding:10px 13px; font-size:.79rem;
margin-bottom:18px; background:#F0FFF4; color:#2E7D32; border-left:3px solid #43A047;
display:flex; align-items:center; gap:7px;
}
/* Field */
.fp-field { margin-bottom:18px; }
.fp-lbl { display:block; font-size:.7rem; font-weight:700; letter-spacing:.8px; text-transform:uppercase; color:#2A4235; margin-bottom:7px; }
.fp-shell { position:relative; display:flex; align-items:center; }
.fp-shell .fi { position:absolute; left:15px; color:#A8D8C6; font-size:.8rem; pointer-events:none; transition:color .2s; }
.fp-shell input {
width:100%; padding:12px 15px 12px 40px;
background:#EBF7F2; border:1.5px solid transparent;
border-radius:11px; font-family:inherit; font-size:.87rem; color:#0F2118; outline:none;
transition:all .2s;
}
.fp-shell input::placeholder { color:#8AADA0; font-size:.83rem; }
.fp-shell input:focus { background:#fff; border-color:#e57373; box-shadow:0 0 0 4px rgba(229,115,115,.12); }
.fp-shell .fi.active { color:#e57373; }
/* Button */
.fp-btn {
width:100%; padding:13px;
background:linear-gradient(135deg, #e57373, #ef5350);
color:#fff; border:none; border-radius:12px;
font-family:inherit; font-size:.89rem; font-weight:700;
cursor:pointer; letter-spacing:.3px;
display:flex; align-items:center; justify-content:center; gap:8px;
box-shadow:0 4px 18px rgba(229,115,115,.35);
transition:all .25s;
}
.fp-btn:hover { transform:translateY(-2px); box-shadow:0 8px 26px rgba(229,115,115,.45); }
.fp-btn:active { transform:none; }
.fp-back {
display:flex; align-items:center; justify-content:center; gap:6px;
margin-top:20px; font-size:.78rem; color:#5EA98C; font-weight:600; text-decoration:none;
transition:color .2s;
}
.fp-back:hover { color:#3D8A6E; text-decoration:underline; }
/* Responsive */
@media (max-width: 900px) {
.fp-layout { gap:48px; padding:32px 36px; }
.fp-brand { flex:0 0 260px; }
.fp-title { font-size:2.7rem; }
}
@media (max-width: 720px) {
body.auth-page { align-items:flex-start !important; overflow-y:auto !important; }
.fp-wrap { align-items:flex-start; min-height:auto; padding:24px 0 40px; }
.fp-layout { flex-direction:column; padding:0 20px; gap:28px; }
.fp-form-panel { order:2; max-width:100%; }
.fp-brand { order:1; flex:none; text-align:center; }
.fp-title { font-size:2.2rem; }
.fp-steps, .fp-desc, .fp-divider { display:none; }
.fp-sub { margin-bottom:0; }
.fp-card { padding:28px 20px; }
.fp-logo { width:56px; height:56px; margin:0 auto 14px; display:block; }
}
@media (max-width: 420px) {
.fp-title { font-size:1.85rem; }
.fp-card { padding:24px 16px; border-radius:16px; }
.fp-card-title { font-size:1.5rem; }
}
@media (min-width: 1280px) {
.fp-layout { max-width:1160px; padding:40px 80px; }
.fp-brand { flex:0 0 360px; }
.fp-title { font-size:3.6rem; }
}
</style>
<div class="fp-wrap">
<div class="fp-bg"></div>
<div class="fp-ring r1"></div>
<div class="fp-ring r2"></div>
<div class="fp-dot d1"></div>
<div class="fp-dot d2"></div>
<div class="fp-line l1"></div>
<div class="fp-layout">
<!-- ═══ Brand (kiri) ═══ -->
<div class="fp-brand">
<img src="<?php echo e(asset('images/logo.png')); ?>" alt="Logo PKPPS" class="fp-logo">
<div class="fp-eyebrow">Reset Akses</div>
<h1 class="fp-title">Lupa<br><em>Password?</em></h1>
<p class="fp-sub">Jangan khawatir, kami bantu pulihkan.</p>
<div class="fp-divider"></div>
<p class="fp-desc">Ikuti langkah berikut untuk mengatur ulang password akun Super Admin Anda.</p>
<div class="fp-steps">
<div class="fp-step">
<div class="fp-step-num active">1</div>
<div class="fp-step-text">Masukkan email terdaftar <small>Kami kirim kode OTP 6 digit</small></div>
</div>
<div class="fp-step">
<div class="fp-step-num">2</div>
<div class="fp-step-text">Verifikasi kode OTP <small>Cek email masuk / spam</small></div>
</div>
<div class="fp-step">
<div class="fp-step-num">3</div>
<div class="fp-step-text">Buat password baru <small>Minimal 8 karakter</small></div>
</div>
</div>
</div>
<!-- ═══ Form (kanan) ═══ -->
<div class="fp-form-panel">
<div class="fp-card">
<div class="fp-card-icon">
<i class="fas fa-envelope-open-text"></i>
</div>
<div class="fp-card-lbl">Langkah 1 dari 3</div>
<div class="fp-card-title">Masukkan Email</div>
<div class="fp-card-desc">Masukkan email Super Admin yang terdaftar di sistem. Kami akan mengirim kode OTP ke email tersebut.</div>
<?php if($errors->any()): ?>
<div class="fp-alert-danger">
<i class="fas fa-exclamation-circle"></i>
<?php echo e($errors->first()); ?>
</div>
<?php endif; ?>
<?php if(session('success')): ?>
<div class="fp-alert-success" id="fpSuccessAlert">
<i class="fas fa-check-circle"></i>
<?php echo e(session('success')); ?>
</div>
<?php endif; ?>
<form method="POST" action="<?php echo e(route('admin.forgot.send_otp')); ?>">
<?php echo csrf_field(); ?>
<div class="fp-field">
<label class="fp-lbl">Email Super Admin</label>
<div class="fp-shell">
<i class="fas fa-envelope fi" id="ico-email"></i>
<input type="email" id="email" name="email"
value="<?php echo e(old('email')); ?>"
placeholder="contoh@email.com"
required autofocus
onfocus="document.getElementById('ico-email').classList.add('active')"
onblur="document.getElementById('ico-email').classList.remove('active')">
</div>
</div>
<button type="submit" class="fp-btn">
<i class="fas fa-paper-plane"></i>
Kirim Kode OTP
</button>
</form>
<a href="<?php echo e(route('admin.login')); ?>" class="fp-back">
<i class="fas fa-arrow-left"></i> Kembali ke Login
</a>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const sa = document.getElementById('fpSuccessAlert');
if (sa) {
setTimeout(() => {
sa.style.transition = 'opacity .5s ease';
sa.style.opacity = '0';
setTimeout(() => sa.remove(), 500);
}, 5000);
}
});
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('auth.auth_layout', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/auth/forgot_password.blade.php ENDPATH**/ ?>

View File

@ -1,426 +0,0 @@
<?php $__env->startSection('title', 'Login Admin'); ?>
<?php $__env->startSection('auth-content'); ?>
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:opsz,wght@9..40,300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body.auth-page {
background: #F8FDFB !important;
font-family: 'DM Sans', sans-serif !important;
padding: 0 !important;
align-items: stretch !important;
min-height: 100vh !important;
}
.auth-container {
width: 100vw !important; max-width: 100vw !important;
min-height: 100vh !important; background: transparent !important;
padding: 0 !important; border-radius: 0 !important; box-shadow: none !important;
}
/* ── Wrapper & Background ── */
.lg-wrap {
position: relative; width: 100%; min-height: 100vh;
display: flex; align-items: center; justify-content: center;
overflow: hidden; font-family: 'DM Sans', sans-serif;
}
.lg-bg {
position: absolute; inset: 0; z-index: 0;
background:
radial-gradient(ellipse 80% 60% at 70% 50%, rgba(111,186,157,.12) 0%, transparent 60%),
radial-gradient(ellipse 50% 80% at 10% 80%, rgba(111,186,157,.08) 0%, transparent 55%),
#F8FDFB;
}
.lg-bg::before {
content: ''; position: absolute; inset: 0;
background-image:
linear-gradient(rgba(111,186,157,.055) 1px, transparent 1px),
linear-gradient(90deg, rgba(111,186,157,.055) 1px, transparent 1px);
background-size: 48px 48px;
}
/* Decorations */
.lg-ring { position: absolute; border-radius: 50%; border: 1.5px solid rgba(111,186,157,.18); pointer-events: none; }
.lg-ring.r1 { width:420px; height:420px; top:-110px; right:-110px; }
.lg-ring.r2 { width:270px; height:270px; bottom:50px; right:60px; border-color:rgba(111,186,157,.10); }
.lg-ring.r3 { width:200px; height:200px; bottom:-50px; left:60px; border-color:rgba(111,186,157,.14); }
.lg-dot { position:absolute; border-radius:50%; background:#6FBA9D; pointer-events:none; }
.lg-dot.d1 { width:8px; height:8px; top:22%; right:18%; opacity:.14; }
.lg-dot.d2 { width:5px; height:5px; bottom:40%; right:30%; opacity:.09; }
.lg-dot.d3 { width:11px; height:11px; top:55%; left:15%; opacity:.08; }
.lg-line { position:absolute; height:1px; background:linear-gradient(90deg,transparent,rgba(111,186,157,.14),transparent); pointer-events:none; }
.lg-line.l1 { width:280px; top:28%; left:-60px; transform:rotate(-15deg); }
.lg-line.l2 { width:220px; bottom:30%; right:-40px; transform:rotate(18deg); }
/* Layout */
.lg-layout {
position: relative; z-index: 2;
display: flex; align-items: center;
width: 100%; max-width: 1100px;
padding: 40px 60px; gap: 80px;
animation: lgIn .6s ease both;
}
@keyframes lgIn {
from { opacity:0; transform:translateY(20px); }
to { opacity:1; transform:translateY(0); }
}
/* Brand left, Form right */
.lg-brand { flex: 0 0 340px; order: 1; }
.lg-form-panel { flex: 1; max-width: 430px; order: 2; }
/* ── Brand section ── */
.lg-logo { width:72px; height:72px; margin-bottom:20px; border-radius:16px; box-shadow:0 4px 20px rgba(111,186,157,.2); object-fit:contain; background:#fff; }
.lg-eyebrow {
display:inline-flex; align-items:center; gap:8px;
font-size:.68rem; font-weight:700; letter-spacing:2px;
text-transform:uppercase; color:#6FBA9D; margin-bottom:18px;
}
.lg-eyebrow::before {
content:''; display:inline-block; width:22px; height:2px;
background:#6FBA9D; border-radius:2px;
}
.lg-title {
font-family:'DM Serif Display',serif;
font-size:3.2rem; line-height:1.05; color:#0F2118; margin-bottom:6px;
}
.lg-title em { font-style:italic; color:#5EA98C; }
.lg-sub { font-size:.9rem; font-weight:500; color:#8AADA0; margin-bottom:32px; line-height:1.6; }
.lg-divider { width:44px; height:3px; background:linear-gradient(90deg,#6FBA9D,#A8D8C6); border-radius:3px; margin-bottom:24px; }
.lg-desc { font-size:.81rem; color:#8AADA0; line-height:1.8; max-width:290px; margin-bottom:32px; }
.lg-features { display:flex; flex-direction:column; gap:11px; }
.lg-feat { display:flex; align-items:center; gap:11px; font-size:.79rem; color:#2A4235; font-weight:500; }
.lg-feat-ico {
width:30px; height:30px; border-radius:8px; background:#EBF7F2;
display:flex; align-items:center; justify-content:center;
color:#3D8A6E; font-size:.73rem; flex-shrink:0;
}
/* ── Card ── */
.lg-card {
background: #fff; border-radius: 24px;
padding: 42px 38px;
box-shadow:
0 0 0 1px rgba(111,186,157,.1),
0 4px 6px rgba(15,33,24,.03),
0 20px 44px rgba(15,33,24,.08);
position: relative; overflow: hidden;
}
.lg-card::before {
content: ''; position:absolute; top:0; left:0; right:0; height:3px;
background: linear-gradient(90deg, #6FBA9D, #A8D8C6, #6FBA9D);
}
.lg-card::after {
content: ''; position:absolute; bottom:-50px; right:-50px;
width:140px; height:140px; border-radius:50%;
background: radial-gradient(circle, rgba(111,186,157,.06) 0%, transparent 70%);
}
.lg-card-lbl {
font-size:.67rem; font-weight:700; letter-spacing:2px;
text-transform:uppercase; color:#6FBA9D; margin-bottom:7px;
}
.lg-card-title {
font-family:'DM Serif Display',serif;
font-size:1.85rem; color:#0F2118; line-height:1.1; margin-bottom:5px;
}
.lg-card-desc { font-size:.79rem; color:#8AADA0; line-height:1.6; margin-bottom:26px; }
/* Alert */
.lg-alert-danger {
border-radius:10px; padding:10px 13px; font-size:.79rem;
margin-bottom:18px; background:#FFF3F3; color:#c62828; border-left:3px solid #e53935;
display:flex; align-items:center; gap:7px;
}
.lg-alert-success {
border-radius:10px; padding:10px 13px; font-size:.79rem;
margin-bottom:18px; background:#F0FFF4; color:#2E7D32; border-left:3px solid #43A047;
display:flex; align-items:center; gap:7px;
}
/* Fields */
.lg-field { margin-bottom:15px; }
.lg-lbl {
display:block; font-size:.7rem; font-weight:700;
letter-spacing:.8px; text-transform:uppercase; color:#2A4235; margin-bottom:7px;
}
.lg-shell { position:relative; display:flex; align-items:center; }
.lg-shell .fi { position:absolute; left:15px; color:#A8D8C6; font-size:.8rem; pointer-events:none; transition:color .2s; }
.lg-shell input {
width:100%; padding:12px 15px 12px 40px;
background:#EBF7F2; border:1.5px solid transparent;
border-radius:11px; font-family:inherit; font-size:.87rem; color:#0F2118; outline:none;
transition:all .2s;
}
.lg-shell input::placeholder { color:#8AADA0; font-size:.83rem; }
.lg-shell input:focus {
background:#fff; border-color:#6FBA9D;
box-shadow:0 0 0 4px rgba(111,186,157,.12);
}
.lg-shell .fi.active { color:#6FBA9D; }
.lg-show {
position:absolute; right:13px;
background:none; border:none; font-size:.68rem; font-weight:800;
letter-spacing:.8px; color:#5EA98C; cursor:pointer; font-family:inherit;
}
.lg-show:hover { color:#3D8A6E; }
/* Remember + Forgot row */
.lg-options {
display:flex; align-items:center; justify-content:space-between;
margin-bottom:18px; font-size:.78rem;
}
.lg-remember { display:flex; align-items:center; gap:7px; color:#2A4235; font-weight:500; cursor:pointer; }
.lg-remember input[type="checkbox"] {
width:16px; height:16px; accent-color:#6FBA9D; cursor:pointer;
}
.lg-forgot { color:#e57373; font-weight:600; text-decoration:none; transition:color .2s; }
.lg-forgot:hover { color:#c62828; text-decoration:underline; }
/* Buttons */
.lg-btn {
width:100%; padding:13px;
background:linear-gradient(135deg, #6FBA9D, #5EA98C);
color:#fff; border:none; border-radius:12px;
font-family:inherit; font-size:.89rem; font-weight:700;
cursor:pointer; letter-spacing:.3px; margin-top:6px;
display:flex; align-items:center; justify-content:center; gap:8px;
box-shadow:0 4px 18px rgba(94,169,140,.35);
transition:all .25s;
}
.lg-btn:hover { transform:translateY(-2px); box-shadow:0 8px 26px rgba(94,169,140,.45); }
.lg-btn:active { transform:none; }
.lg-foot { text-align:center; font-size:.77rem; color:#8AADA0; margin-top:20px; }
.lg-foot a { color:#5EA98C; font-weight:700; text-decoration:none; }
.lg-foot a:hover { text-decoration:underline; }
/* Santri separator */
.lg-sep {
display:flex; align-items:center; gap:12px; margin-top:22px;
font-size:.7rem; color:#B8D4C8; letter-spacing:1px; text-transform:uppercase; font-weight:600;
}
.lg-sep::before, .lg-sep::after {
content:''; flex:1; height:1px; background:linear-gradient(90deg,transparent,#D6EDE5,transparent);
}
.lg-santri-link {
display:flex; align-items:center; justify-content:center; gap:8px;
margin-top:12px; padding:10px;
background:#EBF7F2; border:1.5px solid transparent; border-radius:10px;
font-size:.8rem; font-weight:600; color:#3D8A6E; text-decoration:none;
transition:all .2s;
}
.lg-santri-link:hover {
border-color:#6FBA9D; background:#fff; box-shadow:0 0 0 3px rgba(111,186,157,.1);
}
/* Responsive */
@media (max-width: 900px) {
.lg-layout { gap:48px; padding:32px 36px; }
.lg-brand { flex:0 0 260px; }
.lg-title { font-size:2.7rem; }
}
@media (max-width: 720px) {
body.auth-page { align-items:flex-start !important; overflow-y:auto !important; }
.lg-wrap { align-items:flex-start; min-height:auto; padding:24px 0 40px; }
.lg-layout { flex-direction:column; padding:0 20px; gap:28px; }
.lg-form-panel { order:2; max-width:100%; }
.lg-brand { order:1; flex:none; text-align:center; }
.lg-title { font-size:2.2rem; }
.lg-features, .lg-desc, .lg-divider { display:none; }
.lg-sub { margin-bottom:0; }
.lg-card { padding:28px 20px; }
.lg-ring.r1 { width:260px; height:260px; top:-70px; right:-70px; }
.lg-ring.r2 { display:none; }
.lg-logo { width:56px; height:56px; margin:0 auto 14px; display:block; }
}
@media (max-width: 420px) {
.lg-title { font-size:1.85rem; }
.lg-card { padding:24px 16px; border-radius:16px; }
.lg-card-title { font-size:1.5rem; }
.lg-options { flex-direction:column; align-items:flex-start; gap:10px; }
}
@media (min-width: 1280px) {
.lg-layout { max-width:1160px; padding:40px 80px; }
.lg-brand { flex:0 0 360px; }
.lg-title { font-size:3.6rem; }
}
</style>
<div class="lg-wrap">
<div class="lg-bg"></div>
<div class="lg-ring r1"></div>
<div class="lg-ring r2"></div>
<div class="lg-ring r3"></div>
<div class="lg-dot d1"></div>
<div class="lg-dot d2"></div>
<div class="lg-dot d3"></div>
<div class="lg-line l1"></div>
<div class="lg-line l2"></div>
<div class="lg-layout">
<!-- ═══ Brand (kiri) ═══ -->
<div class="lg-brand">
<img src="<?php echo e(asset('images/logo.png')); ?>" alt="Logo PKPPS" class="lg-logo">
<div class="lg-eyebrow">Selamat Datang</div>
<h1 class="lg-title">Masuk ke<br>Panel<br><em>Admin.</em></h1>
<p class="lg-sub">PKPPS Riyadlul Jannah</p>
<div class="lg-divider"></div>
<p class="lg-desc">Kelola data santri, absensi, keuangan, dan seluruh aktivitas pesantren dalam satu sistem terpadu.</p>
<div class="lg-features">
<div class="lg-feat">
<div class="lg-feat-ico"><i class="fas fa-chart-line"></i></div>
<span>Dashboard monitoring real-time</span>
</div>
<div class="lg-feat">
<div class="lg-feat-ico"><i class="fas fa-shield-alt"></i></div>
<span>Akses aman berbasis role</span>
</div>
<div class="lg-feat">
<div class="lg-feat-ico"><i class="fas fa-mobile-alt"></i></div>
<span>Terintegrasi aplikasi mobile wali</span>
</div>
</div>
</div>
<!-- ═══ Form (kanan) ═══ -->
<div class="lg-form-panel">
<div class="lg-card">
<div class="lg-card-lbl">Login Admin</div>
<div class="lg-card-title">Masuk Akun</div>
<div class="lg-card-desc">Masukkan username dan password untuk mengakses panel admin.</div>
<?php if($errors->any()): ?>
<div class="lg-alert-danger">
<i class="fas fa-exclamation-circle"></i>
<?php echo e($errors->first()); ?>
</div>
<?php endif; ?>
<?php if(session('success')): ?>
<div class="lg-alert-success" id="lgSuccessAlert">
<i class="fas fa-check-circle"></i>
<?php echo e(session('success')); ?>
</div>
<?php endif; ?>
<form method="POST" action="<?php echo e(route('admin.login')); ?>" id="adminLoginForm">
<?php echo csrf_field(); ?>
<div class="lg-field">
<label class="lg-lbl">Username</label>
<div class="lg-shell">
<i class="fas fa-user fi" id="ico-u"></i>
<input type="text" id="username" name="username"
value="<?php echo e(old('username')); ?>"
placeholder="Masukkan username admin"
autocomplete="username" required autofocus
onfocus="document.getElementById('ico-u').classList.add('active')"
onblur="document.getElementById('ico-u').classList.remove('active')">
</div>
</div>
<div class="lg-field">
<label class="lg-lbl">Password</label>
<div class="lg-shell">
<i class="fas fa-lock fi" id="ico-p"></i>
<input type="password" id="password" name="password"
placeholder="Masukkan password"
autocomplete="current-password" required
onfocus="document.getElementById('ico-p').classList.add('active')"
onblur="document.getElementById('ico-p').classList.remove('active')">
<button type="button" class="lg-show" id="lgTglBtn">SHOW</button>
</div>
</div>
<div class="lg-options">
<label class="lg-remember">
<input type="checkbox" name="remember" id="remember"> Ingat Saya
</label>
<a href="<?php echo e(route('admin.forgot.email_form')); ?>" class="lg-forgot">
<i class="fas fa-key"></i> Lupa Password?
</a>
</div>
<button type="submit" class="lg-btn">
<i class="fas fa-sign-in-alt"></i>
Login
</button>
<div class="lg-foot">
Admin baru? <a href="<?php echo e(route('admin.register')); ?>">Daftar Sekarang</a>
</div>
<div class="lg-sep">atau</div>
<a href="<?php echo e(route('santri.login')); ?>" class="lg-santri-link">
<i class="fas fa-user-graduate"></i> Login sebagai Santri / Wali
</a>
</form>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Toggle password
const btn = document.getElementById('lgTglBtn');
const pw = document.getElementById('password');
if (btn && pw) {
btn.addEventListener('click', () => {
const isP = pw.type === 'password';
pw.type = isP ? 'text' : 'password';
btn.textContent = isP ? 'HIDE' : 'SHOW';
});
}
// CSRF check
const form = document.getElementById('adminLoginForm');
if (form) {
form.addEventListener('submit', function(e) {
const csrf = document.querySelector('input[name="_token"]');
if (!csrf || !csrf.value || csrf.value.length < 40) {
e.preventDefault();
alert('Session expired. Halaman akan dimuat ulang.');
window.location.reload();
return false;
}
});
}
// Clear error on input
const alertBox = document.querySelector('.lg-alert-danger');
if (alertBox) {
['username','password'].forEach(id => {
const el = document.getElementById(id);
if (el) el.addEventListener('input', () => alertBox.style.display = 'none');
});
}
// Auto-hide success
const sa = document.getElementById('lgSuccessAlert');
if (sa) {
setTimeout(() => {
sa.style.transition = 'opacity .5s ease';
sa.style.opacity = '0';
setTimeout(() => sa.remove(), 500);
}, 5000);
}
// Focus management
const u = document.getElementById('username');
const p = document.getElementById('password');
if (u && !u.value) u.focus();
if (u && p) {
u.addEventListener('keypress', e => {
if (e.key === 'Enter') { e.preventDefault(); p.focus(); }
});
}
});
</script>
<?php $__env->stopSection(); ?>
<?php echo $__env->make('auth.auth_layout', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?><?php /**PATH C:\xampp\htdocs\TugasAkhir\sim-pkpps\resources\views/admin/auth/login.blade.php ENDPATH**/ ?>