Compare commits

...

10 Commits

Author SHA1 Message Date
ghozahimma65 6f241e19fb upload TA 2026-05-19 14:37:54 +07:00
ghozahimma65 fe34628f50 update revisi 2026-05-09 13:44:30 +07:00
ghozahimma65 6e5f120cbd Update landing.blade.php 2026-04-09 01:58:57 +07:00
ghozahimma65 589a53f094 bismillah TA web selesai
insyaallah gaada yang bug/error
2026-04-09 01:56:37 +07:00
ghozahimma65 6c2fb15d9f oke baik 2026-04-08 02:14:30 +07:00
ghozahimma65 4db17667aa oke mantap lanjut👍🤙 2026-04-08 01:12:54 +07:00
ghozahimma65 885f55c0f5 update 2026-03-05 09:48:48 +07:00
ghozahimma65 f01d25f200 update walimurid 2026-03-05 04:51:27 +07:00
ghozahimma65 d49d88e122 checkpion 2026-02-26 05:44:37 +07:00
ghozahimma65 9f467b7dab save dulu 2026-02-25 18:08:09 +07:00
77 changed files with 4333 additions and 858 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

BIN
app/Http/Controllers.zip Normal file

Binary file not shown.

View File

@ -21,14 +21,14 @@ public function create()
{
$gurus = Guru::whereNull('user_id')->get(['id', 'nama_guru']);
$waliMurids = WaliMurid::whereNull('user_id')->get(['id', 'nama_wali']);
return view('admin.akun.create', compact('gurus', 'waliMurids'));
}
public function store(Request $request)
{
$request->validate([
'role' => 'required|in:guru,wali_murid',
'user_guru_id' => 'required',
@ -43,22 +43,22 @@ public function store(Request $request)
$name = $data->nama_wali;
$email = $data->email ?? strtolower(str_replace(' ', '', $data->nama_wali)) . '@paud.local';
}
if (User::where('email', $email)->exists()) {
return back()->with('error', 'Email sudah digunakan untuk akun lain.');
}
$user = User::create([
'name' => $name,
'email' => $email,
'password' => Hash::make('123456'),
'role' => $request->role,
]);
$data->update(['user_id' => $user->id]);
return redirect()->route('akun.index')->with('success', 'Akun berhasil dibuat!');
}
@ -67,12 +67,12 @@ public function destroy(User $akun)
$akun->delete();
return redirect()->route('akun.index')->with('success', 'Akun berhasil dihapus.');
}
public function edit(User $akun)
{
return view('admin.akun.edit', compact('akun'));
}
public function update(Request $request, User $akun)
{
$request->validate([
@ -80,22 +80,22 @@ public function update(Request $request, User $akun)
'email' => 'required|email|unique:users,email,' . $akun->id,
'role' => 'required|in:admin,guru,wali_murid',
]);
$akun->update([
'name' => $request->name,
'email' => $request->email,
'role' => $request->role,
]);
return redirect()->route('akun.index')->with('success', 'Akun berhasil diperbarui.');
}
public function resetPassword(User $akun)
{
$akun->update([
'password' => Hash::make('123456'),
]);
return redirect()->route('akun.index')->with('success', 'Password berhasil direset ke default (123456).');
}
}

View File

