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 @@ +

Laravel Logo

+ +

+Build Status +Total Downloads +Latest Stable Version +License +

+ +## 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 array> + */ + protected $middlewareGroups = [ + 'web' => [ + \App\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \App\Http\Middleware\VerifyCsrfToken::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, + \App\Http\Middleware\ClearStuckSession::class, + ], + + 'api' => [ + // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + ]; + + /** + * The application's middleware aliases. + * + * Aliases may be used instead of class names to conveniently assign middleware to routes and groups. + * + * @var array + */ + protected $middlewareAliases = [ + 'auth' => \App\Http\Middleware\Authenticate::class, + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, + 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, + 'can' => \Illuminate\Auth\Middleware\Authorize::class, + 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, + 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, + 'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, + 'signed' => \App\Http\Middleware\ValidateSignature::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'role' => \App\Http\Middleware\Role::class, + ]; +} diff --git a/sim-pkpps/app/Http/Middleware/Authenticate.php b/sim-pkpps/app/Http/Middleware/Authenticate.php new file mode 100644 index 0000000..d4ef644 --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/Authenticate.php @@ -0,0 +1,17 @@ +expectsJson() ? null : route('login'); + } +} diff --git a/sim-pkpps/app/Http/Middleware/ClearStuckSession.php b/sim-pkpps/app/Http/Middleware/ClearStuckSession.php new file mode 100644 index 0000000..9214e66 --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/ClearStuckSession.php @@ -0,0 +1,31 @@ +session()->has('_token')) { + $lastActivity = $request->session()->get('last_activity', 0); + $now = time(); + + // Jika session idle lebih dari 5 menit, flush + if (($now - $lastActivity) > 300) { + $request->session()->flush(); + $request->session()->regenerate(); + } + } + + // Update last activity timestamp + $request->session()->put('last_activity', time()); + + return $next($request); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Middleware/EncryptCookies.php b/sim-pkpps/app/Http/Middleware/EncryptCookies.php new file mode 100644 index 0000000..867695b --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/EncryptCookies.php @@ -0,0 +1,17 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/sim-pkpps/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/sim-pkpps/app/Http/Middleware/PreventRequestsDuringMaintenance.php new file mode 100644 index 0000000..74cbd9a --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -0,0 +1,17 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/sim-pkpps/app/Http/Middleware/RedirectIfAuthenticated.php b/sim-pkpps/app/Http/Middleware/RedirectIfAuthenticated.php new file mode 100644 index 0000000..afc78c4 --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/RedirectIfAuthenticated.php @@ -0,0 +1,30 @@ +check()) { + return redirect(RouteServiceProvider::HOME); + } + } + + return $next($request); + } +} diff --git a/sim-pkpps/app/Http/Middleware/Role.php b/sim-pkpps/app/Http/Middleware/Role.php new file mode 100644 index 0000000..5b2680a --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/Role.php @@ -0,0 +1,53 @@ +session()->flush(); + $request->session()->regenerate(); + + return redirect('/admin/login'); + } + + // Ambil role pengguna saat ini + $currentRole = Auth::user()->role; + + // Pisahkan daftar role yang diizinkan + $allowedRoles = explode(',', $roles); + + // 2. Cek apakah role pengguna termasuk dalam daftar yang diizinkan + if (!in_array($currentRole, $allowedRoles)) { + // ✅ TAMBAHAN: Redirect ke dashboard yang sesuai, jangan abort + if ($currentRole === 'admin') { + return redirect()->route('admin.dashboard') + ->with('error', 'Anda tidak memiliki akses ke halaman tersebut.'); + } + + if ($currentRole === 'santri' || $currentRole === 'wali') { + return redirect()->route('santri.dashboard') + ->with('error', 'Anda tidak memiliki akses ke halaman tersebut.'); + } + + // Jika role tidak dikenali, logout paksa + Auth::logout(); + $request->session()->invalidate(); + $request->session()->regenerate(); + + return redirect('/admin/login') + ->with('error', 'Role tidak valid. Silakan login kembali.'); + } + + return $next($request); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Middleware/TrimStrings.php b/sim-pkpps/app/Http/Middleware/TrimStrings.php new file mode 100644 index 0000000..88cadca --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/TrimStrings.php @@ -0,0 +1,19 @@ + + */ + protected $except = [ + 'current_password', + 'password', + 'password_confirmation', + ]; +} diff --git a/sim-pkpps/app/Http/Middleware/TrustHosts.php b/sim-pkpps/app/Http/Middleware/TrustHosts.php new file mode 100644 index 0000000..c9c58bd --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/TrustHosts.php @@ -0,0 +1,20 @@ + + */ + public function hosts(): array + { + return [ + $this->allSubdomainsOfApplicationUrl(), + ]; + } +} diff --git a/sim-pkpps/app/Http/Middleware/TrustProxies.php b/sim-pkpps/app/Http/Middleware/TrustProxies.php new file mode 100644 index 0000000..3391630 --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/TrustProxies.php @@ -0,0 +1,28 @@ +|string|null + */ + protected $proxies; + + /** + * The headers that should be used to detect proxies. + * + * @var int + */ + protected $headers = + Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB; +} diff --git a/sim-pkpps/app/Http/Middleware/ValidateSignature.php b/sim-pkpps/app/Http/Middleware/ValidateSignature.php new file mode 100644 index 0000000..093bf64 --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/ValidateSignature.php @@ -0,0 +1,22 @@ + + */ + protected $except = [ + // 'fbclid', + // 'utm_campaign', + // 'utm_content', + // 'utm_medium', + // 'utm_source', + // 'utm_term', + ]; +} diff --git a/sim-pkpps/app/Http/Middleware/VerifyCsrfToken.php b/sim-pkpps/app/Http/Middleware/VerifyCsrfToken.php new file mode 100644 index 0000000..476da31 --- /dev/null +++ b/sim-pkpps/app/Http/Middleware/VerifyCsrfToken.php @@ -0,0 +1,35 @@ + + */ + protected $except = [ + // JANGAN tambahkan apa-apa disini, biarkan kosong + ]; + + /** + * Add CSRF token to response headers + */ + protected function addCookieToResponse($request, $response) + { + $config = config('session'); + + if ($config['driver'] === 'array') { + return $response; + } + + $response->headers->setCookie( + $this->newCookie($request, $config) + ); + + return $response; + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Http/Requests/Auth/LoginRequest.php b/sim-pkpps/app/Http/Requests/Auth/LoginRequest.php new file mode 100644 index 0000000..2b92f65 --- /dev/null +++ b/sim-pkpps/app/Http/Requests/Auth/LoginRequest.php @@ -0,0 +1,85 @@ + + */ + public function rules(): array + { + return [ + 'email' => ['required', 'string', 'email'], + 'password' => ['required', 'string'], + ]; + } + + /** + * Attempt to authenticate the request's credentials. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function authenticate(): void + { + $this->ensureIsNotRateLimited(); + + if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { + RateLimiter::hit($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.failed'), + ]); + } + + RateLimiter::clear($this->throttleKey()); + } + + /** + * Ensure the login request is not rate limited. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function ensureIsNotRateLimited(): void + { + if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + return; + } + + event(new Lockout($this)); + + $seconds = RateLimiter::availableIn($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.throttle', [ + 'seconds' => $seconds, + 'minutes' => ceil($seconds / 60), + ]), + ]); + } + + /** + * Get the rate limiting throttle key for the request. + */ + public function throttleKey(): string + { + return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip()); + } +} diff --git a/sim-pkpps/app/Http/Requests/ProfileUpdateRequest.php b/sim-pkpps/app/Http/Requests/ProfileUpdateRequest.php new file mode 100644 index 0000000..93b0022 --- /dev/null +++ b/sim-pkpps/app/Http/Requests/ProfileUpdateRequest.php @@ -0,0 +1,23 @@ + + */ + public function rules(): array + { + return [ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)], + ]; + } +} diff --git a/sim-pkpps/app/Models/AbsensiKegiatan.php b/sim-pkpps/app/Models/AbsensiKegiatan.php new file mode 100644 index 0000000..b22e7cb --- /dev/null +++ b/sim-pkpps/app/Models/AbsensiKegiatan.php @@ -0,0 +1,91 @@ + 'date', + 'waktu_absen' => 'datetime:H:i', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Auto-generate absensi_id (A001, A002...) + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->absensi_id)) { + $last = self::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->absensi_id, 1)) + 1 : 1; + $model->absensi_id = 'A' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi ke Santri + */ + public function santri() + { + return $this->belongsTo(Santri::class, 'id_santri', 'id_santri'); + } + + /** + * Relasi ke Kegiatan + */ + public function kegiatan() + { + return $this->belongsTo(Kegiatan::class, 'kegiatan_id', 'kegiatan_id'); + } + + /** + * Scope: Filter berdasarkan tanggal + */ + public function scopeTanggal($query, $tanggal) + { + return $query->whereDate('tanggal', $tanggal); + } + + /** + * Scope: Filter berdasarkan kegiatan + */ + public function scopeKegiatan($query, $kegiatan_id) + { + return $query->where('kegiatan_id', $kegiatan_id); + } + + /** + * Accessor: Status Badge + */ + public function getStatusBadgeAttribute() + { + $badges = [ + 'Hadir' => ' Hadir', + 'Izin' => ' Izin', + 'Sakit' => ' Sakit', + 'Alpa' => ' Alpa', + ]; + + return $badges[$this->status] ?? $this->status; + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/Berita.php b/sim-pkpps/app/Models/Berita.php new file mode 100644 index 0000000..8e01021 --- /dev/null +++ b/sim-pkpps/app/Models/Berita.php @@ -0,0 +1,113 @@ + 'array', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Auto-generate ID Berita (B001, B002, ...) + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_berita)) { + $last = Berita::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_berita, 1)) + 1 : 1; + $model->id_berita = 'B' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi Many-to-Many dengan Santri + */ + public function santriTertentu() + { + return $this->belongsToMany(Santri::class, 'berita_santri', 'id_berita', 'id_santri', 'id_berita', 'id_santri') + ->withPivot('sudah_dibaca', 'tanggal_baca') + ->withTimestamps(); + } + + /** + * Accessor: Tanggal Formatted + */ + public function getTanggalFormattedAttribute() + { + return $this->created_at->format('d M Y'); + } + + /** + * Accessor: Status Badge + */ + public function getStatusBadgeAttribute() + { + return $this->status === 'published' ? 'badge-success' : 'badge-warning'; + } + + /** + * Accessor: Target Audience (untuk display) + */ + public function getTargetAudienceAttribute() + { + return match($this->target_berita) { + 'semua' => 'Semua Santri', + 'kelas_tertentu' => 'Kelas: ' . implode(', ', $this->target_kelas ?? []), + 'santri_tertentu' => $this->santriTertentu->count() . ' Santri', + default => '-' + }; + } + + /** + * Scope: Filter by status + */ + public function scopeStatus($query, $status) + { + return $query->where('status', $status); + } + + /** + * Scope: Filter by target + */ + public function scopeTarget($query, $target) + { + return $query->where('target_berita', $target); + } + + /** + * Scope: Search berita + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('judul', 'like', "%{$search}%") + ->orWhere('penulis', 'like', "%{$search}%") + ->orWhere('id_berita', 'like', "%{$search}%"); + }); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/Capaian.php b/sim-pkpps/app/Models/Capaian.php new file mode 100644 index 0000000..234314c --- /dev/null +++ b/sim-pkpps/app/Models/Capaian.php @@ -0,0 +1,207 @@ + 'decimal:2', + 'tanggal_input' => 'date', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Generator ID Kustom (CP001, CP002, ...) + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_capaian)) { + $last = Capaian::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_capaian, 2)) + 1 : 1; + $model->id_capaian = 'CP' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + + // Auto-calculate persentase + $model->persentase = self::calculatePersentase($model->halaman_selesai, $model->id_materi); + }); + + static::updating(function ($model) { + // Recalculate persentase saat update + $model->persentase = self::calculatePersentase($model->halaman_selesai, $model->id_materi); + }); + } + + /** + * Relasi: Capaian belongs to Santri + */ + public function santri() + { + return $this->belongsTo(Santri::class, 'id_santri', 'id_santri'); + } + + /** + * Relasi: Capaian belongs to Materi + */ + public function materi() + { + return $this->belongsTo(Materi::class, 'id_materi', 'id_materi'); + } + + /** + * Relasi: Capaian belongs to Semester + */ + public function semester() + { + return $this->belongsTo(Semester::class, 'id_semester', 'id_semester'); + } + + /** + * Parse halaman_selesai string menjadi array halaman + * Input: "1-10,16-21,40" + * Output: [1,2,3,...,10,16,17,...,21,40] + */ + public static function parseHalamanSelesai($rangeString) + { + $pages = []; + $ranges = explode(',', $rangeString); + + foreach ($ranges as $range) { + $range = trim($range); + + if (strpos($range, '-') !== false) { + // Range format: "1-10" + list($start, $end) = explode('-', $range); + $start = intval(trim($start)); + $end = intval(trim($end)); + + for ($i = $start; $i <= $end; $i++) { + $pages[] = $i; + } + } else { + // Single page: "40" + $pages[] = intval($range); + } + } + + return array_unique($pages); + } + + /** + * Calculate persentase dari halaman_selesai + */ + public static function calculatePersentase($halamanSelesai, $idMateri) + { + if (empty($halamanSelesai)) { + return 0; + } + + $materi = Materi::where('id_materi', $idMateri)->first(); + if (!$materi || $materi->total_halaman == 0) { + return 0; + } + + $pages = self::parseHalamanSelesai($halamanSelesai); + $jumlahHalamanSelesai = count($pages); + + $persentase = ($jumlahHalamanSelesai / $materi->total_halaman) * 100; + + // Batasi max 100% + return min($persentase, 100); + } + + /** + * Get array halaman yang sudah selesai + */ + public function getPagesArrayAttribute() + { + return self::parseHalamanSelesai($this->halaman_selesai); + } + + /** + * Get jumlah halaman yang sudah selesai + */ + public function getJumlahHalamanSelesaiAttribute() + { + return count($this->pages_array); + } + + /** + * Accessor: Badge persentase dengan warna + */ + public function getPersentaseBadgeAttribute() + { + $persentase = $this->persentase; + + if ($persentase >= 100) { + $class = 'badge-success'; + $icon = 'fa-check-circle'; + } elseif ($persentase >= 75) { + $class = 'badge-primary'; + $icon = 'fa-battery-three-quarters'; + } elseif ($persentase >= 50) { + $class = 'badge-warning'; + $icon = 'fa-battery-half'; + } elseif ($persentase >= 25) { + $class = 'badge-danger'; + $icon = 'fa-battery-quarter'; + } else { + $class = 'badge-secondary'; + $icon = 'fa-battery-empty'; + } + + return sprintf( + ' %.2f%%', + $class, + $icon, + $persentase + ); + } + + /** + * Scope: Filter by santri + */ + public function scopeBySantri($query, $idSantri) + { + return $query->where('id_santri', $idSantri); + } + + /** + * Scope: Filter by semester + */ + public function scopeBySemester($query, $idSemester) + { + return $query->where('id_semester', $idSemester); + } + + /** + * Scope: Filter by kategori materi + */ + public function scopeByKategori($query, $kategori) + { + return $query->whereHas('materi', function($q) use ($kategori) { + $q->where('kategori', $kategori); + }); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/KategoriKegiatan.php b/sim-pkpps/app/Models/KategoriKegiatan.php new file mode 100644 index 0000000..b32ddff --- /dev/null +++ b/sim-pkpps/app/Models/KategoriKegiatan.php @@ -0,0 +1,66 @@ + 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Auto-generate kategori_id (KT001, KT002...) + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->kategori_id)) { + $last = self::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->kategori_id, 2)) + 1 : 1; + $model->kategori_id = 'KT' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi ke Kegiatan (One to Many) + */ + public function kegiatans() + { + return $this->hasMany(Kegiatan::class, 'kategori_id', 'kategori_id'); + } + + /** + * Accessor: Total Kegiatan + */ + public function getTotalKegiatanAttribute() + { + return $this->kegiatans()->count(); + } + + /** + * Scope: Search + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('nama_kategori', 'like', "%{$search}%") + ->orWhere('kategori_id', 'like', "%{$search}%") + ->orWhere('keterangan', 'like', "%{$search}%"); + }); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/KategoriPelanggaran.php b/sim-pkpps/app/Models/KategoriPelanggaran.php new file mode 100644 index 0000000..d79124b --- /dev/null +++ b/sim-pkpps/app/Models/KategoriPelanggaran.php @@ -0,0 +1,116 @@ + 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Generator ID Kustom (KP001, KP002, ...) + * Metode ini akan dijalankan setiap kali model baru dibuat (insert). + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + // Pastikan ID kustom belum terisi + if (empty($model->id_kategori)) { + // Ambil data kategori terakhir berdasarkan ID default + $last = KategoriPelanggaran::orderBy('id', 'desc')->first(); + + // Tentukan nomor urut berikutnya + // Jika ada data terakhir, ambil angka dari ID kustom (misal KP001 -> 1) dan tambahkan 1 + $num = $last ? intval(substr($last->id_kategori, 2)) + 1 : 1; + + // Format ID: 'KP' + nomor urut 3 digit (dengan padding 0) + $model->id_kategori = 'KP' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi: Kategori memiliki banyak riwayat pelanggaran (hasMany). + * Satu kategori bisa digunakan untuk banyak riwayat pelanggaran. + */ + public function riwayatPelanggaran() + { + return $this->hasMany(RiwayatPelanggaran::class, 'id_kategori', 'id_kategori'); + } + + /** + * Accessor: Mendapatkan total penggunaan kategori + */ + public function getTotalPenggunaanAttribute() + { + return $this->riwayatPelanggaran()->count(); + } + + /** + * Accessor: Mendapatkan total poin terkumpul dari kategori ini + */ + public function getTotalPoinTerkumpulAttribute() + { + return $this->riwayatPelanggaran()->sum('poin'); + } + + /** + * Scope: Filter kategori berdasarkan rentang poin + */ + public function scopePoinRendah($query) + { + return $query->where('poin', '<', 10); + } + + public function scopePoinSedang($query) + { + return $query->whereBetween('poin', [10, 20]); + } + + public function scopePoinTinggi($query) + { + return $query->where('poin', '>', 20); + } + + /** + * Scope: Search kategori berdasarkan nama + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('nama_pelanggaran', 'like', "%{$search}%") + ->orWhere('id_kategori', 'like', "%{$search}%"); + }); + } + + /** + * Method: Cek apakah kategori masih digunakan + */ + public function isUsed() + { + return $this->riwayatPelanggaran()->exists(); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/Kegiatan.php b/sim-pkpps/app/Models/Kegiatan.php new file mode 100644 index 0000000..aaa56b6 --- /dev/null +++ b/sim-pkpps/app/Models/Kegiatan.php @@ -0,0 +1,90 @@ + 'datetime:H:i', + 'waktu_selesai' => 'datetime:H:i', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Auto-generate kegiatan_id (KG001, KG002...) + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->kegiatan_id)) { + $last = self::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->kegiatan_id, 2)) + 1 : 1; + $model->kegiatan_id = 'KG' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi ke Kategori + */ + public function kategori() + { + return $this->belongsTo(KategoriKegiatan::class, 'kategori_id', 'kategori_id'); + } + + /** + * Relasi ke Absensi (akan dibuat di tahap selanjutnya) + */ + public function absensis() + { + return $this->hasMany(AbsensiKegiatan::class, 'kegiatan_id', 'kegiatan_id'); + } + + /** + * Scope: Filter berdasarkan hari + */ + public function scopeHari($query, $hari) + { + return $query->where('hari', $hari); + } + + /** + * Scope: Search + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('nama_kegiatan', 'like', "%{$search}%") + ->orWhere('kegiatan_id', 'like', "%{$search}%") + ->orWhere('materi', 'like', "%{$search}%"); + }); + } + + /** + * Accessor: Waktu Lengkap + */ + public function getWaktuLengkapAttribute() + { + return date('H:i', strtotime($this->waktu_mulai)) . ' - ' . + date('H:i', strtotime($this->waktu_selesai)); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/Kepulangan.php b/sim-pkpps/app/Models/Kepulangan.php new file mode 100644 index 0000000..ac197c7 --- /dev/null +++ b/sim-pkpps/app/Models/Kepulangan.php @@ -0,0 +1,247 @@ + 'date', + 'tanggal_pulang' => 'date', + 'tanggal_kembali' => 'date', + 'approved_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Boot method untuk auto-generate ID Kepulangan + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_kepulangan)) { + $last = Kepulangan::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_kepulangan, 2)) + 1 : 1; + $model->id_kepulangan = 'KP' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + + // Auto-calculate durasi_izin + if ($model->tanggal_pulang && $model->tanggal_kembali) { + $pulang = Carbon::parse($model->tanggal_pulang); + $kembali = Carbon::parse($model->tanggal_kembali); + $model->durasi_izin = $pulang->diffInDays($kembali) + 1; + } + + // Set tanggal_izin ke hari ini jika tidak diisi + if (empty($model->tanggal_izin)) { + $model->tanggal_izin = now(); + } + }); + + static::updating(function ($model) { + // Recalculate durasi_izin saat update + if ($model->isDirty(['tanggal_pulang', 'tanggal_kembali'])) { + $pulang = Carbon::parse($model->tanggal_pulang); + $kembali = Carbon::parse($model->tanggal_kembali); + $model->durasi_izin = $pulang->diffInDays($kembali) + 1; + } + }); + } + + /** + * Relasi ke Santri + */ + public function santri() + { + return $this->belongsTo(Santri::class, 'id_santri', 'id_santri'); + } + + /** + * Accessor: Format tanggal izin + */ + public function getTanggalIzinFormattedAttribute() + { + return $this->tanggal_izin ? $this->tanggal_izin->format('d F Y') : '-'; + } + + /** + * Accessor: Format tanggal pulang + */ + public function getTanggalPulangFormattedAttribute() + { + return $this->tanggal_pulang ? $this->tanggal_pulang->format('d F Y') : '-'; + } + + /** + * Accessor: Format tanggal kembali + */ + public function getTanggalKembaliFormattedAttribute() + { + return $this->tanggal_kembali ? $this->tanggal_kembali->format('d F Y') : '-'; + } + + /** + * Accessor: Format approved_at + */ + public function getApprovedAtFormattedAttribute() + { + return $this->approved_at ? $this->approved_at->format('d F Y H:i') : '-'; + } + + /** + * Accessor: Durasi izin calculated + */ + public function getDurasiIzinCalculatedAttribute() + { + if ($this->tanggal_pulang && $this->tanggal_kembali) { + $pulang = Carbon::parse($this->tanggal_pulang); + $kembali = Carbon::parse($this->tanggal_kembali); + return $pulang->diffInDays($kembali) + 1; + } + return $this->durasi_izin ?? 0; + } + + /** + * Accessor: Status badge HTML + */ + public function getStatusBadgeAttribute() + { + $badges = [ + 'Menunggu' => 'badge-warning', + 'Disetujui' => 'badge-success', + 'Ditolak' => 'badge-danger', + 'Selesai' => 'badge-secondary', + ]; + + return $badges[$this->status] ?? 'badge-secondary'; + } + + /** + * Accessor: Apakah sedang dalam periode izin + */ + public function getIsAktifAttribute() + { + $today = Carbon::today(); + return $this->status === 'Disetujui' + && $today->between($this->tanggal_pulang, $this->tanggal_kembali); + } + + /** + * Accessor: Apakah terlambat kembali + */ + public function getIsTerlambatAttribute() + { + if ($this->status !== 'Disetujui') { + return false; + } + return Carbon::today()->greaterThan($this->tanggal_kembali); + } + + /** + * Scope: Filter berdasarkan status + */ + public function scopeStatus($query, $status) + { + return $query->where('status', $status); + } + + /** + * Scope: Filter berdasarkan santri + */ + public function scopeSantri($query, $idSantri) + { + return $query->where('id_santri', $idSantri); + } + + /** + * Scope: Kepulangan yang sedang aktif + */ + public function scopeAktif($query) + { + $today = Carbon::today(); + return $query->where('status', 'Disetujui') + ->whereDate('tanggal_pulang', '<=', $today) + ->whereDate('tanggal_kembali', '>=', $today); + } + + /** + * Scope: Kepulangan yang terlambat + */ + public function scopeTerlambat($query) + { + return $query->where('status', 'Disetujui') + ->whereDate('tanggal_kembali', '<', Carbon::today()); + } + + /** + * Scope: Search kepulangan + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('id_kepulangan', 'like', "%{$search}%") + ->orWhere('alasan', 'like', "%{$search}%") + ->orWhereHas('santri', function($sq) use ($search) { + $sq->where('nama_lengkap', 'like', "%{$search}%") + ->orWhere('id_santri', 'like', "%{$search}%") + ->orWhere('nis', 'like', "%{$search}%"); + }); + }); + } + + /** + * Static method: Get total hari izin per santri per tahun + */ + public static function getTotalHariIzinSantri($idSantri, $tahun = null) + { + $tahun = $tahun ?? Carbon::now()->year; + + return self::where('id_santri', $idSantri) + ->where('status', 'Disetujui') + ->whereYear('tanggal_pulang', $tahun) + ->sum('durasi_izin'); + } + + /** + * Static method: Check apakah santri over limit (lebih dari 12 hari/tahun) + */ + public static function isOverLimit($idSantri, $tahun = null) + { + $totalHari = self::getTotalHariIzinSantri($idSantri, $tahun); + return $totalHari > 12; + } + + /** + * Static method: Get sisa kuota izin santri + */ + public static function getSisaKuota($idSantri, $tahun = null) + { + $totalHari = self::getTotalHariIzinSantri($idSantri, $tahun); + return max(0, 12 - $totalHari); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/KesehatanSantri.php b/sim-pkpps/app/Models/KesehatanSantri.php new file mode 100644 index 0000000..f019432 --- /dev/null +++ b/sim-pkpps/app/Models/KesehatanSantri.php @@ -0,0 +1,157 @@ + 'date', + 'tanggal_keluar' => 'date', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Boot method untuk auto-generate ID Kesehatan + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_kesehatan)) { + // Ambil data terakhir + $last = KesehatanSantri::orderBy('id', 'desc')->first(); + + // Generate nomor urut + $num = $last ? intval(substr($last->id_kesehatan, 1)) + 1 : 1; + + // Format: K001, K002, dst + $model->id_kesehatan = 'K' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi ke Santri + */ + public function santri() + { + return $this->belongsTo(Santri::class, 'id_santri', 'id_santri'); + } + + /** + * Accessor: Format tanggal masuk Indonesia + */ + public function getTanggalMasukFormattedAttribute() + { + return $this->tanggal_masuk ? + Carbon::parse($this->tanggal_masuk)->locale('id')->isoFormat('D MMM Y') : + '-'; + } + + /** + * Accessor: Format tanggal keluar Indonesia + */ + public function getTanggalKeluarFormattedAttribute() + { + return $this->tanggal_keluar ? + Carbon::parse($this->tanggal_keluar)->locale('id')->isoFormat('D MMM Y') : + null; + } + + /** + * Accessor: Hitung lama dirawat + */ + public function getLamaDirawatAttribute() + { + $tanggalMasuk = Carbon::parse($this->tanggal_masuk); + $tanggalKeluar = $this->tanggal_keluar ? + Carbon::parse($this->tanggal_keluar) : + Carbon::now(); + + return $tanggalMasuk->diffInDays($tanggalKeluar); + } + + /** + * Accessor: Warna badge status + */ + public function getStatusBadgeColorAttribute() + { + return [ + 'dirawat' => 'danger', + 'sembuh' => 'success', + 'izin' => 'warning', + ][$this->status] ?? 'secondary'; + } + + /** + * Scope: Filter santri yang sedang dirawat + */ + public function scopeDirawat($query) + { + return $query->where('status', 'dirawat'); + } + + /** + * Scope: Filter santri yang sudah sembuh + */ + public function scopeSembuh($query) + { + return $query->where('status', 'sembuh'); + } + + /** + * Scope: Filter santri yang izin + */ + public function scopeIzin($query) + { + return $query->where('status', 'izin'); + } + + /** + * Scope: Filter berdasarkan bulan dan tahun + */ + public function scopeByMonthYear($query, $month = null, $year = null) + { + if ($month) { + $query->whereMonth('tanggal_masuk', $month); + } + if ($year) { + $query->whereYear('tanggal_masuk', $year); + } + return $query; + } + + /** + * Scope: Search + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('id_kesehatan', 'like', "%{$search}%") + ->orWhere('id_santri', 'like', "%{$search}%") + ->orWhere('keluhan', 'like', "%{$search}%") + ->orWhereHas('santri', function($query) use ($search) { + $query->where('nama_lengkap', 'like', "%{$search}%"); + }); + }); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/Materi.php b/sim-pkpps/app/Models/Materi.php new file mode 100644 index 0000000..8f6ffbf --- /dev/null +++ b/sim-pkpps/app/Models/Materi.php @@ -0,0 +1,138 @@ + 'integer', + 'halaman_akhir' => 'integer', + 'total_halaman' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Generator ID Kustom (M001, M002, ...) + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_materi)) { + $last = Materi::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_materi, 1)) + 1 : 1; + $model->id_materi = 'M' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + + // Auto-calculate total_halaman + if ($model->halaman_mulai && $model->halaman_akhir) { + $model->total_halaman = $model->halaman_akhir - $model->halaman_mulai + 1; + } + }); + + static::updating(function ($model) { + // Auto-calculate total_halaman saat update + if ($model->halaman_mulai && $model->halaman_akhir) { + $model->total_halaman = $model->halaman_akhir - $model->halaman_mulai + 1; + } + }); + } + + /** + * Scope untuk filter berdasarkan kategori + */ + public function scopeKategori($query, $kategori) + { + return $query->where('kategori', $kategori); + } + + /** + * Scope untuk filter berdasarkan kelas + */ + public function scopeKelas($query, $kelas) + { + return $query->where('kelas', $kelas); + } + + /** + * Scope untuk search + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('nama_kitab', 'like', "%{$search}%") + ->orWhere('id_materi', 'like', "%{$search}%") + ->orWhere('deskripsi', 'like', "%{$search}%"); + }); + } + + /** + * Accessor untuk badge kategori + */ + public function getKategoriBadgeAttribute() + { + $badges = [ + 'Al-Qur\'an' => ' Al-Qur\'an', + 'Hadist' => ' Hadist', + 'Materi Tambahan' => ' Materi Tambahan', + ]; + + return $badges[$this->kategori] ?? $this->kategori; + } + + /** + * Accessor untuk badge kelas + */ + public function getKelasBadgeAttribute() + { + $badges = [ + 'Lambatan' => 'Lambatan', + 'Cepatan' => 'Cepatan', + 'PB' => 'PB', + ]; + + return $badges[$this->kelas] ?? $this->kelas; + } + + /** + * Relasi: Materi memiliki banyak capaian + */ + public function capaian() + { + return $this->hasMany(Capaian::class, 'id_materi', 'id_materi'); + } + + /** + * Get jumlah santri yang sudah ada capaian + */ + public function getJumlahSantriAttribute() + { + return $this->capaian()->distinct('id_santri')->count('id_santri'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/PembayaranSpp.php b/sim-pkpps/app/Models/PembayaranSpp.php new file mode 100644 index 0000000..a8bf376 --- /dev/null +++ b/sim-pkpps/app/Models/PembayaranSpp.php @@ -0,0 +1,172 @@ + 'date', + 'batas_bayar' => 'date', + 'nominal' => 'decimal:2', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Boot method untuk auto-generate ID + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_pembayaran)) { + $last = PembayaranSpp::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_pembayaran, 3)) + 1 : 1; + $model->id_pembayaran = 'SPP' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi: Pembayaran SPP milik satu Santri + */ + public function santri() + { + return $this->belongsTo(Santri::class, 'id_santri', 'id_santri'); + } + + /** + * Accessor: Nama bulan dalam bahasa Indonesia + */ + public function getBulanNamaAttribute() + { + $bulanIndo = [ + 1 => 'Januari', 2 => 'Februari', 3 => 'Maret', + 4 => 'April', 5 => 'Mei', 6 => 'Juni', + 7 => 'Juli', 8 => 'Agustus', 9 => 'September', + 10 => 'Oktober', 11 => 'November', 12 => 'Desember' + ]; + + return $bulanIndo[$this->bulan] ?? '-'; + } + + /** + * Accessor: Periode lengkap (Januari 2024) + */ + public function getPeriodeLengkapAttribute() + { + return $this->bulan_nama . ' ' . $this->tahun; + } + + /** + * Accessor: Status Badge HTML + */ + public function getStatusBadgeAttribute() + { + if ($this->status === 'Lunas') { + return ' Lunas'; + } + + // Cek apakah telat + if ($this->isTelat()) { + return ' Belum Lunas (Telat)'; + } + + return ' Belum Lunas'; + } + + /** + * Cek apakah pembayaran sudah telat + */ + public function isTelat() + { + if ($this->status === 'Lunas') { + return false; + } + + return Carbon::now()->isAfter($this->batas_bayar); + } + + /** + * Accessor: Nominal format Rupiah + */ + public function getNominalFormatAttribute() + { + return 'Rp ' . number_format($this->nominal, 0, ',', '.'); + } + + /** + * Scope: Filter pembayaran belum lunas + */ + public function scopeBelumLunas($query) + { + return $query->where('status', 'Belum Lunas'); + } + + /** + * Scope: Filter pembayaran lunas + */ + public function scopeLunas($query) + { + return $query->where('status', 'Lunas'); + } + + /** + * Scope: Filter pembayaran telat + */ + public function scopeTelat($query) + { + return $query->where('status', 'Belum Lunas') + ->where('batas_bayar', '<', Carbon::now()); + } + + /** + * Scope: Filter by tahun + */ + public function scopeTahun($query, $tahun) + { + return $query->where('tahun', $tahun); + } + + /** + * Scope: Filter by bulan + */ + public function scopeBulan($query, $bulan) + { + return $query->where('bulan', $bulan); + } + + /** + * Scope: Search + */ + public function scopeSearch($query, $search) + { + return $query->whereHas('santri', function($q) use ($search) { + $q->where('nama_lengkap', 'like', "%{$search}%") + ->orWhere('id_santri', 'like', "%{$search}%") + ->orWhere('nis', 'like', "%{$search}%"); + })->orWhere('id_pembayaran', 'like', "%{$search}%"); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/RiwayatPelanggaran.php b/sim-pkpps/app/Models/RiwayatPelanggaran.php new file mode 100644 index 0000000..b851cfd --- /dev/null +++ b/sim-pkpps/app/Models/RiwayatPelanggaran.php @@ -0,0 +1,163 @@ + 'date', + 'poin' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Generator ID Kustom (P001, P002, ...) + * Metode ini akan dijalankan setiap kali model baru dibuat (insert). + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + // Pastikan ID kustom belum terisi + if (empty($model->id_riwayat)) { + // Ambil data riwayat terakhir berdasarkan ID default + $last = RiwayatPelanggaran::orderBy('id', 'desc')->first(); + + // Tentukan nomor urut berikutnya + // Jika ada data terakhir, ambil angka dari ID kustom (misal P001 -> 1) dan tambahkan 1 + $num = $last ? intval(substr($last->id_riwayat, 1)) + 1 : 1; + + // Format ID: 'P' + nomor urut 3 digit (dengan padding 0) + $model->id_riwayat = 'P' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi: Riwayat belongsTo Santri + * Setiap riwayat pelanggaran dimiliki oleh satu santri + */ + public function santri() + { + return $this->belongsTo(Santri::class, 'id_santri', 'id_santri'); + } + + /** + * Relasi: Riwayat belongsTo Kategori + * Setiap riwayat pelanggaran memiliki satu kategori + */ + public function kategori() + { + return $this->belongsTo(KategoriPelanggaran::class, 'id_kategori', 'id_kategori'); + } + + /** + * Accessor: Format tanggal Indonesia + */ + public function getTanggalFormatAttribute() + { + return Carbon::parse($this->tanggal)->isoFormat('D MMMM YYYY'); + } + + /** + * Accessor: Get nama santri (dengan fallback) + */ + public function getNamaSantriAttribute() + { + return $this->santri ? $this->santri->nama_lengkap : 'Santri tidak ditemukan'; + } + + /** + * Accessor: Get nama kategori (dengan fallback) + */ + public function getNamaKategoriAttribute() + { + return $this->kategori ? $this->kategori->nama_pelanggaran : 'Kategori tidak ditemukan'; + } + + /** + * Scope: Filter riwayat berdasarkan santri + */ + public function scopeBySantri($query, $idSantri) + { + return $query->where('id_santri', $idSantri); + } + + /** + * Scope: Filter riwayat berdasarkan kategori + */ + public function scopeByKategori($query, $idKategori) + { + return $query->where('id_kategori', $idKategori); + } + + /** + * Scope: Filter riwayat berdasarkan tanggal + */ + public function scopeByTanggal($query, $tanggalMulai, $tanggalSelesai = null) + { + if ($tanggalSelesai) { + return $query->whereBetween('tanggal', [$tanggalMulai, $tanggalSelesai]); + } + return $query->whereDate('tanggal', $tanggalMulai); + } + + /** + * Scope: Filter riwayat bulan ini + */ + public function scopeBulanIni($query) + { + return $query->whereMonth('tanggal', Carbon::now()->month) + ->whereYear('tanggal', Carbon::now()->year); + } + + /** + * Scope: Urutkan berdasarkan tanggal terbaru + */ + public function scopeTerbaru($query) + { + return $query->orderBy('tanggal', 'desc') + ->orderBy('created_at', 'desc'); + } + + /** + * Scope: Search riwayat + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('id_riwayat', 'like', "%{$search}%") + ->orWhere('keterangan', 'like', "%{$search}%") + ->orWhereHas('santri', function($sq) use ($search) { + $sq->where('nama_lengkap', 'like', "%{$search}%"); + }) + ->orWhereHas('kategori', function($sq) use ($search) { + $sq->where('nama_pelanggaran', 'like', "%{$search}%"); + }); + }); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/Santri.php b/sim-pkpps/app/Models/Santri.php new file mode 100644 index 0000000..53b6d81 --- /dev/null +++ b/sim-pkpps/app/Models/Santri.php @@ -0,0 +1,308 @@ + 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Generator ID Kustom (S001, S002, ...) + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_santri)) { + $last = Santri::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_santri, 1)) + 1 : 1; + $model->id_santri = 'S' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } + + /** + * Relasi: Santri memiliki satu User Account (hasOne) + */ + public function user() + { + return $this->hasOne(User::class, 'role_id', 'id_santri') + ->where('role', 'santri'); + } + + /** + * Relasi: Santri memiliki banyak data kesehatan + */ + public function kesehatanSantri() + { + return $this->hasMany(KesehatanSantri::class, 'id_santri', 'id_santri'); + } + + /** + * Relasi: Kesehatan santri yang masih dirawat + */ + public function kesehatanAktif() + { + return $this->hasMany(KesehatanSantri::class, 'id_santri', 'id_santri') + ->where('status', 'dirawat'); + } + + /** + * Relasi: Santri memiliki banyak data kepulangan + */ + public function kepulangan() + { + return $this->hasMany(Kepulangan::class, 'id_santri', 'id_santri'); + } + + /** + * Relasi: Kepulangan yang sedang aktif + */ + public function kepulanganAktif() + { + return $this->hasMany(Kepulangan::class, 'id_santri', 'id_santri') + ->where('status', 'Disetujui') + ->whereDate('tanggal_pulang', '<=', now()) + ->whereDate('tanggal_kembali', '>=', now()); + } + + /** + * Relasi: Santri memiliki banyak berita (Many-to-Many) + */ + public function berita() + { + return $this->belongsToMany(Berita::class, 'berita_santri', 'id_santri', 'id_berita', 'id_santri', 'id_berita') + ->withPivot('sudah_dibaca', 'tanggal_baca') + ->withTimestamps(); + } + + /** + * Relasi: Santri memiliki banyak riwayat pelanggaran + */ + public function riwayatPelanggaran() + { + return $this->hasMany(RiwayatPelanggaran::class, 'id_santri', 'id_santri'); + } + + /** + * Relasi: Santri memiliki banyak pembayaran SPP + */ + public function pembayaranSpp() + { + return $this->hasMany(PembayaranSpp::class, 'id_santri', 'id_santri'); + } + + /** + * Relasi: SPP yang belum lunas + */ + public function sppBelumLunas() + { + return $this->hasMany(PembayaranSpp::class, 'id_santri', 'id_santri') + ->where('status', 'Belum Lunas'); + } + + /** + * Relasi: SPP yang telat + */ + public function sppTelat() + { + return $this->hasMany(PembayaranSpp::class, 'id_santri', 'id_santri') + ->where('status', 'Belum Lunas') + ->where('batas_bayar', '<', now()); + } + + /** + * Relasi: Santri memiliki banyak transaksi uang saku + */ + public function uangSaku() + { + return $this->hasMany(UangSaku::class, 'id_santri', 'id_santri'); + } + + /** + * Relasi: Santri memiliki banyak absensi kegiatan (BARU) + */ + public function absensiKegiatans() + { + return $this->hasMany(AbsensiKegiatan::class, 'id_santri', 'id_santri'); + } + + /** + * Accessor untuk mendapatkan nama kelas lengkap + */ + public function getKelasLengkapAttribute() + { + $kelasMap = [ + 'PB' => 'Pembinaan (PB)', + 'Lambatan' => 'Lambatan', + 'Cepatan' => 'Cepatan', + ]; + + return $kelasMap[$this->kelas] ?? $this->kelas; + } + + /** + * Accessor untuk mendapatkan badge HTML status + */ + public function getStatusBadgeAttribute() + { + $badges = [ + 'Aktif' => ' Aktif', + 'Lulus' => ' Lulus', + 'Tidak Aktif' => ' Tidak Aktif', + ]; + + return $badges[$this->status] ?? $this->status; + } + + /** + * Accessor: Total poin pelanggaran + */ + public function getTotalPoinPelanggaranAttribute() + { + return $this->riwayatPelanggaran()->sum('poin'); + } + + /** + * Accessor: Total tunggakan SPP + */ + public function getTotalTunggakanAttribute() + { + return $this->sppBelumLunas()->sum('nominal'); + } + + /** + * Accessor: Saldo uang saku terakhir + */ + public function getSaldoUangSakuAttribute() + { + $transaksiTerakhir = $this->uangSaku() + ->orderBy('tanggal_transaksi', 'desc') + ->orderBy('created_at', 'desc') + ->first(); + + return $transaksiTerakhir ? $transaksiTerakhir->saldo_sesudah : 0; + } + + /** + * Accessor: Total pemasukan uang saku + */ + public function getTotalPemasukanUangSakuAttribute() + { + return $this->uangSaku()->where('jenis_transaksi', 'pemasukan')->sum('nominal'); + } + + /** + * Accessor: Total pengeluaran uang saku + */ + public function getTotalPengeluaranUangSakuAttribute() + { + return $this->uangSaku()->where('jenis_transaksi', 'pengeluaran')->sum('nominal'); + } + + /** + * Accessor: Status RFID (BARU) + */ + public function getHasRfidAttribute() + { + return !empty($this->rfid_uid); + } + + /** + * Accessor: Total kehadiran kegiatan (BARU) + */ + public function getTotalKehadiranAttribute() + { + return $this->absensiKegiatans()->where('status', 'Hadir')->count(); + } + + /** + * Scope untuk filter santri aktif + */ + public function scopeAktif($query) + { + return $query->where('status', 'Aktif'); + } + + /** + * Scope untuk filter santri lulus + */ + public function scopeLulus($query) + { + return $query->where('status', 'Lulus'); + } + + /** + * Scope untuk filter santri tidak aktif + */ + public function scopeTidakAktif($query) + { + return $query->where('status', 'Tidak Aktif'); + } + + /** + * Scope untuk filter berdasarkan kelas + */ + public function scopeKelas($query, $kelas) + { + return $query->where('kelas', $kelas); + } + + /** + * Scope untuk search santri + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('nama_lengkap', 'like', "%{$search}%") + ->orWhere('nis', 'like', "%{$search}%") + ->orWhere('id_santri', 'like', "%{$search}%"); + }); + } + + /** + * Relasi: Santri memiliki banyak capaian + */ + public function capaian() + { + return $this->hasMany(Capaian::class, 'id_santri', 'id_santri'); + } + + /** + * Get rata-rata capaian per semester + */ + public function getRataRataCapaianAttribute() + { + return $this->capaian()->avg('persentase') ?? 0; + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/Semester.php b/sim-pkpps/app/Models/Semester.php new file mode 100644 index 0000000..01dfb14 --- /dev/null +++ b/sim-pkpps/app/Models/Semester.php @@ -0,0 +1,95 @@ + 'date', + 'tanggal_akhir' => 'date', + 'is_active' => 'boolean', + 'periode' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Generator ID Kustom (SEM001, SEM002, ...) + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_semester)) { + $last = Semester::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_semester, 3)) + 1 : 1; + $model->id_semester = 'SEM' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + + // Auto-generate nama_semester jika kosong + if (empty($model->nama_semester)) { + $model->nama_semester = "Semester {$model->periode} {$model->tahun_ajaran}"; + } + }); + + // Pastikan hanya 1 semester yang aktif + static::saving(function ($model) { + if ($model->is_active) { + Semester::where('id', '!=', $model->id)->update(['is_active' => 0]); + } + }); + } + + /** + * Relasi: Semester memiliki banyak capaian + */ + public function capaian() + { + return $this->hasMany(Capaian::class, 'id_semester', 'id_semester'); + } + + /** + * Scope: Semester aktif + */ + public function scopeAktif($query) + { + return $query->where('is_active', 1); + } + + /** + * Scope: Tahun ajaran tertentu + */ + public function scopeTahunAjaran($query, $tahun) + { + return $query->where('tahun_ajaran', $tahun); + } + + /** + * Accessor: Badge status aktif + */ + public function getStatusBadgeAttribute() + { + if ($this->is_active) { + return ' Aktif'; + } + return 'Tidak Aktif'; + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/UangSaku.php b/sim-pkpps/app/Models/UangSaku.php new file mode 100644 index 0000000..70a37e6 --- /dev/null +++ b/sim-pkpps/app/Models/UangSaku.php @@ -0,0 +1,145 @@ + 'date', + 'nominal' => 'decimal:2', + 'saldo_sebelum' => 'decimal:2', + 'saldo_sesudah' => 'decimal:2', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Auto-generate ID kustom saat create + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->id_uang_saku)) { + $last = UangSaku::orderBy('id', 'desc')->first(); + $num = $last ? intval(substr($last->id_uang_saku, 2)) + 1 : 1; + $model->id_uang_saku = 'SK' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + + // Hitung saldo otomatis + $saldoTerakhir = UangSaku::where('id_santri', $model->id_santri) + ->orderBy('tanggal_transaksi', 'desc') + ->orderBy('created_at', 'desc') + ->first(); + + $model->saldo_sebelum = $saldoTerakhir ? $saldoTerakhir->saldo_sesudah : 0; + + if ($model->jenis_transaksi === 'pemasukan') { + $model->saldo_sesudah = $model->saldo_sebelum + $model->nominal; + } else { + $model->saldo_sesudah = $model->saldo_sebelum - $model->nominal; + } + }); + + static::updating(function ($model) { + // Recalculate saldo when updating + if ($model->isDirty(['nominal', 'jenis_transaksi'])) { + $saldoTerakhir = UangSaku::where('id_santri', $model->id_santri) + ->where('id', '<', $model->id) + ->orderBy('tanggal_transaksi', 'desc') + ->orderBy('created_at', 'desc') + ->first(); + + $model->saldo_sebelum = $saldoTerakhir ? $saldoTerakhir->saldo_sesudah : 0; + + if ($model->jenis_transaksi === 'pemasukan') { + $model->saldo_sesudah = $model->saldo_sebelum + $model->nominal; + } else { + $model->saldo_sesudah = $model->saldo_sebelum - $model->nominal; + } + } + }); + } + + /** + * Relasi ke Santri + */ + public function santri() + { + return $this->belongsTo(Santri::class, 'id_santri', 'id_santri'); + } + + /** + * Scope untuk filter berdasarkan santri + */ + public function scopeBySantri($query, $idSantri) + { + return $query->where('id_santri', $idSantri); + } + + /** + * Scope untuk filter berdasarkan jenis transaksi + */ + public function scopeByJenis($query, $jenis) + { + return $query->where('jenis_transaksi', $jenis); + } + + /** + * Scope untuk filter berdasarkan tanggal + */ + public function scopeByDateRange($query, $start, $end) + { + return $query->whereBetween('tanggal_transaksi', [$start, $end]); + } + + /** + * Scope untuk search + */ + public function scopeSearch($query, $search) + { + return $query->where(function($q) use ($search) { + $q->where('id_uang_saku', 'like', "%{$search}%") + ->orWhere('keterangan', 'like', "%{$search}%") + ->orWhereHas('santri', function($sq) use ($search) { + $sq->where('nama_lengkap', 'like', "%{$search}%") + ->orWhere('id_santri', 'like', "%{$search}%"); + }); + }); + } + + /** + * Accessor untuk format nominal + */ + public function getNominalFormatAttribute() + { + return 'Rp ' . number_format($this->nominal, 0, ',', '.'); + } + + /** + * Accessor untuk format saldo + */ + public function getSaldoSesudahFormatAttribute() + { + return 'Rp ' . number_format($this->saldo_sesudah, 0, ',', '.'); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/User.php b/sim-pkpps/app/Models/User.php new file mode 100644 index 0000000..d45d1da --- /dev/null +++ b/sim-pkpps/app/Models/User.php @@ -0,0 +1,65 @@ + + */ + protected $fillable = [ + 'name', + 'email', + 'username', // Ditambahkan + 'password', + 'role', // Ditambahkan + 'role_id', // Ditambahkan + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; + + // Helper method untuk cek role + public function isAdmin() + { + return $this->role === 'admin'; + } + + public function isSantri() + { + return $this->role === 'santri'; + } + + public function isWali() + { + return $this->role === 'wali'; + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Models/Wali.php b/sim-pkpps/app/Models/Wali.php new file mode 100644 index 0000000..24a520b --- /dev/null +++ b/sim-pkpps/app/Models/Wali.php @@ -0,0 +1,32 @@ +id_wali)) { + $last = Wali::orderBy('id', 'desc')->first(); + // Ambil nomor urut terakhir, jika ada, tambahkan 1. Jika tidak, mulai dari 1. + $num = $last ? intval(substr($last->id_wali, 2)) + 1 : 1; + // Format ID: 'WS' + nomor urut 3 digit (dengan padding 0) + $model->id_wali = 'WS' . str_pad($num, 3, '0', STR_PAD_LEFT); + } + }); + } +} \ No newline at end of file diff --git a/sim-pkpps/app/Providers/AppServiceProvider.php b/sim-pkpps/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..452e6b6 --- /dev/null +++ b/sim-pkpps/app/Providers/AppServiceProvider.php @@ -0,0 +1,24 @@ + + */ + protected $policies = [ + // + ]; + + /** + * Register any authentication / authorization services. + */ + public function boot(): void + { + // + } +} diff --git a/sim-pkpps/app/Providers/BroadcastServiceProvider.php b/sim-pkpps/app/Providers/BroadcastServiceProvider.php new file mode 100644 index 0000000..2be04f5 --- /dev/null +++ b/sim-pkpps/app/Providers/BroadcastServiceProvider.php @@ -0,0 +1,19 @@ +> + */ + protected $listen = [ + Registered::class => [ + SendEmailVerificationNotification::class, + ], + ]; + + /** + * Register any events for your application. + */ + public function boot(): void + { + // + } + + /** + * Determine if events and listeners should be automatically discovered. + */ + public function shouldDiscoverEvents(): bool + { + return false; + } +} diff --git a/sim-pkpps/app/Providers/RouteServiceProvider.php b/sim-pkpps/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..025e874 --- /dev/null +++ b/sim-pkpps/app/Providers/RouteServiceProvider.php @@ -0,0 +1,40 @@ +by($request->user()?->id ?: $request->ip()); + }); + + $this->routes(function () { + Route::middleware('api') + ->prefix('api') + ->group(base_path('routes/api.php')); + + Route::middleware('web') + ->group(base_path('routes/web.php')); + }); + } +} diff --git a/sim-pkpps/app/View/Components/AppLayout.php b/sim-pkpps/app/View/Components/AppLayout.php new file mode 100644 index 0000000..de0d46f --- /dev/null +++ b/sim-pkpps/app/View/Components/AppLayout.php @@ -0,0 +1,17 @@ +make(Illuminate\Contracts\Console\Kernel::class); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running, we will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/sim-pkpps/bootstrap/app.php b/sim-pkpps/bootstrap/app.php new file mode 100644 index 0000000..037e17d --- /dev/null +++ b/sim-pkpps/bootstrap/app.php @@ -0,0 +1,55 @@ +singleton( + Illuminate\Contracts\Http\Kernel::class, + App\Http\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Console\Kernel::class, + App\Console\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + App\Exceptions\Handler::class +); + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/sim-pkpps/bootstrap/cache/.gitignore b/sim-pkpps/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/sim-pkpps/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/sim-pkpps/composer.json b/sim-pkpps/composer.json new file mode 100644 index 0000000..9e492b1 --- /dev/null +++ b/sim-pkpps/composer.json @@ -0,0 +1,68 @@ +{ + "name": "laravel/laravel", + "type": "project", + "description": "The skeleton application for the Laravel framework.", + "keywords": ["laravel", "framework"], + "license": "MIT", + "require": { + "php": "^8.1", + "barryvdh/laravel-dompdf": "^3.1", + "guzzlehttp/guzzle": "^7.2", + "laravel/framework": "^10.10", + "laravel/sanctum": "^3.3", + "laravel/tinker": "^2.8" + }, + "require-dev": { + "fakerphp/faker": "^1.9.1", + "laravel/breeze": "^1.29", + "laravel/pint": "^1.0", + "laravel/sail": "^1.18", + "mockery/mockery": "^1.4.4", + "nunomaduro/collision": "^7.0", + "phpunit/phpunit": "^10.1", + "spatie/laravel-ignition": "^2.0" + }, + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Factories\\": "database/factories/", + "Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "scripts": { + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi" + ], + "post-update-cmd": [ + "@php artisan vendor:publish --tag=laravel-assets --ansi --force" + ], + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate --ansi" + ] + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "php-http/discovery": true + } + }, + "minimum-stability": "stable", + "prefer-stable": true +} diff --git a/sim-pkpps/composer.lock b/sim-pkpps/composer.lock new file mode 100644 index 0000000..3eedc5f --- /dev/null +++ b/sim-pkpps/composer.lock @@ -0,0 +1,8678 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "3b130f909d8c7acd5973043a958c243d", + "packages": [ + { + "name": "barryvdh/laravel-dompdf", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-dompdf.git", + "reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d", + "reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d", + "shasum": "" + }, + "require": { + "dompdf/dompdf": "^3.0", + "illuminate/support": "^9|^10|^11|^12", + "php": "^8.1" + }, + "require-dev": { + "larastan/larastan": "^2.7|^3.0", + "orchestra/testbench": "^7|^8|^9|^10", + "phpro/grumphp": "^2.5", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "PDF": "Barryvdh\\DomPDF\\Facade\\Pdf", + "Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf" + }, + "providers": [ + "Barryvdh\\DomPDF\\ServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\DomPDF\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "A DOMPDF Wrapper for Laravel", + "keywords": [ + "dompdf", + "laravel", + "pdf" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-dompdf/issues", + "source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-02-13T15:07:54+00:00" + }, + { + "name": "brick/math", + "version": "0.12.3", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "6.8.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.12.3" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2025-02-28T13:11:00+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" + }, + "require-dev": { + "doctrine/dbal": "^3.7.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2023-12-11T17:09:12+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dompdf/dompdf", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "baed300e4fb8226359c04395518059a136e2a2e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/baed300e4fb8226359c04395518059a136e2a2e2", + "reference": "baed300e4fb8226359c04395518059a136e2a2e2", + "shasum": "" + }, + "require": { + "dompdf/php-font-lib": "^1.0.0", + "dompdf/php-svg-lib": "^1.0.0", + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-gd": "*", + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "type": "library", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v3.1.3" + }, + "time": "2025-10-14T13:10:17+00:00" + }, + { + "name": "dompdf/php-font-lib", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "The FontLib Community", + "homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/dompdf/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.1" + }, + "time": "2024-12-02T14:37:59+00:00" + }, + { + "name": "dompdf/php-svg-lib", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The SvgLib Community", + "homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/dompdf/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0" + }, + "time": "2024-04-29T13:26:35+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "8c784d071debd117328803d86b2097615b457500" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2024-10-09T13:47:03+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "21dc724a0583619cd1652f673303492272778051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-08-23T21:21:41+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:27:06+00:00" + }, + { + "name": "laravel/framework", + "version": "v10.49.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "b6848517f93445e7f243aaa4f3c32cc70f6739e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/b6848517f93445e7f243aaa4f3c32cc70f6739e2", + "reference": "b6848517f93445e7f243aaa4f3c32cc70f6739e2", + "shasum": "" + }, + "require": { + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.3.2", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.9", + "laravel/serializable-closure": "^1.3", + "league/commonmark": "^2.2.1", + "league/flysystem": "^3.8.0", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^2.67", + "nunomaduro/termwind": "^1.13", + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^6.2", + "symfony/error-handler": "^6.2", + "symfony/finder": "^6.2", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.2", + "symfony/mailer": "^6.2", + "symfony/mime": "^6.2", + "symfony/process": "^6.2", + "symfony/routing": "^6.2", + "symfony/uid": "^6.2", + "symfony/var-dumper": "^6.2", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.4.1", + "voku/portable-ascii": "^2.0" + }, + "conflict": { + "carbonphp/carbon-doctrine-types": ">=3.0", + "doctrine/dbal": ">=4.0", + "mockery/mockery": "1.6.8", + "phpunit/phpunit": ">=11.0.0", + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.235.5", + "doctrine/dbal": "^3.5.1", + "ext-gmp": "*", + "fakerphp/faker": "^1.21", + "guzzlehttp/guzzle": "^7.5", + "league/flysystem-aws-s3-v3": "^3.0", + "league/flysystem-ftp": "^3.0", + "league/flysystem-path-prefixing": "^3.3", + "league/flysystem-read-only": "^3.3", + "league/flysystem-sftp-v3": "^3.0", + "mockery/mockery": "^1.5.1", + "nyholm/psr7": "^1.2", + "orchestra/testbench-core": "^8.23.4", + "pda/pheanstalk": "^4.0", + "phpstan/phpstan": "~1.11.11", + "phpunit/phpunit": "^10.0.7", + "predis/predis": "^2.0.2", + "symfony/cache": "^6.2", + "symfony/http-client": "^6.2.4", + "symfony/psr-http-message-bridge": "^2.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", + "brianium/paratest": "Required to run tests in parallel (^6.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", + "league/flysystem-read-only": "Required to use read-only disks (^3.3)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "mockery/mockery": "Required to use mocking (^1.5.1).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", + "predis/predis": "Required to use the predis connector (^2.0.2).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-09-08T22:02:05+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.25", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/7b4029a84c37cb2725fc7f011586e2997040bc95", + "reference": "7b4029a84c37cb2725fc7f011586e2997040bc95", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.25" + }, + "time": "2024-08-12T22:06:33+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v3.3.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/8c104366459739f3ada0e994bcd3e6fd681ce3d5", + "reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.21|^10.0", + "illuminate/contracts": "^9.21|^10.0", + "illuminate/database": "^9.21|^10.0", + "illuminate/support": "^9.21|^10.0", + "php": "^8.0.2" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.28.2|^8.8.3", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2023-12-19T18:44:48+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.3.7", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "4f48ade902b94323ca3be7646db16209ec76be3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/4f48ade902b94323ca3be7646db16209ec76be3d", + "reference": "4f48ade902b94323ca3be7646db16209ec76be3d", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "nesbot/carbon": "^2.61|^3.0", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2024-11-14T18:34:49+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.10.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.10.1" + }, + "time": "2025-01-27T14:24:01+00:00" + }, + { + "name": "league/commonmark", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2025-07-20T12:47:49+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.30.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e", + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.30.0" + }, + "time": "2025-06-25T13:29:59+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.30.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/6691915f77c7fb69adfb87dcd550052dc184ee10", + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.0" + }, + "time": "2025-05-21T10:34:19+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + }, + "time": "2025-07-25T09:04:22+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2025-03-24T10:02:05+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.73.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/9228ce90e1035ff2f0db84b40ec2e023ed802075", + "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "*", + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "<6", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2025-01-08T20:10:23+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.4" + }, + "require-dev": { + "nette/tester": "^2.5.2", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.2" + }, + "time": "2024-10-06T23:10:23+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/tester": "^2.5", + "phpstan/phpstan-nette": "^2.0@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.8" + }, + "time": "2025-08-06T21:43:34+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + }, + "time": "2025-08-13T20:13:15+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/5369ef84d8142c1d87e4ec278711d4ece3cbf301", + "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.4.15" + }, + "require-dev": { + "illuminate/console": "^10.48.24", + "illuminate/support": "^10.48.24", + "laravel/pint": "^1.18.2", + "pestphp/pest": "^2.36.0", + "pestphp/pest-plugin-mock": "2.0.0", + "phpstan/phpstan": "^1.12.11", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^6.4.15", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v1.17.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2024-11-21T10:36:35+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.4", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-08-21T11:53:16+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.12", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "cd23863404a40ccfaf733e3af4db2b459837f7e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/cd23863404a40ccfaf733e3af4db2b459837f7e7", + "reference": "cd23863404a40ccfaf733e3af4db2b459837f7e7", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "https://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.12" + }, + "time": "2025-09-20T13:46:31+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.1" + }, + "time": "2025-09-04T20:59:21+00:00" + }, + { + "name": "sabberworm/php-css-parser", + "version": "v8.9.0", + "source": { + "type": "git", + "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "require-dev": { + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41", + "rawr/cross-data-providers": "^2.0.0" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + }, + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Jake Hotson", + "email": "jake.github@qzdesign.co.uk" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0" + }, + "time": "2025-07-11T13:20:48+00:00" + }, + { + "name": "symfony/console", + "version": "v6.4.25", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "273fd29ff30ba0a88ca5fb83f7cf1ab69306adae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/273fd29ff30ba0a88ca5fb83f7cf1ab69306adae", + "reference": "273fd29ff30ba0a88ca5fb83f7cf1ab69306adae", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.4.25" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-22T10:21:53+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v6.4.24", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "30fd0b3cf0e972e82636038ce4db0e4fe777112c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/30fd0b3cf0e972e82636038ce4db0e4fe777112c", + "reference": "30fd0b3cf0e972e82636038ce4db0e4fe777112c", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v6.4.24" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-24T08:25:04+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-13T11:49:31+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.4.24", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "73089124388c8510efb8d2d1689285d285937b08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/73089124388c8510efb8d2d1689285d285937b08", + "reference": "73089124388c8510efb8d2d1689285d285937b08", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.4.24" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T12:02:45+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v6.4.25", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "6bc974c0035b643aa497c58d46d9e25185e4b272" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6bc974c0035b643aa497c58d46d9e25185e4b272", + "reference": "6bc974c0035b643aa497c58d46d9e25185e4b272", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v6.4.25" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-20T06:48:20+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v6.4.25", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "a0ee3cea5cabf4ed960fd2ef57668ceeacdb6e15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/a0ee3cea5cabf4ed960fd2ef57668ceeacdb6e15", + "reference": "a0ee3cea5cabf4ed960fd2ef57668ceeacdb6e15", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.3", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^5.4|^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v6.4.25" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-29T07:55:45+00:00" + }, + { + "name": "symfony/mailer", + "version": "v6.4.25", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "628b43b45a3e6b15c8a633fb22df547ed9b492a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/628b43b45a3e6b15c8a633fb22df547ed9b492a2", + "reference": "628b43b45a3e6b15c8a633fb22df547ed9b492a2", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v6.4.25" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-13T09:41:44+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.4.24", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "664d5e844a2de5e11c8255d0aef6bc15a9660ac7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/664d5e844a2de5e11c8255d0aef6bc15a9660ac7", + "reference": "664d5e844a2de5e11c8255d0aef6bc15a9660ac7", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.4|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v6.4.24" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T12:02:45+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-27T09:58:17+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-08T02:45:35+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v6.4.25", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "6be2f0c9ab3428587c07bed03aa9e3d1b823c6c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/6be2f0c9ab3428587c07bed03aa9e3d1b823c6c8", + "reference": "6be2f0c9ab3428587c07bed03aa9e3d1b823c6c8", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.4.25" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-14T06:23:17+00:00" + }, + { + "name": "symfony/routing", + "version": "v6.4.24", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "e4f94e625c8e6f910aa004a0042f7b2d398278f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/e4f94e625c8e6f910aa004a0042f7b2d398278f5", + "reference": "e4f94e625c8e6f910aa004a0042f7b2d398278f5", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v6.4.24" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T08:46:37+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-25T09:37:31+00:00" + }, + { + "name": "symfony/string", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-25T06:35:40+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.4.24", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/300b72643e89de0734d99a9e3f8494a3ef6936e1", + "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.4.24" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-30T17:30:48+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-27T08:32:26+00:00" + }, + { + "name": "symfony/uid", + "version": "v6.4.24", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "17da16a750541a42cf2183935e0f6008316c23f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/17da16a750541a42cf2183935e0f6008316c23f7", + "reference": "17da16a750541a42cf2183935e0f6008316c23f7", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v6.4.24" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-10T08:14:14+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.4.25", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "c6cd92486e9fc32506370822c57bc02353a5a92c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c6cd92486e9fc32506370822c57bc02353a5a92c", + "reference": "c6cd92486e9fc32506370822c57bc02353a5a92c", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.4.25" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-13T09:41:44+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + }, + "time": "2024-12-21T16:25:41+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.2", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2025-04-30T23:37:27+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2024-11-21T01:49:47+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "laravel/breeze", + "version": "v1.29.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/breeze.git", + "reference": "22c53b84b7fff91b01a318d71a10dfc251e92849" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/breeze/zipball/22c53b84b7fff91b01a318d71a10dfc251e92849", + "reference": "22c53b84b7fff91b01a318d71a10dfc251e92849", + "shasum": "" + }, + "require": { + "illuminate/console": "^10.17", + "illuminate/filesystem": "^10.17", + "illuminate/support": "^10.17", + "illuminate/validation": "^10.17", + "php": "^8.1.0" + }, + "require-dev": { + "orchestra/testbench": "^8.0", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Breeze\\BreezeServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Breeze\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Minimal Laravel authentication scaffolding with Blade and Tailwind.", + "keywords": [ + "auth", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/breeze/issues", + "source": "https://github.com/laravel/breeze" + }, + "time": "2024-03-04T14:35:21+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.25.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.87.2", + "illuminate/view": "^11.46.0", + "larastan/larastan": "^3.7.1", + "laravel-zero/framework": "^11.45.0", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.3.1", + "pestphp/pest": "^2.36.0" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2025-09-19T02:57:12+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.46.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "eb90c4f113c4a9637b8fdd16e24cfc64f2b0ae6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/eb90c4f113c4a9637b8fdd16e24cfc64f2b0ae6e", + "reference": "eb90c4f113c4a9637b8fdd16e24cfc64f2b0ae6e", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0", + "symfony/yaml": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.10" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2025-09-23T13:44:39+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v7.12.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/995245421d3d7593a6960822063bdba4f5d7cf1a", + "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.17.0", + "nunomaduro/termwind": "^1.17.0", + "php": "^8.1.0", + "symfony/console": "^6.4.17" + }, + "conflict": { + "laravel/framework": ">=11.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.4.8", + "laravel/framework": "^10.48.29", + "laravel/pint": "^1.21.2", + "laravel/sail": "^1.41.0", + "laravel/sanctum": "^3.3.3", + "laravel/tinker": "^2.10.1", + "nunomaduro/larastan": "^2.10.0", + "orchestra/testbench-core": "^8.35.0", + "pestphp/pest": "^2.36.0", + "phpunit/phpunit": "^10.5.36", + "sebastian/environment": "^6.1.0", + "spatie/laravel-ignition": "^2.9.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-03-14T22:35:49+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.57", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "8e7598bbb17bb5cd80728f4831d3f83223d3a6b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e7598bbb17bb5cd80728f4831d3f83223d3a6b3", + "reference": "8e7598bbb17bb5cd80728f4831d3f83223d3a6b3", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.4", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.4", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.57" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:30:38+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-09-07T05:25:07+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "0735b90f4da94969541dac1da743446e276defa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:09:11+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-10T07:50:56+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "spatie/backtrace", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/backtrace.git", + "reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/8c0f16a59ae35ec8c62d85c3c17585158f430110", + "reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-json": "*", + "laravel/serializable-closure": "^1.3 || ^2.0", + "phpunit/phpunit": "^9.3 || ^11.4.3", + "spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1.6", + "symfony/var-dumper": "^5.1 || ^6.0 || ^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Backtrace\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van de Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "A better backtrace", + "homepage": "https://github.com/spatie/backtrace", + "keywords": [ + "Backtrace", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/backtrace/issues", + "source": "https://github.com/spatie/backtrace/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/spatie", + "type": "github" + }, + { + "url": "https://spatie.be/open-source/support-us", + "type": "other" + } + ], + "time": "2025-08-26T08:22:30+00:00" + }, + { + "name": "spatie/error-solutions", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/error-solutions.git", + "reference": "e495d7178ca524f2dd0fe6a1d99a1e608e1c9936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/error-solutions/zipball/e495d7178ca524f2dd0fe6a1d99a1e608e1c9936", + "reference": "e495d7178ca524f2dd0fe6a1d99a1e608e1c9936", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "illuminate/broadcasting": "^10.0|^11.0|^12.0", + "illuminate/cache": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0", + "livewire/livewire": "^2.11|^3.5.20", + "openai-php/client": "^0.10.1", + "orchestra/testbench": "8.22.3|^9.0|^10.0", + "pestphp/pest": "^2.20|^3.0", + "phpstan/phpstan": "^2.1", + "psr/simple-cache": "^3.0", + "psr/simple-cache-implementation": "^3.0", + "spatie/ray": "^1.28", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "simple-cache-implementation": "To cache solutions from OpenAI" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Ignition\\": "legacy/ignition", + "Spatie\\ErrorSolutions\\": "src", + "Spatie\\LaravelIgnition\\": "legacy/laravel-ignition" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ruben Van Assche", + "email": "ruben@spatie.be", + "role": "Developer" + } + ], + "description": "This is my package error-solutions", + "homepage": "https://github.com/spatie/error-solutions", + "keywords": [ + "error-solutions", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/error-solutions/issues", + "source": "https://github.com/spatie/error-solutions/tree/1.1.3" + }, + "funding": [ + { + "url": "https://github.com/Spatie", + "type": "github" + } + ], + "time": "2025-02-14T12:29:50+00:00" + }, + { + "name": "spatie/flare-client-php", + "version": "1.10.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/flare-client-php.git", + "reference": "bf1716eb98bd689451b071548ae9e70738dce62f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/bf1716eb98bd689451b071548ae9e70738dce62f", + "reference": "bf1716eb98bd689451b071548ae9e70738dce62f", + "shasum": "" + }, + "require": { + "illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^8.0", + "spatie/backtrace": "^1.6.1", + "symfony/http-foundation": "^5.2|^6.0|^7.0", + "symfony/mime": "^5.2|^6.0|^7.0", + "symfony/process": "^5.2|^6.0|^7.0", + "symfony/var-dumper": "^5.2|^6.0|^7.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.5.0", + "pestphp/pest": "^1.20|^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "spatie/pest-plugin-snapshots": "^1.0|^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\FlareClient\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Send PHP errors to Flare", + "homepage": "https://github.com/spatie/flare-client-php", + "keywords": [ + "exception", + "flare", + "reporting", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/flare-client-php/issues", + "source": "https://github.com/spatie/flare-client-php/tree/1.10.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-02-14T13:42:06+00:00" + }, + { + "name": "spatie/ignition", + "version": "1.15.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/ignition.git", + "reference": "31f314153020aee5af3537e507fef892ffbf8c85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/ignition/zipball/31f314153020aee5af3537e507fef892ffbf8c85", + "reference": "31f314153020aee5af3537e507fef892ffbf8c85", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.0", + "spatie/error-solutions": "^1.0", + "spatie/flare-client-php": "^1.7", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "illuminate/cache": "^9.52|^10.0|^11.0|^12.0", + "mockery/mockery": "^1.4", + "pestphp/pest": "^1.20|^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "psr/simple-cache-implementation": "*", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "simple-cache-implementation": "To cache solutions from OpenAI" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\Ignition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for PHP applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/ignition/issues", + "source": "https://github.com/spatie/ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-02-21T14:31:39+00:00" + }, + { + "name": "spatie/laravel-ignition", + "version": "2.9.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-ignition.git", + "reference": "1baee07216d6748ebd3a65ba97381b051838707a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/1baee07216d6748ebd3a65ba97381b051838707a", + "reference": "1baee07216d6748ebd3a65ba97381b051838707a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/support": "^10.0|^11.0|^12.0", + "php": "^8.1", + "spatie/ignition": "^1.15", + "symfony/console": "^6.2.3|^7.0", + "symfony/var-dumper": "^6.2.3|^7.0" + }, + "require-dev": { + "livewire/livewire": "^2.11|^3.3.5", + "mockery/mockery": "^1.5.1", + "openai-php/client": "^0.8.1|^0.10", + "orchestra/testbench": "8.22.3|^9.0|^10.0", + "pestphp/pest": "^2.34|^3.7", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan-deprecation-rules": "^1.1.1|^2.0", + "phpstan/phpstan-phpunit": "^1.3.16|^2.0", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "psr/simple-cache-implementation": "Needed to cache solutions from OpenAI" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare" + }, + "providers": [ + "Spatie\\LaravelIgnition\\IgnitionServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\LaravelIgnition\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" + } + ], + "description": "A beautiful error page for Laravel applications.", + "homepage": "https://flareapp.io/ignition", + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/laravel-ignition/issues", + "source": "https://github.com/spatie/laravel-ignition" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-02-20T13:13:55+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-27T11:34:33+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.1" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/sim-pkpps/config/app.php b/sim-pkpps/config/app.php new file mode 100644 index 0000000..9207160 --- /dev/null +++ b/sim-pkpps/config/app.php @@ -0,0 +1,188 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + 'asset_url' => env('ASSET_URL'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + + 'locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the current one + | is not available. You may change the value to correspond to any of + | the language folders that are provided through your application. + | + */ + + 'fallback_locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Faker Locale + |-------------------------------------------------------------------------- + | + | This locale will be used by the Faker PHP library when generating fake + | data for your database seeds. For example, this will be used to get + | localized telephone numbers, street address information and more. + | + */ + + 'faker_locale' => 'en_US', + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | + */ + + 'key' => env('APP_KEY'), + + 'cipher' => 'AES-256-CBC', + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => 'file', + // 'store' => 'redis', + ], + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => ServiceProvider::defaultProviders()->merge([ + /* + * Package Service Providers... + */ + + /* + * Application Service Providers... + */ + App\Providers\AppServiceProvider::class, + App\Providers\AuthServiceProvider::class, + // App\Providers\BroadcastServiceProvider::class, + App\Providers\EventServiceProvider::class, + App\Providers\RouteServiceProvider::class, + ])->toArray(), + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | + */ + + 'aliases' => Facade::defaultAliases()->merge([ + // 'Example' => App\Facades\Example::class, + ])->toArray(), + +]; diff --git a/sim-pkpps/config/auth.php b/sim-pkpps/config/auth.php new file mode 100644 index 0000000..9548c15 --- /dev/null +++ b/sim-pkpps/config/auth.php @@ -0,0 +1,115 @@ + [ + 'guard' => 'web', + 'passwords' => 'users', + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | here which uses session storage and the Eloquent user provider. + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | If you have multiple user tables or models you may configure multiple + | sources which represent each model / table. These sources may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => App\Models\User::class, + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | You may specify multiple password reset configurations if you have more + | than one user table or model in the application and you want to have + | separate password reset settings based on the specific user types. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => 'password_reset_tokens', + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | times out and the user is prompted to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => 10800, + +]; diff --git a/sim-pkpps/config/broadcasting.php b/sim-pkpps/config/broadcasting.php new file mode 100644 index 0000000..2410485 --- /dev/null +++ b/sim-pkpps/config/broadcasting.php @@ -0,0 +1,71 @@ + env('BROADCAST_DRIVER', 'null'), + + /* + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over websockets. Samples of + | each available type of connection are provided inside this array. + | + */ + + 'connections' => [ + + 'pusher' => [ + 'driver' => 'pusher', + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + 'cluster' => env('PUSHER_APP_CLUSTER'), + 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', + 'port' => env('PUSHER_PORT', 443), + 'scheme' => env('PUSHER_SCHEME', 'https'), + 'encrypted' => true, + 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'ably' => [ + 'driver' => 'ably', + 'key' => env('ABLY_KEY'), + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + ], + + 'log' => [ + 'driver' => 'log', + ], + + 'null' => [ + 'driver' => 'null', + ], + + ], + +]; diff --git a/sim-pkpps/config/cache.php b/sim-pkpps/config/cache.php new file mode 100644 index 0000000..d4171e2 --- /dev/null +++ b/sim-pkpps/config/cache.php @@ -0,0 +1,111 @@ + env('CACHE_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "apc", "array", "database", "file", + | "memcached", "redis", "dynamodb", "octane", "null" + | + */ + + 'stores' => [ + + 'apc' => [ + 'driver' => 'apc', + ], + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', + 'connection' => null, + 'lock_connection' => null, + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'cache', + 'lock_connection' => 'default', + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + 'octane' => [ + 'driver' => 'octane', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, or DynamoDB cache + | stores there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), + +]; diff --git a/sim-pkpps/config/cors.php b/sim-pkpps/config/cors.php new file mode 100644 index 0000000..8a39e6d --- /dev/null +++ b/sim-pkpps/config/cors.php @@ -0,0 +1,34 @@ + ['api/*', 'sanctum/csrf-cookie'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => false, + +]; diff --git a/sim-pkpps/config/database.php b/sim-pkpps/config/database.php new file mode 100644 index 0000000..137ad18 --- /dev/null +++ b/sim-pkpps/config/database.php @@ -0,0 +1,151 @@ + env('DB_CONNECTION', 'mysql'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DATABASE_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + // 'encrypt' => env('DB_ENCRYPT', 'yes'), + // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), + ], + + ], + +]; diff --git a/sim-pkpps/config/filesystems.php b/sim-pkpps/config/filesystems.php new file mode 100644 index 0000000..e9d9dbd --- /dev/null +++ b/sim-pkpps/config/filesystems.php @@ -0,0 +1,76 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been set up for each driver as an example of the required values. + | + | Supported Drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + 'throw' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/sim-pkpps/config/hashing.php b/sim-pkpps/config/hashing.php new file mode 100644 index 0000000..0e8a0bb --- /dev/null +++ b/sim-pkpps/config/hashing.php @@ -0,0 +1,54 @@ + 'bcrypt', + + /* + |-------------------------------------------------------------------------- + | Bcrypt Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Bcrypt algorithm. This will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'bcrypt' => [ + 'rounds' => env('BCRYPT_ROUNDS', 12), + 'verify' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Argon Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Argon algorithm. These will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'argon' => [ + 'memory' => 65536, + 'threads' => 1, + 'time' => 4, + 'verify' => true, + ], + +]; diff --git a/sim-pkpps/config/logging.php b/sim-pkpps/config/logging.php new file mode 100644 index 0000000..c44d276 --- /dev/null +++ b/sim-pkpps/config/logging.php @@ -0,0 +1,131 @@ + env('LOG_CHANNEL', 'stack'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => false, + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Out of + | the box, Laravel uses the Monolog PHP logging library. This gives + | you a variety of powerful log handlers / formatters to utilize. + | + | Available Drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", + | "custom", "stack" + | + */ + + 'channels' => [ + 'stack' => [ + 'driver' => 'stack', + 'channels' => ['single'], + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => 14, + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => 'Laravel Log', + 'emoji' => ':boom:', + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'with' => [ + 'stream' => 'php://stderr', + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => LOG_USER, + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + ], + +]; diff --git a/sim-pkpps/config/mail.php b/sim-pkpps/config/mail.php new file mode 100644 index 0000000..e894b2e --- /dev/null +++ b/sim-pkpps/config/mail.php @@ -0,0 +1,134 @@ + env('MAIL_MAILER', 'smtp'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers to be used while + | sending an e-mail. You will specify which one you are using for your + | mailers below. You are free to add additional mailers as required. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "log", "array", "failover", "roundrobin" + | + */ + + 'mailers' => [ + 'smtp' => [ + 'transport' => 'smtp', + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + 'port' => env('MAIL_PORT', 587), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN'), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => null, + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'mailgun' => [ + 'transport' => 'mailgun', + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => 'default', + + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], + +]; diff --git a/sim-pkpps/config/queue.php b/sim-pkpps/config/queue.php new file mode 100644 index 0000000..01c6b05 --- /dev/null +++ b/sim-pkpps/config/queue.php @@ -0,0 +1,109 @@ + env('QUEUE_CONNECTION', 'sync'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', + 'retry_after' => 90, + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + 'retry_after' => 90, + 'block_for' => 0, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => 90, + 'block_for' => null, + 'after_commit' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/sim-pkpps/config/sanctum.php b/sim-pkpps/config/sanctum.php new file mode 100644 index 0000000..35d75b3 --- /dev/null +++ b/sim-pkpps/config/sanctum.php @@ -0,0 +1,83 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, + 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, + ], + +]; diff --git a/sim-pkpps/config/services.php b/sim-pkpps/config/services.php new file mode 100644 index 0000000..0ace530 --- /dev/null +++ b/sim-pkpps/config/services.php @@ -0,0 +1,34 @@ + [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), + 'scheme' => 'https', + ], + + 'postmark' => [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + +]; diff --git a/sim-pkpps/config/session.php b/sim-pkpps/config/session.php new file mode 100644 index 0000000..2eab085 --- /dev/null +++ b/sim-pkpps/config/session.php @@ -0,0 +1,135 @@ + env('SESSION_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | ✅ DIUBAH: Dari 120 menit ke 60 menit (expired lebih cepat) + */ + + 'lifetime' => env('SESSION_LIFETIME', 60), + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | ✅ DIUBAH: Dari false ke true (lebih aman) + */ + + 'encrypt' => env('SESSION_ENCRYPT', false), + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + */ + + 'domain' => env('SESSION_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + */ + + 'secure' => env('SESSION_SECURE_COOKIE', false), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + */ + + 'http_only' => true, + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | ✅ SUDAH BENAR: 'lax' untuk menghindari CSRF strict issues + */ + + 'same_site' => env('SESSION_SAME_SITE', 'lax'), + + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + */ + + 'partitioned' => false, + +]; \ No newline at end of file diff --git a/sim-pkpps/config/view.php b/sim-pkpps/config/view.php new file mode 100644 index 0000000..22b8a18 --- /dev/null +++ b/sim-pkpps/config/view.php @@ -0,0 +1,36 @@ + [ + resource_path('views'), + ], + + /* + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | + */ + + 'compiled' => env( + 'VIEW_COMPILED_PATH', + realpath(storage_path('framework/views')) + ), + +]; diff --git a/sim-pkpps/database/.gitignore b/sim-pkpps/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/sim-pkpps/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/sim-pkpps/database/factories/UserFactory.php b/sim-pkpps/database/factories/UserFactory.php new file mode 100644 index 0000000..584104c --- /dev/null +++ b/sim-pkpps/database/factories/UserFactory.php @@ -0,0 +1,44 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/sim-pkpps/database/migrations/2014_10_12_000000_create_users_table.php b/sim-pkpps/database/migrations/2014_10_12_000000_create_users_table.php new file mode 100644 index 0000000..444fafb --- /dev/null +++ b/sim-pkpps/database/migrations/2014_10_12_000000_create_users_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + } +}; diff --git a/sim-pkpps/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php b/sim-pkpps/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php new file mode 100644 index 0000000..81a7229 --- /dev/null +++ b/sim-pkpps/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php @@ -0,0 +1,28 @@ +string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('password_reset_tokens'); + } +}; diff --git a/sim-pkpps/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/sim-pkpps/database/migrations/2019_08_19_000000_create_failed_jobs_table.php new file mode 100644 index 0000000..249da81 --- /dev/null +++ b/sim-pkpps/database/migrations/2019_08_19_000000_create_failed_jobs_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/sim-pkpps/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/sim-pkpps/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/sim-pkpps/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/sim-pkpps/database/migrations/2025_09_29_033444_create_santris_table.php b/sim-pkpps/database/migrations/2025_09_29_033444_create_santris_table.php new file mode 100644 index 0000000..476e5d2 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_09_29_033444_create_santris_table.php @@ -0,0 +1,48 @@ +id(); + + // ID KUSTOM Santri + $table->string('id_santri')->unique(); // Format S001, S002, ... + + // Data Utama + $table->string('nis')->unique()->nullable(); // Nomor Induk Santri + $table->string('nama_lengkap'); + $table->enum('jenis_kelamin', ['Laki-laki', 'Perempuan']); + $table->enum('kelas', ['PB', 'Lambatan', 'Cepatan']); // PB = Pembinaan + $table->enum('status', ['Aktif', 'Lulus', 'Tidak Aktif'])->default('Aktif'); + $table->text('alamat_santri')->nullable(); + $table->string('daerah_asal')->nullable(); + + // Data Wali Santri + $table->string('nama_orang_tua')->nullable(); + $table->string('nomor_hp_ortu')->nullable(); + + // Foreign key ke tabel walis (opsional, bisa ditambahkan nanti) + // $table->foreignId('wali_id')->nullable()->constrained('walis')->onDelete('set null'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('santris'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_09_29_034134_create_walis_table.php b/sim-pkpps/database/migrations/2025_09_29_034134_create_walis_table.php new file mode 100644 index 0000000..147b14e --- /dev/null +++ b/sim-pkpps/database/migrations/2025_09_29_034134_create_walis_table.php @@ -0,0 +1,35 @@ +id(); + // ID KUSTOM Wali Santri + $table->string('id_wali')->unique(); // Format WS001, WS002, ... + + $table->string('nama_wali'); + $table->string('nomor_hp'); + $table->text('alamat')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('walis'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_09_29_035958_add_role_and_role_id_to_users_table.php b/sim-pkpps/database/migrations/2025_09_29_035958_add_role_and_role_id_to_users_table.php new file mode 100644 index 0000000..6535a33 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_09_29_035958_add_role_and_role_id_to_users_table.php @@ -0,0 +1,46 @@ +enum('role', ['admin', 'santri', 'wali'])->default('admin'); + // role_id: ID kustom dari tabel santris (S001) atau walis (WS001) + $table->string('role_id')->nullable()->after('email'); + + // Tambahkan kolom untuk menghubungkan ke tabel santris/walis + // Akan diisi dengan id_santri atau id_wali + // Note: Kita gunakan string role_id karena FK nya berbeda tabel dan formatnya string (S001, WS001) + + // Santri/Wali akan menggunakan 'username' sebagai pengganti email untuk login + $table->string('username')->unique()->nullable()->after('name'); + + // Mengubah email menjadi nullable karena tidak semua santri/wali punya email + $table->string('email')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('role'); + $table->dropColumn('role_id'); + $table->dropColumn('username'); + // Kembalikan email ke non-nullable jika diperlukan + $table->string('email')->nullable(false)->change(); + }); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_20_035247_create_kesehatan_santris_table.php b/sim-pkpps/database/migrations/2025_10_20_035247_create_kesehatan_santris_table.php new file mode 100644 index 0000000..caade91 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_20_035247_create_kesehatan_santris_table.php @@ -0,0 +1,40 @@ +id(); + $table->string('id_kesehatan')->unique(); // K001, K002, dst + $table->string('id_santri'); // Relasi ke tabel santris + $table->date('tanggal_masuk'); + $table->date('tanggal_keluar')->nullable(); + $table->text('keluhan'); + $table->text('catatan')->nullable(); + $table->enum('status', ['dirawat', 'sembuh', 'izin'])->default('dirawat'); + $table->timestamps(); + + // Foreign key constraint + $table->foreign('id_santri') + ->references('id_santri') + ->on('santris') + ->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('kesehatan_santris'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_20_080216_create_kepulangan_table.php b/sim-pkpps/database/migrations/2025_10_20_080216_create_kepulangan_table.php new file mode 100644 index 0000000..f3d76db --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_20_080216_create_kepulangan_table.php @@ -0,0 +1,45 @@ +id(); + $table->string('id_kepulangan')->unique(); // KP001, KP002, dst + $table->string('id_santri'); // Relasi ke santri (S001, S002, dst) + $table->date('tanggal_izin'); // Tanggal pengajuan izin + $table->date('tanggal_pulang'); // Tanggal mulai pulang + $table->date('tanggal_kembali'); // Tanggal rencana kembali + $table->integer('durasi_izin'); // Durasi dalam hari + $table->text('alasan'); // Alasan kepulangan + $table->enum('status', ['Menunggu', 'Disetujui', 'Ditolak', 'Selesai'])->default('Menunggu'); + $table->string('approved_by')->nullable(); // Admin yang menyetujui + $table->timestamp('approved_at')->nullable(); // Waktu persetujuan + $table->text('catatan')->nullable(); // Catatan dari admin + $table->timestamps(); + + // Foreign key + $table->foreign('id_santri') + ->references('id_santri') + ->on('santris') + ->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('kepulangan'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_21_070405_create_berita_table.php b/sim-pkpps/database/migrations/2025_10_21_070405_create_berita_table.php new file mode 100644 index 0000000..dd5d9d0 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_21_070405_create_berita_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('id_berita')->unique(); // B001, B002, dst + $table->string('judul'); + $table->text('konten'); + $table->string('penulis'); + $table->string('gambar')->nullable(); + $table->enum('status', ['draft', 'published'])->default('draft'); + $table->enum('target_berita', ['semua', 'kelas_tertentu', 'santri_tertentu'])->default('semua'); + $table->json('target_kelas')->nullable(); // Untuk menyimpan array kelas + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('berita'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_21_070413_create_berita_santri_table.php b/sim-pkpps/database/migrations/2025_10_21_070413_create_berita_santri_table.php new file mode 100644 index 0000000..b3b562c --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_21_070413_create_berita_santri_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('id_berita'); + $table->string('id_santri'); + $table->boolean('sudah_dibaca')->default(false); + $table->timestamp('tanggal_baca')->nullable(); + $table->timestamps(); + + // Foreign keys + $table->foreign('id_berita')->references('id_berita')->on('berita')->onDelete('cascade'); + $table->foreign('id_santri')->references('id_santri')->on('santris')->onDelete('cascade'); + + // Unique constraint untuk mencegah duplikasi + $table->unique(['id_berita', 'id_santri']); + }); + } + + public function down(): void + { + Schema::dropIfExists('berita_santri'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_24_012324_create_kategori_pelanggarans_table.php b/sim-pkpps/database/migrations/2025_10_24_012324_create_kategori_pelanggarans_table.php new file mode 100644 index 0000000..387c5f1 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_24_012324_create_kategori_pelanggarans_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('id_kategori', 10)->unique()->comment('ID Kategori format KP001, KP002, dst'); + $table->string('nama_pelanggaran', 255)->comment('Nama jenis pelanggaran'); + $table->integer('poin')->comment('Poin pelanggaran (1-100)'); + $table->timestamps(); + + // Index untuk performa + $table->index('id_kategori'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('kategori_pelanggarans'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_24_013350_create_riwayat_pelanggarans_table.php b/sim-pkpps/database/migrations/2025_10_24_013350_create_riwayat_pelanggarans_table.php new file mode 100644 index 0000000..38b5339 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_24_013350_create_riwayat_pelanggarans_table.php @@ -0,0 +1,53 @@ +id(); + $table->string('id_riwayat', 10)->unique()->comment('ID Riwayat format P001, P002, dst'); + $table->string('id_santri', 10)->comment('ID Santri dari tabel santris'); + $table->string('id_kategori', 10)->comment('ID Kategori dari tabel kategori_pelanggarans'); + $table->date('tanggal')->comment('Tanggal terjadinya pelanggaran'); + $table->integer('poin')->comment('Poin pelanggaran (diambil dari kategori)'); + $table->text('keterangan')->nullable()->comment('Keterangan tambahan tentang pelanggaran'); + $table->timestamps(); + + // Foreign Key Constraints + $table->foreign('id_santri') + ->references('id_santri') + ->on('santris') + ->onDelete('cascade') + ->onUpdate('cascade'); + + $table->foreign('id_kategori') + ->references('id_kategori') + ->on('kategori_pelanggarans') + ->onDelete('cascade') + ->onUpdate('cascade'); + + // Index untuk performa query + $table->index('id_riwayat'); + $table->index('id_santri'); + $table->index('id_kategori'); + $table->index('tanggal'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('riwayat_pelanggarans'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_24_070612_create_pembayaran_spp_table.php b/sim-pkpps/database/migrations/2025_10_24_070612_create_pembayaran_spp_table.php new file mode 100644 index 0000000..c9dfabe --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_24_070612_create_pembayaran_spp_table.php @@ -0,0 +1,46 @@ +id(); + $table->string('id_pembayaran')->unique(); // SPP001, SPP002, dst + $table->string('id_santri'); // Foreign key ke tabel santris + $table->integer('bulan'); // 1-12 + $table->integer('tahun'); // 2024, 2025, dst + $table->decimal('nominal', 10, 2); // Nominal pembayaran + $table->enum('status', ['Lunas', 'Belum Lunas'])->default('Belum Lunas'); + $table->date('tanggal_bayar')->nullable(); // Tanggal pembayaran + $table->date('batas_bayar'); // Batas waktu pembayaran + $table->text('keterangan')->nullable(); // Catatan tambahan + $table->timestamps(); + + // Foreign key constraint + $table->foreign('id_santri') + ->references('id_santri') + ->on('santris') + ->onDelete('cascade'); + + // Unique constraint untuk mencegah duplikasi bulan & tahun per santri + $table->unique(['id_santri', 'bulan', 'tahun']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('pembayaran_spp'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_27_090620_create_uang_saku_table.php b/sim-pkpps/database/migrations/2025_10_27_090620_create_uang_saku_table.php new file mode 100644 index 0000000..463cab4 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_27_090620_create_uang_saku_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('id_uang_saku', 10)->unique(); // SK001, SK002, dst + $table->string('id_santri', 10); + $table->enum('jenis_transaksi', ['pemasukan', 'pengeluaran']); + $table->decimal('nominal', 15, 2); + $table->text('keterangan')->nullable(); + $table->date('tanggal_transaksi'); + $table->decimal('saldo_sebelum', 15, 2)->default(0); + $table->decimal('saldo_sesudah', 15, 2)->default(0); + $table->timestamps(); + + $table->foreign('id_santri')->references('id_santri')->on('santris')->onDelete('cascade'); + $table->index(['id_santri', 'tanggal_transaksi']); + }); + } + + public function down() + { + Schema::dropIfExists('uang_saku'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_28_092701_create_kategori_kegiatans_table.php b/sim-pkpps/database/migrations/2025_10_28_092701_create_kategori_kegiatans_table.php new file mode 100644 index 0000000..4c12450 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_28_092701_create_kategori_kegiatans_table.php @@ -0,0 +1,26 @@ +id(); + $table->string('kategori_id', 10)->unique(); // KT001, KT002... + $table->string('nama_kategori', 100); + $table->text('keterangan')->nullable(); + $table->timestamps(); + + $table->index('kategori_id'); + }); + } + + public function down(): void + { + Schema::dropIfExists('kategori_kegiatans'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_29_042339_create_kegiatans_table.php b/sim-pkpps/database/migrations/2025_10_29_042339_create_kegiatans_table.php new file mode 100644 index 0000000..af771a2 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_29_042339_create_kegiatans_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('kegiatan_id', 10)->unique(); // KG001, KG002... + $table->string('kategori_id', 10); + $table->string('nama_kegiatan', 150); + $table->enum('hari', ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu', 'Ahad']); + $table->time('waktu_mulai'); + $table->time('waktu_selesai'); + $table->string('materi', 200)->nullable(); + $table->text('keterangan')->nullable(); + $table->timestamps(); + + $table->index('kegiatan_id'); + $table->index('kategori_id'); + $table->index('hari'); + + $table->foreign('kategori_id') + ->references('kategori_id') + ->on('kategori_kegiatans') + ->onDelete('cascade'); + }); + } + + public function down(): void + { + Schema::dropIfExists('kegiatans'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_29_042902_create_absensi_kegiatans_table.php b/sim-pkpps/database/migrations/2025_10_29_042902_create_absensi_kegiatans_table.php new file mode 100644 index 0000000..ac87d80 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_29_042902_create_absensi_kegiatans_table.php @@ -0,0 +1,46 @@ +id(); + $table->string('absensi_id', 10)->unique(); // A001, A002... + $table->string('kegiatan_id', 10); + $table->string('id_santri', 10); + $table->date('tanggal'); + $table->enum('status', ['Hadir', 'Izin', 'Sakit', 'Alpa']); + $table->enum('metode_absen', ['Manual', 'RFID'])->default('Manual'); + $table->time('waktu_absen')->nullable(); + $table->timestamps(); + + $table->index('absensi_id'); + $table->index('kegiatan_id'); + $table->index('id_santri'); + $table->index('tanggal'); + + // Unique constraint: 1 santri hanya bisa absen 1x per kegiatan per hari + $table->unique(['kegiatan_id', 'id_santri', 'tanggal']); + + $table->foreign('kegiatan_id') + ->references('kegiatan_id') + ->on('kegiatans') + ->onDelete('cascade'); + + $table->foreign('id_santri') + ->references('id_santri') + ->on('santris') + ->onDelete('cascade'); + }); + } + + public function down(): void + { + Schema::dropIfExists('absensi_kegiatans'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_29_042912_add_rfid_to_santris_table.php b/sim-pkpps/database/migrations/2025_10_29_042912_add_rfid_to_santris_table.php new file mode 100644 index 0000000..3cc06d5 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_29_042912_add_rfid_to_santris_table.php @@ -0,0 +1,22 @@ +string('rfid_uid', 50)->nullable()->unique()->after('nomor_hp_ortu'); + }); + } + + public function down(): void + { + Schema::table('santris', function (Blueprint $table) { + $table->dropColumn('rfid_uid'); + }); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_31_064743_create_materi_table.php b/sim-pkpps/database/migrations/2025_10_31_064743_create_materi_table.php new file mode 100644 index 0000000..db5377b --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_31_064743_create_materi_table.php @@ -0,0 +1,39 @@ +id(); + $table->string('id_materi', 10)->unique()->comment('Format: M001, M002, dst'); + $table->string('kategori', 50)->index()->comment('Al-Quran, Hadist, Materi Tambahan'); + $table->enum('kelas', ['Lambatan', 'Cepatan', 'PB'])->index(); + $table->string('nama_kitab')->comment('Contoh: K. Sholah, Tafsir Jalalain'); + $table->integer('halaman_mulai')->unsigned(); + $table->integer('halaman_akhir')->unsigned(); + $table->integer('total_halaman')->unsigned()->comment('Auto-calculated'); + $table->text('deskripsi')->nullable(); + $table->timestamps(); + + // Index untuk optimasi query + $table->index(['kategori', 'kelas']); + $table->index('nama_kitab'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('materi'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_31_065514_create_semester_table.php b/sim-pkpps/database/migrations/2025_10_31_065514_create_semester_table.php new file mode 100644 index 0000000..0572bca --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_31_065514_create_semester_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('id_semester', 10)->unique()->comment('Format: SEM001, SEM002'); + $table->string('nama_semester')->comment('Contoh: Semester 1 2024/2025'); + $table->string('tahun_ajaran', 20)->comment('Contoh: 2024/2025')->index(); + $table->tinyInteger('periode')->comment('1 atau 2')->index(); + $table->date('tanggal_mulai'); + $table->date('tanggal_akhir'); + $table->boolean('is_active')->default(0)->comment('Hanya 1 semester yang aktif')->index(); + $table->timestamps(); + + // Index untuk optimasi + $table->index(['tahun_ajaran', 'periode']); + }); + } + + public function down(): void + { + Schema::dropIfExists('semester'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/migrations/2025_10_31_065538_create_capaian_table.php b/sim-pkpps/database/migrations/2025_10_31_065538_create_capaian_table.php new file mode 100644 index 0000000..809ac51 --- /dev/null +++ b/sim-pkpps/database/migrations/2025_10_31_065538_create_capaian_table.php @@ -0,0 +1,42 @@ +id(); + $table->string('id_capaian', 10)->unique()->comment('Format: CP001, CP002'); + $table->string('id_santri', 10)->comment('FK ke santri'); + $table->string('id_materi', 10)->comment('FK ke materi'); + $table->string('id_semester', 10)->comment('FK ke semester'); + $table->text('halaman_selesai')->comment('Format: 1-10,16-21,40,45-50'); + $table->decimal('persentase', 5, 2)->default(0)->comment('Auto-calculated'); + $table->text('catatan')->nullable(); + $table->date('tanggal_input'); + $table->timestamps(); + + // Foreign keys + $table->foreign('id_santri')->references('id_santri')->on('santris')->onDelete('cascade'); + $table->foreign('id_materi')->references('id_materi')->on('materi')->onDelete('cascade'); + $table->foreign('id_semester')->references('id_semester')->on('semester')->onDelete('cascade'); + + // Index untuk optimasi query + $table->index('id_santri'); + $table->index('id_materi'); + $table->index('id_semester'); + $table->index(['id_santri', 'id_semester']); + $table->index(['id_materi', 'id_semester']); + $table->index('persentase'); + }); + } + + public function down(): void + { + Schema::dropIfExists('capaian'); + } +}; \ No newline at end of file diff --git a/sim-pkpps/database/seeders/DatabaseSeeder.php b/sim-pkpps/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..a9f4519 --- /dev/null +++ b/sim-pkpps/database/seeders/DatabaseSeeder.php @@ -0,0 +1,22 @@ +create(); + + // \App\Models\User::factory()->create([ + // 'name' => 'Test User', + // 'email' => 'test@example.com', + // ]); + } +} diff --git a/sim-pkpps/package-lock.json b/sim-pkpps/package-lock.json new file mode 100644 index 0000000..018f5c3 --- /dev/null +++ b/sim-pkpps/package-lock.json @@ -0,0 +1,2881 @@ +{ + "name": "sim-pkpps", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@tailwindcss/forms": "^0.5.2", + "alpinejs": "^3.4.2", + "autoprefixer": "^10.4.2", + "axios": "^1.6.4", + "laravel-vite-plugin": "^1.0.0", + "postcss": "^8.4.31", + "tailwindcss": "^3.1.0", + "vite": "^5.0.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/alpinejs": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.0.tgz", + "integrity": "sha512-lpokA5okCF1BKh10LG8YjqhfpxyHBk4gE7boIgVHltJzYoM7O9nK3M7VlntLEJGsVmu7U/RzUWajmHREGT38Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", + "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001745", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", + "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.227", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", + "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/laravel-vite-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.3.0.tgz", + "integrity": "sha512-P5qyG56YbYxM8OuYmK2OkhcKe0AksNVJUjq9LUZ5tOekU9fBn9LujYyctI4t9XoLjuMvHJXXpCoPntY1oKltuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "vite-plugin-full-reload": "^1.1.0" + }, + "bin": { + "clean-orphaned-assets": "bin/clean.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.20", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", + "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-full-reload": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz", + "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "picomatch": "^2.3.1" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + } + } +} diff --git a/sim-pkpps/package.json b/sim-pkpps/package.json new file mode 100644 index 0000000..31208d1 --- /dev/null +++ b/sim-pkpps/package.json @@ -0,0 +1,18 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.2", + "alpinejs": "^3.4.2", + "autoprefixer": "^10.4.2", + "axios": "^1.6.4", + "laravel-vite-plugin": "^1.0.0", + "postcss": "^8.4.31", + "tailwindcss": "^3.1.0", + "vite": "^5.0.0" + } +} diff --git a/sim-pkpps/phpunit.xml b/sim-pkpps/phpunit.xml new file mode 100644 index 0000000..bc86714 --- /dev/null +++ b/sim-pkpps/phpunit.xml @@ -0,0 +1,32 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + diff --git a/sim-pkpps/postcss.config.js b/sim-pkpps/postcss.config.js new file mode 100644 index 0000000..49c0612 --- /dev/null +++ b/sim-pkpps/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/sim-pkpps/public/.htaccess b/sim-pkpps/public/.htaccess new file mode 100644 index 0000000..3aec5e2 --- /dev/null +++ b/sim-pkpps/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/sim-pkpps/public/css/app.css b/sim-pkpps/public/css/app.css new file mode 100644 index 0000000..15e65f6 --- /dev/null +++ b/sim-pkpps/public/css/app.css @@ -0,0 +1,1818 @@ +/* =================================== + OPTIMIZED CSS - Eucalyptus Green Theme + Performance improvements: + - Reduced duplicate selectors + - Combined similar rules + - Used CSS shorthand properties + - Optimized animations + - Better organization for faster parsing + =================================== */ + +/* =================================== + 1. CSS VARIABLES (Critical - Load First) + =================================== */ +:root { + /* Primary Colors */ + --primary-color: #6FBA9D; + --primary-dark: #5EA98C; + --primary-light: #E8F7F2; + --secondary-color: #FF8B94; + --secondary-dark: #E77580; + + /* Sidebar */ + --sidebar-bg: linear-gradient(180deg, #6FBA9D 0%, #8FCAAE 100%); + --sidebar-bg-solid: #6FBA9D; + --sidebar-text: #FFFFFF; + --sidebar-hover: #5EA98C; + --sidebar-active: #4D987B; + + /* Text & Background */ + --text-color: #2C3E50; + --text-light: #7F8C8D; + --bg-color: #F8FBF9; + --white: #FFFFFF; + + /* Accents */ + --accent-yellow: #FFD56B; + --accent-peach: #FFAB91; + --accent-lavender: #B39DDB; + + /* Status Colors */ + --danger-color: #FF8B94; + --warning-color: #FFD56B; + --info-color: #81C6E8; + --success-color: #6FBA9D; + + /* Shadows & Borders */ + --shadow-sm: 0 2px 4px rgba(111, 186, 157, 0.08); + --shadow-md: 0 4px 6px rgba(111, 186, 157, 0.12); + --shadow-lg: 0 10px 25px rgba(111, 186, 157, 0.18); + --border-radius: 12px; + --border-radius-sm: 8px; + + /* Transitions (Centralized) */ + --transition-base: all 0.3s ease; + --transition-fast: all 0.2s ease; +} + +/* =================================== + 2. BASE STYLES & RESET + =================================== */ +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font: 15px/1.6 'Inter', 'Segoe UI', 'Roboto', sans-serif; + background-color: var(--bg-color); + color: var(--text-color); + scroll-behavior: smooth; +} + +/* =================================== + 3. SPLASH SCREEN + =================================== */ +.splash-screen { + position: fixed; + inset: 0; + background: linear-gradient(135deg, #6FBA9D 0%, #FF8B94 100%); + color: white; + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; + opacity: 1; + transition: opacity 0.5s ease-out; +} + +.splash-content { + text-align: center; +} + +.splash-content h1 { + font: 700 3rem/1.2 inherit; + margin-bottom: 1rem; + letter-spacing: -1px; +} + +.spinner, +.loading-spinner { + border: 4px solid rgba(255, 255, 255, 0.2); + border-top-color: #fff; + border-radius: 50%; + width: 50px; + height: 50px; + animation: spin 0.8s linear infinite; + margin: 20px auto 0; +} + +.loading-spinner { + border-color: var(--primary-light); + border-top-color: var(--primary-color); + width: 40px; + height: 40px; + animation-duration: 1s; + margin: 0; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* =================================== + 4. LAYOUT + =================================== */ +.app-wrapper { + display: flex; + min-height: 100vh; +} + +/* =================================== + 5. SIDEBAR + =================================== */ +.sidebar { + width: 260px; + background: var(--sidebar-bg); + color: var(--sidebar-text); + transition: var(--transition-base); + flex-shrink: 0; + box-shadow: 4px 0 15px rgba(111, 186, 157, 0.15); + position: relative; + z-index: 100; +} + +.sidebar-header { + padding: 30px 20px; + text-align: center; + border-bottom: 1px solid rgba(255, 255, 255, 0.15); + background: rgba(255, 255, 255, 0.05); +} + +.sidebar-header h3 { + font: 700 1.6rem/1 inherit; + margin: 0; + letter-spacing: 0.5px; + color: #FFFFFF; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.sidebar-menu { + list-style: none; + padding: 20px 0; +} + +.sidebar-menu li { + margin: 0; +} + +.sidebar-menu li a { + display: flex; + align-items: center; + padding: 14px 20px; + color: var(--sidebar-text); + text-decoration: none; + transition: var(--transition-base); + font-size: 0.95rem; + position: relative; + border-left: 3px solid transparent; +} + +.sidebar-menu li a i { + margin-right: 12px; + width: 22px; + text-align: center; + font-size: 1.1rem; +} + +.sidebar-menu li a:hover { + background-color: var(--sidebar-hover); + border-left-color: var(--accent-yellow); + padding-left: 25px; + box-shadow: inset 4px 0 8px rgba(0, 0, 0, 0.1); +} + +.sidebar-menu li a.active { + background-color: var(--sidebar-active); + border-left-color: var(--accent-yellow); + font-weight: 600; + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.15); +} + +/* Submenu */ +.sidebar-menu .submenu { + list-style: none; + padding: 0; + max-height: 0; + overflow: hidden; + transition: max-height 0.4s ease; + background-color: rgba(0, 0, 0, 0.1); +} + +.menu-toggle.active > .submenu { + max-height: 600px; +} + +.sidebar-menu .submenu li a { + padding-left: 50px; + font-size: 0.9rem; + border-left: none; +} + +.sidebar-menu .submenu li a:hover { + padding-left: 55px; + background-color: rgba(255, 255, 255, 0.08); +} + +.sidebar-menu .menu-parent { + display: flex; + justify-content: space-between; + align-items: center; +} + +.sidebar-menu .toggle-icon { + transition: transform 0.3s ease; + font-size: 0.8rem; +} + +.menu-toggle.active .toggle-icon { + transform: rotate(-180deg); +} + +/* Sidebar Collapsed */ +.sidebar.collapsed { + width: 70px; +} + +.sidebar.collapsed .sidebar-header h3, +.sidebar.collapsed .sidebar-menu li a span { + display: none; +} + +.sidebar.collapsed .sidebar-header { + padding: 25px 0; +} + +.sidebar.collapsed .sidebar-menu li a { + justify-content: center; + padding: 14px 0; + border-left: none; +} + +.sidebar.collapsed .sidebar-menu li a:hover { + padding-left: 0; +} + +.sidebar.collapsed .sidebar-menu li a i { + margin-right: 0; +} + +.sidebar.collapsed .toggle-icon { + display: none; +} + +.sidebar.collapsed .submenu { + position: absolute; + left: 70px; + width: 200px; + background-color: var(--sidebar-bg-solid); + box-shadow: 4px 0 15px rgba(0, 0, 0, 0.2); + z-index: 1000; +} + +.sidebar-menu .logout-item { + border-top: 1px solid rgba(255, 255, 255, 0.15); + margin-top: 20px; +} + +.sidebar-toggle-btn-mobile { + display: none; + background: none; + border: none; + font-size: 1.3rem; + cursor: pointer; + color: var(--sidebar-text); + position: absolute; + top: 25px; + right: 20px; + z-index: 1001; +} + +/* =================================== + 6. MAIN CONTENT + =================================== */ +.main-content-wrapper { + flex-grow: 1; + display: flex; + flex-direction: column; + min-height: 100vh; + transition: margin-left 0.3s ease; +} + +.main-header { + background: linear-gradient(135deg, #FFFFFF 0%, #F8FBF9 100%); + padding: 18px 30px; + box-shadow: var(--shadow-sm); + display: flex; + justify-content: space-between; + align-items: center; + position: sticky; + top: 0; + z-index: 50; + border-bottom: 2px solid var(--primary-light); +} + +.sidebar-toggle-btn { + background: none; + border: none; + font-size: 1.4rem; + cursor: pointer; + color: var(--primary-color); + padding: 8px; + border-radius: var(--border-radius-sm); + transition: var(--transition-base); +} + +.sidebar-toggle-btn:hover { + background-color: var(--primary-light); + color: var(--primary-dark); + transform: scale(1.1); +} + +.main-content { + padding: 30px; + flex-grow: 1; +} + +/* =================================== + 7. PAGE HEADER + =================================== */ +.page-header { + margin-bottom: 30px; + padding-bottom: 15px; + border-bottom: 3px solid; + border-image: linear-gradient(90deg, var(--primary-color), var(--secondary-color)) 1; +} + +.page-header h2 { + font: 700 1.8rem/1 inherit; + margin: 0; + background: linear-gradient(135deg, var(--primary-color), var(--primary-dark)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* =================================== + 8. CARDS + =================================== */ +.row-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 24px; + margin-bottom: 30px; +} + +.card { + background: linear-gradient(135deg, #FFFFFF 0%, #FEFFFE 100%); + border-radius: var(--border-radius); + box-shadow: var(--shadow-sm); + padding: 24px; + display: flex; + flex-direction: column; + position: relative; + overflow: hidden; + transition: var(--transition-base); + border: 2px solid transparent; +} + +.card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 4px; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + opacity: 0; + transition: opacity 0.3s ease; +} + +.card:hover { + box-shadow: var(--shadow-lg); + transform: translateY(-5px); + border-color: var(--primary-light); +} + +.card:hover::before { + opacity: 1; +} + +.card h3 { + margin: 0 0 8px 0; + font: 600 0.95rem/1 inherit; + color: var(--text-light); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.card-value { + font: 700 2.5rem/1 inherit; + margin: 12px 0; + color: var(--text-color); +} + +.card-value-small { + font: 700 1.8rem/1 inherit; + margin: 12px 0; + color: var(--text-color); +} + +.card-icon { + position: absolute; + top: 20px; + right: 20px; + font-size: 3.5rem; + opacity: 0.1; +} + +/* Card Color Variants (Optimized) */ +.card-info { border-left: 4px solid var(--info-color); } +.card-info .card-icon, +.card-info .card-value { color: var(--info-color); } + +.card-success { border-left: 4px solid var(--success-color); } +.card-success .card-icon, +.card-success .card-value { color: var(--success-color); } + +.card-warning { border-left: 4px solid var(--warning-color); } +.card-warning .card-icon { color: var(--warning-color); } +.card-warning .card-value { color: #E6B85C; } + +.card-danger { border-left: 4px solid var(--danger-color); } +.card-danger .card-icon, +.card-danger .card-value { color: var(--danger-color); } + +.card-primary { border-left: 4px solid var(--primary-color); } +.card-primary .card-icon, +.card-primary .card-value { color: var(--primary-color); } + +.card-secondary { border-left: 4px solid var(--secondary-color); } +.card-secondary .card-icon, +.card-secondary .card-value { color: var(--secondary-color); } + +/* =================================== + 9. CONTENT BOX + =================================== */ +.content-box { + background-color: var(--white); + padding: 24px; + border-radius: var(--border-radius); + box-shadow: var(--shadow-sm); + border: 1px solid var(--primary-light); +} + +/* =================================== + 10. ALERTS + =================================== */ +.alert { + padding: 16px 20px; + margin-bottom: 20px; + border-radius: var(--border-radius-sm); + border-left: 4px solid; + display: flex; + align-items: center; + animation: slideInDown 0.4s ease; +} + +.alert-success { + color: #2C5F4F; + background: linear-gradient(135deg, #E8F7F2 0%, #D4F1E3 100%); + border-color: var(--success-color); +} + +.alert-danger { + color: #7C2D35; + background: linear-gradient(135deg, #FFE8EA 0%, #FFD5D8 100%); + border-color: var(--danger-color); +} + +.alert-warning { + color: #7C6A2D; + background: linear-gradient(135deg, #FFF8E1 0%, #FFF3CD 100%); + border-color: var(--warning-color); +} + +.alert-info { + color: #2D4A7C; + background: linear-gradient(135deg, #E3F2FD 0%, #D1E9F9 100%); + border-color: var(--info-color); +} + +@keyframes slideInDown { + from { + opacity: 0; + transform: translateY(-20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* =================================== + 11. FORMS + =================================== */ +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: flex; + align-items: center; + margin-bottom: 8px; + font: 600 0.9rem/1 inherit; + color: var(--text-color); +} + +.form-icon { + margin-right: 8px; + color: var(--primary-color); +} + +.form-control { + width: 100%; + padding: 12px 16px; + border: 2px solid #E0F0EC; + border-radius: var(--border-radius-sm); + font-size: 0.95rem; + transition: var(--transition-base); + background-color: var(--white); + color: var(--text-color); +} + +.form-control:focus { + border-color: var(--primary-color); + outline: none; + box-shadow: 0 0 0 4px rgba(111, 186, 157, 0.15); + background-color: var(--primary-light); +} + +.form-control.is-invalid { + border-color: var(--danger-color); +} + +.invalid-feedback { + display: block; + margin-top: 6px; + font-size: 0.85rem; + color: var(--danger-color); +} + +textarea.form-control { + resize: vertical; + min-height: 100px; +} + +.form-text { + font-size: 0.85rem; + color: var(--text-light); + margin-top: 5px; + display: block; +} + +.form-container { + background: white; + border-radius: var(--border-radius); + padding: 30px; + box-shadow: var(--shadow-sm); + border: 1px solid var(--primary-light); +} + +/* =================================== + 12. BUTTONS + =================================== */ +.btn { + padding: 12px 24px; + border: none; + border-radius: var(--border-radius-sm); + cursor: pointer; + font: 600 0.95rem/1 inherit; + text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; + transition: var(--transition-base); + box-shadow: var(--shadow-sm); + position: relative; + overflow: hidden; +} + +.btn::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.3); + transform: translate(-50%, -50%); + transition: width 0.6s, height 0.6s; +} + +.btn:hover::before { + width: 300px; + height: 300px; +} + +.btn i { + margin-right: 6px; +} + +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--primary-dark)); + color: white; +} + +.btn-primary:hover, +.btn-success:hover { + box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +.btn-primary:hover { + background: linear-gradient(135deg, var(--primary-dark), #4D987B); +} + +.btn-success { + background: linear-gradient(135deg, var(--success-color), #5EA98C); + color: white; +} + +.btn-success:hover { + background: linear-gradient(135deg, #5EA98C, #4D987B); +} + +.btn-warning { + background: linear-gradient(135deg, var(--warning-color), var(--accent-peach)); + color: #7C6A2D; +} + +.btn-warning:hover, +.btn-danger:hover { + box-shadow: var(--shadow-md); +} + +.btn-warning:hover { + background: linear-gradient(135deg, var(--accent-peach), #FF9F80); +} + +.btn-danger { + background: linear-gradient(135deg, var(--danger-color), #FF6B7A); + color: white; +} + +.btn-danger:hover { + background: linear-gradient(135deg, #FF6B7A, var(--secondary-dark)); +} + +.btn-secondary { + background: linear-gradient(135deg, #E8ECF0, #D1D8E0); + color: var(--text-color); +} + +.btn-secondary:hover { + background: linear-gradient(135deg, #D1D8E0, #BDC6CF); +} + +.btn-sm { + padding: 8px 16px; + font-size: 0.85rem; +} + +.btn-lg { + padding: 15px 30px; + font-size: 1.1em; +} + +.hover-shadow { + transition: var(--transition-base); +} + +.hover-shadow:hover, +.hover-lift:hover { + box-shadow: var(--shadow-lg); + transform: translateY(-2px); +} + +.hover-lift { + transition: var(--transition-base); +} + +.hover-lift:hover { + transform: translateY(-3px); +} + +.btn-group { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +/* =================================== + 13. TABLES + =================================== */ +.data-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + margin-top: 15px; + background-color: var(--white); + box-shadow: var(--shadow-sm); + border-radius: var(--border-radius); + overflow: hidden; +} + +.data-table th, +.data-table td { + padding: 16px 20px; + text-align: left; + border-bottom: 1px solid #E8F7F2; +} + +.data-table th { + background: linear-gradient(135deg, #E8F7F2 0%, #D4F1E3 100%); + font: 700 0.85rem/1 inherit; + color: var(--primary-dark); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.data-table tbody tr { + transition: var(--transition-fast); +} + +.data-table tbody tr:hover { + background: linear-gradient(135deg, #FEFFFF 0%, #F8FBF9 100%); + box-shadow: inset 0 0 0 2px var(--primary-light); +} + +.data-table td.text-center { + text-align: center; +} + +/* Detail Table */ +.detail-table { + width: 100%; + border-collapse: collapse; + background-color: var(--white); + border-radius: var(--border-radius-sm); + overflow: hidden; + box-shadow: var(--shadow-sm); +} + +.detail-table tr { + border-bottom: 1px solid var(--primary-light); + transition: background-color 0.2s ease; +} + +.detail-table tr:last-child { + border-bottom: none; +} + +.detail-table tr:hover { + background-color: #FEFFFF; +} + +.detail-table th, +.detail-table td { + padding: 14px 16px; + text-align: left; +} + +.detail-table th { + background: linear-gradient(135deg, #E8F7F2 0%, #D4F1E3 100%); + font: 600 0.9rem/1 inherit; + color: var(--primary-dark); + width: 200px; +} + +.detail-table td { + color: var(--text-color); + font-size: 0.95rem; +} + +/* SPP Table Row Highlight */ +tr.spp-telat { + background: linear-gradient(135deg, #FFF8F9 0%, #FFF3F4 100%) !important; +} + +tr.spp-telat:hover { + background: linear-gradient(135deg, #FFE8EA 0%, #FFD5D8 100%) !important; +} + +.content-header-flex { + display: flex; + justify-content: flex-end; + margin-bottom: 20px; +} + +.table-container { + background: white; + border-radius: var(--border-radius); + padding: 20px; + box-shadow: var(--shadow-sm); + border: 1px solid var(--primary-light); +} + +/* =================================== + 14. DETAIL SECTIONS + =================================== */ +.detail-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + flex-wrap: wrap; + gap: 15px; +} + +.detail-header h3 { + margin: 0; + color: var(--text-color); + font: 700 1.4rem/1 inherit; +} + +.detail-section { + margin-bottom: 30px; +} + +.detail-section h4 { + color: var(--primary-dark); + border-bottom: 2px solid var(--primary-color); + padding-bottom: 10px; + margin-bottom: 15px; + font: 700 1.1rem/1 inherit; + display: flex; + align-items: center; +} + +.detail-section h4 i { + margin-right: 10px; + color: var(--primary-color); +} + +/* =================================== + 15. BADGES + =================================== */ +.badge { + padding: 6px 12px; + border-radius: var(--border-radius-sm); + font: 600 0.85em/1 inherit; + display: inline-flex; + align-items: center; + gap: 5px; +} + +.badge-success { + background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); + color: #155724; + border: 1px solid #c3e6cb; +} + +.badge-warning { + background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%); + color: #856404; + border: 1px solid #ffeaa7; +} + +.badge-primary, +.badge-info { + background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%); + color: #0c5460; + border: 1px solid #bee5eb; +} + +.badge-danger { + background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%); + color: #721c24; + border: 1px solid #f5c6cb; +} + +.badge-secondary { + background: linear-gradient(135deg, #e2e3e5 0%, #d6d8db 100%); + color: #383d41; + border: 1px solid #d6d8db; +} + +.badge-lg { + padding: 10px 18px; + font-size: 1em; +} + +/* =================================== + 16. AUTH PAGES + =================================== */ +.logo-circle { + width: 70px; + height: 70px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + margin: 0 auto 20px; + box-shadow: 0 8px 20px rgba(111, 186, 157, 0.3); +} + +.logo-circle i { + color: white; + font-size: 2rem; +} + +.auth-header h2 { + font-weight: 700; + color: var(--text-color); + margin-bottom: 8px; +} + +.auth-container { + border: 2px solid var(--primary-light); + background: var(--white); + border-radius: var(--border-radius); + box-shadow: var(--shadow-md); + transition: var(--transition-base); +} + +.auth-container:hover { + box-shadow: var(--shadow-lg); + border-color: var(--primary-color); +} + +.link-primary { + color: var(--primary-color); + text-decoration: none; + font-weight: 600; + transition: color 0.2s; +} + +.link-primary:hover { + color: var(--primary-dark); + text-decoration: underline; +} + +/* =================================== + 17. SPECIALIZED COMPONENTS + =================================== */ +.spp-telat-indicator { + display: inline-block; + padding: 4px 8px; + border-radius: 4px; + background: linear-gradient(135deg, #FFE8EA 0%, #FFD5D8 100%); + color: #7C2D35; + font: 600 0.8rem/1 inherit; + margin-left: 8px; +} + +.nominal-highlight { + color: var(--primary-color); + font: 700 1.1rem/1 inherit; +} + +.status-cell { + white-space: nowrap; +} + +.info-box { + background: linear-gradient(135deg, #E3F2FD 0%, #D1E9F9 100%); + padding: 20px; + border-radius: var(--border-radius-sm); + border-left: 4px solid var(--info-color); + margin: 20px 0; +} + +.info-box p { + margin: 0; + color: var(--text-color); + line-height: 1.6; +} + +.info-box i { + margin-right: 8px; + color: var(--info-color); +} + +.filter-form-inline { + display: flex; + gap: 10px; + flex-wrap: wrap; + align-items: center; +} + +.filter-form-inline .form-control { + flex: 0 1 auto; + min-width: 150px; +} + +/* Berita & Grid Components */ +.berita-card { + background: white; + border-radius: var(--border-radius); + padding: 20px; + box-shadow: var(--shadow-sm); + transition: var(--transition-base); + border: 2px solid transparent; +} + +.berita-card:hover { + box-shadow: var(--shadow-lg); + transform: translateY(-3px); + border-color: var(--primary-light); +} + +.santri-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 12px; +} + +.santri-item { + background: white; + padding: 12px; + border-radius: var(--border-radius-sm); + box-shadow: var(--shadow-sm); + transition: var(--transition-fast); + cursor: pointer; +} + +.santri-item:hover { + box-shadow: var(--shadow-md); + background: var(--primary-light); +} + +.santri-item input[type="checkbox"]:checked + label { + background: var(--primary-light); +} + +.kelas-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 15px; +} + +/* Progress Bar */ +.progress-bar { + background-color: #e0e0e0; + border-radius: 20px; + height: 12px; + overflow: hidden; + position: relative; +} + +.progress-fill { + height: 100%; + border-radius: 20px; + transition: width 0.5s ease; +} + +/* Content Preview */ +.content-preview { + max-width: 250px; + max-height: 3em; + line-height: 1.5em; + overflow: hidden; + text-overflow: ellipsis; + white-space: normal; + word-wrap: break-word; +} + +/* Santri Avatar */ +.santri-avatar, +.santri-avatar-initial { + width: 40px; + height: 40px; + border-radius: 50%; + flex-shrink: 0; +} + +.santri-avatar { + object-fit: cover; + border: 2px solid var(--primary-color); +} + +.santri-avatar-initial { + background: var(--primary-color); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: bold; +} + +.santri-avatar-lg { + width: 60px; + height: 60px; + border: 3px solid var(--primary-color); +} + +.santri-avatar-initial-lg { + width: 60px; + height: 60px; + font-size: 1.5em; +} + +/* Image Preview */ +.image-preview { + max-width: 200px; + max-height: 150px; + border-radius: var(--border-radius-sm); + border: 2px solid var(--primary-light); + margin-top: 8px; + object-fit: cover; +} + +/* =================================== + 18. PAGINATION + =================================== */ +.pagination { + display: flex; + justify-content: center; + align-items: center; + gap: 5px; + margin-top: 20px; + flex-wrap: wrap; +} + +.pagination a, +.pagination span { + padding: 8px 12px; + border: 1px solid var(--primary-light); + border-radius: var(--border-radius-sm); + color: var(--primary-color); + text-decoration: none; + transition: var(--transition-base); + display: inline-block; +} + +.pagination a:hover { + background: var(--primary-color); + color: white; + transform: translateY(-2px); + box-shadow: var(--shadow-sm); +} + +.pagination .active span { + background: var(--primary-color); + color: white; + border-color: var(--primary-color); + font-weight: 600; +} + +.pagination .disabled span { + color: var(--text-light); + cursor: not-allowed; + opacity: 0.5; +} + +/* =================================== + 19. EMPTY STATE & LOADING + =================================== */ +.empty-state { + text-align: center; + padding: 60px 20px; + color: var(--text-light); +} + +.empty-state i { + font-size: 4em; + color: #ccc; + margin-bottom: 20px; + display: block; +} + +.empty-state h3 { + color: var(--text-light); + margin-bottom: 15px; + font-size: 1.3rem; +} + +.empty-state p { + color: var(--text-light); + margin-bottom: 25px; + max-width: 500px; + margin-left: auto; + margin-right: auto; +} + +.loading { + display: flex; + justify-content: center; + align-items: center; + padding: 40px; +} + +.generating-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.7); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; +} + +.generating-content { + background: white; + padding: 40px; + border-radius: var(--border-radius); + text-align: center; + box-shadow: var(--shadow-lg); +} + +.generating-spinner { + border: 4px solid var(--primary-light); + border-top-color: var(--primary-color); + border-radius: 50%; + width: 50px; + height: 50px; + animation: spin 1s linear infinite; + margin: 0 auto 20px; +} + +/* =================================== + 20. UTILITIES + =================================== */ +.text-muted { + color: var(--text-light) !important; +} + +.text-center { + text-align: center !important; +} + +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--primary-dark)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.header-section { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 25px; + flex-wrap: wrap; + gap: 15px; +} + +.header-section h1 { + color: var(--primary-color); + font: 700 1.8rem/1 inherit; + margin: 0; +} + +.header-section p { + color: var(--text-light); + margin: 5px 0 0 0; +} + +.icon-wrapper { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + background: var(--primary-light); + color: var(--primary-color); + margin-right: 10px; +} + +.icon-wrapper-lg { + width: 60px; + height: 60px; + font-size: 1.5em; +} + +/* Checkbox Styling */ +input[type="checkbox"] { + cursor: pointer; + accent-color: var(--primary-color); +} + +input[type="checkbox"]:hover { + transform: scale(1.1); +} + +#select-all { + width: 20px; + height: 20px; +} + +/* Success Animation */ +@keyframes successPulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.05); } +} + +.success-animation { + animation: successPulse 0.5s ease; +} + +/* Scrollbar Custom */ +.santri-section::-webkit-scrollbar { + width: 8px; +} + +.santri-section::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 10px; +} + +.santri-section::-webkit-scrollbar-thumb { + background: var(--primary-color); + border-radius: 10px; +} + +.santri-section::-webkit-scrollbar-thumb:hover { + background: var(--primary-dark); +} + +/* =================================== + 21. RESPONSIVE DESIGN + =================================== */ + +/* Desktop Large (1024px+) - Already optimized above */ + +/* Tablet (768px - 1023px) */ +@media (max-width: 1024px) { + .row-cards { + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + } + + .main-content { + padding: 25px; + } +} + +/* Tablet & Mobile (max-width: 768px) */ +@media (max-width: 768px) { + /* Sidebar Mobile */ + .sidebar { + position: fixed; + left: -260px; + height: 100vh; + z-index: 1000; + box-shadow: 4px 0 20px rgba(111, 186, 157, 0.3); + } + + .sidebar.mobile-active { + left: 0; + } + + .sidebar-toggle-btn-mobile { + display: block; + } + + .sidebar.collapsed { + width: 260px; + left: -260px; + } + + .sidebar.collapsed.mobile-active { + left: 0; + } + + .main-content-wrapper { + margin-left: 0; + } + + /* Header & Content */ + .main-header { + padding: 15px 20px; + } + + .main-content { + padding: 20px; + } + + .page-header h2 { + font-size: 1.5rem; + } + + /* Cards */ + .row-cards { + grid-template-columns: 1fr; + gap: 16px; + } + + .card-value { + font-size: 2rem; + } + + /* Tables */ + .data-table { + font-size: 0.9rem; + } + + .data-table th, + .data-table td { + padding: 12px; + } + + /* Detail Components */ + .detail-header { + flex-direction: column; + align-items: flex-start; + } + + .detail-header h3 { + font-size: 1.2rem; + } + + .detail-table th { + width: 120px; + font-size: 0.85rem; + padding: 12px; + } + + .detail-table td { + font-size: 0.9rem; + padding: 12px; + } + + .detail-section h4 { + font-size: 1rem; + } + + /* Auth Pages */ + .auth-container { + margin: 15px; + padding: 20px; + } + + .auth-header h2 { + font-size: 1.4rem; + } + + .logo-circle { + width: 60px; + height: 60px; + } + + /* Forms */ + .filter-form-inline { + flex-direction: column; + align-items: stretch; + } + + .filter-form-inline .form-control { + width: 100%; + max-width: 100%; + } + + .filter-form-inline .btn { + width: 100%; + justify-content: center; + } + + /* Content Box */ + .content-box > div:first-child { + flex-direction: column; + align-items: stretch !important; + } + + .content-box form { + width: 100%; + } + + .content-box form input[type="text"], + .content-box form select { + width: 100% !important; + max-width: 100% !important; + } + + .content-box form button, + .content-box form a { + width: 48%; + justify-content: center; + } + + /* Grids */ + .santri-grid { + grid-template-columns: 1fr; + gap: 10px; + } + + .kelas-grid { + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 10px; + } + + .berita-card { + padding: 15px; + } + + .badge { + padding: 4px 8px; + font-size: 0.75em; + } + + /* Button Group */ + .btn-group { + flex-direction: column; + width: 100%; + } + + .btn-group .btn { + width: 100%; + justify-content: center; + } + + /* Header Section */ + .header-section { + flex-direction: column; + align-items: flex-start; + } + + .header-section h1 { + font-size: 1.5rem; + } + + /* Pagination */ + .pagination { + font-size: 0.85rem; + } + + .pagination a, + .pagination span { + padding: 6px 10px; + } +} + +/* Mobile Small (max-width: 480px) */ +@media (max-width: 480px) { + .main-content { + padding: 15px; + } + + /* Buttons */ + .btn { + padding: 10px 18px; + font-size: 0.9rem; + } + + .card { + padding: 20px; + } + + .form-control { + padding: 10px 14px; + } + + /* Tables - Stack Layout */ + .detail-table, + .data-table { + font-size: 0.85rem; + } + + .detail-table th, + .detail-table td { + display: block; + width: 100%; + } + + .detail-table th { + background: var(--primary-color); + color: white; + border-bottom: none; + padding-bottom: 8px; + } + + .detail-table td { + padding-top: 8px; + padding-bottom: 15px; + border-bottom: 2px solid var(--primary-light); + } + + .detail-table tr { + display: block; + margin-bottom: 15px; + border: 1px solid var(--primary-light); + border-radius: var(--border-radius-sm); + overflow: hidden; + } + + .data-table th, + .data-table td { + padding: 10px 8px; + } + + /* Hide less important columns */ + .data-table th:nth-child(2), + .data-table td:nth-child(2) { + display: none; + } + + /* Detail Header */ + .detail-header { + flex-direction: column; + align-items: flex-start !important; + } + + .detail-header h3 { + font-size: 1.2rem; + } + + .detail-header > div:last-child { + width: 100%; + flex-direction: column; + } + + .detail-header > div:last-child .btn { + width: 100%; + justify-content: center; + } + + /* Content Box */ + .content-box form button, + .content-box form a { + width: 100%; + } + + /* Grids */ + .santri-grid { + gap: 8px; + } + + .santri-item { + padding: 10px; + } + + .kelas-grid { + grid-template-columns: 1fr; + } +} + +/* =================================== + 22. PRINT STYLES + =================================== */ +@media print { + .sidebar, + .main-header, + .btn, + .page-header, + .pagination, + .sidebar-toggle-btn, + .sidebar-toggle-btn-mobile { + display: none !important; + } + + .main-content { + padding: 0; + } + + .content-box { + box-shadow: none; + border: 1px solid #ddd; + } + + .data-table { + page-break-inside: avoid; + } + + body { + background: white; + } + + /* =================================== + UANG SAKU - ADDITIONAL STYLES + =================================== */ + +/* Chart Container */ +#chartUangSaku { + animation: fadeInUp 0.8s ease; +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Nominal Formatting */ +.nominal-highlight { + font-weight: 700; + font-size: 1.05rem; +} + +/* Transaction Type Icons */ +.badge i { + margin-right: 4px; +} + +/* Responsive Chart */ +@media (max-width: 768px) { + #chartUangSaku { + max-height: 300px !important; + } + + /* Responsive untuk Dashboard Santri */ +@media (max-width: 768px) { + /* Cards Statistik */ + .row-cards { + grid-template-columns: 1fr; + gap: 15px; + } + + /* Quick Links Grid */ + .content-box > div[style*="grid"] { + grid-template-columns: repeat(2, 1fr) !important; + gap: 10px !important; + } + + /* Alert Kesehatan */ + .alert { + font-size: 0.9rem; + padding: 12px 15px; + } + + /* Berita Terbaru */ + .content-box a[href*="berita.show"] { + padding: 12px !important; + } + + .content-box a[href*="berita.show"] h4 { + font-size: 0.85rem !important; + } +} + +@media (max-width: 480px) { + /* Quick Links Grid - Full Width */ + .content-box > div[style*="grid"] { + grid-template-columns: 1fr !important; + } + + /* Card Values */ + .card-value { + font-size: 2rem !important; + } + + /* Page Header */ + .page-header h2 { + font-size: 1.3rem; + } + + .page-header p { + font-size: 0.85rem; + } +} +/* Filter Form Kesehatan Santri */ +@media (max-width: 768px) { + #filterForm > div { + grid-template-columns: 1fr !important; + } + + #filterForm .form-group label { + font-size: 0.9rem; + } + + #filterForm input[type="date"], + #filterForm select { + font-size: 0.9rem; + } + + #filterForm > div > div:last-child { + flex-direction: column; + } + + #filterForm > div > div:last-child .btn { + width: 100%; + } +} + +@media (max-width: 480px) { + .row-cards { + grid-template-columns: 1fr !important; + } + + .card-value { + font-size: 2rem !important; + } +} + +} +} + +/* =================================== + END OF OPTIMIZED CSS + =================================== */ \ No newline at end of file diff --git a/sim-pkpps/public/favicon.ico b/sim-pkpps/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/sim-pkpps/public/index.php b/sim-pkpps/public/index.php new file mode 100644 index 0000000..1d69f3a --- /dev/null +++ b/sim-pkpps/public/index.php @@ -0,0 +1,55 @@ +make(Kernel::class); + +$response = $kernel->handle( + $request = Request::capture() +)->send(); + +$kernel->terminate($request, $response); diff --git a/sim-pkpps/public/robots.txt b/sim-pkpps/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/sim-pkpps/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/sim-pkpps/resources/css/app.css b/sim-pkpps/resources/css/app.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/sim-pkpps/resources/css/app.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/sim-pkpps/resources/js/app.js b/sim-pkpps/resources/js/app.js new file mode 100644 index 0000000..a8093be --- /dev/null +++ b/sim-pkpps/resources/js/app.js @@ -0,0 +1,7 @@ +import './bootstrap'; + +import Alpine from 'alpinejs'; + +window.Alpine = Alpine; + +Alpine.start(); diff --git a/sim-pkpps/resources/js/bootstrap.js b/sim-pkpps/resources/js/bootstrap.js new file mode 100644 index 0000000..846d350 --- /dev/null +++ b/sim-pkpps/resources/js/bootstrap.js @@ -0,0 +1,32 @@ +/** + * We'll load the axios HTTP library which allows us to easily issue requests + * to our Laravel back-end. This library automatically handles sending the + * CSRF token as a header based on the value of the "XSRF" token cookie. + */ + +import axios from 'axios'; +window.axios = axios; + +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; + +/** + * Echo exposes an expressive API for subscribing to channels and listening + * for events that are broadcast by Laravel. Echo and event broadcasting + * allows your team to easily build robust real-time web applications. + */ + +// import Echo from 'laravel-echo'; + +// import Pusher from 'pusher-js'; +// window.Pusher = Pusher; + +// window.Echo = new Echo({ +// broadcaster: 'pusher', +// key: import.meta.env.VITE_PUSHER_APP_KEY, +// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1', +// wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`, +// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, +// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, +// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', +// enabledTransports: ['ws', 'wss'], +// }); diff --git a/sim-pkpps/resources/views/admin/auth/login.blade.php b/sim-pkpps/resources/views/admin/auth/login.blade.php new file mode 100644 index 0000000..40b17c3 --- /dev/null +++ b/sim-pkpps/resources/views/admin/auth/login.blade.php @@ -0,0 +1,217 @@ +{{-- resources/views/admin/auth/login.blade.php --}} +@extends('auth.auth_layout') + +@section('title', 'Login Admin') + +@section('auth-content') +
+

Admin Login

+

Sistem Informasi Monitoring Santri

+
+ +{{-- Alert Error --}} +@if ($errors->any()) +
+ + {{ $errors->first() }} +
+@endif + +{{-- Alert Success (dari logout) --}} +@if(session('success')) +
+ + {{ session('success') }} +
+@endif + +
+ @csrf + + {{-- Username Field --}} +
+ + + @error('username') +
{{ $message }}
+ @enderror +
+ + {{-- Password Field --}} +
+ +
+ + +
+ @error('password') +
{{ $message }}
+ @enderror +
+ + {{-- Remember Me Checkbox --}} +
+ + +
+ + {{-- Submit Button --}} +
+ +
+ + {{-- Link ke Register --}} +

+ Admin baru? Daftar Sekarang +

+ + {{-- Link ke Login Santri --}} +
+

+ Login sebagai santri/wali? +

+ + Login Santri/Wali + +
+
+ +{{-- JavaScript --}} + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/auth/register.blade.php b/sim-pkpps/resources/views/admin/auth/register.blade.php new file mode 100644 index 0000000..4e339c6 --- /dev/null +++ b/sim-pkpps/resources/views/admin/auth/register.blade.php @@ -0,0 +1,53 @@ +@extends('auth.auth_layout') + +@section('title', 'Register Admin') + +@section('auth-content') +
+
+ +
+

Pendaftaran Akun Admin

+

Mohon gunakan email dan password yang kuat untuk keamanan sistem.

+
+ +{{-- Tampilkan error dari validator --}} +@if ($errors->any()) +
+ @foreach ($errors->all() as $error) +

{{ $error }}

+ @endforeach +
+@endif + +
+ @csrf + +
+ + + @error('email')
{{ $message }}
@enderror +
+ +
+ + + @error('password')
{{ $message }}
@enderror +
+ +
+ + +
+ +
+ +
+ +

+ Sudah punya akun? Login di sini +

+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/berita/create.blade.php b/sim-pkpps/resources/views/admin/berita/create.blade.php new file mode 100644 index 0000000..e21dd82 --- /dev/null +++ b/sim-pkpps/resources/views/admin/berita/create.blade.php @@ -0,0 +1,331 @@ +@extends('layouts.app') + +@section('title', 'Tambah Berita Baru') + +@section('content') + + + +@if($errors->any()) +
+ Terdapat kesalahan: +
    + @foreach($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+@endif + +
+
+ @csrf + + +
+ + + @error('judul') + {{ $message }} + @enderror +
+ + +
+ + + @error('konten') + {{ $message }} + @enderror +
+ + +
+
+ + + @error('penulis') + {{ $message }} + @enderror +
+ +
+ + + Format: JPG, PNG, GIF. Maksimal 2MB. + @error('gambar') + {{ $message }} + @enderror +
+
+ + +
+
+ + + @error('target_berita') + {{ $message }} + @enderror +
+ +
+ + + @error('status') + {{ $message }} + @enderror +
+
+ + + + + + + + +
+ + + Batal + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/berita/edit.blade.php b/sim-pkpps/resources/views/admin/berita/edit.blade.php new file mode 100644 index 0000000..b1d192c --- /dev/null +++ b/sim-pkpps/resources/views/admin/berita/edit.blade.php @@ -0,0 +1,350 @@ +@extends('layouts.app') + +@section('title', 'Edit Berita') + +@section('content') + + + +@if($errors->any()) +
+ Terdapat kesalahan: +
    + @foreach($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+@endif + +
+
+ @csrf + @method('PUT') + + +
+ + ID Berita: {{ $berita->id_berita }} + +
+ + +
+ + + @error('judul') + {{ $message }} + @enderror +
+ + +
+ + + @error('konten') + {{ $message }} + @enderror +
+ + +
+
+ + + @error('penulis') + {{ $message }} + @enderror +
+ +
+ + + Format: JPG, PNG, GIF. Maksimal 2MB. + @error('gambar') + {{ $message }} + @enderror + + @if($berita->gambar) +
+ Gambar Saat Ini: +
+ Gambar Berita +
+ @endif +
+
+ + +
+
+ + + @error('target_berita') + {{ $message }} + @enderror +
+ +
+ + + @error('status') + {{ $message }} + @enderror +
+
+ + +
+ +
+
+ @foreach($kelasOptions as $kelas) +
+ +
+ @endforeach +
+
+ + + {{ count(old('target_kelas', $berita->target_kelas ?? [])) }} kelas dipilih dari {{ count($kelasOptions) }} total kelas. + +
+ + +
+ + + +
+ +
+ + +
+
+ @foreach($santri as $s) +
+ +
+ @endforeach +
+
+ + + {{ count(old('santri_tertentu', $selectedSantri)) }} santri dipilih dari {{ $santri->count() }} total santri aktif. + +
+ + +
+ + + Lihat Berita + + + Batal + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/berita/index.blade.php b/sim-pkpps/resources/views/admin/berita/index.blade.php new file mode 100644 index 0000000..07df9f2 --- /dev/null +++ b/sim-pkpps/resources/views/admin/berita/index.blade.php @@ -0,0 +1,165 @@ +@extends('layouts.app') + +@section('title', 'Daftar Berita') + +@section('content') + + + +
+
+ +
+ + + + + + + + + + Reset + +
+ + + +
+
+ + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + + +
+ @if($berita->count() > 0) + + + + + + + + + + + + + + @foreach($berita as $item) + + + + + + + + + + @endforeach + +
IDJudul & KontenPenulisTanggalStatusTargetAksi
{{ $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 }} + + +
+ + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ + +
+ {{ $berita->appends(request()->query())->links() }} +
+ @else +
+ +

Belum Ada Berita

+

+ Mulai tambahkan berita pertama untuk santri pesantren. +

+ + Tambah Berita Pertama + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/berita/show.blade.php b/sim-pkpps/resources/views/admin/berita/show.blade.php new file mode 100644 index 0000000..1bd7838 --- /dev/null +++ b/sim-pkpps/resources/views/admin/berita/show.blade.php @@ -0,0 +1,184 @@ +@extends('layouts.app') + +@section('title', 'Detail Berita - ' . $berita->id_berita) + +@section('content') + + + +
+
+
+ + @if($berita->status === 'published') + Published + @else + Draft + @endif + +
+ +
+
+ + +
+
+ +
+
+ + ID: {{ $berita->id_berita }} + +
+ +

+ {{ $berita->judul }} +

+ +
+ + + Penulis: {{ $berita->penulis }} + + + + Tanggal: {{ $berita->created_at->format('d M Y, H:i') }} WIB + + + @php + $badgeClass = match($berita->target_berita) { + 'semua' => 'badge-primary', + 'kelas_tertentu' => 'badge-info', + 'santri_tertentu' => 'badge-warning', + default => 'badge-secondary' + }; + @endphp + + {{ $berita->target_audience }} + + +
+
+ + + @if($berita->gambar) +
+ Gambar Berita +
+ @endif + + +
+

Konten Berita

+
+ {!! nl2br(e($berita->konten)) !!} +
+
+ + + @if($berita->target_berita === 'santri_tertentu' || $berita->target_berita === 'kelas_tertentu') +
+ @if($berita->target_berita === 'kelas_tertentu') +

+ + Target Kelas: {{ implode(', ', $berita->target_kelas ?? []) }} +

+
+

+ + Berita ini ditujukan untuk santri dari kelas: + {{ implode(', ', $berita->target_kelas ?? []) }} +

+
+ @endif + + @if($berita->santriTertentu->count() > 0) +

+ + Daftar Penerima Berita ({{ $berita->santriTertentu->count() }} Santri) +

+ +
+ @foreach($berita->santriTertentu as $santri) +
+
+ +
+ {{ strtoupper(substr($santri->nama_lengkap, 0, 1)) }} +
+ +
+
+ {{ $santri->id_santri }} +
+
+ {{ $santri->nama_lengkap }} +
+
+ {{ $santri->kelas }} +
+
+ + +
+ @if($santri->pivot->sudah_dibaca) + + Dibaca + + @else + + Belum + + @endif +
+
+
+ @endforeach +
+ @else +
+ +

Belum ada santri yang dipilih untuk berita ini.

+
+ @endif +
+ @endif + + +
+
+ + Edit Berita + + +
+ @csrf + @method('DELETE') + +
+ + + Daftar Berita + +
+
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/berita/statistik.blade.php b/sim-pkpps/resources/views/admin/berita/statistik.blade.php new file mode 100644 index 0000000..28047dc --- /dev/null +++ b/sim-pkpps/resources/views/admin/berita/statistik.blade.php @@ -0,0 +1,198 @@ +@extends('layouts.app') + +@section('title', 'Statistik Berita') + +@section('content') + + + + + + +
+
+

Total Berita

+
{{ $totalBerita }}
+ +
+ +
+

Published

+
{{ $totalPublished }}
+ +
+ +
+

Draft

+
{{ $totalDraft }}
+ +
+ +
+

Semua Santri

+
{{ $beritaSemua }}
+ +
+ +
+

Target Tertentu

+
{{ $beritaTertentu }}
+ +
+
+ + +
+

+ + Distribusi Berita +

+ +
+ +
+

+ + Berdasarkan Status +

+ + @php + $totalForPercentage = max($totalBerita, 1); + $publishedPercent = round(($totalPublished / $totalForPercentage) * 100, 1); + $draftPercent = round(($totalDraft / $totalForPercentage) * 100, 1); + @endphp + + +
+
+ + Published + + + {{ $publishedPercent }}% + +
+
+
+
+ + {{ $totalPublished }} dari {{ $totalBerita }} berita + +
+ + +
+
+ + Draft + + + {{ $draftPercent }}% + +
+
+
+
+ + {{ $totalDraft }} dari {{ $totalBerita }} berita + +
+
+ + +
+

+ + Berdasarkan Target +

+ + @php + $semuaPercent = round(($beritaSemua / $totalForPercentage) * 100, 1); + $tertentuPercent = round(($beritaTertentu / $totalForPercentage) * 100, 1); + @endphp + + +
+
+ + Semua Santri + + + {{ $semuaPercent }}% + +
+
+
+
+ + {{ $beritaSemua }} dari {{ $totalBerita }} berita + +
+ + +
+
+ + Target Tertentu + + + {{ $tertentuPercent }}% + +
+
+
+
+ + {{ $beritaTertentu }} dari {{ $totalBerita }} berita + +
+
+
+
+ + + + + +@if($totalBerita == 0) +
+ +

Belum Ada Berita

+

+ Mulai dengan membuat berita pertama untuk pesantren Anda. Berita dapat dipublikasikan untuk semua santri atau target tertentu. +

+ + Buat Berita Pertama + +
+@endif +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/capaian/create.blade.php b/sim-pkpps/resources/views/admin/capaian/create.blade.php new file mode 100644 index 0000000..f5e1bfc --- /dev/null +++ b/sim-pkpps/resources/views/admin/capaian/create.blade.php @@ -0,0 +1,614 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + + {{-- Info Box --}} +
+ + Tips: Pilih santri terlebih dahulu untuk melihat materi yang sesuai dengan kelasnya. +
+ + {{-- Santri Selection --}} +
+ + + @error('id_santri') + {{ $message }} + @enderror +
+ + {{-- Kelas Display (Auto) --}} + + +
+ {{-- Materi Selection --}} +
+ + + Materi akan muncul sesuai kelas santri + @error('id_materi') + {{ $message }} + @enderror +
+ + {{-- Semester Selection --}} +
+ + + @error('id_semester') + {{ $message }} + @enderror +
+
+ + {{-- Materi Info Display --}} + + + {{-- METODE INPUT HALAMAN --}} + + + {{-- Catatan --}} +
+ + + @error('catatan') + {{ $message }} + @enderror +
+ + {{-- Tanggal Input --}} +
+ + + @error('tanggal_input') + {{ $message }} + @enderror +
+ + {{-- Action Buttons --}} +
+ + + Kembali + +
+
+
+ + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/capaian/dashboard.blade.php b/sim-pkpps/resources/views/admin/capaian/dashboard.blade.php new file mode 100644 index 0000000..a7e0701 --- /dev/null +++ b/sim-pkpps/resources/views/admin/capaian/dashboard.blade.php @@ -0,0 +1,376 @@ +@extends('layouts.app') + +@section('content') + + +{{-- Filter Section --}} +
+
+ + + + + + + + + @if($idSantri || $kelas) + + Reset + + @endif +
+
+ +{{-- Statistik Cards --}} +
+
+

Total Capaian

+
{{ $totalCapaian }}
+

Data capaian tercatat

+ +
+
+

Total Santri

+
{{ $totalSantri }}
+

Santri aktif dengan capaian

+ +
+
+

Rata-rata Progress

+
{{ number_format($rataRataPersentase, 1) }}%
+

Progress keseluruhan

+ +
+
+

Selesai 100%

+
{{ $capaianSelesai }}
+

Materi yang diselesaikan

+ +
+
+ +{{-- Statistik Per Kategori --}} +
+ @foreach($statistikKategori as $kategori => $stats) +
+

{{ $kategori }}

+
{{ number_format($stats['avg'], 1) }}%
+

+ {{ $stats['count'] }} capaian | {{ $stats['selesai'] }} selesai +

+ +
+ @endforeach +
+ +{{-- Grafik Section --}} +
+ {{-- Grafik Pie - Progress per Kategori --}} +
+

+ Progress per Kategori +

+ +
+ + {{-- Grafik Bar - Distribusi Persentase --}} +
+

+ Distribusi Progress Santri +

+ +
+
+ +{{-- Grafik Line - Trend Progress --}} +
+

+ Trend Progress dari Waktu ke Waktu +

+ +
+ +{{-- Top 10 Santri --}} +
+

+ Top 10 Santri dengan Progress Tertinggi +

+ @if($topSantri->count() > 0) + + + + + + + + + + + + + @foreach($topSantri as $index => $item) + + + + + + + + + @endforeach + +
RankNISNama SantriKelasRata-rata ProgressAksi
+ @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 }} +
+
+ {{ number_format($item->rata_rata, 1) }}% +
+
+
+ + Lihat Detail + +
+ @else +
+ +

Belum ada data untuk ditampilkan

+
+ @endif +
+ +{{-- Materi dengan Progress Terendah (Perlu Perhatian) --}} +@if($materiTerendah->count() > 0) +
+

+ Materi yang Perlu Perhatian (Progress < 50%) +

+ + + + + + + + + + + + + @foreach($materiTerendah as $item) + + + + + + + + + @endforeach + +
Nama MateriKategoriKelasJumlah SantriRata-rata ProgressAksi
{{ $item->materi->nama_kitab }}{!! $item->materi->kategori_badge !!}{!! $item->materi->kelas_badge !!}{{ $item->jumlah_santri }} santri +
+
+ {{ number_format($item->rata_rata, 1) }}% +
+
+
+ + Detail + +
+
+@endif + +{{-- Quick Actions --}} + + +{{-- Chart.js Script --}} + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/capaian/detail-materi.blade.php b/sim-pkpps/resources/views/admin/capaian/detail-materi.blade.php new file mode 100644 index 0000000..ecb46d2 --- /dev/null +++ b/sim-pkpps/resources/views/admin/capaian/detail-materi.blade.php @@ -0,0 +1,224 @@ +@extends('layouts.app') + +@section('content') + + +{{-- Materi Info Card --}} +
+
+
+ +
+
+

{{ $materi->nama_kitab }}

+

+ {!! $materi->kategori_badge !!} | + {!! $materi->kelas_badge !!} | + Total: {{ $materi->total_halaman }} halaman +

+
+ +
+
+ +{{-- Filter Semester --}} +
+
+ + + +
+
+ +{{-- Statistik Cards --}} +
+
+

Total Santri

+
{{ $totalSantri }}
+

Santri yang belajar materi ini

+ +
+
+

Selesai 100%

+
{{ $santriSelesai }}
+

{{ $totalSantri > 0 ? number_format(($santriSelesai/$totalSantri)*100, 1) : 0 }}% dari total santri

+ +
+
+

Sedang Belajar

+
{{ $santriMulai }}
+

Progress 1-99%

+ +
+
+

Rata-rata Progress

+
{{ number_format($rataRataPersentase, 1) }}%
+

Progress keseluruhan

+ +
+
+ +{{-- Grafik Distribusi --}} +
+

+ Distribusi Progress Santri +

+ +
+ +{{-- Tabel Detail Capaian Santri --}} +
+

+ Detail Capaian per Santri +

+ + @if($capaians->count() > 0) + + + + + + + + + + + + + + + @foreach($capaians as $index => $capaian) + + + + + + + + + + + @endforeach + +
NoNISNama SantriKelasSemesterHalamanProgressAksi
{{ $index + 1 }}{{ $capaian->santri->nis }} + {{ $capaian->santri->nama_lengkap }} + + {{ $capaian->santri->kelas }} + + {{ $capaian->semester->nama_semester }} + + + {{ $capaian->jumlah_halaman_selesai }} / {{ $materi->total_halaman }} + + +
+
+ {{ number_format($capaian->persentase, 1) }}% +
+
+
+ +
+ @else +
+ +

Belum Ada Capaian

+

Belum ada santri yang mencatat capaian untuk materi ini.

+
+ @endif +
+ +{{-- Chart.js Script --}} + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/capaian/edit.blade.php b/sim-pkpps/resources/views/admin/capaian/edit.blade.php new file mode 100644 index 0000000..e6ba79f --- /dev/null +++ b/sim-pkpps/resources/views/admin/capaian/edit.blade.php @@ -0,0 +1,445 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + @method('PUT') + + {{-- Info Box --}} +
+ + ID Capaian: {{ $capaian->id_capaian }} +
+ + {{-- Santri Info (Read Only) --}} +
+

Informasi Santri & Materi

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Santri{{ $capaian->santri->nama_lengkap }} ({{ $capaian->santri->nis }})
Kelas{{ $capaian->santri->kelas }}
Materi{{ $capaian->materi->nama_kitab }}
Kategori{!! $capaian->materi->kategori_badge !!}
Total Halaman{{ $capaian->materi->total_halaman }} halaman ({{ $capaian->materi->halaman_mulai }} - {{ $capaian->materi->halaman_akhir }})
Semester{{ $capaian->semester->nama_semester }}
+
+ + {{-- METODE INPUT HALAMAN --}} +
+

Update Halaman yang Sudah Selesai

+ + {{-- Tab Metode Input --}} +
+ + + +
+ + {{-- METODE 1: INPUT RANGE TEXT --}} +
+
+ + + + Format: Gunakan tanda minus (-) untuk range dan koma (,) untuk memisahkan.
+ Contoh: "1-10, 16-21, 40" artinya halaman 1 sampai 10, 16 sampai 21, dan halaman 40. +
+ @error('halaman_selesai') + {{ $message }} + @enderror +
+ + +
+ + {{-- METODE 2: VISUAL GRID --}} + + + {{-- METODE 3: QUICK INPUT --}} + + + {{-- Preview Result --}} +
+
+

+ Preview Capaian +

+
+
+

Halaman Selesai

+

{{ $capaian->jumlah_halaman_selesai }}

+
+
+

Total Halaman

+

{{ $capaian->materi->total_halaman }}

+
+
+

Persentase

+

{{ number_format($capaian->persentase, 2) }}%

+
+
+
+
+
+
+
+
+
+
+ + {{-- Catatan --}} +
+ + + @error('catatan') + {{ $message }} + @enderror +
+ + {{-- Tanggal Input --}} +
+ + + @error('tanggal_input') + {{ $message }} + @enderror +
+ + {{-- Action Buttons --}} +
+ + + Kembali + +
+
+
+ + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/capaian/index.blade.php b/sim-pkpps/resources/views/admin/capaian/index.blade.php new file mode 100644 index 0000000..be982bd --- /dev/null +++ b/sim-pkpps/resources/views/admin/capaian/index.blade.php @@ -0,0 +1,147 @@ +@extends('layouts.app') + +@section('content') + + +{{-- Alert Messages --}} +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +{{-- Filter & Search Section --}} +
+
+ + + + + + + + + @if(request()->anyFilled(['id_santri', 'id_semester', 'kategori'])) + + Reset + + @endif + + + Input Capaian + +
+
+ +{{-- Table Section --}} +
+ @if($capaians->count() > 0) + + + + + + + + + + + + + + + + @foreach($capaians as $index => $capaian) + + + + + + + + + + + + @endforeach + +
NoSantriKelasMateriKategoriSemesterHalamanProgressAksi
{{ $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 !!} +
+ + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ + {{-- Pagination --}} +
+ {{ $capaians->links() }} +
+ @else +
+ +

Belum Ada Data Capaian

+

Silakan input capaian santri terlebih dahulu.

+ + Input Capaian Pertama + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/capaian/rekap-kelas.blade.php b/sim-pkpps/resources/views/admin/capaian/rekap-kelas.blade.php new file mode 100644 index 0000000..8929496 --- /dev/null +++ b/sim-pkpps/resources/views/admin/capaian/rekap-kelas.blade.php @@ -0,0 +1,187 @@ +@extends('layouts.app') + +@section('content') + + +{{-- Filter Section --}} +
+
+ + + + + + + +
+
+ +{{-- Info Box --}} +
+ + Kelas: {{ $kelas }} | + Total Santri: {{ count($rekapData) }} santri + @if($selectedSemester) + | Semester: {{ $semesters->where('id_semester', $selectedSemester)->first()->nama_semester ?? 'Semua' }} + @endif +
+ +{{-- Rekap Table --}} +
+ @if(count($rekapData) > 0) + + + + + + + + + + + + + + + + + + + @foreach($rekapData as $index => $data) + + + + + + + + + + + + @endforeach + +
RankNISNama SantriTotal MateriProgress per Kategori (%)Rata-rataSelesai
Al-Qur'anHadistTambahan
+ @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) }}% + +
+
+ {{ number_format($data['rata_rata'], 1) }}% +
+
+
+ + {{ $data['selesai'] }} / {{ $data['total_materi'] }} + +
+ + {{-- Summary Statistics --}} +
+

+ Statistik Kelas {{ $kelas }} +

+
+
+

Rata-rata Kelas

+

+ {{ number_format(collect($rekapData)->avg('rata_rata'), 1) }}% +

+
+
+

Progress Tertinggi

+

+ {{ number_format(collect($rekapData)->max('rata_rata'), 1) }}% +

+
+
+

Progress Terendah

+

+ {{ number_format(collect($rekapData)->min('rata_rata'), 1) }}% +

+
+
+

Total Selesai

+

+ {{ collect($rekapData)->sum('selesai') }} materi +

+
+
+
+ @else +
+ +

Tidak Ada Data

+

Belum ada santri di kelas {{ $kelas }} atau belum ada capaian yang tercatat.

+
+ @endif +
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/capaian/riwayat-santri.blade.php b/sim-pkpps/resources/views/admin/capaian/riwayat-santri.blade.php new file mode 100644 index 0000000..5747e09 --- /dev/null +++ b/sim-pkpps/resources/views/admin/capaian/riwayat-santri.blade.php @@ -0,0 +1,173 @@ +@extends('layouts.app') + +@section('content') + + +{{-- Santri Info Card --}} +
+
+
+ +
+
+

{{ $santri->nama_lengkap }}

+

+ NIS: {{ $santri->nis }} | + Kelas: {{ $santri->kelas }} +

+
+ +
+
+ +{{-- Statistik Cards --}} +
+
+

Total Capaian

+
{{ $totalCapaian }}
+

Data capaian tercatat

+ +
+
+

Rata-rata Progress

+
{{ number_format($rataRataPersentase, 1) }}%
+

Progress keseluruhan

+ +
+
+

Al-Qur'an

+
{{ number_format($statistikKategori['Al-Qur\'an'] ?? 0, 1) }}%
+

Progress kategori

+ +
+
+

Hadist

+
{{ number_format($statistikKategori['Hadist'] ?? 0, 1) }}%
+

Progress kategori

+ +
+
+ +{{-- Filter Section --}} +
+
+ + + + + @if(request()->filled('id_semester')) + + Reset + + @endif + + + Tambah Capaian + +
+
+ +{{-- Capaian Table --}} +
+ @if($capaians->count() > 0) + {{-- Group by Kategori --}} + @php + $groupedCapaians = $capaians->groupBy(function($item) { + return $item->materi->kategori; + }); + @endphp + + @foreach(['Al-Qur\'an', 'Hadist', 'Materi Tambahan'] as $kategori) + @if(isset($groupedCapaians[$kategori]) && $groupedCapaians[$kategori]->count() > 0) +
+

+ + Kategori: {{ $kategori }} +

+ + + + + + + + + + + + + + + @foreach($groupedCapaians[$kategori] as $index => $capaian) + + + + + + + + + + @endforeach + +
NoMateriSemesterHalamanProgressTanggal InputAksi
{{ $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') }} + +
+
+ @endif + @endforeach + + {{-- Pagination --}} +
+ {{ $capaians->links() }} +
+ @else +
+ +

Belum Ada Capaian

+

Santri ini belum memiliki data capaian.

+ + Tambah Capaian Pertama + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/capaian/show.blade.php b/sim-pkpps/resources/views/admin/capaian/show.blade.php new file mode 100644 index 0000000..4c57451 --- /dev/null +++ b/sim-pkpps/resources/views/admin/capaian/show.blade.php @@ -0,0 +1,251 @@ +@extends('layouts.app') + +@section('content') + + +
+ {{-- Header Section --}} +
+
+

{{ $capaian->santri->nama_lengkap }}

+

{{ $capaian->id_capaian }} | {{ $capaian->materi->nama_kitab }}

+
+ +
+ + {{-- Progress Card --}} +
+

+ Progress Capaian +

+
+
+

Halaman Selesai

+

{{ $capaian->jumlah_halaman_selesai }}

+ dari {{ $capaian->materi->total_halaman }} halaman +
+
+

Persentase

+

{{ number_format($capaian->persentase, 2) }}%

+ progress keseluruhan +
+
+

Status

+
+ {!! $capaian->persentase_badge !!} +
+ + @if($capaian->persentase >= 100) + Selesai 100% + @elseif($capaian->persentase >= 75) + Hampir Selesai + @elseif($capaian->persentase >= 50) + Sedang Berlangsung + @else + Baru Dimulai + @endif + +
+
+
+
+
+ {{ number_format($capaian->persentase, 1) }}% +
+
+
+
+ + {{-- Detail Santri & Materi --}} +
+

Informasi Santri

+ + + + + + + + + + + + + +
Nama Santri{{ $capaian->santri->nama_lengkap }}
NIS{{ $capaian->santri->nis }}
Kelas{{ $capaian->santri->kelas }}
+
+ +
+

Informasi Materi

+ + + + + + + + + + + + + + + + + + + + + +
Nama Kitab{{ $capaian->materi->nama_kitab }}
Kategori{!! $capaian->materi->kategori_badge !!}
Total Halaman{{ $capaian->materi->total_halaman }} halaman
Range HalamanHalaman {{ $capaian->materi->halaman_mulai }} - {{ $capaian->materi->halaman_akhir }}
Semester + {{ $capaian->semester->nama_semester }} + {!! $capaian->semester->status_badge !!} +
+
+ + {{-- Detail Capaian --}} +
+

Detail Halaman yang Selesai

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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
+
+ + {{-- Visual Halaman (Grid Preview) --}} +
+

Visual Halaman yang Selesai

+
+
+ +
+

+ = Selesai    + = Belum +

+
+
+ + {{-- Action Buttons --}} +
+ + Edit Capaian + + + Lihat Riwayat Santri + +
+ @csrf + @method('DELETE') + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/dashboardAdmin.blade.php b/sim-pkpps/resources/views/admin/dashboardAdmin.blade.php new file mode 100644 index 0000000..a110539 --- /dev/null +++ b/sim-pkpps/resources/views/admin/dashboardAdmin.blade.php @@ -0,0 +1,36 @@ + +@extends('layouts.app', ['isAdmin' => true]) + +@section('title', 'Dashboard Admin') + +@section('content') + + +
+
+

Total Santri

+

{{ $data['total_santri'] }}

+ +
+
+

Total Wali Santri

+

{{ $data['total_wali'] }}

+ +
+
+

Kegiatan Hari Ini

+

{{ $data['kegiatan_hari_ini'] }}

+ +
+
+ +
+

Statistik & Grafik

+
+

Area untuk menempatkan statistik dan grafik sistem.

+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kategori_pelanggaran/create.blade.php b/sim-pkpps/resources/views/admin/kategori_pelanggaran/create.blade.php new file mode 100644 index 0000000..0dea694 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kategori_pelanggaran/create.blade.php @@ -0,0 +1,99 @@ +{{-- resources/views/admin/kategori_pelanggaran/create.blade.php --}} +@extends('layouts.app') + +@section('title', 'Tambah Kategori Pelanggaran') + +@section('content') + + + +
+ +
+ +
+
+

+ Form Tambah Kategori +

+
+ ID Kategori Berikutnya: + {{ $nextIdKategori }} +
+
+ +
+ @csrf + +
+ +
+ + + @error('nama_pelanggaran') + {{ $message }} + @enderror +
+ + +
+ + + @error('poin') + {{ $message }} + @enderror + Poin antara 1-100 (semakin tinggi, semakin berat pelanggarannya) +
+
+ + +
+

+ Panduan Poin Pelanggaran +

+
    +
  • 1-10 poin: Pelanggaran ringan (terlambat, tidak rapi)
  • +
  • 11-30 poin: Pelanggaran sedang (bolos, tidak mengikuti kegiatan)
  • +
  • 31-100 poin: Pelanggaran berat (berkelahi, mencuri)
  • +
+
+ +
+ + + Kembali + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kategori_pelanggaran/index.blade.php b/sim-pkpps/resources/views/admin/kategori_pelanggaran/index.blade.php new file mode 100644 index 0000000..20a8757 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kategori_pelanggaran/index.blade.php @@ -0,0 +1,185 @@ +{{-- resources/views/admin/kategori_pelanggaran/index.blade.php --}} +@extends('layouts.app') + +@section('title', 'Data Kategori Pelanggaran') + +@section('content') + + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + + +
+

+ @if(isset($kategori)) + Edit Kategori + @else + Tambah Kategori + @endif +

+
+ @csrf + @if(isset($kategori)) + @method('PUT') + @endif + +
+
+ + + @error('nama_pelanggaran') + {{ $message }} + @enderror +
+ +
+ + + @error('poin') + {{ $message }} + @enderror + Poin antara 1-100 +
+
+ +
+ + @if(isset($kategori)) + + Batal + + @endif +
+
+
+ + +
+
+

+ Daftar Kategori Pelanggaran +

+ + Total: {{ $data->count() }} Kategori + +
+ + @if($data->isNotEmpty()) + + + + + + + + + + + + + @foreach($data as $index => $item) + + + + + + + + + @endforeach + +
NoID KategoriNama PelanggaranPoinDigunakanAksi
{{ $index + 1 }} + {{ $item->id_kategori }} + + {{ $item->nama_pelanggaran }} + + + {{ $item->poin }} + + + + {{ $item->riwayatPelanggaran->count() }}x + + +
+ + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ @else +
+ +

Belum ada data kategori pelanggaran

+

Mulai dengan menambahkan kategori pelanggaran baru menggunakan form di atas.

+
+ @endif +
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kategori_pelanggaran/show.blade.php b/sim-pkpps/resources/views/admin/kategori_pelanggaran/show.blade.php new file mode 100644 index 0000000..f0e1258 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kategori_pelanggaran/show.blade.php @@ -0,0 +1,183 @@ +{{-- resources/views/admin/kategori_pelanggaran/show.blade.php --}} +@extends('layouts.app') + +@section('title', 'Detail Kategori Pelanggaran') + +@section('content') + + + +
+ +
+ +
+ +
+
+
+

Informasi Kategori

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
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
+
+ + + @if($kategori->riwayatPelanggaran->isNotEmpty()) +
+

+ Riwayat Pelanggaran Terkait +

+ + + + + + + + + + + + + @foreach($kategori->riwayatPelanggaran->take(10) as $index => $riwayat) + + + + + + + + @endforeach + +
NoTanggalSantriPoinKeterangan
{{ $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 ?? '-' }}
+ + @if($kategori->riwayatPelanggaran->count() > 10) + + @endif +
+ @endif +
+ + +
+ +
+

+ Statistik +

+
+
{{ $kategori->riwayatPelanggaran->count() }}
+

Total Pelanggaran

+
+ + @if($kategori->riwayatPelanggaran->count() > 0) +
+
+
+ {{ $kategori->riwayatPelanggaran->sum('poin') }} +
+

Total Poin Terkumpul

+
+ +
+ + Pelanggaran Terakhir + + + {{ $kategori->riwayatPelanggaran->sortByDesc('tanggal')->first()->tanggal->format('d M Y') }} + +
+
+ @endif +
+ + + + + +
+

+ Informasi +

+

+ Kategori ini telah digunakan {{ $kategori->riwayatPelanggaran->count() }} kali + dalam sistem pencatatan pelanggaran santri. +

+
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/absensi/index.blade.php b/sim-pkpps/resources/views/admin/kegiatan/absensi/index.blade.php new file mode 100644 index 0000000..cfa5d1d --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/absensi/index.blade.php @@ -0,0 +1,89 @@ +@extends('layouts.app') + +@section('content') + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +
+
+
+ + + + + @if(request('hari')) + + Reset + + @endif +
+
+ + @if($kegiatans->count() > 0) + + + + + + + + + + + + + @foreach($kegiatans as $index => $kegiatan) + + + + + + + + + @endforeach + +
NoHariWaktuNama KegiatanKategoriAksi
{{ $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 + +
+ +
+ {{ $kegiatans->links() }} +
+ @else +
+ +

Belum Ada Kegiatan

+

Silakan tambahkan kegiatan terlebih dahulu.

+ + Tambah Kegiatan + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/absensi/input.blade.php b/sim-pkpps/resources/views/admin/kegiatan/absensi/input.blade.php new file mode 100644 index 0000000..0627f5a --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/absensi/input.blade.php @@ -0,0 +1,280 @@ +@extends('layouts.app') + +@section('content') + + +
+
+
+

{{ $kegiatan->nama_kegiatan }}

+

+ {{ $kegiatan->hari }} | + {{ date('H:i', strtotime($kegiatan->waktu_mulai)) }} - {{ date('H:i', strtotime($kegiatan->waktu_selesai)) }} | + {{ $kegiatan->kategori->nama_kategori }} +

+
+
+ + +
+
+
+ + +
+
+ @csrf + + +
+ + +
+ +
+

Pilih status absensi untuk setiap santri. Jika tidak dipilih, akan dianggap Alpa.

+
+ + + + + + + + + + + + + @foreach($santris as $index => $santri) + + + + + + + + @endforeach + +
NoID SantriNama SantriKelasStatus
{{ $index + 1 }}{{ $santri->id_santri }}{{ $santri->nama_lengkap }}{{ $santri->kelas }} + @php + $currentStatus = $absensiData[$santri->id_santri] ?? 'Alpa'; + @endphp +
+ + + + +
+
+ +
+ + + Kembali + +
+
+
+ + + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/absensi/rekap.blade.php b/sim-pkpps/resources/views/admin/kegiatan/absensi/rekap.blade.php new file mode 100644 index 0000000..5030b7a --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/absensi/rekap.blade.php @@ -0,0 +1,103 @@ +@extends('layouts.app') + +@section('content') + + +
+
+

Hadir

+
{{ $stats['Hadir'] ?? 0 }}
+ +
+
+

Izin

+
{{ $stats['Izin'] ?? 0 }}
+ +
+
+

Sakit

+
{{ $stats['Sakit'] ?? 0 }}
+ +
+
+

Alpa

+
{{ $stats['Alpa'] ?? 0 }}
+ +
+
+ +
+
+
+ + + + + + @if(request()->hasAny(['tanggal', 'bulan'])) + + Reset + + @endif + + + Kembali + +
+
+ + @if($absensis->count() > 0) + + + + + + + + + + + + + + + @foreach($absensis as $index => $absensi) + + + + + + + + + + + @endforeach + +
NoTanggalID SantriNama SantriKelasStatusMetodeWaktu
{{ $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)) : '-' }}
+ +
+ {{ $absensis->links() }} +
+ @else +
+ +

Belum Ada Data Absensi

+

Silakan input absensi terlebih dahulu.

+ + Input Absensi + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/data/create.blade.php b/sim-pkpps/resources/views/admin/kegiatan/data/create.blade.php new file mode 100644 index 0000000..11054f1 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/data/create.blade.php @@ -0,0 +1,147 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + +
+ + + ID akan dibuat otomatis saat disimpan +
+ +
+ + + @error('kategori_id') + {{ $message }} + @enderror +
+ +
+ + + @error('nama_kegiatan') + {{ $message }} + @enderror +
+ +
+ + + @error('hari') + {{ $message }} + @enderror +
+ +
+
+ + + @error('waktu_mulai') + {{ $message }} + @enderror +
+ +
+ + + @error('waktu_selesai') + {{ $message }} + @enderror +
+
+ +
+ + + @error('materi') + {{ $message }} + @enderror +
+ +
+ + + @error('keterangan') + {{ $message }} + @enderror +
+ +
+ + + Kembali + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/data/edit.blade.php b/sim-pkpps/resources/views/admin/kegiatan/data/edit.blade.php new file mode 100644 index 0000000..5c5d081 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/data/edit.blade.php @@ -0,0 +1,145 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + @method('PUT') + +
+ + +
+ +
+ + + @error('kategori_id') + {{ $message }} + @enderror +
+ +
+ + + @error('nama_kegiatan') + {{ $message }} + @enderror +
+ +
+ + + @error('hari') + {{ $message }} + @enderror +
+ +
+
+ + + @error('waktu_mulai') + {{ $message }} + @enderror +
+ +
+ + + @error('waktu_selesai') + {{ $message }} + @enderror +
+
+ +
+ + + @error('materi') + {{ $message }} + @enderror +
+ +
+ + + @error('keterangan') + {{ $message }} + @enderror +
+ +
+ + + Kembali + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/data/index.blade.php b/sim-pkpps/resources/views/admin/kegiatan/data/index.blade.php new file mode 100644 index 0000000..6ef2371 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/data/index.blade.php @@ -0,0 +1,109 @@ +@extends('layouts.app') + +@section('content') + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +
+
+
+ + + + + + + + + @if(request()->hasAny(['hari', 'kategori_id', 'search'])) + + Reset + + @endif + + + Tambah Kegiatan + +
+
+ + @if($kegiatans->count() > 0) + + + + + + + + + + + + + + + @foreach($kegiatans as $index => $kegiatan) + + + + + + + + + + + @endforeach + +
NoIDHariWaktuNama KegiatanKategoriMateriAksi
{{ $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) ?? '-' }} + + + + + + +
+ @csrf + @method('DELETE') + +
+
+ +
+ {{ $kegiatans->links() }} +
+ @else +
+ +

Belum Ada Kegiatan

+

Silakan tambahkan jadwal kegiatan santri.

+ + Tambah Kegiatan + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/data/show.blade.php b/sim-pkpps/resources/views/admin/kegiatan/data/show.blade.php new file mode 100644 index 0000000..db8c06b --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/data/show.blade.php @@ -0,0 +1,75 @@ +@extends('layouts.app') + +@section('content') + + +
+
+

{{ $kegiatan->nama_kegiatan }}

+
+ + Edit + +
+ @csrf + @method('DELETE') + +
+ + Kembali + +
+
+ +
+

Informasi Kegiatan

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/kartu/cetak.blade.php b/sim-pkpps/resources/views/admin/kegiatan/kartu/cetak.blade.php new file mode 100644 index 0000000..b0d33e9 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/kartu/cetak.blade.php @@ -0,0 +1,501 @@ + + + + + Kartu RFID 2 Sisi - {{ $santri->nama_lengkap ?? 'Preview' }} + + + + +
+
+
+
+ +
+
+ +
+
+

PKPPS

+

Pesantren Riyadlul Jannah

+
+
+ +
+
+
+
+ @if(isset($santri)){{ strtoupper(substr($santri->nama_lengkap, 0, 1)) }}@else A @endif +
+
+
+
+ @if(isset($santri)){{ strtoupper(Str::limit($santri->nama_lengkap, 12, '')) }}@else YOUR NAME @endif +
+
+ +
+
+
+ ID + : @if(isset($santri)){{ $santri->id_santri }}@else S004 @endif +
+
+ Kelas + : @if(isset($santri)){{ $santri->kelas }}@else Lambatan @endif +
+
+ Status + : @if(isset($santri)){{ $santri->status }}@else Aktif @endif +
+
+ RFID + : @if(isset($santri) && $santri->rfid_uid){{ Str::limit($santri->rfid_uid, 14) }}@else 04A3B62FD9C1 @endif +
+
+
+ +
+
+ @if(isset($santri) && $santri->rfid_uid) + QR + @else + QR + @endif +
+
SCAN ME
+
+
+ + +
+ + +
+
+
+ +
+
+ +
PKPPS
+
Pesantren Riyadlul Jannah
+
+ +
+
📋 Ketentuan Kartu
+
+ + Kartu ini adalah identitas resmi santri PKPPS +
+
+ + Wajib dibawa setiap saat di lingkungan pesantren +
+
+ + Digunakan untuk absensi kegiatan dan akses fasilitas +
+
+ + Jika hilang segera lapor ke bagian administrasi +
+
+ + Harap dijaga dan tidak dipinjamkan +
+
+ +
+
+ 📍 Jl. Pesantren No. 123, Yogyakarta +
+
+ 📞 (0274) 123-4567 +
+
+ 📧 admin@pkpps.ac.id +
+
+
+
+ + \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/kartu/daftar.blade.php b/sim-pkpps/resources/views/admin/kegiatan/kartu/daftar.blade.php new file mode 100644 index 0000000..d2f9e39 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/kartu/daftar.blade.php @@ -0,0 +1,93 @@ +@extends('layouts.app') + +@section('content') + + +
+
+

Tempelkan kartu RFID ke reader, UID akan otomatis terdeteksi pada kolom di bawah.

+
+ +
+ @csrf + +
+ + + + + + + + + + + + + + +
ID Santri{{ $santri->id_santri }}
Nama Lengkap{{ $santri->nama_lengkap }}
Kelas{{ $santri->kelas }}
+
+ +
+ +
+ + +
+ @error('rfid_uid') + {{ $message }} + @enderror + UID akan otomatis terisi saat kartu ditempelkan ke reader. +
+ + + +
+ + + Kembali + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/kartu/index.blade.php b/sim-pkpps/resources/views/admin/kegiatan/kartu/index.blade.php new file mode 100644 index 0000000..bb38593 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/kartu/index.blade.php @@ -0,0 +1,109 @@ +@extends('layouts.app') + +@section('content') + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +
+
+
+ + + + + @if(request('filter')) + + Reset + + @endif +
+
+ + @if($santris->count() > 0) + + + + + + + + + + + + + + @foreach($santris as $index => $santri) + + + + + + + + + + @endforeach + +
NoID SantriNama SantriKelasUID RFIDStatusAksi
{{ $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 + +
+ @csrf + @method('DELETE') + +
+ @else + + Daftarkan RFID + + @endif +
+ +
+ {{ $santris->links() }} +
+ @else +
+ +

Tidak Ada Data Santri

+

Belum ada santri aktif yang terdaftar.

+
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/kategori/create.blade.php b/sim-pkpps/resources/views/admin/kegiatan/kategori/create.blade.php new file mode 100644 index 0000000..672024f --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/kategori/create.blade.php @@ -0,0 +1,63 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + +
+ + + ID akan dibuat otomatis saat disimpan +
+ +
+ + + @error('nama_kategori') + {{ $message }} + @enderror +
+ +
+ + + @error('keterangan') + {{ $message }} + @enderror +
+ +
+ + + Kembali + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/kategori/edit.blade.php b/sim-pkpps/resources/views/admin/kegiatan/kategori/edit.blade.php new file mode 100644 index 0000000..5997731 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/kategori/edit.blade.php @@ -0,0 +1,61 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + @method('PUT') + +
+ + +
+ +
+ + + @error('nama_kategori') + {{ $message }} + @enderror +
+ +
+ + + @error('keterangan') + {{ $message }} + @enderror +
+ +
+ + + Kembali + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/kategori/index.blade.php b/sim-pkpps/resources/views/admin/kegiatan/kategori/index.blade.php new file mode 100644 index 0000000..0951ba6 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/kategori/index.blade.php @@ -0,0 +1,88 @@ +@extends('layouts.app') + +@section('content') + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +
+
+
+
+ + + @if(request('search')) + + + + @endif +
+
+ + Tambah Kategori + +
+ + @if($kategoris->count() > 0) + + + + + + + + + + + + + @foreach($kategoris as $index => $kategori) + + + + + + + + + @endforeach + +
NoID KategoriNama KategoriKeteranganDibuatAksi
{{ $kategoris->firstItem() + $index }}{{ $kategori->kategori_id }}{{ $kategori->nama_kategori }}{{ Str::limit($kategori->keterangan, 50) ?? '-' }}{{ $kategori->created_at->format('d M Y') }} + + + + + + +
+ @csrf + @method('DELETE') + +
+
+ +
+ {{ $kategoris->links() }} +
+ @else +
+ +

Belum Ada Kategori

+

Silakan tambahkan kategori kegiatan baru.

+ + Tambah Kategori + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/kategori/show.blade.php b/sim-pkpps/resources/views/admin/kegiatan/kategori/show.blade.php new file mode 100644 index 0000000..a112c09 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/kategori/show.blade.php @@ -0,0 +1,54 @@ +@extends('layouts.app') + +@section('content') + + +
+
+

{{ $kategoriKegiatan->nama_kategori }}

+
+ + Edit + +
+ @csrf + @method('DELETE') + +
+ + Kembali + +
+
+ +
+

Informasi Kategori

+ + + + + + + + + + + + + + + + + + + + + +
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
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/riwayat/detail-santri.blade.php b/sim-pkpps/resources/views/admin/kegiatan/riwayat/detail-santri.blade.php new file mode 100644 index 0000000..20be0e0 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/riwayat/detail-santri.blade.php @@ -0,0 +1,179 @@ +@extends('layouts.app') + +@section('content') + + + +
+
+
+

{{ $santri->nama_lengkap }}

+

+ ID: {{ $santri->id_santri }} | + Kelas: {{ $santri->kelas }} | + Status: {{ $santri->status }} +

+
+ + Kembali + +
+
+ + +
+
+

Total Hadir

+
{{ $stats['Hadir'] ?? 0 }}
+ +
+
+

Total Izin

+
{{ $stats['Izin'] ?? 0 }}
+ +
+
+

Total Sakit

+
{{ $stats['Sakit'] ?? 0 }}
+ +
+
+

Total Alpa

+
{{ $stats['Alpa'] ?? 0 }}
+ +
+
+ + +@if($statsByKategori->count() > 0) +
+

+ Kehadiran Per Kategori +

+ +
+@endif + + +@if($riwayat30Hari->count() > 0) +
+

+ Tren Kehadiran 30 Hari Terakhir +

+ +
+@endif + + +
+

+ Riwayat Lengkap +

+ + @if($riwayats->count() > 0) + + + + + + + + + + + + + @foreach($riwayats as $index => $riwayat) + + + + + + + + + @endforeach + +
NoTanggalKegiatanKategoriStatusWaktu
{{ $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)) : '-' }}
+ +
+ {{ $riwayats->links() }} +
+ @else +
+ +

Belum Ada Riwayat

+

Santri ini belum memiliki riwayat kehadiran.

+
+ @endif +
+ + + +@if($statsByKategori->count() > 0) + +@endif + +@if($riwayat30Hari->count() > 0) + +@endif +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/riwayat/edit.blade.php b/sim-pkpps/resources/views/admin/kegiatan/riwayat/edit.blade.php new file mode 100644 index 0000000..0a83414 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/riwayat/edit.blade.php @@ -0,0 +1,81 @@ +@extends('layouts.app') + +@section('content') + + +
+
+

Hanya status kehadiran dan waktu absen yang dapat diubah.

+
+ +
+ @csrf + @method('PUT') + +
+ + + + + + + + + + + + + + + + + + +
ID Absensi{{ $riwayat->absensi_id }}
Santri{{ $riwayat->santri->nama_lengkap }} ({{ $riwayat->santri->id_santri }})
Kegiatan{{ $riwayat->kegiatan->nama_kegiatan }}
Tanggal{{ $riwayat->tanggal->format('d F Y') }}
+
+ +
+ + + @error('status') + {{ $message }} + @enderror +
+ +
+ + + @error('waktu_absen') + {{ $message }} + @enderror + Kosongkan jika tidak ingin mengubah waktu absen +
+ +
+ + + Kembali + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/riwayat/index.blade.php b/sim-pkpps/resources/views/admin/kegiatan/riwayat/index.blade.php new file mode 100644 index 0000000..b602cf1 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/riwayat/index.blade.php @@ -0,0 +1,247 @@ +@extends('layouts.app') + +@section('content') + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + + +
+
+

Total Hadir

+
{{ $stats['Hadir'] ?? 0 }}
+ +
+
+

Total Izin

+
{{ $stats['Izin'] ?? 0 }}
+ +
+
+

Total Sakit

+
{{ $stats['Sakit'] ?? 0 }}
+ +
+
+

Total Alpa

+
{{ $stats['Alpa'] ?? 0 }}
+ +
+
+ + +@if(($stats['Hadir'] ?? 0) + ($stats['Izin'] ?? 0) + ($stats['Sakit'] ?? 0) + ($stats['Alpa'] ?? 0) > 0) +
+

+ Grafik Statistik Kehadiran +

+ +
+@endif + + +
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + @if(request()->hasAny(['id_santri', 'kategori_id', 'kegiatan_id', 'status', 'tanggal_dari', 'tanggal_sampai', 'bulan'])) + + + + @endif +
+
+
+ + @if($riwayats->count() > 0) + + + + + + + + + + + + + + + + @foreach($riwayats as $index => $riwayat) + + + + + + + + + + + + @endforeach + +
NoTanggalID SantriNama SantriKegiatanKategoriStatusMetodeAksi
{{ $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 + + + + + + + +
+ @csrf + @method('DELETE') + +
+
+ +
+ {{ $riwayats->links() }} +
+ @else +
+ +

Tidak Ada Riwayat

+

Belum ada data riwayat kegiatan dan absensi.

+
+ @endif +
+ +@if(($stats['Hadir'] ?? 0) + ($stats['Izin'] ?? 0) + ($stats['Sakit'] ?? 0) + ($stats['Alpa'] ?? 0) > 0) + + +@endif +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kegiatan/riwayat/show.blade.php b/sim-pkpps/resources/views/admin/kegiatan/riwayat/show.blade.php new file mode 100644 index 0000000..be94804 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kegiatan/riwayat/show.blade.php @@ -0,0 +1,131 @@ +@extends('layouts.app') + +@section('content') + + +
+
+

Riwayat Absensi #{{ $riwayat->absensi_id }}

+
+ + Edit + +
+ @csrf + @method('DELETE') + +
+ + Kembali + +
+
+ +
+

Informasi Santri

+ + + + + + + + + + + + + + + + + +
ID Santri{{ $riwayat->santri->id_santri }}
Nama Lengkap{{ $riwayat->santri->nama_lengkap }}
Kelas{{ $riwayat->santri->kelas }}
Status Santri{{ $riwayat->santri->status }}
+
+ +
+

Informasi Kegiatan

+ + + + + + + + + + + + + + + + + + + + + +
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 +
+
+ +
+

Detail Absensi

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kepulangan/create.blade.php b/sim-pkpps/resources/views/admin/kepulangan/create.blade.php new file mode 100644 index 0000000..95feb86 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kepulangan/create.blade.php @@ -0,0 +1,391 @@ +{{-- resources/views/admin/kepulangan/create.blade.php --}} + +@extends('layouts.app') + +@section('title', 'Tambah Izin Kepulangan') + +@section('content') + + +@if($errors->any()) +
+
    + @foreach($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+@endif + +
+
+ @csrf + +
+ + +
+ + {{-- Info Santri --}} + + +
+
+ + +
+ +
+ + +
+
+ + {{-- Info Durasi --}} + + +
+ + + + 0/500 karakter + +
+ +
+ + + + Batal + +
+
+
+ +{{-- Modal Konfirmasi Over Limit --}} + + + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kepulangan/edit.blade.php b/sim-pkpps/resources/views/admin/kepulangan/edit.blade.php new file mode 100644 index 0000000..3bcfe76 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kepulangan/edit.blade.php @@ -0,0 +1,163 @@ +{{-- resources/views/admin/kepulangan/edit.blade.php --}} + +@extends('layouts.app') + +@section('title', 'Edit Izin Kepulangan') + +@section('content') + + +@if($errors->any()) +
+
    + @foreach($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+@endif + +
+
+ @csrf + @method('PUT') + + {{-- Info Kepulangan --}} +
+

ID Kepulangan: {{ $kepulangan->id_kepulangan }}

+

Santri: {{ $kepulangan->santri->nama_lengkap }} ({{ $kepulangan->santri->id_santri }})

+

Status: + + {{ $kepulangan->status }} + +

+
+ +
+
+ + +
+ +
+ + +
+
+ + {{-- Info Durasi --}} +
+

Durasi Izin: {{ $kepulangan->durasi_izin }} hari

+
+ +
+ + + + {{ strlen(old('alasan', $kepulangan->alasan)) }}/500 karakter + +
+ +
+ + + Kembali + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kepulangan/index.blade.php b/sim-pkpps/resources/views/admin/kepulangan/index.blade.php new file mode 100644 index 0000000..395837d --- /dev/null +++ b/sim-pkpps/resources/views/admin/kepulangan/index.blade.php @@ -0,0 +1,501 @@ +{{-- resources/views/admin/kepulangan/index.blade.php --}} + +@extends('layouts.app') + +@section('title', 'Data Kepulangan Santri') + +@section('content') + + +{{-- Dashboard Cards --}} +
+
+

Total Data

+
{{ $stats['total_data'] }}
+ +
+
+

Menunggu Approval

+
{{ $stats['menunggu_approval'] }}
+ +
+
+

Sedang Izin

+
{{ $stats['sedang_izin'] }}
+ +
+
+

Over Limit

+
{{ $stats['over_limit_santri'] }}
+ +
+
+ +{{-- Flash Messages --}} +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +{{-- Main Content --}} +
+ {{-- Header Actions --}} + + + {{-- Filter Section --}} +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + Reset + +
+
+
+ + {{-- Data Table --}} +
+ + + + + + + + + + + + + + + @forelse($kepulangan as $item) + + + + + + + + + + + @empty + + + + @endforelse + +
IDSantriTanggal PulangTanggal KembaliDurasiAlasanStatusAksi
+ {{ $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->alasan }} + + + + {{ $item->status }} + + @if($item->is_aktif) +
Sedang Izin + @elseif($item->is_terlambat) +
Terlambat + @endif +
+
+ + + + + @if($item->status == 'Menunggu') + + + + + + @endif + + @if($item->status == 'Disetujui') + + + + + @endif + + @if(in_array($item->status, ['Menunggu', 'Ditolak'])) + + @endif +
+
+ +

Tidak ada data kepulangan ditemukan

+
+
+ + {{-- Pagination --}} + @if($kepulangan->hasPages()) +
+
+ Menampilkan {{ $kepulangan->firstItem() ?? 0 }} - {{ $kepulangan->lastItem() ?? 0 }} + dari {{ $kepulangan->total() }} data +
+
+ {{ $kepulangan->appends(request()->query())->links() }} +
+
+ @endif +
+ +{{-- Modal Approve --}} + + +{{-- Modal Reject --}} + + +{{-- Modal Delete --}} + + + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kepulangan/show.blade.php b/sim-pkpps/resources/views/admin/kepulangan/show.blade.php new file mode 100644 index 0000000..11ff8ad --- /dev/null +++ b/sim-pkpps/resources/views/admin/kepulangan/show.blade.php @@ -0,0 +1,454 @@ +{{-- resources/views/admin/kepulangan/show.blade.php --}} + +@extends('layouts.app') + +@section('title', 'Detail Kepulangan Santri') + +@section('content') + + +{{-- Flash Messages --}} +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +
+ {{-- Main Detail --}} +
+
+

Informasi Kepulangan

+ + {{ $kepulangan->status }} + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @if($kepulangan->approved_by) + + + + + + + + + @endif + @if($kepulangan->catatan) + + + + + @endif +
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 }}
+ + {{-- Action Buttons --}} +
+ + Kembali + + + @if($kepulangan->status == 'Menunggu') + + Edit + + + + @endif + + @if($kepulangan->status == 'Disetujui') + + Cetak Surat + + + @endif +
+
+ + {{-- Sidebar --}} +
+ {{-- Info Santri --}} +
+

+ Informasi Santri +

+ + + + + + + + + + + + + + + + + + + + + +
ID Santri:{{ $kepulangan->santri->id_santri }}
Nama:{{ $kepulangan->santri->nama_lengkap }}
NIS:{{ $kepulangan->santri->nis ?? '-' }}
Kelas:{{ $kepulangan->santri->kelas }}
Status: + + {{ $kepulangan->santri->status }} + +
+
+ + {{-- Statistik Penggunaan Izin --}} +
+

+ Statistik Izin {{ $kepulangan->tanggal_pulang->year }} +

+
+
+
+

{{ $detailIzin['total_hari'] }}

+ Total Hari +
+
+

{{ $detailIzin['total_izin'] }}

+ Total Izin +
+
+
+
+
+

+ {{ $detailIzin['sisa_kuota'] }} +

+ Sisa Kuota +
+
+ @if($detailIzin['over_limit']) + Over Limit + @else + Normal + @endif +
+
+
+ + {{-- Progress Bar --}} +
+ +
+
+
+
+
+ + {{-- Riwayat Izin --}} + @if(count($detailIzin['details']) > 0) +
+

+ Riwayat Izin {{ $kepulangan->tanggal_pulang->year }} +

+
+ @foreach($detailIzin['details'] as $detail) +
+ @if($detail['id'] == $kepulangan->id_kepulangan) +
+ +
+ @endif + {{ $detail['id'] }} +
{{ $detail['tanggal'] }}
+
+ + {{ $detail['durasi'] }} hari + +
+
+ {{ \Illuminate\Support\Str::limit($detail['alasan'], 50) }} +
+
+ @endforeach +
+
+ @endif +
+
+ +{{-- History Kepulangan Lainnya --}} +@if($history->count() > 0) +
+

+ Riwayat Kepulangan Lainnya +

+
+ + + + + + + + + + + + @foreach($history as $item) + + + + + + + + @endforeach + +
IDTanggalDurasiStatusAlasan
+ + {{ $item->id_kepulangan }} + + {{ $item->tanggal_pulang_formatted }} + + {{ $item->durasi_izin_calculated }} hari + + + + {{ $item->status }} + + {{ \Illuminate\Support\Str::limit($item->alasan, 30) }}
+
+
+@endif + +{{-- Modals (sama seperti di index) --}} + + + + + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kepulangan/surat-pdf.blade.php b/sim-pkpps/resources/views/admin/kepulangan/surat-pdf.blade.php new file mode 100644 index 0000000..bb3661b --- /dev/null +++ b/sim-pkpps/resources/views/admin/kepulangan/surat-pdf.blade.php @@ -0,0 +1,270 @@ +{{-- resources/views/admin/kepulangan/surat-pdf.blade.php --}} + + + + + + Surat Izin Kepulangan - {{ $kepulangan->id_kepulangan }} + + + + {{-- Watermark --}} +
{{ strtoupper($kepulangan->status) }}
+ + {{-- Header --}} +
+

Pondok Pesantren Al-Hikmah

+

Yayasan Pendidikan Islam

+

Jl. Raya Pesantren No. 123, Jakarta Selatan

+

Telp: (021) 123456 | Email: info@alhikmah.ac.id

+
+ + {{-- Title --}} +
+ Surat Izin Kepulangan Santri +
+ + {{-- Nomor Surat --}} +
+ Nomor: {{ $kepulangan->id_kepulangan }}/IZIN/{{ $kepulangan->tanggal_pulang->format('m') }}/{{ $kepulangan->tanggal_pulang->year }} +
+ + {{-- Content --}} +
+

Yang bertanda tangan di bawah ini, Pengurus Pondok Pesantren Al-Hikmah, dengan ini menerangkan bahwa:

+ +
+
+
Nama
+
:
+
{{ $santri->nama_lengkap }}
+
+
+
NIS
+
:
+
{{ $santri->nis ?? '-' }}
+
+
+
ID Santri
+
:
+
{{ $santri->id_santri }}
+
+
+
Kelas
+
:
+
{{ $santri->kelas }}
+
+
+
Jenis Kelamin
+
:
+
{{ $santri->jenis_kelamin }}
+
+
+
Alamat
+
:
+
{{ $santri->alamat_santri ?? $santri->daerah_asal ?? '-' }}
+
+
+ +

Adalah benar-benar santri aktif di Pondok Pesantren Al-Hikmah dan telah mendapat izin untuk pulang ke rumah dengan keterangan sebagai berikut:

+ +
+
+
Tanggal Pulang
+
:
+
{{ $kepulangan->tanggal_pulang->format('d F Y') }}
+
+
+
Tanggal Kembali
+
:
+
{{ $kepulangan->tanggal_kembali->format('d F Y') }}
+
+
+
Durasi Izin
+
:
+
{{ $kepulangan->durasi_izin_calculated }} hari
+
+
+
Alasan Kepulangan
+
:
+
{{ $kepulangan->alasan }}
+
+
+
Status Izin
+
:
+
{{ $kepulangan->status }}
+
+ @if($kepulangan->catatan) +
+
Catatan
+
:
+
{{ $kepulangan->catatan }}
+
+ @endif +
+ +

Demikian surat izin ini dibuat untuk dapat digunakan sebagaimana mestinya. Santri yang bersangkutan WAJIB kembali pada tanggal yang telah ditentukan.

+ +

Catatan Penting:

+
    +
  • Santri wajib kembali sesuai tanggal yang tertera dalam surat ini
  • +
  • Apabila terlambat kembali tanpa pemberitahuan, akan dikenakan sanksi sesuai peraturan pesantren
  • +
  • Surat ini harus dibawa dan ditunjukkan kepada petugas jaga saat keluar dan masuk pesantren
  • +
  • Orang tua/wali santri bertanggung jawab penuh selama santri berada di rumah
  • +
+
+ + {{-- Signature Section --}} +
+
+
+

Mengetahui,
Wali Santri

+
+
{{ $santri->nama_orang_tua ?? '........................' }}
+
+
+

Jakarta, {{ $tanggalCetak }}

+

Pengurus Pondok Pesantren

+
+
{{ $kepulangan->approved_by ?? 'Admin' }}
+

Bidang Kesiswaan

+
+
+
+ + {{-- Footer --}} + + + \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kesehatan-santri/cetak-surat.blade.php b/sim-pkpps/resources/views/admin/kesehatan-santri/cetak-surat.blade.php new file mode 100644 index 0000000..aa62a08 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kesehatan-santri/cetak-surat.blade.php @@ -0,0 +1,309 @@ + + + + + + Surat Izin Sakit - {{ $kesehatanSantri->santri->nama_lengkap }} + + + + + +
+

Pondok Pesantren [Nama Pesantren]

+

Unit Kesehatan Pesantren (UKP)

+

Alamat: [Alamat Lengkap Pesantren]

+

Telepon: [Nomor Telepon] | Email: [Email Pesantren]

+
+ + +
+ Nomor: UKP/{{ str_pad($kesehatanSantri->id, 3, '0', STR_PAD_LEFT) }}/{{ $kesehatanSantri->tanggal_masuk->format('m/Y') }} +
+ + +
+

Surat Keterangan Sakit

+
+ + +
+

Yang bertanda tangan di bawah ini, Petugas Unit Kesehatan Pesantren (UKP) + Pondok Pesantren [Nama Pesantren], dengan ini menerangkan bahwa:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @if($kesehatanSantri->tanggal_keluar) + + + + + + @endif + + + + + +
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:

+ +
+ @if($kesehatanSantri->status == 'izin') + ✓ Diberikan izin untuk istirahat/pulang selama masa pemulihan
+ ✓ Tidak mengikuti kegiatan fisik yang berat
+ ✓ Kontrol kembali jika gejala memburuk + @elseif($kesehatanSantri->status == 'sembuh') + ✓ Sudah sembuh dan dapat mengikuti kegiatan normal
+ ✓ Tetap menjaga kesehatan dan istirahat yang cukup
+ ✓ Konsultasi jika ada keluhan lanjutan + @else + ✓ Masih dalam perawatan di UKP
+ ✓ Memerlukan istirahat dan pengobatan lanjutan
+ ✓ Belum diperkenankan mengikuti kegiatan normal + @endif +
+ +

Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat digunakan sebagaimana mestinya.

+
+ + +
+ {{ $kesehatanSantri->tanggal_masuk->locale('id')->isoFormat('dddd, D MMMM Y') }} +
+ +
+
+

Wali Santri/Keluarga

+
+

{{ $kesehatanSantri->santri->nama_orang_tua ?: '(...............................)' }}

+
+
+ +
+

Petugas UKP

+
+
+ Cap & Tanda Tangan +
+

[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 newline at end of file diff --git a/sim-pkpps/resources/views/admin/kesehatan-santri/create.blade.php b/sim-pkpps/resources/views/admin/kesehatan-santri/create.blade.php new file mode 100644 index 0000000..21425b0 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kesehatan-santri/create.blade.php @@ -0,0 +1,195 @@ +@extends('layouts.app') + +@section('title', 'Tambah Data Kesehatan Santri') + +@section('content') + + + +
+
+

+ Form Data Kesehatan +

+ + Kembali + +
+ +
+ @csrf + + +
+ + + @error('id_santri') +
{{ $message }}
+ @enderror +
+ +
+ +
+ + + @error('tanggal_masuk') +
{{ $message }}
+ @enderror +
+ + +
+ + + @error('status') +
{{ $message }}
+ @enderror +
+
+ + + + + +
+ + + @error('keluhan') +
{{ $message }}
+ @enderror + Maksimal 1000 karakter +
+ + +
+ + + @error('catatan') +
{{ $message }}
+ @enderror + Maksimal 1000 karakter (opsional) +
+ + +
+ + Batal + + +
+
+
+ + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kesehatan-santri/edit.blade.php b/sim-pkpps/resources/views/admin/kesehatan-santri/edit.blade.php new file mode 100644 index 0000000..a90a548 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kesehatan-santri/edit.blade.php @@ -0,0 +1,201 @@ +@extends('layouts.app') + +@section('title', 'Edit Data Kesehatan Santri') + +@section('content') + + + +
+
+

+ Form Edit Data Kesehatan +

+ +
+ +
+ @csrf + @method('PUT') + + +
+ + + + @error('id_santri') +
{{ $message }}
+ @enderror + + Santri tidak dapat diubah setelah data disimpan + +
+ +
+ +
+ + + @error('tanggal_masuk') +
{{ $message }}
+ @enderror +
+ + +
+ + + @error('status') +
{{ $message }}
+ @enderror +
+
+ + +
+ + + @error('tanggal_keluar') +
{{ $message }}
+ @enderror + + Kosongkan jika santri masih dirawat + +
+ + +
+ + + @error('keluhan') +
{{ $message }}
+ @enderror + Maksimal 1000 karakter +
+ + +
+ + + @error('catatan') +
{{ $message }}
+ @enderror + Maksimal 1000 karakter (opsional) +
+ + +
+ + Batal + + +
+
+
+ + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kesehatan-santri/index.blade.php b/sim-pkpps/resources/views/admin/kesehatan-santri/index.blade.php new file mode 100644 index 0000000..b286620 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kesehatan-santri/index.blade.php @@ -0,0 +1,273 @@ +@extends('layouts.app') + +@section('title', 'Data Kesehatan Santri') + +@section('content') + + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + + +
+ + + + +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + Reset + +
+
+
+
+ + + + @if($kesehatanSantri->count() > 0) + + + + + + + + + + + + + + + + @foreach($kesehatanSantri as $index => $data) + + + + + + + + + + + + @endforeach + +
NoID SantriNama SantriTgl MasukKeluhanTgl KeluarStatusLamaAksi
{{ $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 +
+ + + + + + + + + @if($data->status == 'dirawat') + + @endif + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ + +
+ {{ $kesehatanSantri->appends(request()->query())->links() }} +
+ @else +
+ +

Tidak ada data kesehatan santri

+

Belum ada data kesehatan santri yang tercatat atau sesuai dengan filter yang dipilih.

+ + Tambah Data Kesehatan + +
+ @endif + + + + + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kesehatan-santri/riwayat.blade.php b/sim-pkpps/resources/views/admin/kesehatan-santri/riwayat.blade.php new file mode 100644 index 0000000..1bc5719 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kesehatan-santri/riwayat.blade.php @@ -0,0 +1,216 @@ +@extends('layouts.app') + +@section('title', 'Riwayat Kesehatan Santri') + +@section('content') + + + +
+
+
+

+ {{ $santri->nama_lengkap }} +

+

{{ $santri->id_santri }}

+
+ +
+ Kelas: {{ $santri->kelas }}
+ Jenis Kelamin: {{ $santri->jenis_kelamin }} +
+ +
+ Status: {{ $santri->status_badge }}
+ NIS: {{ $santri->nis ?: '-' }} +
+ +
+ Total Riwayat: {{ $riwayatKesehatan->total() }} kali
+ Orang Tua: {{ $santri->nama_orang_tua ?: '-' }} +
+
+
+ + +
+
+

Sedang Dirawat

+

{{ $riwayatKesehatan->where('status', 'dirawat')->count() }}

+
+
+ +
+

Sembuh

+

{{ $riwayatKesehatan->where('status', 'sembuh')->count() }}

+
+
+ +
+

Izin Pulang

+

{{ $riwayatKesehatan->where('status', 'izin')->count() }}

+
+
+ +
+

Rata-rata Dirawat

+

+ @if($riwayatKesehatan->count() > 0) + {{ round($riwayatKesehatan->avg(function($item) { return $item->lama_dirawat; }), 1) }} hari + @else + 0 hari + @endif +

+
+
+
+ + +
+
+

+ Riwayat Kesehatan Lengkap +

+ +
+ + @if($riwayatKesehatan->count() > 0) + + + + + + + + + + + + + + + + @foreach($riwayatKesehatan as $index => $data) + + + + + + + + + + + + @endforeach + +
NoID KesehatanTanggal MasukKeluhanCatatanTanggal KeluarStatusLamaAksi
{{ $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 + +
+ + + + + + + + + + + +
+
+ + +
+ {{ $riwayatKesehatan->links() }} +
+ + @else +
+ +

Belum Ada Riwayat Kesehatan

+

Santri {{ $santri->nama_lengkap }} belum memiliki riwayat kesehatan.

+ + Tambah Data Kesehatan + +
+ @endif +
+ + +@if($riwayatKesehatan->count() > 0) +
+

+ Informasi Tambahan +

+ +
+
+ Kunjungan Pertama:
+ {{ $riwayatKesehatan->sortBy('tanggal_masuk')->first()->tanggal_masuk->format('d M Y') }} +
+ +
+ Kunjungan Terakhir:
+ {{ $riwayatKesehatan->sortByDesc('tanggal_masuk')->first()->tanggal_masuk->format('d M Y') }} +
+ +
+ Lama Dirawat Terlama:
+ {{ $riwayatKesehatan->max('lama_dirawat') }} hari +
+ +
+ Total Hari Dirawat:
+ {{ $riwayatKesehatan->sum('lama_dirawat') }} hari +
+
+
+@endif + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/kesehatan-santri/show.blade.php b/sim-pkpps/resources/views/admin/kesehatan-santri/show.blade.php new file mode 100644 index 0000000..86f3a08 --- /dev/null +++ b/sim-pkpps/resources/views/admin/kesehatan-santri/show.blade.php @@ -0,0 +1,485 @@ +@extends('layouts.app') + +@section('title', 'Detail Kesehatan Santri') + +@section('content') + + + +
+
+
+

+ ID Kesehatan: {{ $kesehatanSantri->id_kesehatan }} +

+

+ Terakhir diupdate: {{ $kesehatanSantri->updated_at->locale('id')->isoFormat('D MMMM Y, HH:mm') }} WIB +

+
+
+ @if($kesehatanSantri->status == 'dirawat') + + @endif + + Edit + + + Kembali + +
+
+
+ + +
+
+
+

+ + Status Kesehatan +

+
+ + {{ strtoupper($kesehatanSantri->status) }} + +
+

Lama Dirawat

+

+ {{ $kesehatanSantri->lama_dirawat }} Hari +

+
+
+
+ @if($kesehatanSantri->status != 'dirawat') +
+ +

Selesai Perawatan

+
+ @endif +
+
+ + +
+ + +
+

+ + Informasi Santri +

+ +
+
+
+ + ID SANTRI +
+
+ {{ $kesehatanSantri->santri->id_santri }} +
+
+ +
+
+ + NAMA LENGKAP +
+
+ {{ $kesehatanSantri->santri->nama_lengkap }} +
+
+ +
+
+
+ + NIS +
+
{{ $kesehatanSantri->santri->nis ?: '-' }}
+
+ +
+
+ + Kelas +
+
{{ $kesehatanSantri->santri->kelas_lengkap }}
+
+ +
+
+ + Jenis Kelamin +
+
{{ $kesehatanSantri->santri->jenis_kelamin }}
+
+ +
+
+ + Status +
+
{!! $kesehatanSantri->santri->status_badge !!}
+
+
+ +
+
+
+ + Orang Tua / Wali +
+
{{ $kesehatanSantri->santri->nama_orang_tua ?: '-' }}
+
+ +
+
+ + Nomor HP +
+
+ @if($kesehatanSantri->santri->nomor_hp_ortu) + + {{ $kesehatanSantri->santri->nomor_hp_ortu }} + + @else + - + @endif +
+
+
+
+
+ + +
+

+ + Timeline Perawatan +

+ +
+ +
+ + +
+
+
+
+ + MASUK UKP +
+
+ {{ $kesehatanSantri->tanggal_masuk_formatted }} +
+
+ {{ $kesehatanSantri->tanggal_masuk->locale('id')->isoFormat('dddd, D MMMM Y') }} +
+
+ {{ $kesehatanSantri->tanggal_masuk->diffForHumans() }} +
+
+
+ + +
+
+
+
+ + KELUAR UKP +
+ @if($kesehatanSantri->tanggal_keluar) +
+ {{ $kesehatanSantri->tanggal_keluar_formatted }} +
+
+ {{ $kesehatanSantri->tanggal_keluar->locale('id')->isoFormat('dddd, D MMMM Y') }} +
+
+ {{ $kesehatanSantri->tanggal_keluar->diffForHumans() }} +
+ @else +
+ Belum Keluar +
+
+ Santri masih dalam perawatan +
+ @endif +
+
+
+
+
+ + +
+

+ + Informasi Medis +

+ +
+ +
+
+
+ +
+
+

Keluhan / Diagnosa

+

Gejala yang dialami

+
+
+
+

+ {{ $kesehatanSantri->keluhan }} +

+
+
+ + + @if($kesehatanSantri->catatan) +
+
+
+ +
+
+

Catatan Petugas

+

Informasi tambahan dari petugas UKP

+
+
+
+

+ {{ $kesehatanSantri->catatan }} +

+
+
+ @endif +
+
+ + +
+

+ Aksi Tersedia +

+
+ + Cetak Surat Izin + + + + Riwayat Kesehatan + + +
+ @csrf + @method('DELETE') + +
+
+
+ + +@if($riwayatKesehatan->count() > 0) +
+

+ + Riwayat Kesehatan Lainnya + + + {{ $riwayatKesehatan->count() }} data terakhir + +

+ + + + + + + + + + + + + + + @foreach($riwayatKesehatan as $riwayat) + + + + + + + + + + @endforeach + +
IDTanggal MasukKeluhanTanggal KeluarStatusLamaAksi
{{ $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 + + + +
+ + +
+@endif + + + + + + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/materi/create.blade.php b/sim-pkpps/resources/views/admin/materi/create.blade.php new file mode 100644 index 0000000..cd7ade4 --- /dev/null +++ b/sim-pkpps/resources/views/admin/materi/create.blade.php @@ -0,0 +1,133 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + + {{-- Info Box --}} +
+ + ID Materi Selanjutnya: {{ $nextIdMateri }} (Auto-generated) +
+ +
+ {{-- Kategori --}} +
+ + + @error('kategori') + {{ $message }} + @enderror +
+ + {{-- Kelas --}} +
+ + + @error('kelas') + {{ $message }} + @enderror +
+
+ + {{-- Nama Kitab --}} +
+ + + @error('nama_kitab') + {{ $message }} + @enderror +
+ +
+ {{-- Halaman Mulai --}} +
+ + + @error('halaman_mulai') + {{ $message }} + @enderror +
+ + {{-- Halaman Akhir --}} +
+ + + @error('halaman_akhir') + {{ $message }} + @enderror +
+ + {{-- Total Halaman (Auto) --}} +
+ + + Auto-calculated +
+
+ + {{-- Deskripsi --}} +
+ + + @error('deskripsi') + {{ $message }} + @enderror +
+ + {{-- Action Buttons --}} +
+ + + Kembali + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/materi/edit.blade.php b/sim-pkpps/resources/views/admin/materi/edit.blade.php new file mode 100644 index 0000000..edfd1f3 --- /dev/null +++ b/sim-pkpps/resources/views/admin/materi/edit.blade.php @@ -0,0 +1,134 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + @method('PUT') + + {{-- Info Box --}} +
+ + ID Materi: {{ $materi->id_materi }} +
+ +
+ {{-- Kategori --}} +
+ + + @error('kategori') + {{ $message }} + @enderror +
+ + {{-- Kelas --}} +
+ + + @error('kelas') + {{ $message }} + @enderror +
+
+ + {{-- Nama Kitab --}} +
+ + + @error('nama_kitab') + {{ $message }} + @enderror +
+ +
+ {{-- Halaman Mulai --}} +
+ + + @error('halaman_mulai') + {{ $message }} + @enderror +
+ + {{-- Halaman Akhir --}} +
+ + + @error('halaman_akhir') + {{ $message }} + @enderror +
+ + {{-- Total Halaman (Auto) --}} +
+ + + Auto-calculated +
+
+ + {{-- Deskripsi --}} +
+ + + @error('deskripsi') + {{ $message }} + @enderror +
+ + {{-- Action Buttons --}} +
+ + + Kembali + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/materi/index.blade.php b/sim-pkpps/resources/views/admin/materi/index.blade.php new file mode 100644 index 0000000..560c381 --- /dev/null +++ b/sim-pkpps/resources/views/admin/materi/index.blade.php @@ -0,0 +1,130 @@ +@extends('layouts.app') + +@section('content') + + +{{-- Alert Messages --}} +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +{{-- Filter & Search Section --}} +
+
+ + + + + + + + + @if(request()->anyFilled(['kategori', 'kelas', 'search'])) + + Reset + + @endif + + + Tambah Materi + +
+
+ +{{-- Table Section --}} +
+ @if($materis->count() > 0) + + + + + + + + + + + + + + + @foreach($materis as $index => $materi) + + + + + + + + + + + @endforeach + +
NoID MateriKategoriKelasNama KitabHalamanTotal HalAksi
{{ $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 + +
+ + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ + {{-- Pagination --}} +
+ {{ $materis->links() }} +
+ @else +
+ +

Belum Ada Data Materi

+

Silakan tambahkan materi pembelajaran Al-Qur'an, Hadist, atau Materi Tambahan.

+ + Tambah Materi Pertama + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/materi/show.blade.php b/sim-pkpps/resources/views/admin/materi/show.blade.php new file mode 100644 index 0000000..9228a07 --- /dev/null +++ b/sim-pkpps/resources/views/admin/materi/show.blade.php @@ -0,0 +1,125 @@ +@extends('layouts.app') + +@section('content') + + +
+ {{-- Header Section --}} +
+
+

{{ $materi->nama_kitab }}

+

ID: {{ $materi->id_materi }}

+
+ +
+ + {{-- Detail Section --}} +
+

Informasi 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
+
+ + {{-- Statistik Section (untuk nanti di Langkah 2-3) --}} +
+

Statistik Capaian

+
+ +

+ Fitur statistik capaian santri akan tersedia setelah implementasi Langkah 2: Input Capaian per Santri. +

+
+ + {{-- Uncomment ini setelah Langkah 2 selesai +
+
+

Total Santri

+
0
+

Santri yang ada capaian

+ +
+
+

Selesai 100%

+
0
+

Santri yang menyelesaikan

+ +
+
+

Rata-rata Progress

+
0%
+

Progress keseluruhan

+ +
+
+ --}} +
+ + {{-- Action Buttons --}} +
+ + Edit Materi + +
+ @csrf + @method('DELETE') + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-bukti.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-bukti.blade.php new file mode 100644 index 0000000..4a53293 --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-bukti.blade.php @@ -0,0 +1,159 @@ +{{-- resources/views/admin/pembayaran-spp/cetak-bukti.blade.php --}} + + + + + + Bukti Pembayaran SPP - {{ $pembayaranSpp->id_pembayaran }} + + + + +
+ +
+ +
+ +
+

PONDOK PESANTREN PKPPS

+

BUKTI PEMBAYARAN SPP

+

No. Bukti: {{ $pembayaranSpp->id_pembayaran }}

+
+ + + @if($pembayaranSpp->status === 'Lunas') +
+ ✓ LUNAS +
+ @else +
+ ⚠ BELUM LUNAS +
+ @endif + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @if($pembayaranSpp->keterangan) + + + + + + @endif +
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 }}
+
+ + +
+

JUMLAH PEMBAYARAN

+
{{ $pembayaranSpp->nominal_format }}
+
+ + +
+
+

Santri / Wali Santri

+ (                            ) +
+
+

Petugas

+ (                            ) +
+
+ + + +
+ + + + \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-laporan-santri.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-laporan-santri.blade.php new file mode 100644 index 0000000..3e42ff9 --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-laporan-santri.blade.php @@ -0,0 +1,158 @@ +{{-- resources/views/admin/pembayaran-spp/cetak-laporan-santri.blade.php --}} + + + + + + Laporan Pembayaran SPP - {{ $santri->nama_lengkap }} + + + + +
+ +
+ + +
+

PONDOK PESANTREN PKPPS

+

LAPORAN PEMBAYARAN SPP SANTRI

+

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 }}
+
+ + +
+
+

Total Terbayar

+
Rp {{ number_format($totalLunas, 0, ',', '.') }}
+
+
+

Total Tunggakan

+
Rp {{ number_format($totalTunggakan, 0, ',', '.') }}
+
+
+

Pembayaran Telat

+
{{ $jumlahTelat }}
+
+
+ + + + + + + + + + + + + + + + @forelse($pembayaranSpp as $index => $spp) + + + + + + + + + + @empty + + + + @endforelse + +
NoID PembayaranPeriodeNominalBatas BayarTanggal BayarStatus
{{ $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 +
+ + + + + \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-laporan.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-laporan.blade.php new file mode 100644 index 0000000..13b7dbc --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/cetak-laporan.blade.php @@ -0,0 +1,172 @@ +{{-- resources/views/admin/pembayaran-spp/cetak-laporan.blade.php --}} + + + + + + Laporan Pembayaran SPP + + + + +
+ +
+ + +
+

PONDOK PESANTREN PKPPS

+

LAPORAN PEMBAYARAN SPP

+

Tanggal Cetak: {{ date('d F Y, H:i') }} WIB

+
+ + + @if(request()->has('bulan') || request()->has('tahun') || request()->has('status')) +
+ + @if(request()->filled('bulan')) + + + + + @endif + @if(request()->filled('tahun')) + + + + + @endif + @if(request()->filled('status')) + + + + + @endif +
Bulan: {{ DateTime::createFromFormat('!m', request('bulan'))->format('F') }}
Tahun: {{ request('tahun') }}
Status: {{ request('status') }}
+
+ @endif + + +
+
+

Total Terbayar

+
Rp {{ number_format($totalLunas, 0, ',', '.') }}
+
+
+

Total Tunggakan

+
Rp {{ number_format($totalTunggakan, 0, ',', '.') }}
+
+
+

Pembayaran Telat

+
{{ $jumlahTelat }}
+
+
+

Total Data

+
{{ $pembayaranSpp->count() }}
+
+
+ + + + + + + + + + + + + + + + + @forelse($pembayaranSpp as $index => $spp) + + + + + + + + + + + @empty + + + + @endforelse + +
NoIDSantriPeriodeNominalBatas BayarTgl BayarStatus
{{ $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 newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/create.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/create.blade.php new file mode 100644 index 0000000..697c3bb --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/create.blade.php @@ -0,0 +1,171 @@ +{{-- resources/views/admin/pembayaran-spp/create.blade.php --}} +@extends('layouts.app') + +@section('title', 'Tambah Pembayaran SPP') + +@section('content') + + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +
+
+ @csrf + + +
+ + + ID akan digenerate otomatis saat data disimpan. +
+ + +
+ + + @error('id_santri') +
{{ $message }}
+ @enderror +
+ +
+ +
+ + + @error('bulan') +
{{ $message }}
+ @enderror +
+ + +
+ + + @error('tahun') +
{{ $message }}
+ @enderror +
+
+ +
+ +
+ + + @error('nominal') +
{{ $message }}
+ @enderror + Masukkan nominal tanpa titik atau koma. +
+ + +
+ + + @error('batas_bayar') +
{{ $message }}
+ @enderror +
+
+ +
+ +
+ + + @error('status') +
{{ $message }}
+ @enderror +
+ + +
+ + + @error('tanggal_bayar') +
{{ $message }}
+ @enderror + Kosongkan jika belum dibayar. +
+
+ + +
+ + + @error('keterangan') +
{{ $message }}
+ @enderror +
+ + +
+ + + Kembali + +
+
+
+ +@push('scripts') + +@endpush +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/edit.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/edit.blade.php new file mode 100644 index 0000000..b380b1c --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/edit.blade.php @@ -0,0 +1,176 @@ +{{-- resources/views/admin/pembayaran-spp/edit.blade.php --}} +@extends('layouts.app') + +@section('title', 'Edit Pembayaran SPP') + +@section('content') + + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +
+
+ @csrf + @method('PUT') + + +
+ + +
+ + +
+ + + @error('id_santri') +
{{ $message }}
+ @enderror +
+ +
+ +
+ + + @error('bulan') +
{{ $message }}
+ @enderror +
+ + +
+ + + @error('tahun') +
{{ $message }}
+ @enderror +
+
+ +
+ +
+ + + @error('nominal') +
{{ $message }}
+ @enderror +
+ + +
+ + + @error('batas_bayar') +
{{ $message }}
+ @enderror +
+
+ +
+ +
+ + + @error('status') +
{{ $message }}
+ @enderror +
+ + +
+ + + @error('tanggal_bayar') +
{{ $message }}
+ @enderror + Kosongkan jika belum dibayar. +
+
+ + +
+ + + @error('keterangan') +
{{ $message }}
+ @enderror +
+ + +
+ + + Kembali + +
+
+
+ +@push('scripts') + +@endpush +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/generate.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/generate.blade.php new file mode 100644 index 0000000..ca37662 --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/generate.blade.php @@ -0,0 +1,247 @@ +{{-- resources/views/admin/pembayaran-spp/generate.blade.php --}} +@extends('layouts.app') + +@section('title', 'Generate SPP Massal') + +@section('content') + + +
+
+ + Informasi: Fitur ini akan membuat data pembayaran SPP untuk semua santri aktif dalam periode yang ditentukan. + Data yang sudah ada akan dilewati (tidak duplikat). +
+ +
+ @csrf + +
+ +
+ + + @error('bulan') +
{{ $message }}
+ @enderror +
+ + +
+ + + @error('tahun') +
{{ $message }}
+ @enderror +
+
+ +
+ +
+ + + @error('nominal') +
{{ $message }}
+ @enderror + + Masukkan nominal pembayaran + +
+ + +
+ + + @error('batas_bayar') +
{{ $message }}
+ @enderror +
+
+ + +
+

+ + Data SPP akan dibuat untuk {{ \App\Models\Santri::where('status', 'Aktif')->count() }} santri dengan status Aktif.
+ + Pastikan data sudah benar sebelum melanjutkan. Proses ini tidak dapat dibatalkan. +

+
+ + + @php + $periodeGenerated = \App\Models\PembayaranSpp::selectRaw('DISTINCT bulan, tahun') + ->orderBy('tahun', 'desc') + ->orderBy('bulan', 'desc') + ->get(); + @endphp + + @if($periodeGenerated->count() > 0) +
+

+ Periode Yang Sudah Di-Generate: +

+
+ @foreach($periodeGenerated as $periode) + @php + $bulanNama = DateTime::createFromFormat('!m', $periode->bulan)->format('F'); + @endphp + + {{ $bulanNama }} {{ $periode->tahun }} + + @endforeach +
+

+ Jika Anda generate periode yang sudah ada, data akan di-skip (tidak duplikat). +

+
+ @endif + + +
+ + + Kembali + +
+
+
+ + + + +@push('styles') + +@endpush + +@push('scripts') + +@endpush +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/index.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/index.blade.php new file mode 100644 index 0000000..958285d --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/index.blade.php @@ -0,0 +1,159 @@ +{{-- resources/views/admin/pembayaran-spp/index.blade.php --}} +@extends('layouts.app') + +@section('title', 'Pembayaran SPP') + +@section('content') + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +
+ +
+ +
+ + + + + + + + + + + @if(request()->hasAny(['search', 'status', 'bulan', 'tahun'])) + + Reset + + @endif +
+ + + +
+ + +
+ + + + + + + + + + + + + + + @forelse($pembayaranSpp as $index => $spp) + + + + + + + + + + + @empty + + + + @endforelse + +
NoID PembayaranSantriPeriodeNominalBatas BayarStatusAksi
{{ $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 !!} + + + + + + +
+ @csrf + @method('DELETE') + +
+
+ +

Tidak ada data pembayaran SPP.

+
+
+ + + @if($pembayaranSpp->hasPages()) +
+ {{ $pembayaranSpp->links() }} +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/laporan.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/laporan.blade.php new file mode 100644 index 0000000..9708c54 --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/laporan.blade.php @@ -0,0 +1,99 @@ +{{-- resources/views/admin/pembayaran-spp/laporan.blade.php --}} +@extends('layouts.app') + +@section('title', 'Laporan Pembayaran SPP') + +@section('content') + + +
+ +
+

Laporan Semua Data

+

+ Cetak laporan semua pembayaran SPP atau dengan filter tertentu +

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + +
+

Laporan Per Santri

+

+ Cetak laporan pembayaran SPP untuk santri tertentu +

+
+
+ + +
+ + +
+
+
+ + + +@push('scripts') + +@endpush +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/riwayat.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/riwayat.blade.php new file mode 100644 index 0000000..c1d57a3 --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/riwayat.blade.php @@ -0,0 +1,127 @@ +{{-- resources/views/admin/pembayaran-spp/riwayat.blade.php --}} +@extends('layouts.app') + +@section('title', 'Riwayat Pembayaran SPP') + +@section('content') + + + +
+
+
+

{{ $santri->nama_lengkap }}

+

+ {{ $santri->id_santri }} • {{ $santri->nis ?? '-' }} • {{ $santri->kelas_lengkap }} +

+
+ +
+
+ + +
+
+

Total Terbayar

+
{{ 'Rp ' . number_format($totalBayar, 0, ',', '.') }}
+ +
+ +
+

Total Tunggakan

+
{{ 'Rp ' . number_format($totalTunggakan, 0, ',', '.') }}
+ +
+ +
+

Pembayaran Telat

+
{{ $jumlahTelat }}
+ +
+
+ + +
+

+ Daftar Pembayaran +

+ +
+ + + + + + + + + + + + + + + @forelse($pembayaranSpp as $index => $spp) + + + + + + + + + + + @empty + + + + @endforelse + +
NoID PembayaranPeriodeNominalBatas BayarTanggal BayarStatusAksi
{{ $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.

+
+
+ + + @if($pembayaranSpp->hasPages()) +
+ {{ $pembayaranSpp->links() }} +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/pembayaran-spp/show.blade.php b/sim-pkpps/resources/views/admin/pembayaran-spp/show.blade.php new file mode 100644 index 0000000..150873b --- /dev/null +++ b/sim-pkpps/resources/views/admin/pembayaran-spp/show.blade.php @@ -0,0 +1,135 @@ +{{-- resources/views/admin/pembayaran-spp/show.blade.php --}} +@extends('layouts.app') + +@section('title', 'Detail Pembayaran SPP') + +@section('content') + + +
+ +
+

{{ $pembayaranSpp->id_pembayaran }}

+ +
+ + +
+

Informasi Pembayaran

+ + + + + + + + + + + + + + + + + + + + + + + + + + @if($pembayaranSpp->keterangan) + + + + + @endif +
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 }}
+
+ + +
+

Informasi Santri

+ + + + + + + + + + + + + + + + + + + + + + + + + +
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 + +
+
+ + +
+

Metadata

+ + + + + + + + + +
Dibuat Pada{{ $pembayaranSpp->created_at->format('d F Y, H:i') }} WIB
Diperbarui Pada{{ $pembayaranSpp->updated_at->format('d F Y, H:i') }} WIB
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/riwayat_pelanggaran/create.blade.php b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/create.blade.php new file mode 100644 index 0000000..8951b5d --- /dev/null +++ b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/create.blade.php @@ -0,0 +1,156 @@ +{{-- resources/views/admin/riwayat_pelanggaran/create.blade.php --}} +@extends('layouts.app') + +@section('title', 'Tambah Riwayat Pelanggaran') + +@section('content') + + + +
+ +
+ +
+
+

+ Form Tambah Riwayat +

+
+ ID Riwayat Berikutnya: + {{ $nextIdRiwayat }} +
+
+ +
+ @csrf + +
+ +
+ + + @error('id_santri') + {{ $message }} + @enderror +
+ + +
+ + + @error('tanggal') + {{ $message }} + @enderror +
+
+ + +
+ + + @error('id_kategori') + {{ $message }} + @enderror + + + +
+ + +
+ + + @error('keterangan') + {{ $message }} + @enderror + Maksimal 1000 karakter +
+ +
+ + + Kembali + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/riwayat_pelanggaran/edit.blade.php b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/edit.blade.php new file mode 100644 index 0000000..0869bf5 --- /dev/null +++ b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/edit.blade.php @@ -0,0 +1,156 @@ +{{-- resources/views/admin/riwayat_pelanggaran/edit.blade.php --}} +@extends('layouts.app') + +@section('title', 'Edit Riwayat Pelanggaran') + +@section('content') + + + +
+ +
+ +
+
+

+ Form Edit Riwayat +

+
+ ID Riwayat: + {{ $riwayatPelanggaran->id_riwayat }} +
+
+ +
+ @csrf + @method('PUT') + +
+ +
+ + + @error('id_santri') + {{ $message }} + @enderror +
+ + +
+ + + @error('tanggal') + {{ $message }} + @enderror +
+
+ + +
+ + + @error('id_kategori') + {{ $message }} + @enderror + + + +
+ + +
+ + + @error('keterangan') + {{ $message }} + @enderror + Maksimal 1000 karakter +
+ +
+ + + Kembali + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/riwayat_pelanggaran/index.blade.php b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/index.blade.php new file mode 100644 index 0000000..66c71e1 --- /dev/null +++ b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/index.blade.php @@ -0,0 +1,264 @@ +{{-- resources/views/admin/riwayat_pelanggaran/index.blade.php --}} +@extends('layouts.app') + +@section('title', 'Riwayat Pelanggaran') + +@section('content') + + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + + +
+
+

Total Pelanggaran

+
{{ $totalPelanggaran }}
+

Semua Riwayat

+ +
+ +
+

Bulan Ini

+
{{ $pelanggaranBulanIni }}
+

{{ \Carbon\Carbon::now()->format('F Y') }}

+ +
+ +
+

Total Poin

+
{{ $totalPoin }}
+

Akumulasi Poin

+ +
+
+ + +
+

+ Filter & Pencarian +

+ +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ +
+ + + Reset + + +
+
+
+ + +
+
+

+ Daftar Riwayat Pelanggaran +

+ + Tambah Riwayat + +
+ + @if($data->isNotEmpty()) + + + + + + + + + + + + + + @foreach($data as $index => $item) + + + + + + + + + + @endforeach + +
NoID RiwayatTanggalSantriKategori PelanggaranPoinAksi
{{ $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 }} + + +
+ + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ + +
+ {{ $data->links() }} +
+ @else +
+ +

Belum ada riwayat pelanggaran

+

Silakan tambah riwayat pelanggaran baru menggunakan tombol di atas.

+ + Tambah Riwayat + +
+ @endif +
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/riwayat_pelanggaran/riwayat_santri.blade.php b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/riwayat_santri.blade.php new file mode 100644 index 0000000..68666b2 --- /dev/null +++ b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/riwayat_santri.blade.php @@ -0,0 +1,205 @@ +{{-- resources/views/admin/riwayat_pelanggaran/riwayat_santri.blade.php --}} +@extends('layouts.app') + +@section('title', 'Riwayat Pelanggaran - ' . $santri->nama_lengkap) + +@section('content') + + + +
+ +
+ + +
+
+

Data Santri

+
+ {{ $santri->nama_lengkap }} +

+ {{ $santri->id_santri }} | {{ $santri->kelas }} +

+
+ +
+ +
+

Total Pelanggaran

+
{{ $totalPelanggaran }}
+

Jumlah Pelanggaran

+ +
+ +
+

Total Poin

+
{{ $totalPoin }}
+

Akumulasi Poin

+ +
+
+ + +
+
+

+ Daftar Riwayat Pelanggaran +

+ +
+ + @if($riwayat->isNotEmpty()) + + + + + + + + + + + + + + @foreach($riwayat as $index => $item) + + + + + + + + + + @endforeach + +
NoID RiwayatTanggalKategori PelanggaranPoinKeteranganAksi
{{ $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) +
+ {{ $item->keterangan }} +
+ @else + - + @endif +
+
+ + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ + +
+ {{ $riwayat->links() }} +
+ @else +
+ +

Belum ada riwayat pelanggaran

+

Santri {{ $santri->nama_lengkap }} belum memiliki catatan pelanggaran.

+ + Tambah Pelanggaran + +
+ @endif +
+ + +@if($totalPoin > 0) +
+

+ Analisis Pelanggaran +

+ +
+
+ +
+ {{ $totalPelanggaran > 0 ? number_format($totalPoin / $totalPelanggaran, 1) : 0 }} +
+

Rata-rata Poin/Pelanggaran

+
+ +
+ +
+ @if($totalPoin >= 50) + Berat + @elseif($totalPoin >= 20) + Sedang + @else + Ringan + @endif +
+

Kategori Pelanggaran

+
+ +
+ +
+ {{ $riwayat->first() ? \Carbon\Carbon::parse($riwayat->first()->tanggal)->format('d M Y') : '-' }} +
+

Pelanggaran Terakhir

+
+
+
+@endif +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/riwayat_pelanggaran/show.blade.php b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/show.blade.php new file mode 100644 index 0000000..9dec30b --- /dev/null +++ b/sim-pkpps/resources/views/admin/riwayat_pelanggaran/show.blade.php @@ -0,0 +1,291 @@ +{{-- resources/views/admin/riwayat_pelanggaran/show.blade.php --}} +@extends('layouts.app') + +@section('title', 'Detail Riwayat Pelanggaran') + +@section('content') + + + +
+ +
+ +
+ +
+
+
+

Informasi Riwayat

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
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
+
+ + +
+

+ Data Santri +

+ + @if($riwayatPelanggaran->santri) + + + + + + + + + + + + + + + + + + + + + +
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 + +
+ + + @else +
+ +

Data santri tidak ditemukan

+
+ @endif +
+ + +
+

+ Kategori Pelanggaran +

+ + @if($riwayatPelanggaran->kategori) + + + + + + + + + + + + + +
ID Kategori + {{ $riwayatPelanggaran->kategori->id_kategori }} +
Nama Pelanggaran + {{ $riwayatPelanggaran->kategori->nama_pelanggaran }} +
Poin Kategori + + {{ $riwayatPelanggaran->kategori->poin }} Poin + +
+ + + @else +
+ +

Kategori tidak ditemukan

+
+ @endif +
+ + + @if($riwayatPelanggaran->keterangan) +
+

+ Keterangan Tambahan +

+
+

{{ $riwayatPelanggaran->keterangan }}

+
+
+ @endif + + + @if($riwayatLainnya->isNotEmpty()) +
+

+ Riwayat Pelanggaran Lainnya (Santri yang Sama) +

+ + + + + + + + + + + + + @foreach($riwayatLainnya as $index => $riwayat) + + + + + + + + @endforeach + +
NoTanggalKategoriPoinAksi
{{ $index + 1 }}{{ \Carbon\Carbon::parse($riwayat->tanggal)->format('d M Y') }} + @if($riwayat->kategori) + {{ $riwayat->kategori->nama_pelanggaran }} + @else + - + @endif + + {{ $riwayat->poin }} + + + + +
+ + +
+ @endif +
+ + +
+ +
+

+ Aksi Cepat +

+ +
+ + +
+

+ Poin Pelanggaran +

+
+
{{ $riwayatPelanggaran->poin }}
+

Poin Pelanggaran Ini

+
+ + @if($riwayatPelanggaran->santri) +
+
+
+ {{ $riwayatPelanggaran->santri->total_poin_pelanggaran }} +
+

Total Poin Santri

+
+
+ @endif +
+ + +
+

+ Informasi +

+

+ 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') }}. +

+
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/santri/create.blade.php b/sim-pkpps/resources/views/admin/santri/create.blade.php new file mode 100644 index 0000000..6633b01 --- /dev/null +++ b/sim-pkpps/resources/views/admin/santri/create.blade.php @@ -0,0 +1,24 @@ +@extends('layouts.app', ['isAdmin' => true]) + +@section('title', 'Tambah Santri') + +@section('content') + + +
+ @if ($errors->any()) +
+ Terdapat kesalahan: +
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + + @include('admin.santri.form') +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/santri/edit.blade.php b/sim-pkpps/resources/views/admin/santri/edit.blade.php new file mode 100644 index 0000000..c12fd2e --- /dev/null +++ b/sim-pkpps/resources/views/admin/santri/edit.blade.php @@ -0,0 +1,31 @@ +@extends('layouts.app', ['isAdmin' => true]) + +@section('title', 'Edit Santri: ' . $santri->nama_lengkap) + +@section('content') + + +
+
+

+ + Sedang mengedit data: {{ $santri->nama_lengkap }} ({{ $santri->id_santri }}) +

+
+ + @if ($errors->any()) +
+ Terdapat kesalahan: +
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + + @include('admin.santri.form', ['santri' => $santri]) +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/santri/form.blade.php b/sim-pkpps/resources/views/admin/santri/form.blade.php new file mode 100644 index 0000000..ce5dfe5 --- /dev/null +++ b/sim-pkpps/resources/views/admin/santri/form.blade.php @@ -0,0 +1,96 @@ +@php + $isEdit = isset($santri); +@endphp + +
+ @csrf + @if ($isEdit) + @method('PUT') + @endif + +
+ + + {{ $isEdit ? 'ID Santri tidak dapat diubah.' : 'ID akan otomatis di-generate (Contoh: ' . ($nextIdSantri ?? 'S001') . ')' }} +
+ +
+ + + @error('nis')
{{ $message }}
@enderror +
+ +
+ + + @error('nama_lengkap')
{{ $message }}
@enderror +
+ +
+ + + @error('jenis_kelamin')
{{ $message }}
@enderror +
+ +
+ + + @error('kelas')
{{ $message }}
@enderror +
+ +
+ + + @error('status')
{{ $message }}
@enderror +
+ +
+ + + @error('alamat_santri')
{{ $message }}
@enderror +
+ +
+ + + @error('daerah_asal')
{{ $message }}
@enderror +
+ +
+

Data Orang Tua / Wali

+ +
+ + + @error('nama_orang_tua')
{{ $message }}
@enderror +
+ +
+ + + @error('nomor_hp_ortu')
{{ $message }}
@enderror +
+ +
+ + + Batal + +
+
\ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/santri/index.blade.php b/sim-pkpps/resources/views/admin/santri/index.blade.php new file mode 100644 index 0000000..bfe3d4f --- /dev/null +++ b/sim-pkpps/resources/views/admin/santri/index.blade.php @@ -0,0 +1,141 @@ +@extends('layouts.app', ['isAdmin' => true]) + +@section('title', 'Data Santri') + +@section('content') + + +@if (session('success')) +
+ {{ session('success') }} +
+@endif + +
+ +
+ + + Tambah Santri + + + +
+ + + + + + + + + @if(request('search') || request('status') || request('kelas')) + + Reset + + @endif +
+
+ + + + + + + + + + + + + + + + @forelse ($santris as $index => $santri) + + + + + + + + + + + @empty + + + + @endforelse + +
NoID SantriNISNama LengkapJenis KelaminKelasStatusAksi
{{ $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 + + + + + +
+ @csrf + @method('DELETE') + +
+
+ + @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 +
+ + @if($santris->count() > 0) +
+

+ + Menampilkan {{ $santris->count() }} data santri + @if(request('search') || request('status') || request('kelas')) + dari hasil pencarian/filter + @endif +

+
+ @endif + + + @if(method_exists($santris, 'links')) +
+ {{ $santris->links() }} +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/santri/show.blade.php b/sim-pkpps/resources/views/admin/santri/show.blade.php new file mode 100644 index 0000000..6da5f1c --- /dev/null +++ b/sim-pkpps/resources/views/admin/santri/show.blade.php @@ -0,0 +1,164 @@ +@extends('layouts.app', ['isAdmin' => true]) + +@section('title', 'Detail Santri: ' . $santri->nama_lengkap) + +@section('content') + + +
+
+
+

{{ $santri->nama_lengkap }}

+

+ {{ $santri->id_santri }} + @if($santri->nis) + | NIS: {{ $santri->nis }} + @endif +

+
+ +
+ +
+ +
+

Informasi Dasar

+ + + + + + + + + + + + + + + + + + + + + + + + + +
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 & Asal

+ + + + + + + + + +
Alamat Santri{{ $santri->alamat_santri ?? '-' }}
Daerah Asal + @if($santri->daerah_asal) + {{ $santri->daerah_asal }} + @else + - + @endif +
+
+ +
+

Data Orang Tua / Wali

+ + + + + + + + + +
Nama Orang Tua{{ $santri->nama_orang_tua ?? '-' }}
Nomor HP Orang Tua + @if($santri->nomor_hp_ortu) + + + {{ $santri->nomor_hp_ortu }} + + @else + - + @endif +
+
+ +
+

Informasi Sistem

+ + + + + + + + + +
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. +

+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/semester/create.blade.php b/sim-pkpps/resources/views/admin/semester/create.blade.php new file mode 100644 index 0000000..8b7aa95 --- /dev/null +++ b/sim-pkpps/resources/views/admin/semester/create.blade.php @@ -0,0 +1,86 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + +
+ + ID Semester Selanjutnya: {{ $nextIdSemester }} (Auto-generated) +
+ +
+ {{-- Tahun Ajaran --}} +
+ + + Format: YYYY/YYYY + @error('tahun_ajaran') + {{ $message }} + @enderror +
+ + {{-- Periode --}} +
+ + + @error('periode') + {{ $message }} + @enderror +
+
+ +
+ {{-- Tanggal Mulai --}} +
+ + + @error('tanggal_mulai') + {{ $message }} + @enderror +
+ + {{-- Tanggal Akhir --}} +
+ + + @error('tanggal_akhir') + {{ $message }} + @enderror +
+
+ + {{-- Status Aktif --}} +
+ + Hanya 1 semester yang bisa aktif. Jika dicentang, semester lain akan otomatis non-aktif. +
+ + {{-- Action Buttons --}} +
+ + + Kembali + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/semester/edit.blade.php b/sim-pkpps/resources/views/admin/semester/edit.blade.php new file mode 100644 index 0000000..85cbd56 --- /dev/null +++ b/sim-pkpps/resources/views/admin/semester/edit.blade.php @@ -0,0 +1,88 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + @method('PUT') + +
+ + ID Semester: {{ $semester->id_semester }} +
+ +
+ {{-- Tahun Ajaran --}} +
+ + + Format: YYYY/YYYY + @error('tahun_ajaran') + {{ $message }} + @enderror +
+ + {{-- Periode --}} +
+ + + @error('periode') + {{ $message }} + @enderror +
+
+ +
+ {{-- Tanggal Mulai --}} +
+ + + @error('tanggal_mulai') + {{ $message }} + @enderror +
+ + {{-- Tanggal Akhir --}} +
+ + + @error('tanggal_akhir') + {{ $message }} + @enderror +
+
+ + {{-- Status Aktif --}} +
+ + Hanya 1 semester yang bisa aktif. Jika dicentang, semester lain akan otomatis non-aktif. +
+ + {{-- Action Buttons --}} +
+ + + Kembali + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/semester/index.blade.php b/sim-pkpps/resources/views/admin/semester/index.blade.php new file mode 100644 index 0000000..22d486b --- /dev/null +++ b/sim-pkpps/resources/views/admin/semester/index.blade.php @@ -0,0 +1,104 @@ +@extends('layouts.app') + +@section('content') + + +{{-- Alert Messages --}} +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +{{-- Action Buttons --}} + + +{{-- Table Section --}} +
+ @if($semesters->count() > 0) + + + + + + + + + + + + + + + @foreach($semesters as $index => $semester) + + + + + + + + + + + @endforeach + +
NoID SemesterNama SemesterTahun AjaranPeriodeTanggalStatusAksi
{{ $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 !!} +
+ + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ + {{-- Pagination --}} +
+ {{ $semesters->links() }} +
+ @else +
+ +

Belum Ada Semester

+

Silakan tambahkan semester terlebih dahulu sebelum mengelola capaian santri.

+ + Tambah Semester Pertama + +
+ @endif +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/semester/show.blade.php b/sim-pkpps/resources/views/admin/semester/show.blade.php new file mode 100644 index 0000000..b15753b --- /dev/null +++ b/sim-pkpps/resources/views/admin/semester/show.blade.php @@ -0,0 +1,94 @@ +@extends('layouts.app') + +@section('content') + + +
+ {{-- Header Section --}} +
+
+

{{ $semester->nama_semester }}

+

{{ $semester->id_semester }}

+
+ +
+ + {{-- Statistik Cards --}} +
+
+

Total Capaian

+
{{ $totalCapaian }}
+

Data capaian tercatat

+ +
+
+

Santri Aktif

+
{{ $santriUnik }}
+

Santri dengan capaian

+ +
+
+

Rata-rata Progress

+
{{ number_format($rataRataPersentase, 1) }}%
+

Progress keseluruhan

+ +
+
+ + {{-- Detail Section --}} +
+

Informasi Semester

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/uang-saku/create.blade.php b/sim-pkpps/resources/views/admin/uang-saku/create.blade.php new file mode 100644 index 0000000..1a5ca83 --- /dev/null +++ b/sim-pkpps/resources/views/admin/uang-saku/create.blade.php @@ -0,0 +1,122 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + +
+ + + @error('id_santri') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('jenis_transaksi') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('nominal') +
{{ $message }}
+ @enderror + Masukkan nominal tanpa titik atau koma +
+ +
+ + + @error('tanggal_transaksi') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('keterangan') +
{{ $message }}
+ @enderror + Opsional - Jelaskan detail transaksi +
+ +
+ + + Kembali + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/uang-saku/edit.blade.php b/sim-pkpps/resources/views/admin/uang-saku/edit.blade.php new file mode 100644 index 0000000..0c9fd14 --- /dev/null +++ b/sim-pkpps/resources/views/admin/uang-saku/edit.blade.php @@ -0,0 +1,101 @@ +@extends('layouts.app') + +@section('content') + + +
+
+ @csrf + @method('PUT') + +
+ + + Santri tidak dapat diubah +
+ +
+ + + @error('jenis_transaksi') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('nominal') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('tanggal_transaksi') +
{{ $message }}
+ @enderror +
+ +
+ + + @error('keterangan') +
{{ $message }}
+ @enderror +
+ +
+ + + Kembali + +
+
+
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/uang-saku/index.blade.php b/sim-pkpps/resources/views/admin/uang-saku/index.blade.php new file mode 100644 index 0000000..2ebc76a --- /dev/null +++ b/sim-pkpps/resources/views/admin/uang-saku/index.blade.php @@ -0,0 +1,219 @@ +@extends('layouts.app') + +@section('content') + + +@if(session('success')) +
+ {{ session('success') }} +
+@endif + +@if(session('error')) +
+ {{ session('error') }} +
+@endif + +{{-- Main Content --}} +
+ {{-- Header Actions --}} + + + {{-- Filter Section --}} +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + @if(request()->hasAny(['search', 'id_santri', 'jenis_transaksi', 'tanggal_dari', 'tanggal_sampai'])) + + Reset + + @endif +
+
+
+ + {{-- Tabel Transaksi --}} + @if($transaksi->count() > 0) + + + + + + + + + + + + + + + + @foreach($transaksi as $index => $item) + + + + + + + + + + + + @endforeach + +
NoID TransaksiSantriTanggalJenisNominalKeteranganSaldoAksi
{{ $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 }} + + +
+ + + + + + +
+ @csrf + @method('DELETE') + +
+
+
+ +
+ {{ $transaksi->links() }} +
+ @else +
+ +

Belum Ada Transaksi

+

Belum ada transaksi uang saku yang tercatat. Tambahkan transaksi pertama!

+ + Tambah Transaksi + +
+ @endif +
+ + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/uang-saku/riwayat.blade.php b/sim-pkpps/resources/views/admin/uang-saku/riwayat.blade.php new file mode 100644 index 0000000..eb967c2 --- /dev/null +++ b/sim-pkpps/resources/views/admin/uang-saku/riwayat.blade.php @@ -0,0 +1,383 @@ +@extends('layouts.app') + +@section('content') + + +{{-- Filter Periode --}} +
+
+
+
+ + +
+ +
+ + +
+ +
+ + + + + + Reset + +
+
+
+
+ +{{-- Info Periode --}} +
+

+ + Periode: + {{ $periodeDari->format('d F Y') }} - {{ $periodeSampai->format('d F Y') }} + ({{ $periodeDari->diffInDays($periodeSampai) + 1 }} hari) +

+
+ + +
+
+

Total Pemasukan

+
Rp {{ number_format($totalPemasukan, 0, ',', '.') }}
+

+ Periode yang dipilih +

+ +
+ +
+

Total Pengeluaran

+
Rp {{ number_format($totalPengeluaran, 0, ',', '.') }}
+

+ Periode yang dipilih +

+ +
+ +
+

Selisih

+
+ Rp {{ number_format($totalPemasukan - $totalPengeluaran, 0, ',', '.') }} +
+

+ Pemasukan - Pengeluaran +

+ +
+ +
+

Saldo Saat Ini

+
+ Rp {{ number_format($saldoTerakhir, 0, ',', '.') }} +
+

+ Total keseluruhan +

+ +
+
+ + +
+

+ Grafik Arus Uang Saku +

+ +
+ + + + + +
+

+ Daftar Transaksi + @if($transaksi->total() > 0) + ({{ $transaksi->total() }} transaksi) + @endif +

+ + @if($transaksi->count() > 0) + + + + + + + + + + + + + + + + @foreach($transaksi as $index => $item) + + + + + + + + + + + + @endforeach + +
NoID TransaksiTanggalJenisNominalSaldo SebelumSaldo SesudahKeteranganAksi
{{ $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 ?? '-' }} +
+
+ +
+ +
+ {{ $transaksi->links() }} +
+ @else +
+ +

Tidak Ada Transaksi

+

Tidak ada transaksi pada periode {{ $periodeDari->format('d F Y') }} - {{ $periodeSampai->format('d F Y') }}

+ + Tambah Transaksi + +
+ @endif +
+ + + + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/uang-saku/show.blade.php b/sim-pkpps/resources/views/admin/uang-saku/show.blade.php new file mode 100644 index 0000000..daa4bf6 --- /dev/null +++ b/sim-pkpps/resources/views/admin/uang-saku/show.blade.php @@ -0,0 +1,132 @@ +@extends('layouts.app') + +@section('content') + + +
+
+

{{ $transaksi->id_uang_saku }}

+ +
+ +
+

Informasi 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 ?? '-' }}
+
+ +
+

Rincian Saldo

+ + + + + + + + + + + + + +
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 }} + +
+
+ +
+

Informasi Waktu

+ + + + + + + + + +
Dibuat Pada{{ $transaksi->created_at->format('d F Y, H:i') }} WIB
Terakhir Diubah{{ $transaksi->updated_at->format('d F Y, H:i') }} WIB
+
+ + {{-- Action Buttons Bottom --}} +
+ + Lihat Riwayat + + + Edit Transaksi + +
+ @csrf + @method('DELETE') + +
+
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/users/create_account.blade.php b/sim-pkpps/resources/views/admin/users/create_account.blade.php new file mode 100644 index 0000000..98b7feb --- /dev/null +++ b/sim-pkpps/resources/views/admin/users/create_account.blade.php @@ -0,0 +1,57 @@ +@extends('layouts.app', ['isAdmin' => true]) + +@section('title', 'Buat Akun ' . ucfirst($role)) + +@section('content') + + +
+
+ @csrf + +
+ + + @error('role_id')
{{ $message }}
@enderror + Hanya menampilkan data {{ $role }} yang belum memiliki akun login. +
+ +
+ + + @error('username')
{{ $message }}
@enderror + Contoh: {{ $role == 'santri' ? 'S001' : 'WS001' }} +
+ +
+ + + @error('password')
{{ $message }}
@enderror +
+ +
+ + +
+ + + Batal +
+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/users/santri_accounts.blade.php b/sim-pkpps/resources/views/admin/users/santri_accounts.blade.php new file mode 100644 index 0000000..40a04ea --- /dev/null +++ b/sim-pkpps/resources/views/admin/users/santri_accounts.blade.php @@ -0,0 +1,52 @@ +@extends('layouts.app', ['isAdmin' => true]) + +@section('title', 'Manajemen Akun Santri') + +@section('content') + + +@if (session('success')) +
{{ session('success') }}
+@endif + +
+ + +

Daftar Akun Santri ({{ $users->count() }})

+ + + + + + + + + + + @forelse ($users as $user) + + + + + + + @empty + + + + @endforelse + +
ID SantriNamaUsernameAksi
{{ $user->role_id }}{{ $user->name }}{{ $user->username }} + Reset Password + Hapus Akun +
Belum ada akun Santri yang terdaftar.
+ +

Data Santri Tanpa Akun ({{ $santris_tanpa_akun->count() }})

+

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) --}} +
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/admin/users/wali_accounts.blade.php b/sim-pkpps/resources/views/admin/users/wali_accounts.blade.php new file mode 100644 index 0000000..39a886b --- /dev/null +++ b/sim-pkpps/resources/views/admin/users/wali_accounts.blade.php @@ -0,0 +1,51 @@ +@extends('layouts.app', ['isAdmin' => true]) + +@section('title', 'Manajemen Akun Wali Santri') + +@section('content') + + +@if (session('success')) +
{{ session('success') }}
+@endif + +
+ + +

Daftar Akun Wali Santri ({{ $users->count() }})

+ + + + + + + + + + + @forelse ($users as $user) + + + + + + + @empty + + + + @endforelse + +
ID WaliNamaUsernameAksi
{{ $user->role_id }}{{ $user->name }}{{ $user->username }} + Reset Password + Hapus Akun +
Belum ada akun Wali Santri yang terdaftar.
+ +

Data Wali Terdaftar ({{ $walis->count() }})

+

Pastikan Anda sudah mendaftarkan biodata wali santri di tabel `walis` sebelum membuat akun login.

+
+@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/auth/auth_layout.blade.php b/sim-pkpps/resources/views/auth/auth_layout.blade.php new file mode 100644 index 0000000..86efcf7 --- /dev/null +++ b/sim-pkpps/resources/views/auth/auth_layout.blade.php @@ -0,0 +1,50 @@ + + + + + + @yield('title', 'Login') | SIM Santri + + + + + +
+ @yield('auth-content') +
+ + \ No newline at end of file diff --git a/sim-pkpps/resources/views/auth/confirm-password.blade.php b/sim-pkpps/resources/views/auth/confirm-password.blade.php new file mode 100644 index 0000000..3d38186 --- /dev/null +++ b/sim-pkpps/resources/views/auth/confirm-password.blade.php @@ -0,0 +1,27 @@ + +
+ {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} +
+ +
+ @csrf + + +
+ + + + + +
+ +
+ + {{ __('Confirm') }} + +
+
+
diff --git a/sim-pkpps/resources/views/auth/forgot-password.blade.php b/sim-pkpps/resources/views/auth/forgot-password.blade.php new file mode 100644 index 0000000..cb32e08 --- /dev/null +++ b/sim-pkpps/resources/views/auth/forgot-password.blade.php @@ -0,0 +1,25 @@ + +
+ {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} +
+ + + + +
+ @csrf + + +
+ + + +
+ +
+ + {{ __('Email Password Reset Link') }} + +
+
+
diff --git a/sim-pkpps/resources/views/auth/login.blade.php b/sim-pkpps/resources/views/auth/login.blade.php new file mode 100644 index 0000000..78b684f --- /dev/null +++ b/sim-pkpps/resources/views/auth/login.blade.php @@ -0,0 +1,47 @@ + + + + +
+ @csrf + + +
+ + + +
+ + +
+ + + + + +
+ + +
+ +
+ +
+ @if (Route::has('password.request')) + + {{ __('Forgot your password?') }} + + @endif + + + {{ __('Log in') }} + +
+
+
diff --git a/sim-pkpps/resources/views/auth/register.blade.php b/sim-pkpps/resources/views/auth/register.blade.php new file mode 100644 index 0000000..a857242 --- /dev/null +++ b/sim-pkpps/resources/views/auth/register.blade.php @@ -0,0 +1,52 @@ + +
+ @csrf + + +
+ + + +
+ + +
+ + + +
+ + +
+ + + + + +
+ + +
+ + + + + +
+ +
+ + {{ __('Already registered?') }} + + + + {{ __('Register') }} + +
+
+
diff --git a/sim-pkpps/resources/views/auth/reset-password.blade.php b/sim-pkpps/resources/views/auth/reset-password.blade.php new file mode 100644 index 0000000..a6494cc --- /dev/null +++ b/sim-pkpps/resources/views/auth/reset-password.blade.php @@ -0,0 +1,39 @@ + +
+ @csrf + + + + + +
+ + + +
+ + +
+ + + +
+ + +
+ + + + + +
+ +
+ + {{ __('Reset Password') }} + +
+
+
diff --git a/sim-pkpps/resources/views/auth/verify-email.blade.php b/sim-pkpps/resources/views/auth/verify-email.blade.php new file mode 100644 index 0000000..eaf811d --- /dev/null +++ b/sim-pkpps/resources/views/auth/verify-email.blade.php @@ -0,0 +1,31 @@ + +
+ {{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }} +
+ + @if (session('status') == 'verification-link-sent') +
+ {{ __('A new verification link has been sent to the email address you provided during registration.') }} +
+ @endif + +
+
+ @csrf + +
+ + {{ __('Resend Verification Email') }} + +
+
+ +
+ @csrf + + +
+
+
diff --git a/sim-pkpps/resources/views/components/application-logo.blade.php b/sim-pkpps/resources/views/components/application-logo.blade.php new file mode 100644 index 0000000..46579cf --- /dev/null +++ b/sim-pkpps/resources/views/components/application-logo.blade.php @@ -0,0 +1,3 @@ + + + diff --git a/sim-pkpps/resources/views/components/auth-session-status.blade.php b/sim-pkpps/resources/views/components/auth-session-status.blade.php new file mode 100644 index 0000000..c4bd6e2 --- /dev/null +++ b/sim-pkpps/resources/views/components/auth-session-status.blade.php @@ -0,0 +1,7 @@ +@props(['status']) + +@if ($status) +
merge(['class' => 'font-medium text-sm text-green-600']) }}> + {{ $status }} +
+@endif diff --git a/sim-pkpps/resources/views/components/danger-button.blade.php b/sim-pkpps/resources/views/components/danger-button.blade.php new file mode 100644 index 0000000..d17d288 --- /dev/null +++ b/sim-pkpps/resources/views/components/danger-button.blade.php @@ -0,0 +1,3 @@ + diff --git a/sim-pkpps/resources/views/components/dropdown-link.blade.php b/sim-pkpps/resources/views/components/dropdown-link.blade.php new file mode 100644 index 0000000..e0f8ce1 --- /dev/null +++ b/sim-pkpps/resources/views/components/dropdown-link.blade.php @@ -0,0 +1 @@ +merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }} diff --git a/sim-pkpps/resources/views/components/dropdown.blade.php b/sim-pkpps/resources/views/components/dropdown.blade.php new file mode 100644 index 0000000..db38742 --- /dev/null +++ b/sim-pkpps/resources/views/components/dropdown.blade.php @@ -0,0 +1,43 @@ +@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white']) + +@php +switch ($align) { + case 'left': + $alignmentClasses = 'ltr:origin-top-left rtl:origin-top-right start-0'; + break; + case 'top': + $alignmentClasses = 'origin-top'; + break; + case 'right': + default: + $alignmentClasses = 'ltr:origin-top-right rtl:origin-top-left end-0'; + break; +} + +switch ($width) { + case '48': + $width = 'w-48'; + break; +} +@endphp + +
+
+ {{ $trigger }} +
+ + +
diff --git a/sim-pkpps/resources/views/components/input-error.blade.php b/sim-pkpps/resources/views/components/input-error.blade.php new file mode 100644 index 0000000..9e6da21 --- /dev/null +++ b/sim-pkpps/resources/views/components/input-error.blade.php @@ -0,0 +1,9 @@ +@props(['messages']) + +@if ($messages) +
    merge(['class' => 'text-sm text-red-600 space-y-1']) }}> + @foreach ((array) $messages as $message) +
  • {{ $message }}
  • + @endforeach +
+@endif diff --git a/sim-pkpps/resources/views/components/input-label.blade.php b/sim-pkpps/resources/views/components/input-label.blade.php new file mode 100644 index 0000000..1cc65e2 --- /dev/null +++ b/sim-pkpps/resources/views/components/input-label.blade.php @@ -0,0 +1,5 @@ +@props(['value']) + + diff --git a/sim-pkpps/resources/views/components/modal.blade.php b/sim-pkpps/resources/views/components/modal.blade.php new file mode 100644 index 0000000..70704c1 --- /dev/null +++ b/sim-pkpps/resources/views/components/modal.blade.php @@ -0,0 +1,78 @@ +@props([ + 'name', + 'show' => false, + 'maxWidth' => '2xl' +]) + +@php +$maxWidth = [ + 'sm' => 'sm:max-w-sm', + 'md' => 'sm:max-w-md', + 'lg' => 'sm:max-w-lg', + 'xl' => 'sm:max-w-xl', + '2xl' => 'sm:max-w-2xl', +][$maxWidth]; +@endphp + +
+
+
+
+ +
+ {{ $slot }} +
+
diff --git a/sim-pkpps/resources/views/components/nav-link.blade.php b/sim-pkpps/resources/views/components/nav-link.blade.php new file mode 100644 index 0000000..5c101a2 --- /dev/null +++ b/sim-pkpps/resources/views/components/nav-link.blade.php @@ -0,0 +1,11 @@ +@props(['active']) + +@php +$classes = ($active ?? false) + ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out' + : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out'; +@endphp + +merge(['class' => $classes]) }}> + {{ $slot }} + diff --git a/sim-pkpps/resources/views/components/primary-button.blade.php b/sim-pkpps/resources/views/components/primary-button.blade.php new file mode 100644 index 0000000..d71f0b6 --- /dev/null +++ b/sim-pkpps/resources/views/components/primary-button.blade.php @@ -0,0 +1,3 @@ + diff --git a/sim-pkpps/resources/views/components/responsive-nav-link.blade.php b/sim-pkpps/resources/views/components/responsive-nav-link.blade.php new file mode 100644 index 0000000..43b91e7 --- /dev/null +++ b/sim-pkpps/resources/views/components/responsive-nav-link.blade.php @@ -0,0 +1,11 @@ +@props(['active']) + +@php +$classes = ($active ?? false) + ? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out' + : 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out'; +@endphp + +merge(['class' => $classes]) }}> + {{ $slot }} + diff --git a/sim-pkpps/resources/views/components/secondary-button.blade.php b/sim-pkpps/resources/views/components/secondary-button.blade.php new file mode 100644 index 0000000..b32b69f --- /dev/null +++ b/sim-pkpps/resources/views/components/secondary-button.blade.php @@ -0,0 +1,3 @@ + diff --git a/sim-pkpps/resources/views/components/text-input.blade.php b/sim-pkpps/resources/views/components/text-input.blade.php new file mode 100644 index 0000000..1df7f0d --- /dev/null +++ b/sim-pkpps/resources/views/components/text-input.blade.php @@ -0,0 +1,3 @@ +@props(['disabled' => false]) + +merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) !!}> diff --git a/sim-pkpps/resources/views/dashboard.blade.php b/sim-pkpps/resources/views/dashboard.blade.php new file mode 100644 index 0000000..66028f2 --- /dev/null +++ b/sim-pkpps/resources/views/dashboard.blade.php @@ -0,0 +1,17 @@ + + +

+ {{ __('Dashboard') }} +

+
+ +
+
+
+
+ {{ __("You're logged in!") }} +
+
+
+
+
diff --git a/sim-pkpps/resources/views/layouts/admin-sidebar.blade.php b/sim-pkpps/resources/views/layouts/admin-sidebar.blade.php new file mode 100644 index 0000000..b605f50 --- /dev/null +++ b/sim-pkpps/resources/views/layouts/admin-sidebar.blade.php @@ -0,0 +1,185 @@ +{{-- Sidebar Menu Lengkap - Admin dengan FITUR CAPAIAN --}} + +
  • + + Dashboard + +
  • + + + + + + + + + + + + + + + + + +
  • + + Kesehatan Santri + +
  • + + +
  • + + Kepulangan Santri + +
  • + + +
  • + + Berita + +
  • + + +
  • +
    + @csrf + + Logout + +
    +
  • \ No newline at end of file diff --git a/sim-pkpps/resources/views/layouts/app.blade.php b/sim-pkpps/resources/views/layouts/app.blade.php new file mode 100644 index 0000000..ceca77e --- /dev/null +++ b/sim-pkpps/resources/views/layouts/app.blade.php @@ -0,0 +1,179 @@ + + + + + + @yield('title', 'SIM Santri') + + + + + + + + + + +
    +
    +

    SIM Santri

    +

    Monitoring Santri Berbasis Web

    +
    +
    +
    + + +
    + + + + + +
    + +
    + + +
    + + + +
    + @yield('content') +
    + +
    + +
    + + + + + diff --git a/sim-pkpps/resources/views/layouts/guest.blade.php b/sim-pkpps/resources/views/layouts/guest.blade.php new file mode 100644 index 0000000..11feb47 --- /dev/null +++ b/sim-pkpps/resources/views/layouts/guest.blade.php @@ -0,0 +1,30 @@ + + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + +
    +
    + + + +
    + +
    + {{ $slot }} +
    +
    + + diff --git a/sim-pkpps/resources/views/layouts/santri-wali-sidebar.blade.php b/sim-pkpps/resources/views/layouts/santri-wali-sidebar.blade.php new file mode 100644 index 0000000..9d6376e --- /dev/null +++ b/sim-pkpps/resources/views/layouts/santri-wali-sidebar.blade.php @@ -0,0 +1,86 @@ + + \ No newline at end of file diff --git a/sim-pkpps/resources/views/profile/edit.blade.php b/sim-pkpps/resources/views/profile/edit.blade.php new file mode 100644 index 0000000..e0e1d38 --- /dev/null +++ b/sim-pkpps/resources/views/profile/edit.blade.php @@ -0,0 +1,29 @@ + + +

    + {{ __('Profile') }} +

    +
    + +
    +
    +
    +
    + @include('profile.partials.update-profile-information-form') +
    +
    + +
    +
    + @include('profile.partials.update-password-form') +
    +
    + +
    +
    + @include('profile.partials.delete-user-form') +
    +
    +
    +
    +
    diff --git a/sim-pkpps/resources/views/profile/partials/delete-user-form.blade.php b/sim-pkpps/resources/views/profile/partials/delete-user-form.blade.php new file mode 100644 index 0000000..edeeb4a --- /dev/null +++ b/sim-pkpps/resources/views/profile/partials/delete-user-form.blade.php @@ -0,0 +1,55 @@ +
    +
    +

    + {{ __('Delete Account') }} +

    + +

    + {{ __('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.') }} +

    +
    + + {{ __('Delete Account') }} + + +
    + @csrf + @method('delete') + +

    + {{ __('Are you sure you want to delete your account?') }} +

    + +

    + {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }} +

    + +
    + + + + + +
    + +
    + + {{ __('Cancel') }} + + + + {{ __('Delete Account') }} + +
    +
    +
    +
    diff --git a/sim-pkpps/resources/views/profile/partials/update-password-form.blade.php b/sim-pkpps/resources/views/profile/partials/update-password-form.blade.php new file mode 100644 index 0000000..eaca1ac --- /dev/null +++ b/sim-pkpps/resources/views/profile/partials/update-password-form.blade.php @@ -0,0 +1,48 @@ +
    +
    +

    + {{ __('Update Password') }} +

    + +

    + {{ __('Ensure your account is using a long, random password to stay secure.') }} +

    +
    + +
    + @csrf + @method('put') + +
    + + + +
    + +
    + + + +
    + +
    + + + +
    + +
    + {{ __('Save') }} + + @if (session('status') === 'password-updated') +

    {{ __('Saved.') }}

    + @endif +
    +
    +
    diff --git a/sim-pkpps/resources/views/profile/partials/update-profile-information-form.blade.php b/sim-pkpps/resources/views/profile/partials/update-profile-information-form.blade.php new file mode 100644 index 0000000..5ae3d35 --- /dev/null +++ b/sim-pkpps/resources/views/profile/partials/update-profile-information-form.blade.php @@ -0,0 +1,64 @@ +
    +
    +

    + {{ __('Profile Information') }} +

    + +

    + {{ __("Update your account's profile information and email address.") }} +

    +
    + +
    + @csrf +
    + +
    + @csrf + @method('patch') + +
    + + + +
    + +
    + + + + + @if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail()) +
    +

    + {{ __('Your email address is unverified.') }} + + +

    + + @if (session('status') === 'verification-link-sent') +

    + {{ __('A new verification link has been sent to your email address.') }} +

    + @endif +
    + @endif +
    + +
    + {{ __('Save') }} + + @if (session('status') === 'profile-updated') +

    {{ __('Saved.') }}

    + @endif +
    +
    +
    diff --git a/sim-pkpps/resources/views/santri/auth/login.blade.php b/sim-pkpps/resources/views/santri/auth/login.blade.php new file mode 100644 index 0000000..f54e376 --- /dev/null +++ b/sim-pkpps/resources/views/santri/auth/login.blade.php @@ -0,0 +1,46 @@ + +@extends('auth.auth_layout') + +@section('title', 'Login Santri') + +@section('auth-content') +
    +

    Login Santri/Wali

    +

    Akses progres dan laporan santri.

    +
    + +@if ($errors->any()) +
    + {{ $errors->first() }} +
    +@endif + +
    + @csrf + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    + +

    + Lupa akun? Hubungi Admin. +

    +
    +@endsection diff --git a/sim-pkpps/resources/views/santri/berita/index.blade.php b/sim-pkpps/resources/views/santri/berita/index.blade.php new file mode 100644 index 0000000..cfb5bf0 --- /dev/null +++ b/sim-pkpps/resources/views/santri/berita/index.blade.php @@ -0,0 +1,130 @@ +{{-- Alternative dengan style yang lebih responsive --}} +@extends('layouts.app') + +@section('title', 'Berita') + +@section('content') + + +@if($berita->isEmpty()) +
    + +

    Belum Ada Berita

    +

    Belum ada berita atau pengumuman yang dipublikasikan untuk Anda.

    +
    +@else + +@endif + +{{-- Info Box --}} +
    + + Info: Berita yang ditandai dengan badge Baru adalah berita yang belum Anda baca. Klik pada berita untuk membaca selengkapnya. +
    + +{{-- Quick Actions --}} + + +{{-- Responsive Style untuk Mobile --}} + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/berita/show.blade.php b/sim-pkpps/resources/views/santri/berita/show.blade.php new file mode 100644 index 0000000..674ab98 --- /dev/null +++ b/sim-pkpps/resources/views/santri/berita/show.blade.php @@ -0,0 +1,55 @@ +@extends('layouts.app') + +@section('title', $berita->judul) + +@section('content') +
    + {{-- Header --}} +
    +
    +

    {{ $berita->judul }}

    +
    + {{ $berita->penulis }} + {{ $berita->created_at->format('d F Y, H:i') }} WIB + Sudah dibaca +
    +
    + + Kembali + +
    + +
    + + {{-- Gambar Berita --}} + @if($berita->gambar) +
    + {{ $berita->judul }} +
    + @endif + + {{-- Konten Berita --}} +
    + {!! nl2br(e($berita->konten)) !!} +
    + + {{-- Footer --}} +
    +

    + Dipublikasikan pada {{ $berita->created_at->format('d F Y, H:i') }} WIB +

    +
    +
    + +{{-- Quick Action --}} + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/capaian/index.blade.php b/sim-pkpps/resources/views/santri/capaian/index.blade.php new file mode 100644 index 0000000..cd32ed9 --- /dev/null +++ b/sim-pkpps/resources/views/santri/capaian/index.blade.php @@ -0,0 +1,330 @@ +@extends('layouts.app') + +@section('title', 'Capaian Al-Qur\'an & Hadist') + +@section('content') + + +{{-- Alert Success --}} +@if(session('success')) +
    + {{ session('success') }} +
    +@endif + +{{-- Cards Statistik --}} +
    + {{-- Total Capaian --}} +
    +

    Total Materi

    +
    {{ $totalCapaian }}
    +
    +
    + + {{-- Rata-rata Progress --}} +
    +

    Rata-rata Progress

    +
    {{ number_format($rataRataPersentase, 1) }}%
    +
    +
    +
    +
    +
    +
    +
    + + {{-- Materi Selesai --}} +
    +

    Materi Selesai

    +
    {{ $materiSelesai }}
    +
    + @if($totalCapaian > 0) +

    + {{ number_format(($materiSelesai / $totalCapaian) * 100, 1) }}% dari total materi +

    + @endif +
    + + {{-- Kelas --}} +
    +

    Kelas

    +
    {{ $santri->kelas }}
    +
    +

    + NIS: {{ $santri->nis ?? '-' }} +

    +
    +
    + +{{-- Statistik per Kategori --}} +
    +

    + Statistik per Kategori +

    + +
    + @foreach($statistikKategori as $kategori => $data) +
    +
    +

    {{ $kategori }}

    + + {{ $data['count'] }} Materi + +
    + +
    +
    + Progress + {{ number_format($data['avg'], 1) }}% +
    +
    +
    +
    +
    + +
    + {{ $data['selesai'] }} Selesai + {{ $data['count'] - $data['selesai'] }} Berlangsung +
    +
    + @endforeach +
    +
    + +{{-- Grafik --}} +
    +

    + Visualisasi Progress +

    + +
    + {{-- Chart Kategori --}} +
    +

    Progress per Kategori

    + +
    + + {{-- Chart Distribusi --}} +
    +

    Distribusi Persentase

    + +
    +
    +
    + +{{-- Filter & Daftar Capaian --}} +
    +
    +

    + Daftar Capaian +

    + + {{-- Filter Semester --}} +
    + +
    +
    + + @if($capaians->count() > 0) +
    + + + + + + + + + + + + + + @foreach($capaians as $index => $capaian) + + + + + + + + + + @endforeach + +
    NoMateriKategoriHalaman SelesaiProgressTanggal InputAksi
    {{ $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 + +
    +
    + @else +
    + +

    Belum Ada Data Capaian

    +

    + @if($selectedSemester) + Tidak ada data capaian untuk semester yang dipilih. + @else + Belum ada data capaian yang tercatat untuk Anda. + @endif +

    +
    + @endif +
    + +{{-- Chart.js Script --}} + + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/capaian/show.blade.php b/sim-pkpps/resources/views/santri/capaian/show.blade.php new file mode 100644 index 0000000..e36f7c9 --- /dev/null +++ b/sim-pkpps/resources/views/santri/capaian/show.blade.php @@ -0,0 +1,187 @@ +@extends('layouts.app') + +@section('title', 'Detail Capaian') + +@section('content') + + +
    +
    +

    Informasi Capaian

    + + Kembali + +
    + + {{-- Info Materi --}} +
    +

    Informasi Materi

    + + + + + + + + + + + + + +
    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 }})
    +
    + + {{-- Progress --}} +
    +

    Progress Pembelajaran

    + + + + + + + + + + + + + + + + + + + + + + @if($capaian->catatan) + + + + + @endif +
    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 }} +
    +
    +
    + + {{-- Visualisasi Halaman --}} +
    +

    Visualisasi Halaman yang Diselesaikan

    +
    +
    + @php + $completedPages = $capaian->pages_array; + $totalPages = $capaian->materi->total_halaman; + $startPage = $capaian->materi->halaman_mulai; + @endphp + + @for($i = $startPage; $i <= ($startPage + $totalPages - 1); $i++) + @php + $isCompleted = in_array($i, $completedPages); + @endphp +
    + {{ $i }} +
    + @endfor +
    + +
    +
    +
    + Selesai ({{ count($completedPages) }}) +
    +
    +
    + Belum ({{ $totalPages - count($completedPages) }}) +
    +
    +
    +
    + + {{-- Status Badge --}} +
    + @if($capaian->persentase >= 100) +
    + +

    Alhamdulillah, Materi Selesai! 🎉

    +

    Terus semangat untuk materi berikutnya!

    +
    + @elseif($capaian->persentase >= 75) +
    + +

    Hampir Selesai! 💪

    +

    Tinggal sedikit lagi, pertahankan semangatmu!

    +
    + @elseif($capaian->persentase >= 50) +
    + +

    Setengah Perjalanan! ⚡

    +

    Terus berjuang, jangan menyerah!

    +
    + @else +
    + +

    Baru Memulai! 🌱

    +

    Setiap perjalanan dimulai dari langkah pertama. Semangat!

    +
    + @endif +
    +
    +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/dashboardSantri.blade.php b/sim-pkpps/resources/views/santri/dashboardSantri.blade.php new file mode 100644 index 0000000..e69670d --- /dev/null +++ b/sim-pkpps/resources/views/santri/dashboardSantri.blade.php @@ -0,0 +1,194 @@ +@extends('layouts.app') + +@section('title', 'Dashboard Santri') + +@section('content') + + +{{-- ✅ ALERTS (Kesehatan & Kepulangan) --}} +@if(isset($statusKesehatan) && $statusKesehatan) +
    + + Perhatian: Anda sedang dalam perawatan UKP sejak {{ $statusKesehatan->tanggal_masuk_formatted }} + ({{ $statusKesehatan->lama_dirawat }} hari). Keluhan: {{ $statusKesehatan->keluhan }}. + Lihat Detail +
    +@endif + +@if(isset($kepulanganAktif) && $kepulanganAktif) +
    + + Sedang Pulang: Anda sedang dalam periode kepulangan + ({{ $kepulanganAktif->tanggal_pulang_formatted }} - {{ $kepulanganAktif->tanggal_kembali_formatted }}). + Pastikan kembali tepat waktu! + Lihat Detail +
    +@endif + +{{-- Cards Statistik --}} +
    + {{-- Card Progres Al-Qur'an --}} +
    +

    Progres Al-Qur'an

    +
    {{ $data['progres_quran'] }}%
    +
    +
    +
    +
    +
    +
    +
    + + {{-- Card Progres Hadist --}} +
    +

    Progres Hadist

    +
    {{ $data['progres_hadist'] }}%
    +
    +
    +
    +
    +
    +
    +
    + + {{-- Card Saldo Uang Saku --}} +
    +

    Saldo Uang Saku

    +
    {{ 'Rp ' . number_format($data['saldo_uang_saku'], 0, ',', '.') }}
    +
    + +
    + + {{-- Card Poin Pelanggaran --}} +
    +

    Total Poin Pelanggaran

    +
    {{ $data['poin_pelanggaran'] }}
    +
    + @if($data['poin_pelanggaran'] > 0) + + @else +
    + + Tidak ada pelanggaran + +
    + @endif +
    +
    + +{{-- ✅ BERITA TERBARU --}} +@if($beritaTerbaru->isNotEmpty()) +
    +
    +

    + Berita Terbaru (7 Hari Terakhir) +

    + + Lihat Semua + +
    + + +
    +@endif + +{{-- Quick Links --}} +
    +

    + Akses Cepat +

    + +
    + {{-- Profil Saya --}} + + + Profil Saya + + + {{-- Berita --}} + + + Berita + + + {{-- Uang Saku --}} + + + Uang Saku + + + {{-- Pelanggaran --}} + + + Pelanggaran + + + {{-- ✅ Kesehatan (dengan badge dinamis) --}} + + + Kesehatan + @if(isset($statusKesehatan) && $statusKesehatan) + + Sedang Dirawat + + @endif + + + {{-- ✅ Kepulangan (dengan badge dinamis) --}} + + + Kepulangan + @if(isset($kepulanganAktif) && $kepulanganAktif) + + Sedang Pulang + + @endif + +
    +
    + +{{-- Tips Hari Ini --}} +
    +

    + Tips Hari Ini +

    +

    + 💡 Jaga Kedisiplinan: Hindari pelanggaran dengan mematuhi tata tertib pondok. + Lihat Daftar Kategori Pelanggaran + untuk mengetahui peraturan yang berlaku. +

    +
    +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/kepulangan/index.blade.php b/sim-pkpps/resources/views/santri/kepulangan/index.blade.php new file mode 100644 index 0000000..0f26cba --- /dev/null +++ b/sim-pkpps/resources/views/santri/kepulangan/index.blade.php @@ -0,0 +1,224 @@ +@extends('layouts.app') + +@section('title', 'Riwayat Kepulangan') + +@section('content') + + +{{-- Statistik Cards --}} +
    +
    +

    Total Pengajuan

    +
    {{ $statistik['total_izin'] }}
    +
    +

    + Tahun {{ $tahunSekarang }} +

    +
    + +
    +

    Disetujui

    +
    {{ $statistik['disetujui'] }}
    +
    +

    + Izin diterima +

    +
    + +
    +

    Total Hari Pulang

    +
    {{ $statistik['total_hari'] }} Hari
    +
    + @if($statistik['over_limit']) +

    + Melebihi batas! +

    + @else +

    + Dari kuota 12 hari +

    + @endif +
    + +
    +

    Sisa Kuota

    +
    {{ $statistik['sisa_kuota'] }} Hari
    +
    + @if($statistik['menunggu'] > 0) +

    + {{ $statistik['menunggu'] }} menunggu +

    + @else +

    + Kuota tersisa +

    + @endif +
    +
    + +{{-- Info Box Kuota --}} +@if($statistik['over_limit']) +
    + + Peringatan: Anda telah melebihi batas kuota kepulangan (12 hari/tahun). + Total hari pulang Anda tahun ini: {{ $statistik['total_hari'] }} hari. +
    +@elseif($statistik['sisa_kuota'] <= 3 && $statistik['sisa_kuota'] > 0) +
    + + Perhatian: Sisa kuota kepulangan Anda tinggal {{ $statistik['sisa_kuota'] }} hari. + Gunakan dengan bijak. +
    +@endif + +{{-- Filter --}} +
    +
    + + + + + + + + Reset + +
    +
    + +{{-- Riwayat Kepulangan --}} +@if($riwayatKepulangan->isEmpty()) +
    + +

    Belum Ada Riwayat Kepulangan

    +

    Anda belum pernah mengajukan izin kepulangan pada periode yang dipilih.

    +
    +@else + +@endif + +{{-- Info Box --}} +
    + + Info: Kuota kepulangan maksimal 12 hari per tahun. + Pastikan Anda merencanakan kepulangan dengan bijak agar tidak melebihi batas kuota. +
    + +{{-- Quick Actions --}} + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/kepulangan/show.blade.php b/sim-pkpps/resources/views/santri/kepulangan/show.blade.php new file mode 100644 index 0000000..281f995 --- /dev/null +++ b/sim-pkpps/resources/views/santri/kepulangan/show.blade.php @@ -0,0 +1,185 @@ +@extends('layouts.app') + +@section('title', 'Detail Kepulangan') + +@section('content') +
    +
    +
    +

    Detail Izin Kepulangan

    +

    + ID: {{ $kepulangan->id_kepulangan }} +

    +
    + + Kembali + +
    + +
    + + {{-- Status Badge --}} +
    + + + Status: {{ $kepulangan->status }} + + + @if($kepulangan->status == 'Disetujui' && $kepulangan->is_aktif) + + Sedang Dalam Periode Pulang + + @elseif($kepulangan->status == 'Disetujui' && $kepulangan->is_terlambat) + + Terlambat Kembali + + @endif +
    + + {{-- Detail Table --}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @if($kepulangan->approved_by) + + + + + + + + + @endif + @if($kepulangan->catatan) + + + + + @endif +
    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 }}
    + + {{-- Info Kuota --}} +
    +

    + Informasi Kuota Tahun {{ date('Y') }} +

    +
    +
    +

    Total Hari Pulang

    +

    + {{ $totalHariTahunIni }} hari +

    +
    +
    +

    Sisa Kuota

    +

    + {{ $sisaKuota }} hari +

    +
    +
    +

    Kuota Maksimal

    +

    + 12 hari +

    +
    +
    +
    +
    + +{{-- Alert berdasarkan Status --}} +@if($kepulangan->status == 'Menunggu') +
    + + Menunggu Persetujuan: Izin kepulangan Anda sedang dalam proses review. + Mohon tunggu konfirmasi dari pengurus. +
    +@elseif($kepulangan->status == 'Disetujui') + @if($kepulangan->is_aktif) +
    + + Sedang Pulang: Anda sedang dalam periode kepulangan. + Pastikan kembali sesuai jadwal: {{ $kepulangan->tanggal_kembali_formatted }}. +
    + @elseif($kepulangan->is_terlambat) +
    + + Terlambat Kembali: Anda telah melewati tanggal kembali yang dijadwalkan. + Segera hubungi pengurus! +
    + @else +
    + + Izin Disetujui: Kepulangan Anda telah disetujui. + Pastikan untuk pulang dan kembali sesuai jadwal yang telah ditentukan. +
    + @endif +@elseif($kepulangan->status == 'Ditolak') +
    + + Izin Ditolak: Maaf, izin kepulangan Anda tidak disetujui. + @if($kepulangan->catatan) + Alasan: {{ $kepulangan->catatan }} + @endif +
    +@elseif($kepulangan->status == 'Selesai') +
    + + Kepulangan Selesai: Anda telah menyelesaikan periode kepulangan ini. +
    +@endif + +{{-- Quick Actions --}} + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/kesehatan/index.blade.php b/sim-pkpps/resources/views/santri/kesehatan/index.blade.php new file mode 100644 index 0000000..1aa7544 --- /dev/null +++ b/sim-pkpps/resources/views/santri/kesehatan/index.blade.php @@ -0,0 +1,265 @@ +@extends('layouts.app') + +@section('title', 'Riwayat Kesehatan') + +@section('content') + + +{{-- ✅ DISPLAY ERROR VALIDATION --}} +@if($errors->any()) +
    + + Error: {{ $errors->first() }} +
    +@endif + +{{-- ✅ STATISTIK CARDS (BERDASARKAN FILTER) --}} +
    +
    +

    Total Kunjungan

    +
    {{ $statistik['total_kunjungan'] }}
    +
    +

    + Periode yang dipilih +

    +
    + +
    +

    Sedang Dirawat

    +
    {{ $statistik['sedang_dirawat'] }}
    +
    + @if($statistik['sedang_dirawat'] > 0) +

    + Perlu perhatian +

    + @else +

    + Tidak ada yang dirawat +

    + @endif +
    + +
    +

    Sembuh

    +
    {{ $statistik['sembuh'] }}
    +
    +

    + Alhamdulillah +

    +
    + +
    +

    Izin Sakit

    +
    {{ $statistik['izin'] }}
    +
    +

    + Izin pulang +

    +
    +
    + +{{-- ✅ FILTER TANGGAL (DI ATAS CARDS) --}} +
    +
    +
    + {{-- Tanggal Dari --}} +
    + + +
    + + {{-- Tanggal Sampai --}} +
    + + +
    + + {{-- Status Filter --}} +
    + + +
    + + {{-- Buttons --}} +
    + + + Reset + +
    +
    +
    + + {{-- Info Periode --}} +
    +

    + + Menampilkan data periode: + + {{ $tanggalDari->locale('id')->isoFormat('D MMMM Y') }} - {{ $tanggalSampai->locale('id')->isoFormat('D MMMM Y') }} + + ({{ $tanggalDari->diffInDays($tanggalSampai) + 1 }} hari) +

    +
    +
    + +{{-- Riwayat Kesehatan --}} +@if($riwayatKesehatan->isEmpty()) +
    + +

    Tidak Ada Data

    +

    Tidak ada riwayat kesehatan pada periode yang dipilih.

    + + Lihat Semua Data + +
    +@else + +@endif + +{{-- Info Box --}} +
    + + Info: Gunakan filter tanggal untuk melihat riwayat kesehatan pada periode tertentu. + Jika tidak difilter, data yang ditampilkan adalah untuk bulan berjalan. +
    + +{{-- Quick Actions --}} + + +{{-- ✅ JAVASCRIPT UNTUK AUTO SUBMIT SAAT TANGGAL BERUBAH (OPTIONAL) --}} + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/kesehatan/show.blade.php b/sim-pkpps/resources/views/santri/kesehatan/show.blade.php new file mode 100644 index 0000000..b95ca25 --- /dev/null +++ b/sim-pkpps/resources/views/santri/kesehatan/show.blade.php @@ -0,0 +1,102 @@ +@extends('layouts.app') + +@section('title', 'Detail Riwayat Kesehatan') + +@section('content') +
    +
    +
    +

    Detail Riwayat Kesehatan

    +

    + ID: {{ $kesehatanSantri->id_kesehatan }} +

    +
    + + Kembali + +
    + +
    + + {{-- Status Badge --}} +
    + + + Status: {{ ucfirst($kesehatanSantri->status) }} + +
    + + {{-- Detail Table --}} + + + + + + + + + + + + + + + + + + + + + + @if($kesehatanSantri->catatan) + + + + + @endif + + + + +
    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) }} + +
    +
    + +{{-- Info Box --}} +@if($kesehatanSantri->status == 'dirawat') +
    + + Perhatian: Anda masih dalam perawatan UKP. Ikuti instruksi petugas kesehatan dan jaga kesehatan Anda. +
    +@elseif($kesehatanSantri->status == 'sembuh') +
    + + Alhamdulillah: Anda sudah sembuh. Jaga kesehatan dan pola hidup sehat agar tidak sakit lagi. +
    +@endif + +{{-- Quick Actions --}} + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/pelanggaran/index.blade.php b/sim-pkpps/resources/views/santri/pelanggaran/index.blade.php new file mode 100644 index 0000000..8ed80fd --- /dev/null +++ b/sim-pkpps/resources/views/santri/pelanggaran/index.blade.php @@ -0,0 +1,116 @@ +{{-- resources/views/santri/pelanggaran/index.blade.php --}} +@extends('layouts.app') + +@section('title', 'Riwayat Pelanggaran') + +@section('content') + + +{{-- Statistik Cards --}} +
    +
    +

    Total Pelanggaran

    +
    {{ $totalPelanggaran }}
    + +
    + +
    +

    Total Poin

    +
    {{ $totalPoin }}
    + +
    + +
    +

    Pelanggaran Bulan Ini

    +
    {{ $pelanggaranBulanIni }}
    + +
    +
    + +{{-- Filter & Action Buttons --}} + + +{{-- Tabel Riwayat Pelanggaran --}} +
    + @if($riwayat->count() > 0) +
    + + + + + + + + + + + + + + @foreach($riwayat as $index => $item) + + + + + + + + + + @endforeach + +
    NoID RiwayatTanggalJenis PelanggaranPoinKeteranganAksi
    {{ $riwayat->firstItem() + $index }}{{ $item->id_riwayat }} + + {{ \Carbon\Carbon::parse($item->tanggal)->isoFormat('D MMM YYYY') }} + {{ $item->kategori->nama_pelanggaran ?? '-' }} + + {{ $item->poin }} + + +
    + {{ $item->keterangan ?: '-' }} +
    +
    + + Detail + +
    +
    + + {{-- Pagination --}} +
    + {{ $riwayat->links() }} +
    + @else +
    + +

    Tidak Ada Riwayat Pelanggaran

    +

    Selamat! Anda belum memiliki catatan pelanggaran.

    +
    + @endif +
    +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/pelanggaran/kategori.blade.php b/sim-pkpps/resources/views/santri/pelanggaran/kategori.blade.php new file mode 100644 index 0000000..3b3f139 --- /dev/null +++ b/sim-pkpps/resources/views/santri/pelanggaran/kategori.blade.php @@ -0,0 +1,67 @@ +{{-- resources/views/santri/pelanggaran/kategori.blade.php --}} +@extends('layouts.app') + +@section('title', 'Daftar Kategori Pelanggaran') + +@section('content') + + +
    +
    +

    + + Berikut adalah daftar kategori pelanggaran beserta poin yang berlaku di pondok. +

    + + Kembali ke Riwayat + +
    + + @if($kategoriList->count() > 0) +
    + + + + + + + + + + + @foreach($kategoriList as $index => $kategori) + + + + + + + @endforeach + +
    NoKodeJenis PelanggaranPoin
    {{ $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 +
    +
    + @else +
    + +

    Belum Ada Kategori

    +

    Daftar kategori pelanggaran belum tersedia.

    +
    + @endif +
    +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/pelanggaran/show.blade.php b/sim-pkpps/resources/views/santri/pelanggaran/show.blade.php new file mode 100644 index 0000000..8b4cce4 --- /dev/null +++ b/sim-pkpps/resources/views/santri/pelanggaran/show.blade.php @@ -0,0 +1,67 @@ +{{-- resources/views/santri/pelanggaran/show.blade.php --}} +@extends('layouts.app') + +@section('title', 'Detail Riwayat Pelanggaran') + +@section('content') + + +
    +
    +

    + + Riwayat ID: {{ $riwayatPelanggaran->id_riwayat }} +

    + +
    + +
    +

    Informasi Pelanggaran

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    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
    +
    + + {{-- Info Box --}} +
    +

    + + Catatan: Data pelanggaran ini dicatat oleh admin/pengurus pondok. + Jika ada kesalahan data, silakan hubungi bagian administrasi. +

    +
    +
    +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/profil/edit.blade.php b/sim-pkpps/resources/views/santri/profil/edit.blade.php new file mode 100644 index 0000000..c0b0fd1 --- /dev/null +++ b/sim-pkpps/resources/views/santri/profil/edit.blade.php @@ -0,0 +1,136 @@ +@extends('layouts.app') + +@section('title', 'Edit Profil Santri') + +@section('content') + + +
    +
    + @csrf + @method('PUT') + + {{-- Info Box --}} +
    +

    + + Info: Anda hanya dapat mengubah alamat dan nomor HP orang tua. + Untuk perubahan data lain, silakan hubungi admin. +

    +
    + + {{-- Nama Santri (Read-only) --}} +
    + + + Data ini tidak dapat diubah +
    + + {{-- Alamat Santri --}} +
    + + + + @error('alamat_santri') +
    {{ $message }}
    + @enderror + + + + Contoh: Jl. Merdeka No. 123, RT 02/RW 05, Kel. Sukamaju, Kec. Bandung Tengah, Kota Bandung + +
    + + {{-- Nomor HP Orang Tua --}} +
    + + + + @error('nomor_hp_ortu') +
    {{ $message }}
    + @enderror + + + + Format: 08XX-XXXX-XXXX atau +628XX-XXXX-XXXX + +
    + + {{-- Tombol Aksi --}} +
    + + + Batal + +
    +
    +
    + +{{-- Script untuk Auto-format Nomor HP --}} + +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/profil/index.blade.php b/sim-pkpps/resources/views/santri/profil/index.blade.php new file mode 100644 index 0000000..a8eab38 --- /dev/null +++ b/sim-pkpps/resources/views/santri/profil/index.blade.php @@ -0,0 +1,153 @@ +@extends('layouts.app') + +@section('title', 'Profil Santri') + +@section('content') + + +{{-- Alert Success --}} +@if(session('success')) +
    + {{ session('success') }} +
    +@endif + +
    + {{-- Header Profil --}} +
    +
    + {{-- Avatar Santri --}} +
    + {{ strtoupper(substr($santri->nama_lengkap, 0, 1)) }} +
    + +
    +

    + {{ $santri->nama_lengkap }} +

    +

    + {{ $santri->id_santri }} + @if($santri->nis) + | NIS: {{ $santri->nis }} + @endif +

    +
    +
    + + +
    + +
    + + {{-- Data Pribadi --}} +
    +

    Data Pribadi

    + + + + + + @if($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') }}
    +
    + + {{-- Data Kontak & Alamat --}} +
    +

    Kontak & Alamat

    + + + + + + + + + + + + + + + + + +
    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 +
    +
    + + {{-- Data Kartu RFID --}} + @if($santri->has_rfid) +
    +

    Kartu RFID

    +
    +

    + + Kartu RFID Anda sudah terdaftar +

    +

    + UID: {{ $santri->rfid_uid }} +

    +
    +
    + @else +
    +

    Kartu RFID

    +
    + + Kartu RFID Anda belum terdaftar. Silakan hubungi admin untuk pendaftaran kartu. +
    +
    + @endif + + {{-- Info Tambahan --}} +
    +

    + + Catatan: Jika ada data yang perlu diperbarui selain alamat dan nomor HP orang tua, silakan hubungi admin atau pengurus pesantren. +

    +
    +
    +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/uang-saku/index.blade.php b/sim-pkpps/resources/views/santri/uang-saku/index.blade.php new file mode 100644 index 0000000..6bf6ee2 --- /dev/null +++ b/sim-pkpps/resources/views/santri/uang-saku/index.blade.php @@ -0,0 +1,130 @@ +{{-- resources/views/santri/uang-saku/index.blade.php --}} +@extends('layouts.app') + +@section('title', 'Riwayat Uang Saku') + +@section('content') + + +{{-- Alert Messages --}} +@if(session('success')) +
    + {{ session('success') }} +
    +@endif + +@if(session('error')) +
    + {{ session('error') }} +
    +@endif + +{{-- Statistik Cards --}} +
    +
    +

    Total Pemasukan

    +
    {{ 'Rp ' . number_format($totalPemasukan, 0, ',', '.') }}
    +
    +
    + +
    +

    Total Pengeluaran

    +
    {{ 'Rp ' . number_format($totalPengeluaran, 0, ',', '.') }}
    +
    +
    + +
    +

    Saldo Terakhir

    +
    {{ 'Rp ' . number_format($saldoTerakhir, 0, ',', '.') }}
    +
    +
    +
    + +{{-- Filter Form --}} +
    +
    + + + + + + + + + + + + Reset + +
    +
    + +{{-- Tabel Riwayat --}} +
    + @if($riwayatUangSaku->count() > 0) + + + + + + + + + + + + + + + + @foreach($riwayatUangSaku as $index => $transaksi) + + + + + + + + + + + + @endforeach + +
    NoID TransaksiTanggalJenisNominalKeteranganSaldo SebelumSaldo SesudahAksi
    {{ $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 + +
    + + {{-- Pagination --}} +
    + {{ $riwayatUangSaku->links() }} +
    + @else +
    + +

    Belum Ada Transaksi

    +

    Riwayat uang saku Anda masih kosong.

    +
    + @endif +
    +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/santri/uang-saku/show.blade.php b/sim-pkpps/resources/views/santri/uang-saku/show.blade.php new file mode 100644 index 0000000..ad867aa --- /dev/null +++ b/sim-pkpps/resources/views/santri/uang-saku/show.blade.php @@ -0,0 +1,178 @@ +{{-- resources/views/santri/uang-saku/show.blade.php --}} +@extends('layouts.app') + +@section('title', 'Detail Transaksi Uang Saku') + +@section('content') + + +
    +
    +
    +

    Informasi Transaksi

    +

    Detail lengkap transaksi uang saku

    +
    + +
    + +
    +

    Data Transaksi

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    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') }}
    +
    + +
    +

    Data Santri

    + + + + + + + + + + + + + +
    ID Santri{{ $transaksi->santri->id_santri }}
    Nama Lengkap{{ $transaksi->santri->nama_lengkap }}
    Kelas{{ $transaksi->santri->kelas }}
    +
    + + {{-- Grafik Pergerakan Saldo --}} +
    +

    Visualisasi Transaksi

    + +
    + {{-- Card Perbandingan --}} +
    + +
    Nominal Pemasukan
    +

    + {{ $transaksi->jenis_transaksi === 'pemasukan' ? 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') : 'Rp 0' }} +

    +
    + +
    + +
    Nominal Pengeluaran
    +

    + {{ $transaksi->jenis_transaksi === 'pengeluaran' ? 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') : 'Rp 0' }} +

    +
    +
    + + {{-- Progress Bar Perubahan Saldo --}} +
    +
    + Perubahan Saldo +
    + +
    + Saldo Sebelum + {{ 'Rp ' . number_format($transaksi->saldo_sebelum, 0, ',', '.') }} +
    + + @php + $maxSaldo = max($transaksi->saldo_sebelum, $transaksi->saldo_sesudah); + $persentaseSebelum = $maxSaldo > 0 ? ($transaksi->saldo_sebelum / $maxSaldo) * 100 : 0; + $persentaseSesudah = $maxSaldo > 0 ? ($transaksi->saldo_sesudah / $maxSaldo) * 100 : 0; + @endphp + +
    +
    + @if($persentaseSebelum > 15) + {{ number_format($persentaseSebelum, 0) }}% + @endif +
    +
    + +
    + +

    + {{ $transaksi->jenis_transaksi === 'pemasukan' ? 'Bertambah' : 'Berkurang' }} + {{ 'Rp ' . number_format(abs($transaksi->saldo_sesudah - $transaksi->saldo_sebelum), 0, ',', '.') }} +

    +
    + +
    +
    + @if($persentaseSesudah > 15) + {{ number_format($persentaseSesudah, 0) }}% + @endif +
    +
    + +
    + Saldo Sesudah + + {{ 'Rp ' . number_format($transaksi->saldo_sesudah, 0, ',', '.') }} + +
    +
    +
    + + +
    +@endsection \ No newline at end of file diff --git a/sim-pkpps/resources/views/welcome.blade.php b/sim-pkpps/resources/views/welcome.blade.php new file mode 100644 index 0000000..3fce17a --- /dev/null +++ b/sim-pkpps/resources/views/welcome.blade.php @@ -0,0 +1,133 @@ + + + + + + + Laravel + + + + + + + + + + + + diff --git a/sim-pkpps/routes/api.php b/sim-pkpps/routes/api.php new file mode 100644 index 0000000..889937e --- /dev/null +++ b/sim-pkpps/routes/api.php @@ -0,0 +1,19 @@ +get('/user', function (Request $request) { + return $request->user(); +}); diff --git a/sim-pkpps/routes/auth.php b/sim-pkpps/routes/auth.php new file mode 100644 index 0000000..1040b51 --- /dev/null +++ b/sim-pkpps/routes/auth.php @@ -0,0 +1,59 @@ +group(function () { + Route::get('register', [RegisteredUserController::class, 'create']) + ->name('register'); + + Route::post('register', [RegisteredUserController::class, 'store']); + + Route::get('login', [AuthenticatedSessionController::class, 'create']) + ->name('login'); + + Route::post('login', [AuthenticatedSessionController::class, 'store']); + + Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) + ->name('password.request'); + + Route::post('forgot-password', [PasswordResetLinkController::class, 'store']) + ->name('password.email'); + + Route::get('reset-password/{token}', [NewPasswordController::class, 'create']) + ->name('password.reset'); + + Route::post('reset-password', [NewPasswordController::class, 'store']) + ->name('password.store'); +}); + +Route::middleware('auth')->group(function () { + Route::get('verify-email', EmailVerificationPromptController::class) + ->name('verification.notice'); + + Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) + ->middleware(['signed', 'throttle:6,1']) + ->name('verification.verify'); + + Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) + ->middleware('throttle:6,1') + ->name('verification.send'); + + Route::get('confirm-password', [ConfirmablePasswordController::class, 'show']) + ->name('password.confirm'); + + Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']); + + Route::put('password', [PasswordController::class, 'update'])->name('password.update'); + + Route::post('logout', [AuthenticatedSessionController::class, 'destroy']) + ->name('logout'); +}); diff --git a/sim-pkpps/routes/channels.php b/sim-pkpps/routes/channels.php new file mode 100644 index 0000000..5d451e1 --- /dev/null +++ b/sim-pkpps/routes/channels.php @@ -0,0 +1,18 @@ +id === (int) $id; +}); diff --git a/sim-pkpps/routes/console.php b/sim-pkpps/routes/console.php new file mode 100644 index 0000000..e05f4c9 --- /dev/null +++ b/sim-pkpps/routes/console.php @@ -0,0 +1,19 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote'); diff --git a/sim-pkpps/routes/web.php b/sim-pkpps/routes/web.php new file mode 100644 index 0000000..567ba16 --- /dev/null +++ b/sim-pkpps/routes/web.php @@ -0,0 +1,335 @@ +route('admin.login'); +})->name('login'); + +// Halaman utama (root /) akan dialihkan ke halaman login admin +Route::get('/', function () { + return redirect()->route('admin.login'); +})->name('home'); + +// --- RUTE OTENTIKASI ADMIN --- +Route::prefix('admin')->middleware('guest')->group(function () { + // Login + Route::get('login', [AdminAuthController::class, 'login'])->name('admin.login'); + Route::post('login', [AdminAuthController::class, 'authenticate']); + + // Register (Hanya untuk Admin) + Route::get('register', [AdminAuthController::class, 'register'])->name('admin.register'); + Route::post('register', [AdminAuthController::class, 'storeRegister']); +}); + +// --- RUTE OTENTIKASI SANTRI/WALI --- +Route::prefix('santri')->middleware('guest')->group(function () { + // Login + Route::get('login', [SantriAuthController::class, 'login'])->name('santri.login'); + Route::post('login', [SantriAuthController::class, 'authenticate']); +}); + +// --- RUTE ADMINISTRATOR (Prefix: admin) --- +Route::prefix('admin') + ->middleware(['auth', 'role:admin']) + ->name('admin.')->group(function () { + + // Logout Admin + Route::post('logout', [AdminAuthController::class, 'logout'])->name('logout'); + + // 1. Dashboard Admin + Route::get('/dashboard', [DashboardController::class, 'admin'])->name('dashboard'); + + // 2. Data Santri (CRUD) + Route::resource('santri', SantriController::class); + + // 3. Manajemen Pengguna (Akun Santri & Wali) + Route::prefix('users')->name('users.')->group(function () { + Route::get('santri', [UserController::class, 'santriAccounts'])->name('santri_accounts'); + Route::get('santri/create', [UserController::class, 'createAccount'])->defaults('role', 'santri')->name('santri_create'); + Route::post('santri/store', [UserController::class, 'storeAccount'])->defaults('role', 'santri')->name('santri_store'); + + Route::get('wali', [UserController::class, 'waliAccounts'])->name('wali_accounts'); + Route::get('wali/create', [UserController::class, 'createAccount'])->defaults('role', 'wali')->name('wali_create'); + Route::post('wali/store', [UserController::class, 'storeAccount'])->defaults('role', 'wali')->name('wali_store'); + }); + + // 4. Kesehatan Santri + Route::resource('kesehatan-santri', KesehatanSantriController::class); + + // Route tambahan untuk keluar UKP + Route::patch('kesehatan-santri/{kesehatanSantri}/keluar-ukp', [ + KesehatanSantriController::class, + 'keluarUkp' + ])->name('kesehatan-santri.keluar-ukp'); + + // Route untuk riwayat kesehatan per santri + Route::get('kesehatan-santri/riwayat/{id_santri}', [ + KesehatanSantriController::class, + 'riwayat' + ])->name('kesehatan-santri.riwayat'); + + // Route untuk cetak surat + Route::get('kesehatan-santri/{kesehatanSantri}/cetak-surat', [ + KesehatanSantriController::class, + 'cetakSurat' + ])->name('kesehatan-santri.cetak-surat'); + + // 5. KEPULANGAN SANTRI + Route::resource('kepulangan', KepulanganController::class); + + // Route tambahan untuk approve/reject + Route::post('kepulangan/{kepulangan}/approve', [ + KepulanganController::class, + 'approve' + ])->name('kepulangan.approve'); + + Route::post('kepulangan/{kepulangan}/reject', [ + KepulanganController::class, + 'reject' + ])->name('kepulangan.reject'); + + Route::post('kepulangan/{kepulangan}/complete', [ + KepulanganController::class, + 'complete' + ])->name('kepulangan.complete'); + + Route::get('kepulangan/{kepulangan}/print', [ + KepulanganController::class, + 'print' + ])->name('kepulangan.print'); + + // API route untuk get santri data + Route::get('api/kepulangan/santri/{id_santri}', [ + KepulanganController::class, + 'getSantriData' + ])->name('api.kepulangan.santri'); + + // 6. BERITA + Route::prefix('berita')->name('berita.')->group(function () { + Route::get('/', [BeritaController::class, 'index'])->name('index'); + Route::get('/create', [BeritaController::class, 'create'])->name('create'); + Route::post('/', [BeritaController::class, 'store'])->name('store'); + Route::get('/statistik', [BeritaController::class, 'statistik'])->name('statistik'); + Route::get('/{berita:id_berita}', [BeritaController::class, 'show'])->name('show'); + Route::get('/{berita:id_berita}/edit', [BeritaController::class, 'edit'])->name('edit'); + Route::put('/{berita:id_berita}', [BeritaController::class, 'update'])->name('update'); + Route::delete('/{berita:id_berita}', [BeritaController::class, 'destroy'])->name('destroy'); + }); + + // 7. KATEGORI PELANGGARAN + Route::resource('kategori-pelanggaran', KategoriPelanggaranController::class); + + // 8. RIWAYAT PELANGGARAN + Route::resource('riwayat-pelanggaran', RiwayatPelanggaranController::class); + + // Route tambahan untuk riwayat per santri + Route::get('riwayat-pelanggaran/santri/{id_santri}', [ + RiwayatPelanggaranController::class, + 'riwayatSantri' + ])->name('riwayat-pelanggaran.riwayat-santri'); + + // 9. PEMBAYARAN SPP + Route::prefix('pembayaran-spp')->name('pembayaran-spp.')->group(function () { + Route::get('/', [PembayaranSppController::class, 'index'])->name('index'); + Route::get('/create', [PembayaranSppController::class, 'create'])->name('create'); + Route::post('/', [PembayaranSppController::class, 'store'])->name('store'); + + // Generate + Route::get('/generate', [PembayaranSppController::class, 'generate'])->name('generate'); + Route::post('/generate', [PembayaranSppController::class, 'generate']); + + // Laporan + Route::get('/laporan', [PembayaranSppController::class, 'laporan'])->name('laporan'); + Route::get('/cetak-laporan', [PembayaranSppController::class, 'cetakLaporan'])->name('cetak-laporan'); + Route::get('/cetak-laporan-santri/{id_santri}', [PembayaranSppController::class, 'cetakLaporanSantri'])->name('cetak-laporan-santri'); + Route::get('/{pembayaranSpp}/cetak-bukti', [PembayaranSppController::class, 'cetakBukti'])->name('cetak-bukti'); + + // Riwayat + Route::get('/riwayat/{id_santri}', [PembayaranSppController::class, 'riwayat'])->name('riwayat'); + + // Show, Edit, Update, Delete + Route::get('/{pembayaranSpp}', [PembayaranSppController::class, 'show'])->name('show'); + Route::get('/{pembayaranSpp}/edit', [PembayaranSppController::class, 'edit'])->name('edit'); + Route::put('/{pembayaranSpp}', [PembayaranSppController::class, 'update'])->name('update'); + Route::delete('/{pembayaranSpp}', [PembayaranSppController::class, 'destroy'])->name('destroy'); + }); + + // 10. UANG SAKU SANTRI + Route::prefix('uang-saku')->name('uang-saku.')->group(function () { + Route::get('/', [UangSakuController::class, 'index'])->name('index'); + Route::get('/create', [UangSakuController::class, 'create'])->name('create'); + Route::post('/', [UangSakuController::class, 'store'])->name('store'); + Route::get('/riwayat/{id_santri}', [UangSakuController::class, 'riwayat'])->name('riwayat'); + Route::get('/{uangSaku}', [UangSakuController::class, 'show'])->name('show'); + Route::get('/{uangSaku}/edit', [UangSakuController::class, 'edit'])->name('edit'); + Route::put('/{uangSaku}', [UangSakuController::class, 'update'])->name('update'); + Route::delete('/{uangSaku}', [UangSakuController::class, 'destroy'])->name('destroy'); + }); + + // 11. KATEGORI KEGIATAN + Route::resource('kategori-kegiatan', KategoriKegiatanController::class); + + // 12. KEGIATAN + Route::resource('kegiatan', KegiatanController::class); + + // 13. ABSENSI KEGIATAN + Route::prefix('absensi-kegiatan')->name('absensi-kegiatan.')->group(function () { + Route::get('/', [AbsensiKegiatanController::class, 'index'])->name('index'); + Route::get('/input/{kegiatan_id}', [AbsensiKegiatanController::class, 'inputAbsensi'])->name('input'); + Route::post('/simpan', [AbsensiKegiatanController::class, 'simpanAbsensi'])->name('simpan'); + Route::get('/rekap/{kegiatan_id}', [AbsensiKegiatanController::class, 'rekapAbsensi'])->name('rekap'); + Route::post('/scan-rfid', [AbsensiKegiatanController::class, 'scanRfid'])->name('scan-rfid'); + }); + + // 14. KARTU RFID + Route::prefix('kartu-rfid')->name('kartu-rfid.')->group(function () { + Route::get('/', [KartuRfidController::class, 'index'])->name('index'); + Route::get('/daftar/{id_santri}', [KartuRfidController::class, 'daftarRfid'])->name('daftar'); + Route::post('/simpan/{id_santri}', [KartuRfidController::class, 'simpanRfid'])->name('simpan'); + Route::delete('/hapus/{id_santri}', [KartuRfidController::class, 'hapusRfid'])->name('hapus'); + Route::get('/cetak/{id_santri}', [KartuRfidController::class, 'cetakKartu'])->name('cetak'); + }); + + // 15. RIWAYAT KEGIATAN & ABSENSI + Route::prefix('riwayat-kegiatan')->name('riwayat-kegiatan.')->group(function () { + Route::get('/', [RiwayatKegiatanController::class, 'index'])->name('index'); + Route::get('/detail-santri/{id_santri}', [RiwayatKegiatanController::class, 'detailSantri'])->name('detail-santri'); + Route::get('/{riwayat}', [RiwayatKegiatanController::class, 'show'])->name('show'); + Route::get('/{riwayat}/edit', [RiwayatKegiatanController::class, 'edit'])->name('edit'); + Route::put('/{riwayat}', [RiwayatKegiatanController::class, 'update'])->name('update'); + Route::delete('/{riwayat}', [RiwayatKegiatanController::class, 'destroy'])->name('destroy'); + Route::get('/export/pdf', [RiwayatKegiatanController::class, 'exportPdf'])->name('export-pdf'); + }); + + // 16. MASTER MATERI (Capaian Al-Qur'an & Hadist) + Route::resource('materi', MateriController::class); + + // 17. SEMESTER (Capaian Al-Qur'an & Hadist) + Route::resource('semester', SemesterController::class); + Route::post('semester/{semester}/toggle-aktif', [SemesterController::class, 'toggleAktif'])->name('semester.toggle-aktif'); + + // 18. CAPAIAN SANTRI (Al-Qur'an & Hadist) + Route::prefix('capaian')->name('capaian.')->group(function () { + // Dashboard & Rekap + Route::get('/dashboard', [CapaianController::class, 'dashboard'])->name('dashboard'); + Route::get('/rekap-kelas', [CapaianController::class, 'rekapKelas'])->name('rekap-kelas'); + Route::get('/detail-materi/{id_materi}', [CapaianController::class, 'detailMateri'])->name('detail-materi'); + + // CRUD Capaian + Route::get('/', [CapaianController::class, 'index'])->name('index'); + Route::get('/create', [CapaianController::class, 'create'])->name('create'); + Route::post('/', [CapaianController::class, 'store'])->name('store'); + Route::get('/riwayat/{id_santri}', [CapaianController::class, 'riwayatSantri'])->name('riwayat-santri'); + Route::get('/{capaian}', [CapaianController::class, 'show'])->name('show'); + Route::get('/{capaian}/edit', [CapaianController::class, 'edit'])->name('edit'); + Route::put('/{capaian}', [CapaianController::class, 'update'])->name('update'); + Route::delete('/{capaian}', [CapaianController::class, 'destroy'])->name('destroy'); + + // AJAX Routes + Route::post('/ajax/get-materi', [CapaianController::class, 'getMateriByKelas'])->name('ajax.get-materi'); + Route::post('/ajax/get-detail-materi', [CapaianController::class, 'getDetailMateri'])->name('ajax.get-detail-materi'); + Route::post('/ajax/calculate-persentase', [CapaianController::class, 'calculatePersentase'])->name('ajax.calculate-persentase'); + + // API untuk Grafik + Route::get('/api/grafik-data', [CapaianController::class, 'apiGrafikData'])->name('api.grafik-data'); + }); +}); + +/* +|-------------------------------------------------------------------------- +| BAGIAN SANTRI/WALI ROUTES +|-------------------------------------------------------------------------- +*/ + +Route::prefix('santri') + ->middleware(['auth', 'role:santri,wali']) + ->name('santri.') + ->group(function () { + + // Logout Santri + Route::post('logout', [SantriAuthController::class, 'logout'])->name('logout'); + + // Dashboard Santri + Route::get('/dashboard', [DashboardController::class, 'santri'])->name('dashboard'); + + // 1. PROFIL SANTRI + Route::prefix('profil')->name('profil.')->group(function () { + Route::get('/', [SantriProfileController::class, 'index'])->name('index'); + Route::get('/edit', [SantriProfileController::class, 'edit'])->name('edit'); + Route::put('/update', [SantriProfileController::class, 'update'])->name('update'); + }); + + // 2. RIWAYAT UANG SAKU + Route::prefix('uang-saku')->name('uang-saku.')->group(function () { + Route::get('/', [SantriUangSakuController::class, 'index'])->name('index'); + Route::get('/{id}', [SantriUangSakuController::class, 'show'])->name('show'); + }); + + // 3. RIWAYAT PELANGGARAN + Route::prefix('pelanggaran')->name('pelanggaran.')->group(function () { + Route::get('/', [SantriPelanggaranController::class, 'index'])->name('index'); + Route::get('/kategori/daftar', [SantriPelanggaranController::class, 'kategoriList'])->name('kategori'); + Route::get('/{riwayatPelanggaran}', [SantriPelanggaranController::class, 'show'])->name('show'); + }); + + // 4. BERITA SANTRI + Route::prefix('berita')->name('berita.')->group(function () { + Route::get('/', [SantriBeritaController::class, 'index'])->name('index'); + Route::get('/{berita:id_berita}', [SantriBeritaController::class, 'show'])->name('show'); + }); + + // 5. RIWAYAT KESEHATAN + Route::prefix('kesehatan')->name('kesehatan.')->group(function () { + Route::get('/', [SantriKesehatanController::class, 'index'])->name('index'); + Route::get('/{kesehatanSantri}', [SantriKesehatanController::class, 'show'])->name('show'); + }); + + // 6. CAPAIAN AL-QUR'AN & HADIST + Route::prefix('capaian')->name('capaian.')->group(function () { + Route::get('/', [SantriCapaianController::class, 'index'])->name('index'); + Route::get('/{id}', [SantriCapaianController::class, 'show'])->name('show'); + Route::get('/api/grafik-data', [SantriCapaianController::class, 'apiGrafikData'])->name('api.grafik-data'); + }); + + // 7. RIWAYAT KEPULANGAN + Route::prefix('kepulangan')->name('kepulangan.')->group(function () { + Route::get('/', [SantriKepulanganController::class, 'index'])->name('index'); + Route::get('/{kepulangan:id_kepulangan}', [SantriKepulanganController::class, 'show'])->name('show'); + }); +}); \ No newline at end of file diff --git a/sim-pkpps/storage/app/.gitignore b/sim-pkpps/storage/app/.gitignore new file mode 100644 index 0000000..8f4803c --- /dev/null +++ b/sim-pkpps/storage/app/.gitignore @@ -0,0 +1,3 @@ +* +!public/ +!.gitignore diff --git a/sim-pkpps/storage/app/public/.gitignore b/sim-pkpps/storage/app/public/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/sim-pkpps/storage/app/public/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/sim-pkpps/storage/framework/.gitignore b/sim-pkpps/storage/framework/.gitignore new file mode 100644 index 0000000..05c4471 --- /dev/null +++ b/sim-pkpps/storage/framework/.gitignore @@ -0,0 +1,9 @@ +compiled.php +config.php +down +events.scanned.php +maintenance.php +routes.php +routes.scanned.php +schedule-* +services.json diff --git a/sim-pkpps/storage/framework/cache/.gitignore b/sim-pkpps/storage/framework/cache/.gitignore new file mode 100644 index 0000000..01e4a6c --- /dev/null +++ b/sim-pkpps/storage/framework/cache/.gitignore @@ -0,0 +1,3 @@ +* +!data/ +!.gitignore diff --git a/sim-pkpps/storage/framework/sessions/D6FZEBgpwe0tXYewWTPUoB8jkSCmj1vaOfTQZXC2 b/sim-pkpps/storage/framework/sessions/D6FZEBgpwe0tXYewWTPUoB8jkSCmj1vaOfTQZXC2 new file mode 100644 index 0000000..a8b0bf9 --- /dev/null +++ b/sim-pkpps/storage/framework/sessions/D6FZEBgpwe0tXYewWTPUoB8jkSCmj1vaOfTQZXC2 @@ -0,0 +1 @@ +a:4:{s:6:"_token";s:40:"8ML9ZdqWCLmjAek9Pi9CSKX0CGiYJqxIKIH97x95";s:3:"url";a:1:{s:8:"intended";s:37:"http://127.0.0.1:8000/admin/dashboard";}s:9:"_previous";a:1:{s:3:"url";s:37:"http://127.0.0.1:8000/admin/dashboard";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}} \ No newline at end of file diff --git a/sim-pkpps/storage/framework/sessions/VZHgNRG1TSdcy9Emm4N24LHRBagOaImjTjDq7Jpu b/sim-pkpps/storage/framework/sessions/VZHgNRG1TSdcy9Emm4N24LHRBagOaImjTjDq7Jpu new file mode 100644 index 0000000..f9d298b --- /dev/null +++ b/sim-pkpps/storage/framework/sessions/VZHgNRG1TSdcy9Emm4N24LHRBagOaImjTjDq7Jpu @@ -0,0 +1 @@ +a:4:{s:6:"_token";s:40:"tLWa7PcNO0JI4f1QHde6luLyp4Ex4H9kRkY0yFqo";s:13:"last_activity";i:1763627918;s:9:"_previous";a:1:{s:3:"url";s:33:"http://127.0.0.1:8000/admin/login";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}} \ No newline at end of file diff --git a/sim-pkpps/storage/framework/sessions/g11HwTdR2XIUjyb8Ix2frVaf3dZj8VdBDNCTZCi6 b/sim-pkpps/storage/framework/sessions/g11HwTdR2XIUjyb8Ix2frVaf3dZj8VdBDNCTZCi6 new file mode 100644 index 0000000..9dfabfd --- /dev/null +++ b/sim-pkpps/storage/framework/sessions/g11HwTdR2XIUjyb8Ix2frVaf3dZj8VdBDNCTZCi6 @@ -0,0 +1 @@ +a:6:{s:6:"_token";s:40:"9vNjPLLee09wl7aDFMu598IYqYrzhRXo2tGcpKYW";s:50:"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d";i:2;s:17:"password_hash_web";s:60:"$2y$12$2MjNMVyBNLiANDZq0.J7E.wltuOfMH21Eqqx4teTEcr1T4ZayVGGq";s:13:"last_activity";i:1763627930;s:9:"_previous";a:1:{s:3:"url";s:38:"http://127.0.0.1:8000/santri/dashboard";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}} \ No newline at end of file diff --git a/sim-pkpps/storage/framework/testing/.gitignore b/sim-pkpps/storage/framework/testing/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/sim-pkpps/storage/framework/testing/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/sim-pkpps/storage/framework/views/072da14fbb17a3a780dd86ec6fb2a220.php b/sim-pkpps/storage/framework/views/072da14fbb17a3a780dd86ec6fb2a220.php new file mode 100644 index 0000000..e73b2c1 --- /dev/null +++ b/sim-pkpps/storage/framework/views/072da14fbb17a3a780dd86ec6fb2a220.php @@ -0,0 +1,272 @@ + + + +startSection('title', 'Riwayat Pelanggaran'); ?> + +startSection('content'); ?> + + + + +
    + + +
    + + + +
    + + +
    + + + +
    +
    +

    Total Pelanggaran

    +
    +

    Semua Riwayat

    + +
    + +
    +

    Bulan Ini

    +
    +

    format('F Y')); ?>

    + +
    + +
    +

    Total Poin

    +
    +

    Akumulasi Poin

    + +
    +
    + + +
    +

    + Filter & Pencarian +

    + +
    +
    + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    +
    + +
    + + + Reset + + +
    +
    +
    + + +
    +
    +

    + Daftar Riwayat Pelanggaran +

    + + Tambah Riwayat + +
    + + isNotEmpty()): ?> + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoID RiwayatTanggalSantriKategori PelanggaranPoinAksi
    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); ?> + + + +
    + + + + + + +
    + + + +
    +
    +
    + + +
    + links()); ?> + +
    + +
    + +

    Belum ada riwayat pelanggaran

    +

    Silakan tambah riwayat pelanggaran baru menggunakan tombol di atas.

    + + Tambah Riwayat + +
    + +
    + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/075d452fbecb627adfa4fe88bdcc349d.php b/sim-pkpps/storage/framework/views/075d452fbecb627adfa4fe88bdcc349d.php new file mode 100644 index 0000000..956dfc0 --- /dev/null +++ b/sim-pkpps/storage/framework/views/075d452fbecb627adfa4fe88bdcc349d.php @@ -0,0 +1,113 @@ + + +startSection('content'); ?> + + + +
    + + +
    + + +
    +
    +
    + + + + + + + + + hasAny(['hari', 'kategori_id', 'search'])): ?> + + Reset + + + + + Tambah Kegiatan + +
    +
    + + count() > 0): ?> + + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $kegiatan): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoIDHariWaktuNama KegiatanKategoriMateriAksi
    firstItem() + $index); ?>kegiatan_id); ?>hari); ?>waktu_mulai))); ?> - waktu_selesai))); ?>nama_kegiatan); ?>kategori->nama_kategori); ?>materi, 40) ?? '-'); ?> + + + + + + +
    + + + +
    +
    + +
    + links()); ?> + +
    + +
    + +

    Belum Ada Kegiatan

    +

    Silakan tambahkan jadwal kegiatan santri.

    + + Tambah Kegiatan + +
    + +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/0b444879160a4358a991decaae9d874a.php b/sim-pkpps/storage/framework/views/0b444879160a4358a991decaae9d874a.php new file mode 100644 index 0000000..b53a5cb --- /dev/null +++ b/sim-pkpps/storage/framework/views/0b444879160a4358a991decaae9d874a.php @@ -0,0 +1,180 @@ + + + + + + <?php echo $__env->yieldContent('title', 'SIM Santri'); ?> + + + + + + + + + + +
    +
    +

    SIM Santri

    +

    Monitoring Santri Berbasis Web

    +
    +
    +
    + + +
    + + + + + +
    + +
    + + +
    + + + +
    + yieldContent('content'); ?> +
    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/105000334f95beae17d9647d42ae0af0.php b/sim-pkpps/storage/framework/views/105000334f95beae17d9647d42ae0af0.php new file mode 100644 index 0000000..498ecfd --- /dev/null +++ b/sim-pkpps/storage/framework/views/105000334f95beae17d9647d42ae0af0.php @@ -0,0 +1,194 @@ + + +startSection('title', 'Detail Capaian'); ?> + +startSection('content'); ?> + + +
    +
    +

    Informasi Capaian

    + + Kembali + +
    + + +
    +

    Informasi Materi

    + + + + + + + + + + + + + +
    Nama Materimateri->nama_kitab); ?>
    Kategori + + materi->kategori); ?> + + +
    Total Halamanmateri->total_halaman); ?> halaman (Hal. materi->halaman_mulai); ?> - materi->halaman_akhir); ?>)
    +
    + + +
    +

    Progress Pembelajaran

    + + + + + + + + + + + + + + + + + + + + + + catatan): ?> + + + + + +
    Persentase Selesai +
    +
    +
    +
    + + persentase, 2)); ?>% + +
    +
    Jumlah Halaman Selesai + + pages_array)); ?> + + dari materi->total_halaman); ?> halaman +
    Detail Halaman Selesai +
    + halaman_selesai); ?> + +
    +
    Tanggal Inputtanggal_input)->format('d F Y')); ?>
    Semester + + semester->nama_semester); ?> - semester->tahun_ajaran); ?> + + +
    Catatan +
    + + catatan); ?> + +
    +
    +
    + + +
    +

    Visualisasi Halaman yang Diselesaikan

    +
    +
    + pages_array; + $totalPages = $capaian->materi->total_halaman; + $startPage = $capaian->materi->halaman_mulai; + ?> + + + +
    + + +
    + +
    + +
    +
    +
    + Selesai () +
    +
    +
    + Belum () +
    +
    +
    +
    + + +
    + persentase >= 100): ?> +
    + +

    Alhamdulillah, Materi Selesai! 🎉

    +

    Terus semangat untuk materi berikutnya!

    +
    + persentase >= 75): ?> +
    + +

    Hampir Selesai! 💪

    +

    Tinggal sedikit lagi, pertahankan semangatmu!

    +
    + persentase >= 50): ?> +
    + +

    Setengah Perjalanan! ⚡

    +

    Terus berjuang, jangan menyerah!

    +
    + +
    + +

    Baru Memulai! 🌱

    +

    Setiap perjalanan dimulai dari langkah pertama. Semangat!

    +
    + +
    +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/1bcee449d95fd7f4af3e834ca3f373a2.php b/sim-pkpps/storage/framework/views/1bcee449d95fd7f4af3e834ca3f373a2.php new file mode 100644 index 0000000..78a3857 --- /dev/null +++ b/sim-pkpps/storage/framework/views/1bcee449d95fd7f4af3e834ca3f373a2.php @@ -0,0 +1,121 @@ + + + +startSection('title', 'Riwayat Pelanggaran'); ?> + +startSection('content'); ?> + + + +
    +
    +

    Total Pelanggaran

    +
    + +
    + +
    +

    Total Poin

    +
    + +
    + +
    +

    Pelanggaran Bulan Ini

    +
    + +
    +
    + + + + + +
    + count() > 0): ?> +
    + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoID RiwayatTanggalJenis PelanggaranPoinKeteranganAksi
    firstItem() + $index); ?>id_riwayat); ?> + + tanggal)->isoFormat('D MMM YYYY')); ?> + + kategori->nama_pelanggaran ?? '-'); ?> + + poin); ?> + + + +
    + keterangan ?: '-'); ?> + +
    +
    + + Detail + +
    +
    + + +
    + links()); ?> + +
    + +
    + +

    Tidak Ada Riwayat Pelanggaran

    +

    Selamat! Anda belum memiliki catatan pelanggaran.

    +
    + +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/25e18337d272f9d0e44384a50ffc0f2e.php b/sim-pkpps/storage/framework/views/25e18337d272f9d0e44384a50ffc0f2e.php new file mode 100644 index 0000000..767eb97 --- /dev/null +++ b/sim-pkpps/storage/framework/views/25e18337d272f9d0e44384a50ffc0f2e.php @@ -0,0 +1,228 @@ + + +startSection('content'); ?> + + + +
    + + +
    + + + +
    + + +
    + + + +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + + hasAny(['search', 'id_santri', 'jenis_transaksi', 'tanggal_dari', 'tanggal_sampai'])): ?> + + Reset + + +
    +
    +
    + + + count() > 0): ?> + + + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoID TransaksiSantriTanggalJenisNominalKeteranganSaldoAksi
    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); ?> + + + +
    + + + + + + +
    + + + +
    +
    +
    + +
    + links()); ?> + +
    + +
    + +

    Belum Ada Transaksi

    +

    Belum ada transaksi uang saku yang tercatat. Tambahkan transaksi pertama!

    + + Tambah Transaksi + +
    + +
    + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/31a477eb975396d9e3dfe350a914f63d.php b/sim-pkpps/storage/framework/views/31a477eb975396d9e3dfe350a914f63d.php new file mode 100644 index 0000000..8041f05 --- /dev/null +++ b/sim-pkpps/storage/framework/views/31a477eb975396d9e3dfe350a914f63d.php @@ -0,0 +1,184 @@ + + + +startSection('title', 'Detail Transaksi Uang Saku'); ?> + +startSection('content'); ?> + + +
    +
    +
    +

    Informasi Transaksi

    +

    Detail lengkap transaksi uang saku

    +
    + +
    + +
    +

    Data Transaksi

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID Transaksiid_uang_saku); ?>
    Tanggal Transaksitanggal_transaksi)->isoFormat('dddd, D MMMM YYYY')); ?>
    Jenis Transaksi + jenis_transaksi === 'pemasukan'): ?> + + Pemasukan + + + + Pengeluaran + + +
    Nominal + + nominal, 0, ',', '.')); ?> + + +
    Saldo Sebelumsaldo_sebelum, 0, ',', '.')); ?>
    Saldo Sesudah + + saldo_sesudah, 0, ',', '.')); ?> + + +
    Keteranganketerangan ?? '-'); ?>
    Dicatat Padacreated_at->format('d/m/Y H:i:s')); ?>
    +
    + +
    +

    Data Santri

    + + + + + + + + + + + + + +
    ID Santrisantri->id_santri); ?>
    Nama Lengkapsantri->nama_lengkap); ?>
    Kelassantri->kelas); ?>
    +
    + + +
    +

    Visualisasi Transaksi

    + +
    + +
    + +
    Nominal Pemasukan
    +

    + jenis_transaksi === 'pemasukan' ? 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') : 'Rp 0'); ?> + +

    +
    + +
    + +
    Nominal Pengeluaran
    +

    + jenis_transaksi === 'pengeluaran' ? 'Rp ' . number_format($transaksi->nominal, 0, ',', '.') : 'Rp 0'); ?> + +

    +
    +
    + + +
    +
    + Perubahan Saldo +
    + +
    + Saldo Sebelum + saldo_sebelum, 0, ',', '.')); ?> +
    + + saldo_sebelum, $transaksi->saldo_sesudah); + $persentaseSebelum = $maxSaldo > 0 ? ($transaksi->saldo_sebelum / $maxSaldo) * 100 : 0; + $persentaseSesudah = $maxSaldo > 0 ? ($transaksi->saldo_sesudah / $maxSaldo) * 100 : 0; + ?> + +
    +
    + 15): ?> + % + +
    +
    + +
    + +

    + jenis_transaksi === 'pemasukan' ? 'Bertambah' : 'Berkurang'); ?> + saldo_sesudah - $transaksi->saldo_sebelum), 0, ',', '.')); ?> +

    +
    + +
    +
    + 15): ?> + % + +
    +
    + +
    + Saldo Sesudah + + saldo_sesudah, 0, ',', '.')); ?> + + +
    +
    +
    + + +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/34a382bc0d43efb21e7b74c864109136.php b/sim-pkpps/storage/framework/views/34a382bc0d43efb21e7b74c864109136.php new file mode 100644 index 0000000..5d7df1d --- /dev/null +++ b/sim-pkpps/storage/framework/views/34a382bc0d43efb21e7b74c864109136.php @@ -0,0 +1,295 @@ + + + +startSection('title', 'Detail Riwayat Pelanggaran'); ?> + +startSection('content'); ?> + + + +
    + +
    + +
    + +
    +
    +
    +

    Informasi Riwayat

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    ID Riwayat + + id_riwayat); ?> + + +
    Tanggal Pelanggaran + + tanggal)->isoFormat('dddd, D MMMM YYYY')); ?> + + +
    Poin Pelanggaran + + poin); ?> Poin + +
    Tanggal Dicatatcreated_at->format('d F Y, H:i')); ?> WIB
    Terakhir Diperbaruiupdated_at->format('d F Y, H:i')); ?> WIB
    +
    + + +
    +

    + Data Santri +

    + + santri): ?> + + + + + + + + + + + + + + + + + + + + + +
    ID Santri + santri->id_santri); ?> +
    Nama Lengkap + santri->nama_lengkap); ?> +
    Kelassantri->kelas); ?>
    NISsantri->nis); ?>
    Total Poin Pelanggaran + + santri->total_poin_pelanggaran); ?> Poin + +
    + + + +
    + +

    Data santri tidak ditemukan

    +
    + +
    + + +
    +

    + Kategori Pelanggaran +

    + + kategori): ?> + + + + + + + + + + + + + +
    ID Kategori + kategori->id_kategori); ?> +
    Nama Pelanggaran + kategori->nama_pelanggaran); ?> +
    Poin Kategori + + kategori->poin); ?> Poin + +
    + + + +
    + +

    Kategori tidak ditemukan

    +
    + +
    + + + keterangan): ?> +
    +

    + Keterangan Tambahan +

    +
    +

    keterangan); ?>

    +
    +
    + + + + isNotEmpty()): ?> +
    +

    + Riwayat Pelanggaran Lainnya (Santri yang Sama) +

    + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $riwayat): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoTanggalKategoriPoinAksi
    tanggal)->format('d M Y')); ?> + kategori): ?> + kategori->nama_pelanggaran); ?> + + - + + + poin); ?> + + + + +
    + + +
    + +
    + + +
    + + + + +
    +

    + Poin Pelanggaran +

    +
    +
    poin); ?>
    +

    Poin Pelanggaran Ini

    +
    + + santri): ?> +
    +
    +
    + santri->total_poin_pelanggaran); ?> + +
    +

    Total Poin Santri

    +
    +
    + +
    + + +
    +

    + Informasi +

    +

    + Riwayat ini dicatat pada created_at->format('d F Y')); ?> + untuk pelanggaran yang terjadi pada tanggal)->format('d F Y')); ?>. +

    +
    +
    +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/35ee7d3140dd2cb469794c46a4c597db.php b/sim-pkpps/storage/framework/views/35ee7d3140dd2cb469794c46a4c597db.php new file mode 100644 index 0000000..2509ce3 --- /dev/null +++ b/sim-pkpps/storage/framework/views/35ee7d3140dd2cb469794c46a4c597db.php @@ -0,0 +1,185 @@ + + +startSection('title', 'Detail Kepulangan'); ?> + +startSection('content'); ?> +
    +
    +
    +

    Detail Izin Kepulangan

    +

    + ID: id_kepulangan); ?> +

    +
    + + Kembali + +
    + +
    + + +
    + + + Status: status); ?> + + + + status == 'Disetujui' && $kepulangan->is_aktif): ?> + + Sedang Dalam Periode Pulang + + status == 'Disetujui' && $kepulangan->is_terlambat): ?> + + Terlambat Kembali + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + approved_by): ?> + + + + + + + + + + catatan): ?> + + + + + +
    ID Kepulanganid_kepulangan); ?>
    Tanggal Pengajuantanggal_izin_formatted); ?>
    Tanggal Pulangtanggal_pulang_formatted); ?>
    Tanggal Kembalitanggal_kembali_formatted); ?>
    Durasi Izin + durasi_izin); ?> hari +
    Alasan Kepulanganalasan); ?>
    Status + + status); ?> + + +
    Disetujui Olehapproved_by); ?>
    Tanggal Persetujuanapproved_at_formatted); ?>
    Catatan Admincatatan); ?>
    + + +
    +

    + Informasi Kuota Tahun + +

    +
    +
    +

    Total Hari Pulang

    +

    + hari +

    +
    +
    +

    Sisa Kuota

    +

    + hari +

    +
    +
    +

    Kuota Maksimal

    +

    + 12 hari +

    +
    +
    +
    +
    + + +status == 'Menunggu'): ?> +
    + + Menunggu Persetujuan: Izin kepulangan Anda sedang dalam proses review. + Mohon tunggu konfirmasi dari pengurus. +
    +status == 'Disetujui'): ?> + is_aktif): ?> +
    + + Sedang Pulang: Anda sedang dalam periode kepulangan. + Pastikan kembali sesuai jadwal: tanggal_kembali_formatted); ?>. +
    + is_terlambat): ?> +
    + + Terlambat Kembali: Anda telah melewati tanggal kembali yang dijadwalkan. + Segera hubungi pengurus! +
    + +
    + + Izin Disetujui: Kepulangan Anda telah disetujui. + Pastikan untuk pulang dan kembali sesuai jadwal yang telah ditentukan. +
    + +status == 'Ditolak'): ?> +
    + + Izin Ditolak: Maaf, izin kepulangan Anda tidak disetujui. + catatan): ?> + Alasan: catatan); ?> + +
    +status == 'Selesai'): ?> +
    + + Kepulangan Selesai: Anda telah menyelesaikan periode kepulangan ini. +
    + + + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/42e0614621fd9cbad7786f12bdf5b5c9.php b/sim-pkpps/storage/framework/views/42e0614621fd9cbad7786f12bdf5b5c9.php new file mode 100644 index 0000000..c8060d5 --- /dev/null +++ b/sim-pkpps/storage/framework/views/42e0614621fd9cbad7786f12bdf5b5c9.php @@ -0,0 +1,134 @@ + + + +startSection('title', 'Riwayat Uang Saku'); ?> + +startSection('content'); ?> + + + + +
    + + +
    + + + +
    + + +
    + + + +
    +
    +

    Total Pemasukan

    +
    +
    +
    + +
    +

    Total Pengeluaran

    +
    +
    +
    + +
    +

    Saldo Terakhir

    +
    +
    +
    +
    + + +
    +
    + + + + + + + + + + + + Reset + +
    +
    + + +
    + count() > 0): ?> + + + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $transaksi): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoID TransaksiTanggalJenisNominalKeteranganSaldo SebelumSaldo SesudahAksi
    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 + +
    + + +
    + links()); ?> + +
    + +
    + +

    Belum Ada Transaksi

    +

    Riwayat uang saku Anda masih kosong.

    +
    + +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/43f661f38fbd4f777442ca71c4728fc9.php b/sim-pkpps/storage/framework/views/43f661f38fbd4f777442ca71c4728fc9.php new file mode 100644 index 0000000..ba8c5bd --- /dev/null +++ b/sim-pkpps/storage/framework/views/43f661f38fbd4f777442ca71c4728fc9.php @@ -0,0 +1,195 @@ + + +startSection('title', 'Detail Berita - ' . $berita->id_berita); ?> + +startSection('content'); ?> + + + +
    +
    +
    + + status === 'published'): ?> + Published + + Draft + + +
    + +
    +
    + + +
    +
    + +
    +
    + + ID: id_berita); ?> + + +
    + +

    + judul); ?> + +

    + +
    + + + Penulis: penulis); ?> + + + + + Tanggal: created_at->format('d M Y, H:i')); ?> WIB + + + target_berita) { + 'semua' => 'badge-primary', + 'kelas_tertentu' => 'badge-info', + 'santri_tertentu' => 'badge-warning', + default => 'badge-secondary' + }; + ?> + + target_audience); ?> + + + +
    +
    + + + gambar): ?> +
    + Gambar Berita +
    + + + +
    +

    Konten Berita

    +
    + konten)); ?> + +
    +
    + + + target_berita === 'santri_tertentu' || $berita->target_berita === 'kelas_tertentu'): ?> +
    + target_berita === 'kelas_tertentu'): ?> +

    + + Target Kelas: target_kelas ?? [])); ?> + +

    +
    +

    + + Berita ini ditujukan untuk santri dari kelas: + target_kelas ?? [])); ?> +

    +
    + + + santriTertentu->count() > 0): ?> +

    + + Daftar Penerima Berita (santriTertentu->count()); ?> Santri) +

    + +
    + santriTertentu; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $santri): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> +
    +
    + +
    + nama_lengkap, 0, 1))); ?> + +
    + +
    +
    + id_santri); ?> + +
    +
    + nama_lengkap); ?> + +
    +
    + kelas); ?> + +
    +
    + + +
    + pivot->sudah_dibaca): ?> + + Dibaca + + + + Belum + + +
    +
    +
    + popLoop(); $loop = $__env->getLastLoop(); ?> +
    + +
    + +

    Belum ada santri yang dipilih untuk berita ini.

    +
    + +
    + + + +
    +
    + + Edit Berita + + +
    + + + +
    + + + Daftar Berita + +
    +
    +
    +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/452c34a3ceda4cc1b53f0fe5d3c51b99.php b/sim-pkpps/storage/framework/views/452c34a3ceda4cc1b53f0fe5d3c51b99.php new file mode 100644 index 0000000..9ae3d92 --- /dev/null +++ b/sim-pkpps/storage/framework/views/452c34a3ceda4cc1b53f0fe5d3c51b99.php @@ -0,0 +1,57 @@ + + +startSection('title', $berita->judul); ?> + +startSection('content'); ?> +
    + +
    +
    +

    judul); ?>

    +
    + penulis); ?> + created_at->format('d F Y, H:i')); ?> WIB + Sudah dibaca +
    +
    + + Kembali + +
    + +
    + + + gambar): ?> +
    + <?php echo e($berita->judul); ?> +
    + + + +
    + konten)); ?> + +
    + + +
    +

    + Dipublikasikan pada created_at->format('d F Y, H:i')); ?> WIB +

    +
    +
    + + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/46ab65c9284ea5ee3a8118881424f90c.php b/sim-pkpps/storage/framework/views/46ab65c9284ea5ee3a8118881424f90c.php new file mode 100644 index 0000000..a94117e --- /dev/null +++ b/sim-pkpps/storage/framework/views/46ab65c9284ea5ee3a8118881424f90c.php @@ -0,0 +1,91 @@ + + +startSection('content'); ?> + + + +
    + + +
    + + +
    +
    +
    +
    + + + + + + + +
    +
    + + Tambah Kategori + +
    + + count() > 0): ?> + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $kategori): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoID KategoriNama KategoriKeteranganDibuatAksi
    firstItem() + $index); ?>kategori_id); ?>nama_kategori); ?>keterangan, 50) ?? '-'); ?>created_at->format('d M Y')); ?> + + + + + + +
    + + + +
    +
    + +
    + links()); ?> + +
    + +
    + +

    Belum Ada Kategori

    +

    Silakan tambahkan kategori kegiatan baru.

    + + Tambah Kategori + +
    + +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/4c2e61e495793889f762efcce89628d8.php b/sim-pkpps/storage/framework/views/4c2e61e495793889f762efcce89628d8.php new file mode 100644 index 0000000..0fc59c1 --- /dev/null +++ b/sim-pkpps/storage/framework/views/4c2e61e495793889f762efcce89628d8.php @@ -0,0 +1,153 @@ + + + + + + 500 - Server Error + + + + +
    +
    + +
    + +

    500

    +

    Oops! Terjadi Kesalahan Server

    + +

    + Maaf, terjadi kesalahan pada server. Tim kami sudah diberitahu dan sedang menangani masalah ini. +

    + + +
    + Detail Error (Debug Mode): +

    + +

    Message:

    + +
    + + + + Kembali + + + + Dashboard + +
    + + \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/6611960912faff880255c6e631dc5a29.php b/sim-pkpps/storage/framework/views/6611960912faff880255c6e631dc5a29.php new file mode 100644 index 0000000..8f948f2 --- /dev/null +++ b/sim-pkpps/storage/framework/views/6611960912faff880255c6e631dc5a29.php @@ -0,0 +1,117 @@ +hasPages()): ?> + + + \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/66e2096cb3c35849e326885249110f62.php b/sim-pkpps/storage/framework/views/66e2096cb3c35849e326885249110f62.php new file mode 100644 index 0000000..011beb1 --- /dev/null +++ b/sim-pkpps/storage/framework/views/66e2096cb3c35849e326885249110f62.php @@ -0,0 +1,275 @@ + + +startSection('title', 'Riwayat Kesehatan'); ?> + +startSection('content'); ?> + + + +any()): ?> +
    + + Error: first()); ?> + +
    + + + +
    +
    +

    Total Kunjungan

    +
    +
    +

    + Periode yang dipilih +

    +
    + +
    +

    Sedang Dirawat

    +
    +
    + 0): ?> +

    + Perlu perhatian +

    + +

    + Tidak ada yang dirawat +

    + +
    + +
    +

    Sembuh

    +
    +
    +

    + Alhamdulillah +

    +
    + +
    +

    Izin Sakit

    +
    +
    +

    + Izin pulang +

    +
    +
    + + +
    +
    +
    + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + + Reset + +
    +
    +
    + + +
    +

    + + Menampilkan data periode: + + locale('id')->isoFormat('D MMMM Y')); ?> - locale('id')->isoFormat('D MMMM Y')); ?> + + + (diffInDays($tanggalSampai) + 1); ?> hari) +

    +
    +
    + + +isEmpty()): ?> +
    + +

    Tidak Ada Data

    +

    Tidak ada riwayat kesehatan pada periode yang dipilih.

    + + Lihat Semua Data + +
    + +
    +

    + Daftar Riwayat (total()); ?> data) +

    + + + + +
    + links()); ?> + +
    +
    + + + +
    + + Info: Gunakan filter tanggal untuk melihat riwayat kesehatan pada periode tertentu. + Jika tidak difilter, data yang ditampilkan adalah untuk bulan berjalan. +
    + + + + + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/72906883c2124eae424a49669807e619.php b/sim-pkpps/storage/framework/views/72906883c2124eae424a49669807e619.php new file mode 100644 index 0000000..c0a6ea6 --- /dev/null +++ b/sim-pkpps/storage/framework/views/72906883c2124eae424a49669807e619.php @@ -0,0 +1,69 @@ + + + +startSection('title', 'Detail Riwayat Pelanggaran'); ?> + +startSection('content'); ?> + + +
    +
    +

    + + Riwayat ID: id_riwayat); ?> + +

    + +
    + +
    +

    Informasi Pelanggaran

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID Riwayatid_riwayat); ?>
    Tanggal Kejadiantanggal)->isoFormat('dddd, D MMMM YYYY')); ?>
    Jenis Pelanggarankategori->nama_pelanggaran ?? '-'); ?>
    Poin Pelanggaran + + poin); ?> Poin + +
    Keteranganketerangan ?: '-'); ?>
    Dicatat Padacreated_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. +

    +
    +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/84c709f0b9be2a14b454115d6ed83ed6.php b/sim-pkpps/storage/framework/views/84c709f0b9be2a14b454115d6ed83ed6.php new file mode 100644 index 0000000..56c1d6c --- /dev/null +++ b/sim-pkpps/storage/framework/views/84c709f0b9be2a14b454115d6ed83ed6.php @@ -0,0 +1,50 @@ + + + + + + <?php echo $__env->yieldContent('title', 'Login'); ?> | SIM Santri + + + + + +
    + yieldContent('auth-content'); ?> +
    + + \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/87643fe3b359872f8ce3c66a3684eab2.php b/sim-pkpps/storage/framework/views/87643fe3b359872f8ce3c66a3684eab2.php new file mode 100644 index 0000000..78259ee --- /dev/null +++ b/sim-pkpps/storage/framework/views/87643fe3b359872f8ce3c66a3684eab2.php @@ -0,0 +1,510 @@ + + + + +startSection('title', 'Data Kepulangan Santri'); ?> + +startSection('content'); ?> + + + +
    +
    +

    Total Data

    +
    + +
    +
    +

    Menunggu Approval

    +
    + +
    +
    +

    Sedang Izin

    +
    + +
    +
    +

    Over Limit

    +
    + +
    +
    + + + +
    + + +
    + + + +
    + + +
    + + + +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + + Reset + +
    +
    +
    + + +
    + + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?> + + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?> + + + + + +
    IDSantriTanggal PulangTanggal KembaliDurasiAlasanStatusAksi
    + id_kepulangan); ?> + id_santri])): ?> + + + + + +
    + santri->nama_lengkap ?? 'N/A'); ?>
    + + santri->id_santri ?? ''); ?> | santri->kelas ?? ''); ?> + + +
    +
    tanggal_pulang_formatted); ?>tanggal_kembali_formatted); ?> + + durasi_izin_calculated); ?> hari + + + + alasan); ?> + + + + + status); ?> + + + is_aktif): ?> +
    Sedang Izin + is_terlambat): ?> +
    Terlambat + +
    +
    + + + + + status == 'Menunggu'): ?> + + + + + + + + status == 'Disetujui'): ?> + + + + + + + status, ['Menunggu', 'Ditolak'])): ?> + + +
    +
    + +

    Tidak ada data kepulangan ditemukan

    +
    +
    + + + hasPages()): ?> +
    +
    + Menampilkan firstItem() ?? 0); ?> - lastItem() ?? 0); ?> + dari total()); ?> data +
    +
    + appends(request()->query())->links()); ?> + +
    +
    + +
    + + + + + + + + + + + + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/995c109454d8c33c3f0f95e2c6a9b380.php b/sim-pkpps/storage/framework/views/995c109454d8c33c3f0f95e2c6a9b380.php new file mode 100644 index 0000000..66c3673 --- /dev/null +++ b/sim-pkpps/storage/framework/views/995c109454d8c33c3f0f95e2c6a9b380.php @@ -0,0 +1,217 @@ + + + +startSection('title', 'Data Kategori Pelanggaran'); ?> + +startSection('content'); ?> + + + + +
    + + +
    + + + +
    + + +
    + + + +
    +

    + + Edit Kategori + + Tambah Kategori + +

    +
    + + + + + +
    +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + + Poin antara 1-100 +
    +
    + +
    + + + + Batal + + +
    +
    +
    + + +
    +
    +

    + Daftar Kategori Pelanggaran +

    + + Total: count()); ?> Kategori + +
    + + isNotEmpty()): ?> + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoID KategoriNama PelanggaranPoinDigunakanAksi
    + id_kategori); ?> + + nama_pelanggaran); ?> + + + poin); ?> + + + + + riwayatPelanggaran->count()); ?>x + + +
    + + + + + + +
    + + + +
    +
    +
    + +
    + +

    Belum ada data kategori pelanggaran

    +

    Mulai dengan menambahkan kategori pelanggaran baru menggunakan form di atas.

    +
    + +
    + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/9b446f86a5179b2559bd927fbd573416.php b/sim-pkpps/storage/framework/views/9b446f86a5179b2559bd927fbd573416.php new file mode 100644 index 0000000..b0b522a --- /dev/null +++ b/sim-pkpps/storage/framework/views/9b446f86a5179b2559bd927fbd573416.php @@ -0,0 +1,53 @@ + + +startSection('title', 'Manajemen Akun Santri'); ?> + +startSection('content'); ?> + + + +
    + + +
    + + +

    Daftar Akun Santri (count()); ?>)

    + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $user): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?> + + + + + + + popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?> + + + + + +
    ID SantriNamaUsernameAksi
    role_id); ?>name); ?>username); ?> + Reset Password + Hapus Akun +
    Belum ada akun Santri yang terdaftar.
    + +

    Data Santri Tanpa Akun (count()); ?>)

    +

    Berikut adalah data santri yang sudah terdaftar di Data Santri namun belum memiliki akun login. Mereka dapat dipilih saat Anda membuat akun baru.

    + +
    +stopSection(); ?> +make('layouts.app', ['isAdmin' => true], \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/a35bdf7f5682e1163e4e422bf7ed3156.php b/sim-pkpps/storage/framework/views/a35bdf7f5682e1163e4e422bf7ed3156.php new file mode 100644 index 0000000..e87b7dd --- /dev/null +++ b/sim-pkpps/storage/framework/views/a35bdf7f5682e1163e4e422bf7ed3156.php @@ -0,0 +1,86 @@ + + \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/a3a0962d896f35dffe41a6f25b643d59.php b/sim-pkpps/storage/framework/views/a3a0962d896f35dffe41a6f25b643d59.php new file mode 100644 index 0000000..23b98d1 --- /dev/null +++ b/sim-pkpps/storage/framework/views/a3a0962d896f35dffe41a6f25b643d59.php @@ -0,0 +1,134 @@ + + + +startSection('title', 'Berita'); ?> + +startSection('content'); ?> + + +isEmpty()): ?> +
    + +

    Belum Ada Berita

    +

    Belum ada berita atau pengumuman yang dipublikasikan untuk Anda.

    +
    + +
    +
    + addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + sudah_dibaca): ?> + + Baru + + + + +
    + gambar): ?> + <?php echo e($item->judul); ?> + +
    + +
    + +
    + + +
    + +
    +

    + judul); ?> + +

    + + +

    + konten), 150)); ?> + +

    +
    + + +
    + penulis); ?> + created_at->format('d M Y')); ?> + + Baca + +
    +
    +
    + popLoop(); $loop = $__env->getLastLoop(); ?> +
    + + +
    + links()); ?> + +
    +
    + + + +
    + + Info: Berita yang ditandai dengan badge Baru adalah berita yang belum Anda baca. Klik pada berita untuk membaca selengkapnya. +
    + + + + + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/a55ee0bd4bb0ff61d001bcba38d0f720.php b/sim-pkpps/storage/framework/views/a55ee0bd4bb0ff61d001bcba38d0f720.php new file mode 100644 index 0000000..41c14aa --- /dev/null +++ b/sim-pkpps/storage/framework/views/a55ee0bd4bb0ff61d001bcba38d0f720.php @@ -0,0 +1,147 @@ + + +startSection('title', 'Data Santri'); ?> + +startSection('content'); ?> + + + +
    + + +
    + + +
    + +
    + + + Tambah Santri + + + +
    + + + + + + + + + + + Reset + + +
    +
    + + + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $santri): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); $__empty_1 = false; ?> + + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); if ($__empty_1): ?> + + + + + +
    NoID SantriNISNama LengkapJenis KelaminKelasStatusAksi
    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. + +
    + + count() > 0): ?> +
    +

    + + Menampilkan count()); ?> data santri + + dari hasil pencarian/filter + +

    +
    + + + + +
    + links()); ?> + +
    + +
    +stopSection(); ?> +make('layouts.app', ['isAdmin' => true], \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/a65eb8f69fbdf55d07dff257bb47073e.php b/sim-pkpps/storage/framework/views/a65eb8f69fbdf55d07dff257bb47073e.php new file mode 100644 index 0000000..b0048ae --- /dev/null +++ b/sim-pkpps/storage/framework/views/a65eb8f69fbdf55d07dff257bb47073e.php @@ -0,0 +1,93 @@ + + +startSection('content'); ?> + + + +
    + + +
    + + + +
    + + +
    + + +
    +
    +
    + + + + + + + Reset + + +
    +
    + + count() > 0): ?> + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $kegiatan): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoHariWaktuNama KegiatanKategoriAksi
    firstItem() + $index); ?>hari); ?>waktu_mulai))); ?> - waktu_selesai))); ?>nama_kegiatan); ?>kategori->nama_kategori); ?> + + Input + + + Rekap + +
    + +
    + links()); ?> + +
    + +
    + +

    Belum Ada Kegiatan

    +

    Silakan tambahkan kegiatan terlebih dahulu.

    + + Tambah Kegiatan + +
    + +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/a866f51eee86f2c6a47752a971068445.php b/sim-pkpps/storage/framework/views/a866f51eee86f2c6a47752a971068445.php new file mode 100644 index 0000000..60daadd --- /dev/null +++ b/sim-pkpps/storage/framework/views/a866f51eee86f2c6a47752a971068445.php @@ -0,0 +1,283 @@ + + +startSection('title', 'Data Kesehatan Santri'); ?> + +startSection('content'); ?> + + + + +
    + + +
    + + + +
    + + +
    + + + +
    + + + + +
    +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + + Reset + +
    +
    +
    +
    + + + + count() > 0): ?> + + + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $data): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoID SantriNama SantriTgl MasukKeluhanTgl KeluarStatusLamaAksi
    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 +
    + + + + + + + + + status == 'dirawat'): ?> + + + + + + + +
    + + + +
    +
    +
    + + +
    + appends(request()->query())->links()); ?> + +
    + +
    + +

    Tidak ada data kesehatan santri

    +

    Belum ada data kesehatan santri yang tercatat atau sesuai dengan filter yang dipilih.

    + + Tambah Data Kesehatan + +
    + + + + + + + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/b189c66d92090748551bf01fbdf8d452.php b/sim-pkpps/storage/framework/views/b189c66d92090748551bf01fbdf8d452.php new file mode 100644 index 0000000..6f9583a --- /dev/null +++ b/sim-pkpps/storage/framework/views/b189c66d92090748551bf01fbdf8d452.php @@ -0,0 +1,82 @@ + + +startSection('title', 'Register Admin'); ?> + +startSection('auth-content'); ?> +
    +
    + +
    +

    Pendaftaran Akun Admin

    +

    Mohon gunakan email dan password yang kuat untuk keamanan sistem.

    +
    + + +any()): ?> +
    + all(); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $error): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> +

    + popLoop(); $loop = $__env->getLastLoop(); ?> +
    + + +
    + + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?>
    +
    + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?>
    +
    + +
    + + +
    + +
    + +
    + +

    + Sudah punya akun? Login di sini +

    +
    +stopSection(); ?> +make('auth.auth_layout', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/b97350e57d8d92217317f5f41c8d18a5.php b/sim-pkpps/storage/framework/views/b97350e57d8d92217317f5f41c8d18a5.php new file mode 100644 index 0000000..3053c9f --- /dev/null +++ b/sim-pkpps/storage/framework/views/b97350e57d8d92217317f5f41c8d18a5.php @@ -0,0 +1,198 @@ + + +startSection('title', 'Dashboard Santri'); ?> + +startSection('content'); ?> + + + + +
    + + Perhatian: Anda sedang dalam perawatan UKP sejak tanggal_masuk_formatted); ?> + (lama_dirawat); ?> hari). Keluhan: keluhan); ?>. + Lihat Detail +
    + + + +
    + + Sedang Pulang: Anda sedang dalam periode kepulangan + (tanggal_pulang_formatted); ?> - tanggal_kembali_formatted); ?>). + Pastikan kembali tepat waktu! + Lihat Detail +
    + + + +
    + +
    +

    Progres Al-Qur'an

    +
    %
    +
    +
    +
    +
    +
    +
    +
    + + +
    +

    Progres Hadist

    +
    %
    +
    +
    +
    +
    +
    +
    +
    + + +
    +

    Saldo Uang Saku

    +
    +
    + +
    + + +
    +

    Total Poin Pelanggaran

    +
    +
    + 0): ?> + + +
    + + Tidak ada pelanggaran + +
    + +
    +
    + + +isNotEmpty()): ?> +
    +
    +

    + Berita Terbaru (7 Hari Terakhir) +

    + + Lihat Semua + +
    + +
    + addLoop($__currentLoopData); foreach($__currentLoopData as $berita): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + +
    +

    + + judul); ?> + +

    +

    + created_at->diffForHumans()); ?> + +

    +
    + + + +
    + popLoop(); $loop = $__env->getLastLoop(); ?> +
    +
    + + + + + + +
    +

    + Tips Hari Ini +

    +

    + 💡 Jaga Kedisiplinan: Hindari pelanggaran dengan mematuhi tata tertib pondok. + Lihat Daftar Kategori Pelanggaran + untuk mengetahui peraturan yang berlaku. +

    +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/bbf90b40396229b9af5ccf996fe33770.php b/sim-pkpps/storage/framework/views/bbf90b40396229b9af5ccf996fe33770.php new file mode 100644 index 0000000..c4b8d9e --- /dev/null +++ b/sim-pkpps/storage/framework/views/bbf90b40396229b9af5ccf996fe33770.php @@ -0,0 +1,194 @@ + + +startSection('content'); ?> + + +
    +
    + + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> +
    + +
    + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> +
    + +
    + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> +
    + + Masukkan nominal tanpa titik atau koma +
    + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> +
    + +
    + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> +
    + + Opsional - Jelaskan detail transaksi +
    + +
    + + + Kembali + +
    +
    +
    + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/c5f0b5e8cf684d75aae3123501a0192f.php b/sim-pkpps/storage/framework/views/c5f0b5e8cf684d75aae3123501a0192f.php new file mode 100644 index 0000000..922b50f --- /dev/null +++ b/sim-pkpps/storage/framework/views/c5f0b5e8cf684d75aae3123501a0192f.php @@ -0,0 +1,213 @@ + + + +startSection('title', 'Tambah Riwayat Pelanggaran'); ?> + +startSection('content'); ?> + + + +
    + +
    + +
    +
    +

    + Form Tambah Riwayat +

    +
    + ID Riwayat Berikutnya: + +
    +
    + +
    + + +
    + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    + + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    +
    + + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + + + + +
    + + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + + Maksimal 1000 karakter +
    + +
    + + + Kembali + +
    +
    +
    + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/c9c645ef9ca26f7f8150d4f3aae0f194.php b/sim-pkpps/storage/framework/views/c9c645ef9ca26f7f8150d4f3aae0f194.php new file mode 100644 index 0000000..6254072 --- /dev/null +++ b/sim-pkpps/storage/framework/views/c9c645ef9ca26f7f8150d4f3aae0f194.php @@ -0,0 +1,231 @@ + + +startSection('title', 'Riwayat Kepulangan'); ?> + +startSection('content'); ?> + + + +
    +
    +

    Total Pengajuan

    +
    +
    +

    + Tahun + +

    +
    + +
    +

    Disetujui

    +
    +
    +

    + Izin diterima +

    +
    + +
    +

    Total Hari Pulang

    +
    Hari
    +
    + +

    + Melebihi batas! +

    + +

    + Dari kuota 12 hari +

    + +
    + +
    +

    Sisa Kuota

    +
    Hari
    +
    + 0): ?> +

    + menunggu +

    + +

    + Kuota tersisa +

    + +
    +
    + + + +
    + + Peringatan: Anda telah melebihi batas kuota kepulangan (12 hari/tahun). + Total hari pulang Anda tahun ini: hari. +
    + 0): ?> +
    + + Perhatian: Sisa kuota kepulangan Anda tinggal hari. + Gunakan dengan bijak. +
    + + + +
    +
    + + + + + + + + Reset + +
    +
    + + +isEmpty()): ?> +
    + +

    Belum Ada Riwayat Kepulangan

    +

    Anda belum pernah mengajukan izin kepulangan pada periode yang dipilih.

    +
    + +
    +

    + Daftar Riwayat (total()); ?> data) +

    + + + + +
    + links()); ?> + +
    +
    + + + +
    + + Info: Kuota kepulangan maksimal 12 hari per tahun. + Pastikan Anda merencanakan kepulangan dengan bijak agar tidak melebihi batas kuota. +
    + + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/e126e1062982b7622152c3d631f3ccc4.php b/sim-pkpps/storage/framework/views/e126e1062982b7622152c3d631f3ccc4.php new file mode 100644 index 0000000..ddceaea --- /dev/null +++ b/sim-pkpps/storage/framework/views/e126e1062982b7622152c3d631f3ccc4.php @@ -0,0 +1,161 @@ + + +startSection('title', 'Profil Santri'); ?> + +startSection('content'); ?> + + + + +
    + + +
    + + +
    + +
    +
    + +
    + nama_lengkap, 0, 1))); ?> + +
    + +
    +

    + nama_lengkap); ?> + +

    +

    + id_santri); ?> + + nis): ?> + | NIS: nis); ?> + + +

    +
    +
    + + +
    + +
    + + +
    +

    Data Pribadi

    + + + + + + nis): ?> + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID Santriid_santri); ?>
    NISnis); ?>
    Nama Lengkapnama_lengkap); ?>
    Jenis Kelaminjenis_kelamin); ?>
    Kelas + + + kelas_lengkap); ?> + + +
    Statusstatus_badge; ?>
    Terdaftar Sejakcreated_at->format('d F Y')); ?>
    +
    + + +
    +

    Kontak & Alamat

    + + + + + + + + + + + + + + + + + +
    Alamat Lengkapalamat_santri ?? '-'); ?>
    Daerah Asaldaerah_asal ?? '-'); ?>
    Nama Orang Tua/Walinama_orang_tua ?? '-'); ?>
    Nomor HP Orang Tua + nomor_hp_ortu): ?> + + nomor_hp_ortu); ?> + + + + - + +
    +
    + + + has_rfid): ?> +
    +

    Kartu RFID

    +
    +

    + + Kartu RFID Anda sudah terdaftar +

    +

    + UID: rfid_uid); ?> +

    +
    +
    + +
    +

    Kartu RFID

    +
    + + Kartu RFID Anda belum terdaftar. Silakan hubungi admin untuk pendaftaran kartu. +
    +
    + + + +
    +

    + + Catatan: Jika ada data yang perlu diperbarui selain alamat dan nomor HP orang tua, silakan hubungi admin atau pengurus pesantren. +

    +
    +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/e7a3eb2c7c53a4310ad6e32050798b86.php b/sim-pkpps/storage/framework/views/e7a3eb2c7c53a4310ad6e32050798b86.php new file mode 100644 index 0000000..9bafc25 --- /dev/null +++ b/sim-pkpps/storage/framework/views/e7a3eb2c7c53a4310ad6e32050798b86.php @@ -0,0 +1,49 @@ + + + +startSection('title', 'Login Santri'); ?> + +startSection('auth-content'); ?> +
    +

    Login Santri/Wali

    +

    Akses progres dan laporan santri.

    +
    + +any()): ?> +
    + first()); ?> + +
    + + +
    + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    + +

    + Lupa akun? Hubungi Admin. +

    +
    +stopSection(); ?> + +make('auth.auth_layout', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/ef3052ff8b6e3fc5621ceb75a6dd6413.php b/sim-pkpps/storage/framework/views/ef3052ff8b6e3fc5621ceb75a6dd6413.php new file mode 100644 index 0000000..bf498f8 --- /dev/null +++ b/sim-pkpps/storage/framework/views/ef3052ff8b6e3fc5621ceb75a6dd6413.php @@ -0,0 +1,337 @@ + + +startSection('title', 'Capaian Al-Qur\'an & Hadist'); ?> + +startSection('content'); ?> + + + + +
    + + +
    + + + +
    + +
    +

    Total Materi

    +
    +
    +
    + + +
    +

    Rata-rata Progress

    +
    %
    +
    +
    +
    +
    +
    +
    +
    + + +
    +

    Materi Selesai

    +
    +
    + 0): ?> +

    + % dari total materi +

    + +
    + + +
    +

    Kelas

    +
    kelas); ?>
    +
    +

    + NIS: nis ?? '-'); ?> + +

    +
    +
    + + +
    +

    + Statistik per Kategori +

    + +
    + addLoop($__currentLoopData); foreach($__currentLoopData as $kategori => $data): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> +
    +
    +

    + + Materi + +
    + +
    +
    + Progress + % +
    +
    +
    +
    +
    + +
    + Selesai + Berlangsung +
    +
    + popLoop(); $loop = $__env->getLastLoop(); ?> +
    +
    + + +
    +

    + Visualisasi Progress +

    + +
    + +
    +

    Progress per Kategori

    + +
    + + +
    +

    Distribusi Persentase

    + +
    +
    +
    + + +
    +
    +

    + Daftar Capaian +

    + + +
    + +
    +
    + + count() > 0): ?> +
    + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $capaian): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoMateriKategoriHalaman SelesaiProgressTanggal InputAksi
    + materi->nama_kitab); ?> + + + materi->kategori); ?> + + + + + pages_array)); ?> dari materi->total_halaman); ?> halaman + + +
    +
    +
    +
    + + persentase, 1)); ?>% + +
    +
    tanggal_input)->format('d M Y')); ?> + + Detail + +
    +
    + +
    + +

    Belum Ada Data Capaian

    +

    + + Tidak ada data capaian untuk semester yang dipilih. + + Belum ada data capaian yang tercatat untuk Anda. + +

    +
    + +
    + + + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/f00f9dae67317e274040c9fcaec81e32.php b/sim-pkpps/storage/framework/views/f00f9dae67317e274040c9fcaec81e32.php new file mode 100644 index 0000000..b482467 --- /dev/null +++ b/sim-pkpps/storage/framework/views/f00f9dae67317e274040c9fcaec81e32.php @@ -0,0 +1,421 @@ + + +startSection('title', 'Tambah Berita Baru'); ?> + +startSection('content'); ?> + + + +any()): ?> +
    + Terdapat kesalahan: +
      + all(); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $error): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> +
    • + popLoop(); $loop = $__env->getLastLoop(); ?> +
    +
    + + +
    +
    + + + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    + + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    + + +
    +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    + +
    + + + Format: JPG, PNG, GIF. Maksimal 2MB. + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    +
    + + +
    +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> + + +
    +
    + + + + + + + + +
    + + + Batal + +
    +
    +
    + + +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/f2968196d7f31ca7e2832869d2e2aef0.php b/sim-pkpps/storage/framework/views/f2968196d7f31ca7e2832869d2e2aef0.php new file mode 100644 index 0000000..58cb7ed --- /dev/null +++ b/sim-pkpps/storage/framework/views/f2968196d7f31ca7e2832869d2e2aef0.php @@ -0,0 +1,185 @@ + + +
  • + + Dashboard + +
  • + + + + + + + + + + + + + + + + + +
  • + + Kesehatan Santri + +
  • + + +
  • + + Kepulangan Santri + +
  • + + +
  • + + Berita + +
  • + + +
  • +
    + + + Logout + +
    +
  • \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/f61d0f67d6967e731c17960b0dff77d3.php b/sim-pkpps/storage/framework/views/f61d0f67d6967e731c17960b0dff77d3.php new file mode 100644 index 0000000..9b6906e --- /dev/null +++ b/sim-pkpps/storage/framework/views/f61d0f67d6967e731c17960b0dff77d3.php @@ -0,0 +1,37 @@ + + + +startSection('title', 'Dashboard Admin'); ?> + +startSection('content'); ?> + + +
    +
    +

    Total Santri

    +

    + +
    +
    +

    Total Wali Santri

    +

    + +
    +
    +

    Kegiatan Hari Ini

    +

    + +
    +
    + +
    +

    Statistik & Grafik

    +
    +

    Area untuk menempatkan statistik dan grafik sistem.

    +
    +
    +stopSection(); ?> +make('layouts.app', ['isAdmin' => true], \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/f7d8d067fc835ce54b0dc2d499afcbbd.php b/sim-pkpps/storage/framework/views/f7d8d067fc835ce54b0dc2d499afcbbd.php new file mode 100644 index 0000000..98a6585 --- /dev/null +++ b/sim-pkpps/storage/framework/views/f7d8d067fc835ce54b0dc2d499afcbbd.php @@ -0,0 +1,248 @@ + + + +startSection('title', 'Login Admin'); ?> + +startSection('auth-content'); ?> +
    +

    Admin Login

    +

    Sistem Informasi Monitoring Santri

    +
    + + +any()): ?> +
    + + first()); ?> + +
    + + + + +
    + + + +
    + + +
    + + + +
    + + + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> +
    + +
    + + +
    + +
    + + +
    + getBag($__errorArgs[1] ?? 'default'); +if ($__bag->has($__errorArgs[0])) : +if (isset($message)) { $__messageOriginal = $message; } +$message = $__bag->first($__errorArgs[0]); ?> +
    + +
    + + +
    + + +
    + + +
    + +
    + + +

    + Admin baru? Daftar Sekarang +

    + + +
    +

    + Login sebagai santri/wali? +

    + + Login Santri/Wali + +
    +
    + + + +stopSection(); ?> +make('auth.auth_layout', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/f9c3e31ca3780ee24993ca37d8e92eee.php b/sim-pkpps/storage/framework/views/f9c3e31ca3780ee24993ca37d8e92eee.php new file mode 100644 index 0000000..5d699dd --- /dev/null +++ b/sim-pkpps/storage/framework/views/f9c3e31ca3780ee24993ca37d8e92eee.php @@ -0,0 +1,68 @@ + + + +startSection('title', 'Daftar Kategori Pelanggaran'); ?> + +startSection('content'); ?> + + +
    +
    +

    + + Berikut adalah daftar kategori pelanggaran beserta poin yang berlaku di pondok. +

    + + Kembali ke Riwayat + +
    + + count() > 0): ?> +
    + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $index => $kategori): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    NoKodeJenis PelanggaranPoin
    id_kategori); ?>nama_pelanggaran); ?> + poin <= 5): ?> + + poin); ?> Poin + + poin <= 15): ?> + + poin); ?> Poin + + + + poin); ?> Poin + + +
    +
    + +
    + +

    Belum Ada Kategori

    +

    Daftar kategori pelanggaran belum tersedia.

    +
    + +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/framework/views/f9de4ee507702ddee56d80d4870eb11b.php b/sim-pkpps/storage/framework/views/f9de4ee507702ddee56d80d4870eb11b.php new file mode 100644 index 0000000..14896f1 --- /dev/null +++ b/sim-pkpps/storage/framework/views/f9de4ee507702ddee56d80d4870eb11b.php @@ -0,0 +1,170 @@ + + +startSection('title', 'Daftar Berita'); ?> + +startSection('content'); ?> + + + +
    +
    + +
    + + + + + + + + + + Reset + +
    + + + +
    +
    + + + +
    + + +
    + + + +
    + + +
    + + + +
    + count() > 0): ?> + + + + + + + + + + + + + + addLoop($__currentLoopData); foreach($__currentLoopData as $item): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?> + + + + + + + + + + popLoop(); $loop = $__env->getLastLoop(); ?> + +
    IDJudul & KontenPenulisTanggalStatusTargetAksi
    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); ?> + + + +
    + + + + + + +
    + + + +
    +
    +
    + + +
    + appends(request()->query())->links()); ?> + +
    + +
    + +

    Belum Ada Berita

    +

    + Mulai tambahkan berita pertama untuk santri pesantren. +

    + + Tambah Berita Pertama + +
    + +
    +stopSection(); ?> +make('layouts.app', \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))->render(); ?> \ No newline at end of file diff --git a/sim-pkpps/storage/logs/.gitignore b/sim-pkpps/storage/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/sim-pkpps/storage/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/sim-pkpps/tailwind.config.js b/sim-pkpps/tailwind.config.js new file mode 100644 index 0000000..c29eb1a --- /dev/null +++ b/sim-pkpps/tailwind.config.js @@ -0,0 +1,21 @@ +import defaultTheme from 'tailwindcss/defaultTheme'; +import forms from '@tailwindcss/forms'; + +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', + './storage/framework/views/*.php', + './resources/views/**/*.blade.php', + ], + + theme: { + extend: { + fontFamily: { + sans: ['Figtree', ...defaultTheme.fontFamily.sans], + }, + }, + }, + + plugins: [forms], +}; diff --git a/sim-pkpps/tests/CreatesApplication.php b/sim-pkpps/tests/CreatesApplication.php new file mode 100644 index 0000000..cc68301 --- /dev/null +++ b/sim-pkpps/tests/CreatesApplication.php @@ -0,0 +1,21 @@ +make(Kernel::class)->bootstrap(); + + return $app; + } +} diff --git a/sim-pkpps/tests/Feature/Auth/AuthenticationTest.php b/sim-pkpps/tests/Feature/Auth/AuthenticationTest.php new file mode 100644 index 0000000..0303b29 --- /dev/null +++ b/sim-pkpps/tests/Feature/Auth/AuthenticationTest.php @@ -0,0 +1,55 @@ +get('/login'); + + $response->assertStatus(200); + } + + public function test_users_can_authenticate_using_the_login_screen(): void + { + $user = User::factory()->create(); + + $response = $this->post('/login', [ + 'email' => $user->email, + 'password' => 'password', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(RouteServiceProvider::HOME); + } + + public function test_users_can_not_authenticate_with_invalid_password(): void + { + $user = User::factory()->create(); + + $this->post('/login', [ + 'email' => $user->email, + 'password' => 'wrong-password', + ]); + + $this->assertGuest(); + } + + public function test_users_can_logout(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/logout'); + + $this->assertGuest(); + $response->assertRedirect('/'); + } +} diff --git a/sim-pkpps/tests/Feature/Auth/EmailVerificationTest.php b/sim-pkpps/tests/Feature/Auth/EmailVerificationTest.php new file mode 100644 index 0000000..ba19d9c --- /dev/null +++ b/sim-pkpps/tests/Feature/Auth/EmailVerificationTest.php @@ -0,0 +1,65 @@ +create([ + 'email_verified_at' => null, + ]); + + $response = $this->actingAs($user)->get('/verify-email'); + + $response->assertStatus(200); + } + + public function test_email_can_be_verified(): void + { + $user = User::factory()->create([ + 'email_verified_at' => null, + ]); + + Event::fake(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1($user->email)] + ); + + $response = $this->actingAs($user)->get($verificationUrl); + + Event::assertDispatched(Verified::class); + $this->assertTrue($user->fresh()->hasVerifiedEmail()); + $response->assertRedirect(RouteServiceProvider::HOME.'?verified=1'); + } + + public function test_email_is_not_verified_with_invalid_hash(): void + { + $user = User::factory()->create([ + 'email_verified_at' => null, + ]); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1('wrong-email')] + ); + + $this->actingAs($user)->get($verificationUrl); + + $this->assertFalse($user->fresh()->hasVerifiedEmail()); + } +} diff --git a/sim-pkpps/tests/Feature/Auth/PasswordConfirmationTest.php b/sim-pkpps/tests/Feature/Auth/PasswordConfirmationTest.php new file mode 100644 index 0000000..ff85721 --- /dev/null +++ b/sim-pkpps/tests/Feature/Auth/PasswordConfirmationTest.php @@ -0,0 +1,44 @@ +create(); + + $response = $this->actingAs($user)->get('/confirm-password'); + + $response->assertStatus(200); + } + + public function test_password_can_be_confirmed(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/confirm-password', [ + 'password' => 'password', + ]); + + $response->assertRedirect(); + $response->assertSessionHasNoErrors(); + } + + public function test_password_is_not_confirmed_with_invalid_password(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/confirm-password', [ + 'password' => 'wrong-password', + ]); + + $response->assertSessionHasErrors(); + } +} diff --git a/sim-pkpps/tests/Feature/Auth/PasswordResetTest.php b/sim-pkpps/tests/Feature/Auth/PasswordResetTest.php new file mode 100644 index 0000000..aa50350 --- /dev/null +++ b/sim-pkpps/tests/Feature/Auth/PasswordResetTest.php @@ -0,0 +1,73 @@ +get('/forgot-password'); + + $response->assertStatus(200); + } + + public function test_reset_password_link_can_be_requested(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class); + } + + public function test_reset_password_screen_can_be_rendered(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) { + $response = $this->get('/reset-password/'.$notification->token); + + $response->assertStatus(200); + + return true; + }); + } + + public function test_password_can_be_reset_with_valid_token(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { + $response = $this->post('/reset-password', [ + 'token' => $notification->token, + 'email' => $user->email, + 'password' => 'password', + 'password_confirmation' => 'password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect(route('login')); + + return true; + }); + } +} diff --git a/sim-pkpps/tests/Feature/Auth/PasswordUpdateTest.php b/sim-pkpps/tests/Feature/Auth/PasswordUpdateTest.php new file mode 100644 index 0000000..ca28c6c --- /dev/null +++ b/sim-pkpps/tests/Feature/Auth/PasswordUpdateTest.php @@ -0,0 +1,51 @@ +create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->put('/password', [ + 'current_password' => 'password', + 'password' => 'new-password', + 'password_confirmation' => 'new-password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); + } + + public function test_correct_password_must_be_provided_to_update_password(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->put('/password', [ + 'current_password' => 'wrong-password', + 'password' => 'new-password', + 'password_confirmation' => 'new-password', + ]); + + $response + ->assertSessionHasErrorsIn('updatePassword', 'current_password') + ->assertRedirect('/profile'); + } +} diff --git a/sim-pkpps/tests/Feature/Auth/RegistrationTest.php b/sim-pkpps/tests/Feature/Auth/RegistrationTest.php new file mode 100644 index 0000000..30829b1 --- /dev/null +++ b/sim-pkpps/tests/Feature/Auth/RegistrationTest.php @@ -0,0 +1,32 @@ +get('/register'); + + $response->assertStatus(200); + } + + public function test_new_users_can_register(): void + { + $response = $this->post('/register', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'password' => 'password', + 'password_confirmation' => 'password', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(RouteServiceProvider::HOME); + } +} diff --git a/sim-pkpps/tests/Feature/ExampleTest.php b/sim-pkpps/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..8364a84 --- /dev/null +++ b/sim-pkpps/tests/Feature/ExampleTest.php @@ -0,0 +1,19 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/sim-pkpps/tests/Feature/ProfileTest.php b/sim-pkpps/tests/Feature/ProfileTest.php new file mode 100644 index 0000000..252fdcc --- /dev/null +++ b/sim-pkpps/tests/Feature/ProfileTest.php @@ -0,0 +1,99 @@ +create(); + + $response = $this + ->actingAs($user) + ->get('/profile'); + + $response->assertOk(); + } + + public function test_profile_information_can_be_updated(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->patch('/profile', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $user->refresh(); + + $this->assertSame('Test User', $user->name); + $this->assertSame('test@example.com', $user->email); + $this->assertNull($user->email_verified_at); + } + + public function test_email_verification_status_is_unchanged_when_the_email_address_is_unchanged(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->patch('/profile', [ + 'name' => 'Test User', + 'email' => $user->email, + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $this->assertNotNull($user->refresh()->email_verified_at); + } + + public function test_user_can_delete_their_account(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->delete('/profile', [ + 'password' => 'password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/'); + + $this->assertGuest(); + $this->assertNull($user->fresh()); + } + + public function test_correct_password_must_be_provided_to_delete_account(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->delete('/profile', [ + 'password' => 'wrong-password', + ]); + + $response + ->assertSessionHasErrorsIn('userDeletion', 'password') + ->assertRedirect('/profile'); + + $this->assertNotNull($user->fresh()); + } +} diff --git a/sim-pkpps/tests/TestCase.php b/sim-pkpps/tests/TestCase.php new file mode 100644 index 0000000..2932d4a --- /dev/null +++ b/sim-pkpps/tests/TestCase.php @@ -0,0 +1,10 @@ +assertTrue(true); + } +} diff --git a/sim-pkpps/vite.config.js b/sim-pkpps/vite.config.js new file mode 100644 index 0000000..89f26f5 --- /dev/null +++ b/sim-pkpps/vite.config.js @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: [ + 'resources/css/app.css', + 'resources/js/app.js', + ], + refresh: true, + }), + ], +});