From 3201648b166193cfe9bd9a535e510fa5edd22b3a Mon Sep 17 00:00:00 2001 From: HelgaFaisa <158024195+HelgaFaisa@users.noreply.github.com> Date: Thu, 20 Nov 2025 16:25:03 +0700 Subject: [PATCH] Initial commit --- .gitattributes | 2 + README.md | 2 + sim-pkpps/.editorconfig | 18 + sim-pkpps/.env.example | 59 + sim-pkpps/.gitattributes | 11 + sim-pkpps/.gitignore | 19 + sim-pkpps/README.md | 66 + sim-pkpps/app/Console/Kernel.php | 27 + sim-pkpps/app/Exceptions/Handler.php | 30 + .../Admin/AbsensiKegiatanController.php | 184 + .../Controllers/Admin/BeritaController.php | 215 + .../Controllers/Admin/CapaianController.php | 637 ++ .../Controllers/Admin/KartuRfidController.php | 91 + .../Admin/KategoriKegiatanController.php | 119 + .../Admin/KategoriPelanggaranController.php | 118 + .../Controllers/Admin/KegiatanController.php | 151 + .../Admin/KepulanganController.php | 411 + .../Admin/KesehatanSantriController.php | 240 + .../Controllers/Admin/MateriController.php | 168 + .../Admin/PembayaranSppController.php | 368 + .../Admin/RiwayatKegiatanController.php | 222 + .../Admin/RiwayatPelanggaranController.php | 223 + .../Controllers/Admin/SantriController.php | 174 + .../Controllers/Admin/SemesterController.php | 156 + .../Controllers/Admin/UangSakuController.php | 317 + .../Http/Controllers/Admin/UserController.php | 113 + .../Controllers/Auth/AdminAuthController.php | 126 + .../Auth/AuthenticatedSessionController.php | 48 + .../Auth/ConfirmablePasswordController.php | 41 + ...mailVerificationNotificationController.php | 25 + .../EmailVerificationPromptController.php | 22 + .../Auth/NewPasswordController.php | 61 + .../Controllers/Auth/PasswordController.php | 29 + .../Auth/PasswordResetLinkController.php | 44 + .../Auth/RegisteredUserController.php | 51 + .../Controllers/Auth/SantriAuthController.php | 93 + .../Auth/VerifyEmailController.php | 28 + sim-pkpps/app/Http/Controllers/Controller.php | 12 + .../Http/Controllers/DashboardController.php | 134 + .../Http/Controllers/ProfileController.php | 62 + .../Santri/SantriBeritaController.php | 112 + .../Santri/SantriCapaianController.php | 214 + .../Santri/SantriKepulanganController.php | 128 + .../Santri/SantriKesehatanController.php | 121 + .../Santri/SantriPelanggaranController.php | 111 + .../Santri/SantriProfileController.php | 107 + .../Santri/SantriUangSakuController.php | 151 + sim-pkpps/app/Http/Kernel.php | 71 + .../app/Http/Middleware/Authenticate.php | 17 + .../app/Http/Middleware/ClearStuckSession.php | 31 + .../app/Http/Middleware/EncryptCookies.php | 17 + .../PreventRequestsDuringMaintenance.php | 17 + .../Middleware/RedirectIfAuthenticated.php | 30 + sim-pkpps/app/Http/Middleware/Role.php | 53 + sim-pkpps/app/Http/Middleware/TrimStrings.php | 19 + sim-pkpps/app/Http/Middleware/TrustHosts.php | 20 + .../app/Http/Middleware/TrustProxies.php | 28 + .../app/Http/Middleware/ValidateSignature.php | 22 + .../app/Http/Middleware/VerifyCsrfToken.php | 35 + .../app/Http/Requests/Auth/LoginRequest.php | 85 + .../Http/Requests/ProfileUpdateRequest.php | 23 + sim-pkpps/app/Models/AbsensiKegiatan.php | 91 + sim-pkpps/app/Models/Berita.php | 113 + sim-pkpps/app/Models/Capaian.php | 207 + sim-pkpps/app/Models/KategoriKegiatan.php | 66 + sim-pkpps/app/Models/KategoriPelanggaran.php | 116 + sim-pkpps/app/Models/Kegiatan.php | 90 + sim-pkpps/app/Models/Kepulangan.php | 247 + sim-pkpps/app/Models/KesehatanSantri.php | 157 + sim-pkpps/app/Models/Materi.php | 138 + sim-pkpps/app/Models/PembayaranSpp.php | 172 + sim-pkpps/app/Models/RiwayatPelanggaran.php | 163 + sim-pkpps/app/Models/Santri.php | 308 + sim-pkpps/app/Models/Semester.php | 95 + sim-pkpps/app/Models/UangSaku.php | 145 + sim-pkpps/app/Models/User.php | 65 + sim-pkpps/app/Models/Wali.php | 32 + .../app/Providers/AppServiceProvider.php | 24 + .../app/Providers/AuthServiceProvider.php | 26 + .../Providers/BroadcastServiceProvider.php | 19 + .../app/Providers/EventServiceProvider.php | 38 + .../app/Providers/RouteServiceProvider.php | 40 + sim-pkpps/app/View/Components/AppLayout.php | 17 + sim-pkpps/app/View/Components/GuestLayout.php | 17 + sim-pkpps/artisan | 53 + sim-pkpps/bootstrap/app.php | 55 + sim-pkpps/bootstrap/cache/.gitignore | 2 + sim-pkpps/composer.json | 68 + sim-pkpps/composer.lock | 8678 +++++++++++++++++ sim-pkpps/config/app.php | 188 + sim-pkpps/config/auth.php | 115 + sim-pkpps/config/broadcasting.php | 71 + sim-pkpps/config/cache.php | 111 + sim-pkpps/config/cors.php | 34 + sim-pkpps/config/database.php | 151 + sim-pkpps/config/filesystems.php | 76 + sim-pkpps/config/hashing.php | 54 + sim-pkpps/config/logging.php | 131 + sim-pkpps/config/mail.php | 134 + sim-pkpps/config/queue.php | 109 + sim-pkpps/config/sanctum.php | 83 + sim-pkpps/config/services.php | 34 + sim-pkpps/config/session.php | 135 + sim-pkpps/config/view.php | 36 + sim-pkpps/database/.gitignore | 1 + sim-pkpps/database/factories/UserFactory.php | 44 + .../2014_10_12_000000_create_users_table.php | 32 + ...000_create_password_reset_tokens_table.php | 28 + ..._08_19_000000_create_failed_jobs_table.php | 32 + ...01_create_personal_access_tokens_table.php | 33 + ...2025_09_29_033444_create_santris_table.php | 48 + .../2025_09_29_034134_create_walis_table.php | 35 + ...58_add_role_and_role_id_to_users_table.php | 46 + ..._035247_create_kesehatan_santris_table.php | 40 + ...5_10_20_080216_create_kepulangan_table.php | 45 + .../2025_10_21_070405_create_berita_table.php | 29 + ...0_21_070413_create_berita_santri_table.php | 32 + ...324_create_kategori_pelanggarans_table.php | 34 + ...3350_create_riwayat_pelanggarans_table.php | 53 + ..._24_070612_create_pembayaran_spp_table.php | 46 + ...25_10_27_090620_create_uang_saku_table.php | 32 + ...092701_create_kategori_kegiatans_table.php | 26 + ...25_10_29_042339_create_kegiatans_table.php | 38 + ..._042902_create_absensi_kegiatans_table.php | 46 + ...10_29_042912_add_rfid_to_santris_table.php | 22 + .../2025_10_31_064743_create_materi_table.php | 39 + ...025_10_31_065514_create_semester_table.php | 31 + ...2025_10_31_065538_create_capaian_table.php | 42 + sim-pkpps/database/seeders/DatabaseSeeder.php | 22 + sim-pkpps/package-lock.json | 2881 ++++++ sim-pkpps/package.json | 18 + sim-pkpps/phpunit.xml | 32 + sim-pkpps/postcss.config.js | 6 + sim-pkpps/public/.htaccess | 21 + sim-pkpps/public/css/app.css | 1818 ++++ sim-pkpps/public/favicon.ico | 0 sim-pkpps/public/index.php | 55 + sim-pkpps/public/robots.txt | 2 + sim-pkpps/resources/css/app.css | 3 + sim-pkpps/resources/js/app.js | 7 + sim-pkpps/resources/js/bootstrap.js | 32 + .../views/admin/auth/login.blade.php | 217 + .../views/admin/auth/register.blade.php | 53 + .../views/admin/berita/create.blade.php | 331 + .../views/admin/berita/edit.blade.php | 350 + .../views/admin/berita/index.blade.php | 165 + .../views/admin/berita/show.blade.php | 184 + .../views/admin/berita/statistik.blade.php | 198 + .../views/admin/capaian/create.blade.php | 614 ++ .../views/admin/capaian/dashboard.blade.php | 376 + .../admin/capaian/detail-materi.blade.php | 224 + .../views/admin/capaian/edit.blade.php | 445 + .../views/admin/capaian/index.blade.php | 147 + .../views/admin/capaian/rekap-kelas.blade.php | 187 + .../admin/capaian/riwayat-santri.blade.php | 173 + .../views/admin/capaian/show.blade.php | 251 + .../views/admin/dashboardAdmin.blade.php | 36 + .../kategori_pelanggaran/create.blade.php | 99 + .../kategori_pelanggaran/index.blade.php | 185 + .../admin/kategori_pelanggaran/show.blade.php | 183 + .../admin/kegiatan/absensi/index.blade.php | 89 + .../admin/kegiatan/absensi/input.blade.php | 280 + .../admin/kegiatan/absensi/rekap.blade.php | 103 + .../admin/kegiatan/data/create.blade.php | 147 + .../views/admin/kegiatan/data/edit.blade.php | 145 + .../views/admin/kegiatan/data/index.blade.php | 109 + .../views/admin/kegiatan/data/show.blade.php | 75 + .../admin/kegiatan/kartu/cetak.blade.php | 501 + .../admin/kegiatan/kartu/daftar.blade.php | 93 + .../admin/kegiatan/kartu/index.blade.php | 109 + .../admin/kegiatan/kategori/create.blade.php | 63 + .../admin/kegiatan/kategori/edit.blade.php | 61 + .../admin/kegiatan/kategori/index.blade.php | 88 + .../admin/kegiatan/kategori/show.blade.php | 54 + .../kegiatan/riwayat/detail-santri.blade.php | 179 + .../admin/kegiatan/riwayat/edit.blade.php | 81 + .../admin/kegiatan/riwayat/index.blade.php | 247 + .../admin/kegiatan/riwayat/show.blade.php | 131 + .../views/admin/kepulangan/create.blade.php | 391 + .../views/admin/kepulangan/edit.blade.php | 163 + .../views/admin/kepulangan/index.blade.php | 501 + .../views/admin/kepulangan/show.blade.php | 454 + .../admin/kepulangan/surat-pdf.blade.php | 270 + .../kesehatan-santri/cetak-surat.blade.php | 309 + .../admin/kesehatan-santri/create.blade.php | 195 + .../admin/kesehatan-santri/edit.blade.php | 201 + .../admin/kesehatan-santri/index.blade.php | 273 + .../admin/kesehatan-santri/riwayat.blade.php | 216 + .../admin/kesehatan-santri/show.blade.php | 485 + .../views/admin/materi/create.blade.php | 133 + .../views/admin/materi/edit.blade.php | 134 + .../views/admin/materi/index.blade.php | 130 + .../views/admin/materi/show.blade.php | 125 + .../pembayaran-spp/cetak-bukti.blade.php | 159 + .../cetak-laporan-santri.blade.php | 158 + .../pembayaran-spp/cetak-laporan.blade.php | 172 + .../admin/pembayaran-spp/create.blade.php | 171 + .../views/admin/pembayaran-spp/edit.blade.php | 176 + .../admin/pembayaran-spp/generate.blade.php | 247 + .../admin/pembayaran-spp/index.blade.php | 159 + .../admin/pembayaran-spp/laporan.blade.php | 99 + .../admin/pembayaran-spp/riwayat.blade.php | 127 + .../views/admin/pembayaran-spp/show.blade.php | 135 + .../riwayat_pelanggaran/create.blade.php | 156 + .../admin/riwayat_pelanggaran/edit.blade.php | 156 + .../admin/riwayat_pelanggaran/index.blade.php | 264 + .../riwayat_santri.blade.php | 205 + .../admin/riwayat_pelanggaran/show.blade.php | 291 + .../views/admin/santri/create.blade.php | 24 + .../views/admin/santri/edit.blade.php | 31 + .../views/admin/santri/form.blade.php | 96 + .../views/admin/santri/index.blade.php | 141 + .../views/admin/santri/show.blade.php | 164 + .../views/admin/semester/create.blade.php | 86 + .../views/admin/semester/edit.blade.php | 88 + .../views/admin/semester/index.blade.php | 104 + .../views/admin/semester/show.blade.php | 94 + .../views/admin/uang-saku/create.blade.php | 122 + .../views/admin/uang-saku/edit.blade.php | 101 + .../views/admin/uang-saku/index.blade.php | 219 + .../views/admin/uang-saku/riwayat.blade.php | 383 + .../views/admin/uang-saku/show.blade.php | 132 + .../admin/users/create_account.blade.php | 57 + .../admin/users/santri_accounts.blade.php | 52 + .../views/admin/users/wali_accounts.blade.php | 51 + .../views/auth/auth_layout.blade.php | 50 + .../views/auth/confirm-password.blade.php | 27 + .../views/auth/forgot-password.blade.php | 25 + .../resources/views/auth/login.blade.php | 47 + .../resources/views/auth/register.blade.php | 52 + .../views/auth/reset-password.blade.php | 39 + .../views/auth/verify-email.blade.php | 31 + .../components/application-logo.blade.php | 3 + .../components/auth-session-status.blade.php | 7 + .../views/components/danger-button.blade.php | 3 + .../views/components/dropdown-link.blade.php | 1 + .../views/components/dropdown.blade.php | 43 + .../views/components/input-error.blade.php | 9 + .../views/components/input-label.blade.php | 5 + .../views/components/modal.blade.php | 78 + .../views/components/nav-link.blade.php | 11 + .../views/components/primary-button.blade.php | 3 + .../components/responsive-nav-link.blade.php | 11 + .../components/secondary-button.blade.php | 3 + .../views/components/text-input.blade.php | 3 + sim-pkpps/resources/views/dashboard.blade.php | 17 + .../views/layouts/admin-sidebar.blade.php | 185 + .../resources/views/layouts/app.blade.php | 179 + .../resources/views/layouts/guest.blade.php | 30 + .../layouts/santri-wali-sidebar.blade.php | 86 + .../resources/views/profile/edit.blade.php | 29 + .../partials/delete-user-form.blade.php | 55 + .../partials/update-password-form.blade.php | 48 + .../update-profile-information-form.blade.php | 64 + .../views/santri/auth/login.blade.php | 46 + .../views/santri/berita/index.blade.php | 130 + .../views/santri/berita/show.blade.php | 55 + .../views/santri/capaian/index.blade.php | 330 + .../views/santri/capaian/show.blade.php | 187 + .../views/santri/dashboardSantri.blade.php | 194 + .../views/santri/kepulangan/index.blade.php | 224 + .../views/santri/kepulangan/show.blade.php | 185 + .../views/santri/kesehatan/index.blade.php | 265 + .../views/santri/kesehatan/show.blade.php | 102 + .../views/santri/pelanggaran/index.blade.php | 116 + .../santri/pelanggaran/kategori.blade.php | 67 + .../views/santri/pelanggaran/show.blade.php | 67 + .../views/santri/profil/edit.blade.php | 136 + .../views/santri/profil/index.blade.php | 153 + .../views/santri/uang-saku/index.blade.php | 130 + .../views/santri/uang-saku/show.blade.php | 178 + sim-pkpps/resources/views/welcome.blade.php | 133 + sim-pkpps/routes/api.php | 19 + sim-pkpps/routes/auth.php | 59 + sim-pkpps/routes/channels.php | 18 + sim-pkpps/routes/console.php | 19 + sim-pkpps/routes/web.php | 335 + sim-pkpps/storage/app/.gitignore | 3 + sim-pkpps/storage/app/public/.gitignore | 2 + sim-pkpps/storage/framework/.gitignore | 9 + sim-pkpps/storage/framework/cache/.gitignore | 3 + .../D6FZEBgpwe0tXYewWTPUoB8jkSCmj1vaOfTQZXC2 | 1 + .../VZHgNRG1TSdcy9Emm4N24LHRBagOaImjTjDq7Jpu | 1 + .../g11HwTdR2XIUjyb8Ix2frVaf3dZj8VdBDNCTZCi6 | 1 + .../storage/framework/testing/.gitignore | 2 + .../072da14fbb17a3a780dd86ec6fb2a220.php | 272 + .../075d452fbecb627adfa4fe88bdcc349d.php | 113 + .../0b444879160a4358a991decaae9d874a.php | 180 + .../105000334f95beae17d9647d42ae0af0.php | 194 + .../1bcee449d95fd7f4af3e834ca3f373a2.php | 121 + .../25e18337d272f9d0e44384a50ffc0f2e.php | 228 + .../31a477eb975396d9e3dfe350a914f63d.php | 184 + .../34a382bc0d43efb21e7b74c864109136.php | 295 + .../35ee7d3140dd2cb469794c46a4c597db.php | 185 + .../42e0614621fd9cbad7786f12bdf5b5c9.php | 134 + .../43f661f38fbd4f777442ca71c4728fc9.php | 195 + .../452c34a3ceda4cc1b53f0fe5d3c51b99.php | 57 + .../46ab65c9284ea5ee3a8118881424f90c.php | 91 + .../4c2e61e495793889f762efcce89628d8.php | 153 + .../6611960912faff880255c6e631dc5a29.php | 117 + .../66e2096cb3c35849e326885249110f62.php | 275 + .../72906883c2124eae424a49669807e619.php | 69 + .../84c709f0b9be2a14b454115d6ed83ed6.php | 50 + .../87643fe3b359872f8ce3c66a3684eab2.php | 510 + .../995c109454d8c33c3f0f95e2c6a9b380.php | 217 + .../9b446f86a5179b2559bd927fbd573416.php | 53 + .../a35bdf7f5682e1163e4e422bf7ed3156.php | 86 + .../a3a0962d896f35dffe41a6f25b643d59.php | 134 + .../a55ee0bd4bb0ff61d001bcba38d0f720.php | 147 + .../a65eb8f69fbdf55d07dff257bb47073e.php | 93 + .../a866f51eee86f2c6a47752a971068445.php | 283 + .../b189c66d92090748551bf01fbdf8d452.php | 82 + .../b97350e57d8d92217317f5f41c8d18a5.php | 198 + .../bbf90b40396229b9af5ccf996fe33770.php | 194 + .../c5f0b5e8cf684d75aae3123501a0192f.php | 213 + .../c9c645ef9ca26f7f8150d4f3aae0f194.php | 231 + .../e126e1062982b7622152c3d631f3ccc4.php | 161 + .../e7a3eb2c7c53a4310ad6e32050798b86.php | 49 + .../ef3052ff8b6e3fc5621ceb75a6dd6413.php | 337 + .../f00f9dae67317e274040c9fcaec81e32.php | 421 + .../f2968196d7f31ca7e2832869d2e2aef0.php | 185 + .../f61d0f67d6967e731c17960b0dff77d3.php | 37 + .../f7d8d067fc835ce54b0dc2d499afcbbd.php | 248 + .../f9c3e31ca3780ee24993ca37d8e92eee.php | 68 + .../f9de4ee507702ddee56d80d4870eb11b.php | 170 + sim-pkpps/storage/logs/.gitignore | 2 + sim-pkpps/tailwind.config.js | 21 + sim-pkpps/tests/CreatesApplication.php | 21 + .../tests/Feature/Auth/AuthenticationTest.php | 55 + .../Feature/Auth/EmailVerificationTest.php | 65 + .../Feature/Auth/PasswordConfirmationTest.php | 44 + .../tests/Feature/Auth/PasswordResetTest.php | 73 + .../tests/Feature/Auth/PasswordUpdateTest.php | 51 + .../tests/Feature/Auth/RegistrationTest.php | 32 + sim-pkpps/tests/Feature/ExampleTest.php | 19 + sim-pkpps/tests/Feature/ProfileTest.php | 99 + sim-pkpps/tests/TestCase.php | 10 + sim-pkpps/tests/Unit/ExampleTest.php | 16 + sim-pkpps/vite.config.js | 14 + 339 files changed, 52223 insertions(+) create mode 100644 .gitattributes create mode 100644 README.md create mode 100644 sim-pkpps/.editorconfig create mode 100644 sim-pkpps/.env.example create mode 100644 sim-pkpps/.gitattributes create mode 100644 sim-pkpps/.gitignore create mode 100644 sim-pkpps/README.md create mode 100644 sim-pkpps/app/Console/Kernel.php create mode 100644 sim-pkpps/app/Exceptions/Handler.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/AbsensiKegiatanController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/BeritaController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/CapaianController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/KartuRfidController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/KategoriKegiatanController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/KategoriPelanggaranController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/KegiatanController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/KepulanganController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/KesehatanSantriController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/MateriController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/PembayaranSppController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/RiwayatKegiatanController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/RiwayatPelanggaranController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/SantriController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/SemesterController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/UangSakuController.php create mode 100644 sim-pkpps/app/Http/Controllers/Admin/UserController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/AdminAuthController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/AuthenticatedSessionController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/ConfirmablePasswordController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/EmailVerificationNotificationController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/EmailVerificationPromptController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/NewPasswordController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/PasswordController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/PasswordResetLinkController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/RegisteredUserController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/SantriAuthController.php create mode 100644 sim-pkpps/app/Http/Controllers/Auth/VerifyEmailController.php create mode 100644 sim-pkpps/app/Http/Controllers/Controller.php create mode 100644 sim-pkpps/app/Http/Controllers/DashboardController.php create mode 100644 sim-pkpps/app/Http/Controllers/ProfileController.php create mode 100644 sim-pkpps/app/Http/Controllers/Santri/SantriBeritaController.php create mode 100644 sim-pkpps/app/Http/Controllers/Santri/SantriCapaianController.php create mode 100644 sim-pkpps/app/Http/Controllers/Santri/SantriKepulanganController.php create mode 100644 sim-pkpps/app/Http/Controllers/Santri/SantriKesehatanController.php create mode 100644 sim-pkpps/app/Http/Controllers/Santri/SantriPelanggaranController.php create mode 100644 sim-pkpps/app/Http/Controllers/Santri/SantriProfileController.php create mode 100644 sim-pkpps/app/Http/Controllers/Santri/SantriUangSakuController.php create mode 100644 sim-pkpps/app/Http/Kernel.php create mode 100644 sim-pkpps/app/Http/Middleware/Authenticate.php create mode 100644 sim-pkpps/app/Http/Middleware/ClearStuckSession.php create mode 100644 sim-pkpps/app/Http/Middleware/EncryptCookies.php create mode 100644 sim-pkpps/app/Http/Middleware/PreventRequestsDuringMaintenance.php create mode 100644 sim-pkpps/app/Http/Middleware/RedirectIfAuthenticated.php create mode 100644 sim-pkpps/app/Http/Middleware/Role.php create mode 100644 sim-pkpps/app/Http/Middleware/TrimStrings.php create mode 100644 sim-pkpps/app/Http/Middleware/TrustHosts.php create mode 100644 sim-pkpps/app/Http/Middleware/TrustProxies.php create mode 100644 sim-pkpps/app/Http/Middleware/ValidateSignature.php create mode 100644 sim-pkpps/app/Http/Middleware/VerifyCsrfToken.php create mode 100644 sim-pkpps/app/Http/Requests/Auth/LoginRequest.php create mode 100644 sim-pkpps/app/Http/Requests/ProfileUpdateRequest.php create mode 100644 sim-pkpps/app/Models/AbsensiKegiatan.php create mode 100644 sim-pkpps/app/Models/Berita.php create mode 100644 sim-pkpps/app/Models/Capaian.php create mode 100644 sim-pkpps/app/Models/KategoriKegiatan.php create mode 100644 sim-pkpps/app/Models/KategoriPelanggaran.php create mode 100644 sim-pkpps/app/Models/Kegiatan.php create mode 100644 sim-pkpps/app/Models/Kepulangan.php create mode 100644 sim-pkpps/app/Models/KesehatanSantri.php create mode 100644 sim-pkpps/app/Models/Materi.php create mode 100644 sim-pkpps/app/Models/PembayaranSpp.php create mode 100644 sim-pkpps/app/Models/RiwayatPelanggaran.php create mode 100644 sim-pkpps/app/Models/Santri.php create mode 100644 sim-pkpps/app/Models/Semester.php create mode 100644 sim-pkpps/app/Models/UangSaku.php create mode 100644 sim-pkpps/app/Models/User.php create mode 100644 sim-pkpps/app/Models/Wali.php create mode 100644 sim-pkpps/app/Providers/AppServiceProvider.php create mode 100644 sim-pkpps/app/Providers/AuthServiceProvider.php create mode 100644 sim-pkpps/app/Providers/BroadcastServiceProvider.php create mode 100644 sim-pkpps/app/Providers/EventServiceProvider.php create mode 100644 sim-pkpps/app/Providers/RouteServiceProvider.php create mode 100644 sim-pkpps/app/View/Components/AppLayout.php create mode 100644 sim-pkpps/app/View/Components/GuestLayout.php create mode 100644 sim-pkpps/artisan create mode 100644 sim-pkpps/bootstrap/app.php create mode 100644 sim-pkpps/bootstrap/cache/.gitignore create mode 100644 sim-pkpps/composer.json create mode 100644 sim-pkpps/composer.lock create mode 100644 sim-pkpps/config/app.php create mode 100644 sim-pkpps/config/auth.php create mode 100644 sim-pkpps/config/broadcasting.php create mode 100644 sim-pkpps/config/cache.php create mode 100644 sim-pkpps/config/cors.php create mode 100644 sim-pkpps/config/database.php create mode 100644 sim-pkpps/config/filesystems.php create mode 100644 sim-pkpps/config/hashing.php create mode 100644 sim-pkpps/config/logging.php create mode 100644 sim-pkpps/config/mail.php create mode 100644 sim-pkpps/config/queue.php create mode 100644 sim-pkpps/config/sanctum.php create mode 100644 sim-pkpps/config/services.php create mode 100644 sim-pkpps/config/session.php create mode 100644 sim-pkpps/config/view.php create mode 100644 sim-pkpps/database/.gitignore create mode 100644 sim-pkpps/database/factories/UserFactory.php create mode 100644 sim-pkpps/database/migrations/2014_10_12_000000_create_users_table.php create mode 100644 sim-pkpps/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php create mode 100644 sim-pkpps/database/migrations/2019_08_19_000000_create_failed_jobs_table.php create mode 100644 sim-pkpps/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php create mode 100644 sim-pkpps/database/migrations/2025_09_29_033444_create_santris_table.php create mode 100644 sim-pkpps/database/migrations/2025_09_29_034134_create_walis_table.php create mode 100644 sim-pkpps/database/migrations/2025_09_29_035958_add_role_and_role_id_to_users_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_20_035247_create_kesehatan_santris_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_20_080216_create_kepulangan_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_21_070405_create_berita_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_21_070413_create_berita_santri_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_24_012324_create_kategori_pelanggarans_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_24_013350_create_riwayat_pelanggarans_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_24_070612_create_pembayaran_spp_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_27_090620_create_uang_saku_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_28_092701_create_kategori_kegiatans_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_29_042339_create_kegiatans_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_29_042902_create_absensi_kegiatans_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_29_042912_add_rfid_to_santris_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_31_064743_create_materi_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_31_065514_create_semester_table.php create mode 100644 sim-pkpps/database/migrations/2025_10_31_065538_create_capaian_table.php create mode 100644 sim-pkpps/database/seeders/DatabaseSeeder.php create mode 100644 sim-pkpps/package-lock.json create mode 100644 sim-pkpps/package.json create mode 100644 sim-pkpps/phpunit.xml create mode 100644 sim-pkpps/postcss.config.js create mode 100644 sim-pkpps/public/.htaccess create mode 100644 sim-pkpps/public/css/app.css create mode 100644 sim-pkpps/public/favicon.ico create mode 100644 sim-pkpps/public/index.php create mode 100644 sim-pkpps/public/robots.txt create mode 100644 sim-pkpps/resources/css/app.css create mode 100644 sim-pkpps/resources/js/app.js create mode 100644 sim-pkpps/resources/js/bootstrap.js create mode 100644 sim-pkpps/resources/views/admin/auth/login.blade.php create mode 100644 sim-pkpps/resources/views/admin/auth/register.blade.php create mode 100644 sim-pkpps/resources/views/admin/berita/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/berita/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/berita/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/berita/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/berita/statistik.blade.php create mode 100644 sim-pkpps/resources/views/admin/capaian/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/capaian/dashboard.blade.php create mode 100644 sim-pkpps/resources/views/admin/capaian/detail-materi.blade.php create mode 100644 sim-pkpps/resources/views/admin/capaian/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/capaian/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/capaian/rekap-kelas.blade.php create mode 100644 sim-pkpps/resources/views/admin/capaian/riwayat-santri.blade.php create mode 100644 sim-pkpps/resources/views/admin/capaian/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/dashboardAdmin.blade.php create mode 100644 sim-pkpps/resources/views/admin/kategori_pelanggaran/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/kategori_pelanggaran/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/kategori_pelanggaran/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/absensi/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/absensi/input.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/absensi/rekap.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/data/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/data/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/data/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/data/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/kartu/cetak.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/kartu/daftar.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/kartu/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/kategori/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/kategori/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/kategori/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/kategori/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/riwayat/detail-santri.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/riwayat/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/riwayat/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/kegiatan/riwayat/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/kepulangan/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/kepulangan/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/kepulangan/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/kepulangan/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/kepulangan/surat-pdf.blade.php create mode 100644 sim-pkpps/resources/views/admin/kesehatan-santri/cetak-surat.blade.php create mode 100644 sim-pkpps/resources/views/admin/kesehatan-santri/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/kesehatan-santri/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/kesehatan-santri/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/kesehatan-santri/riwayat.blade.php create mode 100644 sim-pkpps/resources/views/admin/kesehatan-santri/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/materi/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/materi/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/materi/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/materi/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/cetak-bukti.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/cetak-laporan-santri.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/cetak-laporan.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/generate.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/laporan.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/riwayat.blade.php create mode 100644 sim-pkpps/resources/views/admin/pembayaran-spp/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/riwayat_pelanggaran/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/riwayat_pelanggaran/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/riwayat_pelanggaran/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/riwayat_pelanggaran/riwayat_santri.blade.php create mode 100644 sim-pkpps/resources/views/admin/riwayat_pelanggaran/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/santri/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/santri/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/santri/form.blade.php create mode 100644 sim-pkpps/resources/views/admin/santri/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/santri/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/semester/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/semester/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/semester/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/semester/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/uang-saku/create.blade.php create mode 100644 sim-pkpps/resources/views/admin/uang-saku/edit.blade.php create mode 100644 sim-pkpps/resources/views/admin/uang-saku/index.blade.php create mode 100644 sim-pkpps/resources/views/admin/uang-saku/riwayat.blade.php create mode 100644 sim-pkpps/resources/views/admin/uang-saku/show.blade.php create mode 100644 sim-pkpps/resources/views/admin/users/create_account.blade.php create mode 100644 sim-pkpps/resources/views/admin/users/santri_accounts.blade.php create mode 100644 sim-pkpps/resources/views/admin/users/wali_accounts.blade.php create mode 100644 sim-pkpps/resources/views/auth/auth_layout.blade.php create mode 100644 sim-pkpps/resources/views/auth/confirm-password.blade.php create mode 100644 sim-pkpps/resources/views/auth/forgot-password.blade.php create mode 100644 sim-pkpps/resources/views/auth/login.blade.php create mode 100644 sim-pkpps/resources/views/auth/register.blade.php create mode 100644 sim-pkpps/resources/views/auth/reset-password.blade.php create mode 100644 sim-pkpps/resources/views/auth/verify-email.blade.php create mode 100644 sim-pkpps/resources/views/components/application-logo.blade.php create mode 100644 sim-pkpps/resources/views/components/auth-session-status.blade.php create mode 100644 sim-pkpps/resources/views/components/danger-button.blade.php create mode 100644 sim-pkpps/resources/views/components/dropdown-link.blade.php create mode 100644 sim-pkpps/resources/views/components/dropdown.blade.php create mode 100644 sim-pkpps/resources/views/components/input-error.blade.php create mode 100644 sim-pkpps/resources/views/components/input-label.blade.php create mode 100644 sim-pkpps/resources/views/components/modal.blade.php create mode 100644 sim-pkpps/resources/views/components/nav-link.blade.php create mode 100644 sim-pkpps/resources/views/components/primary-button.blade.php create mode 100644 sim-pkpps/resources/views/components/responsive-nav-link.blade.php create mode 100644 sim-pkpps/resources/views/components/secondary-button.blade.php create mode 100644 sim-pkpps/resources/views/components/text-input.blade.php create mode 100644 sim-pkpps/resources/views/dashboard.blade.php create mode 100644 sim-pkpps/resources/views/layouts/admin-sidebar.blade.php create mode 100644 sim-pkpps/resources/views/layouts/app.blade.php create mode 100644 sim-pkpps/resources/views/layouts/guest.blade.php create mode 100644 sim-pkpps/resources/views/layouts/santri-wali-sidebar.blade.php create mode 100644 sim-pkpps/resources/views/profile/edit.blade.php create mode 100644 sim-pkpps/resources/views/profile/partials/delete-user-form.blade.php create mode 100644 sim-pkpps/resources/views/profile/partials/update-password-form.blade.php create mode 100644 sim-pkpps/resources/views/profile/partials/update-profile-information-form.blade.php create mode 100644 sim-pkpps/resources/views/santri/auth/login.blade.php create mode 100644 sim-pkpps/resources/views/santri/berita/index.blade.php create mode 100644 sim-pkpps/resources/views/santri/berita/show.blade.php create mode 100644 sim-pkpps/resources/views/santri/capaian/index.blade.php create mode 100644 sim-pkpps/resources/views/santri/capaian/show.blade.php create mode 100644 sim-pkpps/resources/views/santri/dashboardSantri.blade.php create mode 100644 sim-pkpps/resources/views/santri/kepulangan/index.blade.php create mode 100644 sim-pkpps/resources/views/santri/kepulangan/show.blade.php create mode 100644 sim-pkpps/resources/views/santri/kesehatan/index.blade.php create mode 100644 sim-pkpps/resources/views/santri/kesehatan/show.blade.php create mode 100644 sim-pkpps/resources/views/santri/pelanggaran/index.blade.php create mode 100644 sim-pkpps/resources/views/santri/pelanggaran/kategori.blade.php create mode 100644 sim-pkpps/resources/views/santri/pelanggaran/show.blade.php create mode 100644 sim-pkpps/resources/views/santri/profil/edit.blade.php create mode 100644 sim-pkpps/resources/views/santri/profil/index.blade.php create mode 100644 sim-pkpps/resources/views/santri/uang-saku/index.blade.php create mode 100644 sim-pkpps/resources/views/santri/uang-saku/show.blade.php create mode 100644 sim-pkpps/resources/views/welcome.blade.php create mode 100644 sim-pkpps/routes/api.php create mode 100644 sim-pkpps/routes/auth.php create mode 100644 sim-pkpps/routes/channels.php create mode 100644 sim-pkpps/routes/console.php create mode 100644 sim-pkpps/routes/web.php create mode 100644 sim-pkpps/storage/app/.gitignore create mode 100644 sim-pkpps/storage/app/public/.gitignore create mode 100644 sim-pkpps/storage/framework/.gitignore create mode 100644 sim-pkpps/storage/framework/cache/.gitignore create mode 100644 sim-pkpps/storage/framework/sessions/D6FZEBgpwe0tXYewWTPUoB8jkSCmj1vaOfTQZXC2 create mode 100644 sim-pkpps/storage/framework/sessions/VZHgNRG1TSdcy9Emm4N24LHRBagOaImjTjDq7Jpu create mode 100644 sim-pkpps/storage/framework/sessions/g11HwTdR2XIUjyb8Ix2frVaf3dZj8VdBDNCTZCi6 create mode 100644 sim-pkpps/storage/framework/testing/.gitignore create mode 100644 sim-pkpps/storage/framework/views/072da14fbb17a3a780dd86ec6fb2a220.php create mode 100644 sim-pkpps/storage/framework/views/075d452fbecb627adfa4fe88bdcc349d.php create mode 100644 sim-pkpps/storage/framework/views/0b444879160a4358a991decaae9d874a.php create mode 100644 sim-pkpps/storage/framework/views/105000334f95beae17d9647d42ae0af0.php create mode 100644 sim-pkpps/storage/framework/views/1bcee449d95fd7f4af3e834ca3f373a2.php create mode 100644 sim-pkpps/storage/framework/views/25e18337d272f9d0e44384a50ffc0f2e.php create mode 100644 sim-pkpps/storage/framework/views/31a477eb975396d9e3dfe350a914f63d.php create mode 100644 sim-pkpps/storage/framework/views/34a382bc0d43efb21e7b74c864109136.php create mode 100644 sim-pkpps/storage/framework/views/35ee7d3140dd2cb469794c46a4c597db.php create mode 100644 sim-pkpps/storage/framework/views/42e0614621fd9cbad7786f12bdf5b5c9.php create mode 100644 sim-pkpps/storage/framework/views/43f661f38fbd4f777442ca71c4728fc9.php create mode 100644 sim-pkpps/storage/framework/views/452c34a3ceda4cc1b53f0fe5d3c51b99.php create mode 100644 sim-pkpps/storage/framework/views/46ab65c9284ea5ee3a8118881424f90c.php create mode 100644 sim-pkpps/storage/framework/views/4c2e61e495793889f762efcce89628d8.php create mode 100644 sim-pkpps/storage/framework/views/6611960912faff880255c6e631dc5a29.php create mode 100644 sim-pkpps/storage/framework/views/66e2096cb3c35849e326885249110f62.php create mode 100644 sim-pkpps/storage/framework/views/72906883c2124eae424a49669807e619.php create mode 100644 sim-pkpps/storage/framework/views/84c709f0b9be2a14b454115d6ed83ed6.php create mode 100644 sim-pkpps/storage/framework/views/87643fe3b359872f8ce3c66a3684eab2.php create mode 100644 sim-pkpps/storage/framework/views/995c109454d8c33c3f0f95e2c6a9b380.php create mode 100644 sim-pkpps/storage/framework/views/9b446f86a5179b2559bd927fbd573416.php create mode 100644 sim-pkpps/storage/framework/views/a35bdf7f5682e1163e4e422bf7ed3156.php create mode 100644 sim-pkpps/storage/framework/views/a3a0962d896f35dffe41a6f25b643d59.php create mode 100644 sim-pkpps/storage/framework/views/a55ee0bd4bb0ff61d001bcba38d0f720.php create mode 100644 sim-pkpps/storage/framework/views/a65eb8f69fbdf55d07dff257bb47073e.php create mode 100644 sim-pkpps/storage/framework/views/a866f51eee86f2c6a47752a971068445.php create mode 100644 sim-pkpps/storage/framework/views/b189c66d92090748551bf01fbdf8d452.php create mode 100644 sim-pkpps/storage/framework/views/b97350e57d8d92217317f5f41c8d18a5.php create mode 100644 sim-pkpps/storage/framework/views/bbf90b40396229b9af5ccf996fe33770.php create mode 100644 sim-pkpps/storage/framework/views/c5f0b5e8cf684d75aae3123501a0192f.php create mode 100644 sim-pkpps/storage/framework/views/c9c645ef9ca26f7f8150d4f3aae0f194.php create mode 100644 sim-pkpps/storage/framework/views/e126e1062982b7622152c3d631f3ccc4.php create mode 100644 sim-pkpps/storage/framework/views/e7a3eb2c7c53a4310ad6e32050798b86.php create mode 100644 sim-pkpps/storage/framework/views/ef3052ff8b6e3fc5621ceb75a6dd6413.php create mode 100644 sim-pkpps/storage/framework/views/f00f9dae67317e274040c9fcaec81e32.php create mode 100644 sim-pkpps/storage/framework/views/f2968196d7f31ca7e2832869d2e2aef0.php create mode 100644 sim-pkpps/storage/framework/views/f61d0f67d6967e731c17960b0dff77d3.php create mode 100644 sim-pkpps/storage/framework/views/f7d8d067fc835ce54b0dc2d499afcbbd.php create mode 100644 sim-pkpps/storage/framework/views/f9c3e31ca3780ee24993ca37d8e92eee.php create mode 100644 sim-pkpps/storage/framework/views/f9de4ee507702ddee56d80d4870eb11b.php create mode 100644 sim-pkpps/storage/logs/.gitignore create mode 100644 sim-pkpps/tailwind.config.js create mode 100644 sim-pkpps/tests/CreatesApplication.php create mode 100644 sim-pkpps/tests/Feature/Auth/AuthenticationTest.php create mode 100644 sim-pkpps/tests/Feature/Auth/EmailVerificationTest.php create mode 100644 sim-pkpps/tests/Feature/Auth/PasswordConfirmationTest.php create mode 100644 sim-pkpps/tests/Feature/Auth/PasswordResetTest.php create mode 100644 sim-pkpps/tests/Feature/Auth/PasswordUpdateTest.php create mode 100644 sim-pkpps/tests/Feature/Auth/RegistrationTest.php create mode 100644 sim-pkpps/tests/Feature/ExampleTest.php create mode 100644 sim-pkpps/tests/Feature/ProfileTest.php create mode 100644 sim-pkpps/tests/TestCase.php create mode 100644 sim-pkpps/tests/Unit/ExampleTest.php create mode 100644 sim-pkpps/vite.config.js diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/README.md b/README.md new file mode 100644 index 0000000..20401b2 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# TugasAkhir + diff --git a/sim-pkpps/.editorconfig b/sim-pkpps/.editorconfig new file mode 100644 index 0000000..8f0de65 --- /dev/null +++ b/sim-pkpps/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/sim-pkpps/.env.example b/sim-pkpps/.env.example new file mode 100644 index 0000000..ea0665b --- /dev/null +++ b/sim-pkpps/.env.example @@ -0,0 +1,59 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=laravel +DB_USERNAME=root +DB_PASSWORD= + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +MEMCACHED_HOST=127.0.0.1 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_HOST= +PUSHER_PORT=443 +PUSHER_SCHEME=https +PUSHER_APP_CLUSTER=mt1 + +VITE_APP_NAME="${APP_NAME}" +VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +VITE_PUSHER_HOST="${PUSHER_HOST}" +VITE_PUSHER_PORT="${PUSHER_PORT}" +VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" +VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/sim-pkpps/.gitattributes b/sim-pkpps/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/sim-pkpps/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/sim-pkpps/.gitignore b/sim-pkpps/.gitignore new file mode 100644 index 0000000..7fe978f --- /dev/null +++ b/sim-pkpps/.gitignore @@ -0,0 +1,19 @@ +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/vendor +.env +.env.backup +.env.production +.phpunit.result.cache +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.vscode diff --git a/sim-pkpps/README.md b/sim-pkpps/README.md new file mode 100644 index 0000000..1a4c26b --- /dev/null +++ b/sim-pkpps/README.md @@ -0,0 +1,66 @@ +
+ + + +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. + +If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +## Laravel Sponsors + +We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). + +### Premium Partners + +- **[Vehikl](https://vehikl.com/)** +- **[Tighten Co.](https://tighten.co)** +- **[WebReinvent](https://webreinvent.com/)** +- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** +- **[64 Robots](https://64robots.com)** +- **[Curotec](https://www.curotec.com/services/technologies/laravel/)** +- **[Cyber-Duck](https://cyber-duck.co.uk)** +- **[DevSquad](https://devsquad.com/hire-laravel-developers)** +- **[Jump24](https://jump24.co.uk)** +- **[Redberry](https://redberry.international/laravel/)** +- **[Active Logic](https://activelogic.com)** +- **[byte5](https://byte5.de)** +- **[OP.GG](https://op.gg)** + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/sim-pkpps/app/Console/Kernel.php b/sim-pkpps/app/Console/Kernel.php new file mode 100644 index 0000000..e6b9960 --- /dev/null +++ b/sim-pkpps/app/Console/Kernel.php @@ -0,0 +1,27 @@ +command('inspire')->hourly(); + } + + /** + * Register the commands for the application. + */ + protected function commands(): void + { + $this->load(__DIR__.'/Commands'); + + require base_path('routes/console.php'); + } +} diff --git a/sim-pkpps/app/Exceptions/Handler.php b/sim-pkpps/app/Exceptions/Handler.php new file mode 100644 index 0000000..56af264 --- /dev/null +++ b/sim-pkpps/app/Exceptions/Handler.php @@ -0,0 +1,30 @@ + + */ + protected $dontFlash = [ + 'current_password', + 'password', + 'password_confirmation', + ]; + + /** + * Register the exception handling callbacks for the application. + */ + public function register(): void + { + $this->reportable(function (Throwable $e) { + // + }); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Admin/AbsensiKegiatanController.php b/sim-pkpps/app/Http/Controllers/Admin/AbsensiKegiatanController.php new file mode 100644 index 0000000..efb567d --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/AbsensiKegiatanController.php @@ -0,0 +1,184 @@ +filled('hari')) { + $query->where('hari', $request->hari); + } + + $kegiatans = $query->orderBy('hari')->orderBy('waktu_mulai')->paginate(10); + $hariList = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad']; + + return view('admin.kegiatan.absensi.index', compact('kegiatans', 'hariList')); + } + + /** + * Form input absensi + */ + public function inputAbsensi($kegiatan_id) + { + $kegiatan = Kegiatan::with('kategori')->where('kegiatan_id', $kegiatan_id)->firstOrFail(); + $tanggal = request('tanggal', now()->format('Y-m-d')); + + // Ambil semua santri aktif + $santris = Santri::where('status', 'Aktif') + ->select('id', 'id_santri', 'nama_lengkap', 'kelas', 'rfid_uid') + ->orderBy('nama_lengkap') + ->get(); + + // Ambil data absensi yang sudah ada + $absensiData = AbsensiKegiatan::where('kegiatan_id', $kegiatan_id) + ->whereDate('tanggal', $tanggal) + ->pluck('status', 'id_santri') + ->toArray(); + + return view('admin.kegiatan.absensi.input', compact('kegiatan', 'santris', 'absensiData', 'tanggal')); + } + + /** + * Simpan absensi manual + */ + public function simpanAbsensi(Request $request) + { + $validated = $request->validate([ + 'kegiatan_id' => 'required|exists:kegiatans,kegiatan_id', + 'tanggal' => 'required|date', + 'absensi' => 'required|array', + 'absensi.*' => 'required|in:Hadir,Izin,Sakit,Alpa', + ]); + + DB::beginTransaction(); + try { + foreach ($request->absensi as $id_santri => $status) { + AbsensiKegiatan::updateOrCreate( + [ + 'kegiatan_id' => $request->kegiatan_id, + 'id_santri' => $id_santri, + 'tanggal' => $request->tanggal, + ], + [ + 'status' => $status, + 'metode_absen' => 'Manual', + 'waktu_absen' => now()->format('H:i:s'), + ] + ); + } + + DB::commit(); + return redirect()->route('admin.absensi-kegiatan.index') + ->with('success', 'Absensi berhasil disimpan.'); + } catch (\Exception $e) { + DB::rollBack(); + return back()->with('error', 'Gagal menyimpan absensi: ' . $e->getMessage()); + } + } + + /** + * Rekap absensi kegiatan + */ + public function rekapAbsensi(Request $request, $kegiatan_id) + { + $kegiatan = Kegiatan::with('kategori')->where('kegiatan_id', $kegiatan_id)->firstOrFail(); + + $query = AbsensiKegiatan::with('santri') + ->where('kegiatan_id', $kegiatan_id); + + // Filter tanggal + if ($request->filled('tanggal')) { + $query->whereDate('tanggal', $request->tanggal); + } + + // Filter bulan + if ($request->filled('bulan')) { + $query->whereMonth('tanggal', date('m', strtotime($request->bulan))) + ->whereYear('tanggal', date('Y', strtotime($request->bulan))); + } + + $absensis = $query->orderBy('tanggal', 'desc') + ->orderBy('waktu_absen', 'desc') + ->paginate(20); + + // Statistik + $stats = AbsensiKegiatan::where('kegiatan_id', $kegiatan_id) + ->select('status', DB::raw('count(*) as total')) + ->groupBy('status') + ->pluck('total', 'status') + ->toArray(); + + return view('admin.kegiatan.absensi.rekap', compact('kegiatan', 'absensis', 'stats')); + } + + /** + * Scan RFID (API untuk JavaScript) + */ + public function scanRfid(Request $request) + { + $validated = $request->validate([ + 'rfid_uid' => 'required|string', + 'kegiatan_id' => 'required|exists:kegiatans,kegiatan_id', + 'tanggal' => 'required|date', + ]); + + // Cari santri berdasarkan RFID + $santri = Santri::where('rfid_uid', $request->rfid_uid) + ->where('status', 'Aktif') + ->first(); + + if (!$santri) { + return response()->json([ + 'success' => false, + 'message' => 'RFID tidak terdaftar atau santri tidak aktif.' + ], 404); + } + + // Cek apakah sudah absen hari ini + $existing = AbsensiKegiatan::where('kegiatan_id', $request->kegiatan_id) + ->where('id_santri', $santri->id_santri) + ->whereDate('tanggal', $request->tanggal) + ->first(); + + if ($existing) { + return response()->json([ + 'success' => false, + 'message' => $santri->nama_lengkap . ' sudah melakukan absensi (' . $existing->status . ').' + ], 400); + } + + // Simpan absensi + $absensi = AbsensiKegiatan::create([ + 'kegiatan_id' => $request->kegiatan_id, + 'id_santri' => $santri->id_santri, + 'tanggal' => $request->tanggal, + 'status' => 'Hadir', + 'metode_absen' => 'RFID', + 'waktu_absen' => now()->format('H:i:s'), + ]); + + return response()->json([ + 'success' => true, + 'message' => 'Absensi berhasil untuk ' . $santri->nama_lengkap, + 'data' => [ + 'nama' => $santri->nama_lengkap, + 'id_santri' => $santri->id_santri, + 'kelas' => $santri->kelas, + 'waktu' => now()->format('H:i:s'), + ] + ]); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/BeritaController.php b/sim-pkpps/app/Http/Controllers/Admin/BeritaController.php new file mode 100644 index 0000000..d6330e5 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/BeritaController.php @@ -0,0 +1,215 @@ +with('santriTertentu'); + + // Search + if ($request->filled('search')) { + $query->search($request->search); + } + + // Filter status + if ($request->filled('status')) { + $query->status($request->status); + } + + // Filter target + if ($request->filled('target')) { + $query->target($request->target); + } + + $berita = $query->orderBy('created_at', 'desc')->paginate(15); + + return view('admin.berita.index', compact('berita')); + } + + /** + * Tampilkan form create + */ + public function create() + { + // Ambil data santri aktif - sesuaikan dengan kolom yang ada di model Santri + $santri = Santri::aktif() + ->select('id_santri', 'nama_lengkap', 'kelas') + ->orderBy('nama_lengkap') + ->get(); + + $kelasOptions = ['PB', 'Lambatan', 'Cepatan']; + + return view('admin.berita.create', compact('santri', 'kelasOptions')); + } + + /** + * Simpan berita baru + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'judul' => 'required|string|max:255', + 'konten' => 'required|string', + 'penulis' => 'required|string|max:255', + 'gambar' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', + 'status' => 'required|in:draft,published', + 'target_berita' => 'required|in:semua,kelas_tertentu,santri_tertentu', + 'target_kelas' => 'nullable|array', + 'target_kelas.*' => 'in:PB,Lambatan,Cepatan', + 'santri_tertentu' => 'nullable|array', + 'santri_tertentu.*' => 'exists:santris,id_santri', + ], [ + 'judul.required' => 'Judul berita wajib diisi', + 'konten.required' => 'Konten berita wajib diisi', + 'penulis.required' => 'Nama penulis wajib diisi', + 'status.required' => 'Status berita wajib dipilih', + 'target_berita.required' => 'Target berita wajib dipilih', + ]); + + // Upload gambar jika ada + if ($request->hasFile('gambar')) { + $validated['gambar'] = $request->file('gambar')->store('berita', 'public'); + } + + // Buat berita + $berita = Berita::create($validated); + + // Attach santri jika target santri_tertentu + if ($validated['target_berita'] === 'santri_tertentu' && $request->filled('santri_tertentu')) { + $berita->santriTertentu()->attach($request->santri_tertentu); + } + + // Attach santri berdasarkan kelas jika target kelas_tertentu + if ($validated['target_berita'] === 'kelas_tertentu' && $request->filled('target_kelas')) { + $santriKelas = Santri::whereIn('kelas', $request->target_kelas) + ->where('status', 'Aktif') + ->pluck('id_santri'); + $berita->santriTertentu()->attach($santriKelas); + } + + return redirect()->route('admin.berita.index') + ->with('success', 'Berita berhasil ditambahkan!'); + } + + /** + * Tampilkan detail berita + */ + public function show(Berita $berita) + { + $berita->load('santriTertentu'); + return view('admin.berita.show', compact('berita')); + } + + /** + * Tampilkan form edit + */ + public function edit(Berita $berita) + { + $berita->load('santriTertentu'); + + // Ambil data santri aktif - sesuaikan dengan kolom yang ada di model Santri + $santri = Santri::aktif() + ->select('id_santri', 'nama_lengkap', 'kelas') + ->orderBy('nama_lengkap') + ->get(); + + $kelasOptions = ['PB', 'Lambatan', 'Cepatan']; + + $selectedSantri = $berita->santriTertentu->pluck('id_santri')->toArray(); + + return view('admin.berita.edit', compact('berita', 'santri', 'kelasOptions', 'selectedSantri')); + } + + /** + * Update berita + */ + public function update(Request $request, Berita $berita) + { + $validated = $request->validate([ + 'judul' => 'required|string|max:255', + 'konten' => 'required|string', + 'penulis' => 'required|string|max:255', + 'gambar' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', + 'status' => 'required|in:draft,published', + 'target_berita' => 'required|in:semua,kelas_tertentu,santri_tertentu', + 'target_kelas' => 'nullable|array', + 'target_kelas.*' => 'in:PB,Lambatan,Cepatan', + 'santri_tertentu' => 'nullable|array', + 'santri_tertentu.*' => 'exists:santris,id_santri', + ]); + + // Upload gambar baru jika ada + if ($request->hasFile('gambar')) { + // Hapus gambar lama + if ($berita->gambar) { + Storage::disk('public')->delete($berita->gambar); + } + $validated['gambar'] = $request->file('gambar')->store('berita', 'public'); + } + + // Update berita + $berita->update($validated); + + // Sync santri + if ($validated['target_berita'] === 'santri_tertentu' && $request->filled('santri_tertentu')) { + $berita->santriTertentu()->sync($request->santri_tertentu); + } elseif ($validated['target_berita'] === 'kelas_tertentu' && $request->filled('target_kelas')) { + $santriKelas = Santri::whereIn('kelas', $request->target_kelas) + ->where('status', 'Aktif') + ->pluck('id_santri'); + $berita->santriTertentu()->sync($santriKelas); + } else { + $berita->santriTertentu()->detach(); + } + + return redirect()->route('admin.berita.index') + ->with('success', 'Berita berhasil diperbarui!'); + } + + /** + * Hapus berita + */ + public function destroy(Berita $berita) + { + // Hapus gambar jika ada + if ($berita->gambar) { + Storage::disk('public')->delete($berita->gambar); + } + + $berita->delete(); + + return redirect()->route('admin.berita.index') + ->with('success', 'Berita berhasil dihapus!'); + } + + /** + * Tampilkan statistik berita + */ + public function statistik() + { + $totalBerita = Berita::count(); + $totalPublished = Berita::where('status', 'published')->count(); + $totalDraft = Berita::where('status', 'draft')->count(); + $beritaSemua = Berita::where('target_berita', 'semua')->count(); + $beritaTertentu = Berita::where('target_berita', 'santri_tertentu')->count(); + + return view('admin.berita.statistik', compact( + 'totalBerita', + 'totalPublished', + 'totalDraft', + 'beritaSemua', + 'beritaTertentu' + )); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/CapaianController.php b/sim-pkpps/app/Http/Controllers/Admin/CapaianController.php new file mode 100644 index 0000000..023c0bf --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/CapaianController.php @@ -0,0 +1,637 @@ +filled('id_santri')) { + $query->bySantri($request->id_santri); + } + + // Filter semester + if ($request->filled('id_semester')) { + $query->bySemester($request->id_semester); + } + + // Filter kategori + if ($request->filled('kategori')) { + $query->byKategori($request->kategori); + } + + $capaians = $query->orderBy('created_at', 'desc') + ->paginate(20) + ->appends(request()->query()); + + // Data untuk filter + $santris = Santri::aktif()->orderBy('nama_lengkap')->get(); + $semesters = Semester::orderBy('tahun_ajaran', 'desc')->get(); + + return view('admin.capaian.index', compact('capaians', 'santris', 'semesters')); + } + + /** + * Show the form for creating new capaian + */ + public function create(Request $request) + { + // Get santri list + $santris = Santri::aktif() + ->select('id', 'id_santri', 'nis', 'nama_lengkap', 'kelas') + ->orderBy('nama_lengkap') + ->get(); + + // Get semester aktif + $semesterAktif = Semester::aktif()->first(); + $semesters = Semester::orderBy('tahun_ajaran', 'desc')->get(); + + // Jika ada pre-selected santri + $selectedSantri = null; + $materiOptions = []; + + if ($request->filled('id_santri')) { + $selectedSantri = Santri::where('id_santri', $request->id_santri)->first(); + if ($selectedSantri) { + // Get materi sesuai kelas santri + $materiOptions = Materi::where('kelas', $selectedSantri->kelas) + ->orderBy('kategori') + ->orderBy('nama_kitab') + ->get(); + } + } + + return view('admin.capaian.create', compact('santris', 'semesters', 'semesterAktif', 'selectedSantri', 'materiOptions')); + } + + /** + * Get materi by santri kelas (AJAX) + */ + public function getMateriByKelas(Request $request) + { + $santri = Santri::where('id_santri', $request->id_santri)->first(); + + if (!$santri) { + return response()->json(['error' => 'Santri tidak ditemukan'], 404); + } + + $materis = Materi::where('kelas', $santri->kelas) + ->select('id', 'id_materi', 'kategori', 'nama_kitab', 'halaman_mulai', 'halaman_akhir', 'total_halaman') + ->orderBy('kategori') + ->orderBy('nama_kitab') + ->get(); + + return response()->json([ + 'kelas' => $santri->kelas, + 'materis' => $materis + ]); + } + + /** + * Get detail materi (AJAX) + */ + public function getDetailMateri(Request $request) + { + $materi = Materi::where('id_materi', $request->id_materi)->first(); + + if (!$materi) { + return response()->json(['error' => 'Materi tidak ditemukan'], 404); + } + + // Check existing capaian + $existingCapaian = null; + if ($request->filled('id_santri') && $request->filled('id_semester')) { + $existingCapaian = Capaian::where('id_santri', $request->id_santri) + ->where('id_materi', $request->id_materi) + ->where('id_semester', $request->id_semester) + ->first(); + } + + return response()->json([ + 'materi' => $materi, + 'existing_capaian' => $existingCapaian + ]); + } + + /** + * Store a newly created capaian + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'id_materi' => 'required|exists:materi,id_materi', + 'id_semester' => 'required|exists:semester,id_semester', + 'halaman_selesai' => 'required|string', + 'catatan' => 'nullable|string', + 'tanggal_input' => 'required|date', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'id_materi.required' => 'Materi wajib dipilih.', + 'id_semester.required' => 'Semester wajib dipilih.', + 'halaman_selesai.required' => 'Halaman yang selesai wajib diisi.', + 'tanggal_input.required' => 'Tanggal input wajib diisi.', + ]); + + // Check duplikasi + $existing = Capaian::where('id_santri', $validated['id_santri']) + ->where('id_materi', $validated['id_materi']) + ->where('id_semester', $validated['id_semester']) + ->first(); + + if ($existing) { + return redirect()->back() + ->withInput() + ->with('error', 'Capaian untuk santri, materi, dan semester ini sudah ada. Silakan edit data yang ada.'); + } + + Capaian::create($validated); + + return redirect()->route('admin.capaian.index') + ->with('success', 'Capaian berhasil ditambahkan.'); + } + + /** + * Display the specified capaian + */ + public function show(Capaian $capaian) + { + $capaian->load(['santri', 'materi', 'semester']); + + return view('admin.capaian.show', compact('capaian')); + } + + /** + * Show the form for editing the specified capaian + */ + public function edit(Capaian $capaian) + { + $capaian->load(['santri', 'materi', 'semester']); + $semesters = Semester::orderBy('tahun_ajaran', 'desc')->get(); + + return view('admin.capaian.edit', compact('capaian', 'semesters')); + } + + /** + * Update the specified capaian + */ + public function update(Request $request, Capaian $capaian) + { + $validated = $request->validate([ + 'halaman_selesai' => 'required|string', + 'catatan' => 'nullable|string', + 'tanggal_input' => 'required|date', + ], [ + 'halaman_selesai.required' => 'Halaman yang selesai wajib diisi.', + 'tanggal_input.required' => 'Tanggal input wajib diisi.', + ]); + + $capaian->update($validated); + + return redirect()->route('admin.capaian.show', $capaian) + ->with('success', 'Capaian berhasil diperbarui.'); + } + + /** + * Remove the specified capaian + */ + public function destroy(Capaian $capaian) + { + $santriNama = $capaian->santri->nama_lengkap; + $materiNama = $capaian->materi->nama_kitab; + + $capaian->delete(); + + return redirect()->route('admin.capaian.index') + ->with('success', "Capaian {$santriNama} untuk materi {$materiNama} berhasil dihapus."); + } + + /** + * Show riwayat capaian per santri + */ + public function riwayatSantri($id_santri, Request $request) + { + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + + $query = Capaian::with(['materi', 'semester']) + ->bySantri($id_santri); + + // Filter semester + if ($request->filled('id_semester')) { + $query->bySemester($request->id_semester); + } + + $capaians = $query->orderBy('created_at', 'desc') + ->paginate(15) + ->appends(request()->query()); + + // Statistik + $totalCapaian = $capaians->total(); + $rataRataPersentase = Capaian::bySantri($id_santri)->avg('persentase') ?? 0; + + // Statistik per kategori + $statistikKategori = Capaian::bySantri($id_santri) + ->join('materi', 'capaian.id_materi', '=', 'materi.id_materi') + ->select('materi.kategori', DB::raw('AVG(capaian.persentase) as rata_rata')) + ->groupBy('materi.kategori') + ->get() + ->pluck('rata_rata', 'kategori') + ->toArray(); + + $semesters = Semester::orderBy('tahun_ajaran', 'desc')->get(); + + return view('admin.capaian.riwayat-santri', compact('santri', 'capaians', 'totalCapaian', 'rataRataPersentase', 'statistikKategori', 'semesters')); + } + + /** + * Calculate persentase (AJAX untuk preview) + */ + public function calculatePersentase(Request $request) + { + $halamanSelesai = $request->halaman_selesai; + $idMateri = $request->id_materi; + + if (empty($halamanSelesai) || empty($idMateri)) { + return response()->json(['persentase' => 0, 'jumlah' => 0]); + } + + try { + $persentase = Capaian::calculatePersentase($halamanSelesai, $idMateri); + $pages = Capaian::parseHalamanSelesai($halamanSelesai); + $jumlah = count($pages); + + return response()->json([ + 'persentase' => number_format($persentase, 2), + 'jumlah' => $jumlah, + 'pages' => $pages + ]); + } catch (\Exception $e) { + return response()->json(['error' => $e->getMessage()], 400); + } + } + + /** + * Dashboard capaian dengan grafik + */ +public function dashboard(Request $request) +{ + // Get filter inputs + $idSantri = $request->input('id_santri'); + $idSemester = $request->input('id_semester'); + $kelas = $request->input('kelas'); + + // Get semester aktif sebagai default + $semesterAktif = Semester::aktif()->first(); + $selectedSemester = $idSemester ?: ($semesterAktif ? $semesterAktif->id_semester : null); + + // Data untuk filter + $santris = Santri::aktif()->orderBy('nama_lengkap')->get(); + $semesters = Semester::orderBy('tahun_ajaran', 'desc')->get(); + + // Build query capaian + $query = Capaian::with(['santri', 'materi', 'semester']); + + if ($idSantri) { + $query->bySantri($idSantri); + } + + if ($selectedSemester) { + $query->bySemester($selectedSemester); + } + + if ($kelas) { + $query->whereHas('santri', function($q) use ($kelas) { + $q->where('kelas', $kelas); + }); + } + + // Get data + $capaians = $query->get(); + + // Statistik Umum + $totalCapaian = $capaians->count(); + $totalSantri = $capaians->pluck('id_santri')->unique()->count(); + $rataRataPersentase = $capaians->avg('persentase') ?? 0; + $capaianSelesai = $capaians->where('persentase', '>=', 100)->count(); + + // Statistik per Kategori + $statistikKategori = [ + 'Al-Qur\'an' => [ + 'count' => 0, + 'avg' => 0, + 'selesai' => 0, + ], + 'Hadist' => [ + 'count' => 0, + 'avg' => 0, + 'selesai' => 0, + ], + 'Materi Tambahan' => [ + 'count' => 0, + 'avg' => 0, + 'selesai' => 0, + ], + ]; + + foreach ($capaians as $capaian) { + $kategori = $capaian->materi->kategori; + $statistikKategori[$kategori]['count']++; + $statistikKategori[$kategori]['avg'] += $capaian->persentase; + if ($capaian->persentase >= 100) { + $statistikKategori[$kategori]['selesai']++; + } + } + + // Calculate average + foreach ($statistikKategori as $kategori => $data) { + if ($data['count'] > 0) { + $statistikKategori[$kategori]['avg'] = $data['avg'] / $data['count']; + } + } + + // Data untuk grafik distribusi persentase + $distribusiPersentase = [ + '0-25%' => $capaians->whereBetween('persentase', [0, 25])->count(), + '26-50%' => $capaians->whereBetween('persentase', [26, 50])->count(), + '51-75%' => $capaians->whereBetween('persentase', [51, 75])->count(), + '76-99%' => $capaians->whereBetween('persentase', [76, 99])->count(), + '100%' => $capaians->where('persentase', '>=', 100)->count(), + ]; + + // Top 10 Santri dengan Progress Tertinggi + $topSantri = Capaian::select('id_santri', DB::raw('AVG(persentase) as rata_rata')) + ->when($selectedSemester, function($q) use ($selectedSemester) { + return $q->where('id_semester', $selectedSemester); + }) + ->when($kelas, function($q) use ($kelas) { + return $q->whereHas('santri', function($query) use ($kelas) { + $query->where('kelas', $kelas); + }); + }) + ->groupBy('id_santri') + ->orderBy('rata_rata', 'desc') + ->limit(10) + ->with('santri') + ->get(); + + // Materi dengan Progress Terendah + $materiTerendah = Capaian::select('id_materi', DB::raw('AVG(persentase) as rata_rata'), DB::raw('COUNT(*) as jumlah_santri')) + ->when($selectedSemester, function($q) use ($selectedSemester) { + return $q->where('id_semester', $selectedSemester); + }) + ->groupBy('id_materi') + ->having('rata_rata', '<', 50) + ->orderBy('rata_rata', 'asc') + ->limit(5) + ->with('materi') + ->get(); + + return view('admin.capaian.dashboard', compact( + 'santris', + 'semesters', + 'semesterAktif', + 'selectedSemester', + 'idSantri', + 'kelas', + 'totalCapaian', + 'totalSantri', + 'rataRataPersentase', + 'capaianSelesai', + 'statistikKategori', + 'distribusiPersentase', + 'topSantri', + 'materiTerendah' + )); +} + +/** + * Rekap capaian per kelas + */ +public function rekapKelas(Request $request) +{ + $kelas = $request->input('kelas', 'Lambatan'); + $idSemester = $request->input('id_semester'); + + $semesterAktif = Semester::aktif()->first(); + $selectedSemester = $idSemester ?: ($semesterAktif ? $semesterAktif->id_semester : null); + + // Get santri per kelas + $santris = Santri::where('kelas', $kelas) + ->where('status', 'Aktif') + ->orderBy('nama_lengkap') + ->get(); + + // Get capaian per santri + $rekapData = []; + foreach ($santris as $santri) { + $capaians = Capaian::where('id_santri', $santri->id_santri) + ->when($selectedSemester, function($q) use ($selectedSemester) { + return $q->where('id_semester', $selectedSemester); + }) + ->with('materi') + ->get(); + + $rataRata = $capaians->avg('persentase') ?? 0; + $totalMateri = $capaians->count(); + $selesai = $capaians->where('persentase', '>=', 100)->count(); + + // Per kategori + $alquran = $capaians->filter(function($c) { + return $c->materi->kategori == 'Al-Qur\'an'; + })->avg('persentase') ?? 0; + + $hadist = $capaians->filter(function($c) { + return $c->materi->kategori == 'Hadist'; + })->avg('persentase') ?? 0; + + $tambahan = $capaians->filter(function($c) { + return $c->materi->kategori == 'Materi Tambahan'; + })->avg('persentase') ?? 0; + + $rekapData[] = [ + 'santri' => $santri, + 'rata_rata' => $rataRata, + 'total_materi' => $totalMateri, + 'selesai' => $selesai, + 'alquran' => $alquran, + 'hadist' => $hadist, + 'tambahan' => $tambahan, + ]; + } + + // Sort by rata-rata desc + usort($rekapData, function($a, $b) { + return $b['rata_rata'] <=> $a['rata_rata']; + }); + + $semesters = Semester::orderBy('tahun_ajaran', 'desc')->get(); + + return view('admin.capaian.rekap-kelas', compact('rekapData', 'kelas', 'semesters', 'selectedSemester')); +} + + /** + * Detail capaian per materi (semua santri) + */ + public function detailMateri($id_materi, Request $request) + { + $materi = Materi::where('id_materi', $id_materi)->firstOrFail(); + + $idSemester = $request->input('id_semester'); + $semesterAktif = Semester::aktif()->first(); + $selectedSemester = $idSemester ?: ($semesterAktif ? $semesterAktif->id_semester : null); + + // Get all capaian untuk materi ini + $capaians = Capaian::where('id_materi', $id_materi) + ->when($selectedSemester, function($q) use ($selectedSemester) { + return $q->where('id_semester', $selectedSemester); + }) + ->with(['santri', 'semester']) + ->orderBy('persentase', 'desc') + ->get(); + + // Statistik + $totalSantri = $capaians->count(); + $rataRataPersentase = $capaians->avg('persentase') ?? 0; + $santriSelesai = $capaians->where('persentase', '>=', 100)->count(); + $santriMulai = $capaians->where('persentase', '>', 0)->where('persentase', '<', 100)->count(); + + // Distribusi persentase + $distribusi = [ + '0-25%' => $capaians->whereBetween('persentase', [0, 25])->count(), + '26-50%' => $capaians->whereBetween('persentase', [26, 50])->count(), + '51-75%' => $capaians->whereBetween('persentase', [51, 75])->count(), + '76-99%' => $capaians->whereBetween('persentase', [76, 99])->count(), + '100%' => $capaians->where('persentase', '>=', 100)->count(), + ]; + + $semesters = Semester::orderBy('tahun_ajaran', 'desc')->get(); + + return view('admin.capaian.detail-materi', compact( + 'materi', + 'capaians', + 'totalSantri', + 'rataRataPersentase', + 'santriSelesai', + 'santriMulai', + 'distribusi', + 'semesters', + 'selectedSemester' + )); + } + + /** + * API untuk data grafik (AJAX) + */ + public function apiGrafikData(Request $request) + { + $type = $request->input('type', 'kategori'); + $idSemester = $request->input('id_semester'); + $kelas = $request->input('kelas'); + + $query = Capaian::with(['santri', 'materi']); + + if ($idSemester) { + $query->bySemester($idSemester); + } + + if ($kelas) { + $query->whereHas('santri', function($q) use ($kelas) { + $q->where('kelas', $kelas); + }); + } + + $data = []; + + switch ($type) { + case 'kategori': + $data = [ + 'labels' => ['Al-Qur\'an', 'Hadist', 'Materi Tambahan'], + 'datasets' => [[ + 'label' => 'Rata-rata Progress (%)', + 'data' => [ + $query->clone()->byKategori('Al-Qur\'an')->avg('persentase') ?? 0, + $query->clone()->byKategori('Hadist')->avg('persentase') ?? 0, + $query->clone()->byKategori('Materi Tambahan')->avg('persentase') ?? 0, + ], + 'backgroundColor' => [ + 'rgba(111, 186, 157, 0.8)', + 'rgba(129, 198, 232, 0.8)', + 'rgba(255, 213, 107, 0.8)', + ], + ]] + ]; + break; + + case 'distribusi': + $capaians = $query->get(); + $data = [ + 'labels' => ['0-25%', '26-50%', '51-75%', '76-99%', '100%'], + 'datasets' => [[ + 'label' => 'Jumlah Santri', + 'data' => [ + $capaians->whereBetween('persentase', [0, 25])->count(), + $capaians->whereBetween('persentase', [26, 50])->count(), + $capaians->whereBetween('persentase', [51, 75])->count(), + $capaians->whereBetween('persentase', [76, 99])->count(), + $capaians->where('persentase', '>=', 100)->count(), + ], + 'backgroundColor' => [ + 'rgba(255, 139, 148, 0.8)', + 'rgba(255, 171, 145, 0.8)', + 'rgba(255, 213, 107, 0.8)', + 'rgba(129, 198, 232, 0.8)', + 'rgba(111, 186, 157, 0.8)', + ], + ]] + ]; + break; + + case 'trend': + // Get data per semester + $semesters = Semester::orderBy('tahun_ajaran')->orderBy('periode')->get(); + $labels = []; + $dataPoints = []; + + foreach ($semesters as $semester) { + $labels[] = $semester->nama_semester; + $avg = Capaian::where('id_semester', $semester->id_semester) + ->when($kelas, function($q) use ($kelas) { + return $q->whereHas('santri', function($query) use ($kelas) { + $query->where('kelas', $kelas); + }); + }) + ->avg('persentase') ?? 0; + $dataPoints[] = round($avg, 2); + } + + $data = [ + 'labels' => $labels, + 'datasets' => [[ + 'label' => 'Rata-rata Progress (%)', + 'data' => $dataPoints, + 'borderColor' => 'rgba(111, 186, 157, 1)', + 'backgroundColor' => 'rgba(111, 186, 157, 0.2)', + 'tension' => 0.4, + ]] + ]; + break; + } + + return response()->json($data); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/KartuRfidController.php b/sim-pkpps/app/Http/Controllers/Admin/KartuRfidController.php new file mode 100644 index 0000000..dfaedba --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/KartuRfidController.php @@ -0,0 +1,91 @@ +filled('filter')) { + if ($request->filter == 'ada_rfid') { + $query->whereNotNull('rfid_uid'); + } elseif ($request->filter == 'belum_rfid') { + $query->whereNull('rfid_uid'); + } + } + + $santris = $query->select('id', 'id_santri', 'nama_lengkap', 'kelas', 'rfid_uid') + ->orderBy('nama_lengkap') + ->paginate(15); + + return view('admin.kegiatan.kartu.index', compact('santris')); + } + + /** + * Form daftarkan RFID ke santri + */ + public function daftarRfid($id_santri) + { + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + return view('admin.kegiatan.kartu.daftar', compact('santri')); + } + + /** + * Simpan RFID UID ke santri + */ + public function simpanRfid(Request $request, $id_santri) + { + $validated = $request->validate([ + 'rfid_uid' => 'required|string|max:50|unique:santris,rfid_uid', + ], [ + 'rfid_uid.required' => 'UID RFID wajib diisi.', + 'rfid_uid.unique' => 'UID RFID ini sudah terdaftar pada santri lain.', + ]); + + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + $santri->update(['rfid_uid' => $request->rfid_uid]); + + return redirect()->route('admin.kartu-rfid.index') + ->with('success', 'RFID berhasil didaftarkan untuk ' . $santri->nama_lengkap); + } + + /** + * Hapus RFID dari santri + */ + public function hapusRfid($id_santri) + { + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + $santri->update(['rfid_uid' => null]); + + return redirect()->route('admin.kartu-rfid.index') + ->with('success', 'RFID berhasil dihapus dari ' . $santri->nama_lengkap); + } + + /** + * Cetak kartu RFID santri (PDF) + */ + public function cetakKartu($id_santri) + { + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + + if (!$santri->rfid_uid) { + return back()->with('error', 'Santri belum memiliki RFID yang terdaftar.'); + } + + $pdf = Pdf::loadView('admin.kegiatan.kartu.cetak', compact('santri')); + $pdf->setPaper([0, 0, 243, 153], 'landscape'); // Ukuran kartu ID (85.6mm x 54mm) + + return $pdf->stream('Kartu_RFID_' . $santri->id_santri . '.pdf'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/KategoriKegiatanController.php b/sim-pkpps/app/Http/Controllers/Admin/KategoriKegiatanController.php new file mode 100644 index 0000000..ee894b9 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/KategoriKegiatanController.php @@ -0,0 +1,119 @@ +filled('search')) { + $search = $request->search; + $query->where(function($q) use ($search) { + $q->where('nama_kategori', 'like', "%{$search}%") + ->orWhere('kategori_id', 'like', "%{$search}%") + ->orWhere('keterangan', 'like', "%{$search}%"); + }); + } + + $kategoris = $query->select('id', 'kategori_id', 'nama_kategori', 'keterangan', 'created_at') + ->orderBy('created_at', 'desc') + ->paginate(10) + ->appends(request()->query()); + + return view('admin.kegiatan.kategori.index', compact('kategoris')); + } + + /** + * Form tambah kategori + */ + public function create() + { + // Preview ID berikutnya + $nextId = Cache::remember('next_kategori_id', 60, function () { + $last = KategoriKegiatan::select('kategori_id')->orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->kategori_id, 2)) + 1 : 1; + return 'KT' . str_pad($num, 3, '0', STR_PAD_LEFT); + }); + + return view('admin.kegiatan.kategori.create', compact('nextId')); + } + + /** + * Simpan kategori baru + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'nama_kategori' => 'required|string|max:100|unique:kategori_kegiatans,nama_kategori', + 'keterangan' => 'nullable|string', + ], [ + 'nama_kategori.required' => 'Nama kategori wajib diisi.', + 'nama_kategori.unique' => 'Nama kategori sudah digunakan.', + ]); + + KategoriKegiatan::create($validated); + Cache::forget('next_kategori_id'); + + return redirect()->route('admin.kategori-kegiatan.index') + ->with('success', 'Kategori kegiatan berhasil ditambahkan.'); + } + + /** + * Tampilkan detail kategori + */ + public function show(KategoriKegiatan $kategoriKegiatan) + { + return view('admin.kegiatan.kategori.show', compact('kategoriKegiatan')); + } + + /** + * Form edit kategori + */ + public function edit(KategoriKegiatan $kategoriKegiatan) + { + return view('admin.kegiatan.kategori.edit', compact('kategoriKegiatan')); + } + + /** + * Update kategori + */ + public function update(Request $request, KategoriKegiatan $kategoriKegiatan) + { + $validated = $request->validate([ + 'nama_kategori' => 'required|string|max:100|unique:kategori_kegiatans,nama_kategori,' . $kategoriKegiatan->id, + 'keterangan' => 'nullable|string', + ], [ + 'nama_kategori.required' => 'Nama kategori wajib diisi.', + 'nama_kategori.unique' => 'Nama kategori sudah digunakan.', + ]); + + $kategoriKegiatan->update($validated); + + return redirect()->route('admin.kategori-kegiatan.index') + ->with('success', 'Kategori kegiatan berhasil diperbarui.'); + } + + /** + * Hapus kategori + */ + public function destroy(KategoriKegiatan $kategoriKegiatan) + { + $nama = $kategoriKegiatan->nama_kategori; + $kategoriKegiatan->delete(); + Cache::forget('next_kategori_id'); + + return redirect()->route('admin.kategori-kegiatan.index') + ->with('success', "Kategori \"$nama\" berhasil dihapus."); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/KategoriPelanggaranController.php b/sim-pkpps/app/Http/Controllers/Admin/KategoriPelanggaranController.php new file mode 100644 index 0000000..1ba273e --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/KategoriPelanggaranController.php @@ -0,0 +1,118 @@ +get(); + + return view('admin.kategori_pelanggaran.index', compact('data')); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // Generate preview ID kategori berikutnya + $lastKategori = KategoriPelanggaran::orderBy('id', 'desc')->first(); + $nextNum = $lastKategori ? intval(substr($lastKategori->id_kategori, 2)) + 1 : 1; + $nextIdKategori = 'KP' . str_pad($nextNum, 3, '0', STR_PAD_LEFT); + + return view('admin.kategori_pelanggaran.create', compact('nextIdKategori')); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'nama_pelanggaran' => 'required|string|max:255', + 'poin' => 'required|integer|min:1|max:100', + ], [ + 'nama_pelanggaran.required' => 'Nama pelanggaran wajib diisi.', + 'poin.required' => 'Poin wajib diisi.', + 'poin.min' => 'Poin minimal 1.', + 'poin.max' => 'Poin maksimal 100.', + ]); + + KategoriPelanggaran::create($validated); + + return redirect()->route('admin.kategori-pelanggaran.index') + ->with('success', 'Kategori pelanggaran berhasil ditambahkan.'); + } + + /** + * Display the specified resource. + */ + public function show(KategoriPelanggaran $kategoriPelanggaran) + { + $kategoriPelanggaran->load('riwayatPelanggaran.santri'); + + return view('admin.kategori_pelanggaran.show', [ + 'kategori' => $kategoriPelanggaran + ]); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(KategoriPelanggaran $kategoriPelanggaran) + { + return view('admin.kategori_pelanggaran.index', [ + 'data' => KategoriPelanggaran::orderBy('created_at', 'desc')->get(), + 'kategori' => $kategoriPelanggaran + ]); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, KategoriPelanggaran $kategoriPelanggaran) + { + $validated = $request->validate([ + 'nama_pelanggaran' => 'required|string|max:255', + 'poin' => 'required|integer|min:1|max:100', + ], [ + 'nama_pelanggaran.required' => 'Nama pelanggaran wajib diisi.', + 'poin.required' => 'Poin wajib diisi.', + 'poin.min' => 'Poin minimal 1.', + 'poin.max' => 'Poin maksimal 100.', + ]); + + $kategoriPelanggaran->update($validated); + + return redirect()->route('admin.kategori-pelanggaran.index') + ->with('success', 'Kategori pelanggaran berhasil diperbarui.'); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(KategoriPelanggaran $kategoriPelanggaran) + { + $namaKategori = $kategoriPelanggaran->nama_pelanggaran; + + // Cek apakah kategori masih digunakan + if ($kategoriPelanggaran->riwayatPelanggaran()->count() > 0) { + return redirect()->route('admin.kategori-pelanggaran.index') + ->with('error', 'Kategori "' . $namaKategori . '" tidak dapat dihapus karena masih digunakan dalam riwayat pelanggaran.'); + } + + $kategoriPelanggaran->delete(); + + return redirect()->route('admin.kategori-pelanggaran.index') + ->with('success', 'Kategori "' . $namaKategori . '" berhasil dihapus.'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/KegiatanController.php b/sim-pkpps/app/Http/Controllers/Admin/KegiatanController.php new file mode 100644 index 0000000..b45542e --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/KegiatanController.php @@ -0,0 +1,151 @@ +filled('hari')) { + $query->where('hari', $request->hari); + } + + // Filter kategori + if ($request->filled('kategori_id')) { + $query->where('kategori_id', $request->kategori_id); + } + + // Search + if ($request->filled('search')) { + $query->search($request->search); + } + + $kegiatans = $query->select('id', 'kegiatan_id', 'kategori_id', 'nama_kegiatan', 'hari', 'waktu_mulai', 'waktu_selesai', 'materi') + ->orderBy('hari') + ->orderBy('waktu_mulai') + ->paginate(15) + ->appends(request()->query()); + + // Data untuk filter + $kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->get(); + $hariList = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad']; + + return view('admin.kegiatan.data.index', compact('kegiatans', 'kategoris', 'hariList')); + } + + /** + * Form tambah kegiatan + */ + public function create() + { + $nextId = Cache::remember('next_kegiatan_id', 60, function () { + $last = Kegiatan::select('kegiatan_id')->orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->kegiatan_id, 2)) + 1 : 1; + return 'KG' . str_pad($num, 3, '0', STR_PAD_LEFT); + }); + + $kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->get(); + $hariList = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad']; + + return view('admin.kegiatan.data.create', compact('nextId', 'kategoris', 'hariList')); + } + + /** + * Simpan kegiatan baru + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'kategori_id' => 'required|exists:kategori_kegiatans,kategori_id', + 'nama_kegiatan' => 'required|string|max:150', + 'hari' => 'required|in:Senin,Selasa,Rabu,Kamis,Jumat,Sabtu,Ahad', + 'waktu_mulai' => 'required|date_format:H:i', + 'waktu_selesai' => 'required|date_format:H:i|after:waktu_mulai', + 'materi' => 'nullable|string|max:200', + 'keterangan' => 'nullable|string', + ], [ + 'kategori_id.required' => 'Kategori wajib dipilih.', + 'nama_kegiatan.required' => 'Nama kegiatan wajib diisi.', + 'hari.required' => 'Hari wajib dipilih.', + 'waktu_mulai.required' => 'Waktu mulai wajib diisi.', + 'waktu_selesai.required' => 'Waktu selesai wajib diisi.', + 'waktu_selesai.after' => 'Waktu selesai harus lebih dari waktu mulai.', + ]); + + Kegiatan::create($validated); + Cache::forget('next_kegiatan_id'); + + return redirect()->route('admin.kegiatan.index') + ->with('success', 'Kegiatan berhasil ditambahkan.'); + } + + /** + * Tampilkan detail kegiatan + */ + public function show(Kegiatan $kegiatan) + { + $kegiatan->load('kategori'); + return view('admin.kegiatan.data.show', compact('kegiatan')); + } + + /** + * Form edit kegiatan + */ + public function edit(Kegiatan $kegiatan) + { + $kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->get(); + $hariList = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad']; + + return view('admin.kegiatan.data.edit', compact('kegiatan', 'kategoris', 'hariList')); + } + + /** + * Update kegiatan + */ + public function update(Request $request, Kegiatan $kegiatan) + { + $validated = $request->validate([ + 'kategori_id' => 'required|exists:kategori_kegiatans,kategori_id', + 'nama_kegiatan' => 'required|string|max:150', + 'hari' => 'required|in:Senin,Selasa,Rabu,Kamis,Jumat,Sabtu,Ahad', + 'waktu_mulai' => 'required|date_format:H:i', + 'waktu_selesai' => 'required|date_format:H:i|after:waktu_mulai', + 'materi' => 'nullable|string|max:200', + 'keterangan' => 'nullable|string', + ], [ + 'kategori_id.required' => 'Kategori wajib dipilih.', + 'nama_kegiatan.required' => 'Nama kegiatan wajib diisi.', + 'waktu_selesai.after' => 'Waktu selesai harus lebih dari waktu mulai.', + ]); + + $kegiatan->update($validated); + + return redirect()->route('admin.kegiatan.index') + ->with('success', 'Kegiatan berhasil diperbarui.'); + } + + /** + * Hapus kegiatan + */ + public function destroy(Kegiatan $kegiatan) + { + $nama = $kegiatan->nama_kegiatan; + $kegiatan->delete(); + Cache::forget('next_kegiatan_id'); + + return redirect()->route('admin.kegiatan.index') + ->with('success', "Kegiatan \"$nama\" berhasil dihapus."); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/KepulanganController.php b/sim-pkpps/app/Http/Controllers/Admin/KepulanganController.php new file mode 100644 index 0000000..3b20199 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/KepulanganController.php @@ -0,0 +1,411 @@ +filled('search')) { + $query->search($request->search); + } + + // Filter status + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + // Filter tahun + if ($request->filled('tahun')) { + $query->whereYear('tanggal_pulang', $request->tahun); + } + + // Filter bulan + if ($request->filled('bulan')) { + $query->whereMonth('tanggal_pulang', $request->bulan); + } + + // Get data dengan pagination + $kepulangan = $query->orderBy('created_at', 'desc')->paginate(15); + + // Statistics + $stats = [ + 'total_data' => Kepulangan::count(), + 'menunggu_approval' => Kepulangan::where('status', 'Menunggu')->count(), + 'sedang_izin' => Kepulangan::aktif()->count(), + 'over_limit_santri' => $this->getOverLimitSantri()->count(), + ]; + + // Get unique years for filter + $tahunList = Kepulangan::selectRaw('YEAR(tanggal_pulang) as tahun') + ->distinct() + ->orderBy('tahun', 'desc') + ->pluck('tahun'); + + // Get santri yang over limit untuk highlight + $santriOverLimit = $this->getOverLimitSantri()->pluck('total_hari', 'id_santri'); + + return view('admin.kepulangan.index', compact( + 'kepulangan', + 'stats', + 'tahunList', + 'santriOverLimit' + )); + } + + /** + * Show the form for creating a new kepulangan + */ + public function create() + { + $santriList = Santri::where('status', 'Aktif') + ->orderBy('nama_lengkap') + ->get(); + + return view('admin.kepulangan.create', compact('santriList')); + } + + /** + * Store a newly created kepulangan + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'tanggal_pulang' => 'required|date|after_or_equal:today', + 'tanggal_kembali' => 'required|date|after:tanggal_pulang', + 'alasan' => 'required|string|min:10|max:500', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'id_santri.exists' => 'Santri tidak ditemukan.', + 'tanggal_pulang.required' => 'Tanggal pulang wajib diisi.', + 'tanggal_pulang.after_or_equal' => 'Tanggal pulang tidak boleh kurang dari hari ini.', + 'tanggal_kembali.required' => 'Tanggal kembali wajib diisi.', + 'tanggal_kembali.after' => 'Tanggal kembali harus setelah tanggal pulang.', + 'alasan.required' => 'Alasan kepulangan wajib diisi.', + 'alasan.min' => 'Alasan minimal 10 karakter.', + 'alasan.max' => 'Alasan maksimal 500 karakter.', + ]); + + // Create kepulangan + Kepulangan::create($validated); + + return redirect()->route('admin.kepulangan.index') + ->with('success', 'Izin kepulangan berhasil diajukan.'); + } + + /** + * Display the specified kepulangan + */ + public function show($id_kepulangan) + { + // Cari data berdasarkan id_kepulangan (KP001, KP002, dst) + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan) + ->with('santri') + ->firstOrFail(); + + // Get detail izin tahun ini + $tahunSekarang = Carbon::now()->year; + $detailIzin = $this->getDetailIzinSantri($kepulangan->id_santri, $tahunSekarang); + + // Get history kepulangan santri (exclude current) + $history = Kepulangan::where('id_santri', $kepulangan->id_santri) + ->where('id_kepulangan', '!=', $id_kepulangan) + ->orderBy('tanggal_pulang', 'desc') + ->limit(5) + ->get(); + + return view('admin.kepulangan.show', compact( + 'kepulangan', + 'detailIzin', + 'history' + )); + } + + /** + * Show the form for editing kepulangan + */ + public function edit($id_kepulangan) + { + // Cari data berdasarkan id_kepulangan + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan)->firstOrFail(); + + // Hanya bisa edit jika status Menunggu + if ($kepulangan->status !== 'Menunggu') { + return redirect()->route('admin.kepulangan.index') + ->with('error', 'Hanya izin dengan status "Menunggu" yang bisa diedit.'); + } + + $santriList = Santri::where('status', 'Aktif') + ->orderBy('nama_lengkap') + ->get(); + + return view('admin.kepulangan.edit', compact('kepulangan', 'santriList')); + } + + /** + * Update the specified kepulangan + */ + public function update(Request $request, $id_kepulangan) + { + // Cari data berdasarkan id_kepulangan + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan)->firstOrFail(); + + // Hanya bisa update jika status Menunggu + if ($kepulangan->status !== 'Menunggu') { + return redirect()->route('admin.kepulangan.index') + ->with('error', 'Hanya izin dengan status "Menunggu" yang bisa diubah.'); + } + + $validated = $request->validate([ + 'tanggal_pulang' => 'required|date|after_or_equal:today', + 'tanggal_kembali' => 'required|date|after:tanggal_pulang', + 'alasan' => 'required|string|min:10|max:500', + ], [ + 'tanggal_pulang.required' => 'Tanggal pulang wajib diisi.', + 'tanggal_kembali.required' => 'Tanggal kembali wajib diisi.', + 'tanggal_kembali.after' => 'Tanggal kembali harus setelah tanggal pulang.', + 'alasan.required' => 'Alasan kepulangan wajib diisi.', + 'alasan.min' => 'Alasan minimal 10 karakter.', + ]); + + $kepulangan->update($validated); + + return redirect()->route('admin.kepulangan.index') + ->with('success', 'Data kepulangan berhasil diperbarui.'); + } + + /** + * Remove the specified kepulangan + */ + public function destroy($id_kepulangan) + { + // Cari data berdasarkan id_kepulangan + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan)->firstOrFail(); + + // Hanya bisa hapus jika status Menunggu atau Ditolak + if (!in_array($kepulangan->status, ['Menunggu', 'Ditolak'])) { + return response()->json([ + 'success' => false, + 'message' => 'Hanya izin dengan status "Menunggu" atau "Ditolak" yang bisa dihapus.' + ], 403); + } + + $kepulangan->delete(); + + return response()->json([ + 'success' => true, + 'message' => 'Data kepulangan berhasil dihapus.' + ]); + } + + /** + * Approve kepulangan + */ + public function approve(Request $request, $id_kepulangan) + { + // Cari data berdasarkan id_kepulangan + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan)->firstOrFail(); + + if ($kepulangan->status !== 'Menunggu') { + return response()->json([ + 'success' => false, + 'message' => 'Izin sudah diproses sebelumnya.' + ], 400); + } + + // Update status - catatan opsional (tidak perlu validasi) + $kepulangan->update([ + 'status' => 'Disetujui', + 'approved_by' => Auth::user()->name, + 'approved_at' => now(), + 'catatan' => $request->catatan ?? null, + ]); + + return response()->json([ + 'success' => true, + 'message' => 'Izin kepulangan berhasil disetujui.' + ]); + } + + /** + * Reject kepulangan + */ + public function reject(Request $request, $id_kepulangan) + { + // Cari data berdasarkan id_kepulangan + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan)->firstOrFail(); + + // Validasi alasan penolakan (wajib diisi minimal 10 karakter) + $validated = $request->validate([ + 'alasan_penolakan' => 'required|string|min:10', + ], [ + 'alasan_penolakan.required' => 'Alasan penolakan wajib diisi.', + 'alasan_penolakan.min' => 'Alasan penolakan minimal 10 karakter.', + ]); + + if ($kepulangan->status !== 'Menunggu') { + return response()->json([ + 'success' => false, + 'message' => 'Izin sudah diproses sebelumnya.' + ], 400); + } + + $kepulangan->update([ + 'status' => 'Ditolak', + 'approved_by' => Auth::user()->name, + 'approved_at' => now(), + 'catatan' => $validated['alasan_penolakan'], + ]); + + return response()->json([ + 'success' => true, + 'message' => 'Izin kepulangan telah ditolak.' + ]); + } + + /** + * Complete kepulangan (mark as selesai) + */ + public function complete($id_kepulangan) + { + // Cari data berdasarkan id_kepulangan + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan)->firstOrFail(); + + if ($kepulangan->status !== 'Disetujui') { + return response()->json([ + 'success' => false, + 'message' => 'Hanya izin yang disetujui yang bisa diselesaikan.' + ], 400); + } + + $kepulangan->update([ + 'status' => 'Selesai', + ]); + + return response()->json([ + 'success' => true, + 'message' => 'Kepulangan santri berhasil diselesaikan.' + ]); + } + + /** + * Print surat izin kepulangan + */ + public function print($id_kepulangan) + { + // Cari data berdasarkan id_kepulangan + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan) + ->with('santri') + ->firstOrFail(); + + if ($kepulangan->status !== 'Disetujui') { + return redirect()->route('admin.kepulangan.show', $id_kepulangan) + ->with('error', 'Hanya izin yang disetujui yang bisa dicetak.'); + } + + $santri = $kepulangan->santri; + $tanggalCetak = Carbon::now()->format('d F Y'); + + $pdf = Pdf::loadView('admin.kepulangan.surat-pdf', compact( + 'kepulangan', + 'santri', + 'tanggalCetak' + )); + + return $pdf->stream('Surat-Izin-' . $kepulangan->id_kepulangan . '.pdf'); + } + + /** + * API: Get santri data with penggunaan izin + */ + public function getSantriData($idSantri) + { + $santri = Santri::where('id_santri', $idSantri)->first(); + + if (!$santri) { + return response()->json([ + 'success' => false, + 'message' => 'Santri tidak ditemukan.' + ], 404); + } + + $tahunSekarang = Carbon::now()->year; + $penggunaanIzin = $this->getDetailIzinSantri($idSantri, $tahunSekarang); + + return response()->json([ + 'success' => true, + 'santri' => $santri, + 'penggunaan_izin' => [ + 'total_hari' => $penggunaanIzin['total_hari'], + 'total_izin' => $penggunaanIzin['total_izin'], + 'sisa_kuota' => $penggunaanIzin['sisa_kuota'], + 'over_limit' => $penggunaanIzin['over_limit'], + ] + ]); + } + + /** + * Helper: Get detail izin santri per tahun + */ + private function getDetailIzinSantri($idSantri, $tahun) + { + $kepulanganList = Kepulangan::where('id_santri', $idSantri) + ->where('status', 'Disetujui') + ->whereYear('tanggal_pulang', $tahun) + ->orderBy('tanggal_pulang', 'desc') + ->get(); + + $totalHari = $kepulanganList->sum('durasi_izin'); + $totalIzin = $kepulanganList->count(); + $sisaKuota = max(0, 12 - $totalHari); + $overLimit = $totalHari > 12; + + $details = $kepulanganList->map(function($item) { + return [ + 'id' => $item->id_kepulangan, + 'tanggal' => $item->tanggal_pulang_formatted . ' - ' . $item->tanggal_kembali_formatted, + 'durasi' => $item->durasi_izin, + 'alasan' => $item->alasan, + ]; + }); + + return [ + 'total_hari' => $totalHari, + 'total_izin' => $totalIzin, + 'sisa_kuota' => $sisaKuota, + 'over_limit' => $overLimit, + 'details' => $details, + ]; + } + + /** + * Helper: Get santri yang over limit + */ + private function getOverLimitSantri() + { + $tahunSekarang = Carbon::now()->year; + + return Kepulangan::selectRaw('id_santri, SUM(durasi_izin) as total_hari') + ->where('status', 'Disetujui') + ->whereYear('tanggal_pulang', $tahunSekarang) + ->groupBy('id_santri') + ->having('total_hari', '>', 12) + ->get(); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/KesehatanSantriController.php b/sim-pkpps/app/Http/Controllers/Admin/KesehatanSantriController.php new file mode 100644 index 0000000..7c27e8a --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/KesehatanSantriController.php @@ -0,0 +1,240 @@ +filled('search')) { + $query->search($request->search); + } + + // Filter status + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + // Filter bulan + if ($request->filled('month')) { + $query->whereMonth('tanggal_masuk', $request->month); + } + + // Filter tahun + $year = $request->filled('year') ? $request->year : date('Y'); + $query->whereYear('tanggal_masuk', $year); + + // Urutkan terbaru + $kesehatanSantri = $query->orderBy('tanggal_masuk', 'desc')->paginate(15); + + // Data untuk filter + $statusOptions = ['dirawat', 'sembuh', 'izin']; + $monthOptions = [ + 1 => 'Januari', 2 => 'Februari', 3 => 'Maret', + 4 => 'April', 5 => 'Mei', 6 => 'Juni', + 7 => 'Juli', 8 => 'Agustus', 9 => 'September', + 10 => 'Oktober', 11 => 'November', 12 => 'Desember' + ]; + $yearOptions = range(date('Y'), date('Y') - 5); + + return view('admin.kesehatan-santri.index', compact( + 'kesehatanSantri', + 'statusOptions', + 'monthOptions', + 'yearOptions' + )); + } + + /** + * Tampilkan form tambah data + */ + public function create() + { + // Ambil semua santri aktif + $santri = Santri::where('status', 'Aktif') + ->orderBy('nama_lengkap') + ->get(); + + return view('admin.kesehatan-santri.create', compact('santri')); + } + + /** + * Simpan data kesehatan baru + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'tanggal_masuk' => 'required|date|before_or_equal:today', + 'tanggal_keluar' => 'nullable|date|after_or_equal:tanggal_masuk|before_or_equal:today', + 'keluhan' => 'required|string|max:1000', + 'catatan' => 'nullable|string|max:1000', + 'status' => 'required|in:dirawat,sembuh,izin', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'id_santri.exists' => 'Santri tidak ditemukan.', + 'tanggal_masuk.required' => 'Tanggal masuk wajib diisi.', + 'tanggal_masuk.before_or_equal' => 'Tanggal masuk tidak boleh melebihi hari ini.', + 'tanggal_keluar.after_or_equal' => 'Tanggal keluar harus setelah tanggal masuk.', + 'keluhan.required' => 'Keluhan wajib diisi.', + 'keluhan.max' => 'Keluhan maksimal 1000 karakter.', + 'status.required' => 'Status wajib dipilih.', + ]); + + // Validasi: Jika status bukan dirawat, tanggal keluar wajib diisi + if ($validated['status'] != 'dirawat' && empty($validated['tanggal_keluar'])) { + return back()->withErrors([ + 'tanggal_keluar' => 'Tanggal keluar wajib diisi untuk status ' . $validated['status'] + ])->withInput(); + } + + // Jika status dirawat, kosongkan tanggal keluar + if ($validated['status'] == 'dirawat') { + $validated['tanggal_keluar'] = null; + } + + KesehatanSantri::create($validated); + + return redirect()->route('admin.kesehatan-santri.index') + ->with('success', 'Data kesehatan santri berhasil ditambahkan.'); + } + + /** + * Tampilkan detail data kesehatan + */ + public function show(KesehatanSantri $kesehatanSantri) + { + // Load relasi santri + $kesehatanSantri->load('santri'); + + // Ambil riwayat kesehatan santri lainnya (5 data terbaru, kecuali data saat ini) + $riwayatKesehatan = KesehatanSantri::where('id_santri', $kesehatanSantri->id_santri) + ->where('id', '!=', $kesehatanSantri->id) + ->orderBy('tanggal_masuk', 'desc') + ->take(5) + ->get(); + + return view('admin.kesehatan-santri.show', compact('kesehatanSantri', 'riwayatKesehatan')); + } + + /** + * Tampilkan form edit + */ + public function edit(KesehatanSantri $kesehatanSantri) + { + // Ambil semua santri aktif + $santri = Santri::where('status', 'Aktif') + ->orderBy('nama_lengkap') + ->get(); + + return view('admin.kesehatan-santri.edit', compact('kesehatanSantri', 'santri')); + } + + /** + * Update data kesehatan + */ + public function update(Request $request, KesehatanSantri $kesehatanSantri) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'tanggal_masuk' => 'required|date|before_or_equal:today', + 'tanggal_keluar' => 'nullable|date|after_or_equal:tanggal_masuk|before_or_equal:today', + 'keluhan' => 'required|string|max:1000', + 'catatan' => 'nullable|string|max:1000', + 'status' => 'required|in:dirawat,sembuh,izin', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'tanggal_masuk.required' => 'Tanggal masuk wajib diisi.', + 'tanggal_keluar.after_or_equal' => 'Tanggal keluar harus setelah tanggal masuk.', + 'keluhan.required' => 'Keluhan wajib diisi.', + 'status.required' => 'Status wajib dipilih.', + ]); + + // Validasi: Jika status bukan dirawat, tanggal keluar wajib diisi + if ($validated['status'] != 'dirawat' && empty($validated['tanggal_keluar'])) { + return back()->withErrors([ + 'tanggal_keluar' => 'Tanggal keluar wajib diisi untuk status ' . $validated['status'] + ])->withInput(); + } + + // Jika status dirawat, kosongkan tanggal keluar + if ($validated['status'] == 'dirawat') { + $validated['tanggal_keluar'] = null; + } + + $kesehatanSantri->update($validated); + + return redirect()->route('admin.kesehatan-santri.index') + ->with('success', 'Data kesehatan santri berhasil diperbarui.'); + } + + /** + * Hapus data kesehatan + */ + public function destroy(KesehatanSantri $kesehatanSantri) + { + $namaSantri = $kesehatanSantri->santri->nama_lengkap; + $kesehatanSantri->delete(); + + return redirect()->route('admin.kesehatan-santri.index') + ->with('success', 'Data kesehatan "' . $namaSantri . '" berhasil dihapus.'); + } + + /** + * Update status keluar UKP (via AJAX/Modal) + */ + public function keluarUkp(Request $request, KesehatanSantri $kesehatanSantri) + { + $validated = $request->validate([ + 'tanggal_keluar' => 'required|date|after_or_equal:' . $kesehatanSantri->tanggal_masuk . '|before_or_equal:today', + 'status' => 'required|in:sembuh,izin', + ], [ + 'tanggal_keluar.required' => 'Tanggal keluar wajib diisi.', + 'tanggal_keluar.after_or_equal' => 'Tanggal keluar harus setelah tanggal masuk.', + 'status.required' => 'Status wajib dipilih.', + ]); + + $kesehatanSantri->update($validated); + + return redirect()->route('admin.kesehatan-santri.index') + ->with('success', 'Santri berhasil keluar dari UKP.'); + } + + /** + * Tampilkan riwayat kesehatan per santri + */ + public function riwayat($id_santri) + { + // Cari santri + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + + // Ambil semua riwayat kesehatan santri + $riwayatKesehatan = KesehatanSantri::where('id_santri', $id_santri) + ->orderBy('tanggal_masuk', 'desc') + ->paginate(15); + + return view('admin.kesehatan-santri.riwayat', compact('santri', 'riwayatKesehatan')); + } + + /** + * Cetak surat izin sakit + */ + public function cetakSurat(KesehatanSantri $kesehatanSantri) + { + $kesehatanSantri->load('santri'); + + return view('admin.kesehatan-santri.cetak-surat', compact('kesehatanSantri')); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/MateriController.php b/sim-pkpps/app/Http/Controllers/Admin/MateriController.php new file mode 100644 index 0000000..37ecde8 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/MateriController.php @@ -0,0 +1,168 @@ +filled('kategori')) { + $query->kategori($request->kategori); + } + + // Filter berdasarkan kelas + if ($request->filled('kelas')) { + $query->kelas($request->kelas); + } + + // Search + if ($request->filled('search')) { + $query->search($request->search); + } + + // Select kolom yang diperlukan untuk optimasi + $materis = $query->select( + 'id', + 'id_materi', + 'kategori', + 'kelas', + 'nama_kitab', + 'halaman_mulai', + 'halaman_akhir', + 'total_halaman', + 'created_at' + ) + ->orderBy('kategori') + ->orderBy('kelas') + ->orderBy('nama_kitab') + ->paginate(20) + ->appends(request()->query()); + + return view('admin.materi.index', compact('materis')); + } + + /** + * Show the form for creating a new materi + */ + public function create() + { + // Generate next ID untuk preview + $nextIdMateri = Cache::remember('next_materi_id', 60, function () { + $lastMateri = Materi::select('id_materi') + ->orderBy('id', 'desc') + ->first(); + $nextNum = $lastMateri ? intval(substr($lastMateri->id_materi, 1)) + 1 : 1; + return 'M' . str_pad($nextNum, 3, '0', STR_PAD_LEFT); + }); + + return view('admin.materi.create', compact('nextIdMateri')); + } + + /** + * Store a newly created materi in storage + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'kategori' => 'required|in:Al-Qur\'an,Hadist,Materi Tambahan', + 'kelas' => 'required|in:Lambatan,Cepatan,PB', + 'nama_kitab' => 'required|string|max:255', + 'halaman_mulai' => 'required|integer|min:1', + 'halaman_akhir' => 'required|integer|min:1|gte:halaman_mulai', + 'deskripsi' => 'nullable|string', + ], [ + 'kategori.required' => 'Kategori wajib dipilih.', + 'kelas.required' => 'Kelas wajib dipilih.', + 'nama_kitab.required' => 'Nama kitab wajib diisi.', + 'halaman_mulai.required' => 'Halaman mulai wajib diisi.', + 'halaman_mulai.min' => 'Halaman mulai minimal 1.', + 'halaman_akhir.required' => 'Halaman akhir wajib diisi.', + 'halaman_akhir.gte' => 'Halaman akhir harus lebih besar atau sama dengan halaman mulai.', + ]); + + Materi::create($validated); + + // Clear cache + Cache::forget('next_materi_id'); + + return redirect()->route('admin.materi.index') + ->with('success', 'Data materi berhasil ditambahkan.'); + } + + /** + * Display the specified materi + */ + public function show(Materi $materi) + { + // Load relasi capaian jika ada (nanti di langkah 2) + // $materi->load('capaian.santri'); + + return view('admin.materi.show', compact('materi')); + } + + /** + * Show the form for editing the specified materi + */ + public function edit(Materi $materi) + { + return view('admin.materi.edit', compact('materi')); + } + + /** + * Update the specified materi in storage + */ + public function update(Request $request, Materi $materi) + { + $validated = $request->validate([ + 'kategori' => 'required|in:Al-Qur\'an,Hadist,Materi Tambahan', + 'kelas' => 'required|in:Lambatan,Cepatan,PB', + 'nama_kitab' => 'required|string|max:255', + 'halaman_mulai' => 'required|integer|min:1', + 'halaman_akhir' => 'required|integer|min:1|gte:halaman_mulai', + 'deskripsi' => 'nullable|string', + ], [ + 'kategori.required' => 'Kategori wajib dipilih.', + 'kelas.required' => 'Kelas wajib dipilih.', + 'nama_kitab.required' => 'Nama kitab wajib diisi.', + 'halaman_mulai.required' => 'Halaman mulai wajib diisi.', + 'halaman_mulai.min' => 'Halaman mulai minimal 1.', + 'halaman_akhir.required' => 'Halaman akhir wajib diisi.', + 'halaman_akhir.gte' => 'Halaman akhir harus lebih besar atau sama dengan halaman mulai.', + ]); + + $materi->update($validated); + + return redirect()->route('admin.materi.index') + ->with('success', 'Data materi berhasil diperbarui.'); + } + + /** + * Remove the specified materi from storage + */ + public function destroy(Materi $materi) + { + $namaKitab = $materi->nama_kitab; + + // TODO: Check jika ada capaian yang terkait (Langkah 2) + // if ($materi->capaian()->exists()) { + // return redirect()->route('admin.materi.index') + // ->with('error', 'Tidak dapat menghapus materi yang sudah memiliki data capaian.'); + // } + + $materi->delete(); + + return redirect()->route('admin.materi.index') + ->with('success', 'Data materi "' . $namaKitab . '" berhasil dihapus.'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/PembayaranSppController.php b/sim-pkpps/app/Http/Controllers/Admin/PembayaranSppController.php new file mode 100644 index 0000000..88d8ff4 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/PembayaranSppController.php @@ -0,0 +1,368 @@ +filled('search')) { + $query->search($request->search); + } + + // Filter status + if ($request->filled('status')) { + if ($request->status === 'Telat') { + $query->telat(); + } else { + $query->where('status', $request->status); + } + } + + // Filter tahun + if ($request->filled('tahun')) { + $query->tahun($request->tahun); + } + + // Filter bulan + if ($request->filled('bulan')) { + $query->bulan($request->bulan); + } + + $pembayaranSpp = $query->orderBy('tahun', 'desc') + ->orderBy('bulan', 'desc') + ->orderBy('created_at', 'desc') + ->paginate(20) + ->appends(request()->query()); + + // Data untuk filter + $tahunList = PembayaranSpp::selectRaw('DISTINCT tahun') + ->orderBy('tahun', 'desc') + ->pluck('tahun'); + + return view('admin.pembayaran-spp.index', compact('pembayaranSpp', 'tahunList')); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // Ambil santri yang aktif + $santris = Santri::where('status', 'Aktif') + ->orderBy('nama_lengkap', 'asc') + ->get(); + + // Generate preview ID + $last = PembayaranSpp::orderBy('id', 'desc')->first(); + $nextNum = $last ? intval(substr($last->id_pembayaran, 3)) + 1 : 1; + $nextId = 'SPP' . str_pad($nextNum, 3, '0', STR_PAD_LEFT); + + return view('admin.pembayaran-spp.create', compact('santris', 'nextId')); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'bulan' => 'required|integer|min:1|max:12', + 'tahun' => 'required|integer|min:2020|max:2100', + 'nominal' => 'required|numeric|min:0', + 'status' => 'required|in:Lunas,Belum Lunas', + 'tanggal_bayar' => 'nullable|date', + 'batas_bayar' => 'required|date', + 'keterangan' => 'nullable|string', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'id_santri.exists' => 'Santri tidak ditemukan.', + 'bulan.required' => 'Bulan wajib diisi.', + 'bulan.min' => 'Bulan harus antara 1-12.', + 'bulan.max' => 'Bulan harus antara 1-12.', + 'tahun.required' => 'Tahun wajib diisi.', + 'nominal.required' => 'Nominal wajib diisi.', + 'nominal.min' => 'Nominal minimal 0.', + 'status.required' => 'Status wajib dipilih.', + 'batas_bayar.required' => 'Batas bayar wajib diisi.', + ]); + + // Cek duplikasi + $exists = PembayaranSpp::where('id_santri', $validated['id_santri']) + ->where('bulan', $validated['bulan']) + ->where('tahun', $validated['tahun']) + ->exists(); + + if ($exists) { + return back()->withInput()->with('error', 'Data pembayaran untuk periode ini sudah ada.'); + } + + // Jika status lunas dan tanggal_bayar kosong, set ke hari ini + if ($validated['status'] === 'Lunas' && empty($validated['tanggal_bayar'])) { + $validated['tanggal_bayar'] = Carbon::now()->format('Y-m-d'); + } + + PembayaranSpp::create($validated); + + return redirect()->route('admin.pembayaran-spp.index') + ->with('success', 'Data pembayaran SPP berhasil ditambahkan.'); + } + + /** + * Display the specified resource. + */ + public function show(PembayaranSpp $pembayaranSpp) + { + $pembayaranSpp->load('santri'); + return view('admin.pembayaran-spp.show', compact('pembayaranSpp')); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(PembayaranSpp $pembayaranSpp) + { + $santris = Santri::orderBy('nama_lengkap', 'asc')->get(); + return view('admin.pembayaran-spp.edit', compact('pembayaranSpp', 'santris')); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, PembayaranSpp $pembayaranSpp) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'bulan' => 'required|integer|min:1|max:12', + 'tahun' => 'required|integer|min:2020|max:2100', + 'nominal' => 'required|numeric|min:0', + 'status' => 'required|in:Lunas,Belum Lunas', + 'tanggal_bayar' => 'nullable|date', + 'batas_bayar' => 'required|date', + 'keterangan' => 'nullable|string', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'bulan.required' => 'Bulan wajib diisi.', + 'tahun.required' => 'Tahun wajib diisi.', + 'nominal.required' => 'Nominal wajib diisi.', + 'status.required' => 'Status wajib dipilih.', + 'batas_bayar.required' => 'Batas bayar wajib diisi.', + ]); + + // Cek duplikasi (kecuali data sendiri) + $exists = PembayaranSpp::where('id_santri', $validated['id_santri']) + ->where('bulan', $validated['bulan']) + ->where('tahun', $validated['tahun']) + ->where('id', '!=', $pembayaranSpp->id) + ->exists(); + + if ($exists) { + return back()->withInput()->with('error', 'Data pembayaran untuk periode ini sudah ada.'); + } + + // Jika status lunas dan tanggal_bayar kosong, set ke hari ini + if ($validated['status'] === 'Lunas' && empty($validated['tanggal_bayar'])) { + $validated['tanggal_bayar'] = Carbon::now()->format('Y-m-d'); + } + + $pembayaranSpp->update($validated); + + return redirect()->route('admin.pembayaran-spp.index') + ->with('success', 'Data pembayaran SPP berhasil diperbarui.'); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(PembayaranSpp $pembayaranSpp) + { + $periode = $pembayaranSpp->periode_lengkap; + $santri = $pembayaranSpp->santri->nama_lengkap; + + $pembayaranSpp->delete(); + + return redirect()->route('admin.pembayaran-spp.index') + ->with('success', "Data pembayaran SPP {$periode} untuk {$santri} berhasil dihapus."); + } + + /** + * Tampilkan riwayat pembayaran per santri + */ + public function riwayat($id_santri) + { + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + + $pembayaranSpp = PembayaranSpp::where('id_santri', $id_santri) + ->orderBy('tahun', 'desc') + ->orderBy('bulan', 'desc') + ->paginate(15); + + // Statistik + $totalBayar = PembayaranSpp::where('id_santri', $id_santri) + ->where('status', 'Lunas') + ->sum('nominal'); + + $totalTunggakan = PembayaranSpp::where('id_santri', $id_santri) + ->where('status', 'Belum Lunas') + ->sum('nominal'); + + $jumlahTelat = PembayaranSpp::where('id_santri', $id_santri) + ->where('status', 'Belum Lunas') + ->where('batas_bayar', '<', Carbon::now()) + ->count(); + + return view('admin.pembayaran-spp.riwayat', compact( + 'santri', + 'pembayaranSpp', + 'totalBayar', + 'totalTunggakan', + 'jumlahTelat' + )); + } + + /** + * Generate SPP untuk semua santri aktif dalam periode tertentu + */ + public function generate(Request $request) + { + if ($request->isMethod('post')) { + $validated = $request->validate([ + 'bulan' => 'required|integer|min:1|max:12', + 'tahun' => 'required|integer|min:2020|max:2100', + 'nominal' => 'required|numeric|min:0', + 'batas_bayar' => 'required|date', + ]); + + $santris = Santri::where('status', 'Aktif')->get(); + $generated = 0; + $skipped = 0; + + foreach ($santris as $santri) { + // Cek apakah sudah ada + $exists = PembayaranSpp::where('id_santri', $santri->id_santri) + ->where('bulan', $validated['bulan']) + ->where('tahun', $validated['tahun']) + ->exists(); + + if (!$exists) { + PembayaranSpp::create([ + 'id_santri' => $santri->id_santri, + 'bulan' => $validated['bulan'], + 'tahun' => $validated['tahun'], + 'nominal' => $validated['nominal'], + 'status' => 'Belum Lunas', + 'batas_bayar' => $validated['batas_bayar'], + ]); + $generated++; + } else { + $skipped++; + } + } + + return redirect()->route('admin.pembayaran-spp.index') + ->with('success', "Berhasil generate {$generated} data SPP. {$skipped} data dilewati (sudah ada)."); + } + + return view('admin.pembayaran-spp.generate'); + } + + /** + * Halaman pilihan laporan + */ + public function laporan() + { + return view('admin.pembayaran-spp.laporan'); + } + + /** + * Cetak laporan SPP (semua data atau filter) + */ + public function cetakLaporan(Request $request) + { + $query = PembayaranSpp::with('santri'); + + // Filter + if ($request->filled('bulan')) { + $query->where('bulan', $request->bulan); + } + if ($request->filled('tahun')) { + $query->where('tahun', $request->tahun); + } + if ($request->filled('status')) { + if ($request->status === 'Telat') { + $query->telat(); + } else { + $query->where('status', $request->status); + } + } + + $pembayaranSpp = $query->orderBy('tahun', 'desc') + ->orderBy('bulan', 'desc') + ->get(); + + // Statistik + $totalLunas = $pembayaranSpp->where('status', 'Lunas')->sum('nominal'); + $totalTunggakan = $pembayaranSpp->where('status', 'Belum Lunas')->sum('nominal'); + $jumlahTelat = $pembayaranSpp->filter(function($spp) { + return $spp->isTelat(); + })->count(); + + return view('admin.pembayaran-spp.cetak-laporan', compact( + 'pembayaranSpp', + 'totalLunas', + 'totalTunggakan', + 'jumlahTelat' + )); + } + + /** + * Cetak laporan SPP per santri + */ + public function cetakLaporanSantri($id_santri) + { + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + + $pembayaranSpp = PembayaranSpp::where('id_santri', $id_santri) + ->orderBy('tahun', 'desc') + ->orderBy('bulan', 'desc') + ->get(); + + // Statistik + $totalLunas = $pembayaranSpp->where('status', 'Lunas')->sum('nominal'); + $totalTunggakan = $pembayaranSpp->where('status', 'Belum Lunas')->sum('nominal'); + $jumlahTelat = $pembayaranSpp->filter(function($spp) { + return $spp->isTelat(); + })->count(); + + return view('admin.pembayaran-spp.cetak-laporan-santri', compact( + 'santri', + 'pembayaranSpp', + 'totalLunas', + 'totalTunggakan', + 'jumlahTelat' + )); + } + + /** + * Cetak bukti pembayaran + */ + public function cetakBukti(PembayaranSpp $pembayaranSpp) + { + $pembayaranSpp->load('santri'); + return view('admin.pembayaran-spp.cetak-bukti', compact('pembayaranSpp')); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/RiwayatKegiatanController.php b/sim-pkpps/app/Http/Controllers/Admin/RiwayatKegiatanController.php new file mode 100644 index 0000000..d5d2f31 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/RiwayatKegiatanController.php @@ -0,0 +1,222 @@ +filled('id_santri')) { + $query->where('id_santri', $request->id_santri); + } + + // Filter Kategori + if ($request->filled('kategori_id')) { + $query->whereHas('kegiatan', function($q) use ($request) { + $q->where('kategori_id', $request->kategori_id); + }); + } + + // Filter Kegiatan + if ($request->filled('kegiatan_id')) { + $query->where('kegiatan_id', $request->kegiatan_id); + } + + // Filter Status + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + // Filter Tanggal + if ($request->filled('tanggal_dari')) { + $query->whereDate('tanggal', '>=', $request->tanggal_dari); + } + if ($request->filled('tanggal_sampai')) { + $query->whereDate('tanggal', '<=', $request->tanggal_sampai); + } + + // Filter Bulan + if ($request->filled('bulan')) { + $query->whereMonth('tanggal', date('m', strtotime($request->bulan))) + ->whereYear('tanggal', date('Y', strtotime($request->bulan))); + } + + $riwayats = $query->orderBy('tanggal', 'desc') + ->orderBy('waktu_absen', 'desc') + ->paginate(20) + ->appends(request()->query()); + + // Data untuk filter + $santris = Santri::where('status', 'Aktif') + ->select('id_santri', 'nama_lengkap') + ->orderBy('nama_lengkap') + ->get(); + + $kategoris = KategoriKegiatan::select('kategori_id', 'nama_kategori')->get(); + + $kegiatans = Kegiatan::select('kegiatan_id', 'nama_kegiatan') + ->orderBy('nama_kegiatan') + ->get(); + + // Statistik Global + $statsQuery = AbsensiKegiatan::query(); + + // Apply same filters to stats + if ($request->filled('id_santri')) { + $statsQuery->where('id_santri', $request->id_santri); + } + if ($request->filled('kategori_id')) { + $statsQuery->whereHas('kegiatan', function($q) use ($request) { + $q->where('kategori_id', $request->kategori_id); + }); + } + if ($request->filled('kegiatan_id')) { + $statsQuery->where('kegiatan_id', $request->kegiatan_id); + } + if ($request->filled('tanggal_dari')) { + $statsQuery->whereDate('tanggal', '>=', $request->tanggal_dari); + } + if ($request->filled('tanggal_sampai')) { + $statsQuery->whereDate('tanggal', '<=', $request->tanggal_sampai); + } + if ($request->filled('bulan')) { + $statsQuery->whereMonth('tanggal', date('m', strtotime($request->bulan))) + ->whereYear('tanggal', date('Y', strtotime($request->bulan))); + } + + $stats = $statsQuery->select('status', DB::raw('count(*) as total')) + ->groupBy('status') + ->pluck('total', 'status') + ->toArray(); + + return view('admin.kegiatan.riwayat.index', compact( + 'riwayats', + 'santris', + 'kategoris', + 'kegiatans', + 'stats' + )); + } + + /** + * Riwayat kehadiran per santri (detail) + */ + public function detailSantri($id_santri) + { + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + + // Statistik per santri + $stats = AbsensiKegiatan::where('id_santri', $id_santri) + ->select('status', DB::raw('count(*) as total')) + ->groupBy('status') + ->pluck('total', 'status') + ->toArray(); + + // Total kehadiran per kategori + $statsByKategori = AbsensiKegiatan::where('id_santri', $id_santri) + ->join('kegiatans', 'absensi_kegiatans.kegiatan_id', '=', 'kegiatans.kegiatan_id') + ->join('kategori_kegiatans', 'kegiatans.kategori_id', '=', 'kategori_kegiatans.kategori_id') + ->select( + 'kategori_kegiatans.nama_kategori', + DB::raw('SUM(CASE WHEN absensi_kegiatans.status = "Hadir" THEN 1 ELSE 0 END) as hadir'), + DB::raw('COUNT(*) as total') + ) + ->groupBy('kategori_kegiatans.nama_kategori') + ->get(); + + // Riwayat 30 hari terakhir + $riwayat30Hari = AbsensiKegiatan::where('id_santri', $id_santri) + ->whereDate('tanggal', '>=', now()->subDays(30)) + ->select( + DB::raw('DATE(tanggal) as tanggal'), + DB::raw('SUM(CASE WHEN status = "Hadir" THEN 1 ELSE 0 END) as hadir'), + DB::raw('COUNT(*) as total') + ) + ->groupBy('tanggal') + ->orderBy('tanggal', 'asc') + ->get(); + + // Riwayat lengkap + $riwayats = AbsensiKegiatan::with('kegiatan.kategori') + ->where('id_santri', $id_santri) + ->orderBy('tanggal', 'desc') + ->paginate(15); + + return view('admin.kegiatan.riwayat.detail-santri', compact( + 'santri', + 'stats', + 'statsByKategori', + 'riwayat30Hari', + 'riwayats' + )); + } + + /** + * Show detail riwayat + */ + public function show(AbsensiKegiatan $riwayat) + { + $riwayat->load(['santri', 'kegiatan.kategori']); + return view('admin.kegiatan.riwayat.show', compact('riwayat')); + } + + /** + * Edit riwayat absensi + */ + public function edit(AbsensiKegiatan $riwayat) + { + $riwayat->load(['santri', 'kegiatan']); + return view('admin.kegiatan.riwayat.edit', compact('riwayat')); + } + + /** + * Update riwayat absensi + */ + public function update(Request $request, AbsensiKegiatan $riwayat) + { + $validated = $request->validate([ + 'status' => 'required|in:Hadir,Izin,Sakit,Alpa', + 'waktu_absen' => 'nullable|date_format:H:i', + ]); + + $riwayat->update($validated); + + return redirect()->route('admin.riwayat-kegiatan.index') + ->with('success', 'Riwayat absensi berhasil diperbarui.'); + } + + /** + * Hapus riwayat absensi + */ + public function destroy(AbsensiKegiatan $riwayat) + { + $nama = $riwayat->santri->nama_lengkap; + $riwayat->delete(); + + return redirect()->route('admin.riwayat-kegiatan.index') + ->with('success', "Riwayat absensi $nama berhasil dihapus."); + } + + /** + * Export/Cetak laporan (opsional - bisa dikembangkan) + */ + public function exportPdf(Request $request) + { + // Implementasi export PDF jika diperlukan + return response()->json(['message' => 'Fitur export sedang dikembangkan']); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/RiwayatPelanggaranController.php b/sim-pkpps/app/Http/Controllers/Admin/RiwayatPelanggaranController.php new file mode 100644 index 0000000..4cd2f3d --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/RiwayatPelanggaranController.php @@ -0,0 +1,223 @@ +has('search') && $request->search != '') { + $query->search($request->search); + } + + // Filter berdasarkan santri + if ($request->has('id_santri') && $request->id_santri != '') { + $query->bySantri($request->id_santri); + } + + // Filter berdasarkan kategori + if ($request->has('id_kategori') && $request->id_kategori != '') { + $query->byKategori($request->id_kategori); + } + + // Filter berdasarkan tanggal + if ($request->has('tanggal_mulai') && $request->tanggal_mulai != '') { + $tanggalSelesai = $request->tanggal_selesai ?? $request->tanggal_mulai; + $query->byTanggal($request->tanggal_mulai, $tanggalSelesai); + } + + // Filter bulan ini + if ($request->has('bulan_ini') && $request->bulan_ini == '1') { + $query->bulanIni(); + } + + $data = $query->terbaru()->paginate(15); + + // Data untuk filter dropdown + $santriList = Santri::aktif()->orderBy('nama_lengkap')->get(); + $kategoriList = KategoriPelanggaran::orderBy('nama_pelanggaran')->get(); + + // Statistik + $totalPelanggaran = RiwayatPelanggaran::count(); + $pelanggaranBulanIni = RiwayatPelanggaran::bulanIni()->count(); + $totalPoin = RiwayatPelanggaran::sum('poin'); + + return view('admin.riwayat_pelanggaran.index', compact( + 'data', + 'santriList', + 'kategoriList', + 'totalPelanggaran', + 'pelanggaranBulanIni', + 'totalPoin' + )); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // Generate preview ID riwayat berikutnya + $lastRiwayat = RiwayatPelanggaran::orderBy('id', 'desc')->first(); + $nextNum = $lastRiwayat ? intval(substr($lastRiwayat->id_riwayat, 1)) + 1 : 1; + $nextIdRiwayat = 'P' . str_pad($nextNum, 3, '0', STR_PAD_LEFT); + + // Data untuk dropdown + $santriList = Santri::aktif()->orderBy('nama_lengkap')->get(); + $kategoriList = KategoriPelanggaran::orderBy('nama_pelanggaran')->get(); + + return view('admin.riwayat_pelanggaran.create', compact( + 'nextIdRiwayat', + 'santriList', + 'kategoriList' + )); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'id_kategori' => 'required|exists:kategori_pelanggarans,id_kategori', + 'tanggal' => 'required|date', + 'keterangan' => 'nullable|string|max:1000', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'id_santri.exists' => 'Santri tidak ditemukan.', + 'id_kategori.required' => 'Kategori pelanggaran wajib dipilih.', + 'id_kategori.exists' => 'Kategori tidak ditemukan.', + 'tanggal.required' => 'Tanggal wajib diisi.', + 'tanggal.date' => 'Format tanggal tidak valid.', + ]); + + // Ambil poin dari kategori + $kategori = KategoriPelanggaran::where('id_kategori', $validated['id_kategori'])->first(); + $validated['poin'] = $kategori->poin; + + RiwayatPelanggaran::create($validated); + + return redirect()->route('admin.riwayat-pelanggaran.index') + ->with('success', 'Riwayat pelanggaran berhasil ditambahkan.'); + } + + /** + * Display the specified resource. + */ + public function show(RiwayatPelanggaran $riwayatPelanggaran) + { + $riwayatPelanggaran->load(['santri', 'kategori']); + + // Riwayat pelanggaran santri lainnya + $riwayatLainnya = RiwayatPelanggaran::where('id_santri', $riwayatPelanggaran->id_santri) + ->where('id', '!=', $riwayatPelanggaran->id) + ->with('kategori') + ->terbaru() + ->limit(5) + ->get(); + + return view('admin.riwayat_pelanggaran.show', compact( + 'riwayatPelanggaran', + 'riwayatLainnya' + )); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(RiwayatPelanggaran $riwayatPelanggaran) + { + $riwayatPelanggaran->load(['santri', 'kategori']); + + // Data untuk dropdown + $santriList = Santri::aktif()->orderBy('nama_lengkap')->get(); + $kategoriList = KategoriPelanggaran::orderBy('nama_pelanggaran')->get(); + + return view('admin.riwayat_pelanggaran.edit', compact( + 'riwayatPelanggaran', + 'santriList', + 'kategoriList' + )); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, RiwayatPelanggaran $riwayatPelanggaran) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'id_kategori' => 'required|exists:kategori_pelanggarans,id_kategori', + 'tanggal' => 'required|date', + 'keterangan' => 'nullable|string|max:1000', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'id_santri.exists' => 'Santri tidak ditemukan.', + 'id_kategori.required' => 'Kategori pelanggaran wajib dipilih.', + 'id_kategori.exists' => 'Kategori tidak ditemukan.', + 'tanggal.required' => 'Tanggal wajib diisi.', + 'tanggal.date' => 'Format tanggal tidak valid.', + ]); + + // Ambil poin dari kategori + $kategori = KategoriPelanggaran::where('id_kategori', $validated['id_kategori'])->first(); + $validated['poin'] = $kategori->poin; + + $riwayatPelanggaran->update($validated); + + return redirect()->route('admin.riwayat-pelanggaran.index') + ->with('success', 'Riwayat pelanggaran berhasil diperbarui.'); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(RiwayatPelanggaran $riwayatPelanggaran) + { + $idRiwayat = $riwayatPelanggaran->id_riwayat; + $namaSantri = $riwayatPelanggaran->santri->nama_lengkap ?? 'Unknown'; + + $riwayatPelanggaran->delete(); + + return redirect()->route('admin.riwayat-pelanggaran.index') + ->with('success', 'Riwayat pelanggaran ' . $idRiwayat . ' untuk santri ' . $namaSantri . ' berhasil dihapus.'); + } + + /** + * Tampilkan riwayat pelanggaran per santri + */ + public function riwayatSantri($idSantri) + { + $santri = Santri::where('id_santri', $idSantri)->firstOrFail(); + + $riwayat = RiwayatPelanggaran::with('kategori') + ->bySantri($idSantri) + ->terbaru() + ->paginate(10); + + $totalPoin = RiwayatPelanggaran::bySantri($idSantri)->sum('poin'); + $totalPelanggaran = RiwayatPelanggaran::bySantri($idSantri)->count(); + + return view('admin.riwayat_pelanggaran.riwayat_santri', compact( + 'santri', + 'riwayat', + 'totalPoin', + 'totalPelanggaran' + )); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/SantriController.php b/sim-pkpps/app/Http/Controllers/Admin/SantriController.php new file mode 100644 index 0000000..a1271b0 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/SantriController.php @@ -0,0 +1,174 @@ +filled('search')) { + $search = $request->search; + $query->where(function($q) use ($search) { + $q->where('nama_lengkap', 'like', "%{$search}%") + ->orWhere('nis', 'like', "%{$search}%") + ->orWhere('id_santri', 'like', "%{$search}%"); + }); + } + + // Filter berdasarkan status + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + // Filter berdasarkan kelas + if ($request->filled('kelas')) { + $query->where('kelas', $request->kelas); + } + + // Select kolom yang diperlukan saja + $santris = $query->select( + 'id', + 'id_santri', + 'nis', + 'nama_lengkap', + 'jenis_kelamin', + 'kelas', + 'status', + 'created_at' + ) + ->orderBy('created_at', 'desc') + ->paginate(20) + ->appends(request()->query()); + + return view('admin.santri.index', compact('santris')); + } + + /** + * Tampilkan form untuk membuat santri baru. + */ + public function create() + { + // Cache last santri ID selama 1 menit + $nextIdSantri = Cache::remember('next_santri_id', 60, function () { + $lastSantri = Santri::select('id_santri') + ->orderBy('id', 'desc') + ->first(); + $nextNum = $lastSantri ? intval(substr($lastSantri->id_santri, 1)) + 1 : 1; + return 'S' . str_pad($nextNum, 3, '0', STR_PAD_LEFT); + }); + + return view('admin.santri.create', compact('nextIdSantri')); + } + + /** + * Simpan santri baru ke database. + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'nis' => 'nullable|string|max:255|unique:santris,nis', + 'nama_lengkap' => 'required|string|max:255', + 'jenis_kelamin' => 'required|in:Laki-laki,Perempuan', + 'kelas' => 'required|in:PB,Lambatan,Cepatan', + 'status' => 'required|in:Aktif,Lulus,Tidak Aktif', + 'alamat_santri' => 'nullable|string', + 'daerah_asal' => 'nullable|string|max:255', + 'nama_orang_tua' => 'nullable|string|max:255', + 'nomor_hp_ortu' => 'nullable|string|max:20', + ], [ + 'nis.unique' => 'NIS sudah digunakan oleh santri lain.', + 'nama_lengkap.required' => 'Nama lengkap wajib diisi.', + 'jenis_kelamin.required' => 'Jenis kelamin wajib dipilih.', + 'kelas.required' => 'Kelas wajib dipilih.', + 'status.required' => 'Status wajib dipilih.', + ]); + + Santri::create($validated); + + // Clear cache + Cache::forget('next_santri_id'); + Cache::forget('santris_tanpa_akun'); + Cache::forget('santri_aktif_list'); + + return redirect()->route('admin.santri.index') + ->with('success', 'Data santri berhasil ditambahkan.'); + } + + /** + * Tampilkan detail santri. + */ + public function show(Santri $santri) + { + return view('admin.santri.show', compact('santri')); + } + + /** + * Tampilkan form untuk mengedit santri. + */ + public function edit(Santri $santri) + { + return view('admin.santri.edit', compact('santri')); + } + + /** + * Update data santri di database. + */ + public function update(Request $request, Santri $santri) + { + $validated = $request->validate([ + 'nis' => 'nullable|string|max:255|unique:santris,nis,' . $santri->id, + 'nama_lengkap' => 'required|string|max:255', + 'jenis_kelamin' => 'required|in:Laki-laki,Perempuan', + 'kelas' => 'required|in:PB,Lambatan,Cepatan', + 'status' => 'required|in:Aktif,Lulus,Tidak Aktif', + 'alamat_santri' => 'nullable|string', + 'daerah_asal' => 'nullable|string|max:255', + 'nama_orang_tua' => 'nullable|string|max:255', + 'nomor_hp_ortu' => 'nullable|string|max:20', + ], [ + 'nis.unique' => 'NIS sudah digunakan oleh santri lain.', + 'nama_lengkap.required' => 'Nama lengkap wajib diisi.', + 'jenis_kelamin.required' => 'Jenis kelamin wajib dipilih.', + 'kelas.required' => 'Kelas wajib dipilih.', + 'status.required' => 'Status wajib dipilih.', + ]); + + $santri->update($validated); + + // Clear cache + Cache::forget('santris_tanpa_akun'); + Cache::forget('santri_aktif_list'); + + return redirect()->route('admin.santri.index') + ->with('success', 'Data santri berhasil diperbarui.'); + } + + /** + * Hapus data santri dari database. + */ + public function destroy(Santri $santri) + { + $namaSantri = $santri->nama_lengkap; + + $santri->delete(); + + // Clear cache + Cache::forget('santris_tanpa_akun'); + Cache::forget('santri_aktif_list'); + + return redirect()->route('admin.santri.index') + ->with('success', 'Data santri "' . $namaSantri . '" berhasil dihapus.'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/SemesterController.php b/sim-pkpps/app/Http/Controllers/Admin/SemesterController.php new file mode 100644 index 0000000..75c4ecc --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/SemesterController.php @@ -0,0 +1,156 @@ +filled('tahun_ajaran')) { + $query->tahunAjaran($request->tahun_ajaran); + } + + $semesters = $query->orderBy('tahun_ajaran', 'desc') + ->orderBy('periode', 'desc') + ->paginate(10) + ->appends(request()->query()); + + return view('admin.semester.index', compact('semesters')); + } + + /** + * Show the form for creating a new semester + */ + public function create() + { + $nextIdSemester = Cache::remember('next_semester_id', 60, function () { + $last = Semester::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_semester, 3)) + 1 : 1; + return 'SEM' . str_pad($num, 3, '0', STR_PAD_LEFT); + }); + + return view('admin.semester.create', compact('nextIdSemester')); + } + + /** + * Store a newly created semester + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'tahun_ajaran' => 'required|string|max:20', + 'periode' => 'required|in:1,2', + 'tanggal_mulai' => 'required|date', + 'tanggal_akhir' => 'required|date|after:tanggal_mulai', + 'is_active' => 'boolean', + ], [ + 'tahun_ajaran.required' => 'Tahun ajaran wajib diisi.', + 'periode.required' => 'Periode wajib dipilih.', + 'tanggal_mulai.required' => 'Tanggal mulai wajib diisi.', + 'tanggal_akhir.required' => 'Tanggal akhir wajib diisi.', + 'tanggal_akhir.after' => 'Tanggal akhir harus setelah tanggal mulai.', + ]); + + $validated['is_active'] = $request->has('is_active') ? 1 : 0; + + Semester::create($validated); + + Cache::forget('next_semester_id'); + + return redirect()->route('admin.semester.index') + ->with('success', 'Semester berhasil ditambahkan.'); + } + + /** + * Show the specified semester + */ + public function show(Semester $semester) + { + // Load statistik capaian + $semester->load(['capaian.santri', 'capaian.materi']); + + $totalCapaian = $semester->capaian()->count(); + $santriUnik = $semester->capaian()->distinct('id_santri')->count('id_santri'); + $rataRataPersentase = $semester->capaian()->avg('persentase') ?? 0; + + return view('admin.semester.show', compact('semester', 'totalCapaian', 'santriUnik', 'rataRataPersentase')); + } + + /** + * Show the form for editing the specified semester + */ + public function edit(Semester $semester) + { + return view('admin.semester.edit', compact('semester')); + } + + /** + * Update the specified semester + */ + public function update(Request $request, Semester $semester) + { + $validated = $request->validate([ + 'tahun_ajaran' => 'required|string|max:20', + 'periode' => 'required|in:1,2', + 'tanggal_mulai' => 'required|date', + 'tanggal_akhir' => 'required|date|after:tanggal_mulai', + 'is_active' => 'boolean', + ], [ + 'tahun_ajaran.required' => 'Tahun ajaran wajib diisi.', + 'periode.required' => 'Periode wajib dipilih.', + 'tanggal_mulai.required' => 'Tanggal mulai wajib diisi.', + 'tanggal_akhir.required' => 'Tanggal akhir wajib diisi.', + 'tanggal_akhir.after' => 'Tanggal akhir harus setelah tanggal mulai.', + ]); + + $validated['is_active'] = $request->has('is_active') ? 1 : 0; + + $semester->update($validated); + + return redirect()->route('admin.semester.index') + ->with('success', 'Semester berhasil diperbarui.'); + } + + /** + * Remove the specified semester + */ + public function destroy(Semester $semester) + { + // Check jika ada capaian terkait + if ($semester->capaian()->exists()) { + return redirect()->route('admin.semester.index') + ->with('error', 'Tidak dapat menghapus semester yang sudah memiliki data capaian.'); + } + + $namaSemester = $semester->nama_semester; + $semester->delete(); + + return redirect()->route('admin.semester.index') + ->with('success', 'Semester "' . $namaSemester . '" berhasil dihapus.'); + } + + /** + * Toggle status aktif semester + */ + public function toggleAktif(Semester $semester) + { + $semester->is_active = !$semester->is_active; + $semester->save(); + + $status = $semester->is_active ? 'aktif' : 'tidak aktif'; + + return redirect()->route('admin.semester.index') + ->with('success', 'Semester berhasil diubah menjadi ' . $status . '.'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/UangSakuController.php b/sim-pkpps/app/Http/Controllers/Admin/UangSakuController.php new file mode 100644 index 0000000..c402ecf --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/UangSakuController.php @@ -0,0 +1,317 @@ +filled('search')) { + $query->search($request->search); + } + + // Filter berdasarkan santri + if ($request->filled('id_santri')) { + $query->bySantri($request->id_santri); + } + + // Filter berdasarkan jenis transaksi + if ($request->filled('jenis_transaksi')) { + $query->byJenis($request->jenis_transaksi); + } + + // Filter berdasarkan tanggal + if ($request->filled('tanggal_dari') && $request->filled('tanggal_sampai')) { + $query->byDateRange($request->tanggal_dari, $request->tanggal_sampai); + } + + $transaksi = $query->orderBy('tanggal_transaksi', 'desc') + ->orderBy('created_at', 'desc') + ->paginate(20) + ->appends(request()->query()); + + // Cache santri list untuk dropdown + $santriList = Cache::remember('santri_aktif_uang_saku', 300, function () { + return Santri::where('status', 'Aktif') + ->select('id_santri', 'nama_lengkap') + ->orderBy('nama_lengkap') + ->get(); + }); + + return view('admin.uang-saku.index', compact('transaksi', 'santriList')); + } + + /** + * Form tambah transaksi + */ + public function create() + { + $santriList = Santri::where('status', 'Aktif') + ->select('id_santri', 'nama_lengkap') + ->orderBy('nama_lengkap') + ->get(); + + return view('admin.uang-saku.create', compact('santriList')); + } + + /** + * Simpan transaksi baru + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'id_santri' => 'required|exists:santris,id_santri', + 'jenis_transaksi' => 'required|in:pemasukan,pengeluaran', + 'nominal' => 'required|numeric|min:1|max:99999999', + 'keterangan' => 'nullable|string|max:500', + 'tanggal_transaksi' => 'required|date', + ], [ + 'id_santri.required' => 'Santri wajib dipilih.', + 'id_santri.exists' => 'Santri tidak ditemukan.', + 'jenis_transaksi.required' => 'Jenis transaksi wajib dipilih.', + 'nominal.required' => 'Nominal wajib diisi.', + 'nominal.numeric' => 'Nominal harus berupa angka.', + 'nominal.min' => 'Nominal minimal Rp 1.', + 'tanggal_transaksi.required' => 'Tanggal transaksi wajib diisi.', + ]); + + DB::beginTransaction(); + try { + UangSaku::create($validated); + + // Update saldo transaksi berikutnya jika ada + $this->recalculateSaldoAfter($validated['id_santri'], $validated['tanggal_transaksi']); + + DB::commit(); + Cache::forget('santri_aktif_uang_saku'); + + return redirect()->route('admin.uang-saku.index') + ->with('success', 'Transaksi uang saku berhasil ditambahkan.'); + } catch (\Exception $e) { + DB::rollBack(); + return back()->withInput() + ->with('error', 'Gagal menambahkan transaksi: ' . $e->getMessage()); + } + } + + /** + * Tampilkan detail transaksi + */ + public function show($id) + { + $transaksi = UangSaku::with('santri')->findOrFail($id); + return view('admin.uang-saku.show', compact('transaksi')); + } + + /** + * Form edit transaksi + */ + public function edit($id) + { + $transaksi = UangSaku::with('santri')->findOrFail($id); + + $santriList = Santri::where('status', 'Aktif') + ->select('id_santri', 'nama_lengkap') + ->orderBy('nama_lengkap') + ->get(); + + return view('admin.uang-saku.edit', compact('transaksi', 'santriList')); + } + + /** + * Update transaksi + */ + public function update(Request $request, $id) + { + $transaksi = UangSaku::findOrFail($id); + + $validated = $request->validate([ + 'jenis_transaksi' => 'required|in:pemasukan,pengeluaran', + 'nominal' => 'required|numeric|min:1|max:99999999', + 'keterangan' => 'nullable|string|max:500', + 'tanggal_transaksi' => 'required|date', + ], [ + 'jenis_transaksi.required' => 'Jenis transaksi wajib dipilih.', + 'nominal.required' => 'Nominal wajib diisi.', + 'nominal.numeric' => 'Nominal harus berupa angka.', + 'nominal.min' => 'Nominal minimal Rp 1.', + 'tanggal_transaksi.required' => 'Tanggal transaksi wajib diisi.', + ]); + + DB::beginTransaction(); + try { + $transaksi->update($validated); + + // Recalculate semua saldo setelah transaksi ini + $this->recalculateSaldoAfter($transaksi->id_santri, $transaksi->tanggal_transaksi); + + DB::commit(); + Cache::forget('santri_aktif_uang_saku'); + + return redirect()->route('admin.uang-saku.index') + ->with('success', 'Transaksi berhasil diperbarui.'); + } catch (\Exception $e) { + DB::rollBack(); + return back()->withInput() + ->with('error', 'Gagal memperbarui transaksi: ' . $e->getMessage()); + } + } + + /** + * Hapus transaksi + */ + public function destroy($id) + { + $transaksi = UangSaku::findOrFail($id); + $idSantri = $transaksi->id_santri; + $tanggal = $transaksi->tanggal_transaksi; + + DB::beginTransaction(); + try { + $transaksi->delete(); + + // Recalculate saldo setelah transaksi dihapus + $this->recalculateSaldoAfter($idSantri, $tanggal); + + DB::commit(); + Cache::forget('santri_aktif_uang_saku'); + + return redirect()->route('admin.uang-saku.index') + ->with('success', 'Transaksi berhasil dihapus.'); + } catch (\Exception $e) { + DB::rollBack(); + return back()->with('error', 'Gagal menghapus transaksi: ' . $e->getMessage()); + } + } + + /** + * Tampilkan riwayat uang saku per santri dengan filter tanggal + */ + public function riwayat(Request $request, $id_santri) + { + $santri = Santri::where('id_santri', $id_santri)->firstOrFail(); + + // Default: bulan ini + $tanggalDari = $request->filled('tanggal_dari') + ? $request->tanggal_dari + : now()->startOfMonth()->format('Y-m-d'); + + $tanggalSampai = $request->filled('tanggal_sampai') + ? $request->tanggal_sampai + : now()->endOfMonth()->format('Y-m-d'); + + // Query transaksi dengan filter tanggal + $query = UangSaku::where('id_santri', $id_santri); + + if ($tanggalDari && $tanggalSampai) { + $query->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai]); + } + + $transaksi = $query->orderBy('tanggal_transaksi', 'desc') + ->orderBy('created_at', 'desc') + ->paginate(20) + ->appends($request->query()); + + // Statistik dengan filter tanggal + $totalPemasukan = UangSaku::where('id_santri', $id_santri) + ->where('jenis_transaksi', 'pemasukan') + ->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai]) + ->sum('nominal'); + + $totalPengeluaran = UangSaku::where('id_santri', $id_santri) + ->where('jenis_transaksi', 'pengeluaran') + ->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai]) + ->sum('nominal'); + + // Saldo terakhir tetap dari keseluruhan transaksi + $saldoTerakhir = $santri->saldo_uang_saku; + + // Data untuk grafik dengan filter tanggal + $dataGrafik = UangSaku::where('id_santri', $id_santri) + ->whereBetween('tanggal_transaksi', [$tanggalDari, $tanggalSampai]) + ->select( + DB::raw('DATE(tanggal_transaksi) as tanggal'), + DB::raw('SUM(CASE WHEN jenis_transaksi = "pemasukan" THEN nominal ELSE 0 END) as pemasukan'), + DB::raw('SUM(CASE WHEN jenis_transaksi = "pengeluaran" THEN nominal ELSE 0 END) as pengeluaran') + ) + ->groupBy('tanggal') + ->orderBy('tanggal') + ->get(); + + // Jika tidak ada transaksi di rentang tanggal, buat data kosong + if ($dataGrafik->isEmpty()) { + $dataGrafik = collect([ + (object)[ + 'tanggal' => $tanggalDari, + 'pemasukan' => 0, + 'pengeluaran' => 0 + ] + ]); + } + + // Info periode + $periodeDari = \Carbon\Carbon::parse($tanggalDari); + $periodeSampai = \Carbon\Carbon::parse($tanggalSampai); + + return view('admin.uang-saku.riwayat', compact( + 'santri', + 'transaksi', + 'totalPemasukan', + 'totalPengeluaran', + 'saldoTerakhir', + 'dataGrafik', + 'tanggalDari', + 'tanggalSampai', + 'periodeDari', + 'periodeSampai' + )); + } + + /** + * Helper: Recalculate saldo untuk transaksi setelah tanggal tertentu + */ + private function recalculateSaldoAfter($idSantri, $tanggal) + { + $transaksiSetelah = UangSaku::where('id_santri', $idSantri) + ->where('tanggal_transaksi', '>=', $tanggal) + ->orderBy('tanggal_transaksi') + ->orderBy('created_at') + ->get(); + + foreach ($transaksiSetelah as $index => $trans) { + if ($index === 0) { + // Transaksi pertama: ambil saldo dari transaksi sebelumnya + $saldoSebelumnya = UangSaku::where('id_santri', $idSantri) + ->where('id', '<', $trans->id) + ->orderBy('tanggal_transaksi', 'desc') + ->orderBy('created_at', 'desc') + ->first(); + + $trans->saldo_sebelum = $saldoSebelumnya ? $saldoSebelumnya->saldo_sesudah : 0; + } else { + $trans->saldo_sebelum = $transaksiSetelah[$index - 1]->saldo_sesudah; + } + + if ($trans->jenis_transaksi === 'pemasukan') { + $trans->saldo_sesudah = $trans->saldo_sebelum + $trans->nominal; + } else { + $trans->saldo_sesudah = $trans->saldo_sebelum - $trans->nominal; + } + + $trans->saveQuietly(); // Save tanpa trigger event + } + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Admin/UserController.php b/sim-pkpps/app/Http/Controllers/Admin/UserController.php new file mode 100644 index 0000000..91a0c1b --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Admin/UserController.php @@ -0,0 +1,113 @@ +get(); + // Ambil data santri yang belum memiliki akun + $santris_tanpa_akun = Santri::whereDoesntHave('user')->get(); + + return view('admin.users.santri_accounts', compact('users', 'santris_tanpa_akun')); + } + + /** + * Tampilkan daftar akun Wali Santri. + */ + public function waliAccounts() + { + // Ambil akun user dengan role 'wali' + $users = User::where('role', 'wali')->get(); + + // Asumsi: Wali tidak punya tabel biodata terpisah untuk langkah 3 ini, + // jadi kita ambil dari data Santri. + // Jika Wali memiliki tabel biodata Walis, kita bisa tambahkan logika Wali::whereDoesntHave('user') + $walis = Wali::all(); + + return view('admin.users.wali_accounts', compact('users', 'walis')); + } + + /** + * Tampilkan form untuk membuat akun baru (digunakan untuk santri dan wali). + */ + public function createAccount(string $role) + { + if (!in_array($role, ['santri', 'wali'])) { + abort(404); + } + + $list_data = []; + if ($role === 'santri') { + // Ambil santri yang BELUM punya akun + $list_data = Santri::whereDoesntHave('user')->get(); + } elseif ($role === 'wali') { + // Ambil semua data wali (kita asumsikan Wali adalah individu terpisah yang didata admin) + $list_data = Wali::all(); + } + + return view('admin.users.create_account', compact('role', 'list_data')); + } + + /** + * Simpan akun baru. + */ + public function storeAccount(Request $request, string $role) + { + if (!in_array($role, ['santri', 'wali'])) { + abort(404); + } + + // Validasi + $validated = $request->validate([ + 'role_id' => [ + 'required', + Rule::unique('users', 'role_id')->where(function ($query) use ($role) { + return $query->where('role', $role); + }) + ], + 'username' => 'required|string|max:255|unique:users,username', + 'password' => 'required|string|min:8|confirmed', + ], [ + 'role_id.unique' => 'Akun untuk data ini sudah ada.', + 'role_id.required' => 'Wajib memilih data Santri/Wali yang akan dibuatkan akun.', + 'username.unique' => 'Username ini sudah digunakan.', + ]); + + // Dapatkan nama berdasarkan role_id + if ($role === 'santri') { + $data_induk = Santri::where('id_santri', $request->role_id)->firstOrFail(); + $name = $data_induk->nama_lengkap; + } elseif ($role === 'wali') { + $data_induk = Wali::where('id_wali', $request->role_id)->firstOrFail(); + $name = $data_induk->nama_wali; + } + + // Simpan User + User::create([ + 'name' => $name, + 'username' => $validated['username'], + 'password' => Hash::make($validated['password']), + 'role' => $role, + 'role_id' => $validated['role_id'], + ]); + + return redirect()->route('admin.users.'.$role.'_accounts')->with('success', 'Akun '.$role.' berhasil dibuat.'); + } + + // Tambahkan method edit/update/destroy untuk akun di langkah berikutnya +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Auth/AdminAuthController.php b/sim-pkpps/app/Http/Controllers/Auth/AdminAuthController.php new file mode 100644 index 0000000..9783b63 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/AdminAuthController.php @@ -0,0 +1,126 @@ +validate([ + 'username' => ['required', 'string'], + 'password' => ['required', 'string'], + ]); + + // Clear session lama sebelum login + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + // Start session baru + $request->session()->start(); + + // Coba login dengan username DAN role harus 'admin' + if (Auth::attempt([ + 'username' => $credentials['username'], + 'password' => $credentials['password'], + 'role' => 'admin' + ], $request->boolean('remember'))) { + + // Regenerate session untuk keamanan + $request->session()->regenerate(); + + return redirect()->intended(route('admin.dashboard')); + } + + // Track failed attempts + $attempts = $request->session()->get('login_attempts', 0) + 1; + $request->session()->put('login_attempts', $attempts); + + // Auto-flush setelah 3x gagal + if ($attempts >= 3) { + $request->session()->flush(); + $request->session()->regenerate(); + + return redirect()->back()->withErrors([ + 'username' => 'Terlalu banyak percobaan login gagal. Session telah direset. Silakan coba lagi.' + ])->withInput($request->except('password')); + } + + throw ValidationException::withMessages([ + 'username' => "Login gagal (Percobaan ke-{$attempts}/3). Username/Password salah atau bukan akun Admin.", + ]); + } + + /** + * Logout admin + */ + public function logout(Request $request) + { + Auth::logout(); + $request->session()->flush(); + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return redirect()->route('admin.login') + ->with('success', 'Anda berhasil logout.'); + } + + /** + * Tampilkan halaman register admin + */ + public function register() + { + return view('admin.auth.register'); + } + + /** + * Proses register admin baru + */ + public function storeRegister(Request $request) + { + // Validasi hanya Email, Password, dan Konfirmasi Password + $request->validate([ + 'email' => 'required|string|email|max:255|unique:users,email', + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ], [ + 'email.unique' => 'Email ini sudah terdaftar sebagai Admin.', + 'password.confirmed' => 'Konfirmasi password tidak cocok.' + ]); + + // Gunakan email sebagai username dan berikan nama default + $user = User::create([ + 'name' => 'Administrator', + 'email' => $request->email, + 'username' => $request->email, // WAJIB: Gunakan email sebagai username untuk login + 'role' => 'admin', + 'password' => Hash::make($request->password), + ]); + + event(new Registered($user)); + Auth::login($user); + + return redirect()->route('admin.dashboard') + ->with('success', 'Akun admin berhasil dibuat!'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/sim-pkpps/app/Http/Controllers/Auth/AuthenticatedSessionController.php new file mode 100644 index 0000000..494a106 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -0,0 +1,48 @@ +authenticate(); + + $request->session()->regenerate(); + + return redirect()->intended(RouteServiceProvider::HOME); + } + + /** + * Destroy an authenticated session. + */ + public function destroy(Request $request): RedirectResponse + { + Auth::guard('web')->logout(); + + $request->session()->invalidate(); + + $request->session()->regenerateToken(); + + return redirect('/'); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Auth/ConfirmablePasswordController.php b/sim-pkpps/app/Http/Controllers/Auth/ConfirmablePasswordController.php new file mode 100644 index 0000000..523ddda --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/ConfirmablePasswordController.php @@ -0,0 +1,41 @@ +validate([ + 'email' => $request->user()->email, + 'password' => $request->password, + ])) { + throw ValidationException::withMessages([ + 'password' => __('auth.password'), + ]); + } + + $request->session()->put('auth.password_confirmed_at', time()); + + return redirect()->intended(RouteServiceProvider::HOME); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Auth/EmailVerificationNotificationController.php b/sim-pkpps/app/Http/Controllers/Auth/EmailVerificationNotificationController.php new file mode 100644 index 0000000..96ba772 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/EmailVerificationNotificationController.php @@ -0,0 +1,25 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(RouteServiceProvider::HOME); + } + + $request->user()->sendEmailVerificationNotification(); + + return back()->with('status', 'verification-link-sent'); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Auth/EmailVerificationPromptController.php b/sim-pkpps/app/Http/Controllers/Auth/EmailVerificationPromptController.php new file mode 100644 index 0000000..186eb97 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/EmailVerificationPromptController.php @@ -0,0 +1,22 @@ +user()->hasVerifiedEmail() + ? redirect()->intended(RouteServiceProvider::HOME) + : view('auth.verify-email'); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Auth/NewPasswordController.php b/sim-pkpps/app/Http/Controllers/Auth/NewPasswordController.php new file mode 100644 index 0000000..f1e2814 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/NewPasswordController.php @@ -0,0 +1,61 @@ + $request]); + } + + /** + * Handle an incoming new password request. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function store(Request $request): RedirectResponse + { + $request->validate([ + 'token' => ['required'], + 'email' => ['required', 'email'], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + // Here we will attempt to reset the user's password. If it is successful we + // will update the password on an actual user model and persist it to the + // database. Otherwise we will parse the error and return the response. + $status = Password::reset( + $request->only('email', 'password', 'password_confirmation', 'token'), + function ($user) use ($request) { + $user->forceFill([ + 'password' => Hash::make($request->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + return $status == Password::PASSWORD_RESET + ? redirect()->route('login')->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Auth/PasswordController.php b/sim-pkpps/app/Http/Controllers/Auth/PasswordController.php new file mode 100644 index 0000000..6916409 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/PasswordController.php @@ -0,0 +1,29 @@ +validateWithBag('updatePassword', [ + 'current_password' => ['required', 'current_password'], + 'password' => ['required', Password::defaults(), 'confirmed'], + ]); + + $request->user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + return back()->with('status', 'password-updated'); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Auth/PasswordResetLinkController.php b/sim-pkpps/app/Http/Controllers/Auth/PasswordResetLinkController.php new file mode 100644 index 0000000..ce813a6 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/PasswordResetLinkController.php @@ -0,0 +1,44 @@ +validate([ + 'email' => ['required', 'email'], + ]); + + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + $status = Password::sendResetLink( + $request->only('email') + ); + + return $status == Password::RESET_LINK_SENT + ? back()->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Auth/RegisteredUserController.php b/sim-pkpps/app/Http/Controllers/Auth/RegisteredUserController.php new file mode 100644 index 0000000..a15828f --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/RegisteredUserController.php @@ -0,0 +1,51 @@ +validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => Hash::make($request->password), + ]); + + event(new Registered($user)); + + Auth::login($user); + + return redirect(RouteServiceProvider::HOME); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Auth/SantriAuthController.php b/sim-pkpps/app/Http/Controllers/Auth/SantriAuthController.php new file mode 100644 index 0000000..4060aa5 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/SantriAuthController.php @@ -0,0 +1,93 @@ +validate([ + 'username' => 'required|string', + 'password' => 'required|string', + ], [ + 'username.required' => 'Username wajib diisi.', + 'password.required' => 'Password wajib diisi.', + ]); + + // ✅ TAMBAHAN 1: Clear old session data + $request->session()->forget(['login_attempts', 'last_attempt_time']); + + // Coba login dengan guard default + if (Auth::attempt($credentials, $request->boolean('remember'))) { + $user = Auth::user(); + + // Cek apakah user adalah santri atau wali + if ($user->role === 'santri' || $user->role === 'wali') { + // ✅ TAMBAHAN 2: Regenerate & clear + $request->session()->regenerate(); + $request->session()->forget(['login_attempts', 'last_attempt_time']); + + return redirect()->intended(route('santri.dashboard')) + ->with('success', 'Selamat datang, ' . $user->name . '!'); + } + + // ✅ TAMBAHAN 3: Role tidak sesuai - clear session + Auth::logout(); + $request->session()->invalidate(); + $request->session()->regenerate(); + + return redirect()->back()->withErrors([ + 'username' => 'Akun Anda tidak memiliki akses ke halaman ini. Gunakan login Admin jika Anda admin.' + ])->withInput($request->except('password')); + } + + // ✅ TAMBAHAN 4: Track & auto-flush + $attempts = $request->session()->get('login_attempts', 0) + 1; + $request->session()->put('login_attempts', $attempts); + $request->session()->put('last_attempt_time', now()); + + if ($attempts >= 3) { + $request->session()->flush(); + $request->session()->regenerate(); + + return redirect()->back()->withErrors([ + 'username' => 'Terlalu banyak percobaan login gagal. Session telah direset. Silakan coba lagi.' + ])->withInput($request->except('password')); + } + + throw ValidationException::withMessages([ + 'username' => "Login gagal (Percobaan ke-{$attempts}/3). Username/Password salah atau akun tidak terdaftar.", + ]); + } + + /** + * Logout santri/wali + */ + public function logout(Request $request) + { + Auth::logout(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return redirect()->route('santri.login') + ->with('success', 'Anda berhasil logout.'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Auth/VerifyEmailController.php b/sim-pkpps/app/Http/Controllers/Auth/VerifyEmailController.php new file mode 100644 index 0000000..ea87940 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Auth/VerifyEmailController.php @@ -0,0 +1,28 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(RouteServiceProvider::HOME.'?verified=1'); + } + + if ($request->user()->markEmailAsVerified()) { + event(new Verified($request->user())); + } + + return redirect()->intended(RouteServiceProvider::HOME.'?verified=1'); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Controller.php b/sim-pkpps/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..77ec359 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Controller.php @@ -0,0 +1,12 @@ + Santri::count(), + 'total_wali' => User::where('role', 'wali')->count(), + 'kegiatan_hari_ini' => 0, + ]; + + return view('admin.dashboardAdmin', compact('data')); + + } catch (\Exception $e) { + Log::error('Error di Dashboard Admin: ' . $e->getMessage()); + + return response()->view('errors.500', [ + 'error' => 'Terjadi kesalahan saat memuat dashboard', + 'message' => $e->getMessage() + ], 500); + } + } + + /** + * Dashboard Santri/Wali - OPTIMIZED ✅ + */ + public function santri() + { + try { + $user = Auth::user(); + + // Validasi role + if (!in_array($user->role, ['santri', 'wali'])) { + Log::error('Role tidak sesuai: ' . $user->role); + abort(403, 'Akses ditolak. Role Anda: ' . $user->role); + } + + // ✅ QUERY OPTIMIZED - Ambil data santri dengan kolom minimal + $santri = Santri::where('id_santri', $user->role_id) + ->select('id_santri', 'nama_lengkap', 'kelas') + ->firstOrFail(); + + $idSantri = $santri->id_santri; + $today = Carbon::today(); + $weekAgo = Carbon::now()->subDays(7); + + // ✅ PARALLEL QUERIES - Eksekusi query secara bersamaan untuk performa + $data = [ + 'nama_santri' => $santri->nama_lengkap, + 'kelas' => $santri->kelas, + 'progres_quran' => 0, // Nanti diisi dari database capaian + 'progres_hadist' => 0, // Nanti diisi dari database capaian + + // Query langsung untuk saldo uang saku (dari accessor model) + 'saldo_uang_saku' => $santri->saldo_uang_saku, + + // Query optimized untuk poin pelanggaran + 'poin_pelanggaran' => RiwayatPelanggaran::where('id_santri', $idSantri) + ->sum('poin'), + ]; + + // ✅ Query status kesehatan (hanya jika sedang dirawat) + $statusKesehatan = KesehatanSantri::where('id_santri', $idSantri) + ->where('status', 'dirawat') + ->select('id', 'keluhan', 'tanggal_masuk') + ->orderBy('tanggal_masuk', 'desc') + ->first(); + + // ✅ Query kepulangan aktif (hanya jika sedang dalam periode pulang) + $kepulanganAktif = Kepulangan::where('id_santri', $idSantri) + ->where('status', 'Disetujui') + ->whereDate('tanggal_pulang', '<=', $today) + ->whereDate('tanggal_kembali', '>=', $today) + ->select('id_kepulangan', 'tanggal_pulang', 'tanggal_kembali', 'alasan') + ->first(); + + // ✅ Query berita terbaru (7 hari terakhir) - OPTIMIZED + $beritaTerbaru = Berita::select('id_berita', 'judul', 'created_at') + ->where('status', 'published') + ->where('created_at', '>=', $weekAgo) + ->where(function($query) use ($santri) { + $query->where('target_berita', 'semua') + ->orWhere(function($q) use ($santri) { + $q->where('target_berita', 'kelas_tertentu') + ->whereJsonContains('target_kelas', $santri->kelas); + }) + ->orWhereHas('santriTertentu', function($q) use ($santri) { + $q->where('santris.id_santri', $santri->id_santri); + }); + }) + ->orderBy('created_at', 'desc') + ->limit(5) + ->get(); + + // Return view dengan data yang sudah dioptimasi + return view('santri.dashboardSantri', compact( + 'data', + 'santri', + 'user', + 'beritaTerbaru', + 'statusKesehatan', + 'kepulanganAktif' + )); + + } catch (\Exception $e) { + Log::error('=== ERROR DI DASHBOARD SANTRI ==='); + Log::error('Message: ' . $e->getMessage()); + Log::error('File: ' . $e->getFile()); + Log::error('Line: ' . $e->getLine()); + + return response()->view('errors.500', [ + 'error' => $e->getMessage(), + ], 500); + } + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/ProfileController.php b/sim-pkpps/app/Http/Controllers/ProfileController.php new file mode 100644 index 0000000..b243c80 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/ProfileController.php @@ -0,0 +1,62 @@ + $request->user(), + ]); + } + + /** + * Update the user's profile information. + */ + public function update(ProfileUpdateRequest $request): RedirectResponse + { + $request->user()->fill($request->validated()); + + if ($request->user()->isDirty('email')) { + $request->user()->email_verified_at = null; + } + + $request->user()->save(); + + return Redirect::route('profile.edit')->with('status', 'profile-updated'); + } + + /** + * Delete the user's account. + */ + public function destroy(Request $request): RedirectResponse + { + $request->validateWithBag('userDeletion', [ + 'password' => ['required', 'current_password'], + ]); + + $user = $request->user(); + + Auth::logout(); + + $user->delete(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return Redirect::to('/'); + } +} diff --git a/sim-pkpps/app/Http/Controllers/Santri/SantriBeritaController.php b/sim-pkpps/app/Http/Controllers/Santri/SantriBeritaController.php new file mode 100644 index 0000000..572f5ee --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Santri/SantriBeritaController.php @@ -0,0 +1,112 @@ +role_id) + ->select('id_santri', 'kelas') + ->firstOrFail(); + + // Query berita yang published dan sesuai target + $berita = Berita::query() + ->select([ + 'id', + 'id_berita', + 'judul', + 'konten', + 'penulis', + 'gambar', + 'created_at' + ]) + ->where('status', 'published') + ->where(function($query) use ($santri) { + // Berita untuk semua + $query->where('target_berita', 'semua') + // Atau berita untuk kelas santri ini + ->orWhere(function($q) use ($santri) { + $q->where('target_berita', 'kelas_tertentu') + ->whereJsonContains('target_kelas', $santri->kelas); + }) + // Atau berita khusus untuk santri ini + ->orWhereHas('santriTertentu', function($q) use ($santri) { + $q->where('santris.id_santri', $santri->id_santri); + }); + }) + ->orderBy('created_at', 'desc') + ->paginate(12); + + // Ambil status baca santri untuk setiap berita (efficient query) + $beritaIds = $berita->pluck('id_berita')->toArray(); + $statusBaca = DB::table('berita_santri') + ->where('id_santri', $santri->id_santri) + ->whereIn('id_berita', $beritaIds) + ->pluck('sudah_dibaca', 'id_berita') + ->toArray(); + + // Attach status baca ke collection + $berita->getCollection()->transform(function($item) use ($statusBaca) { + $item->sudah_dibaca = $statusBaca[$item->id_berita] ?? false; + return $item; + }); + + return view('santri.berita.index', compact('berita', 'santri')); + } + + /** + * Tampilkan detail berita dan tandai sebagai sudah dibaca + */ + public function show($id_berita) + { + $user = Auth::user(); + + $santri = Santri::where('id_santri', $user->role_id) + ->select('id_santri', 'kelas') + ->firstOrFail(); + + // Ambil berita dengan validasi akses + $berita = Berita::where('id_berita', $id_berita) + ->where('status', 'published') + ->where(function($query) use ($santri) { + $query->where('target_berita', 'semua') + ->orWhere(function($q) use ($santri) { + $q->where('target_berita', 'kelas_tertentu') + ->whereJsonContains('target_kelas', $santri->kelas); + }) + ->orWhereHas('santriTertentu', function($q) use ($santri) { + $q->where('santris.id_santri', $santri->id_santri); + }); + }) + ->firstOrFail(); + + // Tandai sebagai sudah dibaca (insert or update) + DB::table('berita_santri')->updateOrInsert( + [ + 'id_berita' => $berita->id_berita, + 'id_santri' => $santri->id_santri + ], + [ + 'sudah_dibaca' => true, + 'tanggal_baca' => now(), + 'updated_at' => now() + ] + ); + + return view('santri.berita.show', compact('berita', 'santri')); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Santri/SantriCapaianController.php b/sim-pkpps/app/Http/Controllers/Santri/SantriCapaianController.php new file mode 100644 index 0000000..f71fc44 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Santri/SantriCapaianController.php @@ -0,0 +1,214 @@ +role, ['santri', 'wali'])) { + abort(403, 'Unauthorized access'); + } + + $idSantri = $user->role_id; + + // Cache data santri selama 10 menit + $santri = Cache::remember("santri_capaian_{$idSantri}", 600, function () use ($idSantri) { + return Santri::where('id_santri', $idSantri) + ->select('id_santri', 'nama_lengkap', 'kelas', 'nis') + ->firstOrFail(); + }); + + // Get semester aktif + $semesterAktif = Semester::aktif()->first(); + $selectedSemester = $request->input('id_semester', $semesterAktif?->id_semester); + + // Query capaian dengan relasi + $query = Capaian::with(['materi:id_materi,nama_kitab,kategori,total_halaman', 'semester:id_semester,nama_semester']) + ->where('id_santri', $idSantri) + ->select('id', 'id_capaian', 'id_santri', 'id_materi', 'id_semester', 'halaman_selesai', 'persentase', 'tanggal_input'); + + // Filter semester + if ($selectedSemester) { + $query->where('id_semester', $selectedSemester); + } + + $capaians = $query->orderBy('tanggal_input', 'desc')->get(); + + // Statistik Umum + $totalCapaian = $capaians->count(); + $rataRataPersentase = $capaians->avg('persentase') ?? 0; + $materiSelesai = $capaians->where('persentase', '>=', 100)->count(); + + // Statistik per Kategori + $statistikKategori = [ + 'Al-Qur\'an' => [ + 'count' => 0, + 'avg' => 0, + 'selesai' => 0, + ], + 'Hadist' => [ + 'count' => 0, + 'avg' => 0, + 'selesai' => 0, + ], + 'Materi Tambahan' => [ + 'count' => 0, + 'avg' => 0, + 'selesai' => 0, + ], + ]; + + foreach ($capaians as $capaian) { + $kategori = $capaian->materi->kategori; + $statistikKategori[$kategori]['count']++; + $statistikKategori[$kategori]['avg'] += $capaian->persentase; + if ($capaian->persentase >= 100) { + $statistikKategori[$kategori]['selesai']++; + } + } + + // Hitung rata-rata + foreach ($statistikKategori as $kategori => $data) { + if ($data['count'] > 0) { + $statistikKategori[$kategori]['avg'] = $data['avg'] / $data['count']; + } + } + + // Distribusi persentase untuk chart + $distribusiPersentase = [ + '0-25%' => $capaians->whereBetween('persentase', [0, 25])->count(), + '26-50%' => $capaians->whereBetween('persentase', [26, 50])->count(), + '51-75%' => $capaians->whereBetween('persentase', [51, 75])->count(), + '76-99%' => $capaians->whereBetween('persentase', [76, 99])->count(), + '100%' => $capaians->where('persentase', '>=', 100)->count(), + ]; + + // Data untuk semester dropdown + $semesters = Semester::select('id_semester', 'nama_semester', 'tahun_ajaran') + ->orderBy('tahun_ajaran', 'desc') + ->orderBy('periode', 'desc') + ->get(); + + return view('santri.capaian.index', compact( + 'santri', + 'capaians', + 'totalCapaian', + 'rataRataPersentase', + 'materiSelesai', + 'statistikKategori', + 'distribusiPersentase', + 'semesters', + 'selectedSemester', + 'semesterAktif' + )); + } + + /** + * Tampilkan detail capaian tertentu + */ + public function show($id) + { + $user = Auth::user(); + + if (!in_array($user->role, ['santri', 'wali'])) { + abort(403, 'Unauthorized access'); + } + + $capaian = Capaian::with([ + 'materi:id_materi,nama_kitab,kategori,halaman_mulai,halaman_akhir,total_halaman', + 'semester:id_semester,nama_semester,tahun_ajaran', + 'santri:id_santri,nama_lengkap,kelas' + ]) + ->where('id_santri', $user->role_id) + ->findOrFail($id); + + return view('santri.capaian.show', compact('capaian')); + } + + /** + * API untuk data grafik (AJAX) + */ + public function apiGrafikData(Request $request) + { + $user = Auth::user(); + $type = $request->input('type', 'kategori'); + $idSemester = $request->input('id_semester'); + + $query = Capaian::with('materi:id_materi,kategori') + ->where('id_santri', $user->role_id) + ->select('id', 'id_materi', 'persentase', 'id_semester'); + + if ($idSemester) { + $query->where('id_semester', $idSemester); + } + + $capaians = $query->get(); + $data = []; + + switch ($type) { + case 'kategori': + $avgAlquran = $capaians->filter(fn($c) => $c->materi->kategori == 'Al-Qur\'an')->avg('persentase') ?? 0; + $avgHadist = $capaians->filter(fn($c) => $c->materi->kategori == 'Hadist')->avg('persentase') ?? 0; + $avgTambahan = $capaians->filter(fn($c) => $c->materi->kategori == 'Materi Tambahan')->avg('persentase') ?? 0; + + $data = [ + 'labels' => ['Al-Qur\'an', 'Hadist', 'Materi Tambahan'], + 'datasets' => [[ + 'label' => 'Rata-rata Progress (%)', + 'data' => [ + round($avgAlquran, 2), + round($avgHadist, 2), + round($avgTambahan, 2) + ], + 'backgroundColor' => [ + 'rgba(111, 186, 157, 0.8)', + 'rgba(129, 198, 232, 0.8)', + 'rgba(255, 213, 107, 0.8)', + ], + ]] + ]; + break; + + case 'distribusi': + $data = [ + 'labels' => ['0-25%', '26-50%', '51-75%', '76-99%', '100%'], + 'datasets' => [[ + 'label' => 'Jumlah Materi', + 'data' => [ + $capaians->whereBetween('persentase', [0, 25])->count(), + $capaians->whereBetween('persentase', [26, 50])->count(), + $capaians->whereBetween('persentase', [51, 75])->count(), + $capaians->whereBetween('persentase', [76, 99])->count(), + $capaians->where('persentase', '>=', 100)->count(), + ], + 'backgroundColor' => [ + 'rgba(255, 139, 148, 0.8)', + 'rgba(255, 171, 145, 0.8)', + 'rgba(255, 213, 107, 0.8)', + 'rgba(129, 198, 232, 0.8)', + 'rgba(111, 186, 157, 0.8)', + ], + ]] + ]; + break; + } + + return response()->json($data); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Santri/SantriKepulanganController.php b/sim-pkpps/app/Http/Controllers/Santri/SantriKepulanganController.php new file mode 100644 index 0000000..598e069 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Santri/SantriKepulanganController.php @@ -0,0 +1,128 @@ +role_id) + ->select('id_santri', 'nama_lengkap', 'kelas') + ->firstOrFail(); + + // Tahun untuk filter + $tahunSekarang = $request->filled('tahun') ? $request->tahun : Carbon::now()->year; + + // Query riwayat kepulangan + $query = Kepulangan::query() + ->select([ + 'id', + 'id_kepulangan', + 'id_santri', + 'tanggal_izin', + 'tanggal_pulang', + 'tanggal_kembali', + 'durasi_izin', + 'alasan', + 'status', + 'approved_at', + 'created_at' + ]) + ->where('id_santri', $santri->id_santri) + ->whereYear('tanggal_pulang', $tahunSekarang); + + // Filter status jika ada + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + // Urutkan terbaru dan paginate + $riwayatKepulangan = $query->orderBy('tanggal_pulang', 'desc') + ->paginate(10) + ->appends($request->all()); + + // Hitung statistik tahun ini + $statistik = [ + 'total_izin' => Kepulangan::where('id_santri', $santri->id_santri) + ->whereYear('tanggal_pulang', $tahunSekarang) + ->count(), + 'disetujui' => Kepulangan::where('id_santri', $santri->id_santri) + ->where('status', 'Disetujui') + ->whereYear('tanggal_pulang', $tahunSekarang) + ->count(), + 'total_hari' => Kepulangan::where('id_santri', $santri->id_santri) + ->where('status', 'Disetujui') + ->whereYear('tanggal_pulang', $tahunSekarang) + ->sum('durasi_izin'), + 'menunggu' => Kepulangan::where('id_santri', $santri->id_santri) + ->where('status', 'Menunggu') + ->whereYear('tanggal_pulang', $tahunSekarang) + ->count(), + ]; + + // Hitung sisa kuota (maksimal 12 hari/tahun) + $statistik['sisa_kuota'] = max(0, 12 - $statistik['total_hari']); + $statistik['over_limit'] = $statistik['total_hari'] > 12; + + // Data untuk filter + $statusOptions = [ + 'Menunggu' => 'Menunggu Approval', + 'Disetujui' => 'Disetujui', + 'Ditolak' => 'Ditolak', + 'Selesai' => 'Selesai' + ]; + + // Tahun options (5 tahun terakhir) + $tahunOptions = range(Carbon::now()->year, Carbon::now()->year - 4); + + return view('santri.kepulangan.index', compact( + 'riwayatKepulangan', + 'santri', + 'statistik', + 'statusOptions', + 'tahunOptions', + 'tahunSekarang' + )); + } + + /** + * Tampilkan detail kepulangan + */ + public function show($id_kepulangan) + { + $user = Auth::user(); + + $santri = Santri::where('id_santri', $user->role_id) + ->select('id_santri', 'nama_lengkap', 'kelas') + ->firstOrFail(); + + // Ambil data kepulangan dengan validasi kepemilikan + $kepulangan = Kepulangan::where('id_kepulangan', $id_kepulangan) + ->where('id_santri', $santri->id_santri) + ->firstOrFail(); + + // Hitung total hari izin tahun ini + $tahunSekarang = Carbon::now()->year; + $totalHariTahunIni = Kepulangan::where('id_santri', $santri->id_santri) + ->where('status', 'Disetujui') + ->whereYear('tanggal_pulang', $tahunSekarang) + ->sum('durasi_izin'); + + $sisaKuota = max(0, 12 - $totalHariTahunIni); + + return view('santri.kepulangan.show', compact('kepulangan', 'santri', 'totalHariTahunIni', 'sisaKuota')); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Santri/SantriKesehatanController.php b/sim-pkpps/app/Http/Controllers/Santri/SantriKesehatanController.php new file mode 100644 index 0000000..8f4d62c --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Santri/SantriKesehatanController.php @@ -0,0 +1,121 @@ +role_id) + ->select('id_santri', 'nama_lengkap', 'kelas') + ->firstOrFail(); + + // ✅ TENTUKAN RANGE TANGGAL + // Jika tidak ada filter, default bulan ini + $tanggalDari = $request->filled('tanggal_dari') + ? Carbon::parse($request->tanggal_dari) + : Carbon::now()->startOfMonth(); + + $tanggalSampai = $request->filled('tanggal_sampai') + ? Carbon::parse($request->tanggal_sampai) + : Carbon::now()->endOfMonth(); + + // Validasi: tanggal_sampai tidak boleh lebih kecil dari tanggal_dari + if ($tanggalSampai->lt($tanggalDari)) { + return back()->withErrors([ + 'tanggal_sampai' => 'Tanggal sampai harus lebih besar atau sama dengan tanggal dari.' + ])->withInput(); + } + + // ✅ QUERY DASAR DENGAN FILTER TANGGAL + $baseQuery = KesehatanSantri::where('id_santri', $santri->id_santri) + ->whereBetween('tanggal_masuk', [ + $tanggalDari->format('Y-m-d'), + $tanggalSampai->format('Y-m-d') + ]); + + // ✅ HITUNG STATISTIK BERDASARKAN FILTER TANGGAL + $statistik = [ + 'total_kunjungan' => (clone $baseQuery)->count(), + 'sedang_dirawat' => (clone $baseQuery)->where('status', 'dirawat')->count(), + 'sembuh' => (clone $baseQuery)->where('status', 'sembuh')->count(), + 'izin' => (clone $baseQuery)->where('status', 'izin')->count(), + ]; + + // ✅ QUERY RIWAYAT KESEHATAN UNTUK TABEL + $query = KesehatanSantri::query() + ->select([ + 'id', + 'id_kesehatan', + 'id_santri', + 'tanggal_masuk', + 'tanggal_keluar', + 'keluhan', + 'status', + 'created_at' + ]) + ->where('id_santri', $santri->id_santri) + ->whereBetween('tanggal_masuk', [ + $tanggalDari->format('Y-m-d'), + $tanggalSampai->format('Y-m-d') + ]); + + // Filter status jika ada + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + // Urutkan terbaru dan paginate + $riwayatKesehatan = $query->orderBy('tanggal_masuk', 'desc') + ->paginate(10) + ->appends($request->all()); // Append query string untuk pagination + + // Data untuk filter + $statusOptions = [ + 'dirawat' => 'Sedang Dirawat', + 'sembuh' => 'Sembuh', + 'izin' => 'Izin Sakit' + ]; + + return view('santri.kesehatan.index', compact( + 'riwayatKesehatan', + 'santri', + 'statistik', + 'statusOptions', + 'tanggalDari', + 'tanggalSampai' + )); + } + + /** + * Tampilkan detail riwayat kesehatan + */ + public function show($id) + { + $user = Auth::user(); + + $santri = Santri::where('id_santri', $user->role_id) + ->select('id_santri', 'nama_lengkap', 'kelas') + ->firstOrFail(); + + // Ambil data kesehatan dengan validasi kepemilikan + $kesehatanSantri = KesehatanSantri::where('id', $id) + ->where('id_santri', $santri->id_santri) + ->firstOrFail(); + + return view('santri.kesehatan.show', compact('kesehatanSantri', 'santri')); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Santri/SantriPelanggaranController.php b/sim-pkpps/app/Http/Controllers/Santri/SantriPelanggaranController.php new file mode 100644 index 0000000..e7e672b --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Santri/SantriPelanggaranController.php @@ -0,0 +1,111 @@ +role, ['santri', 'wali'])) { + abort(403, 'Akses ditolak.'); + } + + // Query riwayat pelanggaran dengan relasi + $query = RiwayatPelanggaran::with(['kategori:id,id_kategori,nama_pelanggaran,poin']) + ->where('id_santri', $user->role_id) + ->select([ + 'id', + 'id_riwayat', + 'id_santri', + 'id_kategori', + 'tanggal', + 'poin', + 'keterangan', + 'created_at' + ]); + + // Filter berdasarkan tanggal (opsional) + if ($request->filled('tanggal_mulai')) { + $query->whereDate('tanggal', '>=', $request->tanggal_mulai); + } + + if ($request->filled('tanggal_selesai')) { + $query->whereDate('tanggal', '<=', $request->tanggal_selesai); + } + + // Filter bulan ini (jika ada parameter) + if ($request->has('bulan_ini') && $request->bulan_ini == '1') { + $query->bulanIni(); + } + + // Urutkan dari terbaru + $riwayat = $query->terbaru()->paginate(15); + + // Statistik pelanggaran santri + $totalPelanggaran = RiwayatPelanggaran::where('id_santri', $user->role_id)->count(); + $totalPoin = RiwayatPelanggaran::where('id_santri', $user->role_id)->sum('poin'); + $pelanggaranBulanIni = RiwayatPelanggaran::where('id_santri', $user->role_id) + ->bulanIni() + ->count(); + + return view('santri.pelanggaran.index', compact( + 'riwayat', + 'totalPelanggaran', + 'totalPoin', + 'pelanggaranBulanIni' + )); + } + + /** + * Tampilkan detail satu riwayat pelanggaran + */ + public function show(RiwayatPelanggaran $riwayatPelanggaran) + { + $user = Auth::user(); + + // Validasi: pastikan pelanggaran milik santri yang login + if ($riwayatPelanggaran->id_santri !== $user->role_id) { + abort(403, 'Anda tidak memiliki akses ke data ini.'); + } + + // Load relasi kategori + $riwayatPelanggaran->load('kategori:id,id_kategori,nama_pelanggaran,poin'); + + return view('santri.pelanggaran.show', compact('riwayatPelanggaran')); + } + + /** + * Tampilkan daftar semua kategori pelanggaran beserta poinnya + */ + public function kategoriList() + { + // Cache daftar kategori selama 1 jam + $kategoriList = Cache::remember('kategori_pelanggaran_list', 3600, function () { + return KategoriPelanggaran::select([ + 'id', + 'id_kategori', + 'nama_pelanggaran', + 'poin' + ]) + ->orderBy('poin', 'desc') + ->orderBy('nama_pelanggaran', 'asc') + ->get(); + }); + + return view('santri.pelanggaran.kategori', compact('kategoriList')); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Santri/SantriProfileController.php b/sim-pkpps/app/Http/Controllers/Santri/SantriProfileController.php new file mode 100644 index 0000000..54af299 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Santri/SantriProfileController.php @@ -0,0 +1,107 @@ +user(); + + // Pastikan user adalah santri + if ($user->role !== 'santri') { + abort(403, 'Unauthorized access'); + } + + // Cache data santri selama 10 menit untuk mengurangi query database + $santri = Cache::remember( + 'santri_profile_' . $user->role_id, + 600, // 10 menit + function () use ($user) { + return Santri::where('id_santri', $user->role_id) + ->select([ + 'id', + 'id_santri', + 'nis', + 'nama_lengkap', + 'jenis_kelamin', + 'kelas', + 'status', + 'alamat_santri', + 'daerah_asal', + 'nama_orang_tua', + 'nomor_hp_ortu', + 'rfid_uid', + 'created_at' + ]) + ->firstOrFail(); + } + ); + + return view('santri.profil.index', compact('santri')); + } + + /** + * Tampilkan form edit profil (data terbatas yang bisa diedit santri) + */ + public function edit() + { + $user = Auth::guard('web')->user(); + + if ($user->role !== 'santri') { + abort(403, 'Unauthorized access'); + } + + $santri = Santri::where('id_santri', $user->role_id) + ->select([ + 'id', + 'id_santri', + 'nama_lengkap', + 'alamat_santri', + 'nomor_hp_ortu' + ]) + ->firstOrFail(); + + return view('santri.profil.edit', compact('santri')); + } + + /** + * Update profil santri (hanya field tertentu yang boleh diedit) + */ + public function update(Request $request) + { + $user = Auth::guard('web')->user(); + + if ($user->role !== 'santri') { + abort(403, 'Unauthorized access'); + } + + $validated = $request->validate([ + 'alamat_santri' => 'nullable|string|max:500', + 'nomor_hp_ortu' => 'nullable|string|max:20|regex:/^[0-9+\-\s()]+$/', + ], [ + 'nomor_hp_ortu.regex' => 'Format nomor HP tidak valid. Hanya boleh berisi angka, +, -, spasi, dan tanda kurung.', + 'alamat_santri.max' => 'Alamat maksimal 500 karakter.', + ]); + + $santri = Santri::where('id_santri', $user->role_id)->firstOrFail(); + $santri->update($validated); + + // Clear cache setelah update + Cache::forget('santri_profile_' . $user->role_id); + + return redirect()->route('santri.profil.index') + ->with('success', 'Profil berhasil diperbarui.'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Controllers/Santri/SantriUangSakuController.php b/sim-pkpps/app/Http/Controllers/Santri/SantriUangSakuController.php new file mode 100644 index 0000000..2aa63d4 --- /dev/null +++ b/sim-pkpps/app/Http/Controllers/Santri/SantriUangSakuController.php @@ -0,0 +1,151 @@ +role, ['santri', 'wali'])) { + abort(403, 'Akses ditolak'); + } + + // Ambil data santri + $santri = Santri::where('id_santri', $user->role_id)->first(); + + if (!$santri) { + abort(404, 'Data santri tidak ditemukan'); + } + + // Query uang saku dengan pagination dan filter + $query = UangSaku::where('id_santri', $santri->id_santri) + ->with('santri:id_santri,nama_lengkap,kelas'); + + // Filter berdasarkan jenis transaksi + if ($request->filled('jenis_transaksi')) { + $query->where('jenis_transaksi', $request->jenis_transaksi); + } + + // Filter berdasarkan tanggal + if ($request->filled('tanggal_dari')) { + $query->whereDate('tanggal_transaksi', '>=', $request->tanggal_dari); + } + + if ($request->filled('tanggal_sampai')) { + $query->whereDate('tanggal_transaksi', '<=', $request->tanggal_sampai); + } + + // Search + if ($request->filled('search')) { + $search = $request->search; + $query->where(function($q) use ($search) { + $q->where('keterangan', 'like', "%{$search}%") + ->orWhere('id_uang_saku', 'like', "%{$search}%"); + }); + } + + // Urutkan dari yang terbaru + $query->orderBy('tanggal_transaksi', 'desc') + ->orderBy('created_at', 'desc'); + + // Pagination + $riwayatUangSaku = $query->paginate(15)->withQueryString(); + + // ✅ Hitung statistik berdasarkan filter atau bulan ini + $statistikQuery = UangSaku::where('id_santri', $santri->id_santri); + + // Jika ada filter tanggal, gunakan filter tersebut + if ($request->filled('tanggal_dari') || $request->filled('tanggal_sampai')) { + if ($request->filled('tanggal_dari')) { + $statistikQuery->whereDate('tanggal_transaksi', '>=', $request->tanggal_dari); + } + if ($request->filled('tanggal_sampai')) { + $statistikQuery->whereDate('tanggal_transaksi', '<=', $request->tanggal_sampai); + } + } else { + // Jika tidak ada filter, tampilkan data bulan ini saja + $statistikQuery->whereMonth('tanggal_transaksi', now()->month) + ->whereYear('tanggal_transaksi', now()->year); + } + + // Clone query untuk menghitung pemasukan dan pengeluaran + $totalPemasukan = (clone $statistikQuery)->where('jenis_transaksi', 'pemasukan')->sum('nominal'); + $totalPengeluaran = (clone $statistikQuery)->where('jenis_transaksi', 'pengeluaran')->sum('nominal'); + + // Saldo terakhir tetap dari data terbaru (tidak terpengaruh filter) + $saldoTerakhir = $santri->saldo_uang_saku; + + // Info periode untuk ditampilkan di view + if ($request->filled('tanggal_dari') || $request->filled('tanggal_sampai')) { + $periodeTeks = 'Periode: '; + if ($request->filled('tanggal_dari')) { + $periodeTeks .= \Carbon\Carbon::parse($request->tanggal_dari)->format('d/m/Y'); + } + $periodeTeks .= ' - '; + if ($request->filled('tanggal_sampai')) { + $periodeTeks .= \Carbon\Carbon::parse($request->tanggal_sampai)->format('d/m/Y'); + } + } else { + $periodeTeks = 'Bulan Ini: ' . now()->isoFormat('MMMM YYYY'); + } + + return view('santri.uang-saku.index', compact( + 'riwayatUangSaku', + 'santri', + 'totalPemasukan', + 'totalPengeluaran', + 'saldoTerakhir' + )); + + } catch (\Exception $e) { + Log::error('Error di Riwayat Uang Saku Santri: ' . $e->getMessage()); + + return back()->with('error', 'Terjadi kesalahan saat memuat riwayat uang saku'); + } + } + + /** + * Tampilkan detail transaksi + */ + public function show($id) + { + try { + $user = Auth::user(); + + // Ambil data santri + $santri = Santri::where('id_santri', $user->role_id)->first(); + + if (!$santri) { + abort(404, 'Data santri tidak ditemukan'); + } + + // Ambil transaksi dengan validasi kepemilikan + $transaksi = UangSaku::where('id', $id) + ->where('id_santri', $santri->id_santri) + ->with('santri:id_santri,nama_lengkap,kelas') + ->firstOrFail(); + + return view('santri.uang-saku.show', compact('transaksi', 'santri')); + + } catch (\Exception $e) { + Log::error('Error di Detail Uang Saku: ' . $e->getMessage()); + + return back()->with('error', 'Transaksi tidak ditemukan atau Anda tidak memiliki akses'); + } + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Kernel.php b/sim-pkpps/app/Http/Kernel.php new file mode 100644 index 0000000..e6547fa --- /dev/null +++ b/sim-pkpps/app/Http/Kernel.php @@ -0,0 +1,71 @@ + + */ + protected $middleware = [ + // \App\Http\Middleware\TrustHosts::class, + \App\Http\Middleware\TrustProxies::class, + \Illuminate\Http\Middleware\HandleCors::class, + \App\Http\Middleware\PreventRequestsDuringMaintenance::class, + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, + \App\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + ]; + + /** + * The application's route middleware groups. + * + * @var arraySistem Informasi Monitoring Santri
+Mohon gunakan email dan password yang kuat untuk keamanan sistem.
+{{ $error }}
+ @endforeach +| ID | +Judul & Konten | +Penulis | +Tanggal | +Status | +Target | +Aksi | +
|---|---|---|---|---|---|---|
| {{ $item->id_berita }} | +
+
+ {{ $item->judul }}
+
+ + {{ Str::limit(strip_tags($item->konten), 80) }} + |
+ {{ $item->penulis }} | +{{ $item->tanggal_formatted }} | ++ + @if($item->status === 'published') + Published + @else + Draft + @endif + + | ++ @php + $badgeClass = match($item->target_berita) { + 'semua' => 'badge-primary', + 'kelas_tertentu' => 'badge-info', + 'santri_tertentu' => 'badge-warning', + default => 'badge-secondary' + }; + @endphp + + {{ $item->target_audience }} + + | ++ + | +
+ Mulai tambahkan berita pertama untuk santri pesantren. +
+ + Tambah Berita Pertama + ++ + Berita ini ditujukan untuk santri dari kelas: + {{ implode(', ', $berita->target_kelas ?? []) }} +
+Belum ada santri yang dipilih untuk berita ini.
++ Mulai dengan membuat berita pertama untuk pesantren Anda. Berita dapat dipublikasikan untuk semua santri atau target tertentu. +
+ + Buat Berita Pertama + +Data capaian tercatat
+ +Santri aktif dengan capaian
+ +Progress keseluruhan
+ +Materi yang diselesaikan
+ ++ {{ $stats['count'] }} capaian | {{ $stats['selesai'] }} selesai +
+ +| Rank | +NIS | +Nama Santri | +Kelas | +Rata-rata Progress | +Aksi | +
|---|---|---|---|---|---|
| + @if($index < 3) + + @if($index == 0) 🥇 + @elseif($index == 1) 🥈 + @else 🥉 + @endif + + @else + {{ $index + 1 }} + @endif + | +{{ $item->santri->nis }} | +{{ $item->santri->nama_lengkap }} | +{{ $item->santri->kelas }} | ++ + | ++ + Lihat Detail + + | +
Belum ada data untuk ditampilkan
+| Nama Materi | +Kategori | +Kelas | +Jumlah Santri | +Rata-rata Progress | +Aksi | +
|---|---|---|---|---|---|
| {{ $item->materi->nama_kitab }} | +{!! $item->materi->kategori_badge !!} | +{!! $item->materi->kelas_badge !!} | +{{ $item->jumlah_santri }} santri | ++ + | ++ + Detail + + | +
+ {!! $materi->kategori_badge !!} | + {!! $materi->kelas_badge !!} | + Total: {{ $materi->total_halaman }} halaman +
+Santri yang belajar materi ini
+ +{{ $totalSantri > 0 ? number_format(($santriSelesai/$totalSantri)*100, 1) : 0 }}% dari total santri
+ +Progress 1-99%
+ +Progress keseluruhan
+ +| No | +NIS | +Nama Santri | +Kelas | +Semester | +Halaman | +Progress | +Aksi | +
|---|---|---|---|---|---|---|---|
| {{ $index + 1 }} | +{{ $capaian->santri->nis }} | ++ {{ $capaian->santri->nama_lengkap }} + | ++ {{ $capaian->santri->kelas }} + | ++ {{ $capaian->semester->nama_semester }} + | ++ + {{ $capaian->jumlah_halaman_selesai }} / {{ $materi->total_halaman }} + + | ++ + | ++ + | +
Belum ada santri yang mencatat capaian untuk materi ini.
+| No | +Santri | +Kelas | +Materi | +Kategori | +Semester | +Halaman | +Progress | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| {{ $capaians->firstItem() + $index }} | +
+ {{ $capaian->santri->nama_lengkap }} + {{ $capaian->santri->nis }} + |
+ + {{ $capaian->santri->kelas }} + | ++ {{ $capaian->materi->nama_kitab }} + | +{!! $capaian->materi->kategori_badge !!} | ++ {{ $capaian->semester->nama_semester }} + | ++ + {{ $capaian->jumlah_halaman_selesai }} / {{ $capaian->materi->total_halaman }} + + | +{!! $capaian->persentase_badge !!} | ++ + | +
Silakan input capaian santri terlebih dahulu.
+ + Input Capaian Pertama + +| Rank | +NIS | +Nama Santri | +Total Materi | +Progress per Kategori (%) | +Rata-rata | +Selesai | +||
|---|---|---|---|---|---|---|---|---|
| Al-Qur'an | +Hadist | +Tambahan | +||||||
| + @if($index < 3) + + @if($index == 0) 🥇 + @elseif($index == 1) 🥈 + @else 🥉 + @endif + + @else + {{ $index + 1 }} + @endif + | +{{ $data['santri']->nis }} | ++ {{ $data['santri']->nama_lengkap }} + | ++ {{ $data['total_materi'] }} materi + | ++ {{ number_format($data['alquran'], 1) }}% + | ++ {{ number_format($data['hadist'], 1) }}% + | ++ {{ number_format($data['tambahan'], 1) }}% + | ++ + | ++ + {{ $data['selesai'] }} / {{ $data['total_materi'] }} + + | +
Rata-rata Kelas
+Progress Tertinggi
+Progress Terendah
+Total Selesai
+Belum ada santri di kelas {{ $kelas }} atau belum ada capaian yang tercatat.
++ NIS: {{ $santri->nis }} | + Kelas: {{ $santri->kelas }} +
+Data capaian tercatat
+ +Progress keseluruhan
+ +Progress kategori
+ +Progress kategori
+ +| No | +Materi | +Semester | +Halaman | +Progress | +Tanggal Input | +Aksi | +
|---|---|---|---|---|---|---|
| {{ $index + 1 }} | +
+ {{ $capaian->materi->nama_kitab }} + Total: {{ $capaian->materi->total_halaman }} hal + |
+ + {{ $capaian->semester->nama_semester }} + | ++ + {{ $capaian->jumlah_halaman_selesai }} / {{ $capaian->materi->total_halaman }} + + | ++ {!! $capaian->persentase_badge !!} + + | +{{ $capaian->tanggal_input->format('d/m/Y') }} | ++ + | +
{{ $capaian->id_capaian }} | {{ $capaian->materi->nama_kitab }}
+Halaman Selesai
+Persentase
+Status
+| Nama Santri | +{{ $capaian->santri->nama_lengkap }} | +
|---|---|
| NIS | +{{ $capaian->santri->nis }} | +
| Kelas | +{{ $capaian->santri->kelas }} | +
| Nama Kitab | +{{ $capaian->materi->nama_kitab }} | +
|---|---|
| Kategori | +{!! $capaian->materi->kategori_badge !!} | +
| Total Halaman | +{{ $capaian->materi->total_halaman }} halaman | +
| Range Halaman | +Halaman {{ $capaian->materi->halaman_mulai }} - {{ $capaian->materi->halaman_akhir }} | +
| Semester | ++ {{ $capaian->semester->nama_semester }} + {!! $capaian->semester->status_badge !!} + | +
| Halaman Selesai (Range) | +
+
+ {{ $capaian->halaman_selesai }}
+
+ |
+
|---|---|
| Jumlah Halaman | ++ {{ $capaian->jumlah_halaman_selesai }} dari {{ $capaian->materi->total_halaman }} halaman + ({{ $capaian->materi->total_halaman - $capaian->jumlah_halaman_selesai }} halaman tersisa) + | +
| Persentase | +{!! $capaian->persentase_badge !!} ({{ number_format($capaian->persentase, 2) }}%) | +
| Catatan | +{{ $capaian->catatan ?: '-' }} | +
| Tanggal Input | +{{ $capaian->tanggal_input->format('d F Y') }} | +
| Dibuat Pada | +{{ $capaian->created_at->format('d F Y, H:i') }} WIB | +
| Terakhir Diupdate | +{{ $capaian->updated_at->format('d F Y, H:i') }} WIB | +
+ = Selesai + = Belum +
+Selamat datang di Sistem Informasi Monitoring Santri.
+{{ $data['total_santri'] }}
+ +{{ $data['total_wali'] }}
+ +{{ $data['kegiatan_hari_ini'] }}
+ +Area untuk menempatkan statistik dan grafik sistem.
+| No | +ID Kategori | +Nama Pelanggaran | +Poin | +Digunakan | +Aksi | +
|---|---|---|---|---|---|
| {{ $index + 1 }} | ++ {{ $item->id_kategori }} + | ++ {{ $item->nama_pelanggaran }} + | ++ + {{ $item->poin }} + + | ++ + {{ $item->riwayatPelanggaran->count() }}x + + | ++ + | +
Mulai dengan menambahkan kategori pelanggaran baru menggunakan form di atas.
+| ID Kategori | ++ + {{ $kategori->id_kategori }} + + | +
|---|---|
| Nama Pelanggaran | +{{ $kategori->nama_pelanggaran }} | +
| Poin | ++ + {{ $kategori->poin }} Poin + + | +
| Tanggal Dibuat | +{{ $kategori->created_at->format('d F Y, H:i') }} WIB | +
| Terakhir Diperbarui | +{{ $kategori->updated_at->format('d F Y, H:i') }} WIB | +
| No | +Tanggal | +Santri | +Poin | +Keterangan | +
|---|---|---|---|---|
| {{ $index + 1 }} | +{{ \Carbon\Carbon::parse($riwayat->tanggal)->format('d M Y') }} | +
+ @if($riwayat->santri)
+ {{ $riwayat->santri->nama_lengkap }} + {{ $riwayat->id_santri }} + @else + Santri tidak ditemukan + @endif + |
+ + {{ $riwayat->poin }} + | +{{ $riwayat->keterangan ?? '-' }} | +
Total Pelanggaran
+Total Poin Terkumpul
++ Kategori ini telah digunakan {{ $kategori->riwayatPelanggaran->count() }} kali + dalam sistem pencatatan pelanggaran santri. +
+| No | +Hari | +Waktu | +Nama Kegiatan | +Kategori | +Aksi | +
|---|---|---|---|---|---|
| {{ $kegiatans->firstItem() + $index }} | +{{ $kegiatan->hari }} | +{{ date('H:i', strtotime($kegiatan->waktu_mulai)) }} - {{ date('H:i', strtotime($kegiatan->waktu_selesai)) }} | +{{ $kegiatan->nama_kegiatan }} | +{{ $kegiatan->kategori->nama_kategori }} | ++ + Input + + + Rekap + + | +
+ {{ $kegiatan->hari }} | + {{ date('H:i', strtotime($kegiatan->waktu_mulai)) }} - {{ date('H:i', strtotime($kegiatan->waktu_selesai)) }} | + {{ $kegiatan->kategori->nama_kategori }} +
+| No | +Tanggal | +ID Santri | +Nama Santri | +Kelas | +Status | +Metode | +Waktu | +
|---|---|---|---|---|---|---|---|
| {{ $absensis->firstItem() + $index }} | +{{ $absensi->tanggal->format('d/m/Y') }} | +{{ $absensi->id_santri }} | +{{ $absensi->santri->nama_lengkap }} | +{{ $absensi->santri->kelas }} | +{!! $absensi->status_badge !!} | ++ @if($absensi->metode_absen == 'RFID') + RFID + @else + Manual + @endif + | +{{ $absensi->waktu_absen ? date('H:i', strtotime($absensi->waktu_absen)) : '-' }} | +
| No | +ID | +Hari | +Waktu | +Nama Kegiatan | +Kategori | +Materi | +Aksi | +
|---|---|---|---|---|---|---|---|
| {{ $kegiatans->firstItem() + $index }} | +{{ $kegiatan->kegiatan_id }} | +{{ $kegiatan->hari }} | +{{ date('H:i', strtotime($kegiatan->waktu_mulai)) }} - {{ date('H:i', strtotime($kegiatan->waktu_selesai)) }} | +{{ $kegiatan->nama_kegiatan }} | +{{ $kegiatan->kategori->nama_kategori }} | +{{ Str::limit($kegiatan->materi, 40) ?? '-' }} | ++ + + + + + + + | +
| ID Kegiatan | +{{ $kegiatan->kegiatan_id }} | +
|---|---|
| Kategori | ++ {{ $kegiatan->kategori->nama_kategori }} + | +
| Nama Kegiatan | +{{ $kegiatan->nama_kegiatan }} | +
| Hari | +{{ $kegiatan->hari }} | +
| Waktu Pelaksanaan | ++ + {{ date('H:i', strtotime($kegiatan->waktu_mulai)) }} - {{ date('H:i', strtotime($kegiatan->waktu_selesai)) }} WIB + | +
| Materi/Topik | +{{ $kegiatan->materi ?? '-' }} | +
| Keterangan | +{{ $kegiatan->keterangan ?? '-' }} | +
| Dibuat Pada | +{{ $kegiatan->created_at->format('d F Y, H:i') }} WIB | +
| Terakhir Diubah | +{{ $kegiatan->updated_at->format('d F Y, H:i') }} WIB | +
Pesantren Riyadlul Jannah
+Tempelkan kartu RFID ke reader, UID akan otomatis terdeteksi pada kolom di bawah.
+| No | +ID Santri | +Nama Santri | +Kelas | +UID RFID | +Status | +Aksi | +
|---|---|---|---|---|---|---|
| {{ $santris->firstItem() + $index }} | +{{ $santri->id_santri }} | +{{ $santri->nama_lengkap }} | +{{ $santri->kelas }} | +
+ @if($santri->rfid_uid)
+ {{ $santri->rfid_uid }}
+ @else
+ -
+ @endif
+ |
+ + @if($santri->rfid_uid) + Terdaftar + @else + Belum + @endif + | ++ @if($santri->rfid_uid) + + Cetak + + + @else + + Daftarkan RFID + + @endif + | +
Belum ada santri aktif yang terdaftar.
+| No | +ID Kategori | +Nama Kategori | +Keterangan | +Dibuat | +Aksi | +
|---|---|---|---|---|---|
| {{ $kategoris->firstItem() + $index }} | +{{ $kategori->kategori_id }} | +{{ $kategori->nama_kategori }} | +{{ Str::limit($kategori->keterangan, 50) ?? '-' }} | +{{ $kategori->created_at->format('d M Y') }} | ++ + + + + + + + | +
| ID Kategori | +{{ $kategoriKegiatan->kategori_id }} | +
|---|---|
| Nama Kategori | +{{ $kategoriKegiatan->nama_kategori }} | +
| Keterangan | +{{ $kategoriKegiatan->keterangan ?? '-' }} | +
| Dibuat Pada | +{{ $kategoriKegiatan->created_at->format('d F Y, H:i') }} WIB | +
| Terakhir Diubah | +{{ $kategoriKegiatan->updated_at->format('d F Y, H:i') }} WIB | +
+ ID: {{ $santri->id_santri }} | + Kelas: {{ $santri->kelas }} | + Status: {{ $santri->status }} +
+| No | +Tanggal | +Kegiatan | +Kategori | +Status | +Waktu | +
|---|---|---|---|---|---|
| {{ $riwayats->firstItem() + $index }} | +{{ $riwayat->tanggal->format('d/m/Y') }} | +{{ $riwayat->kegiatan->nama_kegiatan }} | +{{ $riwayat->kegiatan->kategori->nama_kategori }} | +{!! $riwayat->status_badge !!} | +{{ $riwayat->waktu_absen ? date('H:i', strtotime($riwayat->waktu_absen)) : '-' }} | +
Santri ini belum memiliki riwayat kehadiran.
+Hanya status kehadiran dan waktu absen yang dapat diubah.
+| No | +Tanggal | +ID Santri | +Nama Santri | +Kegiatan | +Kategori | +Status | +Metode | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| {{ $riwayats->firstItem() + $index }} | +{{ $riwayat->tanggal->format('d/m/Y') }} | +{{ $riwayat->id_santri }} | ++ + {{ $riwayat->santri->nama_lengkap }} + + | +{{ $riwayat->kegiatan->nama_kegiatan }} | +{{ $riwayat->kegiatan->kategori->nama_kategori }} | +{!! $riwayat->status_badge !!} | ++ @if($riwayat->metode_absen == 'RFID') + RFID + @else + Manual + @endif + | ++ + + + + + + + | +
Belum ada data riwayat kegiatan dan absensi.
+| ID Santri | +{{ $riwayat->santri->id_santri }} | +
|---|---|
| Nama Lengkap | +{{ $riwayat->santri->nama_lengkap }} | +
| Kelas | +{{ $riwayat->santri->kelas }} | +
| Status Santri | +{{ $riwayat->santri->status }} | +
| ID Kegiatan | +{{ $riwayat->kegiatan->kegiatan_id }} | +
|---|---|
| Nama Kegiatan | +{{ $riwayat->kegiatan->nama_kegiatan }} | +
| Kategori | +{{ $riwayat->kegiatan->kategori->nama_kategori }} | +
| Hari | +{{ $riwayat->kegiatan->hari }} | +
| Waktu Pelaksanaan | ++ + {{ date('H:i', strtotime($riwayat->kegiatan->waktu_mulai)) }} - + {{ date('H:i', strtotime($riwayat->kegiatan->waktu_selesai)) }} WIB + | +
| ID Absensi | +{{ $riwayat->absensi_id }} | +
|---|---|
| Tanggal Absensi | +{{ $riwayat->tanggal->format('d F Y') }} | +
| Status Kehadiran | +{!! $riwayat->status_badge !!} | +
| Metode Absensi | ++ @if($riwayat->metode_absen == 'RFID') + + RFID + + @else + + Manual + + @endif + | +
| Waktu Absen | ++ @if($riwayat->waktu_absen) + + {{ date('H:i:s', strtotime($riwayat->waktu_absen)) }} WIB + @else + - + @endif + | +
| Dicatat Pada | +{{ $riwayat->created_at->format('d F Y, H:i:s') }} WIB | +
| Terakhir Diubah | +{{ $riwayat->updated_at->format('d F Y, H:i:s') }} WIB | +
| ID | +Santri | +Tanggal Pulang | +Tanggal Kembali | +Durasi | +Alasan | +Status | +Aksi | +
|---|---|---|---|---|---|---|---|
| + {{ $item->id_kepulangan }} + @if(isset($santriOverLimit[$item->id_santri])) + + + + @endif + | +
+
+ {{ $item->santri->nama_lengkap ?? 'N/A' }}
+ + + {{ $item->santri->id_santri ?? '' }} | {{ $item->santri->kelas ?? '' }} + + |
+ {{ $item->tanggal_pulang_formatted }} | +{{ $item->tanggal_kembali_formatted }} | ++ + {{ $item->durasi_izin_calculated }} hari + + | ++ + | +
+
+ {{ $item->status }}
+
+ @if($item->is_aktif)
+ Sedang Izin + @elseif($item->is_terlambat) + Terlambat + @endif + |
+ + + | +
|
+
+ Tidak ada data kepulangan ditemukan + |
+ |||||||
| ID Kepulangan: | +{{ $kepulangan->id_kepulangan }} | +
|---|---|
| Tanggal Pengajuan: | +{{ $kepulangan->tanggal_izin_formatted }} | +
| Tanggal Pulang: | +{{ $kepulangan->tanggal_pulang_formatted }} | +
| Tanggal Kembali: | +{{ $kepulangan->tanggal_kembali_formatted }} | +
| Durasi Izin: | ++ + {{ $kepulangan->durasi_izin_calculated }} hari + + | +
| Status Kepulangan: | ++ @if($kepulangan->is_aktif) + Sedang Izin + @elseif($kepulangan->is_terlambat) + Terlambat Kembali + @elseif($kepulangan->status == 'Selesai') + Sudah Selesai + @else + Belum Dimulai + @endif + | +
| Alasan: | +{{ $kepulangan->alasan }} | +
| Disetujui Oleh: | +{{ $kepulangan->approved_by }} | +
| Tanggal Persetujuan: | +{{ $kepulangan->approved_at_formatted }} | +
| Catatan: | +{{ $kepulangan->catatan }} | +
| ID Santri: | +{{ $kepulangan->santri->id_santri }} | +
|---|---|
| Nama: | +{{ $kepulangan->santri->nama_lengkap }} | +
| NIS: | +{{ $kepulangan->santri->nis ?? '-' }} | +
| Kelas: | +{{ $kepulangan->santri->kelas }} | +
| Status: | ++ + {{ $kepulangan->santri->status }} + + | +
| ID | +Tanggal | +Durasi | +Status | +Alasan | +
|---|---|---|---|---|
| + + {{ $item->id_kepulangan }} + + | +{{ $item->tanggal_pulang_formatted }} | ++ + {{ $item->durasi_izin_calculated }} hari + + | ++ + {{ $item->status }} + + | +{{ \Illuminate\Support\Str::limit($item->alasan, 30) }} | +
Jl. Raya Pesantren No. 123, Jakarta Selatan
+Telp: (021) 123456 | Email: info@alhikmah.ac.id
+Yang bertanda tangan di bawah ini, Pengurus Pondok Pesantren Al-Hikmah, dengan ini menerangkan bahwa:
+ +Adalah benar-benar santri aktif di Pondok Pesantren Al-Hikmah dan telah mendapat izin untuk pulang ke rumah dengan keterangan sebagai berikut:
+ +Demikian surat izin ini dibuat untuk dapat digunakan sebagaimana mestinya. Santri yang bersangkutan WAJIB kembali pada tanggal yang telah ditentukan.
+ +Catatan Penting:
+Mengetahui,
Wali Santri
Jakarta, {{ $tanggalCetak }}
+Pengurus Pondok Pesantren
+ +Bidang Kesiswaan
+Alamat: [Alamat Lengkap Pesantren]
+Telepon: [Nomor Telepon] | Email: [Email Pesantren]
+Yang bertanda tangan di bawah ini, Petugas Unit Kesehatan Pesantren (UKP) + Pondok Pesantren [Nama Pesantren], dengan ini menerangkan bahwa:
+ +| Nama | +: | +{{ $kesehatanSantri->santri->nama_lengkap }} | +
| ID Santri | +: | +{{ $kesehatanSantri->santri->id_santri }} | +
| NIS | +: | +{{ $kesehatanSantri->santri->nis ?: '-' }} | +
| Kelas | +: | +{{ $kesehatanSantri->santri->kelas }} | +
| Jenis Kelamin | +: | +{{ $kesehatanSantri->santri->jenis_kelamin }} | +
| Tanggal Masuk UKP | +: | +{{ $kesehatanSantri->tanggal_masuk->locale('id')->isoFormat('D MMMM Y') }} | +
| Tanggal Keluar UKP | +: | +{{ $kesehatanSantri->tanggal_keluar->locale('id')->isoFormat('D MMMM Y') }} | +
| Lama Dirawat | +: | +{{ $kesehatanSantri->lama_dirawat }} hari | +
Keluhan/Diagnosa:
++ {{ $kesehatanSantri->keluhan }} +
+ + @if($kesehatanSantri->catatan) +Catatan Petugas:
++ {{ $kesehatanSantri->catatan }} +
+ @endif + +Berdasarkan kondisi kesehatan santri tersebut, kami merekomendasikan untuk:
+ +Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat digunakan sebagaimana mestinya.
+Wali Santri/Keluarga
+ +{{ $kesehatanSantri->santri->nama_orang_tua ?: '(...............................)' }}
+Petugas UKP
+ +[Nama Petugas UKP]
+Surat ini dibuat secara elektronis dan sah tanpa tanda tangan basah
+Dicetak pada: {{ now()->locale('id')->isoFormat('D MMMM Y HH:mm:ss') }} WIB
+| No | +ID Santri | +Nama Santri | +Tgl Masuk | +Keluhan | +Tgl Keluar | +Status | +Lama | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| {{ $kesehatanSantri->firstItem() + $index }} | +{{ $data->id_santri }} | +
+ {{ $data->santri->nama_lengkap }} + {{ $data->santri->kelas }} + |
+ {{ $data->tanggal_masuk_formatted }} | ++ + {{ Str::limit($data->keluhan, 50) }} + + | ++ @if($data->tanggal_keluar) + {{ $data->tanggal_keluar_formatted }} + @else + Belum keluar + @endif + | ++ + {{ ucfirst($data->status) }} + + | +{{ $data->lama_dirawat }} hari | ++ + | +
Belum ada data kesehatan santri yang tercatat atau sesuai dengan filter yang dipilih.
+ + Tambah Data Kesehatan + +{{ $santri->id_santri }}
+{{ $riwayatKesehatan->where('status', 'dirawat')->count() }}
+ +{{ $riwayatKesehatan->where('status', 'sembuh')->count() }}
+ +{{ $riwayatKesehatan->where('status', 'izin')->count() }}
+ ++ @if($riwayatKesehatan->count() > 0) + {{ round($riwayatKesehatan->avg(function($item) { return $item->lama_dirawat; }), 1) }} hari + @else + 0 hari + @endif +
+ +| No | +ID Kesehatan | +Tanggal Masuk | +Keluhan | +Catatan | +Tanggal Keluar | +Status | +Lama | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| {{ $riwayatKesehatan->firstItem() + $index }} | +{{ $data->id_kesehatan }} | +
+ {{ $data->tanggal_masuk_formatted }} + {{ $data->tanggal_masuk->format('D') }} + |
+
+
+ {{ Str::limit($data->keluhan, 80) }}
+
+ |
+
+ @if($data->catatan)
+
+ {{ Str::limit($data->catatan, 60) }}
+
+ @else
+ -
+ @endif
+ |
+
+ @if($data->tanggal_keluar)
+ {{ $data->tanggal_keluar_formatted }} + {{ $data->tanggal_keluar->format('D') }} + @else + Belum keluar + @endif + |
+ + + {{ ucfirst($data->status) }} + + | ++ {{ $data->lama_dirawat }} hari + | ++ + | +
Santri {{ $santri->nama_lengkap }} belum memiliki riwayat kesehatan.
+ + Tambah Data Kesehatan + ++ Terakhir diupdate: {{ $kesehatanSantri->updated_at->locale('id')->isoFormat('D MMMM Y, HH:mm') }} WIB +
+Lama Dirawat
++ {{ $kesehatanSantri->lama_dirawat }} Hari +
+Selesai Perawatan
+Gejala yang dialami
++ {{ $kesehatanSantri->keluhan }} +
+Informasi tambahan dari petugas UKP
++ {{ $kesehatanSantri->catatan }} +
+| ID | +Tanggal Masuk | +Keluhan | +Tanggal Keluar | +Status | +Lama | +Aksi | +
|---|---|---|---|---|---|---|
| {{ $riwayat->id_kesehatan }} | +
+ {{ $riwayat->tanggal_masuk_formatted }} + {{ $riwayat->tanggal_masuk->format('D') }} + |
+ + + {{ Str::limit($riwayat->keluhan, 40) }} + + | ++ @if($riwayat->tanggal_keluar) + {{ $riwayat->tanggal_keluar_formatted }} + @else + - + @endif + | ++ + {{ ucfirst($riwayat->status) }} + + | +{{ $riwayat->lama_dirawat }} hari | ++ + + + | +
| No | +ID Materi | +Kategori | +Kelas | +Nama Kitab | +Halaman | +Total Hal | +Aksi | +
|---|---|---|---|---|---|---|---|
| {{ $materis->firstItem() + $index }} | +{{ $materi->id_materi }} | +{!! $materi->kategori_badge !!} | +{!! $materi->kelas_badge !!} | +{{ $materi->nama_kitab }} | ++ + {{ $materi->halaman_mulai }} - {{ $materi->halaman_akhir }} + + | ++ {{ $materi->total_halaman }} hal + | ++ + | +
Silakan tambahkan materi pembelajaran Al-Qur'an, Hadist, atau Materi Tambahan.
+ + Tambah Materi Pertama + +ID: {{ $materi->id_materi }}
+| ID Materi | +{{ $materi->id_materi }} | +
|---|---|
| Kategori | +{!! $materi->kategori_badge !!} | +
| Kelas | +{!! $materi->kelas_badge !!} | +
| Nama Kitab | +{{ $materi->nama_kitab }} | +
| Halaman Mulai | +{{ $materi->halaman_mulai }} | +
| Halaman Akhir | +{{ $materi->halaman_akhir }} | +
| Total Halaman | ++ + {{ $materi->total_halaman }} Halaman + + | +
| Deskripsi | +{{ $materi->deskripsi ?? '-' }} | +
| Dibuat Pada | +{{ $materi->created_at->format('d F Y, H:i') }} WIB | +
| Terakhir Diupdate | +{{ $materi->updated_at->format('d F Y, H:i') }} WIB | +
+ Fitur statistik capaian santri akan tersedia setelah implementasi Langkah 2: Input Capaian per Santri. +
+Santri yang ada capaian
+ +Santri yang menyelesaikan
+ +Progress keseluruhan
+ +No. Bukti: {{ $pembayaranSpp->id_pembayaran }}
+| ID Santri | +: | +{{ $pembayaranSpp->santri->id_santri }} | +
| Nama Santri | +: | +{{ $pembayaranSpp->santri->nama_lengkap }} | +
| Kelas | +: | +{{ $pembayaranSpp->santri->kelas_lengkap }} | +
| Periode Pembayaran | +: | +{{ $pembayaranSpp->periode_lengkap }} | +
| Tanggal Bayar | +: | ++ @if($pembayaranSpp->tanggal_bayar) + {{ $pembayaranSpp->tanggal_bayar->format('d F Y') }} + @else + Belum dibayar + @endif + | +
| Batas Pembayaran | +: | +{{ $pembayaranSpp->batas_bayar->format('d F Y') }} | +
| Keterangan | +: | +{{ $pembayaranSpp->keterangan }} | +
Santri / Wali Santri
+ ( ) +Petugas
+ ( ) +Tanggal Cetak: {{ date('d F Y, H:i') }} WIB
+| ID Santri | +: {{ $santri->id_santri }} | +
| Nama Lengkap | +: {{ $santri->nama_lengkap }} | +
| NIS | +: {{ $santri->nis ?? '-' }} | +
| Kelas | +: {{ $santri->kelas_lengkap }} | +
| Status | +: {{ $santri->status }} | +
| No | +ID Pembayaran | +Periode | +Nominal | +Batas Bayar | +Tanggal Bayar | +Status | +
|---|---|---|---|---|---|---|
| {{ $index + 1 }} | +{{ $spp->id_pembayaran }} | +{{ $spp->periode_lengkap }} | +{{ $spp->nominal_format }} | +{{ $spp->batas_bayar->format('d/m/Y') }} | ++ @if($spp->tanggal_bayar) + {{ $spp->tanggal_bayar->format('d/m/Y') }} + @else + - + @endif + | ++ @if($spp->status === 'Lunas') + Lunas + @elseif($spp->isTelat()) + Telat + @else + Belum Lunas + @endif + | +
| + Belum ada riwayat pembayaran untuk santri ini + | +||||||
Tanggal Cetak: {{ date('d F Y, H:i') }} WIB
+| Bulan | +: {{ DateTime::createFromFormat('!m', request('bulan'))->format('F') }} | +
| Tahun | +: {{ request('tahun') }} | +
| Status | +: {{ request('status') }} | +
| No | +ID | +Santri | +Periode | +Nominal | +Batas Bayar | +Tgl Bayar | +Status | +
|---|---|---|---|---|---|---|---|
| {{ $index + 1 }} | +{{ $spp->id_pembayaran }} | +
+ {{ $spp->santri->nama_lengkap }} + {{ $spp->santri->id_santri }} + |
+ {{ $spp->periode_lengkap }} | +{{ $spp->nominal_format }} | +{{ $spp->batas_bayar->format('d/m/Y') }} | ++ @if($spp->tanggal_bayar) + {{ $spp->tanggal_bayar->format('d/m/Y') }} + @else + - + @endif + | ++ @if($spp->status === 'Lunas') + Lunas + @elseif($spp->isTelat()) + Telat + @else + Belum Lunas + @endif + | +
| + Tidak ada data pembayaran SPP + | +|||||||
| No | +ID Pembayaran | +Santri | +Periode | +Nominal | +Batas Bayar | +Status | +Aksi | +
|---|---|---|---|---|---|---|---|
| {{ $pembayaranSpp->firstItem() + $index }} | +{{ $spp->id_pembayaran }} | +
+ {{ $spp->santri->nama_lengkap }} + {{ $spp->santri->id_santri }} - {{ $spp->santri->kelas }} + |
+ {{ $spp->periode_lengkap }} | +{{ $spp->nominal_format }} | +
+ {{ $spp->batas_bayar->format('d/m/Y') }}
+ @if($spp->isTelat())
+ + Telat + + @endif + |
+ {!! $spp->status_badge !!} | ++ + + + + + + + | +
|
+
+ Tidak ada data pembayaran SPP. + |
+ |||||||
+ Cetak laporan semua pembayaran SPP atau dengan filter tertentu +
+ ++ Cetak laporan pembayaran SPP untuk santri tertentu +
+ ++ {{ $santri->id_santri }} • {{ $santri->nis ?? '-' }} • {{ $santri->kelas_lengkap }} +
+| No | +ID Pembayaran | +Periode | +Nominal | +Batas Bayar | +Tanggal Bayar | +Status | +Aksi | +
|---|---|---|---|---|---|---|---|
| {{ $pembayaranSpp->firstItem() + $index }} | +{{ $spp->id_pembayaran }} | +{{ $spp->periode_lengkap }} | +{{ $spp->nominal_format }} | +
+ {{ $spp->batas_bayar->format('d/m/Y') }}
+ @if($spp->isTelat())
+ + Telat + + @endif + |
+ + @if($spp->tanggal_bayar) + {{ $spp->tanggal_bayar->format('d/m/Y') }} + @else + - + @endif + | +{!! $spp->status_badge !!} | ++ + + + + + + | +
|
+
+ Belum ada riwayat pembayaran untuk santri ini. + |
+ |||||||
| ID Pembayaran | +{{ $pembayaranSpp->id_pembayaran }} | +
|---|---|
| Periode | +{{ $pembayaranSpp->periode_lengkap }} | +
| Nominal | +{{ $pembayaranSpp->nominal_format }} | +
| Status | +{!! $pembayaranSpp->status_badge !!} | +
| Batas Bayar | +
+ {{ $pembayaranSpp->batas_bayar->format('d F Y') }}
+ @if($pembayaranSpp->isTelat())
+ + + + Terlambat {{ $pembayaranSpp->batas_bayar->diffInDays(now()) }} hari + + @endif + |
+
| Tanggal Bayar | ++ @if($pembayaranSpp->tanggal_bayar) + {{ $pembayaranSpp->tanggal_bayar->format('d F Y') }} + @else + Belum dibayar + @endif + | +
| Keterangan | +{{ $pembayaranSpp->keterangan }} | +
| ID Santri | +{{ $pembayaranSpp->santri->id_santri }} | +
|---|---|
| Nama Lengkap | +{{ $pembayaranSpp->santri->nama_lengkap }} | +
| NIS | +{{ $pembayaranSpp->santri->nis ?? '-' }} | +
| Kelas | +{{ $pembayaranSpp->santri->kelas_lengkap }} | +
| Status | +{!! $pembayaranSpp->santri->status_badge !!} | +
| Aksi | ++ + Lihat Riwayat Pembayaran + + | +
| Dibuat Pada | +{{ $pembayaranSpp->created_at->format('d F Y, H:i') }} WIB | +
|---|---|
| Diperbarui Pada | +{{ $pembayaranSpp->updated_at->format('d F Y, H:i') }} WIB | +
Semua Riwayat
+ +{{ \Carbon\Carbon::now()->format('F Y') }}
+ +Akumulasi Poin
+ +| No | +ID Riwayat | +Tanggal | +Santri | +Kategori Pelanggaran | +Poin | +Aksi | +
|---|---|---|---|---|---|---|
| {{ $data->firstItem() + $index }} | ++ {{ $item->id_riwayat }} + | ++ + {{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} + | +
+ @if($item->santri)
+ {{ $item->santri->nama_lengkap }} + + {{ $item->id_santri }} + + @else + Santri tidak ditemukan + @endif + |
+
+ @if($item->kategori)
+ {{ $item->kategori->nama_pelanggaran }} + + {{ $item->id_kategori }} + + @else + Kategori tidak ditemukan + @endif + |
+ + + {{ $item->poin }} + + | ++ + | +
Silakan tambah riwayat pelanggaran baru menggunakan tombol di atas.
+ + Tambah Riwayat + ++ {{ $santri->id_santri }} | {{ $santri->kelas }} +
+Jumlah Pelanggaran
+ +Akumulasi Poin
+ +| No | +ID Riwayat | +Tanggal | +Kategori Pelanggaran | +Poin | +Keterangan | +Aksi | +
|---|---|---|---|---|---|---|
| {{ $riwayat->firstItem() + $index }} | ++ {{ $item->id_riwayat }} + | ++ + {{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} + | +
+ @if($item->kategori)
+ {{ $item->kategori->nama_pelanggaran }} + + {{ $item->id_kategori }} + + @else + Kategori tidak ditemukan + @endif + |
+ + + {{ $item->poin }} + + | ++ @if($item->keterangan) + + @else + - + @endif + | ++ + | +
Santri {{ $santri->nama_lengkap }} belum memiliki catatan pelanggaran.
+ + Tambah Pelanggaran + +Rata-rata Poin/Pelanggaran
+Kategori Pelanggaran
+Pelanggaran Terakhir
+| ID Riwayat | ++ + {{ $riwayatPelanggaran->id_riwayat }} + + | +
|---|---|
| Tanggal Pelanggaran | ++ + {{ \Carbon\Carbon::parse($riwayatPelanggaran->tanggal)->isoFormat('dddd, D MMMM YYYY') }} + + | +
| Poin Pelanggaran | ++ + {{ $riwayatPelanggaran->poin }} Poin + + | +
| Tanggal Dicatat | +{{ $riwayatPelanggaran->created_at->format('d F Y, H:i') }} WIB | +
| Terakhir Diperbarui | +{{ $riwayatPelanggaran->updated_at->format('d F Y, H:i') }} WIB | +
| ID Santri | ++ {{ $riwayatPelanggaran->santri->id_santri }} + | +
|---|---|
| Nama Lengkap | ++ {{ $riwayatPelanggaran->santri->nama_lengkap }} + | +
| Kelas | +{{ $riwayatPelanggaran->santri->kelas }} | +
| NIS | +{{ $riwayatPelanggaran->santri->nis }} | +
| Total Poin Pelanggaran | ++ + {{ $riwayatPelanggaran->santri->total_poin_pelanggaran }} Poin + + | +
Data santri tidak ditemukan
+| ID Kategori | ++ {{ $riwayatPelanggaran->kategori->id_kategori }} + | +
|---|---|
| Nama Pelanggaran | ++ {{ $riwayatPelanggaran->kategori->nama_pelanggaran }} + | +
| Poin Kategori | ++ + {{ $riwayatPelanggaran->kategori->poin }} Poin + + | +
Kategori tidak ditemukan
+{{ $riwayatPelanggaran->keterangan }}
+| No | +Tanggal | +Kategori | +Poin | +Aksi | +
|---|---|---|---|---|
| {{ $index + 1 }} | +{{ \Carbon\Carbon::parse($riwayat->tanggal)->format('d M Y') }} | ++ @if($riwayat->kategori) + {{ $riwayat->kategori->nama_pelanggaran }} + @else + - + @endif + | ++ {{ $riwayat->poin }} + | ++ + + + | +
Poin Pelanggaran Ini
+Total Poin Santri
++ Riwayat ini dicatat pada {{ $riwayatPelanggaran->created_at->format('d F Y') }} + untuk pelanggaran yang terjadi pada {{ \Carbon\Carbon::parse($riwayatPelanggaran->tanggal)->format('d F Y') }}. +
++ + Sedang mengedit data: {{ $santri->nama_lengkap }} ({{ $santri->id_santri }}) +
+| No | +ID Santri | +NIS | +Nama Lengkap | +Jenis Kelamin | +Kelas | +Status | +Aksi | +
|---|---|---|---|---|---|---|---|
| {{ $index + 1 }} | +{{ $santri->id_santri }} | +{{ $santri->nis ?? '-' }} | +{{ $santri->nama_lengkap }} | +{{ $santri->jenis_kelamin }} | +{{ $santri->kelas }} | ++ @if($santri->status == 'Aktif') + + {{ $santri->status }} + + @elseif($santri->status == 'Lulus') + + {{ $santri->status }} + + @else + + {{ $santri->status }} + + @endif + | ++ + + + + + | +
|
+
+ @if(request('search') || request('status') || request('kelas'))
+ Data tidak ditemukan. + Coba ubah kata kunci pencarian atau filter yang digunakan. + @else + Belum ada data santri. + Klik tombol "Tambah Santri" untuk menambahkan data baru. + @endif + |
+ |||||||
+ + Menampilkan {{ $santris->count() }} data santri + @if(request('search') || request('status') || request('kelas')) + dari hasil pencarian/filter + @endif +
++ {{ $santri->id_santri }} + @if($santri->nis) + | NIS: {{ $santri->nis }} + @endif +
+| ID Santri | +{{ $santri->id_santri }} | +
|---|---|
| NIS | +{{ $santri->nis ?? '-' }} | +
| Nama Lengkap | +{{ $santri->nama_lengkap }} | +
| Jenis Kelamin | ++ @if($santri->jenis_kelamin == 'Laki-laki') + {{ $santri->jenis_kelamin }} + @else + {{ $santri->jenis_kelamin }} + @endif + | +
| Kelas | ++ {{ $santri->kelas }} + @if($santri->kelas == 'PB') + (Pembinaan) + @endif + | +
| Status | ++ @if($santri->status == 'Aktif') + + {{ $santri->status }} + + @elseif($santri->status == 'Lulus') + + {{ $santri->status }} + + @else + + {{ $santri->status }} + + @endif + | +
| Alamat Santri | +{{ $santri->alamat_santri ?? '-' }} | +
|---|---|
| Daerah Asal | ++ @if($santri->daerah_asal) + {{ $santri->daerah_asal }} + @else + - + @endif + | +
| Nama Orang Tua | +{{ $santri->nama_orang_tua ?? '-' }} | +
|---|---|
| Nomor HP Orang Tua | ++ @if($santri->nomor_hp_ortu) + + + {{ $santri->nomor_hp_ortu }} + + @else + - + @endif + | +
| Tanggal Dibuat | ++ + {{ $santri->created_at->format('d M Y, H:i') }} WIB + + ({{ $santri->created_at->diffForHumans() }}) + + | +
|---|---|
| Terakhir Diupdate | ++ + {{ $santri->updated_at->format('d M Y, H:i') }} WIB + + ({{ $santri->updated_at->diffForHumans() }}) + + | +
+ + Informasi: Data santri ini dapat diedit atau dihapus melalui halaman index atau menggunakan tombol Edit di atas. +
+| No | +ID Semester | +Nama Semester | +Tahun Ajaran | +Periode | +Tanggal | +Status | +Aksi | +
|---|---|---|---|---|---|---|---|
| {{ $semesters->firstItem() + $index }} | +{{ $semester->id_semester }} | +{{ $semester->nama_semester }} | +{{ $semester->tahun_ajaran }} | ++ + Semester {{ $semester->periode }} + + | +
+
+ {{ $semester->tanggal_mulai->format('d/m/Y') }} - + {{ $semester->tanggal_akhir->format('d/m/Y') }} + + |
+ {!! $semester->status_badge !!} | ++ + | +
Silakan tambahkan semester terlebih dahulu sebelum mengelola capaian santri.
+ + Tambah Semester Pertama + +{{ $semester->id_semester }}
+Data capaian tercatat
+ +Santri dengan capaian
+ +Progress keseluruhan
+ +| ID Semester | +{{ $semester->id_semester }} | +
|---|---|
| Nama Semester | +{{ $semester->nama_semester }} | +
| Tahun Ajaran | +{{ $semester->tahun_ajaran }} | +
| Periode | ++ + Semester {{ $semester->periode }} + + | +
| Tanggal Mulai | +{{ $semester->tanggal_mulai->format('d F Y') }} | +
| Tanggal Akhir | +{{ $semester->tanggal_akhir->format('d F Y') }} | +
| Durasi | +{{ $semester->tanggal_mulai->diffInDays($semester->tanggal_akhir) }} hari | +
| Status | +{!! $semester->status_badge !!} | +
| Dibuat Pada | +{{ $semester->created_at->format('d F Y, H:i') }} WIB | +
| No | +ID Transaksi | +Santri | +Tanggal | +Jenis | +Nominal | +Keterangan | +Saldo | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| {{ $transaksi->firstItem() + $index }} | +{{ $item->id_uang_saku }} | ++ + {{ $item->santri->nama_lengkap }} + + | +{{ $item->tanggal_transaksi->format('d/m/Y') }} | ++ @if($item->jenis_transaksi === 'pemasukan') + + Pemasukan + + @else + + Pengeluaran + + @endif + | ++ {{ $item->nominal_format }} + | +
+
+ {{ $item->keterangan ?? '-' }}
+
+ |
+ + + {{ $item->saldo_sesudah_format }} + + | ++ + | +
Belum ada transaksi uang saku yang tercatat. Tambahkan transaksi pertama!
+ + Tambah Transaksi + ++ + Periode: + {{ $periodeDari->format('d F Y') }} - {{ $periodeSampai->format('d F Y') }} + ({{ $periodeDari->diffInDays($periodeSampai) + 1 }} hari) +
++ Periode yang dipilih +
+ ++ Periode yang dipilih +
+ ++ Pemasukan - Pengeluaran +
+ ++ Total keseluruhan +
+ +| No | +ID Transaksi | +Tanggal | +Jenis | +Nominal | +Saldo Sebelum | +Saldo Sesudah | +Keterangan | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| {{ $transaksi->firstItem() + $index }} | +{{ $item->id_uang_saku }} | +{{ $item->tanggal_transaksi->format('d/m/Y') }} | ++ @if($item->jenis_transaksi === 'pemasukan') + + Pemasukan + + @else + + Pengeluaran + + @endif + | ++ {{ $item->nominal_format }} + | ++ Rp {{ number_format($item->saldo_sebelum, 0, ',', '.') }} + | ++ + {{ $item->saldo_sesudah_format }} + + | +
+
+ {{ $item->keterangan ?? '-' }}
+
+ |
+ + + | +
Tidak ada transaksi pada periode {{ $periodeDari->format('d F Y') }} - {{ $periodeSampai->format('d F Y') }}
+ + Tambah Transaksi + +| ID Transaksi | +{{ $transaksi->id_uang_saku }} | +
|---|---|
| Santri | +
+ {{ $transaksi->santri->nama_lengkap }} + {{ $transaksi->santri->id_santri }} - {{ $transaksi->santri->kelas }} + |
+
| Jenis Transaksi | ++ @if($transaksi->jenis_transaksi === 'pemasukan') + + Pemasukan + + @else + + Pengeluaran + + @endif + | +
| Nominal | ++ {{ $transaksi->nominal_format }} + | +
| Tanggal Transaksi | +{{ $transaksi->tanggal_transaksi->format('d F Y') }} | +
| Keterangan | +{{ $transaksi->keterangan ?? '-' }} | +
| Saldo Sebelum | ++ Rp {{ number_format($transaksi->saldo_sebelum, 0, ',', '.') }} + | +
|---|---|
| {{ $transaksi->jenis_transaksi === 'pemasukan' ? 'Pemasukan' : 'Pengeluaran' }} | ++ + {{ $transaksi->jenis_transaksi === 'pemasukan' ? '+' : '-' }} + {{ $transaksi->nominal_format }} + + | +
| Saldo Sesudah | ++ + {{ $transaksi->saldo_sesudah_format }} + + | +
| Dibuat Pada | +{{ $transaksi->created_at->format('d F Y, H:i') }} WIB | +
|---|---|
| Terakhir Diubah | +{{ $transaksi->updated_at->format('d F Y, H:i') }} WIB | +
| ID Santri | +Nama | +Username | +Aksi | +
|---|---|---|---|
| {{ $user->role_id }} | +{{ $user->name }} | +{{ $user->username }} | ++ Reset Password + Hapus Akun + | +
| Belum ada akun Santri yang terdaftar. | +|||
Berikut adalah data santri yang sudah terdaftar di Data Santri namun belum memiliki akun login. Mereka dapat dipilih saat Anda membuat akun baru.
+ {{-- Tampilkan daftar santri yang belum punya akun (opsional) --}} +| ID Wali | +Nama | +Username | +Aksi | +
|---|---|---|---|
| {{ $user->role_id }} | +{{ $user->name }} | +{{ $user->username }} | ++ Reset Password + Hapus Akun + | +
| Belum ada akun Wali Santri yang terdaftar. | +|||
Pastikan Anda sudah mendaftarkan biodata wali santri di tabel `walis` sebelum membuat akun login.
+Monitoring Santri Berbasis Web
+ ++ {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }} +
++ {{ __('Ensure your account is using a long, random password to stay secure.') }} +
++ {{ __("Update your account's profile information and email address.") }} +
+Akses progres dan laporan santri.
++ Informasi terbaru untuk {{ $santri->kelas }} +
+Belum ada berita atau pengumuman yang dipublikasikan untuk Anda.
++ Dipublikasikan pada {{ $berita->created_at->format('d F Y, H:i') }} WIB +
++ Pantau progres hafalan dan pembelajaran Anda +
++ {{ number_format(($materiSelesai / $totalCapaian) * 100, 1) }}% dari total materi +
+ @endif ++ NIS: {{ $santri->nis ?? '-' }} +
+| No | +Materi | +Kategori | +Halaman Selesai | +Progress | +Tanggal Input | +Aksi | +
|---|---|---|---|---|---|---|
| {{ $index + 1 }} | ++ {{ $capaian->materi->nama_kitab }} + | ++ + {{ $capaian->materi->kategori }} + + | ++ + {{ count($capaian->pages_array) }} dari {{ $capaian->materi->total_halaman }} halaman + + | +
+
+
+
+ {{ number_format($capaian->persentase, 1) }}%
+
+
+ |
+ {{ \Carbon\Carbon::parse($capaian->tanggal_input)->format('d M Y') }} | ++ + Detail + + | +
+ @if($selectedSemester) + Tidak ada data capaian untuk semester yang dipilih. + @else + Belum ada data capaian yang tercatat untuk Anda. + @endif +
+| Nama Materi | +{{ $capaian->materi->nama_kitab }} | +
|---|---|
| Kategori | ++ + {{ $capaian->materi->kategori }} + + | +
| Total Halaman | +{{ $capaian->materi->total_halaman }} halaman (Hal. {{ $capaian->materi->halaman_mulai }} - {{ $capaian->materi->halaman_akhir }}) | +
| Persentase Selesai | +
+
+
+
+ {{ number_format($capaian->persentase, 2) }}%
+
+
+ |
+
|---|---|
| Jumlah Halaman Selesai | ++ + {{ count($capaian->pages_array) }} + dari {{ $capaian->materi->total_halaman }} halaman + | +
| Detail Halaman Selesai | +
+
+ {{ $capaian->halaman_selesai }}
+
+ |
+
| Tanggal Input | +{{ \Carbon\Carbon::parse($capaian->tanggal_input)->format('d F Y') }} | +
| Semester | ++ + {{ $capaian->semester->nama_semester }} - {{ $capaian->semester->tahun_ajaran }} + + | +
| Catatan | +
+
+
+ {{ $capaian->catatan }}
+
+ |
+
Terus semangat untuk materi berikutnya!
+Tinggal sedikit lagi, pertahankan semangatmu!
+Terus berjuang, jangan menyerah!
+Setiap perjalanan dimulai dari langkah pertama. Semangat!
++ Selamat datang, {{ $data['nama_santri'] }} - Kelas {{ $data['kelas'] }} +
++ {{ $berita->created_at->diffForHumans() }} +
++ 💡 Jaga Kedisiplinan: Hindari pelanggaran dengan mematuhi tata tertib pondok. + Lihat Daftar Kategori Pelanggaran + untuk mengetahui peraturan yang berlaku. +
++ Riwayat izin pulang {{ $santri->nama_lengkap }} +
++ Tahun {{ $tahunSekarang }} +
++ Izin diterima +
++ Melebihi batas! +
+ @else ++ Dari kuota 12 hari +
+ @endif ++ {{ $statistik['menunggu'] }} menunggu +
+ @else ++ Kuota tersisa +
+ @endif +Anda belum pernah mengajukan izin kepulangan pada periode yang dipilih.
++ {{ $item->id_kepulangan }} +
++ ID: {{ $kepulangan->id_kepulangan }} +
+| ID Kepulangan | +{{ $kepulangan->id_kepulangan }} | +
|---|---|
| Tanggal Pengajuan | +{{ $kepulangan->tanggal_izin_formatted }} | +
| Tanggal Pulang | +{{ $kepulangan->tanggal_pulang_formatted }} | +
| Tanggal Kembali | +{{ $kepulangan->tanggal_kembali_formatted }} | +
| Durasi Izin | ++ {{ $kepulangan->durasi_izin }} hari + | +
| Alasan Kepulangan | +{{ $kepulangan->alasan }} | +
| Status | ++ + {{ $kepulangan->status }} + + | +
| Disetujui Oleh | +{{ $kepulangan->approved_by }} | +
| Tanggal Persetujuan | +{{ $kepulangan->approved_at_formatted }} | +
| Catatan Admin | +{{ $kepulangan->catatan }} | +
Total Hari Pulang
++ {{ $totalHariTahunIni }} hari +
+Sisa Kuota
++ {{ $sisaKuota }} hari +
+Kuota Maksimal
++ 12 hari +
++ Riwayat kunjungan UKP {{ $santri->nama_lengkap }} +
++ Periode yang dipilih +
++ Perlu perhatian +
+ @else ++ Tidak ada yang dirawat +
+ @endif ++ Alhamdulillah +
++ Izin pulang +
++ + Menampilkan data periode: + + {{ $tanggalDari->locale('id')->isoFormat('D MMMM Y') }} - {{ $tanggalSampai->locale('id')->isoFormat('D MMMM Y') }} + + ({{ $tanggalDari->diffInDays($tanggalSampai) + 1 }} hari) +
+Tidak ada riwayat kesehatan pada periode yang dipilih.
+ + Lihat Semua Data + ++ {{ $item->id_kesehatan }} +
++ ID: {{ $kesehatanSantri->id_kesehatan }} +
+| ID Kesehatan | +{{ $kesehatanSantri->id_kesehatan }} | +
|---|---|
| Tanggal Masuk UKP | +{{ $kesehatanSantri->tanggal_masuk_formatted }} | +
| Tanggal Keluar UKP | ++ @if($kesehatanSantri->tanggal_keluar) + {{ $kesehatanSantri->tanggal_keluar_formatted }} + @else + Belum keluar + @endif + | +
| Lama Dirawat | ++ {{ $kesehatanSantri->lama_dirawat }} hari + | +
| Keluhan | +{{ $kesehatanSantri->keluhan }} | +
| Catatan Medis | +{{ $kesehatanSantri->catatan }} | +
| Status | ++ + {{ ucfirst($kesehatanSantri->status) }} + + | +
| No | +ID Riwayat | +Tanggal | +Jenis Pelanggaran | +Poin | +Keterangan | +Aksi | +
|---|---|---|---|---|---|---|
| {{ $riwayat->firstItem() + $index }} | +{{ $item->id_riwayat }} | ++ + {{ \Carbon\Carbon::parse($item->tanggal)->isoFormat('D MMM YYYY') }} + | +{{ $item->kategori->nama_pelanggaran ?? '-' }} | ++ + {{ $item->poin }} + + | +
+
+ {{ $item->keterangan ?: '-' }}
+
+ |
+ + + Detail + + | +
Selamat! Anda belum memiliki catatan pelanggaran.
++ + Berikut adalah daftar kategori pelanggaran beserta poin yang berlaku di pondok. +
+ + Kembali ke Riwayat + +| No | +Kode | +Jenis Pelanggaran | +Poin | +
|---|---|---|---|
| {{ $index + 1 }} | +{{ $kategori->id_kategori }} | +{{ $kategori->nama_pelanggaran }} | ++ @if($kategori->poin <= 5) + + {{ $kategori->poin }} Poin + + @elseif($kategori->poin <= 15) + + {{ $kategori->poin }} Poin + + @else + + {{ $kategori->poin }} Poin + + @endif + | +
Daftar kategori pelanggaran belum tersedia.
+| ID Riwayat | +{{ $riwayatPelanggaran->id_riwayat }} | +
|---|---|
| Tanggal Kejadian | +{{ \Carbon\Carbon::parse($riwayatPelanggaran->tanggal)->isoFormat('dddd, D MMMM YYYY') }} | +
| Jenis Pelanggaran | +{{ $riwayatPelanggaran->kategori->nama_pelanggaran ?? '-' }} | +
| Poin Pelanggaran | ++ + {{ $riwayatPelanggaran->poin }} Poin + + | +
| Keterangan | +{{ $riwayatPelanggaran->keterangan ?: '-' }} | +
| Dicatat Pada | +{{ $riwayatPelanggaran->created_at->isoFormat('D MMMM YYYY, HH:mm') }} WIB | +
+ + Catatan: Data pelanggaran ini dicatat oleh admin/pengurus pondok. + Jika ada kesalahan data, silakan hubungi bagian administrasi. +
++ {{ $santri->id_santri }} + @if($santri->nis) + | NIS: {{ $santri->nis }} + @endif +
+| ID Santri | +{{ $santri->id_santri }} | +
|---|---|
| NIS | +{{ $santri->nis }} | +
| Nama Lengkap | +{{ $santri->nama_lengkap }} | +
| Jenis Kelamin | +{{ $santri->jenis_kelamin }} | +
| Kelas | ++ + + {{ $santri->kelas_lengkap }} + + | +
| Status | +{!! $santri->status_badge !!} | +
| Terdaftar Sejak | +{{ $santri->created_at->format('d F Y') }} | +
| Alamat Lengkap | +{{ $santri->alamat_santri ?? '-' }} | +
|---|---|
| Daerah Asal | +{{ $santri->daerah_asal ?? '-' }} | +
| Nama Orang Tua/Wali | +{{ $santri->nama_orang_tua ?? '-' }} | +
| Nomor HP Orang Tua | ++ @if($santri->nomor_hp_ortu) + + {{ $santri->nomor_hp_ortu }} + + @else + - + @endif + | +
+ + Kartu RFID Anda sudah terdaftar +
+
+ UID: {{ $santri->rfid_uid }}
+
+ + Catatan: Jika ada data yang perlu diperbarui selain alamat dan nomor HP orang tua, silakan hubungi admin atau pengurus pesantren. +
+| No | +ID Transaksi | +Tanggal | +Jenis | +Nominal | +Keterangan | +Saldo Sebelum | +Saldo Sesudah | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| {{ $riwayatUangSaku->firstItem() + $index }} | +{{ $transaksi->id_uang_saku }} | +{{ \Carbon\Carbon::parse($transaksi->tanggal_transaksi)->format('d/m/Y') }} | ++ @if($transaksi->jenis_transaksi === 'pemasukan') + + Pemasukan + + @else + + Pengeluaran + + @endif + | +{{ 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') }} | +{{ $transaksi->keterangan ?? '-' }} | +{{ 'Rp ' . number_format($transaksi->saldo_sebelum, 0, ',', '.') }} | +{{ 'Rp ' . number_format($transaksi->saldo_sesudah, 0, ',', '.') }} | ++ + Detail + + | +
Riwayat uang saku Anda masih kosong.
+Detail lengkap transaksi uang saku
+| ID Transaksi | +{{ $transaksi->id_uang_saku }} | +
|---|---|
| Tanggal Transaksi | +{{ \Carbon\Carbon::parse($transaksi->tanggal_transaksi)->isoFormat('dddd, D MMMM YYYY') }} | +
| Jenis Transaksi | ++ @if($transaksi->jenis_transaksi === 'pemasukan') + + Pemasukan + + @else + + Pengeluaran + + @endif + | +
| Nominal | ++ + {{ 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') }} + + | +
| Saldo Sebelum | +{{ 'Rp ' . number_format($transaksi->saldo_sebelum, 0, ',', '.') }} | +
| Saldo Sesudah | ++ + {{ 'Rp ' . number_format($transaksi->saldo_sesudah, 0, ',', '.') }} + + | +
| Keterangan | +{{ $transaksi->keterangan ?? '-' }} | +
| Dicatat Pada | +{{ $transaksi->created_at->format('d/m/Y H:i:s') }} | +
| ID Santri | +{{ $transaksi->santri->id_santri }} | +
|---|---|
| Nama Lengkap | +{{ $transaksi->santri->nama_lengkap }} | +
| Kelas | +{{ $transaksi->santri->kelas }} | +
+ {{ $transaksi->jenis_transaksi === 'pemasukan' ? 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') : 'Rp 0' }} +
++ {{ $transaksi->jenis_transaksi === 'pengeluaran' ? 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') : 'Rp 0' }} +
++ {{ $transaksi->jenis_transaksi === 'pemasukan' ? 'Bertambah' : 'Berkurang' }} + {{ 'Rp ' . number_format(abs($transaksi->saldo_sesudah - $transaksi->saldo_sebelum), 0, ',', '.') }} +
++ Laravel has wonderful documentation covering every aspect of the framework. Whether you are a newcomer or have prior experience with Laravel, we recommend reading our documentation from beginning to end. +
++ Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process. +
++ Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials. +
+Semua Riwayat
+ +format('F Y')); ?>
+ +Akumulasi Poin
+ +| No | +ID Riwayat | +Tanggal | +Santri | +Kategori Pelanggaran | +Poin | +Aksi | +
|---|---|---|---|---|---|---|
| firstItem() + $index); ?> | ++ id_riwayat); ?> + | ++ + tanggal)->format('d M Y')); ?> + + | +
+ santri): ?>
+ santri->nama_lengkap); ?> + + id_santri); ?> + + + + Santri tidak ditemukan + + |
+
+ kategori): ?>
+ kategori->nama_pelanggaran); ?> + + id_kategori); ?> + + + + Kategori tidak ditemukan + + |
+ + + poin); ?> + + + | ++ + | +
Silakan tambah riwayat pelanggaran baru menggunakan tombol di atas.
+ + Tambah Riwayat + +| No | +ID | +Hari | +Waktu | +Nama Kegiatan | +Kategori | +Materi | +Aksi | +
|---|---|---|---|---|---|---|---|
| firstItem() + $index); ?> | +kegiatan_id); ?> | +hari); ?> | +waktu_mulai))); ?> - waktu_selesai))); ?> | +nama_kegiatan); ?> | +kategori->nama_kategori); ?> | +materi, 40) ?? '-'); ?> | ++ + + + + + + + | +
Monitoring Santri Berbasis Web
+ +| Nama Materi | +materi->nama_kitab); ?> | +
|---|---|
| Kategori | ++ + materi->kategori); ?> + + + | +
| Total Halaman | +materi->total_halaman); ?> halaman (Hal. materi->halaman_mulai); ?> - materi->halaman_akhir); ?>) | +
| Persentase Selesai | +
+
+
+
+ persentase, 2)); ?>%
+
+
+ |
+
|---|---|
| Jumlah Halaman Selesai | ++ + pages_array)); ?> + + dari materi->total_halaman); ?> halaman + | +
| Detail Halaman Selesai | +
+
+ halaman_selesai); ?>
+
+
+ |
+
| Tanggal Input | +tanggal_input)->format('d F Y')); ?> | +
| Semester | ++ + semester->nama_semester); ?> - semester->tahun_ajaran); ?> + + + | +
| Catatan | +
+
+
+ catatan); ?>
+
+
+ |
+
Terus semangat untuk materi berikutnya!
+Tinggal sedikit lagi, pertahankan semangatmu!
+Terus berjuang, jangan menyerah!
+Setiap perjalanan dimulai dari langkah pertama. Semangat!
+| No | +ID Riwayat | +Tanggal | +Jenis Pelanggaran | +Poin | +Keterangan | +Aksi | +
|---|---|---|---|---|---|---|
| firstItem() + $index); ?> | +id_riwayat); ?> | ++ + tanggal)->isoFormat('D MMM YYYY')); ?> + + | +kategori->nama_pelanggaran ?? '-'); ?> | ++ + poin); ?> + + + | +
+
+ keterangan ?: '-'); ?>
+
+
+ |
+ + + Detail + + | +
Selamat! Anda belum memiliki catatan pelanggaran.
+| No | +ID Transaksi | +Santri | +Tanggal | +Jenis | +Nominal | +Keterangan | +Saldo | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| firstItem() + $index); ?> | +id_uang_saku); ?> | ++ + santri->nama_lengkap); ?> + + + | +tanggal_transaksi->format('d/m/Y')); ?> | ++ jenis_transaksi === 'pemasukan'): ?> + + Pemasukan + + + + Pengeluaran + + + | ++ nominal_format); ?> + + | +
+
+ keterangan ?? '-'); ?>
+
+
+ |
+ + + saldo_sesudah_format); ?> + + + | ++ + | +
Belum ada transaksi uang saku yang tercatat. Tambahkan transaksi pertama!
+ + Tambah Transaksi + +Detail lengkap transaksi uang saku
+| ID Transaksi | +id_uang_saku); ?> | +
|---|---|
| Tanggal Transaksi | +tanggal_transaksi)->isoFormat('dddd, D MMMM YYYY')); ?> | +
| Jenis Transaksi | ++ jenis_transaksi === 'pemasukan'): ?> + + Pemasukan + + + + Pengeluaran + + + | +
| Nominal | ++ + nominal, 0, ',', '.')); ?> + + + | +
| Saldo Sebelum | +saldo_sebelum, 0, ',', '.')); ?> | +
| Saldo Sesudah | ++ + saldo_sesudah, 0, ',', '.')); ?> + + + | +
| Keterangan | +keterangan ?? '-'); ?> | +
| Dicatat Pada | +created_at->format('d/m/Y H:i:s')); ?> | +
| ID Santri | +santri->id_santri); ?> | +
|---|---|
| Nama Lengkap | +santri->nama_lengkap); ?> | +
| Kelas | +santri->kelas); ?> | +
+ jenis_transaksi === 'pemasukan' ? 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') : 'Rp 0'); ?> + +
++ jenis_transaksi === 'pengeluaran' ? 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') : 'Rp 0'); ?> + +
++ jenis_transaksi === 'pemasukan' ? 'Bertambah' : 'Berkurang'); ?> + saldo_sesudah - $transaksi->saldo_sebelum), 0, ',', '.')); ?> +
+| ID Riwayat | ++ + id_riwayat); ?> + + + | +
|---|---|
| Tanggal Pelanggaran | ++ + tanggal)->isoFormat('dddd, D MMMM YYYY')); ?> + + + | +
| Poin Pelanggaran | ++ + poin); ?> Poin + + | +
| Tanggal Dicatat | +created_at->format('d F Y, H:i')); ?> WIB | +
| Terakhir Diperbarui | +updated_at->format('d F Y, H:i')); ?> WIB | +
| ID Santri | ++ santri->id_santri); ?> + | +
|---|---|
| Nama Lengkap | ++ santri->nama_lengkap); ?> + | +
| Kelas | +santri->kelas); ?> | +
| NIS | +santri->nis); ?> | +
| Total Poin Pelanggaran | ++ + santri->total_poin_pelanggaran); ?> Poin + + | +
Data santri tidak ditemukan
+| ID Kategori | ++ kategori->id_kategori); ?> + | +
|---|---|
| Nama Pelanggaran | ++ kategori->nama_pelanggaran); ?> + | +
| Poin Kategori | ++ + kategori->poin); ?> Poin + + | +
Kategori tidak ditemukan
+keterangan); ?>
+| No | +Tanggal | +Kategori | +Poin | +Aksi | +
|---|---|---|---|---|
| + | tanggal)->format('d M Y')); ?> | ++ kategori): ?> + kategori->nama_pelanggaran); ?> + + - + + | ++ poin); ?> + | ++ + + + | +
Poin Pelanggaran Ini
+Total Poin Santri
++ Riwayat ini dicatat pada created_at->format('d F Y')); ?> + untuk pelanggaran yang terjadi pada tanggal)->format('d F Y')); ?>. +
++ ID: id_kepulangan); ?> +
+| ID Kepulangan | +id_kepulangan); ?> | +
|---|---|
| Tanggal Pengajuan | +tanggal_izin_formatted); ?> | +
| Tanggal Pulang | +tanggal_pulang_formatted); ?> | +
| Tanggal Kembali | +tanggal_kembali_formatted); ?> | +
| Durasi Izin | ++ durasi_izin); ?> hari + | +
| Alasan Kepulangan | +alasan); ?> | +
| Status | ++ + status); ?> + + + | +
| Disetujui Oleh | +approved_by); ?> | +
| Tanggal Persetujuan | +approved_at_formatted); ?> | +
| Catatan Admin | +catatan); ?> | +
Total Hari Pulang
++ hari +
+Sisa Kuota
++ hari +
+Kuota Maksimal
++ 12 hari +
+| No | +ID Transaksi | +Tanggal | +Jenis | +Nominal | +Keterangan | +Saldo Sebelum | +Saldo Sesudah | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| firstItem() + $index); ?> | +id_uang_saku); ?> | +tanggal_transaksi)->format('d/m/Y')); ?> | ++ jenis_transaksi === 'pemasukan'): ?> + + Pemasukan + + + + Pengeluaran + + + | +nominal, 0, ',', '.')); ?> | +keterangan ?? '-'); ?> | +saldo_sebelum, 0, ',', '.')); ?> | +saldo_sesudah, 0, ',', '.')); ?> | ++ + Detail + + | +
Riwayat uang saku Anda masih kosong.
++ + Berita ini ditujukan untuk santri dari kelas: + target_kelas ?? [])); ?> +
+Belum ada santri yang dipilih untuk berita ini.
++ Dipublikasikan pada created_at->format('d F Y, H:i')); ?> WIB +
+| No | +ID Kategori | +Nama Kategori | +Keterangan | +Dibuat | +Aksi | +
|---|---|---|---|---|---|
| firstItem() + $index); ?> | +kategori_id); ?> | +nama_kategori); ?> | +keterangan, 50) ?? '-'); ?> | +created_at->format('d M Y')); ?> | ++ + + + + + + + | +
+ Maaf, terjadi kesalahan pada server. Tim kami sudah diberitahu dan sedang menangani masalah ini. +
+ + + + + + + Kembali + + + + Dashboard + ++ Riwayat kunjungan UKP nama_lengkap); ?> +
++ Periode yang dipilih +
++ Perlu perhatian +
+ ++ Tidak ada yang dirawat +
+ ++ Alhamdulillah +
++ Izin pulang +
++ + Menampilkan data periode: + + locale('id')->isoFormat('D MMMM Y')); ?> - locale('id')->isoFormat('D MMMM Y')); ?> + + + (diffInDays($tanggalSampai) + 1); ?> hari) +
+Tidak ada riwayat kesehatan pada periode yang dipilih.
+ + Lihat Semua Data + ++ id_kesehatan); ?> + +
+| ID Riwayat | +id_riwayat); ?> | +
|---|---|
| Tanggal Kejadian | +tanggal)->isoFormat('dddd, D MMMM YYYY')); ?> | +
| Jenis Pelanggaran | +kategori->nama_pelanggaran ?? '-'); ?> | +
| Poin Pelanggaran | ++ + poin); ?> Poin + + | +
| Keterangan | +keterangan ?: '-'); ?> | +
| Dicatat Pada | +created_at->isoFormat('D MMMM YYYY, HH:mm')); ?> WIB | +
+ + Catatan: Data pelanggaran ini dicatat oleh admin/pengurus pondok. + Jika ada kesalahan data, silakan hubungi bagian administrasi. +
+| ID | +Santri | +Tanggal Pulang | +Tanggal Kembali | +Durasi | +Alasan | +Status | +Aksi | +
|---|---|---|---|---|---|---|---|
| + id_kepulangan); ?> + id_santri])): ?> + + + + + | +
+
+ santri->nama_lengkap ?? 'N/A'); ?>
+ + + santri->id_santri ?? ''); ?> | santri->kelas ?? ''); ?> + + + |
+ tanggal_pulang_formatted); ?> | +tanggal_kembali_formatted); ?> | ++ + durasi_izin_calculated); ?> hari + + | ++ + | +
+
+ status); ?>
+
+
+ is_aktif): ?>
+ Sedang Izin + is_terlambat): ?> + Terlambat + + |
+ + + | +
|
+
+ Tidak ada data kepulangan ditemukan + |
+ |||||||
| No | +ID Kategori | +Nama Pelanggaran | +Poin | +Digunakan | +Aksi | +
|---|---|---|---|---|---|
| + | + id_kategori); ?> + | ++ nama_pelanggaran); ?> + | ++ + poin); ?> + + + | ++ + riwayatPelanggaran->count()); ?>x + + | ++ + | +
Mulai dengan menambahkan kategori pelanggaran baru menggunakan form di atas.
+| ID Santri | +Nama | +Username | +Aksi | +
|---|---|---|---|
| role_id); ?> | +name); ?> | +username); ?> | ++ Reset Password + Hapus Akun + | +
| Belum ada akun Santri yang terdaftar. | +|||
Berikut adalah data santri yang sudah terdaftar di Data Santri namun belum memiliki akun login. Mereka dapat dipilih saat Anda membuat akun baru.
+ ++ Informasi terbaru untuk kelas); ?> +
+Belum ada berita atau pengumuman yang dipublikasikan untuk Anda.
+| No | +ID Santri | +NIS | +Nama Lengkap | +Jenis Kelamin | +Kelas | +Status | +Aksi | +
|---|---|---|---|---|---|---|---|
| + | id_santri); ?> | +nis ?? '-'); ?> | +nama_lengkap); ?> | +jenis_kelamin); ?> | +kelas); ?> | ++ status == 'Aktif'): ?> + + status); ?> + + + status == 'Lulus'): ?> + + status); ?> + + + + + status); ?> + + + + | ++ + + + + + | +
|
+
+
+ Data tidak ditemukan. + Coba ubah kata kunci pencarian atau filter yang digunakan. + + Belum ada data santri. + Klik tombol "Tambah Santri" untuk menambahkan data baru. + + |
+ |||||||
+ + Menampilkan count()); ?> data santri + + dari hasil pencarian/filter + +
+| No | +Hari | +Waktu | +Nama Kegiatan | +Kategori | +Aksi | +
|---|---|---|---|---|---|
| firstItem() + $index); ?> | +hari); ?> | +waktu_mulai))); ?> - waktu_selesai))); ?> | +nama_kegiatan); ?> | +kategori->nama_kategori); ?> | ++ + Input + + + Rekap + + | +
| No | +ID Santri | +Nama Santri | +Tgl Masuk | +Keluhan | +Tgl Keluar | +Status | +Lama | +Aksi | +
|---|---|---|---|---|---|---|---|---|
| firstItem() + $index); ?> | +id_santri); ?> | +
+ santri->nama_lengkap); ?> + santri->kelas); ?> + |
+ tanggal_masuk_formatted); ?> | ++ + keluhan, 50)); ?> + + + | ++ tanggal_keluar): ?> + tanggal_keluar_formatted); ?> + + + Belum keluar + + | ++ + status)); ?> + + + | +lama_dirawat); ?> hari | ++ + | +
Belum ada data kesehatan santri yang tercatat atau sesuai dengan filter yang dipilih.
+ + Tambah Data Kesehatan + +Mohon gunakan email dan password yang kuat untuk keamanan sistem.
++ Selamat datang, - Kelas + +
++ created_at->diffForHumans()); ?> + +
++ 💡 Jaga Kedisiplinan: Hindari pelanggaran dengan mematuhi tata tertib pondok. + Lihat Daftar Kategori Pelanggaran + untuk mengetahui peraturan yang berlaku. +
++ Riwayat izin pulang nama_lengkap); ?> +
++ Tahun + +
++ Izin diterima +
++ Melebihi batas! +
+ ++ Dari kuota 12 hari +
+ ++ menunggu +
+ ++ Kuota tersisa +
+ +Anda belum pernah mengajukan izin kepulangan pada periode yang dipilih.
++ id_kepulangan); ?> + +
++ id_santri); ?> + + nis): ?> + | NIS: nis); ?> + + +
+| ID Santri | +id_santri); ?> | +
|---|---|
| NIS | +nis); ?> | +
| Nama Lengkap | +nama_lengkap); ?> | +
| Jenis Kelamin | +jenis_kelamin); ?> | +
| Kelas | ++ + + kelas_lengkap); ?> + + + | +
| Status | +status_badge; ?> | +
| Terdaftar Sejak | +created_at->format('d F Y')); ?> | +
| Alamat Lengkap | +alamat_santri ?? '-'); ?> | +
|---|---|
| Daerah Asal | +daerah_asal ?? '-'); ?> | +
| Nama Orang Tua/Wali | +nama_orang_tua ?? '-'); ?> | +
| Nomor HP Orang Tua | ++ nomor_hp_ortu): ?> + + nomor_hp_ortu); ?> + + + + - + + | +
+ + Kartu RFID Anda sudah terdaftar +
+
+ UID: rfid_uid); ?>
+
+ + Catatan: Jika ada data yang perlu diperbarui selain alamat dan nomor HP orang tua, silakan hubungi admin atau pengurus pesantren. +
+Akses progres dan laporan santri.
++ Pantau progres hafalan dan pembelajaran Anda +
++ % dari total materi +
+ ++ NIS: nis ?? '-'); ?> + +
+| No | +Materi | +Kategori | +Halaman Selesai | +Progress | +Tanggal Input | +Aksi | +
|---|---|---|---|---|---|---|
| + | + materi->nama_kitab); ?> + | ++ + materi->kategori); ?> + + + | ++ + pages_array)); ?> dari materi->total_halaman); ?> halaman + + | +
+
+
+
+ persentase, 1)); ?>%
+
+
+ |
+ tanggal_input)->format('d M Y')); ?> | ++ + Detail + + | +
+ + Tidak ada data capaian untuk semester yang dipilih. + + Belum ada data capaian yang tercatat untuk Anda. + +
+Selamat datang di Sistem Informasi Monitoring Santri.
+Area untuk menempatkan statistik dan grafik sistem.
+Sistem Informasi Monitoring Santri
++ + Berikut adalah daftar kategori pelanggaran beserta poin yang berlaku di pondok. +
+ + Kembali ke Riwayat + +| No | +Kode | +Jenis Pelanggaran | +Poin | +
|---|---|---|---|
| + | id_kategori); ?> | +nama_pelanggaran); ?> | ++ poin <= 5): ?> + + poin); ?> Poin + + poin <= 15): ?> + + poin); ?> Poin + + + + poin); ?> Poin + + + | +
Daftar kategori pelanggaran belum tersedia.
+| ID | +Judul & Konten | +Penulis | +Tanggal | +Status | +Target | +Aksi | +
|---|---|---|---|---|---|---|
| id_berita); ?> | +
+
+ judul); ?>
+
+ + konten), 80)); ?> + |
+ penulis); ?> | +tanggal_formatted); ?> | ++ + status === 'published'): ?> + Published + + Draft + + + | ++ target_berita) { + 'semua' => 'badge-primary', + 'kelas_tertentu' => 'badge-info', + 'santri_tertentu' => 'badge-warning', + default => 'badge-secondary' + }; + ?> + + target_audience); ?> + + + | ++ + | +
+ Mulai tambahkan berita pertama untuk santri pesantren. +
+ + Tambah Berita Pertama + +