@ -23,74 +23,93 @@ public function create()
}
public function store(Request $request)
{
// 1. Validasi
$request->validate([
'nama_guru' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6',
{
// 1. Validasi
$request->validate([
'nama_guru' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6',
]);
DB::transaction(function () use ($request) {
// A. SIMPAN KE TABEL USERS (Disini tempatnya Email & Password)
$user = User::create([
'name' => $request->nama_guru,
'email' => $request->email,
'password' => Hash::make($request->password),
'role' => 'guru',
]);
DB::transaction(function () use ($request) {
// A. SIMPAN KE TABEL USERS (Disini tempatnya Email & Password)
$user = User::create([
'name' => $request->nama_guru,
'email' => $request->email,
'password' => Hash::make($request->password),
'role' => 'guru',
]);
// B. SIMPAN KE TABEL GURU (HANYA DATA PROFIL)
Guru::create([
'user_id' => $user->id,
'nama_guru' => $request->nama_guru,
'nip' => $request->nip,
'jenis_guru' => $request->jenis_guru, // Pastikan kolom ini ada di tabel guru kamu
'no_hp' => $request->no_hp,
'alamat' => $request->alamat,
// ❌ JANGAN ADA baris 'email' => ... disini
// ❌ JANGAN ADA baris 'password' => ... disini
]);
});
return redirect()->route('guru.index')->with('success', 'Berhasil menambahkan Guru!');
}
// B. SIMPAN KE TABEL GURU (HANYA DATA PROFIL)
Guru::create([
'user_id' => $user->id,
'nama_guru' => $request->nama_guru,
'nip' => $request->nip,
'jenis_guru' => $request->jenis_guru, // Pastikan kolom ini ada di tabel guru kamu
'no_hp' => $request->no_hp,
'alamat' => $request->alamat,
// ❌ JANGAN ADA baris 'email' => ... disini
// ❌ JANGAN ADA baris 'password' => ... disini
]);
});
return redirect()->route('guru.index')->with('success', 'Berhasil menambahkan Guru!');
}
public function edit($id)
{
// PENTING: Tambahkan ->with('user') biar data email & nama akun ke-load
$guru = Guru::with('user')->findOrFail($id);
return view('admin.guru.edit', compact('guru'));
}
public function update(Request $request, $id)
{
// Cari data guru
$guru = Guru::findOrFail($id);
// 1. Validasi
$request->validate([
'nama_guru' => 'required|string|max:255', // Harus nama_guru
'email' => 'required|email',
'no_hp' => 'nullable|string',
'jenis_guru' => 'required|string',
]);
// 2. Update Tabel Guru LANGSUNG
// Kita abaikan tabel user dulu karena user_id kamu masih NULL
$guru->update([
'nama_guru' => $request->nama_guru, // Masukkan ke kolom nama_guru
'email' => $request->email,
'no_hp' => $request->no_hp,
'jenis_guru' => $request->jenis_guru,
]);
return redirect()->route('guru.index')->with('success', 'Data Guru berhasil diperbarui!');
{
// PENTING: Tambahkan ->with('user') biar data email & nama akun ke-load
$guru = Guru::with('user')->findOrFail($id);
return view('admin.guru.edit', compact('guru'));
}
public function update(Request $request, $id)
{
// Cari data guru
$guru = Guru::findOrFail($id);
// 1. Validasi
$request->validate([
'nama_guru' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . $guru->user_id,
'password' => 'nullable|min:6',
'no_hp' => 'nullable|string',
'jenis_guru' => 'required|string',
'nip' => 'nullable|string',
'alamat' => 'nullable|string',
]);
DB::transaction(function () use ($request, $guru) {
// Update User Login (jika ada)
if ($guru->user) {
$userData = [
'name' => $request->nama_guru,
'email' => $request->email,
];
if ($request->filled('password')) {
$userData['password'] = Hash::make($request->password);
}
$guru->user->update($userData);
}
// Update Tabel Guru LANGSUNG
$guru->update([
'nama_guru' => $request->nama_guru,
'no_hp' => $request->no_hp,
'jenis_guru' => $request->jenis_guru,
'nip' => $request->nip,
'alamat' => $request->alamat,
]);
});
return redirect()->route('guru.index')->with('success', 'Data Guru berhasil diperbarui!');
}
public function destroy(Guru $guru)
{
$guru->delete();

View File

@ -3,6 +3,7 @@
use App\Http\Controllers\Controller;
use App\Models\Pengumuman;
use App\Models\User;
use Illuminate\Http\Request;
class PengumumanController extends Controller
@ -20,17 +21,33 @@ public function create()
public function store(Request $request)
{
// 1. Validasi Input
$request->validate([
'judul' => 'required|string|max:255',
'isi' => 'required|string',
'tanggal_mulai' => 'nullable|date',
'judul' => 'required|string|max:255',
'isi' => 'required|string',
'tanggal_mulai' => 'nullable|date',
'tanggal_selesai' => 'nullable|date|after_or_equal:tanggal_mulai',
'status' => 'boolean',
'status' => 'boolean',
]);
Pengumuman::create($request->all());
// 2. Simpan Pengumuman ke Database
$pengumuman = Pengumuman::create($request->all());
return redirect()->route('pengumuman.index')->with('success', 'Pengumuman berhasil ditambahkan.');
// 3. Ambil semua FCM token user yang tidak null (guru & wali murid)
$tokens = User::whereNotNull('fcm_token')->pluck('fcm_token')->toArray();
// 4. Trigger Broadcast Notifikasi FCM
if (count($tokens) > 0) {
$notifTitle = "Pengumuman Baru: " . $pengumuman->judul;
$notifBody = "Ada pengumuman baru dari sekolah, yuk cek sekarang!";
// Panggil method dari Controller dasar
$this->sendFCMNotification($notifTitle, $notifBody, $tokens);
}
// 5. Redirect dengan pesan sukses
return redirect()->route('pengumuman.index')
->with('success', 'Pengumuman berhasil ditambahkan dan notifikasi telah dikirim.');
}
public function edit(Pengumuman $pengumuman)

View File

@ -13,7 +13,7 @@ public function index()
{
// Ambil data penjemputan, urutkan dari yang paling baru (latest)
$logs = Penjemputan::with('siswa')->latest('waktu_jemput')->get();
// Kirim ke tampilan
return view('admin.penjemputan.index', compact('logs'));
}

View File

@ -13,29 +13,29 @@ class PerkembanganController extends Controller
{
// Halaman Utama: Tampilkan Daftar Siswa
public function index()
{
// PERBAIKAN: Ganti 'nama' menjadi 'nama_siswa'
$siswas = Siswa::orderBy('nama_siswa', 'asc')->get();
return view('admin.perkembangan.index', compact('siswas'));
}
{
// PERBAIKAN: Ganti 'nama' menjadi 'nama_siswa'
$siswas = Siswa::orderBy('nama_siswa', 'asc')->get();
return view('admin.perkembangan.index', compact('siswas'));
}
// Halaman Detail: Tampilkan Rapot (Gabungan 3 Tabel)
public function show($id)
{
$siswa = Siswa::findOrFail($id);
// 1. Ambil Data Rapot (Untuk Tabel Bawah)
$rapots = \App\Models\Rapot::where('siswa_id', $id)->orderBy('created_at', 'desc')->get();
// 2. Ambil Data Harian (Untuk Tombol/Menu Atas) - SUDAH DIAKTIFKAN
$anekdots = \App\Models\Anekdot::where('siswa_id', $id)->get();
$karyas = \App\Models\HasilKarya::where('siswa_id', $id)->get();
$ceklis = \App\Models\PenilaianCeklis::where('siswa_id', $id)->get();
// Kirim semua variabel ke View
return view('admin.perkembangan.show', compact('siswa', 'rapots', 'anekdots', 'karyas', 'ceklis'));
}
{
$siswa = Siswa::findOrFail($id);
// 1. Ambil Data Rapot (Untuk Tabel Bawah)
$rapots = \App\Models\Rapot::where('siswa_id', $id)->orderBy('created_at', 'desc')->get();
// 2. Ambil Data Harian (Untuk Tombol/Menu Atas) - SUDAH DIAKTIFKAN
$anekdots = \App\Models\Anekdot::where('siswa_id', $id)->get();
$karyas = \App\Models\HasilKarya::where('siswa_id', $id)->get();
$ceklis = \App\Models\PenilaianCeklis::where('siswa_id', $id)->get();
// Kirim semua variabel ke View
return view('admin.perkembangan.show', compact('siswa', 'rapots', 'anekdots', 'karyas', 'ceklis'));
}
// Halaman Cetak (Opsional, logika sama dengan show)
public function print($id)

View File

@ -25,24 +25,31 @@ public function create()
public function store(Request $request)
{
$request->validate([
'nis' => 'required|unique:siswas,nis',
'nisn' => 'nullable|string',
'nama_siswa' => 'required|string|max:255',
'tempat_lahir' => 'required|string',
'nis' => 'required|unique:siswas,nis',
'nisn' => 'nullable|string',
'nama_siswa' => 'required|string|max:255',
'tempat_lahir' => 'required|string',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:L,P',
'wali_murid_id' => 'required|exists:wali_murids,id',
// Alamat dihapus, karena ikut Wali Murid
// --- TAMBAHAN BARU: Validasi Titik Koordinat Peta ---
'latitude' => 'nullable|string',
'longitude' => 'nullable|string',
]);
Siswa::create([
'nis' => $request->nis,
'nisn' => $request->nisn,
'nama_siswa' => $request->nama_siswa,
'tempat_lahir' => $request->tempat_lahir,
'nis' => $request->nis,
'nisn' => $request->nisn,
'nama_siswa' => $request->nama_siswa,
'tempat_lahir' => $request->tempat_lahir,
'tanggal_lahir' => $request->tanggal_lahir,
'jenis_kelamin' => $request->jenis_kelamin,
'wali_murid_id' => $request->wali_murid_id,
// --- TAMBAHAN BARU: Simpan ke Database ---
'latitude' => $request->latitude,
'longitude' => $request->longitude,
]);
return redirect()->route('siswa.index')->with('success', 'Data Siswa berhasil ditambahkan.');
@ -60,23 +67,31 @@ public function update(Request $request, $id)
$siswa = Siswa::findOrFail($id);
$request->validate([
'nis' => 'required|unique:siswas,nis,'.$id,
'nisn' => 'nullable|string',
'nama_siswa' => 'required|string|max:255',
'tempat_lahir' => 'required|string',
'nis' => 'required|unique:siswas,nis,' . $id,
'nisn' => 'nullable|string',
'nama_siswa' => 'required|string|max:255',
'tempat_lahir' => 'required|string',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:L,P',
'wali_murid_id' => 'required|exists:wali_murids,id',
// --- TAMBAHAN BARU: Validasi Titik Koordinat Peta ---
'latitude' => 'nullable|string',
'longitude' => 'nullable|string',
]);
$siswa->update([
'nis' => $request->nis,
'nisn' => $request->nisn,
'nama_siswa' => $request->nama_siswa,
'tempat_lahir' => $request->tempat_lahir,
'nis' => $request->nis,
'nisn' => $request->nisn,
'nama_siswa' => $request->nama_siswa,
'tempat_lahir' => $request->tempat_lahir,
'tanggal_lahir' => $request->tanggal_lahir,
'jenis_kelamin' => $request->jenis_kelamin,
'wali_murid_id' => $request->wali_murid_id,
// --- TAMBAHAN BARU: Update ke Database ---
'latitude' => $request->latitude,
'longitude' => $request->longitude,
]);
return redirect()->route('siswa.index')->with('success', 'Data Siswa berhasil diperbarui!');

View File

@ -4,53 +4,54 @@
use App\Http\Controllers\Controller;
use App\Models\WaliMurid;
use App\Models\User;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
class WaliMuridController extends Controller
{
public function index()
{
$walis = WaliMurid::with('user')->latest()->get();
// PERBAIKAN DI SINI: Sesuaikan dengan nama folder 'wali'
return view('admin.wali.index', compact('walis'));
return view('admin.wali.index', compact('walis'));
}
public function create()
{
// PERBAIKAN DI SINI JUGA
return view('admin.wali.create');
$zonas = \App\Models\MasterZona::orderBy('kategori', 'desc')->get()->groupBy('kategori');
return view('admin.wali.create', compact('zonas'));
}
public function store(Request $request)
{
$request->validate([
'nama_wali' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6',
'no_hp' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6',
'no_hp' => 'required',
]);
DB::transaction(function () use ($request) {
// 1. Buat User Login
$user = User::create([
'name' => $request->nama_wali,
'email' => $request->email,
'name' => $request->nama_wali,
'email' => $request->email,
'password' => Hash::make($request->password),
'role' => 'wali_murid',
'role' => 'wali_murid',
]);
// 2. Buat Profil Wali
WaliMurid::create([
'user_id' => $user->id,
'user_id' => $user->id,
'nama_wali' => $request->nama_wali,
'no_hp' => $request->no_hp,
'alamat' => $request->alamat,
'pekerjaan' => $request->pekerjaan,
'no_hp' => $request->no_hp,
'alamat' => $request->alamat,
'pekerjaan' => $request->pekerjaan,
'master_zona_id' => $request->master_zona_id,
]);
});
@ -60,7 +61,8 @@ public function store(Request $request)
public function edit($id)
{
$data = WaliMurid::with('user')->findOrFail($id);
return view('admin.wali.edit', compact('data'));
$zonas = \App\Models\MasterZona::orderBy('kategori', 'desc')->get()->groupBy('kategori');
return view('admin.wali.edit', compact('data', 'zonas'));
}
public function update(Request $request, $id)
@ -69,25 +71,33 @@ public function update(Request $request, $id)
$request->validate([
'nama_wali' => 'required',
'email' => 'required|email|unique:users,email,' . $wali->user_id,
'no_hp' => 'required',
'email' => 'required|email|unique:users,email,' . $wali->user_id,
'password' => 'nullable|min:6',
'no_hp' => 'required',
]);
DB::transaction(function () use ($request, $wali) {
// 1. Update User Login
if ($wali->user) {
$wali->user->update([
'name' => $request->nama_wali,
'email' => $request->email,
]);
$userData = [
'name' => $request->nama_wali,
'email' => $request->email,
];
if ($request->filled('password')) {
$userData['password'] = Hash::make($request->password);
}
$wali->user->update($userData);
}
// 2. Update Profil Wali
$wali->update([
'nama_wali' => $request->nama_wali,
'no_hp' => $request->no_hp,
'alamat' => $request->alamat,
'no_hp' => $request->no_hp,
'alamat' => $request->alamat,
'master_zona_id' => $request->master_zona_id,
// 'pekerjaan' => $request->pekerjaan, // Form belum ada input pekerjaan
]);
});
@ -98,9 +108,9 @@ public function update(Request $request, $id)
public function destroy($id)
{
$wali = WaliMurid::findOrFail($id);
if($wali->user) {
$wali->user->delete();
if ($wali->user) {
$wali->user->delete();
} else {
$wali->delete();
}

View File

@ -0,0 +1,142 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class AStarController extends Controller
{
public function cariRute(Request $request)
{
$id_awal = 1; // ID PAUD Aisyiyah Kartoharjo (Start)
$awal = DB::table('titik_jalans')->where('id', $id_awal)->first();
// 1. Ambil kordinat dari HP Flutter
$latTujuan = $request->query('lat');
$lngTujuan = $request->query('lng');
if (!$latTujuan || !$lngTujuan) {
return response()->json(['success' => false, 'message' => 'Koordinat tidak valid'], 400);
}
$semua_titik = DB::table('titik_jalans')->get()->keyBy('id');
// 2. CARI TITIK TUJUAN YANG PALING COCOK BERDASARKAN KOORDINAT
$tujuan = null;
$jarakTerdekat = INF;
foreach ($semua_titik as $titik) {
$jarak = $this->hitungJarak($latTujuan, $lngTujuan, $titik->latitude, $titik->longitude);
if ($jarak < $jarakTerdekat) {
$jarakTerdekat = $jarak;
$tujuan = $titik;
}
}
if (!$tujuan) {
return response()->json(['success' => false, 'message' => 'Titik tidak ditemukan'], 404);
}
$id_tujuan = $tujuan->id; // Dapatkan ID aslinya di tabel titik_jalans
// 3. Ambil jalur jembatannya
$edges = DB::table('jalur_jalans')->get();
$graph = [];
foreach ($edges as $edge) {
$graph[$edge->titik_awal_id][] = [
'tujuan' => $edge->titik_tujuan_id,
'jarak' => $edge->jarak
];
}
// ==========================================
// PROSES A-STAR (A*)
// ==========================================
$openList = [$id_awal];
$closedList = [];
$cameFrom = [];
$gCost = [];
$fCost = [];
foreach ($semua_titik as $id => $titik) {
$gCost[$id] = INF;
$fCost[$id] = INF;
}
$gCost[$id_awal] = 0;
$fCost[$id_awal] = $this->hitungHeuristic($awal, $tujuan);
while (!empty($openList)) {
$current = null;
$lowestF = INF;
foreach ($openList as $nodeId) {
if ($fCost[$nodeId] < $lowestF) {
$lowestF = $fCost[$nodeId];
$current = $nodeId;
}
}
if ($current == $id_tujuan) {
return $this->rekonstruksiRute($cameFrom, $current, $semua_titik, $gCost[$current]);
}
$openList = array_diff($openList, [$current]);
$closedList[] = $current;
if (isset($graph[$current])) {
foreach ($graph[$current] as $neighbor) {
$neighborId = $neighbor['tujuan'];
if (in_array($neighborId, $closedList)) continue;
$tentativeGCost = $gCost[$current] + $neighbor['jarak'];
if ($tentativeGCost < $gCost[$neighborId]) {
$cameFrom[$neighborId] = $current;
$gCost[$neighborId] = $tentativeGCost;
$hCost = $this->hitungHeuristic($semua_titik[$neighborId], $tujuan);
$fCost[$neighborId] = $tentativeGCost + $hCost;
if (!in_array($neighborId, $openList)) {
$openList[] = $neighborId;
}
}
}
}
}
return response()->json(['success' => false, 'message' => 'Rute tidak ditemukan (Buntu)'], 404);
}
// --- RUMUS BANTUAN ---
private function hitungJarak($lat1, $lon1, $lat2, $lon2) {
$earthRadius = 6371000;
$latFrom = deg2rad((float)$lat1);
$lonFrom = deg2rad((float)$lon1);
$latTo = deg2rad((float)$lat2);
$lonTo = deg2rad((float)$lon2);
$latDelta = $latTo - $latFrom;
$lonDelta = $lonTo - $lonFrom;
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) + cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
return round($angle * $earthRadius);
}
private function hitungHeuristic($titikA, $titikB) {
return $this->hitungJarak($titikA->latitude, $titikA->longitude, $titikB->latitude, $titikB->longitude);
}
private function rekonstruksiRute($cameFrom, $current, $semua_titik, $jarakTotal) {
$rute = [];
while (isset($cameFrom[$current])) {
array_unshift($rute, $semua_titik[$current]);
$current = $cameFrom[$current];
}
array_unshift($rute, $semua_titik[$current]);
return response()->json(['success' => true, 'jarak_total_meter' => $jarakTotal, 'titik_rute' => $rute]);
}
}

View File

@ -42,4 +42,25 @@ public function logout()
Auth::logout();
return response()->json(['message' => 'Berhasil Logout']);
}
public function updateFcmToken(Request $request)
{
$request->validate([
'fcm_token' => 'required|string'
]);
$user = Auth::user();
if ($user) {
$user->update(['fcm_token' => $request->fcm_token]);
return response()->json([
'success' => true,
'message' => 'FCM Token updated successfully'
], 200);
}
return response()->json([
'success' => false,
'message' => 'Unauthenticated'
], 401);
}
}

View File

@ -8,10 +8,73 @@
use App\Models\HasilKarya;
use App\Models\Penjemputan;
use App\Models\PenilaianCeklis;
use App\Models\Siswa;
use Illuminate\Support\Facades\Storage;
class GuruController extends Controller
{
// Input Rapot Guru Mobile
public function storeRapot(Request $request)
{
$request->validate([
'siswa_id' => 'required',
'semester' => 'required|string',
'tahun_ajaran' => 'required|string',
'nilai_aik' => 'required|string',
'nilai_budi_pekerti' => 'required|string',
'nilai_jati_diri' => 'required|string',
'nilai_literasi_steam' => 'required|string',
'nilai_kokurikuler' => 'required|string',
// 'catatan_guru' => 'nullable|string', // Admin Rapot model does not have catatan_guru
'tinggi_badan' => 'required|numeric',
'berat_badan' => 'required|numeric',
'lingkar_kepala' => 'required|numeric',
'sakit' => 'required|numeric',
'izin' => 'required|numeric',
'alpha' => 'required|numeric',
]);
try {
// Gunakan Model Rapot (sesuai Web Admin)
$rapot = new \App\Models\Rapot();
$rapot->siswa_id = $request->siswa_id;
$rapot->semester = $request->semester;
$rapot->tahun_ajaran = $request->tahun_ajaran;
$rapot->tanggal_rapot = now()->format('Y-m-d');
// Map ke kolom Model Rapot
$rapot->narasi_agama = $request->nilai_aik;
$rapot->narasi_budi_pekerti = $request->nilai_budi_pekerti;
$rapot->narasi_jati_diri = $request->nilai_jati_diri;
$rapot->narasi_literasi = $request->nilai_literasi_steam;
$rapot->narasi_kokurikuler = $request->nilai_kokurikuler;
$rapot->tinggi_badan = $request->tinggi_badan;
$rapot->berat_badan = $request->berat_badan;
$rapot->lingkar_kepala = $request->lingkar_kepala;
$rapot->sakit = $request->sakit;
$rapot->izin = $request->izin;
$rapot->alpha = $request->alpha;
// Default Guru name from logged in user if available
$rapot->nama_guru = auth()->user()->name ?? 'Guru PAUD';
$rapot->save();
return response()->json([
'success' => true,
'message' => 'Data Rapot berhasil disimpan',
'data' => $rapot
], 201);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Gagal menyimpan rapot: ' . $e->getMessage()
], 500);
}
}
// 1. Input Catatan Anekdot
public function storeAnekdot(Request $request)
{
@ -31,6 +94,33 @@ public function storeAnekdot(Request $request)
return response()->json(['success' => true, 'message' => 'Anekdot berhasil disimpan']);
}
// Ambil Riwayat Anekdot yang dibuat oleh Guru yang login
public function getAnekdot(Request $request)
{
$data = Anekdot::with('siswa')->where('guru_id', $request->user()->id)->latest()->get();
return response()->json(['success' => true, 'data' => $data]);
}
// Ambil Riwayat Hasil Karya yang dibuat oleh Guru yang login
public function getKarya(Request $request)
{
$data = HasilKarya::with('siswa')->where('guru_id', $request->user()->id)->latest()->get();
$data->transform(function ($item) {
$item->foto_url = $item->foto ? asset('storage/' . $item->foto) : null;
return $item;
});
return response()->json(['success' => true, 'data' => $data]);
}
// Ambil Riwayat Ceklis yang dibuat oleh Guru yang login
public function getCeklis(Request $request)
{
$data = PenilaianCeklis::with('siswa')->where('guru_id', $request->user()->id)->latest()->get();
return response()->json(['success' => true, 'data' => $data]);
}
// 2. Input Hasil Karya (Upload Foto)
public function storeKarya(Request $request)
{
@ -63,21 +153,23 @@ public function storeCeklis(Request $request)
{
// Validasi disesuaikan dengan struktur tabel penilaian_ceklis kamu
$request->validate([
'siswa_id' => 'required|exists:siswas,id',
'tanggal' => 'required|date',
'indikator' => 'required|string',
'hasil' => 'required|in:BB,MB,BSH,BSB', // Validasi skala PAUD
'keterangan'=> 'nullable|string',
'siswa_id' => 'required|exists:siswas,id',
'tanggal' => 'required|date',
'aspek_perkembangan' => 'required|string',
'indikator' => 'required|string',
'hasil' => 'required|in:BB,MB,BSH,BSB', // Validasi skala PAUD
'keterangan' => 'nullable|string',
]);
// Simpan data ke database
$ceklis = PenilaianCeklis::create([
'siswa_id' => $request->siswa_id,
'guru_id' => $request->user()->id, // Ambil ID dari Guru yang login
'tanggal' => $request->tanggal,
'indikator' => $request->indikator,
'hasil' => $request->hasil,
'keterangan'=> $request->keterangan,
'siswa_id' => $request->siswa_id,
'guru_id' => $request->user()->id, // Ambil ID dari Guru yang login
'tanggal' => $request->tanggal,
'aspek_perkembangan' => $request->aspek_perkembangan,
'indikator' => $request->indikator,
'hasil' => $request->hasil,
'keterangan' => $request->keterangan,
]);
return response()->json([
@ -112,4 +204,69 @@ public function storePenjemputan(Request $request)
return response()->json(['success' => true, 'message' => 'Data penjemputan tercatat']);
}
// 4. Scan QR Penjemputan
public function scanJemput(Request $request)
{
$request->validate([
'qr_code' => 'required',
]);
$qrCodeData = $request->qr_code;
$parts = explode('_', $qrCodeData);
$idSiswa = $parts[0];
if (count($parts) > 1) {
$qrDate = $parts[1];
if ($qrDate !== date('Y-m-d')) {
return response()->json([
'success' => false,
'message' => 'QR Code Kadaluarsa! Harap gunakan QR hari ini.'
], 400);
}
} else {
return response()->json([
'success' => false,
'message' => 'Format QR Code tidak valid atau Kadaluarsa!'
], 400);
}
// Cari siswa berdasarkan qr_code (Biasanya NIS atau ID Siswa)
$siswa = Siswa::where('nis', $idSiswa)
->orWhere('id', $idSiswa)
->first();
if (!$siswa) {
return response()->json([
'success' => false,
'message' => 'QR Code tidak valid! Data siswa tidak ditemukan.'
], 404);
}
// Cek request untuk nama penjemput dan status
$statusPenjemput = $request->status_penjemput ?? 'Orang Tua / Wali';
Penjemputan::create([
'siswa_id' => $siswa->id,
'nama_penjemput' => $statusPenjemput,
'status_hubungan' => $statusPenjemput == 'Orang Tua' ? 'Orang Tua' : 'Diwakilkan',
'waktu_jemput' => now(),
]);
// --- TRIGGER FCM NOTIFICATION KE WALI MURID ---
if ($siswa->waliMurid && $siswa->waliMurid->user && $siswa->waliMurid->user->fcm_token) {
$token = $siswa->waliMurid->user->fcm_token;
$namaAnak = $siswa->nama_siswa ?? $siswa->nama_lengkap ?? 'Ananda';
$this->sendFCMNotification(
"Penjemputan Berhasil",
"Ananda {$namaAnak} telah berhasil dijemput dan diverifikasi oleh Guru.",
[$token]
);
}
return response()->json([
'success' => true,
'message' => 'Berhasil mencatat penjemputan untuk ' . ($siswa->nama_siswa ?? $siswa->nama_lengkap)
]);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\MasterZona;
use App\Models\Siswa;
class HomeVisitController extends Controller
{
/**
* Endpoint 1: Mendapatkan daftar zona, dikelompokkan berdasarkan kategori
*/
public function getZonasi()
{
// Mengambil semua data zona dan mengelompokkan berdasarkan field 'kategori'
$zonas = MasterZona::orderBy('kategori', 'desc')->get()->groupBy('kategori');
return response()->json([
'success' => true,
'message' => 'Berhasil mengambil data zonasi',
'data' => $zonas
]);
}
/**
* Endpoint 2: Mendapatkan daftar siswa berdasarkan zona_id
*/
public function getSiswaByZona($zona_id)
{
// Query Siswa yang berelasi dengan wali_murid, dimana wali_murid.master_zona_id = $zona_id
$siswas = Siswa::with('wali_murid')
->whereHas('wali_murid', function ($query) use ($zona_id) {
$query->where('master_zona_id', $zona_id);
})
->get();
return response()->json([
'success' => true,
'message' => 'Berhasil mengambil data siswa di zona tersebut',
'data' => $siswas
]);
}
}

View File

@ -9,12 +9,11 @@ class PengumumanController extends Controller
{
public function index()
{
// Sementara kita return array kosong dulu atau contoh data
$data = \App\Models\Pengumuman::where('status', true)->latest()->get();
return response()->json([
'success' => true,
'data' => [
['judul' => 'Libur Nasional', 'isi' => 'Besok sekolah libur ya bunda.'],
]
'data' => $data
]);
}
}

View File

@ -19,7 +19,7 @@ public function index()
if ($user->role == 'guru') {
// --- JIKA GURU ---
// Ambil SEMUA data siswa (untuk menu "Data Kelas")
$siswa = Siswa::with(['wali_murid', 'kelompok'])->latest()->get();
$siswa = Siswa::with(['wali_murid', 'kelompok', 'anekdots'])->latest()->get();
} else {
// --- JIKA WALI MURID ---

View File

@ -0,0 +1,205 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Siswa;
use App\Models\WaliMurid;
use App\Models\Pengumuman;
use App\Models\PenilaianCeklis;
class WaliController extends Controller
{
public function getDashboard(Request $request)
{
$user = Auth::user();
// 1. Dapatkan Profil Wali
$wali = WaliMurid::where('user_id', $user->id)->first();
if (!$wali) {
return response()->json([
'success' => false,
'message' => 'Profil Wali Murid tidak ditemukan',
], 404);
}
// 2. Dapatkan Data Siswa (Asumsi: 1 Wali -> 1 Siswa utama)
$siswa = Siswa::with('kelompok')
->where('wali_murid_id', $wali->id)
->first();
if (!$siswa) {
return response()->json([
'success' => false,
'message' => 'Data Anak tidak ditemukan untuk wali ini',
], 404);
}
// 3. Pengumuman Aktif Terbaru
$pengumuman = Pengumuman::where('status', true)
->where(function ($query) {
$query->whereNull('tanggal_mulai')
->orWhereDate('tanggal_mulai', '<=', now());
})
->where(function ($query) {
$query->whereNull('tanggal_selesai')
->orWhereDate('tanggal_selesai', '>=', now());
})
->latest()
->first();
// 4. Hitung Rekapan 6 Aspek Perkembangan dari Ceklis
$ceklis = PenilaianCeklis::where('siswa_id', $siswa->id)->get();
$aspekList = [
'Nilai Agama & Moral',
'Fisik Motorik',
'Kognitif',
'Bahasa',
'Sosial Emosional',
'Seni',
];
$progressPerkembangan = [];
foreach ($aspekList as $aspek) {
// Murni kalkulasi berdasarkan data riil yang match aspeknya
$ceklisAspek = $ceklis->where('aspek_perkembangan', $aspek);
$totalScore = 0;
$count = 0;
foreach ($ceklisAspek as $c) {
if ($c->hasil == 'BM') $totalScore += 25;
elseif ($c->hasil == 'MB') $totalScore += 50;
elseif ($c->hasil == 'BSH') $totalScore += 75;
elseif ($c->hasil == 'BSB') $totalScore += 100;
$count++;
}
// Murni kalkulasi berdasarkan data riil, 0 jika kosong
$percentage = $count > 0 ? round($totalScore / $count) : 0;
$predikat = "Perlu Bimbingan";
if ($percentage >= 76) $predikat = "Sangat Baik";
elseif ($percentage >= 51) $predikat = "Baik";
elseif ($percentage >= 26) $predikat = "Cukup";
$narasi = null;
if ($count > 0) {
$namaAnak = $siswa->nama_siswa ?? $siswa->nama_lengkap ?? 'Ananda';
$narasi = "Ananda {$namaAnak} telah menunjukkan perkembangan yang {$predikat} pada aspek {$aspek} dengan capaian {$percentage}%.";
}
$progressPerkembangan[] = [
'aspek' => $aspek,
'nilai' => $percentage,
'narasi' => $narasi
];
}
// 5. Relasi data penjemputan terakhir
$penjemputanTerakhir = \App\Models\Penjemputan::where('siswa_id', $siswa->id)
->latest()
->first();
// Return Data
return response()->json([
'success' => true,
'data' => [
'siswa' => [
'id' => $siswa->id,
'nis' => $siswa->nis,
'nama' => $siswa->nama_siswa,
'kelas' => $siswa->kelompok->nama_kelompok ?? '-',
'foto' => $siswa->foto ?? null,
],
'pengumuman' => $pengumuman,
'progress' => $progressPerkembangan,
'penjemputan_terakhir' => $penjemputanTerakhir
]
], 200);
}
// --- KHUSUS WALI MURID: Riwayat Penilaian Anak Spesifik (PRIVASI) ---
public function getRiwayatAnak($id)
{
$user = Auth::user();
// 1. Dapatkan Profil Wali
$wali = WaliMurid::where('user_id', $user->id)->first();
if (!$wali) {
return response()->json(['success' => false, 'message' => 'Profil Wali Murid tidak ditemukan'], 404);
}
// 2. Pastikan Siswa yang diminta benar-benar anak dari wali ini (Keamanan Privasi)
$siswa = Siswa::where('id', $id)->where('wali_murid_id', $wali->id)->first();
if (!$siswa) {
return response()->json(['success' => false, 'message' => 'Anda tidak memiliki akses ke data siswa ini'], 403);
}
// 3. Ambil Semua Data Riwayat Khusus Anak Ini Saja
$anekdot = \App\Models\Anekdot::with('siswa')
->where('siswa_id', $siswa->id)
->latest()
->get();
$ceklis = PenilaianCeklis::with('siswa')
->where('siswa_id', $siswa->id)
->latest()
->get();
$karya = \App\Models\HasilKarya::with('siswa')
->where('siswa_id', $siswa->id)
->latest()
->get()
->map(function ($item) {
if ($item->foto) {
$item->foto_url = url('storage/' . $item->foto);
}
return $item;
});
return response()->json([
'status' => 'success',
'data' => [
'anekdot' => $anekdot,
'ceklis' => $ceklis,
'karya' => $karya
]
], 200);
}
// --- KHUSUS WALI MURID: Rapot Penilaian Anak (Semester) ---
public function getRapotAnak($id)
{
$user = Auth::user();
$wali = WaliMurid::where('user_id', $user->id)->first();
if (!$wali) {
return response()->json(['success' => false, 'message' => 'Profil Wali Murid tidak ditemukan'], 404);
}
$siswa = Siswa::where('id', $id)->where('wali_murid_id', $wali->id)->first();
if (!$siswa) {
return response()->json(['success' => false, 'message' => 'Anda tidak memiliki akses ke data siswa ini'], 403);
}
// Ambil SEMUA riwayat rapot untuk siswa ini
$rapots = \App\Models\Rapot::with('siswa')
->where('siswa_id', $siswa->id)
->orderBy('created_at', 'desc')
->get();
if ($rapots->isEmpty()) {
return response()->json(['status' => 'error', 'message' => 'Rapot belum tersedia'], 404);
}
return response()->json([
'status' => 'success',
'data' => $rapots
], 200);
}
}

View File

@ -2,7 +2,75 @@
namespace App\Http\Controllers;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification;
use Kreait\Firebase\Messaging\AndroidConfig;
abstract class Controller
{
//
public function sendFCMNotification($title, $body, $tokens)
{
$credentialsFilePath = storage_path('firebase-auth.json');
if (!file_exists($credentialsFilePath)) {
\Log::error("FCM Service Account file not found at: " . $credentialsFilePath);
return false;
}
try {
$factory = (new Factory)->withServiceAccount($credentialsFilePath);
$messaging = $factory->createMessaging();
$responses = [];
foreach ($tokens as $fcmToken) {
$message = CloudMessage::withTarget('token', $fcmToken)
->withNotification(Notification::create($title, $body))
->withAndroidConfig(AndroidConfig::fromArray([
'priority' => 'high',
'notification' => [
'channel_id' => 'paud_notif_channel',
'sound' => 'default'
]
]));
try {
$result = $messaging->send($message);
$responses[] = $result;
\Log::info("FCM Response for token {$fcmToken}: Sukses via Kreait. Result: " . json_encode($result));
} catch (\Kreait\Firebase\Exception\MessagingException $e) {
$errorMessage = $e->getMessage();
$errors = $e->errors();
\Log::error("Kreait FCM Error for token {$fcmToken}: " . $errorMessage);
// Cek dari errors() bukan getMessage()
$errorCode = $errors['error']['details'][0]['errorCode'] ?? '';
if ($errorCode === 'UNREGISTERED' || str_contains($errorMessage, 'UNREGISTERED') || $errorCode === 'INVALID_ARGUMENT' || str_contains($errorMessage, 'invalid-argument')) {
\App\Models\User::where('fcm_token', $fcmToken)->update(['fcm_token' => null]);
\Log::warning("FCM Token dihapus karena Unregistered/Invalid: {$fcmToken}");
continue;
}
// ERROR HANDLING TEGAS SEPERTI SEBELUMNYA
dd([
'STATUS' => 'ERROR_DITOLAK_GOOGLE',
'FCM_ERROR' => $errorMessage,
'FCM_ERROR_DETAILS' => $errors,
'ERROR_CODE_DETECTED' => $errorCode,
]);
}
}
return $responses;
} catch (\Exception $e) {
\Log::error("FCM System Error: " . $e->getMessage());
dd([
'STATUS' => 'FATAL_SYSTEM_ERROR',
'ERROR' => $e->getMessage()
]);
return false;
}
}
}

View File

@ -22,4 +22,9 @@ class Anekdot extends Model
'analisis_capaian',
'foto',
];
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
}

View File

@ -19,4 +19,9 @@ class HasilKarya extends Model
'deskripsi_foto', // Pastikan ini sama persis dengan DB
'analisis_capaian'
];
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
}

10
app/Models/JalurJalan.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class JalurJalan extends Model
{
//
}

10
app/Models/Kunjungan.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Kunjungan extends Model
{
//
}

11
app/Models/MasterZona.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class MasterZona extends Model
{
protected $table = 'master_zonas';
protected $fillable = ['nama_zona', 'kategori'];
}

View File

@ -15,6 +15,7 @@ class PenilaianCeklis extends Model
protected $fillable = [
'siswa_id',
'guru_id',
'aspek_perkembangan', // Kolom aspek_perkembangan
'indikator', // Sekarang sudah jadi teks
'tanggal',
'hasil', // Ingat, di database kamu namanya 'hasil'

View File

@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PenilaianRapot extends Model
{
protected $table = 'penilaian_rapots';
protected $fillable = [
'siswa_id',
'semester',
'tahun_ajaran',
'catatan_guru',
'nama_guru',
'nilai_aspek',
'nilai_aik',
'nilai_budi_pekerti',
'nilai_jati_diri',
'nilai_literasi_steam',
'nilai_kokurikuler',
'tinggi_badan',
'berat_badan',
'lingkar_kepala',
'sakit',
'izin',
'alpha',
'file_pdf',
];
protected $casts = [
'nilai_aspek' => 'array',
];
public function siswa()
{
return $this->belongsTo(Siswa::class);
}
}

10
app/Models/TitikJalan.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TitikJalan extends Model
{
//
}

View File

@ -24,4 +24,9 @@ public function siswas()
{
return $this->hasMany(Siswa::class, 'wali_id');
}
public function zona()
{
return $this->belongsTo(MasterZona::class, 'master_zona_id');
}
}

View File

@ -8,6 +8,8 @@
"require": {
"php": "^8.2",
"doctrine/dbal": "^4.4",
"google/apiclient": "^2.19",
"kreait/firebase-php": "^7.24",
"laravel/framework": "^12.0",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.10.1",

1374
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::table('siswas', function (Blueprint $table) {
// Menambahkan kolom untuk titik koordinat GPS
$table->string('latitude')->nullable()->after('alamat');
$table->string('longitude')->nullable()->after('latitude');
});
}
public function down()
{
Schema::table('siswas', function (Blueprint $table) {
$table->dropColumn(['latitude', 'longitude']);
});
}
};

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('kunjungans', function (Blueprint $table) {
$table->id();
$table->foreignId('guru_id')->constrained('users')->onDelete('cascade'); // Relasi ke Guru (User)
$table->foreignId('siswa_id')->constrained('siswas')->onDelete('cascade'); // Relasi ke Siswa
$table->date('tanggal_visit');
$table->string('status')->default('menunggu'); // Status: menunggu, jalan, selesai
// --- Kolom untuk Penilaian (Dibuat fleksibel) ---
$table->string('materi_belajar')->nullable();
$table->string('nilai')->nullable(); // String, biar bisa diisi "BB", "BSB", atau angka 80
$table->text('catatan')->nullable();
$table->string('foto_kegiatan')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('kunjungans');
}
};

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('titik_jalans', function (Blueprint $table) {
$table->id();
$table->string('nama_titik'); // Contoh: "Simpang 3 Diponegoro", "PAUD", "Rumah Achazia"
$table->string('latitude');
$table->string('longitude');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('titik_jalans');
}
};

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jalur_jalans', function (Blueprint $table) {
$table->id();
// Titik awal persimpangan
$table->foreignId('titik_awal_id')->constrained('titik_jalans')->onDelete('cascade');
// Titik tujuan persimpangan
$table->foreignId('titik_tujuan_id')->constrained('titik_jalans')->onDelete('cascade');
// Jarak asli jalan raya (dalam meter / kilometer) -> Ini jadi G-Cost di A*
$table->double('jarak');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jalur_jalans');
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('penilaian_ceklis', function (Blueprint $table) {
$table->string('aspek_perkembangan')->nullable()->after('siswa_id'); // Atau diletakkan setelah guru_id/tanggal
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('penilaian_ceklis', function (Blueprint $table) {
$table->dropColumn('aspek_perkembangan');
});
}
};

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('penilaian_rapots', function (Blueprint $table) {
$table->id();
$table->foreignId('siswa_id')->constrained('siswas')->onDelete('cascade');
$table->string('semester');
$table->string('tahun_ajaran');
$table->text('catatan_guru')->nullable();
$table->string('nama_guru')->nullable();
$table->json('nilai_aspek'); // Untuk menyimpan skor 6 aspek PAUD
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('penilaian_rapots');
}
};

View File

@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('penilaian_rapots', function (Blueprint $table) {
$table->text('nilai_aik')->nullable();
$table->text('nilai_budi_pekerti')->nullable();
$table->text('nilai_jati_diri')->nullable();
$table->text('nilai_literasi_steam')->nullable();
$table->text('nilai_kokurikuler')->nullable();
$table->integer('tinggi_badan')->nullable();
$table->integer('berat_badan')->nullable();
$table->integer('lingkar_kepala')->nullable();
$table->integer('sakit')->nullable();
$table->integer('izin')->nullable();
$table->integer('alpha')->nullable();
$table->string('file_pdf')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('penilaian_rapots', function (Blueprint $table) {
$table->dropColumn([
'nilai_aik', 'nilai_budi_pekerti', 'nilai_jati_diri',
'nilai_literasi_steam', 'nilai_kokurikuler',
'tinggi_badan', 'berat_badan', 'lingkar_kepala',
'sakit', 'izin', 'alpha', 'file_pdf'
]);
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->text('fcm_token')->nullable()->after('remember_token');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('fcm_token');
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('master_zonas', function (Blueprint $table) {
$table->id();
$table->string('nama_zona');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('master_zonas');
}
};

View File

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('wali_murids', function (Blueprint $table) {
$table->foreignId('master_zona_id')->nullable()->constrained('master_zonas')->nullOnDelete();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('wali_murids', function (Blueprint $table) {
$table->dropForeign(['master_zona_id']);
$table->dropColumn('master_zona_id');
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('master_zonas', function (Blueprint $table) {
$table->string('kategori')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('master_zonas', function (Blueprint $table) {
$table->dropColumn('kategori');
});
}
};

View File

@ -0,0 +1,87 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class JalurJalanSeeder extends Seeder
{
public function run(): void
{
// 1. Bersihkan tabel jalur_jalans sebelum diisi
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
DB::table('jalur_jalans')->truncate();
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
// 2. Ambil semua data titik yang sudah Ghoza input
$titik = DB::table('titik_jalans')->get()->keyBy('id');
$jalur = [];
// 3. Hubungkan PAUD (ID 1) dengan Simpang A (2) dan Simpang B (3)
$jalur = array_merge($jalur, $this->buatJalurBolakBalik($titik[1], $titik[2]));
$jalur = array_merge($jalur, $this->buatJalurBolakBalik($titik[1], $titik[3]));
// 4. Hubungkan SETIAP RUMAH SISWA (ID 4 sampai 59) ke titik terdekatnya
for ($i = 4; $i <= 59; $i++) {
if(!isset($titik[$i])) continue; // Lewati kalau ID tidak ada
$rumah = $titik[$i];
// Hitung jarak dari rumah ke PAUD & Persimpangan
$jarakKePaud = $this->hitungJarak($rumah->latitude, $rumah->longitude, $titik[1]->latitude, $titik[1]->longitude);
$jarakKeSimpangA = $this->hitungJarak($rumah->latitude, $rumah->longitude, $titik[2]->latitude, $titik[2]->longitude);
$jarakKeSimpangB = $this->hitungJarak($rumah->latitude, $rumah->longitude, $titik[3]->latitude, $titik[3]->longitude);
// Cari mana yang paling dekat
$terdekat = 1;
$jarakMin = $jarakKePaud;
if ($jarakKeSimpangA < $jarakMin) {
$terdekat = 2;
$jarakMin = $jarakKeSimpangA;
}
if ($jarakKeSimpangB < $jarakMin) {
$terdekat = 3;
$jarakMin = $jarakKeSimpangB;
}
// Buat jembatan bolak-balik dari rumah ke titik terdekat tersebut
$jalur = array_merge($jalur, $this->buatJalurBolakBalik($rumah, $titik[$terdekat]));
}
// 5. Simpan semua data jembatannya ke Database!
DB::table('jalur_jalans')->insert($jalur);
$totalJalur = count($jalur);
$this->command->info("WOW! Berhasil membuat {$totalJalur} jembatan rute secara otomatis pakai Haversine Formula!");
}
// --- RUMUS BANTUAN ---
// Fungsi bikin jalur 2 arah (Pergi - Pulang)
private function buatJalurBolakBalik($titikA, $titikB) {
$jarak = $this->hitungJarak($titikA->latitude, $titikA->longitude, $titikB->latitude, $titikB->longitude);
return [
['titik_awal_id' => $titikA->id, 'titik_tujuan_id' => $titikB->id, 'jarak' => $jarak],
['titik_awal_id' => $titikB->id, 'titik_tujuan_id' => $titikA->id, 'jarak' => $jarak],
];
}
// Rumus Haversine: Mengubah Latitude & Longitude menjadi jarak Meter aslinya
private function hitungJarak($lat1, $lon1, $lat2, $lon2) {
$earthRadius = 6371000; // Radius bumi dalam meter
$latFrom = deg2rad((float)$lat1);
$lonFrom = deg2rad((float)$lon1);
$latTo = deg2rad((float)$lat2);
$lonTo = deg2rad((float)$lon2);
$latDelta = $latTo - $latFrom;
$lonDelta = $lonTo - $lonFrom;
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
return round($angle * $earthRadius); // Dibulatkan
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class MasterZonaSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
\Illuminate\Support\Facades\Schema::disableForeignKeyConstraints();
\App\Models\MasterZona::truncate();
\Illuminate\Support\Facades\Schema::enableForeignKeyConstraints();
$kotaMadiun = [
'Kartoharjo', 'Manguharjo', 'Taman'
];
$kabupatenMadiun = [
'Balerejo', 'Dagangan', 'Dolopo', 'Geger', 'Gemarang', 'Jiwan', 'Kare', 'Kebonsari', 'Madiun', 'Mejayan', 'Pilangkenceng', 'Saradan', 'Sawahan', 'Wonoasri', 'Wungu'
];
foreach ($kotaMadiun as $zona) {
\App\Models\MasterZona::create(['nama_zona' => $zona, 'kategori' => 'Kota Madiun']);
}
foreach ($kabupatenMadiun as $zona) {
\App\Models\MasterZona::create(['nama_zona' => $zona, 'kategori' => 'Kabupaten Madiun']);
}
}
}

Binary file not shown.

BIN
public/images/1 (1).jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 KiB

BIN
public/images/1 (2).jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

BIN
public/images/1 (3).jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

BIN
public/images/1 (4).jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
public/images/1 (5).jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

BIN
public/images/hero.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
resources/views.zip Normal file

Binary file not shown.

View File

@ -1,102 +1,103 @@
@extends('layouts.app')
@section('content')
<div class="bg-white p-6 rounded-lg shadow-md">
<div class="flex justify-between items-center mb-4">
<h1 class="text-xl font-semibold text-gray-700">🔐 Daftar Akun</h1>
<a href="{{ route('akun.create') }}" class="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700">+ Tambah Akun</a>
<div class="bg-white p-6 rounded-lg shadow-md">
<div class="flex justify-between items-center mb-4">
<h1 class="text-xl font-semibold text-gray-700">🔐 Daftar Akun</h1>
<a href="{{ route('akun.create') }}" class="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700">+
Tambah Akun</a>
</div>
@if(session('success'))
<div class="bg-green-100 text-green-800 p-3 rounded mb-4">{{ session('success') }}</div>
@endif
@if(session('error'))
<div class="bg-red-100 text-red-800 p-3 rounded mb-4">{{ session('error') }}</div>
@endif
<table class="w-full border border-gray-200 rounded-lg text-sm">
<thead class="bg-gray-100">
<tr>
<th class="p-3 border">#</th>
<th class="p-3 border">Nama</th>
<th class="p-3 border">Email</th>
<th class="p-3 border">Role</th>
<th class="p-3 border">Password</th>
<th class="p-3 border text-center">Aksi</th>
</tr>
</thead>
<tbody>
@forelse ($users as $i => $user)
<tr class="hover:bg-gray-50">
<td class="p-3 border">{{ $i + 1 }}</td>
<td class="p-3 border">{{ $user->name }}</td>
<td class="p-3 border">{{ $user->email }}</td>
<td class="p-3 border text-center">
@if($user->role == 'guru')
<span class="text-blue-600 font-semibold">Guru</span>
@elseif($user->role == 'wali_murid')
<span class="text-purple-600 font-semibold">Wali Murid</span>
@else
<span class="text-gray-600">Admin</span>
@endif
</td>
{{-- Kolom password (disembunyikan tapi bisa dilihat) --}}
<td class="p-3 border text-center">
<div class="relative inline-block">
<input type="password" value="123456" readonly
class="border rounded-lg px-2 py-1 text-center bg-gray-100 text-sm w-24 password-field">
<button type="button"
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500 toggle-password">
<i class="fas fa-eye"></i>
</button>
</div>
</td>
{{-- Tombol Aksi --}}
<td class="p-3 border text-center space-x-2">
<a href="{{ route('akun.edit', $user->id) }}"
class="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600">Edit</a>
<form action="{{ route('akun.reset', $user->id) }}" method="POST" class="inline">
@csrf
<button type="submit"
class="bg-yellow-500 text-white px-3 py-1 rounded hover:bg-yellow-600">Reset</button>
</form>
<form action="{{ route('akun.destroy', $user->id) }}" method="POST"
onsubmit="return confirm('Yakin mau hapus akun ini?')" class="inline">
@csrf
@method('DELETE')
<button class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600">Hapus</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center p-4 text-gray-500">Belum ada akun terdaftar.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if(session('success'))
<div class="bg-green-100 text-green-800 p-3 rounded mb-4">{{ session('success') }}</div>
@endif
@if(session('error'))
<div class="bg-red-100 text-red-800 p-3 rounded mb-4">{{ session('error') }}</div>
@endif
<table class="w-full border border-gray-200 rounded-lg text-sm">
<thead class="bg-gray-100">
<tr>
<th class="p-3 border">#</th>
<th class="p-3 border">Nama</th>
<th class="p-3 border">Email</th>
<th class="p-3 border">Role</th>
<th class="p-3 border">Password</th>
<th class="p-3 border text-center">Aksi</th>
</tr>
</thead>
<tbody>
@forelse ($users as $i => $user)
<tr class="hover:bg-gray-50">
<td class="p-3 border">{{ $i + 1 }}</td>
<td class="p-3 border">{{ $user->name }}</td>
<td class="p-3 border">{{ $user->email }}</td>
<td class="p-3 border text-center">
@if($user->role == 'guru')
<span class="text-blue-600 font-semibold">Guru</span>
@elseif($user->role == 'wali_murid')
<span class="text-purple-600 font-semibold">Wali Murid</span>
@else
<span class="text-gray-600">Admin</span>
@endif
</td>
{{-- Kolom password (disembunyikan tapi bisa dilihat) --}}
<td class="p-3 border text-center">
<div class="relative inline-block">
<input
type="password"
value="123456"
readonly
class="border rounded-lg px-2 py-1 text-center bg-gray-100 text-sm w-24 password-field"
>
<button type="button" class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500 toggle-password">
<i class="fas fa-eye"></i>
</button>
</div>
</td>
{{-- Tombol Aksi --}}
<td class="p-3 border text-center space-x-2">
<a href="{{ route('akun.edit', $user->id) }}" class="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600">Edit</a>
<form action="{{ route('akun.reset', $user->id) }}" method="POST" class="inline">
@csrf
<button type="submit" class="bg-yellow-500 text-white px-3 py-1 rounded hover:bg-yellow-600">Reset</button>
</form>
<form action="{{ route('akun.destroy', $user->id) }}" method="POST" onsubmit="return confirm('Yakin mau hapus akun ini?')" class="inline">
@csrf
@method('DELETE')
<button class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600">Hapus</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center p-4 text-gray-500">Belum ada akun terdaftar.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{-- Script show/hide password --}}
<script>
document.querySelectorAll('.toggle-password').forEach(btn => {
btn.addEventListener('click', function() {
const input = this.closest('div').querySelector('.password-field');
const icon = this.querySelector('i');
if (input.type === 'password') {
input.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
input.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
});
});
</script>
@endsection
{{-- Script show/hide password --}}
<script>
document.querySelectorAll('.toggle-password').forEach(btn => {
btn.addEventListener('click', function () {
const input = this.closest('div').querySelector('.password-field');
const icon = this.querySelector('i');
if (input.type === 'password') {
input.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
input.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
});
});
</script>
@endsection

View File

@ -1,55 +1,81 @@
@extends('layouts.app')
@section('content')
<div class="bg-white shadow-md rounded-lg p-6">
<div class="flex justify-between items-center mb-4">
<h1 class="text-xl font-semibold text-gray-700">👨‍🏫 Data Guru</h1>
<a href="{{ route('guru.create') }}" class="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700">+ Tambah Guru</a>
</div>
<div class="bg-white shadow-md rounded-2xl border border-[#e1f0e8] overflow-hidden">
<!-- Header Section -->
<div
class="flex flex-col md:flex-row justify-between items-start md:items-center bg-gradient-to-br from-green-600 to-teal-700 p-8 shadow-inner relative overflow-hidden">
<!-- Decorative inner pattern -->
<div
class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10 mix-blend-overlay">
</div>
<div class="relative z-10">
<h1 class="text-2xl font-extrabold text-white flex items-center drop-shadow">
<span class="bg-white/20 text-white p-2.5 rounded-xl mr-4 backdrop-blur-md"><i
class="fas fa-chalkboard-teacher"></i></span> Data Guru
</h1>
<p class="text-green-50 mt-2 ml-14 font-medium drop-shadow-sm">Kelola informasi tenaga pengajar PAUD.</p>
</div>
<div class="mt-6 md:mt-0 relative z-10">
<a href="{{ route('guru.create') }}"
class="inline-flex items-center gap-2 bg-white text-green-700 px-6 py-3 rounded-xl hover:bg-green-50 font-bold text-sm shadow-lg transition-all hover:-translate-y-1">
<i class="fas fa-plus"></i> Tambah Guru
</a>
</div>
</div>
<table class="w-full border-collapse">
<thead>
<tr class="bg-green-600 text-white text-left">
<th class="p-2">#</th>
<th class="p-2">Nama Guru</th>
<th class="p-2">Email</th>
<th class="p-2">No HP</th>
<th class="p-2">Jenis Guru</th>
<th class="p-2">Aksi</th>
</tr>
</thead>
<tbody>
@forelse ($gurus as $i => $guru)
<tr class="border-b hover:bg-gray-50">
<td class="p-2">{{ $i + 1 }}</td>
<td class="p-2 font-medium">{{ $guru->nama_guru ?? '-' }}</td>
<td class="p-2">{{ $guru->email ?? '-' }}</td>
<td class="p-2">{{ $guru->no_hp ?? '-' }}</td>
<td class="p-2">
{{-- Logic tampilan badge --}}
@if($guru->jenis_guru == 'guru_kelas')
<span class="bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">Guru Kelas</span>
@elseif($guru->jenis_guru == 'shadow_abk')
<span class="bg-yellow-100 text-yellow-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">Shadow ABK</span>
@else
-
@endif
</td>
<td class="p-2 space-x-2">
<a href="{{ route('guru.edit', $guru->id) }}" class="text-blue-500 hover:underline">Edit</a>
<form action="{{ route('guru.destroy', $guru->id) }}" method="POST" class="inline" onsubmit="return confirm('Yakin hapus data ini?');">
@csrf
@method('DELETE')
<button type="submit" class="text-red-500 hover:underline">Hapus</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center text-gray-500 py-4">Belum ada data guru</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="p-6">
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="bg-green-600 text-white text-left">
<th class="p-2">#</th>
<th class="p-2">Nama Guru</th>
<th class="p-2">Email</th>
<th class="p-2">No HP</th>
<th class="p-2">Jenis Guru</th>
<th class="p-2">Aksi</th>
</tr>
</thead>
<tbody>
@forelse ($gurus as $i => $guru)
<tr class="border-b hover:bg-gray-50">
<td class="p-2">{{ $i + 1 }}</td>
<td class="p-2 font-medium">{{ $guru->nama_guru ?? '-' }}</td>
<td class="p-2">{{ $guru->email ?? '-' }}</td>
<td class="p-2">{{ $guru->no_hp ?? '-' }}</td>
<td class="p-2">
{{-- Logic tampilan badge --}}
@if($guru->jenis_guru == 'guru_kelas')
<span
class="bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">Guru
Kelas</span>
@elseif($guru->jenis_guru == 'shadow_abk')
<span
class="bg-yellow-100 text-yellow-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">Shadow
ABK</span>
@else
-
@endif
</td>
<td class="p-2 space-x-2">
<a href="{{ route('guru.edit', $guru->id) }}" class="text-blue-500 hover:underline">Edit</a>
<form action="{{ route('guru.destroy', $guru->id) }}" method="POST" class="inline"
onsubmit="return confirm('Yakin hapus data ini?');">
@csrf
@method('DELETE')
<button type="submit" class="text-red-500 hover:underline">Hapus</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center text-gray-500 py-4">Belum ada data guru</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection

View File

@ -1,46 +1,71 @@
@extends('layouts.app')
@section('content')
<div class="bg-white shadow-md rounded p-6">
<div class="flex justify-between items-center mb-4">
<h1 class="text-xl font-semibold">📢 Pengumuman</h1>
<a href="{{ route('pengumuman.create') }}" class="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700">+ Tambah</a>
</div>
<div class="bg-white shadow-md rounded-2xl border border-[#e1f0e8] overflow-hidden">
<!-- Header Section -->
<div
class="flex flex-col md:flex-row justify-between items-start md:items-center bg-gradient-to-br from-green-600 to-teal-700 p-8 shadow-inner relative overflow-hidden">
<!-- Decorative inner pattern -->
<div
class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10 mix-blend-overlay">
</div>
<div class="relative z-10">
<h1 class="text-2xl font-extrabold text-white flex items-center drop-shadow">
<span class="bg-white/20 text-white p-2.5 rounded-xl mr-4 backdrop-blur-md"><i
class="fas fa-bullhorn"></i></span> Menu Pengumuman
</h1>
<p class="text-green-50 mt-2 ml-14 font-medium drop-shadow-sm">Kelola informasi dan pengumuman untuk seluruh
pihak.</p>
</div>
<div class="mt-6 md:mt-0 relative z-10">
<a href="{{ route('pengumuman.create') }}"
class="inline-flex items-center gap-2 bg-white text-green-700 px-6 py-3 rounded-xl hover:bg-green-50 font-bold text-sm shadow-lg transition-all hover:-translate-y-1">
<i class="fas fa-plus"></i> Tambah Pengumuman
</a>
</div>
</div>
<table class="w-full border-collapse">
<thead>
<tr class="bg-green-600 text-white">
<th class="p-2">#</th>
<th class="p-2">Judul</th>
<th class="p-2">Tanggal</th>
<th class="p-2">Status</th>
<th class="p-2">Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($pengumuman as $i => $p)
<tr class="border-b hover:bg-gray-50">
<td class="p-2">{{ $i+1 }}</td>
<td class="p-2 font-semibold">{{ $p->judul }}</td>
<td class="p-2">
{{ $p->tanggal_mulai }} - {{ $p->tanggal_selesai }}
</td>
<td class="p-2">
<span class="px-2 py-1 rounded text-white {{ $p->status ? 'bg-green-500' : 'bg-gray-400' }}">
{{ $p->status ? 'Aktif' : 'Nonaktif' }}
</span>
</td>
<td class="p-2 flex gap-2">
<a href="{{ route('pengumuman.edit', $p->id) }}" class="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600">Edit</a>
<form action="{{ route('pengumuman.destroy', $p->id) }}" method="POST" onsubmit="return confirm('Yakin hapus?')">
@csrf
@method('DELETE')
<button class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600">Hapus</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
<div class="p-6">
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="bg-green-600 text-white">
<th class="p-2">#</th>
<th class="p-2">Judul</th>
<th class="p-2">Tanggal</th>
<th class="p-2">Status</th>
<th class="p-2">Aksi</th>
</tr>
</thead>
<tbody>
@foreach ($pengumuman as $i => $p)
<tr class="border-b hover:bg-gray-50">
<td class="p-2">{{ $i + 1 }}</td>
<td class="p-2 font-semibold">{{ $p->judul }}</td>
<td class="p-2">
{{ $p->tanggal_mulai }} - {{ $p->tanggal_selesai }}
</td>
<td class="p-2">
<span
class="px-2 py-1 rounded text-white {{ $p->status ? 'bg-green-500' : 'bg-gray-400' }}">
{{ $p->status ? 'Aktif' : 'Nonaktif' }}
</span>
</td>
<td class="p-2 flex gap-2">
<a href="{{ route('pengumuman.edit', $p->id) }}"
class="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600">Edit</a>
<form action="{{ route('pengumuman.destroy', $p->id) }}" method="POST"
onsubmit="return confirm('Yakin hapus?')">
@csrf
@method('DELETE')
<button class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600">Hapus</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection

View File

@ -1,22 +1,24 @@
@extends('layouts.app')
@section('content')
<div class="container mx-auto px-4">
<div class="flex flex-col md:flex-row justify-between items-center mb-6 mt-6">
<div>
<h1 class="text-2xl font-bold text-gray-800">🛵 Log Penjemputan</h1>
<p class="text-gray-500 text-sm mt-1">Daftar riwayat penjemputan siswa (Real-time).</p>
</div>
<div class="flex gap-3">
<div class="bg-blue-50 text-blue-700 px-4 py-2 rounded-lg text-sm font-semibold border border-blue-100">
Hari Ini: {{ $logs->where('waktu_jemput', '>=', now()->today())->count() }}
<div class="bg-white shadow-md rounded-2xl p-6 border border-[#e1f0e8]">
<div
class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6 border-b border-[#e1f0e8] pb-4">
<div>
<h1 class="text-2xl font-extrabold text-gray-800 flex items-center">
<span class="bg-green-100 text-green-700 p-2 rounded-xl mr-3"><i class="fas fa-bus"></i></span> Log
Penjemputan
</h1>
<p class="text-sm text-gray-500 mt-1 ml-12">Daftar riwayat penjemputan siswa (Real-time).</p>
</div>
<div
class="mt-4 md:mt-0 flex items-center bg-green-50 text-green-800 px-4 py-2.5 rounded-xl font-bold text-sm border border-green-100 shadow-sm">
<i class="fas fa-calendar-day mr-2 text-green-500"></i> Hari Ini:
{{ $logs->where('waktu_jemput', '>=', now()->today())->count() }}
</div>
</div>
</div>
<div class="bg-white shadow-md rounded-xl overflow-hidden border border-gray-100">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
@ -30,56 +32,65 @@
</thead>
<tbody class="divide-y divide-gray-50">
@forelse($logs as $log)
<tr class="hover:bg-blue-50 transition duration-150 group">
<td class="p-4 whitespace-nowrap">
<div class="text-gray-800 font-semibold">
{{ \Carbon\Carbon::parse($log->waktu_jemput)->format('H:i') }} WIB
</div>
<div class="text-xs text-gray-500">
{{ \Carbon\Carbon::parse($log->waktu_jemput)->format('d M Y') }}
</div>
</td>
<td class="p-4">
<div class="font-bold text-gray-800">{{ $log->siswa->nama ?? 'Siswa Terhapus' }}</div>
<div class="text-xs text-gray-500">NIS: {{ $log->siswa->nis ?? '-' }}</div>
</td>
<tr class="hover:bg-green-50/50 transition duration-150 group">
<td class="p-4 whitespace-nowrap">
<div class="text-gray-800 font-semibold">
{{ \Carbon\Carbon::parse($log->waktu_jemput)->timezone('Asia/Jakarta')->format('H:i') }} WIB
</div>
<div class="text-xs text-gray-500">
{{ \Carbon\Carbon::parse($log->waktu_jemput)->timezone('Asia/Jakarta')->format('d M Y') }}
</div>
</td>
<td class="p-4 text-gray-700 font-medium">
{{ $log->nama_penjemput }}
</td>
<td class="p-4">
<div class="font-bold text-gray-800">{{ $log->siswa->nama_siswa ?? 'Siswa Terhapus' }}</div>
<div class="text-xs text-gray-500">NIS: {{ $log->siswa->nis ?? '-' }}</div>
</td>
<td class="p-4 text-center">
<span class="inline-block px-3 py-1 rounded-full text-xs font-bold bg-blue-100 text-blue-700 border border-blue-200">
{{ $log->status_hubungan }}
</span>
</td>
<td class="p-4 text-gray-700 font-medium">
{{ $log->nama_penjemput }}
</td>
<td class="p-4 text-center">
<form action="{{ route('penjemputan.destroy', $log->id) }}" method="POST" onsubmit="return confirm('Hapus data log ini?');">
@csrf
@method('DELETE')
<button type="submit" class="text-gray-400 hover:text-red-500 transition-colors p-2 rounded-full hover:bg-red-50" title="Hapus Riwayat">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</form>
</td>
</tr>
<td class="p-4 text-center">
<span
class="inline-block px-3 py-1 rounded-full text-xs font-bold bg-green-100 text-green-700 border border-green-200 shadow-sm">
{{ $log->status_hubungan }}
</span>
</td>
<td class="p-4 text-center">
<form action="{{ route('penjemputan.destroy', $log->id) }}" method="POST"
onsubmit="return confirm('Hapus data log ini?');">
@csrf
@method('DELETE')
<button type="submit"
class="text-gray-400 hover:text-red-500 transition-colors p-2 rounded-full hover:bg-red-50"
title="Hapus Riwayat">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="p-10 text-center text-gray-400">
<div class="flex flex-col items-center justify-center">
<svg class="w-12 h-12 mb-3 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<p class="text-sm">Belum ada data penjemputan hari ini.</p>
</div>
</td>
</tr>
<tr>
<td colspan="5" class="p-10 text-center text-gray-400">
<div class="flex flex-col items-center justify-center">
<svg class="w-12 h-12 mb-3 text-gray-300" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<p class="text-sm">Belum ada data penjemputan hari ini.</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection

View File

@ -1,70 +1,79 @@
@extends('layouts.app')
@section('content')
<div class="container mx-auto px-4">
<div class="mb-6 flex justify-between items-center">
<div>
<h1 class="text-2xl font-bold text-gray-800">📈 Laporan Perkembangan</h1>
<p class="text-gray-600 text-sm mt-1">Pilih siswa untuk melihat Catatan Anekdot, Hasil Karya, dan Ceklis.</p>
<div class="bg-white shadow-md rounded-2xl border border-[#e1f0e8] overflow-hidden">
<!-- Header Section -->
<div
class="flex flex-col md:flex-row justify-between items-start md:items-center bg-gradient-to-br from-green-600 to-teal-700 p-8 shadow-inner relative overflow-hidden">
<!-- Decorative inner pattern -->
<div
class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10 mix-blend-overlay">
</div>
<div class="relative z-10">
<h1 class="text-2xl font-extrabold text-white flex items-center drop-shadow">
<span class="bg-white/20 text-white p-2.5 rounded-xl mr-4 backdrop-blur-md"><i
class="fas fa-chart-line"></i></span> Laporan Perkembangan
</h1>
<p class="text-green-50 mt-2 ml-14 font-medium drop-shadow-sm">Pilih siswa untuk melihat Catatan Anekdot,
Hasil Karya, dan Ceklis.</p>
</div>
<div
class="mt-6 md:mt-0 relative z-10 flex items-center bg-white text-green-800 px-5 py-3 rounded-xl font-bold text-sm shadow-lg">
<i class="fas fa-users mr-2 text-green-500"></i> Total Siswa: {{ $siswas->count() }}
</div>
</div>
<div class="bg-green-100 text-green-800 px-4 py-2 rounded-lg font-bold text-sm">
Total Siswa: {{ $siswas->count() }}
<div class="p-6">
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead class="bg-green-600 text-white">
<tr>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">No</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">NIS</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">Nama Siswa</th>
<th class="py-3 px-4 text-center uppercase font-semibold text-sm">Aksi</th>
</tr>
</thead>
<tbody class="text-gray-700">
@forelse($siswas as $index => $siswa)
<tr class="hover:bg-gray-100 border-b transition duration-150">
<td class="py-3 px-4">{{ $index + 1 }}</td>
<td class="py-3 px-4">
<span class="bg-gray-100 text-gray-600 py-1 px-2 rounded text-xs font-mono">
{{ $siswa->nis ?? '-' }}
</span>
</td>
<td class="py-3 px-4 font-medium text-gray-900">
{{ $siswa->nama_siswa }}
</td>
<td class="py-3 px-4 text-center">
{{-- PERBAIKAN: Hapus 'admin.' jadi 'perkembangan.show' --}}
<a href="{{ route('perkembangan.show', $siswa->id) }}"
class="inline-flex items-center gap-1 border border-green-600 text-green-600 bg-white hover:bg-green-50 px-4 py-1.5 rounded-md text-sm font-medium transition duration-200 shadow-sm">
📂 Buka Rapot
</a>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="text-center py-8 text-gray-500">
<div class="flex flex-col items-center">
<span class="text-4xl mb-2">📭</span>
<p>Belum ada data siswa.</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="mt-4 pb-6 text-center text-xs text-gray-400">
&copy; {{ date('Y') }} PAUD Aisyiyah Monitoring System
</div>
</div>
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<table class="min-w-full bg-white">
<thead class="bg-green-600 text-white">
<tr>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">No</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">NIS</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">Nama Siswa</th>
<th class="py-3 px-4 text-center uppercase font-semibold text-sm">Kelompok</th>
<th class="py-3 px-4 text-center uppercase font-semibold text-sm">Aksi</th>
</tr>
</thead>
<tbody class="text-gray-700">
@forelse($siswas as $index => $siswa)
<tr class="hover:bg-gray-100 border-b transition duration-150">
<td class="py-3 px-4">{{ $index + 1 }}</td>
<td class="py-3 px-4">
<span class="bg-gray-100 text-gray-600 py-1 px-2 rounded text-xs font-mono">
{{ $siswa->nis ?? '-' }}
</span>
</td>
<td class="py-3 px-4 font-medium text-gray-900">
{{ $siswa->nama_siswa }}
</td>
<td class="py-3 px-4 text-center">
{{ $siswa->kelompok_id ?? '-' }}
</td>
<td class="py-3 px-4 text-center">
{{-- PERBAIKAN: Hapus 'admin.' jadi 'perkembangan.show' --}}
<a href="{{ route('perkembangan.show', $siswa->id) }}" class="inline-flex items-center gap-1 border border-green-600 text-green-600 bg-white hover:bg-green-50 px-4 py-1.5 rounded-md text-sm font-medium transition duration-200 shadow-sm">
📂 Buka Rapot
</a>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center py-8 text-gray-500">
<div class="flex flex-col items-center">
<span class="text-4xl mb-2">📭</span>
<p>Belum ada data siswa.</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-4 text-center text-xs text-gray-400">
&copy; {{ date('Y') }} PAUD Aisyiyah Monitoring System
</div>
</div>
@endsection

View File

@ -1,7 +1,7 @@
@extends('layouts.app')
@section('content')
<div class="bg-white shadow-md rounded-lg p-6 max-w-xl mx-auto mt-10">
<div class="bg-white shadow-md rounded-lg p-6 max-w-xl mx-auto mt-10 mb-10">
<div class="flex justify-between items-center mb-6">
<h1 class="text-xl font-bold text-gray-700"> Tambah Siswa Baru</h1>
<a href="{{ route('siswa.index') }}" class="text-gray-500 hover:text-gray-700">&larr; Kembali</a>
@ -74,6 +74,24 @@
</div>
</div>
<div class="mb-4 p-4 border border-blue-200 bg-blue-50 rounded-lg">
<label class="block text-gray-800 font-bold mb-2">📍 Titik Lokasi Rumah (Untuk Rute Kunjungan)</label>
<p class="text-xs text-gray-600 mb-2">Geser dan klik pada peta di bawah untuk menandai rumah siswa.</p>
<div id="map" style="height: 300px; width: 100%; border-radius: 8px; z-index: 1;"></div>
<div class="grid grid-cols-2 gap-4 mt-3">
<div>
<label class="block text-xs text-gray-500 mb-1">Latitude</label>
<input type="text" name="latitude" id="latitude" value="{{ old('latitude') }}" class="w-full border border-gray-300 rounded p-2 bg-gray-100 text-sm" readonly placeholder="Otomatis terisi">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">Longitude</label>
<input type="text" name="longitude" id="longitude" value="{{ old('longitude') }}" class="w-full border border-gray-300 rounded p-2 bg-gray-100 text-sm" readonly placeholder="Otomatis terisi">
</div>
</div>
</div>
<div class="flex justify-end mt-6 gap-3">
<button type="submit" class="bg-green-600 text-white font-semibold px-6 py-2 rounded-lg hover:bg-green-700 transition shadow-md">
💾 Simpan Data
@ -81,4 +99,41 @@
</div>
</form>
</div>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Titik tengah default: Alun-alun Jember
var map = L.map('map').setView([-7.628337, 111.525506], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);
var marker;
// Kalau ada error validasi dan ada data lama, tampilkan markernya
@if(old('latitude') && old('longitude'))
var oldLat = {{ old('latitude') }};
var oldLng = {{ old('longitude') }};
marker = L.marker([oldLat, oldLng]).addTo(map);
map.setView([oldLat, oldLng], 15);
@endif
// Event saat peta diklik
map.on('click', function(e) {
var lat = e.latlng.lat;
var lng = e.latlng.lng;
document.getElementById('latitude').value = lat;
document.getElementById('longitude').value = lng;
if (marker) {
map.removeLayer(marker);
}
marker = L.marker([lat, lng]).addTo(map);
});
});
</script>
@endsection

View File

@ -1,7 +1,7 @@
@extends('layouts.app')
@section('content')
<div class="bg-white shadow-md rounded-lg p-6 max-w-xl mx-auto mt-10">
<div class="bg-white shadow-md rounded-lg p-6 max-w-xl mx-auto mt-10 mb-10">
<div class="flex justify-between items-center mb-6">
<h1 class="text-xl font-bold text-gray-700">✏️ Edit Data Siswa</h1>
<a href="{{ route('siswa.index') }}" class="text-gray-500 hover:text-gray-700">&larr; Kembali</a>
@ -78,6 +78,24 @@
</div>
</div>
<div class="mb-4 p-4 border border-blue-200 bg-blue-50 rounded-lg">
<label class="block text-gray-800 font-bold mb-2">📍 Titik Lokasi Rumah (Untuk Rute Kunjungan)</label>
<p class="text-xs text-gray-600 mb-2">Geser dan klik pada peta untuk mengubah lokasi rumah siswa.</p>
<div id="map" style="height: 300px; width: 100%; border-radius: 8px; z-index: 1;"></div>
<div class="grid grid-cols-2 gap-4 mt-3">
<div>
<label class="block text-xs text-gray-500 mb-1">Latitude</label>
<input type="text" name="latitude" id="latitude" value="{{ old('latitude', $siswa->latitude) }}" class="w-full border border-gray-300 rounded p-2 bg-gray-100 text-sm" readonly>
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">Longitude</label>
<input type="text" name="longitude" id="longitude" value="{{ old('longitude', $siswa->longitude) }}" class="w-full border border-gray-300 rounded p-2 bg-gray-100 text-sm" readonly>
</div>
</div>
</div>
<div class="flex justify-end mt-6 gap-3">
<button type="submit" class="bg-green-600 text-white font-semibold px-6 py-2 rounded-lg hover:bg-green-700 transition shadow-md">
💾 Simpan Perubahan
@ -85,4 +103,42 @@
</div>
</form>
</div>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Cek apakah siswa sudah punya koordinat
var currentLat = {{ $siswa->latitude ?? '-7.628337' }};
var currentLng = {{ $siswa->longitude ?? '111.525506' }};
var hasLocation = {{ $siswa->latitude ? 'true' : 'false' }};
var map = L.map('map').setView([currentLat, currentLng], hasLocation ? 16 : 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);
var marker;
// Jika sudah ada koordinat, pasang marker
if (hasLocation) {
marker = L.marker([currentLat, currentLng]).addTo(map);
}
// Event saat peta diklik
map.on('click', function(e) {
var lat = e.latlng.lat;
var lng = e.latlng.lng;
document.getElementById('latitude').value = lat;
document.getElementById('longitude').value = lng;
if (marker) {
map.removeLayer(marker);
}
marker = L.marker([lat, lng]).addTo(map);
});
});
</script>
@endsection

View File

@ -1,92 +1,117 @@
@extends('layouts.app')
@section('content')
<div class="container mx-auto px-4">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-800">👶 Data Peserta Didik</h1>
<a href="{{ route('siswa.create') }}" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded shadow-lg transition duration-200">
+ Tambah Siswa
</a>
<div class="bg-white shadow-md rounded-2xl border border-[#e1f0e8] overflow-hidden">
<!-- Header Section -->
<div
class="flex flex-col md:flex-row justify-between items-start md:items-center bg-gradient-to-br from-green-600 to-teal-700 p-8 shadow-inner relative overflow-hidden">
<!-- Decorative inner pattern -->
<div
class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10 mix-blend-overlay">
</div>
<div class="relative z-10">
<h1 class="text-2xl font-extrabold text-white flex items-center drop-shadow">
<span class="bg-white/20 text-white p-2.5 rounded-xl mr-4 backdrop-blur-md"><i
class="fas fa-child"></i></span> Data Peserta Didik
</h1>
<p class="text-green-50 mt-2 ml-14 font-medium drop-shadow-sm">Manajemen data peserta didik PAUD.</p>
</div>
<div class="mt-6 md:mt-0 relative z-10">
<a href="{{ route('siswa.create') }}"
class="inline-flex items-center gap-2 bg-white text-green-700 px-6 py-3 rounded-xl hover:bg-green-50 font-bold text-sm shadow-lg transition-all hover:-translate-y-1">
<i class="fas fa-plus"></i> Tambah Siswa
</a>
</div>
</div>
<div class="p-6">
@if(session('success'))
<div class="bg-green-50/50 border border-green-200 text-green-700 p-4 mb-6 rounded-xl flex items-center shadow-sm"
role="alert">
<i class="fas fa-check-circle mr-3 text-xl text-green-500"></i>
<p class="font-medium">{{ session('success') }}</p>
</div>
@endif
<div class="overflow-x-auto">
<table class="min-w-full bg-white">
<thead class="bg-green-600 text-white">
<tr>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">No</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">NIS / NISN</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">Nama Siswa</th>
<th class="py-3 px-4 text-center uppercase font-semibold text-sm">L/P</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">TTL</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">Wali Murid / Alamat</th>
<th class="py-3 px-4 text-center uppercase font-semibold text-sm">Aksi</th>
</tr>
</thead>
<tbody class="text-gray-700">
@forelse($siswas as $index => $siswa)
<tr class="hover:bg-gray-100 border-b">
<td class="py-3 px-4">{{ $index + 1 }}</td>
<td class="py-3 px-4">
<div class="font-bold text-gray-700">{{ $siswa->nis ?? '-' }}</div>
<div class="text-xs text-gray-500">{{ $siswa->nisn ?? '-' }}</div>
</td>
<td class="py-3 px-4 font-medium">{{ $siswa->nama_siswa }}</td>
<td class="py-3 px-4 text-center">
<span
class="px-2 py-1 rounded text-xs font-bold {{ $siswa->jenis_kelamin == 'L' ? 'bg-blue-100 text-blue-800' : 'bg-pink-100 text-pink-800' }}">
{{ $siswa->jenis_kelamin }}
</span>
</td>
<td class="py-3 px-4 text-sm">
{{ $siswa->tempat_lahir }}, {{ date('d-m-Y', strtotime($siswa->tanggal_lahir)) }}
</td>
<td class="py-3 px-4">
@if($siswa->wali_murid)
<div class="text-sm font-semibold text-gray-800">{{ $siswa->wali_murid->nama_wali }}</div>
{{-- Tampilkan Alamat dari Wali Murid --}}
<div class="text-xs text-gray-500 mt-1">🏠 {{ Str::limit($siswa->wali_murid->alamat, 30) }}
</div>
@if(!empty($siswa->wali_murid->no_hp) && $siswa->wali_murid->no_hp != '-')
<div class="text-xs text-gray-500">📞 {{ $siswa->wali_murid->no_hp }}</div>
@endif
@else
<span class="text-red-500 text-xs italic bg-red-100 px-2 py-1 rounded">⚠️ Belum diset</span>
@endif
</td>
<td class="py-3 px-4 text-center">
<div class="flex items-center justify-center gap-4">
<a href="{{ route('siswa.edit', $siswa->id) }}"
class="text-blue-500 hover:text-blue-700 hover:underline transition duration-150">
Edit
</a>
<form action="{{ route('siswa.destroy', $siswa->id) }}" method="POST"
onsubmit="return confirm('Yakin ingin menghapus data siswa ini?');" class="m-0">
@csrf
@method('DELETE')
<button type="submit"
class="text-red-500 hover:text-red-700 hover:underline transition duration-150">
Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center py-6 text-gray-500">
Belum ada data siswa.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@if(session('success'))
<div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4 mb-4" role="alert">
<p>{{ session('success') }}</p>
</div>
@endif
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<table class="min-w-full bg-white">
<thead class="bg-green-600 text-white">
<tr>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">No</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">NIS / NISN</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">Nama Siswa</th>
<th class="py-3 px-4 text-center uppercase font-semibold text-sm">L/P</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">TTL</th>
<th class="py-3 px-4 text-left uppercase font-semibold text-sm">Wali Murid / Alamat</th>
<th class="py-3 px-4 text-center uppercase font-semibold text-sm">Aksi</th>
</tr>
</thead>
<tbody class="text-gray-700">
@forelse($siswas as $index => $siswa)
<tr class="hover:bg-gray-100 border-b">
<td class="py-3 px-4">{{ $index + 1 }}</td>
<td class="py-3 px-4">
<div class="font-bold text-gray-700">{{ $siswa->nis ?? '-' }}</div>
<div class="text-xs text-gray-500">{{ $siswa->nisn ?? '-' }}</div>
</td>
<td class="py-3 px-4 font-medium">{{ $siswa->nama_siswa }}</td>
<td class="py-3 px-4 text-center">
<span class="px-2 py-1 rounded text-xs font-bold {{ $siswa->jenis_kelamin == 'L' ? 'bg-blue-100 text-blue-800' : 'bg-pink-100 text-pink-800' }}">
{{ $siswa->jenis_kelamin }}
</span>
</td>
<td class="py-3 px-4 text-sm">
{{ $siswa->tempat_lahir }}, {{ date('d-m-Y', strtotime($siswa->tanggal_lahir)) }}
</td>
<td class="py-3 px-4">
@if($siswa->wali_murid)
<div class="text-sm font-semibold text-gray-800">{{ $siswa->wali_murid->nama_wali }}</div>
{{-- Tampilkan Alamat dari Wali Murid --}}
<div class="text-xs text-gray-500 mt-1">🏠 {{ Str::limit($siswa->wali_murid->alamat, 30) }}</div>
@if(!empty($siswa->wali_murid->no_hp) && $siswa->wali_murid->no_hp != '-')
<div class="text-xs text-gray-500">📞 {{ $siswa->wali_murid->no_hp }}</div>
@endif
@else
<span class="text-red-500 text-xs italic bg-red-100 px-2 py-1 rounded">⚠️ Belum diset</span>
@endif
</td>
<td class="py-3 px-4 text-center">
<div class="flex items-center justify-center gap-4">
<a href="{{ route('siswa.edit', $siswa->id) }}" class="text-blue-500 hover:text-blue-700 hover:underline transition duration-150">
Edit
</a>
<form action="{{ route('siswa.destroy', $siswa->id) }}" method="POST" onsubmit="return confirm('Yakin ingin menghapus data siswa ini?');" class="m-0">
@csrf
@method('DELETE')
<button type="submit" class="text-red-500 hover:text-red-700 hover:underline transition duration-150">
Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center py-6 text-gray-500">
Belum ada data siswa.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection

View File

@ -39,6 +39,22 @@
<label class="block text-gray-700 font-medium mb-1">Alamat Lengkap</label>
<textarea name="alamat" rows="2" class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none">{{ old('alamat') }}</textarea>
</div>
<div class="mb-2 md:col-span-2">
<label class="block text-gray-700 font-medium mb-1">Zona Wilayah</label>
<select name="master_zona_id" class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none" required>
<option value="">-- Pilih Zona Wilayah --</option>
@foreach ($zonas as $kategori => $zonaGroup)
<optgroup label="{{ $kategori }}">
@foreach ($zonaGroup as $zona)
<option value="{{ $zona->id }}" {{ old('master_zona_id') == $zona->id ? 'selected' : '' }}>
{{ $zona->nama_zona }}
</option>
@endforeach
</optgroup>
@endforeach
</select>
</div>
</div>
</div>

View File

@ -1,64 +1,90 @@
@extends('layouts.app')
@section('content')
<div class="bg-white shadow-md rounded-lg p-6 max-w-xl mx-auto mt-10">
<div class="flex justify-between items-center mb-6">
<h1 class="text-xl font-bold text-gray-700">✏️ Edit Wali Murid</h1>
<a href="{{ route('wali-murid.index') }}" class="text-gray-500 hover:text-gray-700">&larr; Kembali</a>
</div>
@if ($errors->any())
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-4" role="alert">
<p class="font-bold">Gagal Menyimpan:</p>
<ul class="list-disc list-inside">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
{{-- PERHATIKAN: DI SINI KITA PAKAI $data (BUKAN $wali_murid) --}}
<form action="{{ route('wali-murid.update', $data->id) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">Nama Wali</label>
{{-- PERHATIKAN: value="{{ old('nama_wali', $data->nama_wali) }}" --}}
<input type="text" name="nama_wali"
value="{{ old('nama_wali', $data->nama_wali) }}"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none"
required>
<div class="bg-white shadow-md rounded-lg p-6 max-w-xl mx-auto mt-10">
<div class="flex justify-between items-center mb-6">
<h1 class="text-xl font-bold text-gray-700">✏️ Edit Wali Murid</h1>
<a href="{{ route('wali-murid.index') }}" class="text-gray-500 hover:text-gray-700">&larr; Kembali</a>
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">Email (Login)</label>
<input type="email" name="email"
value="{{ old('email', $data->user?->email ?? '') }}"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none"
placeholder="Email belum didaftarkan">
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">No HP</label>
<input type="text" name="no_hp"
value="{{ old('no_hp', $data->no_hp) }}"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none">
</div>
@if ($errors->any())
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-4" role="alert">
<p class="font-bold">Gagal Menyimpan:</p>
<ul class="list-disc list-inside">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">Alamat</label>
<textarea name="alamat" rows="3"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none">{{ old('alamat', $data->alamat) }}</textarea>
</div>
{{-- PERHATIKAN: DI SINI KITA PAKAI $data (BUKAN $wali_murid) --}}
<form action="{{ route('wali-murid.update', $data->id) }}" method="POST">
@csrf
@method('PUT')
<div class="flex justify-end mt-6 gap-3">
<button type="submit"
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">Nama Wali</label>
{{-- PERHATIKAN: value="{{ old('nama_wali', $data->nama_wali) }}" --}}
<input type="text" name="nama_wali" value="{{ old('nama_wali', $data->nama_wali) }}"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none"
required>
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">No HP</label>
<input type="text" name="no_hp" value="{{ old('no_hp', $data->no_hp) }}"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none">
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">Alamat</label>
<textarea name="alamat" rows="3"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none">{{ old('alamat', $data->alamat) }}</textarea>
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">Zona Wilayah</label>
<select name="master_zona_id" class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none" required>
<option value="">-- Pilih Zona Wilayah --</option>
@foreach ($zonas as $kategori => $zonaGroup)
<optgroup label="{{ $kategori }}">
@foreach ($zonaGroup as $zona)
<option value="{{ $zona->id }}" {{ old('master_zona_id', $data->master_zona_id) == $zona->id ? 'selected' : '' }}>
{{ $zona->nama_zona }}
</option>
@endforeach
</optgroup>
@endforeach
</select>
</div>
<div class="bg-yellow-50 p-4 rounded-lg border border-yellow-200 mb-6 mt-4">
<h2 class="text-lg font-bold text-yellow-800 mb-2 flex items-center gap-2">🔐 Pengaturan Akun Login</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-gray-700 font-medium mb-1">Email Login</label>
<input type="email" name="email" value="{{ old('email', $data->user?->email ?? '') }}"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-yellow-500 focus:outline-none"
placeholder="Email belum didaftarkan">
</div>
<div>
<label class="block text-gray-700 font-medium mb-1">Password Baru</label>
<input type="password" name="password"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-yellow-500 focus:outline-none"
placeholder="Kosongkan jika tidak ganti">
<p class="text-xs text-gray-500 mt-1">*Isi hanya jika ingin mengganti password login.</p>
</div>
</div>
</div>
<div class="flex justify-end mt-6 gap-3">
<button type="submit"
class="bg-green-600 text-white font-semibold px-6 py-2 rounded-lg hover:bg-green-700 transition duration-200 shadow-md">
💾 Simpan Perubahan
</button>
</div>
</form>
</div>
💾 Simpan Perubahan
</button>
</div>
</form>
</div>
@endsection

View File

@ -1,60 +1,89 @@
@extends('layouts.app')
@section('content')
<div class="bg-white shadow-md rounded-lg p-6">
<div class="flex justify-between items-center mb-4">
<h1 class="text-xl font-semibold text-gray-700">👪 Data Wali Murid</h1>
<a href="{{ route('wali-murid.create') }}" class="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700">+ Tambah Wali</a>
</div>
<div class="bg-white shadow-md rounded-2xl border border-[#e1f0e8] overflow-hidden">
<!-- Header Section -->
<div
class="flex flex-col md:flex-row justify-between items-start md:items-center bg-gradient-to-br from-green-600 to-teal-700 p-8 shadow-inner relative overflow-hidden">
<!-- Decorative inner pattern -->
<div
class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10 mix-blend-overlay">
</div>
<div class="relative z-10">
<h1 class="text-2xl font-extrabold text-white flex items-center drop-shadow">
<span class="bg-white/20 text-white p-2.5 rounded-xl mr-4 backdrop-blur-md"><i
class="fas fa-users"></i></span> Data Wali Murid
</h1>
<p class="text-green-50 mt-2 ml-14 font-medium drop-shadow-sm">Kelola informasi orang tua / wali peserta
didik.</p>
</div>
<div class="mt-6 md:mt-0 relative z-10">
<a href="{{ route('wali-murid.create') }}"
class="inline-flex items-center gap-2 bg-white text-green-700 px-6 py-3 rounded-xl hover:bg-green-50 font-bold text-sm shadow-lg transition-all hover:-translate-y-1">
<i class="fas fa-plus"></i> Tambah Wali
</a>
</div>
</div>
{{-- TAMBAHAN: Alert Merah untuk Error --}}
@if(session('error'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
<strong class="font-bold">Error!</strong>
<span class="block sm:inline">{{ session('error') }}</span>
</div>
@endif
<div class="p-6">
{{-- Alert Merah untuk Error --}}
@if(session('error'))
<div class="bg-red-50/50 border border-red-200 text-red-700 p-4 mb-6 rounded-xl flex items-center shadow-sm"
role="alert">
<i class="fas fa-exclamation-circle mr-3 text-xl text-red-500"></i>
<div>
<strong class="font-bold block">Error!</strong>
<span>{{ session('error') }}</span>
</div>
</div>
@endif
{{-- Alert Hijau untuk Sukses --}}
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
<strong class="font-bold">Sukses!</strong>
<span class="block sm:inline">{{ session('success') }}</span>
</div>
@endif
{{-- Alert Hijau untuk Sukses --}}
@if(session('success'))
<div class="bg-green-50/50 border border-green-200 text-green-700 p-4 mb-6 rounded-xl flex items-center shadow-sm"
role="alert">
<i class="fas fa-check-circle mr-3 text-xl text-green-500"></i>
<p class="font-medium">{{ session('success') }}</p>
</div>
@endif
<table class="w-full border-collapse">
<thead>
<tr class="bg-green-600 text-white text-left">
<th class="p-2">No</th>
<th class="p-2">Nama Wali</th>
<th class="p-2">No HP</th>
<th class="p-2">Alamat</th>
<th class="p-2">Aksi</th>
</tr>
</thead>
<tbody>
@forelse ($walis as $i => $wali)
<tr class="border-b hover:bg-gray-50">
<td class="p-2">{{ $i + 1 }}</td>
<td class="p-2 font-medium">{{ $wali->nama_wali ?? '-' }}</td>
<td class="p-2">{{ $wali->no_hp ?? '-' }}</td>
<td class="p-2 text-sm text-gray-600">{{ $wali->alamat ?? '-' }}</td>
<td class="p-2 space-x-2">
<a href="{{ route('wali-murid.edit', $wali->id) }}" class="text-blue-500 hover:underline">Edit</a>
<form action="{{ route('wali-murid.destroy', $wali->id) }}" method="POST" class="inline" onsubmit="return confirm('Yakin hapus?');">
@csrf
@method('DELETE')
<button type="submit" class="text-red-500 hover:underline">Hapus</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-gray-500 py-4">Belum ada data wali murid.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="bg-green-600 text-white text-left">
<th class="p-2">No</th>
<th class="p-2">Nama Wali</th>
<th class="p-2">No HP</th>
<th class="p-2">Alamat</th>
<th class="p-2">Aksi</th>
</tr>
</thead>
<tbody>
@forelse ($walis as $i => $wali)
<tr class="border-b hover:bg-gray-50">
<td class="p-2">{{ $i + 1 }}</td>
<td class="p-2 font-medium">{{ $wali->nama_wali ?? '-' }}</td>
<td class="p-2">{{ $wali->no_hp ?? '-' }}</td>
<td class="p-2 text-sm text-gray-600">{{ $wali->alamat ?? '-' }}</td>
<td class="p-2 space-x-2">
<a href="{{ route('wali-murid.edit', $wali->id) }}"
class="text-blue-500 hover:underline">Edit</a>
<form action="{{ route('wali-murid.destroy', $wali->id) }}" method="POST" class="inline"
onsubmit="return confirm('Yakin hapus?');">
@csrf
@method('DELETE')
<button type="submit" class="text-red-500 hover:underline">Hapus</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-gray-500 py-4">Belum ada data wali murid.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection

View File

@ -31,16 +31,26 @@ class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-gree
required>
</div>
<div class="mb-4">
<label class="block text-gray-700 font-medium mb-1">Email (Login)</label>
{{-- Gunakan $data->user --}}
<input type="email" name="email"
value="{{ old('email', $data->user?->email ?? '') }}"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:outline-none"
placeholder="Email belum didaftarkan">
<div class="bg-yellow-50 p-4 rounded-lg border border-yellow-200 mb-6 mt-4">
<h2 class="text-lg font-bold text-yellow-800 mb-2 flex items-center gap-2">🔐 Pengaturan Akun Login</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-gray-700 font-medium mb-1">Email Login</label>
<input type="email" name="email"
value="{{ old('email', $data->user?->email ?? '') }}"
class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-yellow-500 focus:outline-none"
placeholder="Email belum didaftarkan">
</div>
<div>
<label class="block text-gray-700 font-medium mb-1">Password Baru</label>
<input type="password" name="password" class="w-full border border-gray-300 rounded-lg p-2 focus:ring-2 focus:ring-yellow-500 focus:outline-none" placeholder="Kosongkan jika tidak ingin ganti">
<p class="text-xs text-gray-500 mt-1">*Isi hanya jika ingin mengganti password login.</p>
</div>
</div>
</div>
<div class="mb-4">
<div class="mb-4 mt-4">
<label class="block text-gray-700 font-medium mb-1">No HP</label>
<input type="text" name="no_hp"
value="{{ old('no_hp', $data->no_hp) }}"

View File

@ -1,13 +1,27 @@
@extends('layouts.app')
@section('content')
<div class="bg-white shadow-md rounded-lg p-6">
<div class="flex justify-between items-center mb-4">
<h1 class="text-xl font-semibold text-gray-700">👪 Data Wali Murid</h1>
<a href="{{ route('wali.create') }}" class="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700">+ Tambah Wali</a>
<div class="bg-white shadow-md rounded-2xl border border-[#e1f0e8] overflow-hidden">
<!-- Header Section -->
<div class="flex flex-col md:flex-row justify-between items-start md:items-center bg-gradient-to-br from-green-600 to-teal-700 p-8 shadow-inner relative overflow-hidden">
<!-- Decorative inner pattern -->
<div class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10 mix-blend-overlay"></div>
<div class="relative z-10">
<h1 class="text-2xl font-extrabold text-white flex items-center drop-shadow">
<span class="bg-white/20 text-white p-2.5 rounded-xl mr-4 backdrop-blur-md"><i class="fas fa-users"></i></span> Data Wali Murid
</h1>
<p class="text-green-50 mt-2 ml-14 font-medium drop-shadow-sm">Kelola informasi orang tua / wali peserta didik.</p>
</div>
<div class="mt-6 md:mt-0 relative z-10">
<a href="{{ route('wali.create') }}" class="inline-flex items-center gap-2 bg-white text-green-700 px-6 py-3 rounded-xl hover:bg-green-50 font-bold text-sm shadow-lg transition-all hover:-translate-y-1">
<i class="fas fa-plus"></i> Tambah Wali
</a>
</div>
</div>
<table class="w-full border-collapse">
<div class="p-6">
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="bg-green-600 text-white text-left">
<th class="p-2">No</th>
@ -40,5 +54,7 @@
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection

View File

@ -1,52 +1,104 @@
<!DOCTYPE html>
<html lang="en">
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Simpaud</title>
<title>Login - Simpaud Kartoharjo</title>
<!-- Google Fonts: Plus Jakarta Sans -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: { sans: ['"Plus Jakarta Sans"', 'sans-serif'] },
}
}
}
</script>
<style>
body { font-family: 'Plus Jakarta Sans', sans-serif; }
.mesh-bg {
background-color: #f2f8f5;
background-image:
radial-gradient(at 40% 20%, #c5e2d4 0px, transparent 50%),
radial-gradient(at 80% 0%, #e1f0e8 0px, transparent 50%),
radial-gradient(at 0% 50%, #d4ebe0 0px, transparent 50%),
radial-gradient(at 80% 50%, #b7dac9 0px, transparent 50%),
radial-gradient(at 0% 100%, #e1f0e8 0px, transparent 50%),
radial-gradient(at 80% 100%, #c5e2d4 0px, transparent 50%),
radial-gradient(at 0% 0%, #f2f8f5 0px, transparent 50%);
}
</style>
</head>
<body class="h-screen flex items-center justify-center bg-gradient-to-br from-green-400 via-green-500 to-green-700">
<body class="h-screen flex items-center justify-center mesh-bg relative overflow-hidden">
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-5xl flex flex-col md:flex-row overflow-hidden">
<!-- Decorative floating elements -->
<div class="absolute top-10 left-10 w-72 h-72 bg-green-200/50 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-pulse"></div>
<div class="absolute bottom-10 right-10 w-72 h-72 bg-teal-200/50 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-pulse" style="animation-delay: 2s;"></div>
<div class="bg-[#f0f9f4]/80 backdrop-blur-xl rounded-[2rem] shadow-[0_8px_30px_rgb(0,0,0,0.04)] border border-green-100 w-full max-w-5xl flex flex-col md:flex-row overflow-hidden relative z-10 m-4">
<!-- Left Side (hidden di HP, tampil di desktop) -->
<div class="hidden md:flex w-1/2 bg-gradient-to-br from-green-500 to-green-700 text-white p-10 flex-col justify-center">
<h2 class="text-3xl font-bold">Selamat Datang</h2>
<p class="mt-4 text-lg">Silahkan login untuk mengakses dashboard admin Simpaud Kartoharjo</p>
<!-- Ganti logo -->
<img src="{{ asset('images/logo.png') }}" alt="logo" class="mt-10 w-40 mx-auto animate-pulse">
<div class="hidden md:flex w-1/2 bg-gradient-to-br from-green-500/90 to-teal-600/90 text-white p-12 flex-col justify-center relative overflow-hidden">
<!-- inner flare -->
<div class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10 mix-blend-overlay"></div>
<div class="relative z-10 flex flex-col items-center text-center">
<img src="{{ asset('images/logo.png') }}" alt="logo" class="w-32 h-32 object-contain mb-8 drop-shadow-lg">
<h2 class="text-4xl font-extrabold tracking-tight mb-4">Selamat Datang</h2>
<p class="text-lg text-green-50 font-medium leading-relaxed max-w-sm">
Sistem Informasi Monitoring dan Manajemen PAUD Terpadu Kartoharjo.
</p>
<div class="mt-8 flex items-center space-x-2 text-green-100 text-sm justify-center">
<span class="w-8 h-px bg-green-200/50"></span>
<span>Akses khusus staf & admin</span>
<span class="w-8 h-px bg-green-200/50"></span>
</div>
</div>
</div>
<!-- Right Side (Form Login) -->
<div class="w-full md:w-1/2 p-10 flex flex-col justify-center">
<h2 class="text-2xl font-bold text-gray-800 mb-6 text-center md:text-left">Sign In</h2>
<div class="w-full md:w-1/2 p-10 lg:p-14 flex flex-col justify-center bg-[#e8f5ed]/30 backdrop-blur-md">
<div class="mb-8">
<h2 class="text-3xl font-extrabold text-gray-800 tracking-tight">Sign In</h2>
<p class="text-gray-500 mt-2 font-medium">Masuk untuk melanjutkan ke dashboard.</p>
</div>
<form method="POST" action="{{ route('login') }}" class="space-y-5">
<form method="POST" action="{{ route('login') }}" class="space-y-6">
@csrf
<div>
<label class="block text-sm font-semibold text-gray-600">Email</label>
<input type="text" name="email" required
class="w-full mt-2 px-4 py-2 rounded-lg border focus:ring-2 focus:ring-green-400 outline-none">
<label class="block text-sm font-semibold text-gray-700 mb-2">Alamat Email</label>
<input type="email" name="email" required placeholder="admin@simpaud.com"
class="w-full px-5 py-3.5 bg-[#f0f9f4]/80 border border-green-200 rounded-xl focus:bg-white focus:ring-2 focus:ring-green-400 focus:border-green-400 outline-none transition-all shadow-sm text-gray-700">
</div>
<div>
<label class="block text-sm font-semibold text-gray-600">Password</label>
<input type="password" name="password" required
class="w-full mt-2 px-4 py-2 rounded-lg border focus:ring-2 focus:ring-green-400 outline-none">
<label class="block text-sm font-semibold text-gray-700 mb-2">Kata Sandi</label>
<input type="password" name="password" required placeholder="••••••••"
class="w-full px-5 py-3.5 bg-[#f0f9f4]/80 border border-green-200 rounded-xl focus:bg-white focus:ring-2 focus:ring-green-400 focus:border-green-400 outline-none transition-all shadow-sm text-gray-700">
</div>
@if (session('error'))
<div style="color:red;">{{ session('error') }}</div>
@endif
@if (session('error'))
<div class="p-4 bg-red-50 border-l-4 border-red-500 rounded-r-xl">
<p class="text-sm font-medium text-red-700">{{ session('error') }}</p>
</div>
@endif
<button type="submit"
class="w-full py-3 rounded-lg bg-gradient-to-r from-green-400 to-green-600 text-white font-bold shadow-lg hover:opacity-90 transition">
Login
class="w-full py-3.5 mt-4 rounded-xl bg-green-600 text-white font-bold tracking-wide shadow-[0_4px_14px_0_rgba(22,163,74,0.39)] hover:bg-green-700 hover:shadow-[0_6px_20px_rgba(22,163,74,0.23)] hover:-translate-y-0.5 transition-all duration-200">
Masuk ke Sistem
</button>
</form>
<p class="text-sm text-gray-500 mt-6 text-center md:text-left">© 2025 Simpaud Kartoharjo</p>
<div class="mt-auto pt-10 text-center md:text-left">
<p class="text-sm text-gray-400 font-medium tracking-wide">© 2026 Simpaud Kartoharjo.</p>
</div>
</div>
</div>

View File

@ -1,99 +1,149 @@
@extends('layouts.app')
@section('content')
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="py-6">
<div class="max-w-7xl mx-auto space-y-8">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">📊 Dashboard Utama</h2>
<p class="text-sm text-gray-500">Ringkasan aktivitas dan informasi sekolah.</p>
<!-- Header Section -->
<div class="flex flex-col md:flex-row justify-between items-start md:items-center bg-gradient-to-br from-green-600 to-teal-700 p-8 rounded-2xl shadow-lg relative overflow-hidden">
<!-- Decorative inner pattern -->
<div class="absolute inset-0 bg-white/5 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-20 mix-blend-overlay"></div>
<div class="relative z-10">
<h2 class="text-3xl font-extrabold text-white flex items-center drop-shadow-md">
<span class="bg-white/20 text-white p-2.5 rounded-xl mr-4 backdrop-blur-sm"><i class="fas fa-chart-pie"></i></span>
Dashboard Utama
</h2>
<p class="text-green-50 text-base mt-2 ml-14 font-medium drop-shadow">Ringkasan aktivitas dan informasi sekolah terkini.</p>
</div>
@if(Auth::user()->role == 'admin')
<a href="{{ route('pengumuman.index') }}" class="bg-indigo-600 text-white px-4 py-2 rounded shadow hover:bg-indigo-700 text-sm font-bold flex items-center gap-2">
<span>📢</span> Buat Pengumuman
</a>
<div class="mt-6 md:mt-0 relative z-10">
<a href="{{ route('pengumuman.index') }}" class="group flex items-center gap-2 bg-white text-green-700 px-6 py-3 rounded-xl shadow-lg hover:shadow-xl hover:bg-green-50 font-bold text-sm transition-all hover:-translate-y-1">
<i class="fas fa-bullhorn group-hover:scale-110 transition-transform"></i>
<span>Buat Pengumuman</span>
</a>
</div>
@endif
</div>
<div class="mb-8">
<h3 class="text-lg font-bold text-gray-700 mb-3 flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z"></path></svg>
Papan Pengumuman
</h3>
<!-- Stats Grid -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-white overflow-hidden rounded-2xl p-6 border border-[#e1f0e8] relative group hover:shadow-md transition-shadow">
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity">
<i class="fas fa-child text-6xl text-teal-600"></i>
</div>
<div class="text-green-500/80 text-xs font-bold tracking-widest uppercase mb-1">Total Siswa</div>
<div class="text-4xl font-extrabold text-gray-800 flex items-baseline">
{{ $totalSiswa }} <span class="text-sm text-gray-400 font-medium ml-2">Anak</span>
</div>
</div>
<div class="bg-white overflow-hidden rounded-2xl p-6 border border-[#e1f0e8] relative group hover:shadow-md transition-shadow">
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity">
<i class="fas fa-chalkboard-teacher text-6xl text-green-600"></i>
</div>
<div class="text-green-500/80 text-xs font-bold tracking-widest uppercase mb-1">Total Guru</div>
<div class="text-4xl font-extrabold text-gray-800 flex items-baseline">
{{ $totalGuru }} <span class="text-sm text-gray-400 font-medium ml-2">Pengajar</span>
</div>
</div>
<div class="bg-white overflow-hidden rounded-2xl p-6 border border-[#e1f0e8] relative group hover:shadow-md transition-shadow">
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity">
<i class="fas fa-door-open text-6xl text-emerald-600"></i>
</div>
<div class="text-green-500/80 text-xs font-bold tracking-widest uppercase mb-1">Total Kelas</div>
<div class="text-4xl font-extrabold text-gray-800 flex items-baseline">
{{ $totalKelas }} <span class="text-sm text-gray-400 font-medium ml-2">Ruangan</span>
</div>
</div>
</div>
@forelse($pengumuman as $info)
<div class="bg-white border-l-4 border-indigo-500 shadow-sm rounded-r-lg p-4 mb-4 relative">
<div class="flex justify-between items-start">
<div>
<h4 class="text-lg font-bold text-gray-800">{{ $info->judul }}</h4>
<p class="text-gray-600 mt-1 text-sm">{{ Str::limit($info->isi, 150) }}</p>
<div class="mt-2 text-xs text-gray-500 flex items-center bg-gray-100 w-fit px-2 py-1 rounded">
📅
@if($info->tanggal_mulai)
{{ \Carbon\Carbon::parse($info->tanggal_mulai)->format('d M Y') }}
s/d
{{ \Carbon\Carbon::parse($info->tanggal_selesai)->format('d M Y') }}
@else
Permanen
@endif
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Papan Pengumuman -->
<div class="lg:col-span-2">
<div class="flex items-center mb-4 px-2">
<div class="w-8 h-8 rounded-lg bg-green-100 text-green-600 flex items-center justify-center mr-3">
<i class="fas fa-bell"></i>
</div>
<h3 class="text-lg font-bold text-gray-700">Info & Pengumuman</h3>
</div>
<div class="space-y-4 max-h-[500px] overflow-y-auto custom-scrollbar pr-2">
@forelse($pengumuman as $info)
<div class="bg-white rounded-2xl p-5 border border-l-4 border-[#e1f0e8] border-l-green-400 hover:border-l-green-500 transition-colors shadow-sm group">
<div class="flex justify-between items-start">
<div class="flex-1 pr-4">
<h4 class="text-base font-bold text-gray-800 group-hover:text-green-700 transition-colors">{{ $info->judul }}</h4>
<p class="text-gray-500 mt-2 text-sm leading-relaxed">{{ Str::limit($info->isi, 150) }}</p>
<div class="mt-4 flex items-center gap-3">
<div class="inline-flex items-center text-xs font-medium text-green-700 bg-green-50 px-2.5 py-1 rounded-md">
<i class="far fa-calendar-alt mr-1.5 opacity-70"></i>
@if($info->tanggal_mulai)
{{ \Carbon\Carbon::parse($info->tanggal_mulai)->format('d M y') }}
-
{{ \Carbon\Carbon::parse($info->tanggal_selesai)->format('d M y') }}
@else
Info Permanen
@endif
</div>
</div>
</div>
<div class="text-[10px] uppercase tracking-wider font-bold text-gray-400 bg-gray-50 px-2 py-1 rounded-md whitespace-nowrap">
{{ $info->created_at->diffForHumans() }}
</div>
</div>
</div>
<span class="text-xs text-gray-400 italic">
{{ $info->created_at->diffForHumans() }}
</span>
@empty
<div class="bg-white/50 border border-dashed border-gray-300 rounded-2xl p-10 text-center flex flex-col items-center justify-center">
<div class="w-16 h-16 bg-gray-100 text-gray-300 rounded-full flex items-center justify-center mb-3">
<i class="fas fa-inbox text-2xl"></i>
</div>
<p class="text-gray-500 font-medium">Belum ada pengumuman aktif saat ini.</p>
</div>
@endforelse
</div>
</div>
<!-- Siswa Terbaru -->
<div class="lg:col-span-1">
<div class="bg-white rounded-2xl shadow-sm border border-[#e1f0e8] overflow-hidden flex flex-col h-full">
<div class="px-6 py-5 border-b border-[#f2f8f5] flex items-center justify-between bg-green-50/30">
<h3 class="text-sm font-bold tracking-wider uppercase text-green-800 flex items-center">
<i class="fas fa-user-plus mr-2 text-green-500"></i> Siswa Baru
</h3>
</div>
<div class="p-6 flex-1">
@if($siswaBaru->count() > 0)
<ul class="space-y-4">
@foreach($siswaBaru as $siswa)
<li class="flex items-center justify-between pb-4 border-b border-gray-50 last:border-0 last:pb-0">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-gradient-to-r from-green-100 to-teal-100 flex justify-center items-center text-green-700 font-bold text-sm shadow-sm border border-white">
{{ substr($siswa->nama_siswa, 0, 1) }}
</div>
<div class="ml-3">
<p class="text-sm font-bold text-gray-800">{{ $siswa->nama_siswa }}</p>
<p class="text-xs text-gray-500">{{ $siswa->nis }}</p>
</div>
</div>
<span class="w-2 h-2 rounded-full bg-green-400 cursor-help" title="Aktif"></span>
</li>
@endforeach
</ul>
@else
<div class="h-full flex flex-col items-center justify-center text-center py-8">
<i class="fas fa-user-slash text-gray-300 text-3xl mb-2"></i>
<p class="text-sm text-gray-500">Belum ada siswa baru.</p>
</div>
@endif
</div>
</div>
@empty
<div class="bg-gray-50 border border-gray-200 rounded-lg p-6 text-center text-gray-500">
Belum ada pengumuman aktif saat ini.
</div>
@endforelse
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div class="bg-white overflow-hidden shadow rounded-lg p-5 border-b-4 border-blue-500">
<div class="text-gray-500 text-sm font-bold uppercase">Total Siswa</div>
<div class="text-3xl font-bold text-gray-800">{{ $totalSiswa }}</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg p-5 border-b-4 border-green-500">
<div class="text-gray-500 text-sm font-bold uppercase">Total Guru</div>
<div class="text-3xl font-bold text-gray-800">{{ $totalGuru }}</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg p-5 border-b-4 border-purple-500">
<div class="text-gray-500 text-sm font-bold uppercase">Total Kelas</div>
<div class="text-3xl font-bold text-gray-800">{{ $totalKelas }}</div>
</div>
</div>
<div class="bg-white shadow rounded-lg overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200 font-bold text-gray-700">
Siswa Terbaru
</div>
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Nama</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">NIS</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Status</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($siswaBaru as $siswa)
<tr>
<td class="px-6 py-4 whitespace-nowrap">{{ $siswa->nama_siswa }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ $siswa->nis }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
Aktif
</span>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simpaud Kartoharjo - Landing Page</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Poppins', sans-serif; }
</style>
</head>
<body class="bg-emerald-50 text-gray-800">
<nav class="flex justify-between items-center py-4 px-8 bg-white shadow-sm fixed w-full top-0 z-50">
<div class="flex items-center gap-3">
<img src="{{ asset('images/logo.png') }}" alt="Logo PAUD Kartoharjo" class="w-12 h-12 object-contain">
<h1 class="text-xl font-bold text-emerald-700">Simpaud Kartoharjo</h1>
</div>
<a href="{{ route('login') }}" class="px-5 py-2 bg-emerald-600 text-white text-sm font-semibold rounded-full shadow hover:bg-emerald-700 transition flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
Masuk Admin
</a>
</nav>
<section class="container mx-auto px-8 pt-32 pb-16 flex flex-col md:flex-row items-center gap-12">
<div class="md:w-1/2">
<h2 class="text-4xl md:text-5xl font-bold text-gray-900 leading-tight mb-6">
Selamat Datang di <br><span class="text-emerald-600">Simpaud Kartoharjo</span>
</h2>
<p class="text-gray-600 mb-8 text-lg leading-relaxed">
Lembaga Pendidikan Anak Usia Dini (PAUD) terpercaya di Madiun dengan fokus pada pengembangan holistik anak. Kami menyediakan lingkungan belajar yang aman, menyenangkan, dan transparan bagi wali murid.
</p>
<div class="flex flex-wrap gap-4">
<a href="https://wa.me/6281234567890" target="_blank" class="px-6 py-3 bg-emerald-600 text-white font-semibold rounded-lg shadow-lg hover:bg-emerald-700 transition flex items-center gap-2">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51a12.8 12.8 0 0 0-.57-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413Z"/></svg>
Hubungi Admin
</a>
<a href="{{ asset('assets/apk/simpaud.apk') }}" download class="px-6 py-3 bg-white text-emerald-600 font-semibold border-2 border-emerald-600 rounded-lg shadow-lg hover:bg-emerald-50 transition flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"></path></svg>
Download APK
</a>
</div>
</div>
<div class="md:w-1/2">
<img src="{{ asset('images/hero.jpeg') }}" alt="Kegiatan PAUD" class="rounded-2xl shadow-2xl w-full object-cover h-[400px] border-4 border-white">
</div>
</section>
<section class="bg-emerald-600 py-16 mt-8">
<div class="container mx-auto px-8">
<div class="text-center mb-12 text-white">
<h3 class="text-3xl font-bold mb-2">Fitur Unggulan Simpaud</h3>
<p class="text-emerald-100">Teknologi terdepan untuk mendukung perkembangan anak dan kenyamanan orang tua</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Fitur 1: Akses Real-Time -->
<div class="bg-emerald-700 border-2 border-emerald-500 rounded-xl p-6 text-white shadow-lg hover:shadow-2xl transition transform hover:-translate-y-1">
<svg class="w-12 h-12 text-emerald-100 mb-4 stroke-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"></path></svg>
<h4 class="text-xl font-bold mb-3">Akses Real-Time</h4>
<p class="text-emerald-100 text-sm leading-relaxed">Pantau perkembangan anak kapan saja langsung dari smartphone wali murid.</p>
</div>
<!-- Fitur 2: Validasi QR Code -->
<div class="bg-emerald-700 border-2 border-emerald-500 rounded-xl p-6 text-white shadow-lg hover:shadow-2xl transition transform hover:-translate-y-1">
<svg class="w-12 h-12 text-emerald-100 mb-4 stroke-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
<h4 class="text-xl font-bold mb-3">Validasi QR Code</h4>
<p class="text-emerald-100 text-sm leading-relaxed">Sistem keamanan penjemputan siswa menggunakan scan QR Code unik.</p>
</div>
<!-- Fitur 3: Rute Cerdas A* -->
<div class="bg-emerald-700 border-2 border-emerald-500 rounded-xl p-6 text-white shadow-lg hover:shadow-2xl transition transform hover:-translate-y-1">
<svg class="w-12 h-12 text-emerald-100 mb-4 stroke-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 20l-5.447-2.724A1 1 0 003 16.382V5.618a1 1 0 011.553-.894L9 7.5m0 0l6.553-3.894A1 1 0 0117 5.618v10.764a1 1 0 01-1.553.894L9 7.5m0 0V5m6 9.5v2.764a1 1 0 01-1.553.894l-4.447-2.724"></path></svg>
<h4 class="text-xl font-bold mb-3">Rute Cerdas A*</h4>
<p class="text-emerald-100 text-sm leading-relaxed">Navigasi kunjungan (Home Visit) guru yang otomatis & efisien dengan Algoritma A-Star.</p>
</div>
<!-- Fitur 4: Rapor Digital -->
<div class="bg-emerald-700 border-2 border-emerald-500 rounded-xl p-6 text-white shadow-lg hover:shadow-2xl transition transform hover:-translate-y-1">
<svg class="w-12 h-12 text-emerald-100 mb-4 stroke-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
<h4 class="text-xl font-bold mb-3">Rapor Digital</h4>
<p class="text-emerald-100 text-sm leading-relaxed">Pencatatan penilaian ceklis dan anekdot siswa yang terintegrasi di satu pintu.</p>
</div>
</div>
</div>
</section>
<section class="container mx-auto px-8 py-20">
<h3 class="text-3xl font-bold text-center text-gray-900 mb-12">Galeri Kegiatan Kami</h3>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
<img src="{{ asset('images/1 (1).jpeg') }}" class="rounded-lg shadow hover:opacity-90 transition cursor-pointer h-64 w-full object-cover">
<img src="{{ asset('images/1 (2).jpeg') }}" class="rounded-lg shadow hover:opacity-90 transition cursor-pointer h-64 w-full object-cover">
<img src="{{ asset('images/1 (3).jpeg') }}" class="rounded-lg shadow hover:opacity-90 transition cursor-pointer h-64 w-full object-cover">
<img src="{{ asset('images/1 (4).jpeg') }}" class="rounded-lg shadow hover:opacity-90 transition cursor-pointer h-64 w-full object-cover">
<img src="{{ asset('images/1 (5).jpeg') }}" class="rounded-lg shadow hover:opacity-90 transition cursor-pointer h-64 w-full object-cover">
</div>
</section>
<footer class="bg-gray-900 text-gray-400 py-6 text-center">
<p>&copy; 2026 PAUD 'Aisyiyah Kartoharjo - Sistem Informasi Monitoring Akademik.</p>
</footer>
</body>
</html>

View File

@ -2,82 +2,250 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simpaud Kartoharjo</title>
<!-- Google Fonts: Plus Jakarta Sans -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
<!-- Tailwind Custom Config & Global CSS -->
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['"Plus Jakarta Sans"', 'sans-serif'],
},
colors: {
green: {
50: '#f2f8f5',
100: '#e1f0e8',
200: '#c5e2d4',
300: '#9dcdb9',
400: '#70b197',
500: '#4e957a',
600: '#3a7760',
700: '#30604e',
800: '#274d40',
900: '#213f35',
}
}
}
}
}
</script>
<style>
/* Global Enhancements */
body { font-family: 'Plus Jakarta Sans', sans-serif; background-color: #e5f0ea; }
/* Soften all cards globally and tint white to pastel green */
.bg-white {
background-color: #f3fbf6 !important; /* Soft green tint instead of stark white */
}
.bg-white.shadow-md {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
border-radius: 1rem;
border: 1px solid rgba(225, 240, 232, 0.8);
}
/* Beautify all inputs/selects globally */
input[type="text"], input[type="email"], input[type="password"], input[type="number"], input[type="date"], select, textarea {
border-radius: 0.75rem !important;
border-color: #c5e2d4 !important;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.02) !important;
transition: all 0.2s ease;
}
input:focus, select:focus, textarea:focus {
ring: 2px !important;
ring-color: #70b197 !important;
border-color: #70b197 !important;
outline: none !important;
}
/* Global Button Enhancements */
button, .btn, a.bg-green-600, a.bg-blue-600 {
transition: all 0.2s ease;
}
button:hover, .btn:hover, a.bg-green-600:hover {
transform: translateY(-1px);
box-shadow: 0 4px 6px -1px rgba(58, 119, 96, 0.2);
}
/* Improve HTML Table looks globally */
table.w-full {
border-radius: 0.75rem;
overflow: hidden;
border-collapse: separate;
border-spacing: 0;
border: 1px solid #e1f0e8;
}
table th {
background-color: #f2f8f5 !important;
color: #30604e !important;
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.05em;
padding: 1rem !important;
}
table td {
padding: 1rem !important;
color: #4a5568;
border-bottom: 1px solid #e1f0e8;
}
table tr:last-child td {
border-bottom: none;
}
table tr:hover td {
background-color: #fcfdfd;
}
</style>
</head>
<body class="flex bg-gray-100 font-sans">
<body class="flex bg-[#e5f0ea] text-gray-800 font-sans antialiased selection:bg-green-200 selection:text-green-900">
<!-- Sidebar -->
<aside class="w-64 bg-green-600 text-white min-h-screen p-6 shadow-xl">
<h2 class="text-2xl font-bold mb-10">Simpaud Kartoharjo</h2>
<nav class="space-y-4 text-sm">
<a href="{{ route('dashboard') }}" class="flex items-center p-2 rounded-lg hover:bg-green-700 transition">
<i class="fas fa-home mr-3"></i> Home
<aside class="w-64 bg-[#f1f8f4] border-r border-[#d4ebd8] flex flex-col transition-all duration-300 shadow-sm z-20">
<div class="p-6 flex items-center border-b border-[#f2f8f5]">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-green-400 to-green-600 flex items-center justify-center shadow-lg text-white font-bold text-xl mr-3">
S
</div>
<h2 class="text-xl font-extrabold text-gray-800 tracking-tight">Simpaud</h2>
</div>
<nav class="flex-1 p-4 space-y-6 overflow-y-auto custom-scrollbar">
<!-- Main Menu -->
<div>
<p class="px-3 text-xs font-bold text-green-500 uppercase tracking-wider mb-2">Menu Utama</p>
<a href="{{ route('dashboard') }}" class="flex items-center px-3 py-2.5 text-sm font-medium rounded-xl text-green-900 bg-green-50 hover:bg-green-100 transition-colors shadow-sm">
<i class="fas fa-home w-5 h-5 mr-3 flex items-center justify-center text-green-600"></i>
Dashboard
</a>
@if(auth()->user()->role == 'admin')
<div>
<p class="font-semibold uppercase text-xs mb-2 text-green-200">Data Master</p>
<ul class="ml-4 space-y-1">
<li><a href="{{ route('guru.index') }}" class="block p-2 hover:bg-green-700 rounded text-white">👨‍🏫 Guru</a></li>
<li><a href="{{ route('wali-murid.index') }}" class="block p-2 hover:bg-green-700 rounded text-white">👪 Wali Murid</a></li>
<li><a href="{{ route('siswa.index') }}" class="block p-2 hover:bg-green-700 rounded text-white">🧒 Peserta Didik</a></li>
</ul>
</div>
@if(auth()->user()->role == 'admin')
<!-- Data Master -->
<div>
<p class="px-3 text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Data Master</p>
<ul class="space-y-1">
<li><a href="{{ route('guru.index') }}" class="group flex items-center px-3 py-2 text-sm font-medium rounded-xl text-gray-600 hover:text-green-800 hover:bg-green-50/50 transition-colors">
<i class="fas fa-chalkboard-teacher w-5 h-5 mr-3 text-gray-400 flex items-center justify-center group-hover:text-green-500"></i> Guru</a>
</li>
<li><a href="{{ route('wali-murid.index') }}" class="group flex items-center px-3 py-2 text-sm font-medium rounded-xl text-gray-600 hover:text-green-800 hover:bg-green-50/50 transition-colors">
<i class="fas fa-users w-5 h-5 mr-3 text-gray-400 flex items-center justify-center group-hover:text-green-500"></i> Wali Murid</a>
</li>
<li><a href="{{ route('siswa.index') }}" class="group flex items-center px-3 py-2 text-sm font-medium rounded-xl text-gray-600 hover:text-green-800 hover:bg-green-50/50 transition-colors">
<i class="fas fa-child w-5 h-5 mr-3 text-gray-400 flex items-center justify-center group-hover:text-green-500"></i> Peserta Didik</a>
</li>
</ul>
</div>
@endif
<!-- Laporan & Info -->
<div>
<p class="px-3 text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Laporan & Info</p>
<ul class="space-y-1">
<li>
<a href="{{ route('admin.perkembangan.index') }}" class="group flex items-center px-3 py-2 text-sm font-medium rounded-xl text-gray-600 hover:text-green-800 hover:bg-green-50/50 transition-colors">
<i class="fas fa-chart-line w-5 h-5 mr-3 text-gray-400 flex items-center justify-center group-hover:text-green-500"></i> Perkembangan
</a>
</li>
@if(auth()->user()->role == 'admin')
<li>
<a href="{{ route('pengumuman.index') }}" class="group flex items-center px-3 py-2 text-sm font-medium rounded-xl text-gray-600 hover:text-green-800 hover:bg-green-50/50 transition-colors">
<i class="fas fa-bullhorn w-5 h-5 mr-3 text-gray-400 flex items-center justify-center group-hover:text-green-500"></i> Pengumuman
</a>
</li>
@endif
<li>
<a href="{{ route('penjemputan.index') }}" class="group flex items-center px-3 py-2 text-sm font-medium rounded-xl text-gray-600 hover:text-green-800 hover:bg-green-50/50 transition-colors">
<i class="fas fa-bus w-5 h-5 mr-3 text-gray-400 flex items-center justify-center group-hover:text-green-500"></i> Penjemputan
</a>
</li>
</ul>
</div>
</nav>
<div class="p-4 border-t border-[#e1f0e8]">
<div class="flex items-center p-3 bg-gradient-to-r from-green-50 to-white rounded-xl shadow-sm border border-green-100">
<div class="w-9 h-9 rounded-full bg-green-200 flex items-center justify-center text-green-800 font-bold mr-3 border-2 border-white shadow-sm">
{{ substr(Auth::user()->name, 0, 1) }}
</div>
@endif
<div>
<p class="font-semibold uppercase text-xs mb-2 text-green-200">Laporan & Info</p>
<div class="space-y-1">
<a href="{{ route('admin.perkembangan.index') }}" class="flex items-center p-2 rounded-lg hover:bg-green-700 transition">
<i class="fas fa-chart-line mr-3"></i> Laporan Perkembangan
</a>
@if(auth()->user()->role == 'admin')
<a href="{{ route('pengumuman.index') }}" class="flex items-center p-2 rounded-lg hover:bg-green-700 transition">
<i class="fas fa-bullhorn mr-3"></i> Menu Pengumuman
</a>
@endif
<a href="{{ route('penjemputan.index') }}" class="flex items-center p-2 rounded-lg hover:bg-green-700 transition">
<i class="fas fa-bus mr-3"></i> Penjemputan
</a>
</div>
<div class="overflow-hidden">
<p class="text-sm font-bold text-gray-800 truncate">{{ Auth::user()->name }}</p>
<p class="text-xs text-green-600 uppercase font-semibold">{{ Auth::user()->role }}</p>
</div>
</nav>
</aside>
</div>
</div>
</aside>
<!-- Main Content -->
<div class="flex-1 flex flex-col">
<div class="flex-1 flex flex-col h-screen overflow-hidden relative">
<!-- Navbar -->
<header class="flex items-center justify-between bg-white px-6 py-4 shadow">
Hai, {{ Auth::user()->name }}
<div class="flex items-center space-x-4">
<input type="text" placeholder="Search Class, Documents, Activities..."
class="px-4 py-2 rounded-lg border focus:ring-2 focus:ring-green-400 w-72 text-sm">
<header class="flex items-center justify-between bg-[#f1f8f4]/80 backdrop-blur-lg px-8 py-4 border-b border-[#d4ebd8] z-10 sticky top-0">
<div class="flex items-center text-gray-700">
<h1 class="text-lg font-semibold tracking-wide flex items-center">
👋 <span class="ml-2">Halo, <span class="text-green-700">{{ Auth::user()->name }}</span></span>
</h1>
</div>
<div class="flex items-center space-x-6">
<!-- Search Input with Icon -->
<div class="relative hidden md:block">
<i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 text-sm"></i>
<input type="text" id="global-search" placeholder="Cari data di tabel..."
class="pl-10 pr-4 py-2 bg-gray-50/50 border border-gray-200 focus:bg-white rounded-xl w-64 text-sm transition-all focus:w-80 shadow-sm">
</div>
<form action="{{ route('logout') }}" method="POST">
@csrf
<button type="submit" class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600">
Logout
<button type="submit" class="flex items-center justify-center px-4 py-2 rounded-xl bg-red-50 text-red-500 hover:bg-red-500 hover:text-white transition-all font-medium text-sm shadow-sm" title="Logout">
<i class="fas fa-sign-out-alt mr-2"></i> Keluar
</button>
</form>
</div>
</header>
<!-- Content -->
<main class="p-6">
<!-- Content Area -->
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-[#e5f0ea] p-8">
@yield('content')
@if (session('success'))
<div class="bg-green-100 text-green-800 p-3 rounded mb-4">
{{ session('success') }}
<div class="fixed bottom-6 right-6 bg-green-600 text-white px-6 py-4 rounded-xl shadow-2xl flex items-center animate-bounce border-2 border-green-400 z-50">
<i class="fas fa-check-circle mr-3 text-xl"></i>
<span class="font-semibold">{{ session('success') }}</span>
</div>
@endif
</main>
</div>
</body>
<script>
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('global-search');
if(searchInput) {
searchInput.addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
const rows = document.querySelectorAll('tbody tr');
rows.forEach(row => {
// Jangan sembunyikan baris "Belum ada data pesan"
if(row.querySelector('td[colspan]')) return;
const text = row.textContent.toLowerCase();
if(text.includes(query)) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
});
}
});
</script>
</html>

View File

@ -1,28 +1,85 @@
<!DOCTYPE html>
<html lang="en">
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Splash Screen</title>
<title>Simpaud - Monitoring & Management PAUD</title>
<!-- Google Fonts: Plus Jakarta Sans -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: { sans: ['"Plus Jakarta Sans"', 'sans-serif'] },
}
}
}
</script>
<style>
body { font-family: 'Plus Jakarta Sans', sans-serif; }
.mesh-bg {
background-color: #f2f8f5;
background-image:
radial-gradient(at 40% 20%, #c5e2d4 0px, transparent 50%),
radial-gradient(at 80% 0%, #e1f0e8 0px, transparent 50%),
radial-gradient(at 0% 50%, #d4ebe0 0px, transparent 50%),
radial-gradient(at 80% 50%, #b7dac9 0px, transparent 50%),
radial-gradient(at 0% 100%, #e1f0e8 0px, transparent 50%),
radial-gradient(at 80% 100%, #c5e2d4 0px, transparent 50%),
radial-gradient(at 0% 0%, #f2f8f5 0px, transparent 50%);
}
@keyframes float {
0% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0px); }
}
.animate-float { animation: float 4s ease-in-out infinite; }
@keyframes fadeOut {
to { opacity: 0; visibility: hidden; }
0% { opacity: 1; filter: blur(0); transform: scale(1); }
100% { opacity: 0; filter: blur(10px); transform: scale(1.1); visibility: hidden; }
}
.fade-out {
animation: fadeOut 1s ease-in-out forwards;
animation-delay: 2.5s;
animation: fadeOut 0.8s cubic-bezier(0.4, 0, 0.2, 1) forwards;
animation-delay: 2.2s;
}
@keyframes fadeInScale {
0% { opacity: 0; transform: scale(0.9); }
100% { opacity: 1; transform: scale(1); }
}
.animate-entrance {
animation: fadeInScale 1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
</style>
</head>
<body class="h-screen flex items-center justify-center bg-gradient-to-br from-green-400 via-green-500 to-green-700">
<body class="h-screen flex items-center justify-center mesh-bg overflow-hidden relative">
<div class="text-center fade-out">
<div class="animate-bounce">
<!-- Decorative modern blobs -->
<div class="absolute top-[-10%] left-[-10%] w-96 h-96 bg-green-200/40 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-pulse"></div>
<div class="absolute bottom-[-10%] right-[-10%] w-96 h-96 bg-teal-200/40 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-pulse" style="animation-delay: 1s;"></div>
<div class="text-center fade-out animate-entrance z-10">
<div class="relative inline-block animate-float mb-6">
<div class="absolute inset-0 bg-green-400 blur-xl opacity-30 rounded-full"></div>
<!-- Ganti logo disini -->
<img src="{{ asset('images/logo.png') }}" alt="logo" class="h-28 w-28 mx-auto rounded-full object-cover shadow-lg border-4 border-white">
<h1 class="mt-6 text-4xl font-extrabold text-white drop-shadow-lg">Simpaud Kartoharjo</h1>
<p class="mt-2 text-lg text-green-100">Monitoring & Management PAUD</p>
<img src="{{ asset('images/logo.png') }}" alt="Simpaud Logo" class="relative h-32 w-32 mx-auto rounded-3xl object-cover shadow-[0_8px_30px_rgb(0,0,0,0.08)] border border-white/60 bg-white/40 backdrop-blur-sm p-2">
</div>
<h1 class="text-4xl md:text-5xl font-extrabold tracking-tight text-gray-800 drop-shadow-sm">
<span class="text-transparent bg-clip-text bg-gradient-to-r from-green-600 to-teal-600">Simpaud</span>
</h1>
<p class="mt-3 text-lg md:text-xl font-medium text-gray-600 tracking-wide opacity-80">Monitoring & Management PAUD</p>
<div class="mt-10 flex justify-center space-x-2">
<div class="w-2 h-2 bg-green-400 rounded-full animate-bounce" style="animation-delay: 0s;"></div>
<div class="w-2 h-2 bg-green-400 rounded-full animate-bounce" style="animation-delay: 0.2s;"></div>
<div class="w-2 h-2 bg-green-400 rounded-full animate-bounce" style="animation-delay: 0.4s;"></div>
</div>
</div>
<script>

View File

@ -10,6 +10,8 @@
use App\Http\Controllers\Api\LaporanController;
use App\Http\Controllers\Api\GuruController;
use App\Http\Controllers\Api\PenjemputanController;
use App\Http\Controllers\Api\AStarController;
use App\Http\Controllers\Api\HomeVisitController;
/*
|--------------------------------------------------------------------------
@ -26,7 +28,8 @@
// --- KHUSUS WALI MURID (Lihat Data) ---
// Wali melihat daftar anaknya
use App\Http\Controllers\Api\WaliController;
// ==========================================
// 2. AREA TERKUNCI (BUTUH TOKEN)
// ==========================================
@ -35,6 +38,7 @@
// --- UMUM ---
Route::get('/siswa-saya', [SiswaController::class, 'index']); // Pindah ke sini biar bisa baca Auth::user()
Route::post('/logout', [AuthController::class, 'logout']);
Route::post('/update-fcm-token', [AuthController::class, 'updateFcmToken']);
Route::get('/user', function (Request $request) {
return $request->user(); // Cek siapa yang login
});
@ -45,15 +49,31 @@
Route::get('/karya', [LaporanController::class, 'getKarya']);
Route::get('/penjemputan', [LaporanController::class, 'getPenjemputan']);
// --- KHUSUS WALI MURID ---
Route::get('/wali/dashboard', [WaliController::class, 'getDashboard']);
Route::get('/wali/riwayat-anak/{id}', [WaliController::class, 'getRiwayatAnak']);
Route::get('/wali/rapot-anak/{id}', [WaliController::class, 'getRapotAnak']);
// --- KHUSUS GURU (Input Data) ---
// Nanti kalau Guru login di HP untuk input data:
Route::get('/guru/anekdot', [GuruController::class, 'getAnekdot']); // TAMBAHAN INI
Route::get('/guru/karya', [GuruController::class, 'getKarya']);
Route::get('/guru/ceklis', [GuruController::class, 'getCeklis']);
Route::post('/guru/anekdot', [GuruController::class, 'storeAnekdot']);
Route::post('/guru/karya', [GuruController::class, 'storeKarya']);
Route::post('/guru/penjemputan', [GuruController::class, 'storePenjemputan']);
Route::post('/guru/ceklis', [GuruController::class, 'storeCeklis']);
Route::post('/guru/rapot', [GuruController::class, 'storeRapot']);
// RUTE PENJEMPUTAN BARU
Route::post('/penjemputan', [PenjemputanController::class, 'store']);
Route::post('/guru/scan-jemput', [GuruController::class, 'scanJemput']);
Route::get('/rute-astar', [AStarController::class, 'cariRute']);
// RUTE HOME VISIT / ZONASI
Route::get('/zonasi', [HomeVisitController::class, 'getZonasi']);
Route::get('/home-visit/zona/{zona_id}', [HomeVisitController::class, 'getSiswaByZona']);
});

View File

@ -19,10 +19,10 @@
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
// Splash
// Landing Page - Simpaud Kartoharjo (Root Route)
Route::get('/', function () {
return view('splash');
});
return view('landing');
})->name('landing');
Route::middleware('auth')->group(function () {
@ -82,4 +82,30 @@
Route::get('/rapot/{id}/print', [App\Http\Controllers\Admin\RapotController::class, 'print'])->name('rapot.print');
});
Route::get('/sinkron-kordinat', function () {
// Ambil data dari ID 4 sampai akhir (karena ID 1-3 itu PAUD dan Simpang)
$titikRumah = DB::table('titik_jalans')->where('id', '>=', 4)->get();
$berhasil = 0;
foreach ($titikRumah as $titik) {
// Hilangkan kata "Rumah " biar sisa nama siswanya aja
$namaSiswa = str_replace('Rumah ', '', $titik->nama_titik);
$namaSiswa = trim($namaSiswa);
// Update latitude & longitude di tabel siswas yang namanya mirip
$update = DB::table('siswas')
->where('nama_siswa', 'LIKE', '%' . $namaSiswa . '%')
->update([
'latitude' => $titik->latitude,
'longitude' => $titik->longitude
]);
if($update) {
$berhasil++;
}
}
return "Selesai Bosku! Berhasil menyinkronkan $berhasil data koordinat siswa!";
});
});

View File

@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "simpaudkartoharjo",
"private_key_id": "d5856c1910f178842deff72bb591d8480181c6fc",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYK3IGvE4dnkiy\nOhLyBDZGXigJ3Eu3nqUCixbSs/tBvURF8YR1Wxxmadw7v/ozJNwy5/aHo1wdd0j+\nFrIv5JVhpAWuO7OIXvodtRKYONmyPj9B9ioV9yGsIYsqj3942etbfyUVDdOpwniO\nAQBnJanu4pg6yoAGXLAjo//AuUMI5b+3NUwcfCva1dZgrLvz+pWZclN8nwOx/npU\nXj7SoWveSQXBqnhJFm5TLlacj9OHVW4TWx7DoAC5ePpVvXJGGC7s8xUgdlRXyoPL\n6DrFZVHHYmrdzSSVtgp8Hr0v0iLiKu8y7v79uN7jOyxZkw9ukzwA025TCQxtifSr\n/vEO0l3xAgMBAAECggEAWOGgLEHPsqDqzhFPyd/OeydqugcwwMqDmWScGT10kUaj\nZEjWSDSSnE9phM2+L7o3qdyzgni6PslVo1eso0GO5Es/JLEac/dtSrqxbxJs/DtY\n4iO3kTmASFiwcmr3JDch7Qh9tEbvoSy7SdQCmOalFPppUj8B3dvNdpIPbewesnCX\naNLHx1FkokKnrBD8H9oqSj97q6DutDM8BZpn9xxvZd9vDgFH1PYLO8FM8m7GMR3p\nJEU0Xs1gk9DfC9CQGcmF4WSqY0CDtvC9fscIcLTNG83fp0/DO74Wb6r6RT4q1lFb\nifYBmTiOuQT9J7tMMGGhkPXyQYW6Nw18TyOphE67GwKBgQDuI4FfXot4EcRVU/Lp\nm2BzD8aNAFZcUF/vOGWGiKp2kTLiDCxwEXS01gZzre+MtEGXK+40QpztkVnaU6zN\nlcXZuNHFOXsDA4LLhR2LRC4bOWchH8usroorfzM0kDfFkPMiwY+k6LZQAL1IvKuF\nIsqRTcnpGDDXvvyXVBYbOqrWZwKBgQDoYh063eXL4HIlh/oCOD3Fhp5p9zd/dHzN\nCv2ODOcEQG3N43YLPHHf2mCSpv8Vcp17cV3aGgMYUEU2i7f6TpOX9VUpgLip8tEj\nOxZL4xN3GAy57MC2v1t3RBU58waYk1fOdqFlvwy7xxdCqU/zB8vvdjJ1vqIJnAsw\nICD087eB5wKBgHLGtB0mMWxui8Vgj8yeMc9jRBxDlFwr9QEUmoJMLnS1KOQgX+6n\nyys5mKR6qeGUI6Tb7JRNotsx2i/Lcpcn39M/LAO1358lOw4im4m7E8nVUep6K62P\n9lJenWxxMiBL65PN3RDrhKtsn86F+NlTWnTMHEv3d9sUVyQMyBbZoOtzAoGBAJPj\nvQ51oYU6deqEuwsml8lZfv+ZIWyvya0ETZFVjMAb8MWS+ND1ytLXu5YZSUVxB+BD\nqaLf4xBaJXItQQy/bRbMP0KGdP9TVN3DANGS1hR47cB1d7/V0HP6lDeo/o2jV8JB\npT2HdKcccSUvc34LfDINWtesVpse/8/E4rSBVkwJAoGAI00acL5ragdj1Sz/eTiU\nnvi4uOhRbcegDOnk3H1VYULoy2d6bvTfNObDnI2laWTIZluwBJ4WrBbDAGjNkL4d\nDHNO5ZgjyqtK+3LoSixEac+YKxTvmPLtmXgBExjHwQ5lSWG3VQyCbPYBmruZFLhE\nKb868a/RtMJm9f8P1WDkb/I=\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-fbsvc@simpaudkartoharjo.iam.gserviceaccount.com",
"client_id": "106199666854739826761",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40simpaudkartoharjo.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}

BIN
storage/firebase-auth.zip Normal file

Binary file not shown.

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

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

1
test_db.php Normal file
View File

@ -0,0 +1 @@
<?php try { $db = new PDO('mysql:host=127.0.0.1;dbname=paud_monitoring;port=3306', 'root', '', [PDO::ATTR_TIMEOUT => 3, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); echo 'Koneksi Sukses!'; } catch (Exception $e) { echo 'Error: ' . $e->getMessage(); } ?>

22
tinker_seeder.php Normal file
View File

@ -0,0 +1,22 @@
<?php
$siswas = App\Models\Siswa::all();
foreach($siswas as $siswa) {
App\Models\PenilaianRapot::updateOrCreate(
['siswa_id' => $siswa->id],
[
'semester' => 'Genap',
'tahun_ajaran' => '2025/2026',
'catatan_guru' => 'Ananda sangat interaktif, kreatif, dan mandiri selama di kelas. Pertahankan prestasinya!',
'nama_guru' => 'Siti Aminah, S.Pd',
'nilai_aspek' => [
'Agama' => 'BSB',
'Fisik' => 'BSB',
'Kognitif' => 'BSH',
'Bahasa' => 'MB',
'SosEm' => 'BSB',
'Seni' => 'BSB'
]
]
);
}
echo "DB_RAPOT_SEEDED\n";

26
tinker_seeder_2.php Normal file
View File

@ -0,0 +1,26 @@
<?php
$siswas = App\Models\Siswa::all();
foreach($siswas as $siswa) {
App\Models\PenilaianRapot::updateOrCreate(
['siswa_id' => $siswa->id],
[
'semester' => 'Genap',
'tahun_ajaran' => '2025/2026',
'catatan_guru' => 'Ananda sangat berkembang...',
'nama_guru' => 'Siti Aminah, S.Pd',
'nilai_aik' => 'Alhamdulillah, ananda sudah mampu menghafal doa harian dengan baik.',
'nilai_budi_pekerti' => 'Ananda menunjukkan sikap santun dan peduli pada teman sebayanya.',
'nilai_jati_diri' => 'Berkembang dengan kemandirian yang tinggi saat bermain.',
'nilai_literasi_steam' => 'Menunjukkan ketertarikan luar biasa pada balok susun dan warna.',
'nilai_kokurikuler' => 'Sangat aktif dalam kegiatan seni budaya tari daerah.',
'tinggi_badan' => 110,
'berat_badan' => 18,
'lingkar_kepala' => 50,
'sakit' => 1,
'izin' => 0,
'alpha' => 0,
'file_pdf' => 'rapot/dummy_rapot.pdf'
]
);
}
echo "DB_NEW_RAPOT_SEEDED\n";