From 797713ef492795186d26d7cbc4641d9ebaebe815 Mon Sep 17 00:00:00 2001 From: nico Date: Mon, 8 Sep 2025 14:02:21 +0800 Subject: [PATCH] Fix UI Admin Menu Kesehatan, Login Admin, OTP --- bun.lockb | Bin 349440 -> 349875 bytes package.json | 1 + .../data_kesehatan_warga/artikelKesehatan.ts | 46 +- .../fasilitasKesehatan.ts | 41 +- .../data_kesehatan_warga/jadwalKegiatan.ts | 76 +-- .../(dashboard)/_state/user/user-state.ts | 26 ++ .../(dashboard)/auth/login-admin/page.tsx | 111 +++++ .../auth/registrasi-admin/page.tsx | 121 +++++ .../(dashboard)/auth/validasi-admin/page.tsx | 38 ++ .../data-kesehatan-warga/_lib/layoutTabs.tsx | 191 +++++--- .../artikel_kesehatan/[id]/edit/page.tsx | 380 +++++++-------- .../artikel_kesehatan/[id]/page.tsx | 253 ++++++---- .../artikel_kesehatan/create/page.tsx | 182 +++++--- .../artikel_kesehatan/page.tsx | 167 +++++-- .../fasilitas_kesehatan/[id]/edit/page.tsx | 438 ++++++++---------- .../fasilitas_kesehatan/[id]/page.tsx | 240 ++++++---- .../fasilitas_kesehatan/create/page.tsx | 276 ++++++----- .../fasilitas_kesehatan/page.tsx | 194 +++++--- .../grafik_hasil_kepuasan/[id]/edit/page.tsx | 112 +++-- .../grafik_hasil_kepuasan/[id]/page.tsx | 182 ++++---- .../grafik_hasil_kepuasan/create/page.tsx | 167 ++++--- .../grafik_hasil_kepuasan/page.tsx | 272 ++++++----- .../jadwal_kegiatan/[id]/edit/page.tsx | 386 ++++++--------- .../jadwal_kegiatan/[id]/page.tsx | 191 +++++--- .../jadwal_kegiatan/create/page.tsx | 198 +++++--- .../jadwal_kegiatan/page.tsx | 181 ++++++-- .../kelahiran/[id]/edit/page.tsx | 234 ++++++---- .../kelahiran/[id]/page.tsx | 247 ++++++---- .../kelahiran/create/page.tsx | 181 +++++--- .../kelahiran/page.tsx | 273 +++++++---- .../kematian/[id]/edit/page.tsx | 262 +++++++---- .../kematian/[id]/page.tsx | 253 +++++----- .../kematian/create/page.tsx | 208 +++++---- .../kematian/page.tsx | 267 +++++++---- .../page.tsx | 433 +++++++++-------- .../info-wabah-penyakit/[id]/edit/page.tsx | 240 ++++++---- .../info-wabah-penyakit/[id]/page.tsx | 187 +++++--- .../info-wabah-penyakit/create/page.tsx | 183 +++++--- .../kesehatan/info-wabah-penyakit/page.tsx | 134 ++++-- .../kontak-darurat/[id]/edit/page.tsx | 213 +++++---- .../kesehatan/kontak-darurat/[id]/page.tsx | 167 ++++--- .../kesehatan/kontak-darurat/create/page.tsx | 213 ++++++--- .../kesehatan/kontak-darurat/page.tsx | 148 ++++-- .../penanganan-darurat/[id]/edit/page.tsx | 198 ++++---- .../penanganan-darurat/[id]/page.tsx | 175 ++++--- .../penanganan-darurat/create/page.tsx | 140 ++++-- .../kesehatan/penanganan-darurat/page.tsx | 159 ++++--- .../kesehatan/posyandu/[id]/edit/page.tsx | 391 +++++++++------- .../kesehatan/posyandu/[id]/page.tsx | 248 ++++++---- .../kesehatan/posyandu/create/page.tsx | 341 ++++++++------ .../(dashboard)/kesehatan/posyandu/page.tsx | 252 ++++++---- .../program-kesehatan/[id]/edit/page.tsx | 239 ++++++---- .../kesehatan/program-kesehatan/[id]/page.tsx | 162 ++++--- .../program-kesehatan/create/page.tsx | 197 ++++---- .../kesehatan/program-kesehatan/page.tsx | 151 +++--- .../kesehatan/puskesmas/[id]/edit/page.tsx | 309 ++++++------ .../kesehatan/puskesmas/[id]/page.tsx | 178 ++++--- .../kesehatan/puskesmas/create/page.tsx | 279 +++++------ .../(dashboard)/kesehatan/puskesmas/page.tsx | 148 ++++-- .../profile/pejabat-desa/page.tsx | 2 +- .../info-sekolah/jenjang-pendidikan/page.tsx | 20 +- .../admin/(dashboard)/user&role/user/page.tsx | 21 +- src/app/admin/auth/_lib/api_fetch_auth.ts | 32 +- src/app/admin/auth/login/page.tsx | 81 ---- src/app/admin/layout.tsx | 18 +- .../artikel_kesehatan/findMany.ts | 90 ++-- .../fasilitas_kesehatan/findMany.ts | 75 ++- .../jadwal_kegiatan/findMany.ts | 86 ++-- src/app/api/[[...slugs]]/_lib/user/index.ts | 4 +- src/app/api/[[...slugs]]/_lib/user/updt.ts | 40 ++ src/app/api/auth/_lib/decrypt.ts | 50 ++ src/app/api/auth/_lib/encrypt.ts | 26 ++ src/app/api/auth/_lib/get_KodeOtp_By_Id.ts | 13 + src/app/api/auth/_lib/randomOTP.ts | 4 + src/app/api/auth/_lib/session_create.ts | 36 ++ src/app/api/auth/login/route.ts | 63 +++ .../_lib => }/auth/register/route.ts | 0 src/app/login/page.tsx | 10 + src/app/registrasi/page.tsx | 12 + src/app/validasi/page.tsx | 9 + 80 files changed, 7648 insertions(+), 4924 deletions(-) create mode 100644 src/app/admin/(dashboard)/auth/login-admin/page.tsx create mode 100644 src/app/admin/(dashboard)/auth/registrasi-admin/page.tsx create mode 100644 src/app/admin/(dashboard)/auth/validasi-admin/page.tsx delete mode 100644 src/app/admin/auth/login/page.tsx create mode 100644 src/app/api/[[...slugs]]/_lib/user/updt.ts create mode 100644 src/app/api/auth/_lib/decrypt.ts create mode 100644 src/app/api/auth/_lib/encrypt.ts create mode 100644 src/app/api/auth/_lib/get_KodeOtp_By_Id.ts create mode 100644 src/app/api/auth/_lib/randomOTP.ts create mode 100644 src/app/api/auth/_lib/session_create.ts create mode 100644 src/app/api/auth/login/route.ts rename src/app/api/{[[...slugs]]/_lib => }/auth/register/route.ts (100%) create mode 100644 src/app/login/page.tsx create mode 100644 src/app/registrasi/page.tsx create mode 100644 src/app/validasi/page.tsx diff --git a/bun.lockb b/bun.lockb index 31228ed16a0e5c3fb0190d427135a0a787bf517b..75df6ef63fb6d63d982aef0736cb7217bf450322 100755 GIT binary patch delta 62986 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 ks@=dNfMR20{f-^Al`is*8ugT^cH[] | null, + page: 1, + totalPages: 1, loading: false, - async load() { + search: "", + load: async (page = 1, limit = 10, search = "") => { + artikelKesehatanState.findMany.loading = true; // ✅ Akses langsung via nama path + artikelKesehatanState.findMany.page = page; + artikelKesehatanState.findMany.search = search; + try { - this.loading = true; - const res = await (ApiFetch.api.kesehatan as any)["artikel-kesehatan"][ + const query: any = { page, limit }; + if (search) query.search = search; + + const res = await ApiFetch.api.kesehatan["artikel-kesehatan"][ "find-many" - ].get(); + ].get({ query }); - if (res.status === 200) { - this.data = res.data?.data ?? []; + if (res.status === 200 && res.data?.success) { + artikelKesehatanState.findMany.data = + res.data.data ?? []; + artikelKesehatanState.findMany.totalPages = + res.data.totalPages ?? 1; } else { - toast.error("Gagal memuat data artikel kesehatan"); + artikelKesehatanState.findMany.data = []; + artikelKesehatanState.findMany.totalPages = 1; } - - return res; } catch (err) { - toast.error("Terjadi error saat load data"); - console.error("LOAD ERROR:", err); - throw err; + console.error("Gagal fetch artikel kesehatan paginated:", err); + artikelKesehatanState.findMany.data = []; + artikelKesehatanState.findMany.totalPages = 1; } finally { - this.loading = false; + artikelKesehatanState.findMany.loading = false; } }, }, @@ -280,12 +291,9 @@ const artikelKesehatanState = proxy({ async byId(id: string) { try { artikelKesehatanState.delete.loading = true; - const res = await fetch( - `/api/kesehatan/artikel-kesehatan/del/${id}`, - { - method: "DELETE", - } - ); + const res = await fetch(`/api/kesehatan/artikel-kesehatan/del/${id}`, { + method: "DELETE", + }); const result = await res.json(); if (res.ok && result.success) { diff --git a/src/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan.ts b/src/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan.ts index 3061e4e9..71c04389 100644 --- a/src/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan.ts +++ b/src/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan.ts @@ -116,27 +116,38 @@ const fasilitasKesehatan = proxy({ }; }>[] | null, + page: 1, + totalPages: 1, loading: false, - async load() { + search: "", + load: async (page = 1, limit = 10, search = "") => { + fasilitasKesehatanState.fasilitasKesehatan.findMany.loading = true; // ✅ Akses langsung via nama path + fasilitasKesehatanState.fasilitasKesehatan.findMany.page = page; + fasilitasKesehatanState.fasilitasKesehatan.findMany.search = search; + try { - this.loading = true; - const res = await (ApiFetch.api.kesehatan as any)[ - "fasilitas-kesehatan" - ]["find-many"].get(); + const query: any = { page, limit }; + if (search) query.search = search; - if (res.status === 200) { - this.data = res.data?.data ?? []; + const res = await ApiFetch.api.kesehatan["fasilitas-kesehatan"][ + "find-many" + ].get({ query }); + + if (res.status === 200 && res.data?.success) { + fasilitasKesehatanState.fasilitasKesehatan.findMany.data = + res.data.data ?? []; + fasilitasKesehatanState.fasilitasKesehatan.findMany.totalPages = + res.data.totalPages ?? 1; } else { - toast.error("Gagal memuat data fasilitas kesehatan"); + fasilitasKesehatanState.fasilitasKesehatan.findMany.data = []; + fasilitasKesehatanState.fasilitasKesehatan.findMany.totalPages = 1; } - - return res; } catch (err) { - toast.error("Terjadi error saat load data"); - console.error("LOAD ERROR:", err); - throw err; + console.error("Gagal fetch fasilitas kesehatan paginated:", err); + fasilitasKesehatanState.fasilitasKesehatan.findMany.data = []; + fasilitasKesehatanState.fasilitasKesehatan.findMany.totalPages = 1; } finally { - this.loading = false; + fasilitasKesehatanState.fasilitasKesehatan.findMany.loading = false; } }, }, @@ -558,7 +569,7 @@ const dokter = proxy({ const fasilitasKesehatanState = proxy({ fasilitasKesehatan, - dokter + dokter, }); export default fasilitasKesehatanState; diff --git a/src/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan.ts b/src/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan.ts index fd91dd89..90665395 100644 --- a/src/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan.ts +++ b/src/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan.ts @@ -120,27 +120,36 @@ const jadwalkegiatanState = proxy({ }; }>[] | null, + page: 1, + totalPages: 1, loading: false, - async load() { + search: "", + load: async (page = 1, limit = 10, search = "") => { + jadwalkegiatanState.findMany.loading = true; // ✅ Akses langsung via nama path + jadwalkegiatanState.findMany.page = page; + jadwalkegiatanState.findMany.search = search; + try { - this.loading = true; - const res = await (ApiFetch.api.kesehatan as any)[ - "jadwal-kegiatan" - ]["find-many"].get(); + const query: any = { page, limit }; + if (search) query.search = search; - if (res.status === 200) { - this.data = res.data?.data ?? []; + const res = await ApiFetch.api.kesehatan["jadwal-kegiatan"][ + "find-many" + ].get({ query }); + + if (res.status === 200 && res.data?.success) { + jadwalkegiatanState.findMany.data = res.data.data ?? []; + jadwalkegiatanState.findMany.totalPages = res.data.totalPages ?? 1; } else { - toast.error("Gagal memuat data jadwal kegiatan"); + jadwalkegiatanState.findMany.data = []; + jadwalkegiatanState.findMany.totalPages = 1; } - - return res; } catch (err) { - toast.error("Terjadi error saat load data"); - console.error("LOAD ERROR:", err); - throw err; + console.error("Gagal fetch jadwal kegiatan paginated:", err); + jadwalkegiatanState.findMany.data = []; + jadwalkegiatanState.findMany.totalPages = 1; } finally { - this.loading = false; + jadwalkegiatanState.findMany.loading = false; } }, }, @@ -227,29 +236,42 @@ const jadwalkegiatanState = proxy({ content: jadwalkegiatanState.edit.form.content, informasiJadwalKegiatan: { name: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.name, - tanggal: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.tanggal, + tanggal: + jadwalkegiatanState.edit.form.informasiJadwalKegiatan.tanggal, waktu: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.waktu, - lokasi: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.lokasi, + lokasi: + jadwalkegiatanState.edit.form.informasiJadwalKegiatan.lokasi, }, layananJadwalKegiatan: { - content: jadwalkegiatanState.edit.form.layananJadwalKegiatan.content, + content: + jadwalkegiatanState.edit.form.layananJadwalKegiatan.content, }, deskripsiJadwalKegiatan: { - deskripsi: jadwalkegiatanState.edit.form.deskripsiJadwalKegiatan.deskripsi, + deskripsi: + jadwalkegiatanState.edit.form.deskripsiJadwalKegiatan.deskripsi, }, syaratKetentuanJadwalKegiatan: { - content: jadwalkegiatanState.edit.form.syaratKetentuanJadwalKegiatan.content, + content: + jadwalkegiatanState.edit.form.syaratKetentuanJadwalKegiatan + .content, }, dokumenJadwalKegiatan: { - content: jadwalkegiatanState.edit.form.dokumenJadwalKegiatan.content, + content: + jadwalkegiatanState.edit.form.dokumenJadwalKegiatan.content, }, pendaftaranJadwalKegiatan: { name: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.name, - tanggal: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.tanggal, - namaOrangtua: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.namaOrangtua, - nomor: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.nomor, - alamat: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.alamat, - catatan: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.catatan, + tanggal: + jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.tanggal, + namaOrangtua: + jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan + .namaOrangtua, + nomor: + jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.nomor, + alamat: + jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.alamat, + catatan: + jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.catatan, }, }; @@ -286,7 +308,7 @@ const jadwalkegiatanState = proxy({ }, delete: { loading: false, - async byId(id: string){ + async byId(id: string) { try { jadwalkegiatanState.delete.loading = true; const res = await fetch(`/api/kesehatan/jadwal-kegiatan/del/${id}`, { @@ -305,7 +327,7 @@ const jadwalkegiatanState = proxy({ } finally { jadwalkegiatanState.delete.loading = false; } - } + }, }, }); diff --git a/src/app/admin/(dashboard)/_state/user/user-state.ts b/src/app/admin/(dashboard)/_state/user/user-state.ts index b588088a..93594956 100644 --- a/src/app/admin/(dashboard)/_state/user/user-state.ts +++ b/src/app/admin/(dashboard)/_state/user/user-state.ts @@ -90,6 +90,32 @@ const userState = proxy({ } }, }, + updateActive: { + loading: false, + async submit(id: string, isActive: boolean) { + this.loading = true; + try { + const res = await fetch(`/api/user/updt`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ id, isActive }), + }); + + const data = await res.json(); + if (res.status === 200 && data.success) { + toast.success(data.message); + userState.findMany.load(userState.findMany.page, 10, userState.findMany.search); + } else { + toast.error(data.message || "Gagal update status user"); + } + } catch (e) { + console.error(e); + toast.error("Gagal update status user"); + } finally { + this.loading = false; + } + }, + }, }); const templateRole = z.object({ diff --git a/src/app/admin/(dashboard)/auth/login-admin/page.tsx b/src/app/admin/(dashboard)/auth/login-admin/page.tsx new file mode 100644 index 00000000..db8133c9 --- /dev/null +++ b/src/app/admin/(dashboard)/auth/login-admin/page.tsx @@ -0,0 +1,111 @@ +'use client' +import { apiFetchLogin } from '@/app/admin/auth/_lib/api_fetch_auth'; +import colors from '@/con/colors'; +import { Box, Button, Center, Flex, Image, Paper, Stack, Text, Title } from '@mantine/core'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { PhoneInput } from "react-international-phone"; +import "react-international-phone/style.css"; +import { toast } from 'react-toastify'; + + + +function Login() { + const router = useRouter() + const [phone, setPhone] = useState("") + const [isError, setError] = useState(false) + const [loading, setLoading] = useState(false) + + async function onLogin() { + const nomor = phone.substring(1); + if (nomor.length <= 4) return setError(true) + + + try { + setLoading(true); + const response = await apiFetchLogin({ nomor: nomor }) + if (response && response.success) { + localStorage.setItem("hipmi_auth_code_id", response.kodeId); + toast.success(response.message); + router.push("/validasi", { scroll: false }); + } else { + setLoading(false); + toast.error(response?.message); + } + } catch (error) { + setLoading(false) + console.log("Error Login", error) + toast.error("Terjadi kesalahan saat login") + } + } + + return ( + + + + + + + + Login + +
+ +
+
+ + {/* + Masuk Untuk Akses Admin + setUsername(e.target.value)} + required + /> + */} + { + setPhone(val); + }} + /> + + {isError ? ( + toast.error("Masukan nomor telepon anda") + ) : ( + "" + )} + + + + + Belum punya akun? + + + +
+
+
+
+
+ ); +} + +export default Login; diff --git a/src/app/admin/(dashboard)/auth/registrasi-admin/page.tsx b/src/app/admin/(dashboard)/auth/registrasi-admin/page.tsx new file mode 100644 index 00000000..89790f9f --- /dev/null +++ b/src/app/admin/(dashboard)/auth/registrasi-admin/page.tsx @@ -0,0 +1,121 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ +'use client' +import { apiFetchRegister } from '@/app/admin/auth/_lib/api_fetch_auth'; +import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto'; +import colors from '@/con/colors'; +import { Box, Button, Center, Checkbox, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { PhoneInput } from "react-international-phone"; +import "react-international-phone/style.css"; +import { toast } from 'react-toastify'; + +function Registrasi() { + const [phone, setPhone] = useState("") + const router = useRouter() + const [value, setValue] = useState("") + const [isValue, setIsValue] = useState(false); + const [loading, setLoading] = useState(false); + + async function onRegistarsi() { + if (value.length < 5) { + toast.error("Username minimal 5 karakter!"); + return; + } + + if (value.includes(" ")) { + toast.error("Username tidak boleh ada spasi!"); + return; + } + + if (!phone) { + toast.error("Nomor telepon wajib diisi!"); + return; + } + + try { + setLoading(true); + const respone = await apiFetchRegister({ nomor: phone, username: value }); + + if (respone.success) { + router.push("/login", { scroll: false }); + toast.success(respone.message); + + } else { + setLoading(false); + toast.error(respone.message); + } + } catch (error) { + setLoading(false); + console.log("Error Registrasi", error); + } + } + return ( + + + + + + + + + + Registrasi + +
+ +
+ + 0 && value.length < 5 + ? "Minimal 5 karakter !" + : value.includes(" ") + ? "Tidak boleh ada spasi" + : isValue + ? "Masukan username anda" + : "" + } + onChange={(val) => { + val.currentTarget.value.length > 0 ? setIsValue(false) : ""; + setValue(val.currentTarget.value); + }} + required + + /> + + Nomor Telepon + { + setPhone(val); + }} + /> + + + + + + + + +
+
+
+
+
+ ); +} + +export default Registrasi; diff --git a/src/app/admin/(dashboard)/auth/validasi-admin/page.tsx b/src/app/admin/(dashboard)/auth/validasi-admin/page.tsx new file mode 100644 index 00000000..862edb33 --- /dev/null +++ b/src/app/admin/(dashboard)/auth/validasi-admin/page.tsx @@ -0,0 +1,38 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Paper, PinInput, Stack, Text, Title } from '@mantine/core'; +import { useRouter } from 'next/navigation'; + +function Validasi() { + const router = useRouter() + return ( + + + + + + + + Kode Verifikasi + + + + + Masukkan Kode Verifikasi + + + + + + + + + + + + ); +} + +export default Validasi; diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/_lib/layoutTabs.tsx index abacee4d..a55ffb0c 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/_lib/layoutTabs.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/_lib/layoutTabs.tsx @@ -1,77 +1,140 @@ /* eslint-disable react-hooks/exhaustive-deps */ 'use client' import colors from '@/con/colors'; -import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core'; +import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core'; +import { IconActivity, IconBuildingHospital, IconCalendarEvent, IconGauge, IconNotes } from '@tabler/icons-react'; import { usePathname, useRouter } from 'next/navigation'; import React, { useEffect, useState } from 'react'; + function LayoutTabs({ children }: { children: React.ReactNode }) { - const router = useRouter() - const pathname = usePathname() - const tabs = [ - { - label: "Presentase Kelahiran & Kematian", - value: "presentasekelahiran&kematian", - href: "/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian" - }, - { - label: "Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik", - value: "grafikhasilkepuasan", - href: "/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan" - }, - { - label: "Fasilitas Kesehatan", - value: "fasilitaskesehatan", - href: "/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan" - }, - { - label: "Jadwal Kegiatan", - value: "jadwalkegiatan", - href: "/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan" - }, - { - label: "Artikel Kesehatan", - value: "artikelkesehatan", - href: "/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan" - }, - ]; - const curentTab = tabs.find(tab => tab.href === pathname) - const [activeTab, setActiveTab] = useState(curentTab?.value || tabs[0].value); + const router = useRouter(); + const pathname = usePathname(); - const handleTabChange = (value: string | null) => { - const tab = tabs.find(t => t.value === value) - if (tab) { - router.push(tab.href) - } - setActiveTab(value) - } - useEffect(() => { - const match = tabs.find(tab => tab.href === pathname) - if (match) { - setActiveTab(match.value) - } - }, [pathname]) + const tabs = [ + { + label: "Presentase Kelahiran & Kematian", + value: "presentasekelahiran&kematian", + href: "/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian", + icon: , + tooltip: "Lihat data kelahiran dan kematian" + }, + { + label: "Grafik Hasil Kepuasan Masyarakat", + value: "grafikhasilkepuasan", + href: "/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan", + icon: , + tooltip: "Grafik kepuasan masyarakat terhadap pelayanan" + }, + { + label: "Fasilitas Kesehatan", + value: "fasilitaskesehatan", + href: "/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan", + icon: , + tooltip: "Data fasilitas kesehatan desa" + }, + { + label: "Jadwal Kegiatan", + value: "jadwalkegiatan", + href: "/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan", + icon: , + tooltip: "Atur jadwal kegiatan kesehatan" + }, + { + label: "Artikel Kesehatan", + value: "artikelkesehatan", + href: "/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan", + icon: , + tooltip: "Artikel & informasi seputar kesehatan" + }, + ]; - return ( - - Data Kesehatan Warga - - - {tabs.map((e, i) => ( - {e.label} - ))} - - {tabs.map((e, i) => ( - - {/* Konten dummy, bisa diganti tergantung routing */} - <> - - ))} - - {children} - - ); + + const currentTab = tabs.find(tab => tab.href === pathname); + const [activeTab, setActiveTab] = useState(currentTab?.value || tabs[0].value); + + + const handleTabChange = (value: string | null) => { + const tab = tabs.find(t => t.value === value); + if (tab) { + router.push(tab.href); + } + setActiveTab(value); + }; + + + useEffect(() => { + const match = tabs.find(tab => tab.href === pathname); + if (match) { + setActiveTab(match.value); + } + }, [pathname]); + + + return ( + + + Data Kesehatan Warga + + + + {tabs.map((tab, i) => ( + + + {tab.label} + + + ))} + + + + {tabs.map((tab, i) => ( + + {children} + + ))} + + + ); } + export default LayoutTabs; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/edit/page.tsx index a3be391c..649b36d6 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/edit/page.tsx @@ -4,7 +4,17 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; @@ -14,29 +24,12 @@ import { useProxy } from 'valtio/utils'; interface ArtikelKesehatanFormBase { title: string; content: string; - introduction: { - content: string; - }; - symptom: { - title: string; - content: string; - }; - prevention: { - title: string; - content: string; - }; - firstAid: { - title: string; - content: string; - }; - mythVsFact: { - title: string; - mitos: string; - fakta: string; - }; - doctorSign: { - content: string; - }; + introduction: { content: string }; + symptom: { title: string; content: string }; + prevention: { title: string; content: string }; + firstAid: { title: string; content: string }; + mythVsFact: { title: string; mitos: string; fakta: string }; + doctorSign: { content: string }; } function EditArtikelKesehatan() { @@ -47,29 +40,27 @@ function EditArtikelKesehatan() { const [formData, setFormData] = useState({ title: stateArtikelKesehatan.edit.form.title || '', content: stateArtikelKesehatan.edit.form.content || '', - introduction: { - content: stateArtikelKesehatan.edit.form.introduction?.content || '', - }, + introduction: { content: stateArtikelKesehatan.edit.form.introduction?.content || '' }, symptom: { title: stateArtikelKesehatan.edit.form.symptom?.title || '', - content: stateArtikelKesehatan.edit.form.symptom?.content || '', + content: stateArtikelKesehatan.edit.form.symptom?.content || '' }, prevention: { title: stateArtikelKesehatan.edit.form.prevention?.title || '', - content: stateArtikelKesehatan.edit.form.prevention?.content || '', + content: stateArtikelKesehatan.edit.form.prevention?.content || '' }, firstAid: { title: stateArtikelKesehatan.edit.form.firstAid?.title || '', - content: stateArtikelKesehatan.edit.form.firstAid?.content || '', + content: stateArtikelKesehatan.edit.form.firstAid?.content || '' }, mythVsFact: { title: stateArtikelKesehatan.edit.form.mythVsFact?.title || '', mitos: stateArtikelKesehatan.edit.form.mythVsFact?.mitos || '', - fakta: stateArtikelKesehatan.edit.form.mythVsFact?.fakta || '', + fakta: stateArtikelKesehatan.edit.form.mythVsFact?.fakta || '' }, doctorSign: { - content: stateArtikelKesehatan.edit.form.doctorSign?.content || '', - }, + content: stateArtikelKesehatan.edit.form.doctorSign?.content || '' + } }); useEffect(() => { @@ -84,29 +75,27 @@ function EditArtikelKesehatan() { setFormData({ title: form.title, content: form.content, - introduction: { - content: form.introduction?.content || '', - }, + introduction: { content: form.introduction?.content || '' }, symptom: { title: form.symptom?.title || '', - content: form.symptom?.content || '', + content: form.symptom?.content || '' }, prevention: { title: form.prevention?.title || '', - content: form.prevention?.content || '', + content: form.prevention?.content || '' }, firstAid: { title: form.firstAid?.title || '', - content: form.firstAid?.content || '', + content: form.firstAid?.content || '' }, mythVsFact: { title: form.mythVsFact?.title || '', mitos: form.mythVsFact?.mitos || '', - fakta: form.mythVsFact?.fakta || '', + fakta: form.mythVsFact?.fakta || '' }, doctorSign: { - content: form.doctorSign?.content || '', - }, + content: form.doctorSign?.content || '' + } }); } } catch (error) { @@ -119,34 +108,7 @@ function EditArtikelKesehatan() { const handleSubmit = async () => { try { - stateArtikelKesehatan.edit.form = { - ...stateArtikelKesehatan.edit.form, - title: formData.title, - content: formData.content, - introduction: { - content: formData.introduction.content, - }, - symptom: { - title: formData.symptom.title, - content: formData.symptom.content, - }, - prevention: { - title: formData.prevention.title, - content: formData.prevention.content, - }, - firstAid: { - title: formData.firstAid.title, - content: formData.firstAid.content, - }, - mythVsFact: { - title: formData.mythVsFact.title, - mitos: formData.mythVsFact.mitos, - fakta: formData.mythVsFact.fakta, - }, - doctorSign: { - content: formData.doctorSign.content, - }, - }; + stateArtikelKesehatan.edit.form = { ...formData }; const success = await stateArtikelKesehatan.edit.submit(); if (success) { toast.success("Artikel kesehatan berhasil diperbarui!"); @@ -157,214 +119,196 @@ function EditArtikelKesehatan() { toast.error(error instanceof Error ? error.message : "Gagal memperbarui data artikel kesehatan"); } }; + return ( - - - - - - - Edit Artikel Kesehatan + + {/* Header */} + + + + + + Edit Artikel Kesehatan + + + + {/* Form */} + + Judul} - placeholder="masukkan judul" + label="Judul" + placeholder="Masukkan judul artikel" value={formData.title} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - title: e.target.value - })); - }} + onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))} + required /> Deskripsi} - placeholder="masukkan deskripsi" + label="Deskripsi" + placeholder="Masukkan deskripsi artikel" value={formData.content} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - content: e.target.value - })); - }} + onChange={(e) => setFormData(prev => ({ ...prev, content: e.target.value }))} + required /> Pendahuluan} - placeholder="masukkan pendahuluan" + label="Pendahuluan" + placeholder="Masukkan pendahuluan" value={formData.introduction.content} - onChange={(e) => { + onChange={(e) => setFormData(prev => ({ ...prev, - introduction: { - ...prev.introduction, - content: e.target.value - } - })); - }} + introduction: { ...prev.introduction, content: e.target.value } + })) + } /> + + {/* Gejala */} - Gejala + Gejala Judul} - placeholder="masukkan judul gejala penyakit" + label="Judul Gejala" + placeholder="Masukkan judul gejala" value={formData.symptom.title} - onChange={(e) => { + onChange={(e) => setFormData(prev => ({ ...prev, - symptom: { - ...prev.symptom, - title: e.target.value - } - })); - }} + symptom: { ...prev.symptom, title: e.target.value } + })) + } + /> + + setFormData(prev => ({ + ...prev, + symptom: { ...prev.symptom, content: e } + })) + } /> - - Deskripsi Gejala - { - setFormData(prev => ({ - ...prev, - symptom: { - ...prev.symptom, - content: e - } - })); - }} - /> - + {/* Pencegahan */} - Pencegahan + Pencegahan Judul} - placeholder="masukkan judul" + label="Judul" value={formData.prevention.title} - onChange={(e) => { + onChange={(e) => setFormData(prev => ({ ...prev, - prevention: { - ...prev.prevention, - title: e.target.value - } - })); - }} + prevention: { ...prev.prevention, title: e.target.value } + })) + } /> { + onChange={(e) => setFormData(prev => ({ ...prev, - prevention: { - ...prev.prevention, - content: e - } - })); - }} + prevention: { ...prev.prevention, content: e } + })) + } /> + + {/* Pertolongan Pertama */} - Pertolongan Pertama + Pertolongan Pertama Judul} - placeholder="masukkan judul" + label="Judul" value={formData.firstAid.title} - onChange={(e) => { + onChange={(e) => setFormData(prev => ({ ...prev, - firstAid: { - ...prev.firstAid, - title: e.target.value - } - })); - }} + firstAid: { ...prev.firstAid, title: e.target.value } + })) + } /> { + onChange={(e) => setFormData(prev => ({ ...prev, - firstAid: { - ...prev.firstAid, - content: e - } - })); - }} + firstAid: { ...prev.firstAid, content: e } + })) + } /> + + {/* Mitos vs Fakta */} - Mitos dan Fakta + Mitos vs Fakta Judul} - placeholder="masukkan judul" + label="Judul" value={formData.mythVsFact.title} - onChange={(e) => { + onChange={(e) => setFormData(prev => ({ ...prev, - mythVsFact: { - ...prev.mythVsFact, - title: e.target.value - } - })); - }} + mythVsFact: { ...prev.mythVsFact, title: e.target.value } + })) + } + /> + Mitos + + setFormData(prev => ({ + ...prev, + mythVsFact: { ...prev.mythVsFact, mitos: e } + })) + } + /> + Fakta + + setFormData(prev => ({ + ...prev, + mythVsFact: { ...prev.mythVsFact, fakta: e } + })) + } /> - - - Mitos - - { - setFormData(prev => ({ - ...prev, - mythVsFact: { - ...prev.mythVsFact, - mitos: e - } - })); - }} - /> - - - - Fakta - - { - setFormData(prev => ({ - ...prev, - mythVsFact: { - ...prev.mythVsFact, - fakta: e - } - })); - }} - /> - + + {/* Kapan harus ke dokter */} - Kapan Harus Ke Dokter + Kapan Harus Ke Dokter { + onChange={(e) => setFormData(prev => ({ ...prev, - doctorSign: { - ...prev.doctorSign, - content: e - } - })); - }} + doctorSign: { ...prev.doctorSign, content: e } + })) + } /> - + + {/* Save button */} + + + diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/page.tsx index d5202321..6edb09d0 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/[id]/page.tsx @@ -2,134 +2,181 @@ import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan'; import colors from '@/con/colors'; -import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Skeleton, + Stack, + Text, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; function DetailArtikelKesehatan() { - const params = useParams() + const params = useParams(); const router = useRouter(); - const stateArtikelKesehatan = useProxy(artikelKesehatanState) + const state = useProxy(artikelKesehatanState); const [modalHapus, setModalHapus] = useState(false); - const [selectedId, setSelectedId] = useState(null) + const [selectedId, setSelectedId] = useState(null); useShallowEffect(() => { - stateArtikelKesehatan.findUnique.load(params?.id as string) - }, []) + state.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - stateArtikelKesehatan.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan") + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push('/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan'); } - } + }; - if (!stateArtikelKesehatan.findUnique.data) { + if (!state.findUnique.data) { return ( - + - ) + ); } + const data = state.findUnique.data; return ( - - - - - - - Detail Artikel Kesehatan - {stateArtikelKesehatan.findUnique.data ? ( - - - - Judul - {stateArtikelKesehatan.findUnique.data.title} - - - Deskripsi - - - - Pendahuluan - - - - - Gejala - Judul Gejala - {stateArtikelKesehatan.findUnique.data.symptom.title} - Deskripsi Gejala - - - - - - Pencegahan - Judul Pencegahan - {stateArtikelKesehatan.findUnique.data.prevention.title} - Deskripsi Pencegahan - - - - - - Pertolongan Pertama - Judul Pertolongan Pertama - {stateArtikelKesehatan.findUnique.data.firstaid.title} - Deskripsi Pertolongan Pertama - - - - - - Mitos dan Fakta - Judul Mitos dan Fakta - {stateArtikelKesehatan.findUnique.data.mythvsfact.title} - Deskripsi Mitos - - Deskripsi Fakta - - - - - - Kapan Harus ke Dokter - Deskripsi Kapan Harus ke Dokter - - - - - - - - - - - - ) : null} + + {/* Tombol Back */} + + + {/* Wrapper Detail */} + + + + Detail Artikel Kesehatan + + + + + {/* Judul */} + + Judul + {data.title} + + + {/* Deskripsi */} + + Deskripsi + + + + {/* Pendahuluan */} + + Pendahuluan + + + + {/* Gejala */} + + Gejala + Judul + {data.symptom?.title} + Deskripsi + + + + {/* Pencegahan */} + + Pencegahan + Judul + {data.prevention?.title} + Deskripsi + + + + {/* Pertolongan Pertama */} + + Pertolongan Pertama + Judul + {data.firstaid?.title} + Deskripsi + + + + {/* Mitos vs Fakta */} + + Mitos dan Fakta + Judul + {data.mythvsfact?.title} + Mitos + + Fakta + + + + {/* Kapan ke Dokter */} + + Kapan Harus ke Dokter + + + + {/* Aksi */} + + + + + + + + + + + - {/* Modal Hapus */} + {/* Modal Konfirmasi Hapus */} setModalHapus(false)} diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/create/page.tsx index 3aeab04f..5738c908 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/create/page.tsx @@ -2,101 +2,129 @@ import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor'; import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; - function CreateArtikelKesehatan() { - const stateArtikelKesehatan = useProxy(artikelKesehatanState) + const stateArtikelKesehatan = useProxy(artikelKesehatanState); const router = useRouter(); - const resetForm = () => { stateArtikelKesehatan.create.form = { - title: "", - content: "", + title: '', + content: '', introduction: { - content: "", + content: '', }, symptom: { - title: "", - content: "", + title: '', + content: '', }, prevention: { - title: "", - content: "", + title: '', + content: '', }, firstAid: { - title: "", - content: "", + title: '', + content: '', }, mythVsFact: { - title: "", - mitos: "", - fakta: "", + title: '', + mitos: '', + fakta: '', }, doctorSign: { - content: "" - } + content: '', + }, }; }; - - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); + const handleSubmit = async (e?: React.FormEvent) => { + e?.preventDefault(); await stateArtikelKesehatan.create.submit(); - - toast.success("Data berhasil disimpan"); + toast.success('Data berhasil disimpan'); resetForm(); - // After successful submission, redirect to the list page router.push('/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan'); - } + }; return ( - - - - + + {/* Header */} + + + + + + Tambah Artikel Kesehatan + + - - - Create Artikel Kesehatan + {/* Form */} + + Judul} - placeholder="masukkan judul" + label={"Judul"} + placeholder="Masukkan judul" value={stateArtikelKesehatan.create.form.title} onChange={(e) => { stateArtikelKesehatan.create.form.title = e.target.value; }} + required /> Deskripsi} - placeholder="masukkan deskripsi" + label={"Deskripsi"} + placeholder="Masukkan deskripsi" value={stateArtikelKesehatan.create.form.content} onChange={(e) => { stateArtikelKesehatan.create.form.content = e.target.value; }} + required /> Pendahuluan} - placeholder="masukkan pendahuluan" + label={"Pendahuluan"} + placeholder="Masukkan pendahuluan" + required value={stateArtikelKesehatan.create.form.introduction.content} onChange={(e) => { stateArtikelKesehatan.create.form.introduction.content = e.target.value; }} /> + + {/* Gejala */} - Gejala - + Gejala + Judul} - placeholder="masukkan judul gejala penyakit" + label={"Judul Gejala"} + required + placeholder="Masukkan judul gejala penyakit" value={stateArtikelKesehatan.create.form.symptom.title} onChange={(e) => { stateArtikelKesehatan.create.form.symptom.title = e.target.value; @@ -114,54 +142,62 @@ function CreateArtikelKesehatan() { - + {/* Pencegahan */} + Pencegahan Judul} - placeholder="masukkan judul" + label={"Judul Pencegahan"} + required + placeholder="Masukkan judul" value={stateArtikelKesehatan.create.form.prevention.title} onChange={(e) => { stateArtikelKesehatan.create.form.prevention.title = e.target.value; }} /> + Deskripsi Pencegahan { stateArtikelKesehatan.create.form.prevention.content = e; }} /> - - + + + {/* Pertolongan Pertama */} + Pertolongan Pertama Judul} - placeholder="masukkan judul" + label={"Judul Pertolongan Pertama"} + required + placeholder="Masukkan judul" value={stateArtikelKesehatan.create.form.firstAid.title} onChange={(e) => { stateArtikelKesehatan.create.form.firstAid.title = e.target.value; }} /> + Deskripsi Pertolongan Pertama { stateArtikelKesehatan.create.form.firstAid.content = e; }} /> - + + + {/* Mitos vs Fakta */} Mitos dan Fakta Judul} - placeholder="masukkan judul" + label={"Judul Mitos dan Fakta"} + required + placeholder="Masukkan judul" value={stateArtikelKesehatan.create.form.mythVsFact.title} onChange={(e) => { stateArtikelKesehatan.create.form.mythVsFact.title = e.target.value; }} /> - - - Mitos - + + Mitos { @@ -169,10 +205,8 @@ function CreateArtikelKesehatan() { }} /> - - - Fakta - + + Fakta { @@ -181,8 +215,10 @@ function CreateArtikelKesehatan() { /> + + {/* Kapan Harus ke Dokter */} - Kapan Harus Ke Dokter + Kapan Harus ke Dokter { @@ -191,9 +227,21 @@ function CreateArtikelKesehatan() { /> - + {/* Submit Button */} + + + diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/page.tsx index f4f334b1..8eb96aa1 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/artikel_kesehatan/page.tsx @@ -1,99 +1,164 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; +import { + Box, + Button, + Center, + Group, + Paper, + Pagination, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { IconArrowBack, IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; import artikelKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/artikelKesehatan'; import { useState } from 'react'; - function ArtikelKesehatan() { + const router = useRouter(); const [search, setSearch] = useState(""); + return ( + {/* Tombol Back */} + + + + + {/* Header Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> + ); } function ListArtikelKesehatan({ search }: { search: string }) { - const stateArtikelKesehatan = useProxy(artikelKesehatanState) + const stateArtikel = useProxy(artikelKesehatanState); const router = useRouter(); + const { data, page, totalPages, loading, load } = stateArtikel.findMany; + useShallowEffect(() => { - stateArtikelKesehatan.findMany.load() - }, []) + load(page, 10, search); + }, [page, search]); - const filteredData = (stateArtikelKesehatan.findMany.data || []).filter(item => { - const keyword = search.toLowerCase(); + const filteredData = data || []; + + if (loading || !data) { return ( - item.title.toLowerCase().includes(keyword) || - item.content.toLowerCase().includes(keyword) + + + ); - }); - - if (!stateArtikelKesehatan.findMany.data) { - return ( - - - - ) } + return ( - - - - - - - - Judul - Content - Detail - - - - {filteredData.map((item) => ( + + {/* Judul + Tombol Tambah */} + + Daftar Artikel Kesehatan + + + + + + {/* Tabel */} + +
+ + + Judul + Konten + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( - - {item.title} - + + {item.title} + - - {item.content} - + + {item.content} + - - ))} - -
-
-
+ )) + ) : ( + + +
+ Tidak ada artikel yang cocok +
+
+
+ )} + + +
+ + {/* Pagination */} +
+ { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
- ) + ); } export default ArtikelKesehatan; diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/edit/page.tsx index 37f37421..3cd455ba 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/edit/page.tsx @@ -4,7 +4,17 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; @@ -18,20 +28,14 @@ interface FasilitasKesehatanFormBase { alamat: string; jamOperasional: string; }; - layananUnggulan: { - content: string; - }; + layananUnggulan: { content: string }; dokterdanTenagaMedis: { name: string; specialist: string; jadwal: string; }; - fasilitasPendukung: { - content: string; - }; - prosedurPendaftaran: { - content: string; - }; + fasilitasPendukung: { content: string }; + prosedurPendaftaran: { content: string }; tarifDanLayanan: { layanan: string; tarif: string; @@ -86,20 +90,14 @@ function EditFasilitasKesehatan() { alamat: form.informasiUmum?.alamat || '', jamOperasional: form.informasiUmum?.jamOperasional || '', }, - layananUnggulan: { - content: form.layananUnggulan?.content || '', - }, + layananUnggulan: { content: form.layananUnggulan?.content || '' }, dokterdanTenagaMedis: { name: form.dokterdanTenagaMedis?.name || '', specialist: form.dokterdanTenagaMedis?.specialist || '', jadwal: form.dokterdanTenagaMedis?.jadwal || '', }, - fasilitasPendukung: { - content: form.fasilitasPendukung?.content || '', - }, - prosedurPendaftaran: { - content: form.prosedurPendaftaran?.content || '', - }, + fasilitasPendukung: { content: form.fasilitasPendukung?.content || '' }, + prosedurPendaftaran: { content: form.prosedurPendaftaran?.content || '' }, tarifDanLayanan: { layanan: form.tarifDanLayanan?.layanan || '', tarif: form.tarifDanLayanan?.tarif || '', @@ -118,30 +116,7 @@ function EditFasilitasKesehatan() { try { stateFasilitasKesehatan.edit.form = { ...stateFasilitasKesehatan.edit.form, - name: formData.name, - informasiUmum: { - fasilitas: formData.informasiUmum.fasilitas, - alamat: formData.informasiUmum.alamat, - jamOperasional: formData.informasiUmum.jamOperasional, - }, - layananUnggulan: { - content: formData.layananUnggulan.content, - }, - dokterdanTenagaMedis: { - name: formData.dokterdanTenagaMedis.name, - specialist: formData.dokterdanTenagaMedis.specialist, - jadwal: formData.dokterdanTenagaMedis.jadwal, - }, - fasilitasPendukung: { - content: formData.fasilitasPendukung.content, - }, - prosedurPendaftaran: { - content: formData.prosedurPendaftaran.content, - }, - tarifDanLayanan: { - layanan: formData.tarifDanLayanan.layanan, - tarif: formData.tarifDanLayanan.tarif, - }, + ...formData, }; const success = await stateFasilitasKesehatan.edit.submit(); if (success) { @@ -150,208 +125,197 @@ function EditFasilitasKesehatan() { } } catch (error) { console.error("Error updating fasilitas kesehatan:", error); - toast.error(error instanceof Error ? error.message : "Gagal memperbarui data fasilitas kesehatan"); + toast.error("Terjadi kesalahan saat memperbarui data fasilitas kesehatan"); } }; + return ( - - - - - - - - Edit Fasilitas Kesehatan + + {/* Header */} + + + + + + Edit Fasilitas Kesehatan + + + + {/* Form */} + + + setFormData(prev => ({ ...prev, name: e.target.value }))} + required + /> + + {/* Informasi Umum */} + + Informasi Umum Nama Fasilitas Kesehatan} - placeholder="masukkan nama fasilitas kesehatan" - value={formData.name} - onChange={(e) => { + label="Fasilitas" + value={formData.informasiUmum.fasilitas} + onChange={(e) => setFormData(prev => ({ ...prev, - name: e.target.value - })); - }} + informasiUmum: { ...prev.informasiUmum, fasilitas: e.target.value }, + })) + } /> - - Informasi Umum - Fasilitas} - placeholder="masukkan fasilitas" - value={formData.informasiUmum.fasilitas} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - informasiUmum: { - ...prev.informasiUmum, - fasilitas: e.target.value - } - })); - }} - /> - Alamat} - placeholder="masukkan alamat" - value={formData.informasiUmum.alamat} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - informasiUmum: { - ...prev.informasiUmum, - alamat: e.target.value - } - })); - }} - /> - Jam Operasional} - placeholder="masukkan jam operasional" - value={formData.informasiUmum.jamOperasional} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - informasiUmum: { - ...prev.informasiUmum, - jamOperasional: e.target.value - } - })); - }} - /> - - - Layanan Unggulan - { - setFormData(prev => ({ - ...prev, - layananUnggulan: { - ...prev.layananUnggulan, - content: e - } - })); - }} - /> - - - Dokter dan Tenaga Medis - Nama Dokter} - placeholder="masukkan nama dokter" - value={formData.dokterdanTenagaMedis.name} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - dokterdanTenagaMedis: { - ...prev.dokterdanTenagaMedis, - name: e.target.value - } - })); - }} - /> - Specialist} - placeholder="masukkan specialist" - value={formData.dokterdanTenagaMedis.specialist} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - dokterdanTenagaMedis: { - ...prev.dokterdanTenagaMedis, - specialist: e.target.value - } - })); - }} - /> - Jadwal} - placeholder="masukkan jadwal" - value={formData.dokterdanTenagaMedis.jadwal} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - dokterdanTenagaMedis: { - ...prev.dokterdanTenagaMedis, - jadwal: e.target.value - } - })); - }} - /> - - - Fasilitas Pendukung - { - setFormData(prev => ({ - ...prev, - fasilitasPendukung: { - ...prev.fasilitasPendukung, - content: e - } - })); - }} - /> - - - Prosedur Pendaftaran - { - setFormData(prev => ({ - ...prev, - prosedurPendaftaran: { - ...prev.prosedurPendaftaran, - content: e - } - })); - }} - /> - - - Tarif dan Layanan - Tarif} - placeholder="masukkan tarif" - value={formData.tarifDanLayanan.tarif} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - tarifDanLayanan: { - ...prev.tarifDanLayanan, - tarif: e.target.value - } - })); - }} - /> - Layanan} - placeholder="masukkan layanan" - value={formData.tarifDanLayanan.layanan} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - tarifDanLayanan: { - ...prev.tarifDanLayanan, - layanan: e.target.value - } - })); - }} - /> - + + setFormData(prev => ({ + ...prev, + informasiUmum: { ...prev.informasiUmum, alamat: e.target.value }, + })) + } + /> + + setFormData(prev => ({ + ...prev, + informasiUmum: { ...prev.informasiUmum, jamOperasional: e.target.value }, + })) + } + /> + - - - - + {/* Layanan Unggulan */} + + Layanan Unggulan + + setFormData(prev => ({ + ...prev, + layananUnggulan: { content: e }, + })) + } + /> + + + {/* Dokter dan Tenaga Medis */} + + Dokter dan Tenaga Medis + + setFormData(prev => ({ + ...prev, + dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, name: e.target.value }, + })) + } + /> + + setFormData(prev => ({ + ...prev, + dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, specialist: e.target.value }, + })) + } + /> + + setFormData(prev => ({ + ...prev, + dokterdanTenagaMedis: { ...prev.dokterdanTenagaMedis, jadwal: e.target.value }, + })) + } + /> + + + {/* Fasilitas Pendukung */} + + Fasilitas Pendukung + + setFormData(prev => ({ + ...prev, + fasilitasPendukung: { content: e }, + })) + } + /> + + + {/* Prosedur Pendaftaran */} + + Prosedur Pendaftaran + + setFormData(prev => ({ + ...prev, + prosedurPendaftaran: { content: e }, + })) + } + /> + + + {/* Tarif dan Layanan */} + + Tarif dan Layanan + + setFormData(prev => ({ + ...prev, + tarifDanLayanan: { ...prev.tarifDanLayanan, tarif: e.target.value }, + })) + } + /> + + setFormData(prev => ({ + ...prev, + tarifDanLayanan: { ...prev.tarifDanLayanan, layanan: e.target.value }, + })) + } + /> + + + {/* Tombol Simpan */} + + + + + ); } diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/page.tsx index 5fa25b75..190eb675 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/[id]/page.tsx @@ -2,133 +2,169 @@ import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan'; import colors from '@/con/colors'; -import { Box, Button, Flex, Grid, GridCol, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Skeleton, + Stack, + Text, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; function DetailFasilitasKesehatan() { - const params = useParams() + const params = useParams(); const router = useRouter(); - const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan) + const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan); const [modalHapus, setModalHapus] = useState(false); - const [selectedId, setSelectedId] = useState(null) + const [selectedId, setSelectedId] = useState(null); useShallowEffect(() => { - stateFasilitasKesehatan.findUnique.load(params?.id as string) - }, []) + state.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - stateFasilitasKesehatan.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan") + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push( + '/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan' + ); } - } + }; - if (!stateFasilitasKesehatan.findUnique.data) { + if (!state.findUnique.data) { return ( - + - ) + ); } + const data = state.findUnique.data; return ( - - - - - - - - - Detail Fasilitas Kesehatan - - {/* - - - - - */} - - {stateFasilitasKesehatan.findUnique.data ? ( - - - - Nama Fasilitas Kesehatan - {stateFasilitasKesehatan.findUnique.data.name} - - - Informasi Umum - Fasilitas - {stateFasilitasKesehatan.findUnique.data.informasiumum.fasilitas} - Alamat - {stateFasilitasKesehatan.findUnique.data.informasiumum.alamat} - Jam Operasional - {stateFasilitasKesehatan.findUnique.data.informasiumum.jamOperasional} - - - Layanan Unggulan - - - - Fasilitas Pendukung - - - - Prosedur Pendaftaran - - - - Dokter dan Tenaga Medis - Nama Dokter - {stateFasilitasKesehatan.findUnique.data.dokterdantenagamedis.name} - Specialist - {stateFasilitasKesehatan.findUnique.data.dokterdantenagamedis.specialist} - Jadwal - {stateFasilitasKesehatan.findUnique.data.dokterdantenagamedis.jadwal} - - - Tarif dan Layanan - Layanan - {stateFasilitasKesehatan.findUnique.data.tarifdanlayanan.layanan} - Tarif - {stateFasilitasKesehatan.findUnique.data.tarifdanlayanan.tarif} - - - - - - - - - - ) : null} - + + {/* Tombol Back */} + + {/* Wrapper Detail */} + + + + Detail Fasilitas Kesehatan + + + + + + Nama Fasilitas + {data.name || '-'} + + + + Informasi Umum + Fasilitas + {data.informasiumum?.fasilitas || '-'} + Alamat + {data.informasiumum?.alamat || '-'} + Jam Operasional + {data.informasiumum?.jamOperasional || '-'} + + + + Layanan Unggulan + + + + + Fasilitas Pendukung + + + + + Prosedur Pendaftaran + + + + + Dokter & Tenaga Medis + Nama + {data.dokterdantenagamedis?.name || '-'} + Spesialis + {data.dokterdantenagamedis?.specialist || '-'} + Jadwal + {data.dokterdantenagamedis?.jadwal || '-'} + + + + Tarif & Layanan + Layanan + {data.tarifdanlayanan?.layanan || '-'} + Tarif + {data.tarifdanlayanan?.tarif || '-'} + + + {/* Aksi */} + + + + + + + + + + + + - {/* Modal Hapus */} + {/* Modal Konfirmasi Hapus */} setModalHapus(false)} diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create/page.tsx index e620d99f..7d4338c6 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create/page.tsx @@ -2,7 +2,17 @@ import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor'; import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { toast } from 'react-toastify'; @@ -10,174 +20,196 @@ import { useProxy } from 'valtio/utils'; function CreateFasilitasKesehatan() { - const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan) + const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan); const router = useRouter(); - const resetForm = () => { stateFasilitasKesehatan.create.form = { - name: "", + name: '', informasiUmum: { - fasilitas: "", - alamat: "", - jamOperasional: "", + fasilitas: '', + alamat: '', + jamOperasional: '', }, layananUnggulan: { - content: "", + content: '', }, dokterdanTenagaMedis: { - name: "", - specialist: "", - jadwal: "", + name: '', + specialist: '', + jadwal: '', }, fasilitasPendukung: { - content: "", + content: '', }, prosedurPendaftaran: { - content: "", + content: '', }, tarifDanLayanan: { - layanan: "", - tarif: "", + layanan: '', + tarif: '', }, }; }; - - const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); await stateFasilitasKesehatan.create.submit(); - - toast.success("Data berhasil disimpan"); + toast.success('Data berhasil disimpan'); resetForm(); - // After successful submission, redirect to the list page router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan'); - } + }; return ( - - - - + + {/* Header */} + + + + + + Tambah Data Fasilitas Kesehatan + + - - - Create Fasilitas Kesehatan + {/* Form */} + + Nama Fasilitas Kesehatan} - placeholder="masukkan nama fasilitas kesehatan" + label={"Nama Fasilitas Kesehatan"} + placeholder="Masukkan nama fasilitas kesehatan" value={stateFasilitasKesehatan.create.form.name} - onChange={(e) => { - stateFasilitasKesehatan.create.form.name = e.target.value; - }} + onChange={(e) => (stateFasilitasKesehatan.create.form.name = e.target.value)} + required /> + + {/* Informasi Umum */} - Informasi Umum + Informasi Umum Fasilitas} - placeholder="masukkan fasilitas" + label="Fasilitas" + placeholder="Masukkan fasilitas" value={stateFasilitasKesehatan.create.form.informasiUmum.fasilitas} - onChange={(e) => { - stateFasilitasKesehatan.create.form.informasiUmum.fasilitas = e.target.value; - }} + onChange={(e) => (stateFasilitasKesehatan.create.form.informasiUmum.fasilitas = e.target.value)} + required /> Alamat} - placeholder="masukkan alamat" + label="Alamat" + placeholder="Masukkan alamat" value={stateFasilitasKesehatan.create.form.informasiUmum.alamat} - onChange={(e) => { - stateFasilitasKesehatan.create.form.informasiUmum.alamat = e.target.value; - }} + onChange={(e) => (stateFasilitasKesehatan.create.form.informasiUmum.alamat = e.target.value)} + required /> Jam Operasional} - placeholder="masukkan jam operasional" + label="Jam Operasional" + placeholder="Masukkan jam operasional" value={stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional} - onChange={(e) => { - stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional = e.target.value; - }} + onChange={(e) => (stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional = e.target.value)} + required /> + + {/* Layanan Unggulan */} - Layanan Unggulan + Layanan Unggulan { - stateFasilitasKesehatan.create.form.layananUnggulan.content = e; - }} - /> - - - Dokter dan Tenaga Medis - Nama Dokter} - placeholder="masukkan nama dokter" - value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name} - onChange={(e) => { - stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name = e.target.value; - }} - /> - Specialist} - placeholder="masukkan specialist" - value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist} - onChange={(e) => { - stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist = e.target.value; - }} - /> - Jadwal} - placeholder="masukkan jadwal" - value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal} - onChange={(e) => { - stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal = e.target.value; - }} - /> - - - Fasilitas Pendukung - { - stateFasilitasKesehatan.create.form.fasilitasPendukung.content = e; - }} - /> - - - Prosedur Pendaftaran - { - stateFasilitasKesehatan.create.form.prosedurPendaftaran.content = e; - }} - /> - - - Tarif dan Layanan - Tarif} - placeholder="masukkan tarif" - value={stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif} - onChange={(e) => { - stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif = e.target.value; - }} - /> - Layanan} - placeholder="masukkan layanan" - value={stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan} - onChange={(e) => { - stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan = e.target.value; - }} + onChange={(val) => (stateFasilitasKesehatan.create.form.layananUnggulan.content = val)} /> - + {/* Dokter dan Tenaga Medis */} + + Dokter dan Tenaga Medis + (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name = e.target.value)} + required + /> + (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist = e.target.value)} + required + /> + (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal = e.target.value)} + required + /> + + + {/* Fasilitas Pendukung */} + + Fasilitas Pendukung + (stateFasilitasKesehatan.create.form.fasilitasPendukung.content = val)} + /> + + + {/* Prosedur Pendaftaran */} + + Prosedur Pendaftaran + (stateFasilitasKesehatan.create.form.prosedurPendaftaran.content = val)} + /> + + + {/* Tarif dan Layanan */} + + Tarif dan Layanan + (stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif = e.target.value)} + required + /> + (stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan = e.target.value)} + required + /> + + + {/* Submit */} + + + diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/page.tsx index a0332133..fa14f3a2 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/page.tsx @@ -1,114 +1,172 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { + Box, + Button, + Center, + Group, + Paper, + Pagination, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { IconArrowBack, IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; import fasilitasKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan'; function FasilitasKesehatan() { + const router = useRouter(); const [search, setSearch] = useState(""); - // const router = useRouter(); + return ( - {/* - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - - - - - - */} + {/* Tombol Back */} + + + + + {/* Header Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> + ); } + function ListFasilitasKesehatan({ search }: { search: string }) { const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan) const router = useRouter(); + const { data, page, totalPages, loading, load } = stateFasilitasKesehatan.findMany; + useShallowEffect(() => { - stateFasilitasKesehatan.findMany.load() - }, []) + load(page, 10, search); + }, [page, search]); - const filteredData = (stateFasilitasKesehatan.findMany.data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.name.toLowerCase().includes(keyword) || - item.informasiumum.alamat.toLowerCase().includes(keyword) || - item.informasiumum.jamOperasional.toLowerCase().includes(keyword) - ); - }); + const filteredData = data || []; - if (!stateFasilitasKesehatan.findMany.data) { + if (loading || !data) { return ( - - - + + + ) } + return ( - - - - - - - - Fasilitas Kesehatan - Dokter - Layanan - Detail - - - - {filteredData.map((item) => ( + + {/* Judul + Tombol Tambah */} + + Daftar Fasilitas Kesehatan + + + + + + {/* Tabel */} + +
+ + + Fasilitas Kesehatan + Dokter + Layanan + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( - {item.name} - {item.dokterdantenagamedis.name} - {item.tarifdanlayanan.layanan} - - ))} - -
-
-
+ )) + ) : ( + + +
+ + Tidak ada fasilitas kesehatan yang cocok + +
+
+
+ )} + + +
+ + {/* Pagination */} +
+ { + load(newPage, 10, search); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
) } diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/edit/page.tsx index ab91485c..c5f9bc20 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/edit/page.tsx @@ -2,7 +2,16 @@ 'use client' import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + TextInput, + Title, + Tooltip +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; @@ -10,9 +19,10 @@ import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; function EditGrafikHasilKepuasan() { - const editState = useProxy(grafikkepuasan) + const editState = useProxy(grafikkepuasan); const router = useRouter(); const params = useParams(); + const [formData, setFormData] = useState({ nama: editState.update.form.nama || '', tanggal: editState.update.form.tanggal || '', @@ -22,12 +32,12 @@ function EditGrafikHasilKepuasan() { }); useEffect(() => { - const loadKelahiran = async () => { + const loadData = async () => { const id = params?.id as string; if (!id) return; try { - const data = await editState.update.load(id); // akses langsung, bukan dari proxy + const data = await editState.update.load(id); if (data) { setFormData({ nama: data.nama || '', @@ -43,21 +53,17 @@ function EditGrafikHasilKepuasan() { } }; - loadKelahiran(); + loadData(); }, [params?.id]); const handleSubmit = async () => { try { editState.update.form = { ...editState.update.form, - nama: formData.nama, - tanggal: formData.tanggal, - jenisKelamin: formData.jenisKelamin, - alamat: formData.alamat, - penyakit: formData.penyakit, + ...formData, }; await editState.update.submit(); - toast.success('grafik hasil kepuasan berhasil diperbarui!'); + toast.success('Grafik hasil kepuasan berhasil diperbarui!'); router.push('/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan'); } catch (error) { console.error('Error updating grafik hasil kepuasan:', error); @@ -66,47 +72,87 @@ function EditGrafikHasilKepuasan() { }; return ( - - - - - - - Edit grafik hasil kepuasan + + {/* Header */} + + + + + + Edit Grafik Hasil Kepuasan + + + + {/* Form */} + + setFormData({ ...formData, nama: e.target.value })} - label={Nama} - placeholder="masukkan nama" + label="Nama" + placeholder="Masukkan nama" + required /> setFormData({ ...formData, tanggal: e.target.value })} - label={Tanggal} - placeholder="masukkan tanggal" + label="Tanggal" + placeholder="Masukkan tanggal" + required /> setFormData({ ...formData, jenisKelamin: e.target.value })} - label={Jenis Kelamin} - placeholder="masukkan jenis kelamin" + onChange={(e) => + setFormData({ ...formData, jenisKelamin: e.target.value }) + } + label="Jenis Kelamin" + placeholder="Masukkan jenis kelamin" + required /> setFormData({ ...formData, alamat: e.target.value })} - label={Alamat} - placeholder="masukkan alamat" + label="Alamat" + placeholder="Masukkan alamat" + required /> setFormData({ ...formData, penyakit: e.target.value })} - label={Penyakit} - placeholder="masukkan penyakit" + label="Penyakit" + placeholder="Masukkan penyakit" + required /> - + + + + diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/page.tsx index c6cdf12e..c80da5f8 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/[id]/page.tsx @@ -1,9 +1,8 @@ 'use client' import { useProxy } from 'valtio/utils'; - -import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { Box, Button, Group, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; @@ -11,103 +10,130 @@ import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirma import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan'; import colors from '@/con/colors'; - function DetailGrafikHasilKepuasan() { - const state = useProxy(grafikkepuasan) - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) - const params = useParams() - const router = useRouter() + const state = useProxy(grafikkepuasan); + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); + const params = useParams(); + const router = useRouter(); useShallowEffect(() => { - state.findUnique.load(params?.id as string) - }, []) - + state.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - state.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan") + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan"); } - } + }; if (!state.findUnique.data) { return ( - + - ) + ); } + const data = state.findUnique.data; + return ( - - - - - - - Detail Data Grafik Hasil Kepuasan - {state.findUnique.data ? ( - - - - Nama - {state.findUnique.data?.nama} - - - Tanggal - - {new Date(state.findUnique.data?.tanggal).toLocaleDateString('id-ID', { - day: '2-digit', - month: 'long', - year: 'numeric' - })} - - - - Jenis Kelamin - {state.findUnique.data?.jenisKelamin} - - - Alamat - {state.findUnique.data?.alamat} - - - Penyakit - {state.findUnique.data?.penyakit} - - + + {/* Tombol Back */} + + + {/* Wrapper Detail */} + + + + Detail Data Grafik Hasil Kepuasan + + + + + + Nama + {data.nama || '-'} + + + + Tanggal + + {new Date(data.tanggal).toLocaleDateString("id-ID", { + day: "2-digit", + month: "long", + year: "numeric", + })} + + + + + Jenis Kelamin + {data.jenisKelamin || '-'} + + + + Alamat + {data.alamat || '-'} + + + + Penyakit + {data.penyakit || '-'} + + + {/* Aksi */} + + + + + - - - - ) : null} + + + + @@ -116,10 +142,10 @@ function DetailGrafikHasilKepuasan() { opened={modalHapus} onClose={() => setModalHapus(false)} onConfirm={handleHapus} - text='Apakah anda yakin ingin menghapus data ini?' + text="Apakah anda yakin ingin menghapus data ini?" /> ); } -export default DetailGrafikHasilKepuasan; \ No newline at end of file +export default DetailGrafikHasilKepuasan; diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/create/page.tsx index b3bb8b7f..c84f14db 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/create/page.tsx @@ -3,7 +3,17 @@ 'use client' import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan'; import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; @@ -12,7 +22,7 @@ import { useProxy } from 'valtio/utils'; function CreateGrafikHasilKepuasanMasyarakat() { const stateGrafikKepuasan = useProxy(grafikkepuasan); const [chartData, setChartData] = useState([]); - const router = useRouter() + const router = useRouter(); const resetForm = () => { stateGrafikKepuasan.create.form = { @@ -21,82 +31,97 @@ function CreateGrafikHasilKepuasanMasyarakat() { jenisKelamin: "", alamat: "", penyakit: "", - } - } + }; + }; const handleSubmit = async () => { await stateGrafikKepuasan.create.create(); resetForm(); router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan"); - } + }; + return ( - - - - - - - Tambah Grafik Hasil Kepuasan Masyarakat - - { - stateGrafikKepuasan.create.form.nama = val.currentTarget.value; + + {/* Header */} + + + + + + Tambah Grafik Hasil Kepuasan Masyarakat + + + + {/* Form */} + + + (stateGrafikKepuasan.create.form.nama = e.target.value)} + required + /> + (stateGrafikKepuasan.create.form.tanggal = e.target.value)} + required + /> + (stateGrafikKepuasan.create.form.jenisKelamin = e.target.value)} + required + /> + (stateGrafikKepuasan.create.form.alamat = e.target.value)} + required + /> + (stateGrafikKepuasan.create.form.penyakit = e.target.value)} + required + /> + + + - - - - + > + Simpan + + + + ); } diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/page.tsx index ec639413..037ca698 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/page.tsx @@ -1,99 +1,108 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable react-hooks/exhaustive-deps */ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core'; +import { + Box, + Button, + Center, + Group, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip +} from '@mantine/core'; import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { IconArrowBack, IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; -import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts'; +import { Bar, BarChart, Tooltip as ChartTooltip, Legend, XAxis, YAxis } from 'recharts'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; import grafikkepuasan from '../../../_state/kesehatan/data_kesehatan_warga/grafikKepuasan'; function GrafikHasilKepuasanMasyarakat() { const [search, setSearch] = useState(""); + const router = useRouter(); + return ( + {/* Tombol Back */} + + + + + {/* Header Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> + ); } + function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) { type PDKMGrafik = { id: string; nama: string; - tanggal: string | Date; // Allow both string and Date types + tanggal: string | Date; jenisKelamin: string; alamat: string; penyakit: string; - createdAt?: Date; // Add optional fields that might come from the API - updatedAt?: Date; - deletedAt?: Date | null; - } + }; + const stateGrafikKepuasan = useProxy(grafikkepuasan); const [chartData, setChartData] = useState([]); - const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready - const isTablet = useMediaQuery('(max-width: 1024px)') - const isMobile = useMediaQuery('(max-width: 768px)') + const [mounted, setMounted] = useState(false); + const isTablet = useMediaQuery('(max-width: 1024px)'); + const isMobile = useMediaQuery('(max-width: 768px)'); const router = useRouter(); - const { - data, - page, - totalPages, - loading, - load - } = stateGrafikKepuasan.findMany; + const { data, page, totalPages, loading, load } = stateGrafikKepuasan.findMany; useShallowEffect(() => { - setMounted(true) - load(page, 10, search) - }, [page, search]) + setMounted(true); + load(page, 10, search); + }, [page, search]); useEffect(() => { - setMounted(true); if (data) { setChartData(data.map((item) => ({ - id: item.id, - nama: item.nama, - tanggal: item.tanggal instanceof Date ? item.tanggal.toISOString() : item.tanggal, - jenisKelamin: item.jenisKelamin, - alamat: item.alamat, - penyakit: item.penyakit, + ...item, + tanggal: item.tanggal instanceof Date ? item.tanggal.toISOString() : item.tanggal }))); } }, [data]); - // Add this function to process the data const processDiseaseData = (data: PDKMGrafik[]) => { const diseaseCount: Record = {}; - data.forEach(item => { const penyakit = item.penyakit.trim(); if (penyakit) { diseaseCount[penyakit] = (diseaseCount[penyakit] || 0) + 1; } }); - - return Object.entries(diseaseCount).map(([name, count]) => ({ - name, - count - })); + return Object.entries(diseaseCount).map(([name, count]) => ({ name, count })); }; - // Add this state to store the processed chart data const [diseaseChartData, setDiseaseChartData] = useState<{ name: string, count: number }[]>([]); - // Update the chart data when data changes useEffect(() => { if (data && data.length > 0) { setDiseaseChartData(processDiseaseData(data)); @@ -104,97 +113,136 @@ function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) { if (loading || !data) { return ( - - + + - ) + ); } return ( - - - {/* Form Input */} - - - + + + {/* Judul + Tombol Tambah */} + + Daftar Grafik Hasil Kepuasan Masyarakat + + + + + + {/* Tabel */} + +
Nama Tanggal Jenis Kelamin Penyakit - Detail + Aksi - {filteredData.map((item) => ( - - {item.nama} - - {new Date(item.tanggal).toLocaleDateString('id-ID', { - day: '2-digit', - month: 'long', - year: 'numeric' - })} - - {item.jenisKelamin} - {item.penyakit} - - + {filteredData.length > 0 ? ( + filteredData.map((item) => ( + + {item.nama} + + {new Date(item.tanggal).toLocaleDateString('id-ID', { + day: '2-digit', + month: 'long', + year: 'numeric', + })} + + {item.jenisKelamin} + {item.penyakit} + + + + + )) + ) : ( + + +
+ + Tidak ada data kepuasan masyarakat yang cocok + +
- ))} + )}
+
+ + + {/* Pagination */} +
+ { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
+ + {/* Chart */} + + + Grafik Hasil Kepuasan Masyarakat + {mounted && diseaseChartData.length > 0 ? ( + + + + + + + + ) : ( + Belum ada data untuk ditampilkan dalam grafik + )} -
- load(newPage)} // ini penting! - total={totalPages} - mt="md" - mb="md" - /> -
- - - {/* Chart */} - {!mounted && !chartData ? ( - - - Grafik Hasil Kepuasan Masyarakat - Belum ada data untuk ditampilkan dalam grafik - - - ) : ( - - - Grafik Hasil Kepuasan Masyarakat - {mounted && diseaseChartData.length > 0 && ( - - - - - - - - )} - - - )} -
+
); } diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/edit/page.tsx index 06f84f83..d2c43fed 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/edit/page.tsx @@ -4,7 +4,17 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; @@ -128,33 +138,14 @@ function EditJadwalKegiatan() { stateJadwalKegiatan.edit.form = { ...stateJadwalKegiatan.edit.form, content: formData.content, - informasiJadwalKegiatan: { - name: formData.informasiJadwalKegiatan.name, - tanggal: formData.informasiJadwalKegiatan.tanggal, - waktu: formData.informasiJadwalKegiatan.waktu, - lokasi: formData.informasiJadwalKegiatan.lokasi, - }, - deskripsiJadwalKegiatan: { - deskripsi: formData.deskripsiJadwalKegiatan.deskripsi, - }, - layananJadwalKegiatan: { - content: formData.layananJadwalKegiatan.content, - }, - syaratKetentuanJadwalKegiatan: { - content: formData.syaratKetentuanJadwalKegiatan.content, - }, - dokumenJadwalKegiatan: { - content: formData.dokumenJadwalKegiatan.content, - }, - pendaftaranJadwalKegiatan: { - name: formData.pendaftaranJadwalKegiatan.name, - tanggal: formData.pendaftaranJadwalKegiatan.tanggal, - namaOrangtua: formData.pendaftaranJadwalKegiatan.namaOrangtua, - nomor: formData.pendaftaranJadwalKegiatan.nomor, - alamat: formData.pendaftaranJadwalKegiatan.alamat, - catatan: formData.pendaftaranJadwalKegiatan.catatan, - }, + informasiJadwalKegiatan: { ...formData.informasiJadwalKegiatan }, + deskripsiJadwalKegiatan: { ...formData.deskripsiJadwalKegiatan }, + layananJadwalKegiatan: { ...formData.layananJadwalKegiatan }, + syaratKetentuanJadwalKegiatan: { ...formData.syaratKetentuanJadwalKegiatan }, + dokumenJadwalKegiatan: { ...formData.dokumenJadwalKegiatan }, + pendaftaranJadwalKegiatan: { ...formData.pendaftaranJadwalKegiatan }, }; + const success = await stateJadwalKegiatan.edit.submit(); if (success) { toast.success("Jadwal kegiatan berhasil diperbarui!"); @@ -165,241 +156,164 @@ function EditJadwalKegiatan() { toast.error(error instanceof Error ? error.message : "Gagal memperbarui data jadwal kegiatan"); } }; + return ( - - - - - - - - Edit Jadwal Kegiatan + + {/* Header */} + + + + + + Edit Jadwal Kegiatan + + + + {/* Form */} + + + {/* Nama Jadwal */} Nama Jadwal Kegiatan} - placeholder="masukkan nama jadwal kegiatan" + label="Nama Jadwal Kegiatan" + placeholder="Masukkan nama jadwal kegiatan" value={formData.content} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - content: e.target.value - })); - }} + onChange={(e) => setFormData((prev) => ({ ...prev, content: e.target.value }))} /> - - Deskripsi Jadwal Kegiatan - { - setFormData(prev => ({ - ...prev, - deskripsiJadwalKegiatan: { - ...prev.deskripsiJadwalKegiatan, - deskripsi: e - } - })); - }} - /> - + + {/* Deskripsi */} - Informasi Jadwal Kegiatan - Nama} - placeholder="masukkan nama" - value={formData.informasiJadwalKegiatan.name} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - informasiJadwalKegiatan: { - ...prev.informasiJadwalKegiatan, - name: e.target.value - } - })); - }} - /> - Tanggal} - placeholder="masukkan tanggal" - value={formData.informasiJadwalKegiatan.tanggal} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - informasiJadwalKegiatan: { - ...prev.informasiJadwalKegiatan, - tanggal: e.target.value - } - })); - }} - /> - Waktu} - placeholder="masukkan waktu" - value={formData.informasiJadwalKegiatan.waktu} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - informasiJadwalKegiatan: { - ...prev.informasiJadwalKegiatan, - waktu: e.target.value - } - })); - }} - /> - Lokasi} - placeholder="masukkan lokasi" - value={formData.informasiJadwalKegiatan.lokasi} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - informasiJadwalKegiatan: { - ...prev.informasiJadwalKegiatan, - lokasi: e.target.value - } - })); - }} + Deskripsi Jadwal Kegiatan + setFormData((prev) => ({ + ...prev, + deskripsiJadwalKegiatan: { deskripsi: val } + }))} /> + + {/* Informasi Jadwal */} + + Informasi Jadwal Kegiatan + setFormData((prev) => ({ + ...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, name: e.target.value } + }))} + /> + setFormData((prev) => ({ + ...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, tanggal: e.target.value } + }))} + /> + setFormData((prev) => ({ + ...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, waktu: e.target.value } + }))} + /> + setFormData((prev) => ({ + ...prev, informasiJadwalKegiatan: { ...prev.informasiJadwalKegiatan, lokasi: e.target.value } + }))} + /> + + + {/* Layanan */} Layanan Jadwal Kegiatan { - setFormData(prev => ({ - ...prev, - layananJadwalKegiatan: { - ...prev.layananJadwalKegiatan, - content: e - } - })); - }} + onChange={(val) => setFormData((prev) => ({ + ...prev, + layananJadwalKegiatan: { content: val } + }))} /> + + {/* Syarat */} - Syarat dan Ketentuan Jadwal Kegiatan + Syarat dan Ketentuan { - setFormData(prev => ({ - ...prev, - syaratKetentuanJadwalKegiatan: { - ...prev.syaratKetentuanJadwalKegiatan, - content: e - } - })); - }} + onChange={(val) => setFormData((prev) => ({ + ...prev, + syaratKetentuanJadwalKegiatan: { content: val } + }))} /> + + {/* Dokumen */} Dokumen Jadwal Kegiatan { - setFormData(prev => ({ - ...prev, - dokumenJadwalKegiatan: { - ...prev.dokumenJadwalKegiatan, - content: e - } - })); - }} - /> - - - Pendaftaran Jadwal Kegiatan - Nama} - placeholder="masukkan nama" - value={formData.pendaftaranJadwalKegiatan.name} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - pendaftaranJadwalKegiatan: { - ...prev.pendaftaranJadwalKegiatan, - name: e.target.value - } - })); - }} - /> - Tanggal} - placeholder="masukkan tanggal" - value={formData.pendaftaranJadwalKegiatan.tanggal} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - pendaftaranJadwalKegiatan: { - ...prev.pendaftaranJadwalKegiatan, - tanggal: e.target.value - } - })); - }} - /> - Nama Orangtua} - placeholder="masukkan nama orangtua" - value={formData.pendaftaranJadwalKegiatan.namaOrangtua} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - pendaftaranJadwalKegiatan: { - ...prev.pendaftaranJadwalKegiatan, - namaOrangtua: e.target.value - } - })); - }} - /> - Nomor} - placeholder="masukkan nomor" - value={formData.pendaftaranJadwalKegiatan.nomor} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - pendaftaranJadwalKegiatan: { - ...prev.pendaftaranJadwalKegiatan, - nomor: e.target.value - } - })); - }} - /> - Alamat} - placeholder="masukkan alamat" - value={formData.pendaftaranJadwalKegiatan.alamat} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - pendaftaranJadwalKegiatan: { - ...prev.pendaftaranJadwalKegiatan, - alamat: e.target.value - } - })); - }} - /> - Catatan} - placeholder="masukkan catatan" - value={formData.pendaftaranJadwalKegiatan.catatan} - onChange={(e) => { - setFormData(prev => ({ - ...prev, - pendaftaranJadwalKegiatan: { - ...prev.pendaftaranJadwalKegiatan, - catatan: e.target.value - } - })); - }} + onChange={(val) => setFormData((prev) => ({ + ...prev, + dokumenJadwalKegiatan: { content: val } + }))} /> - + {/* Pendaftaran */} + + Pendaftaran Jadwal Kegiatan + setFormData((prev) => ({ + ...prev, pendaftaranJadwalKegiatan: { ...prev.pendaftaranJadwalKegiatan, name: e.target.value } + }))} + /> + setFormData((prev) => ({ + ...prev, pendaftaranJadwalKegiatan: { ...prev.pendaftaranJadwalKegiatan, tanggal: e.target.value } + }))} + /> + setFormData((prev) => ({ + ...prev, pendaftaranJadwalKegiatan: { ...prev.pendaftaranJadwalKegiatan, namaOrangtua: e.target.value } + }))} + /> + setFormData((prev) => ({ + ...prev, pendaftaranJadwalKegiatan: { ...prev.pendaftaranJadwalKegiatan, nomor: e.target.value } + }))} + /> + setFormData((prev) => ({ + ...prev, pendaftaranJadwalKegiatan: { ...prev.pendaftaranJadwalKegiatan, alamat: e.target.value } + }))} + /> + setFormData((prev) => ({ + ...prev, pendaftaranJadwalKegiatan: { ...prev.pendaftaranJadwalKegiatan, catatan: e.target.value } + }))} + /> + + + {/* Submit */} + + + - - + ); } diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/page.tsx index fcbee448..47c6a91f 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/[id]/page.tsx @@ -2,9 +2,9 @@ import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan'; import colors from '@/con/colors'; -import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { Box, Button, Group, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; @@ -32,83 +32,128 @@ function DetailJadwalKegiatan() { if (!stateJadwalKegiatan.findUnique.data) { return ( - + ) } + const data = stateJadwalKegiatan.findUnique.data return ( - - - - - - - Detail Jadwal Kegiatan - {stateJadwalKegiatan.findUnique.data ? ( - - - - Nama Kegiatan - {stateJadwalKegiatan.findUnique.data.content} - - - Informasi - Nama - {stateJadwalKegiatan.findUnique.data.informasijadwalkegiatan.name} - Tanggal - {stateJadwalKegiatan.findUnique.data.informasijadwalkegiatan.tanggal} - Waktu - {stateJadwalKegiatan.findUnique.data.informasijadwalkegiatan.waktu} - Lokasi - {stateJadwalKegiatan.findUnique.data.informasijadwalkegiatan.lokasi} - - - Deskripsi - - - - Layanan - - - - Syarat Ketentuan - - - - Dokumen - - - - Prosedur Pendaftaran - {stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.name} - {stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.tanggal} - {stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.namaOrangtua} - {stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.nomor} - {stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.alamat} - - - - - - - - - - - ) : null} + + {/* Tombol Back */} + + + {/* Wrapper Detail */} + + + + Detail Jadwal Kegiatan + + + + + {/* Nama Kegiatan */} + + Nama Kegiatan + {data.content || '-'} + + + {/* Informasi */} + + Informasi + Nama + {data.informasijadwalkegiatan.name || '-'} + Tanggal + {data.informasijadwalkegiatan.tanggal || '-'} + Waktu + {data.informasijadwalkegiatan.waktu || '-'} + Lokasi + {data.informasijadwalkegiatan.lokasi || '-'} + + + {/* Deskripsi */} + + Deskripsi + + + + {/* Layanan */} + + Layanan + + + + {/* Syarat Ketentuan */} + + Syarat Ketentuan + + + + {/* Dokumen */} + + Dokumen + + + + {/* Prosedur Pendaftaran */} + + Prosedur Pendaftaran + {data.pendaftaranjadwalkegiatan.name} + {data.pendaftaranjadwalkegiatan.tanggal} + {data.pendaftaranjadwalkegiatan.namaOrangtua} + {data.pendaftaranjadwalkegiatan.nomor} + {data.pendaftaranjadwalkegiatan.alamat} + + + + {/* Aksi */} + + + + + + + + + + + diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create/page.tsx index c9c01fa7..ef5f3879 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create/page.tsx @@ -2,127 +2,158 @@ import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor'; import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; - function CreateJadwalKegiatan() { - const stateJadwalKegiatan = useProxy(jadwalKegiatanState) + const stateJadwalKegiatan = useProxy(jadwalKegiatanState); const router = useRouter(); - const resetForm = () => { - stateJadwalKegiatan.edit.form = { - content: "", + stateJadwalKegiatan.create.form = { + content: '', informasiJadwalKegiatan: { - name: "", - tanggal: "", - waktu: "", - lokasi: "", + name: '', + tanggal: '', + waktu: '', + lokasi: '', }, deskripsiJadwalKegiatan: { - deskripsi: "", + deskripsi: '', }, layananJadwalKegiatan: { - content: "", + content: '', }, syaratKetentuanJadwalKegiatan: { - content: "", + content: '', }, dokumenJadwalKegiatan: { - content: "", + content: '', }, pendaftaranJadwalKegiatan: { - name: "", - tanggal: "", - namaOrangtua: "", - nomor: "", - alamat: "", - catatan: "", + name: '', + tanggal: '', + namaOrangtua: '', + nomor: '', + alamat: '', + catatan: '', }, }; }; - - const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); await stateJadwalKegiatan.create.submit(); - toast.success("Data berhasil disimpan"); + toast.success('Data berhasil disimpan'); resetForm(); - // After successful submission, redirect to the list page router.push('/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan'); - } + }; return ( - - - - + + {/* Header */} + + + + + + Tambah Jadwal Kegiatan + + - - - Create Jadwal Kegiatan + {/* Form */} + + Nama Jadwal Kegiatan} - placeholder="masukkan nama jadwal kegiatan" + label="Nama Jadwal Kegiatan" + placeholder="Masukkan nama jadwal kegiatan" value={stateJadwalKegiatan.create.form.content} onChange={(e) => { stateJadwalKegiatan.create.form.content = e.target.value; }} + required /> - - Deskripsi Jadwal Kegiatan - { - stateJadwalKegiatan.create.form.deskripsiJadwalKegiatan.deskripsi = e; - }} - /> - + - Informasi Jadwal Kegiatan + Deskripsi Jadwal Kegiatan + { + stateJadwalKegiatan.create.form.deskripsiJadwalKegiatan.deskripsi = e; + }} + /> + + + + Informasi Jadwal Kegiatan Nama} - placeholder="masukkan nama" + label="Nama" + required + placeholder="Masukkan nama" value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name} onChange={(e) => { stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name = e.target.value; }} /> Tanggal} - placeholder="masukkan tanggal" + type="date" + required + label="Tanggal" value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal} onChange={(e) => { stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal = e.target.value; }} /> Waktu} - placeholder="masukkan waktu" + label="Waktu" + required + placeholder="Masukkan waktu" value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu} onChange={(e) => { stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu = e.target.value; }} /> Lokasi} - placeholder="masukkan lokasi" + label="Lokasi" + required + placeholder="Masukkan lokasi" value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi} onChange={(e) => { stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi = e.target.value; }} /> + - Layanan Jadwal Kegiatan + Layanan Jadwal Kegiatan { @@ -130,8 +161,9 @@ function CreateJadwalKegiatan() { }} /> + - Syarat dan Ketentuan Jadwal Kegiatan + Syarat & Ketentuan { @@ -139,8 +171,9 @@ function CreateJadwalKegiatan() { }} /> + - Dokumen Jadwal Kegiatan + Dokumen { @@ -148,51 +181,58 @@ function CreateJadwalKegiatan() { }} /> + - Pendaftaran Jadwal Kegiatan + Pendaftaran Jadwal Kegiatan Nama} - placeholder="masukkan nama" + label="Nama" + required + placeholder="Masukkan nama" value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.name} onChange={(e) => { stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.name = e.target.value; }} /> Tanggal} - placeholder="masukkan tanggal" + type="date" + required + label="Tanggal" value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.tanggal} onChange={(e) => { stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.tanggal = e.target.value; }} /> Nama Orangtua} - placeholder="masukkan nama orangtua" + label="Nama Orangtua" + required + placeholder="Masukkan nama orangtua" value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.namaOrangtua} onChange={(e) => { stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.namaOrangtua = e.target.value; }} /> Nomor} - placeholder="masukkan nomor" + label="Nomor" + required + placeholder="Masukkan nomor" value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.nomor} onChange={(e) => { stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.nomor = e.target.value; }} /> Alamat} - placeholder="masukkan alamat" + label="Alamat" + required + placeholder="Masukkan alamat" value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.alamat} onChange={(e) => { stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.alamat = e.target.value; }} /> Catatan} - placeholder="masukkan catatan" + label="Catatan" + required + placeholder="Masukkan catatan" value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.catatan} onChange={(e) => { stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.catatan = e.target.value; @@ -200,9 +240,21 @@ function CreateJadwalKegiatan() { /> - + {/* Save Button */} + + + diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/page.tsx index 3cee8a7a..bf6f97a4 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/jadwal_kegiatan/page.tsx @@ -1,96 +1,185 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { + Box, + Button, + Center, + Group, + Paper, + Pagination, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { IconArrowBack, IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; import jadwalKegiatanState from '../../../_state/kesehatan/data_kesehatan_warga/jadwalKegiatan'; import { useState } from 'react'; function JadwalKegiatan() { + const router = useRouter(); const [search, setSearch] = useState(""); + return ( + {/* Tombol Back */} + + + + + {/* Header Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> - + + ); } function ListJadwalKegiatan({ search }: { search: string }) { - const stateJadwalKegiatan = useProxy(jadwalKegiatanState) + const state = useProxy(jadwalKegiatanState); const router = useRouter(); + const { data, page, totalPages, loading, load } = state.findMany; + useShallowEffect(() => { - stateJadwalKegiatan.findMany.load() - }, []) + load(page, 10, search); + }, [page, search]); - const filteredData = (stateJadwalKegiatan.findMany.data || []).filter(item => { - const keyword = search.toLowerCase(); + const filteredData = data || []; + + if (loading || !data) { return ( - item.informasijadwalkegiatan.name.toLowerCase().includes(keyword) || - item.informasijadwalkegiatan.tanggal.toLowerCase().includes(keyword) || - item.informasijadwalkegiatan.waktu.toLowerCase().includes(keyword) || - item.informasijadwalkegiatan.lokasi.toLowerCase().includes(keyword) + + + ); - }); - - if (!stateJadwalKegiatan.findMany.data) { - return ( - - - - ) } + return ( - - - + + {/* Judul + Tombol Tambah */} + + Daftar Jadwal Kegiatan + + + + + + {/* Tabel */} - +
Nama Tanggal Waktu Lokasi - Detail + Aksi - {filteredData.map((item) => ( - - {item.informasijadwalkegiatan.name} - {item.informasijadwalkegiatan.tanggal} - {item.informasijadwalkegiatan.waktu} - {item.informasijadwalkegiatan.lokasi} - - + {filteredData.length > 0 ? ( + filteredData.map((item) => ( + + + + {item.informasijadwalkegiatan.name} + + + + {new Date(item.informasijadwalkegiatan.tanggal).toLocaleDateString( + 'id-ID', + { + day: '2-digit', + month: 'long', + year: 'numeric', + } + )} + + {item.informasijadwalkegiatan.waktu} + + + {item.informasijadwalkegiatan.lokasi} + + + + + + + )) + ) : ( + + +
+ + Tidak ada jadwal kegiatan yang cocok + +
- ))} + )}
-
-
-
- ) +
+ + {/* Pagination */} +
+ { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
+
+ ); } export default JadwalKegiatan; diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/edit/page.tsx index b23233e5..e05848c5 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/edit/page.tsx @@ -2,106 +2,162 @@ 'use client' import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + TextInput, + Title, + Tooltip +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; + function EditKelahiran() { - const editState = useProxy(persentaseKelahiranKematian.kelahiran) - const router = useRouter(); - const params = useParams(); - const [formData, setFormData] = useState({ - nama: editState.edit.form.nama || '', - tanggal: editState.edit.form.tanggal || '', - jenisKelamin: editState.edit.form.jenisKelamin || '', - alamat: editState.edit.form.alamat || '', - }); + const editState = useProxy(persentaseKelahiranKematian.kelahiran); + const router = useRouter(); + const params = useParams(); - useEffect(() => { - const loadKelahiran = async () => { - const id = params?.id as string; - if (!id) return; - try { - const data = await editState.edit.load(id); // akses langsung, bukan dari proxy - if (data) { - setFormData({ - nama: data.nama || '', - tanggal: data.tanggal || '', - jenisKelamin: data.jenisKelamin || '', - alamat: data.alamat || '', - }); - } - } catch (error) { - console.error("Error loading data kelahiran:", error); - toast.error("Gagal memuat data data kelahiran"); - } - }; + const [formData, setFormData] = useState({ + nama: editState.edit.form.nama || '', + tanggal: editState.edit.form.tanggal || '', + jenisKelamin: editState.edit.form.jenisKelamin || '', + alamat: editState.edit.form.alamat || '', + }); - loadKelahiran(); - }, [params?.id]); - const handleSubmit = async () => { - try { - editState.edit.form = { - ...editState.edit.form, - nama: formData.nama, - tanggal: formData.tanggal, - jenisKelamin: formData.jenisKelamin, - alamat: formData.alamat, - }; - await editState.edit.update(); - toast.success('data kelahiran berhasil diperbarui!'); - router.push('/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran'); - } catch (error) { - console.error('Error updating data kelahiran:', error); - toast.error('Terjadi kesalahan saat memperbarui data kelahiran'); - } - }; + useEffect(() => { + const loadKelahiran = async () => { + const id = params?.id as string; + if (!id) return; - return ( - - - - - - - Edit data kelahiran - setFormData({ ...formData, nama: e.target.value })} - label={Nama} - placeholder="masukkan nama" - /> - setFormData({ ...formData, tanggal: e.target.value })} - label={Tanggal} - placeholder="masukkan tanggal" - /> - setFormData({ ...formData, jenisKelamin: e.target.value })} - label={Jenis Kelamin} - placeholder="masukkan jenis kelamin" - /> - setFormData({ ...formData, alamat: e.target.value })} - label={Alamat} - placeholder="masukkan alamat" - /> - - - - - ); + + try { + const data = await editState.edit.load(id); + if (data) { + setFormData({ + nama: data.nama || '', + tanggal: data.tanggal || '', + jenisKelamin: data.jenisKelamin || '', + alamat: data.alamat || '', + }); + } + } catch (error) { + console.error('Error loading data kelahiran:', error); + toast.error('Gagal memuat data kelahiran'); + } + }; + + + loadKelahiran(); + }, [params?.id]); + + + const handleSubmit = async () => { + try { + editState.edit.form = { + ...editState.edit.form, + nama: formData.nama, + tanggal: formData.tanggal, + jenisKelamin: formData.jenisKelamin, + alamat: formData.alamat, + }; + + + await editState.edit.update(); + toast.success('Data kelahiran berhasil diperbarui!'); + router.push( + '/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran' + ); + } catch (error) { + console.error('Error updating data kelahiran:', error); + toast.error('Terjadi kesalahan saat memperbarui data kelahiran'); + } + }; + + + return ( + + {/* Header */} + + + + + + Edit Data Kelahiran + + + + + {/* Form */} + + + setFormData({ ...formData, nama: e.target.value })} + label="Nama" + placeholder="Masukkan nama" + required + /> + setFormData({ ...formData, tanggal: e.target.value })} + label="Tanggal" + placeholder="Masukkan tanggal" + required + /> + setFormData({ ...formData, jenisKelamin: e.target.value })} + label="Jenis Kelamin" + placeholder="Masukkan jenis kelamin" + required + /> + setFormData({ ...formData, alamat: e.target.value })} + label="Alamat" + placeholder="Masukkan alamat" + required + /> + + + + + + + + + ); } -export default EditKelahiran; + +export default EditKelahiran; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/page.tsx index 90ae22a2..2ce02278 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/[id]/page.tsx @@ -1,11 +1,11 @@ 'use client' -import { useProxy } from 'valtio/utils'; - -import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { Box, Button, Group, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; + import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; @@ -13,109 +13,152 @@ import colors from '@/con/colors'; function DetailKelahiran() { - const state = useProxy(persentaseKelahiranKematian.kelahiran) - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) - const params = useParams() - const router = useRouter() - - useShallowEffect(() => { - state.findUnique.load(params?.id as string) - }, []) + const state = useProxy(persentaseKelahiranKematian.kelahiran); + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); + const params = useParams(); + const router = useRouter(); - const handleHapus = () => { - if (selectedId) { - state.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran") - } - } + useShallowEffect(() => { + state.findUnique.load(params?.id as string); + }, []); - if (!state.findUnique.data) { - return ( - - - - ) - } - return ( - - - - - - - Detail Data Kelahiran - {state.findUnique.data ? ( - - - - Nama - {state.findUnique.data?.nama} - - - Tanggal - - {new Date(state.findUnique.data?.tanggal).toLocaleDateString('id-ID', { - day: '2-digit', - month: 'long', - year: 'numeric' - })} - - - - Jenis Kelamin - {state.findUnique.data?.jenisKelamin} - - - Alamat - {state.findUnique.data?.alamat} - - - - - - - - ) : null} - - + const handleHapus = () => { + if (selectedId) { + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push( + "/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran" + ); + } + }; - {/* Modal Konfirmasi Hapus */} - setModalHapus(false)} - onConfirm={handleHapus} - text='Apakah anda yakin ingin menghapus data ini?' - /> - - ); + + if (!state.findUnique.data) { + return ( + + + + ); + } + + + const data = state.findUnique.data; + + + return ( + + {/* Tombol Back */} + + + + {/* Wrapper Detail */} + + + + Detail Data Kelahiran + + + + + + + Nama + {data.nama || '-'} + + + + + Tanggal + + {new Date(data.tanggal).toLocaleDateString("id-ID", { + day: "2-digit", + month: "long", + year: "numeric", + })} + + + + + + Jenis Kelamin + {data.jenisKelamin || '-'} + + + + + Alamat + {data.alamat || '-'} + + + + {/* Aksi */} + + + + + + + + + + + + + + + + + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text="Apakah anda yakin ingin menghapus data ini?" + /> + + ); } + export default DetailKelahiran; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/create/page.tsx index ffc936f8..e668ac72 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/create/page.tsx @@ -1,83 +1,126 @@ -'use client' +'use client'; import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useProxy } from 'valtio/utils'; - function CreateKelahiran() { - const createState = useProxy(persentaseKelahiranKematian.kelahiran) - const router = useRouter(); + const createState = useProxy(persentaseKelahiranKematian.kelahiran); + const router = useRouter(); - const resetForm = () => { - createState.create.form = { - nama: "", - tanggal: "", - jenisKelamin: "", - alamat: "", - }; - }; - const handleSubmit = async () => { - await createState.create.create(); - resetForm(); - router.push("/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran") - }; + const resetForm = () => { + createState.create.form = { + nama: '', + tanggal: '', + jenisKelamin: '', + alamat: '', + }; + }; - return ( - - - - - - - Create Kelahiran - Nama} - placeholder='Masukkan nama' - value={createState.create.form.nama} - onChange={(val) => { - createState.create.form.nama = val.target.value; - }} - /> - Tanggal} - placeholder='Masukkan tanggal' - value={createState.create.form.tanggal} - onChange={(val) => { - createState.create.form.tanggal = val.target.value; - }} - /> - Jenis Kelamin} - placeholder='Masukkan jenis kelamin' - value={createState.create.form.jenisKelamin} - onChange={(val) => { - createState.create.form.jenisKelamin = val.target.value; - }} - /> - Alamat} - placeholder='Masukkan alamat' - value={createState.create.form.alamat} - onChange={(val) => { - createState.create.form.alamat = val.target.value; - }} - /> - - - - - - - ); + const handleSubmit = async () => { + await createState.create.create(); + resetForm(); + router.push( + '/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran' + ); + }; + + + return ( + + {/* Header */} + + + + + + Tambah Data Kelahiran + + + + + {/* Form */} + + + Nama} + placeholder="Masukkan nama" + value={createState.create.form.nama} + onChange={(e) => (createState.create.form.nama = e.target.value)} + required + /> + Tanggal} + placeholder="Masukkan tanggal" + value={createState.create.form.tanggal} + onChange={(e) => (createState.create.form.tanggal = e.target.value)} + required + /> + Jenis Kelamin} + placeholder="Masukkan jenis kelamin" + value={createState.create.form.jenisKelamin} + onChange={(e) => (createState.create.form.jenisKelamin = e.target.value)} + required + /> + Alamat} + placeholder="Masukkan alamat" + value={createState.create.form.alamat} + onChange={(e) => (createState.create.form.alamat = e.target.value)} + required + /> + + + + + + + + + ); } -export default CreateKelahiran; + +export default CreateKelahiran; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/page.tsx index e55a02ae..550a5403 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/page.tsx @@ -1,118 +1,197 @@ 'use client' import HeaderSearch from '@/app/admin/(dashboard)/_com/header'; -import JudulList from '@/app/admin/(dashboard)/_com/judulList'; import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; import colors from '@/con/colors'; -import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { + Box, + Button, + Center, + Group, + Paper, + Pagination, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { IconArrowBack, IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; function Kelahiran() { - const router = useRouter(); - const [search, setSearch] = useState(""); - return ( - - - - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - ); + const router = useRouter(); + const [search, setSearch] = useState(""); + + + return ( + + {/* Tombol Back */} + + + + + + {/* Header Search */} + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + + + ); } + + function ListKelahiran({ search }: { search: string }) { - const statePersentase = useProxy(persentasekelahiran.kelahiran); - const router = useRouter(); + const statePersentase = useProxy(persentasekelahiran.kelahiran); + const router = useRouter(); - const { - data, - page, - totalPages, - loading, - load - } = statePersentase.findMany; + const { data, page, totalPages, loading, load } = statePersentase.findMany; - useShallowEffect(() => { - load(page, 10, search) - }, [page, search]) - const filteredData = data || [] + useShallowEffect(() => { + load(page, 10, search); + }, [page, search]); - if (loading || !data) { - return ( - - - - ) - } - return ( - - - {/* Form Input */} - - - - - - Nama - Tanggal - Jenis Kelamin - Alamat - Detail - - - - {filteredData.map((item) => ( - - {item.nama} - - {new Date(item.tanggal).toLocaleDateString('id-ID', { - day: '2-digit', - month: 'long', - year: 'numeric' - })} - - {item.jenisKelamin} - {item.alamat} - - - - - ))} - -
-
-
-
- load(newPage)} // ini penting! - total={totalPages} - mt="md" - mb="md" - /> -
-
- ); + const filteredData = data || []; + + + if (loading || !data) { + return ( + + + + ); + } + + + return ( + + + {/* Judul + Tombol Tambah */} + + Daftar Data Kelahiran + + + + + + + {/* Tabel */} + + + + + Nama + Tanggal + Jenis Kelamin + Alamat + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( + + + + {item.nama} + + + + {new Date(item.tanggal).toLocaleDateString('id-ID', { + day: '2-digit', + month: 'long', + year: 'numeric', + })} + + {item.jenisKelamin} + + + {item.alamat} + + + + + + + )) + ) : ( + + +
+ + Tidak ada data kelahiran yang cocok + +
+
+
+ )} +
+
+
+
+ + + {/* Pagination */} +
+ { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
+
+ ); } -export default Kelahiran; + +export default Kelahiran; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/edit/page.tsx index eab57efe..510c60f3 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/edit/page.tsx @@ -3,119 +3,177 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; + function EditKematian() { - const editState = useProxy(persentaseKelahiranKematian.kematian) - const router = useRouter(); - const params = useParams(); - const [formData, setFormData] = useState({ - nama: editState.edit.form.nama || '', - tanggal: editState.edit.form.tanggal || '', - jenisKelamin: editState.edit.form.jenisKelamin || '', - alamat: editState.edit.form.alamat || '', - penyebab: editState.edit.form.penyebab || '', - }); + const editState = useProxy(persentaseKelahiranKematian.kematian); + const router = useRouter(); + const params = useParams(); - useEffect(() => { - const loadKelahiran = async () => { - const id = params?.id as string; - if (!id) return; - try { - const data = await editState.edit.load(id); // akses langsung, bukan dari proxy - if (data) { - setFormData({ - nama: data.nama || '', - tanggal: data.tanggal || '', - jenisKelamin: data.jenisKelamin || '', - alamat: data.alamat || '', - penyebab: data.penyebab || '', - }); - } - } catch (error) { - console.error("Error loading data kelahiran:", error); - toast.error("Gagal memuat data data kelahiran"); - } - }; + const [formData, setFormData] = useState({ + nama: editState.edit.form.nama || '', + tanggal: editState.edit.form.tanggal || '', + jenisKelamin: editState.edit.form.jenisKelamin || '', + alamat: editState.edit.form.alamat || '', + penyebab: editState.edit.form.penyebab || '', + }); - loadKelahiran(); - }, [params?.id]); - const handleSubmit = async () => { - try { - editState.edit.form = { - ...editState.edit.form, - nama: formData.nama, - tanggal: formData.tanggal, - jenisKelamin: formData.jenisKelamin, - alamat: formData.alamat, - penyebab: formData.penyebab, - }; - await editState.edit.update(); - toast.success('data kelahiran berhasil diperbarui!'); - router.push('/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran'); - } catch (error) { - console.error('Error updating data kelahiran:', error); - toast.error('Terjadi kesalahan saat memperbarui data kelahiran'); - } - }; + useEffect(() => { + const loadData = async () => { + const id = params?.id as string; + if (!id) return; - return ( - - - - - - - Edit data kelahiran - setFormData({ ...formData, nama: e.target.value })} - label={Nama} - placeholder="masukkan nama" - /> - setFormData({ ...formData, tanggal: e.target.value })} - label={Tanggal} - placeholder="masukkan tanggal" - /> - setFormData({ ...formData, jenisKelamin: e.target.value })} - label={Jenis Kelamin} - placeholder="masukkan jenis kelamin" - /> - setFormData({ ...formData, alamat: e.target.value })} - label={Alamat} - placeholder="masukkan alamat" - /> - - Penyebab - { - setFormData((prev) => ({ ...prev, penyebab: htmlContent })); - editState.edit.form.penyebab = htmlContent; - }} - /> - - - - - - ); + + try { + const data = await editState.edit.load(id); + if (data) { + setFormData({ + nama: data.nama || '', + tanggal: data.tanggal || '', + jenisKelamin: data.jenisKelamin || '', + alamat: data.alamat || '', + penyebab: data.penyebab || '', + }); + } + } catch (error) { + console.error('Error loading data kematian:', error); + toast.error('Gagal memuat data kematian'); + } + }; + + + loadData(); + }, [params?.id]); + + + const handleSubmit = async () => { + try { + editState.edit.form = { ...editState.edit.form, ...formData }; + await editState.edit.update(); + toast.success('Data kematian berhasil diperbarui!'); + router.push( + '/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian' + ); + } catch (error) { + console.error('Error updating data kematian:', error); + toast.error('Terjadi kesalahan saat memperbarui data kematian'); + } + }; + + + return ( + + {/* Header dengan tombol back */} + + + + + + Edit Data Kematian + + + + + {/* Card Form */} + + + setFormData({ ...formData, nama: e.target.value })} + required + /> + + + setFormData({ ...formData, tanggal: e.target.value })} + required + /> + + + setFormData({ ...formData, jenisKelamin: e.target.value })} + required + /> + + + setFormData({ ...formData, alamat: e.target.value })} + required + /> + + + + + Penyebab + + { + setFormData((prev) => ({ ...prev, penyebab: htmlContent })); + editState.edit.form.penyebab = htmlContent; + }} + /> + + + + + + + + + + ); } -export default EditKematian; + +export default EditKematian; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/page.tsx index b967f25a..1b5cb7a7 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/[id]/page.tsx @@ -1,11 +1,11 @@ 'use client' -import { useProxy } from 'valtio/utils'; - -import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { Box, Button, Group, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; + import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; @@ -13,114 +13,153 @@ import colors from '@/con/colors'; function DetailKematian() { - const state = useProxy(persentaseKelahiranKematian.kematian) - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) - const params = useParams() - const router = useRouter() + const state = useProxy(persentaseKelahiranKematian.kematian); + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); + const params = useParams(); + const router = useRouter(); - useShallowEffect(() => { - state.findUnique.load(params?.id as string) - }, []) - const handleHapus = () => { - if (selectedId) { - state.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran") - } - } + useShallowEffect(() => { + state.findUnique.load(params?.id as string); + }, []); - if (!state.findUnique.data) { - return ( - - - - ) - } - return ( - - - - - - - Detail Data Kematian - {state.findUnique.data ? ( - - - - Nama - {state.findUnique.data?.nama} - - - Tanggal - - {state.findUnique.data?.tanggal instanceof Date - ? state.findUnique.data.tanggal.toLocaleDateString('id-ID', { - day: '2-digit', - month: 'long', - year: 'numeric' - }) - : state.findUnique.data?.tanggal} - - - - Jenis Kelamin - {state.findUnique.data?.jenisKelamin} - - - Alamat - {state.findUnique.data?.alamat} - - - Penyebab - - - - - - - - - ) : null} - - + const handleHapus = () => { + if (selectedId) { + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push("/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran"); + } + }; - {/* Modal Konfirmasi Hapus */} - setModalHapus(false)} - onConfirm={handleHapus} - text='Apakah anda yakin ingin menghapus data ini?' - /> - - ); + + if (!state.findUnique.data) { + return ( + + + + ); + } + + + const data = state.findUnique.data; + + + return ( + + {/* Tombol kembali */} + + + + + + + Detail Data Kematian + + + + + + + Nama + {data?.nama || '-'} + + + + + Tanggal + + {data?.tanggal instanceof Date + ? data.tanggal.toLocaleDateString('id-ID', { + day: '2-digit', + month: 'long', + year: 'numeric' + }) + : data?.tanggal || '-'} + + + + + + Jenis Kelamin + {data?.jenisKelamin || '-'} + + + + + Alamat + {data?.alamat || '-'} + + + + + Penyebab + + + + + + + + + + + + + + + + + + + + + setModalHapus(false)} + onConfirm={handleHapus} + text="Apakah Anda yakin ingin menghapus data ini?" + /> + + ); } + export default DetailKematian; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/create/page.tsx index 310a7a6b..f9477dd5 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/create/page.tsx @@ -1,94 +1,146 @@ -'use client' +'use client'; import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor'; import persentaseKelahiranKematian from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Paper, + Stack, + TextInput, + Title, + Tooltip +} from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; +import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; + + + function CreateKematian() { - const createState = useProxy(persentaseKelahiranKematian.kematian) - const router = useRouter(); + const createState = useProxy(persentaseKelahiranKematian.kematian); + const router = useRouter(); - const resetForm = () => { - createState.create.form = { - nama: "", - tanggal: "", - jenisKelamin: "", - alamat: "", - penyebab: "", - }; - }; - const handleSubmit = async () => { - await createState.create.create(); - resetForm(); - router.push("/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian") - }; + const resetForm = () => { + createState.create.form = { + nama: '', + tanggal: '', + jenisKelamin: '', + alamat: '', + penyebab: '', + }; + }; - return ( - - - - - - - Create Kematian - Nama} - placeholder='Masukkan nama' - value={createState.create.form.nama} - onChange={(val) => { - createState.create.form.nama = val.target.value; - }} - /> - Tanggal} - placeholder='Masukkan tanggal' - value={createState.create.form.tanggal} - onChange={(val) => { - createState.create.form.tanggal = val.target.value; - }} - /> - Jenis Kelamin} - placeholder='Masukkan jenis kelamin' - value={createState.create.form.jenisKelamin} - onChange={(val) => { - createState.create.form.jenisKelamin = val.target.value; - }} - /> - Alamat} - placeholder='Masukkan alamat' - value={createState.create.form.alamat} - onChange={(val) => { - createState.create.form.alamat = val.target.value; - }} - /> - - Penyebab - { - createState.create.form.penyebab = htmlContent; - }} - /> - - - - - - - - ); + const handleSubmit = async () => { + if (!createState.create.form.nama) { + return toast.warn('Nama wajib diisi'); + } + if (!createState.create.form.tanggal) { + return toast.warn('Tanggal wajib diisi'); + } + + + await createState.create.create(); + resetForm(); + router.push( + '/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian' + ); + }; + + + return ( + + {/* Header */} + + + + + + Tambah Data Kematian + + + + + {/* Form Card */} + + + (createState.create.form.nama = e.target.value)} + required + /> + (createState.create.form.tanggal = e.target.value)} + required + /> + (createState.create.form.jenisKelamin = e.target.value)} + required + /> + (createState.create.form.alamat = e.target.value)} + required + /> + + + Penyebab + + { + createState.create.form.penyebab = htmlContent; + }} + /> + + + + + + + + + + ); } -export default CreateKematian; + +export default CreateKematian; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/page.tsx index c7d4df3e..5f8fdeaf 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian/page.tsx @@ -1,118 +1,191 @@ 'use client' import HeaderSearch from '@/app/admin/(dashboard)/_com/header'; -import JudulList from '@/app/admin/(dashboard)/_com/judulList'; import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; import colors from '@/con/colors'; -import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { + Box, + Button, + Center, + Group, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconSearch } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconPlus, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; function Kematian() { - const [search, setSearch] = useState(""); - const router = useRouter(); - return ( - - - - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - ); + const [search, setSearch] = useState(""); + const router = useRouter(); + + + return ( + + {/* Tombol Back */} + + + + + + {/* Header dengan Search */} + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + + + ); } + + function ListKematian({ search }: { search: string }) { - const statePersentase = useProxy(persentasekelahiran.kematian); - const router = useRouter(); + const statePersentase = useProxy(persentasekelahiran.kematian); + const router = useRouter(); - const { - data, - page, - totalPages, - loading, - load - } = statePersentase.findMany; + const { data, page, totalPages, loading, load } = statePersentase.findMany; - useShallowEffect(() => { - load(page, 10, search) - }, [search]) - const filteredData = data || [] + useShallowEffect(() => { + load(page, 10, search); + }, [page, search]); - if (loading || !data) { - return ( - - - - ) - } - return ( - - - {/* Form Input */} - - - - - - Nama - Tanggal - Jenis Kelamin - Alamat - Detail - - - - {filteredData.map((item) => ( - - {item.nama} - - {new Date(item.tanggal).toLocaleDateString('id-ID', { - day: '2-digit', - month: 'long', - year: 'numeric' - })} - - {item.jenisKelamin} - {item.alamat} - - - - - ))} - -
-
-
-
- load(newPage)} // ini penting! - total={totalPages} - mt="md" - mb="md" - /> -
-
- ); + const filteredData = data || []; + + + if (loading || !data) { + return ( + + + + ); + } + + + return ( + + + + Daftar Data Kematian + + + + + + + + + + + Nama + Tanggal + Jenis Kelamin + Alamat + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( + + + + {item.nama} + + + + {new Date(item.tanggal).toLocaleDateString('id-ID', { + day: '2-digit', + month: 'long', + year: 'numeric', + })} + + {item.jenisKelamin} + {item.alamat} + + + + + )) + ) : ( + + +
+ + Tidak ada data kematian yang cocok + +
+
+
+ )} +
+
+
+
+ + + {/* Pagination */} +
+ { + load(newPage, 10, search); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
+
+ ); } -export default Kematian; + +export default Kematian; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/page.tsx b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/page.tsx index 830c4caa..97f198bd 100644 --- a/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/page.tsx @@ -1,228 +1,277 @@ +/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable @typescript-eslint/no-explicit-any */ 'use client' import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran'; import colors from '@/con/colors'; -import { ActionIcon, Box, Center, Flex, Paper, Select, Skeleton, Stack, Table, Text, Title } from '@mantine/core'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import { ActionIcon, Badge, Box, Center, Flex, Tooltip as MantineTooltip, Paper, Select, Skeleton, Stack, Table, Text, Title } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; import { IconBabyCarriage, IconGrave2 } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; -import { Bar, BarChart, Legend, Tooltip, TooltipProps, XAxis, YAxis } from 'recharts'; +import { Bar, BarChart, Legend, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis } from 'recharts'; import { useProxy } from 'valtio/utils'; + type TooltipPayload = { - name: string; - value: number; - payload: any; - color: string; - dataKey: string; + name: string; + value: number; + payload: any; + color: string; + dataKey: string; }; + type CustomTooltipProps = TooltipProps & { - active?: boolean; - payload?: TooltipPayload[]; - label?: string; + active?: boolean; + payload?: TooltipPayload[]; + label?: string; }; + function PersentaseDataKelahiranKematian() { - return ( - - - - ); + return ( + + + + ); } + + function GrafikPersentaseKelahiranKematian() { - const router = useRouter(); - type DataTahunan = { - tahun: string; - totalKelahiran: number; - totalKematian: number; - data: Array<{ - id: string; - bulan: string; - kelahiran: number; - kematian: number; - }>; - }; + const router = useRouter(); - // Count occurrences per year - const countByYear = (data: any[], dateField: string) => { - const counts: Record = {}; - data?.forEach(item => { - const year = new Date(item[dateField]).getFullYear().toString(); - counts[year] = (counts[year] || 0) + 1; - }); - return counts; - }; - const statePersentase = useProxy(persentasekelahiran); - const [chartData, setChartData] = useState([]); - const isTablet = useMediaQuery('(max-width: 1024px)'); - const isMobile = useMediaQuery('(max-width: 768px)'); - const [selectedYear, setSelectedYear] = useState(null); + type DataTahunan = { + tahun: string; + totalKelahiran: number; + totalKematian: number; + data: Array<{ + id: string; + bulan: string; + kelahiran: number; + kematian: number; + }>; + }; - // Format number to Indonesian locale - const formatNumber = (num: number) => { - return new Intl.NumberFormat('id-ID').format(num); - }; - // Format tooltip - const CustomTooltip = ({ active, payload, label }: CustomTooltipProps) => { - if (active && payload && payload.length) { - return ( - - Tahun {label} - Kelahiran: {formatNumber(payload[0].value)} - Kematian: {formatNumber(payload[1].value)} - - ); - } - return null; - }; + // ✅ Fungsi hitung tahunan + bulanan + const countByYearAndMonth = (kelahiran: any[], kematian: any[]): DataTahunan[] => { + const dataTahunan: Record = {}; - useShallowEffect(() => { - statePersentase.kelahiran.findMany.load(1, 1000); // Load all kelahiran data - statePersentase.kematian.findMany.load(1, 1000); // Load all kematian data - }, []); - useEffect(() => { - if (statePersentase.kelahiran.findMany.data && statePersentase.kematian.findMany.data) { - // Count kelahiran and kematian by year - const kelahiranByYear = countByYear(statePersentase.kelahiran.findMany.data, 'tanggal'); - const kematianByYear = countByYear(statePersentase.kematian.findMany.data, 'tanggal'); + const namaBulan = [ + 'Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', + 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember' + ]; - // Get all unique years - const allYears = new Set([ - ...Object.keys(kelahiranByYear), - ...Object.keys(kematianByYear) - ]); - // Create data structure for the chart - const dataByYear = Array.from(allYears).reduce>((acc, year) => { - acc[year] = { - tahun: year, - totalKelahiran: kelahiranByYear[year] || 0, - totalKematian: kematianByYear[year] || 0, - data: [] - }; - return acc; - }, {}); + // Proses kelahiran + kelahiran?.forEach((item: any) => { + const date = new Date(item.tanggal); + const tahun = date.getFullYear().toString(); + const bulanIndex = date.getMonth(); - const sortedData = Object.values(dataByYear).sort((a, b) => - parseInt(a.tahun) - parseInt(b.tahun) - ); - setChartData(sortedData); - setSelectedYear(sortedData[0]?.tahun || ''); - } - }, [ - statePersentase.kelahiran.findMany.data, - statePersentase.kematian.findMany.data, - ]); + if (!dataTahunan[tahun]) { + dataTahunan[tahun] = { + tahun, + totalKelahiran: 0, + totalKematian: 0, + data: namaBulan.map((nama, idx) => ({ + id: `${tahun}-${idx + 1}`, + bulan: nama, + kelahiran: 0, + kematian: 0 + })) + }; + } - if (!statePersentase.kelahiran.findMany.data || !statePersentase.kematian.findMany.data) { - return ( - - - - ); - } - const selectedYearData = chartData.find(d => d.tahun === selectedYear); + dataTahunan[tahun].totalKelahiran += 1; + dataTahunan[tahun].data[bulanIndex].kelahiran += 1; + }); - return ( - - - Statistik Kelahiran & Kematian - - - - router.push('/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran')}> - - - - - router.push('/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian')} > - - - - - - - {chartData.length === 0 ? ( - - Belum ada data yang tersedia untuk ditampilkan - - ) : ( - <> - {/* Year Selector */} - - ({ value: item.tahun, label: item.tahun }))} + value={selectedYear} + onChange={(value) => setSelectedYear(value || null)} + size="sm" + radius="md" + /> + + + + + + + + + } /> + + + + + + + + + {selectedYearData && ( + + + Rincian Tahun {selectedYear} + {formatNumber(selectedYearData.totalKelahiran)} kelahiran + {formatNumber(selectedYearData.totalKematian)} kematian + + + + + Bulan + Kelahiran + Kematian + + + + {selectedYearData.data.length > 0 ? ( + <> + {selectedYearData.data.map((item) => ( + + {item.bulan} + {formatNumber(item.kelahiran)} + {formatNumber(item.kematian)} + + ))} + + Total + {formatNumber(selectedYearData.totalKelahiran)} + {formatNumber(selectedYearData.totalKematian)} + + + ) : ( + + Tidak ada rincian bulanan + + )} + +
+
+ )} + + )} + +
+ ); } -export default PersentaseDataKelahiranKematian; + +export default PersentaseDataKelahiranKematian; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/edit/page.tsx index 327f47a9..a46119b1 100644 --- a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/edit/page.tsx @@ -4,7 +4,18 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import infoWabahPenyakit from '@/app/admin/(dashboard)/_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; @@ -12,11 +23,10 @@ import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; - function EditInfoWabahPenyakit() { - const infoWabahPenyakitState = useProxy(infoWabahPenyakit) + const infoWabahPenyakitState = useProxy(infoWabahPenyakit); const router = useRouter(); - const params = useParams() + const params = useParams(); const [previewImage, setPreviewImage] = useState(null); const [file, setFile] = useState(null); @@ -25,7 +35,7 @@ function EditInfoWabahPenyakit() { deskripsiSingkat: infoWabahPenyakitState.edit.form.deskripsiSingkat || '', deskripsi: infoWabahPenyakitState.edit.form.deskripsiLengkap || '', imageId: infoWabahPenyakitState.edit.form.imageId || '', - }) + }); useEffect(() => { const loadInfoWabahPenyakit = async () => { @@ -47,8 +57,8 @@ function EditInfoWabahPenyakit() { } } } catch (error) { - console.error("Error loading program kesehatan:", error); - toast.error("Gagal memuat data program kesehatan"); + console.error('Error loading info wabah penyakit:', error); + toast.error('Gagal memuat data info wabah penyakit'); } }; @@ -70,115 +80,143 @@ function EditInfoWabahPenyakit() { const uploaded = res.data?.data; if (!uploaded?.id) { - return toast.error("Gagal upload gambar"); + return toast.error('Gagal upload gambar'); } infoWabahPenyakitState.edit.form.imageId = uploaded.id; } await infoWabahPenyakitState.edit.update(); - toast.success("Info wabah penyakit berhasil diperbarui!"); - router.push("/admin/kesehatan/info-wabah-penyakit"); + toast.success('Info wabah penyakit berhasil diperbarui!'); + router.push('/admin/kesehatan/info-wabah-penyakit'); } catch (error) { - console.error("Error updating info wabah penyakit:", error); - toast.error("Gagal memuat data info wabah penyakit"); + console.error('Error updating info wabah penyakit:', error); + toast.error('Terjadi kesalahan saat memperbarui info wabah penyakit'); } - } + }; + return ( - - - - - - - - Edit Info Wabah Penyakit - setFormData({ ...formData, name: e.target.value })} - label={Judul} - placeholder="masukkan judul" - /> + + {/* Header */} + + + + + + Edit Info Wabah Penyakit + + - setFormData({ ...formData, deskripsiSingkat: e.target.value })} - label={Deskripsi Singkat} - placeholder="masukkan deskripsi" - /> + {/* Form */} + + + setFormData({ ...formData, name: e.target.value })} + label="Judul" + placeholder="Masukkan judul" + required + /> - - Deskripsi - setFormData({ ...formData, deskripsi: val })} - /> - - - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } + setFormData({ ...formData, deskripsiSingkat: e.target.value })} + label="Deskripsi Singkat" + placeholder="Masukkan deskripsi singkat" + required + /> + + + + Deskripsi + + setFormData({ ...formData, deskripsi: val })} + /> + + + + + Gambar + + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + + + + + + + + + + + +
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+
+
+ + {previewImage && ( + + Preview toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - - -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
- - {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} - + />
-
- -
-
-
-
+ + + + ); } diff --git a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/page.tsx index fc86ef6b..4e5219ae 100644 --- a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/[id]/page.tsx @@ -1,7 +1,17 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Flex, Text, Image, Skeleton } from '@mantine/core'; -import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react'; +import { + Box, + Button, + Paper, + Stack, + Text, + Skeleton, + Tooltip, + Group, + Image, +} from '@mantine/core'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import React, { useState } from 'react'; import infoWabahPenyakit from '../../../_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit'; @@ -10,86 +20,135 @@ import { useShallowEffect } from '@mantine/hooks'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; function DetailInfoWabahPenyakit() { - const infoWabahPenyakitState = useProxy(infoWabahPenyakit) - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) + const state = useProxy(infoWabahPenyakit); + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); const router = useRouter(); - const params = useParams() + const params = useParams(); useShallowEffect(() => { - infoWabahPenyakitState.findUnique.load(params?.id as string) - }, []) + state.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - infoWabahPenyakitState.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/info-wabah-penyakit") + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push('/admin/kesehatan/info-wabah-penyakit'); } - } + }; - if (!infoWabahPenyakitState.findUnique.data) { + if (!state.findUnique.data) { return ( - + - ) + ); } + const data = state.findUnique.data; + return ( - - - - - - - Detail Info Wabah Penyakit - {infoWabahPenyakitState.findUnique.data ? ( - - - - Judul - {infoWabahPenyakitState.findUnique.data.name} - - - Deskripsi Singkat - {infoWabahPenyakitState.findUnique.data.deskripsiSingkat} - - - Deskripsi - - - - Gambar - gambar - - - - + + {/* Wrapper Detail */} + + + + Detail Info Wabah Penyakit + + + + + + Judul + {data.name || '-'} + + + + Deskripsi Singkat + {data.deskripsiSingkat || '-'} + + + + Deskripsi Lengkap + + + + + Gambar + {data.image?.link ? ( + gambar wabah + ) : ( + - + )} + + + {/* Aksi */} + + + - - - - - - ) : null} + variant="light" + radius="md" + size="md" + > + + + + + + + + + + - {/* Modal Hapus */} + {/* Modal Konfirmasi Hapus */} setModalHapus(false)} diff --git a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/create/page.tsx index 4fbc8104..5fffba47 100644 --- a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/create/page.tsx @@ -1,7 +1,18 @@ 'use client' import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; @@ -18,15 +29,12 @@ function CreateInfoWabahPenyakit() { const [file, setFile] = useState(null); const resetForm = () => { - // Reset state di valtio infoWabahPenyakitState.create.form = { name: "", deskripsiSingkat: "", deskripsiLengkap: "", imageId: "", }; - - // Reset state lokal setPreviewImage(null); setFile(null); }; @@ -36,7 +44,6 @@ function CreateInfoWabahPenyakit() { return toast.warn("Pilih file gambar terlebih dahulu"); } - // Upload gambar dulu const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name, @@ -47,34 +54,50 @@ function CreateInfoWabahPenyakit() { return toast.error("Gagal upload gambar"); } - // Simpan ID gambar ke form infoWabahPenyakitState.create.form.imageId = uploaded.id; - - // Submit data berita await infoWabahPenyakitState.create.create(); - // Reset form setelah submit resetForm(); router.push("/admin/kesehatan/info-wabah-penyakit") }; return ( - - - - - - - Create Info Wabah Penyakit + + {/* Header */} + + + + + + Tambah Info Wabah Penyakit + + + + {/* Form */} + + { infoWabahPenyakitState.create.form.name = val.target.value; }} label={Judul} - placeholder="masukkan judul" + placeholder="Masukkan judul" + required /> Deskripsi Singkat} - placeholder="masukkan deskripsi" + placeholder="Masukkan deskripsi singkat" + required /> @@ -95,65 +119,74 @@ function CreateInfoWabahPenyakit() { }} /> + - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } - }} - onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - + Gambar + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + + + + + + + + + + -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
+
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+
+
- {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} - -
+ {previewImage && ( + + Preview + + )}
- + + + +
diff --git a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/page.tsx b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/page.tsx index 05f8f972..dd37f3f3 100644 --- a/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/info-wabah-penyakit/page.tsx @@ -1,8 +1,26 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; -import JudulList from '../../_com/judulList'; +import { + Box, + Button, + Center, + Image, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, + Group, +} from '@mantine/core'; +import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import HeaderSearch from '../../_com/header'; import { useRouter } from 'next/navigation'; import { useProxy } from 'valtio/utils'; @@ -14,9 +32,10 @@ function InfoWabahPenyakit() { const [search, setSearch] = useState(""); return ( + {/* Header Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} @@ -46,64 +65,99 @@ function ListInfoWabahPenyakit({ search }: { search: string }) { if (loading || !data) { return ( - - - + + + ) } + return ( - - - - - - - - Judul - Deskripsi Singkat - Image - Detail - - - - {filteredData.map((item) => ( + + {/* Judul + Tombol Tambah */} + + Daftar Info Wabah Penyakit + + + + + + {/* Tabel */} + +
+ + + Judul + Deskripsi Singkat + Image + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( - - {item.name} - + + {item.name} + - - {item.deskripsiSingkat} - + + {item.deskripsiSingkat} + - + - - ))} - -
-
-
+ )) + ) : ( + + +
+ + Tidak ada data info wabah penyakit yang cocok + +
+
+
+ )} + + +
+ + {/* Pagination */}
load(newPage)} // ini penting! + onChange={(newPage) => { + load(newPage, 10) + window.scrollTo({ top: 0, behavior: 'smooth' }) + }} total={totalPages} mt="md" mb="md" + color="blue" + radius="md" />
diff --git a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/edit/page.tsx index d2927838..6cdcaad3 100644 --- a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/edit/page.tsx @@ -4,7 +4,18 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import kontakDarurat from '@/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; @@ -13,11 +24,10 @@ import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; - function EditKontakDarurat() { - const kontakDaruratState = useProxy(kontakDarurat) + const kontakDaruratState = useProxy(kontakDarurat); const router = useRouter(); - const params = useParams() + const params = useParams(); const [previewImage, setPreviewImage] = useState(null); const [file, setFile] = useState(null); @@ -25,10 +35,10 @@ function EditKontakDarurat() { name: kontakDaruratState.edit.form.name || '', deskripsi: kontakDaruratState.edit.form.deskripsi || '', imageId: kontakDaruratState.edit.form.imageId || '', - }) + }); useEffect(() => { - const loadProgramKesehatan = async () => { + const loadKontakDarurat = async () => { const id = params?.id as string; if (!id) return; @@ -46,12 +56,12 @@ function EditKontakDarurat() { } } } catch (error) { - console.error("Error loading program kesehatan:", error); - toast.error("Gagal memuat data program kesehatan"); + console.error("Error loading kontak darurat:", error); + toast.error("Gagal memuat data kontak darurat"); } }; - loadProgramKesehatan(); + loadKontakDarurat(); }, [params?.id]); const handleSubmit = async () => { @@ -79,95 +89,116 @@ function EditKontakDarurat() { router.push("/admin/kesehatan/kontak-darurat"); } catch (error) { console.error("Error updating kontak darurat:", error); - toast.error("Gagal memuat data kontak darurat"); + toast.error("Terjadi kesalahan saat memperbarui kontak darurat"); } - } + }; + return ( - - - - - - - - Edit Kontak Darurat - setFormData({ ...formData, name: e.target.value })} - label={Judul} - placeholder="masukkan judul" + + {/* Header */} + + + + + + Edit Kontak Darurat + + + + {/* Form */} + + + setFormData({ ...formData, name: e.target.value })} + label="Judul" + placeholder="Masukkan judul" + required + /> + + + Deskripsi + setFormData({ ...formData, deskripsi: val })} /> - - Deskripsi - setFormData({ ...formData, deskripsi: val })} - /> - - - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } + + + + Gambar + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + + + + + + + + + + + +
+ Drag gambar ke sini atau klik untuk pilih file + Maksimal 5MB dan format gambar +
+
+
+ + {previewImage && ( + + Preview toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - - -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
- - - {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} + />
-
- -
-
-
-
+ + + +
); } diff --git a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/page.tsx index cec338fa..d45e8f86 100644 --- a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/[id]/page.tsx @@ -1,8 +1,8 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { Box, Button, Group, Image, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; @@ -10,82 +10,129 @@ import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; import kontakDarurat from '../../../_state/kesehatan/kontak-darurat/kontakDarurat'; function DetailKontakDarurat() { - const kontakDaruratState = useProxy(kontakDarurat) - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) + const state = useProxy(kontakDarurat); + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); const router = useRouter(); - const params = useParams() + const params = useParams(); useShallowEffect(() => { - kontakDaruratState.findUnique.load(params?.id as string) - }, []) + state.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - kontakDaruratState.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/kontak-darurat") + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push("/admin/kesehatan/kontak-darurat"); } - } + }; - if (!kontakDaruratState.findUnique.data) { + if (!state.findUnique.data) { return ( - + - ) + ); } + const data = state.findUnique.data; + return ( - - - - - - - Detail Kontak Darurat - {kontakDaruratState.findUnique.data ? ( - - - - Judul - {kontakDaruratState.findUnique.data.name} - - - Deskripsi - - - - Gambar - gambar - - - - + + {/* Wrapper Detail */} + + + + Detail Kontak Darurat + + + + + + Judul + {data.name || '-'} + + + + Deskripsi + + + + + Gambar + {data.image?.link ? ( + gambar + ) : ( + - + )} + + + {/* Aksi */} + + + - - - - - - ) : null} + variant="light" + radius="md" + size="md" + disabled={state.delete.loading} + > + + + + + + + + + + - {/* Modal Hapus */} + {/* Modal Konfirmasi Hapus */} setModalHapus(false)} diff --git a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/create/page.tsx index e318dee1..a86e23ce 100644 --- a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/create/page.tsx @@ -1,8 +1,24 @@ 'use client' import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; -import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; +import { + IconArrowBack, + IconPhoto, + IconUpload, + IconX, +} from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { toast } from 'react-toastify'; @@ -11,18 +27,17 @@ import CreateEditor from '../../../_com/createEditor'; import kontakDarurat from '../../../_state/kesehatan/kontak-darurat/kontakDarurat'; import { Dropzone } from '@mantine/dropzone'; - function CreateKontakDarurat() { const router = useRouter(); - const kontakDaruratState = useProxy(kontakDarurat) + const kontakDaruratState = useProxy(kontakDarurat); const [previewImage, setPreviewImage] = useState(null); const [file, setFile] = useState(null); const resetForm = () => { kontakDaruratState.create.form = { - name: "", - deskripsi: "", - imageId: "", + name: '', + deskripsi: '', + imageId: '', }; setPreviewImage(null); setFile(null); @@ -30,7 +45,7 @@ function CreateKontakDarurat() { const handleSubmit = async () => { if (!file) { - return toast.warn("Pilih file gambar terlebih dahulu"); + return toast.warn('Pilih file gambar terlebih dahulu'); } const res = await ApiFetch.api.fileStorage.create.post({ @@ -40,7 +55,7 @@ function CreateKontakDarurat() { const uploaded = res.data?.data; if (!uploaded?.id) { - return toast.error("Gagal upload gambar"); + return toast.error('Gagal upload gambar'); } kontakDaruratState.create.form.imageId = uploaded.id; @@ -48,27 +63,46 @@ function CreateKontakDarurat() { await kontakDaruratState.create.create(); resetForm(); - router.push("/admin/kesehatan/kontak-darurat") - } + router.push('/admin/kesehatan/kontak-darurat'); + }; + return ( - - - - - - - - Create Kontak Darurat + + {/* Header */} + + + + + + Tambah Kontak Darurat + + + {/* Form */} + + { kontakDaruratState.create.form.name = val.target.value; }} label={Judul} - placeholder="masukkan judul" + placeholder="Masukkan judul" + required /> @@ -80,64 +114,91 @@ function CreateKontakDarurat() { }} /> + - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } - }} - onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} + Gambar + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + - - - - - - - - - - - -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
- - {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview + - - )} -
+ + + + + + + + +
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+ + + + {previewImage && ( + + Preview + + )}
- + + + +
diff --git a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/page.tsx b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/page.tsx index 513f0311..1716f8fe 100644 --- a/src/app/admin/(dashboard)/kesehatan/kontak-darurat/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/kontak-darurat/page.tsx @@ -1,26 +1,46 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; -import JudulList from '../../_com/judulList'; -import HeaderSearch from '../../_com/header'; -import { useRouter } from 'next/navigation'; -import { useProxy } from 'valtio/utils'; -import kontakDarurat from '../../_state/kesehatan/kontak-darurat/kontakDarurat'; +import { + Box, + Button, + Center, + Image, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; +import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../_com/header'; +import kontakDarurat from '../../_state/kesehatan/kontak-darurat/kontakDarurat'; function KontakDarurat() { const [search, setSearch] = useState(""); + return ( + {/* Header Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> + ); @@ -30,13 +50,7 @@ function ListKontakDarurat({ search }: { search: string }) { const kontakDaruratState = useProxy(kontakDarurat) const router = useRouter(); - const { - data, - page, - totalPages, - loading, - load, - } = kontakDaruratState.findMany; + const { data, page, totalPages, loading, load } = kontakDaruratState.findMany; useShallowEffect(() => { load(page, 10, search) @@ -46,65 +60,97 @@ function ListKontakDarurat({ search }: { search: string }) { if (loading || !data) { return ( - - - + + + ) } return ( - - - - - - - - Judul - Deskripsi - Image - Detail - - - - {filteredData.map((item) => ( + + {/* Judul + Tombol Tambah */} + + + Daftar Kontak Darurat + + + + + + + {/* Tabel */} + +
+ + + Judul + Deskripsi + Image + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( - - {item.name} - + + {item.name} + - - - + - + - - ))} - -
-
-
+ )) + ) : ( + + +
+ Tidak ada data kontak darurat yang cocok +
+
+
+ )} + + +
+ + {/* Pagination */}
load(newPage)} // ini penting! + onChange={(newPage) => { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} total={totalPages} mt="md" mb="md" + color="blue" + radius="md" />
diff --git a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/edit/page.tsx index 911fdaf2..6f073233 100644 --- a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/edit/page.tsx @@ -4,7 +4,18 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; @@ -77,97 +88,120 @@ function EditPenangananDarurat() { router.push("/admin/kesehatan/penanganan-darurat"); } catch (error) { console.error("Error updating penanganan darurat:", error); - toast.error("Gagal memuat data penanganan darurat"); + toast.error("Gagal memperbarui data penanganan darurat"); } } + return ( - - - - - - - - Edit Penanganan Darurat + + {/* Header */} + + + + + + Edit Penanganan Darurat + + - setFormData({ ...formData, name: e.target.value })} - label={Judul} - placeholder="masukkan judul" + {/* Form */} + + + setFormData({ ...formData, name: e.target.value })} + label="Judul" + placeholder="Masukkan judul" + required + /> + + + Deskripsi + setFormData({ ...formData, deskripsi: val })} /> + - - Deskripsi - setFormData({ ...formData, deskripsi: val })} - /> - - - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } + + Gambar + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + + + + + + + + + + + +
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+
+
+ + {previewImage && ( + + Preview toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - - -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
- - {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} + />
-
- -
-
-
-
+ + + +
); } diff --git a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/page.tsx index 46615005..18661c20 100644 --- a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/[id]/page.tsx @@ -1,93 +1,134 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Flex, Text, Image } from '@mantine/core'; -import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; +import { Box, Button, Group, Paper, Skeleton, Stack, Text, Tooltip, Image } from '@mantine/core'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; +import { useRouter, useParams } from 'next/navigation'; import React, { useState } from 'react'; -// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; -import penangananDarurat from '../../../_state/kesehatan/penanganan-darurat/penangananDarurat'; import { useProxy } from 'valtio/utils'; import { useShallowEffect } from '@mantine/hooks'; -import { useParams } from 'next/navigation'; -import { Skeleton } from '@mantine/core'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; +import penangananDarurat from '../../../_state/kesehatan/penanganan-darurat/penangananDarurat'; function DetailPenangananDarurat() { - const penangananDaruratState = useProxy(penangananDarurat) - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) + const state = useProxy(penangananDarurat); + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); const router = useRouter(); - const params = useParams() + const params = useParams(); useShallowEffect(() => { - penangananDaruratState.findUnique.load(params?.id as string) - }, []) + state.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - penangananDaruratState.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/penanganan-darurat") + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push("/admin/kesehatan/penanganan-darurat"); } + }; + + if (!state.findUnique.data) { + return ( + + + + ); } - if (!penangananDaruratState.findUnique.data) { - return ( - - - - ) - } + const data = state.findUnique.data; return ( - - - - - - - Detail Penanganan Darurat - {penangananDaruratState.findUnique.data ? ( - - - - Nama Penanganan Darurat - {penangananDaruratState.findUnique.data.name} - - - Deskripsi - - - - Gambar - gambar - - - - + + {/* Wrapper Detail */} + + + + Detail Penanganan Darurat + + + + + + Nama Penanganan Darurat + {data.name || '-'} + + + + Deskripsi + + + + + Gambar + gambar penanganan darurat + + + {/* Aksi */} + + + - - - - - - ) : null} + variant="light" + radius="md" + size="md" + > + + + + + + + + + + - {/* Modal Hapus */} + {/* Modal Konfirmasi Hapus */} setModalHapus(false)} diff --git a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/create/page.tsx index a712dba6..3a7a832d 100644 --- a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/create/page.tsx @@ -1,9 +1,25 @@ 'use client' import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; -import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; +import { + IconArrowBack, + IconPhoto, + IconUpload, + IconX, +} from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { toast } from 'react-toastify'; @@ -11,18 +27,17 @@ import { useProxy } from 'valtio/utils'; import CreateEditor from '../../../_com/createEditor'; import penangananDarurat from '../../../_state/kesehatan/penanganan-darurat/penangananDarurat'; - function CreatePenangananDarurat() { const router = useRouter(); - const penangananDaruratState = useProxy(penangananDarurat) + const penangananDaruratState = useProxy(penangananDarurat); const [previewImage, setPreviewImage] = useState(null); const [file, setFile] = useState(null); const resetForm = () => { penangananDaruratState.create.form = { - name: "", - deskripsi: "", - imageId: "", + name: '', + deskripsi: '', + imageId: '', }; setPreviewImage(null); setFile(null); @@ -30,7 +45,7 @@ function CreatePenangananDarurat() { const handleSubmit = async () => { if (!file) { - return toast.warn("Pilih file gambar terlebih dahulu"); + return toast.warn('Pilih file gambar terlebih dahulu'); } const res = await ApiFetch.api.fileStorage.create.post({ @@ -40,7 +55,7 @@ function CreatePenangananDarurat() { const uploaded = res.data?.data; if (!uploaded?.id) { - return toast.error("Gagal upload gambar"); + return toast.error('Gagal upload gambar'); } penangananDaruratState.create.form.imageId = uploaded.id; @@ -48,31 +63,52 @@ function CreatePenangananDarurat() { await penangananDaruratState.create.create(); resetForm(); - router.push("/admin/kesehatan/penanganan-darurat") - } + router.push('/admin/kesehatan/penanganan-darurat'); + }; + return ( - - - - - - - - Create Penanganan Darurat + + {/* Header */} + + + + + + Tambah Penanganan Darurat + + + {/* Form */} + + + {/* Judul */} Judul} + placeholder="Masukkan judul" value={penangananDaruratState.create.form.name} onChange={(val) => { penangananDaruratState.create.form.name = val.target.value; }} - label={Judul} - placeholder="masukkan judul" + required /> + {/* Deskripsi */} - Deskripsi + Deskripsi { @@ -80,30 +116,49 @@ function CreatePenangananDarurat() { }} /> + + {/* Upload Gambar */} - Gambar + Gambar { - const selectedFile = files[0]; // Ambil file pertama + const selectedFile = files[0]; if (selectedFile) { setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview + setPreviewImage(URL.createObjectURL(selectedFile)); } }} onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB + maxSize={5 * 1024 ** 2} accept={{ 'image/*': [] }} > - + - + - + - +
@@ -117,7 +172,6 @@ function CreatePenangananDarurat() { - {/* Tampilkan preview kalau ada */} {previewImage && ( )} - - + + {/* Button Simpan */} + + + diff --git a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/page.tsx b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/page.tsx index 40817204..92eefc86 100644 --- a/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/penanganan-darurat/page.tsx @@ -1,8 +1,26 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; -import JudulList from '../../_com/judulList'; +import { + Box, + Button, + Center, + Group, + Image, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; +import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import HeaderSearch from '../../_com/header'; import { useRouter } from 'next/navigation'; import { useProxy } from 'valtio/utils'; @@ -14,100 +32,135 @@ function PenangananDarurat() { const [search, setSearch] = useState(""); return ( + {/* Header Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> + ); } function ListPenangananDarurat({ search }: { search: string }) { - const penangananDaruratState = useProxy(penangananDarurat) + const state = useProxy(penangananDarurat); const router = useRouter(); - const { - data, - page, - totalPages, - loading, - load, - } = penangananDaruratState.findMany; + const { data, page, totalPages, loading, load } = state.findMany; useShallowEffect(() => { - load(page, 10, search) - }, [page, search]) + load(page, 10, search); + }, [page, search]); - const filteredData = data || [] + const filteredData = data || []; if (loading || !data) { return ( - - - - ) + + + + ); } return ( - - - - - - - - Judul - Deskripsi - Image - Detail - - - - {filteredData.map((item) => ( + + {/* Judul + Tombol Tambah */} + + Daftar Penanganan Darurat + + + + + + {/* Tabel */} + +
+ + + Judul + Deskripsi + Gambar + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( - - {item.name} - + + {item.name} + + - - - + - - ))} - -
-
-
+ )) + ) : ( + + +
+ Tidak ada data penanganan darurat +
+
+
+ )} + + +
+ + {/* Pagination */}
load(newPage)} // ini penting! + onChange={(newPage) => { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} total={totalPages} mt="md" mb="md" + color="blue" + radius="md" />
- ) + ); } export default PenangananDarurat; diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx index a6861418..cb161f32 100644 --- a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx @@ -4,7 +4,18 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import posyandustate from '@/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; @@ -14,185 +25,231 @@ import { useProxy } from 'valtio/utils'; function EditPosyandu() { - const statePosyandu = useProxy(posyandustate) - const router = useRouter(); - const params = useParams() + const statePosyandu = useProxy(posyandustate); + const router = useRouter(); + const params = useParams(); - const [previewImage, setPreviewImage] = useState(null); - const [file, setFile] = useState(null); - const [formData, setFormData] = useState({ - name: statePosyandu.edit.form.name || '', - nomor: statePosyandu.edit.form.nomor || '', - deskripsi: statePosyandu.edit.form.deskripsi || '', - imageId: statePosyandu.edit.form.imageId || '', - jadwalPelayanan: statePosyandu.edit.form.jadwalPelayanan || '', - }); - useEffect(() => { - const loadPosyandu = async () => { - const id = params?.id as string; - if (!id) return; + const [previewImage, setPreviewImage] = useState(null); + const [file, setFile] = useState(null); + const [formData, setFormData] = useState({ + name: statePosyandu.edit.form.name || '', + nomor: statePosyandu.edit.form.nomor || '', + deskripsi: statePosyandu.edit.form.deskripsi || '', + imageId: statePosyandu.edit.form.imageId || '', + jadwalPelayanan: statePosyandu.edit.form.jadwalPelayanan || '', + }); - try { - const data = await statePosyandu.edit.load(id); - if (data) { - setFormData({ - name: data.name || '', - nomor: data.nomor || '', - deskripsi: data.deskripsi || '', - imageId: data.imageId || '', - jadwalPelayanan: data.jadwalPelayanan || '', - }); - if (data?.image?.link) { - setPreviewImage(data.image.link); - } - } - } catch (error) { - console.error("Error loading posyandu:", error); - toast.error("Gagal memuat data posyandu"); - } - } - loadPosyandu(); - }, [params?.id]) + useEffect(() => { + const loadPosyandu = async () => { + const id = params?.id as string; + if (!id) return; - const handleSubmit = async () => { - try { - statePosyandu.edit.form = { - ...statePosyandu.edit.form, - name: formData.name, - nomor: formData.nomor, - deskripsi: formData.deskripsi, - imageId: formData.imageId, - jadwalPelayanan: formData.jadwalPelayanan, - } - if (file) { - const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name }); - const uploaded = res.data?.data; + try { + const data = await statePosyandu.edit.load(id); + if (data) { + setFormData({ + name: data.name || '', + nomor: data.nomor || '', + deskripsi: data.deskripsi || '', + imageId: data.imageId || '', + jadwalPelayanan: data.jadwalPelayanan || '', + }); - if (!uploaded?.id) { - return toast.error("Gagal upload gambar"); - } - statePosyandu.edit.form.imageId = uploaded.id; - } + if (data?.image?.link) { + setPreviewImage(data.image.link); + } + } + } catch (error) { + console.error("Error loading posyandu:", error); + toast.error("Gagal memuat data posyandu"); + } + }; + loadPosyandu(); + }, [params?.id]); - await statePosyandu.edit.update(); - toast.success("Posyandu berhasil diperbarui!"); - router.push("/admin/kesehatan/posyandu"); - } catch (error) { - console.error("Error updating posyandu:", error); - toast.error("Gagal memuat data posyandu"); - } - } - return ( - - - - + const handleSubmit = async () => { + try { + statePosyandu.edit.form = { + ...statePosyandu.edit.form, + ...formData, + }; - - - Edit Posyandu - - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } - }} - onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
+ if (file) { + const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name }); + const uploaded = res.data?.data; - {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} -
-
- setFormData({ ...formData, name: e.target.value })} - label={Nama Posyandu} - placeholder='Masukkan nama posyandu' - /> - setFormData({ ...formData, nomor: e.target.value })} - label={Nomor Posyandu} - placeholder='Masukkan nomor posyandu' - /> - - Deskripsi Posyandu - { - setFormData({ ...formData, deskripsi: htmlContent }); - statePosyandu.edit.form.deskripsi = htmlContent; - }} - /> - - - Jadwal Pelayanan - { - setFormData({ ...formData, jadwalPelayanan: htmlContent }); - statePosyandu.edit.form.jadwalPelayanan = htmlContent; - }} - /> - - - - -
-
-
- ); + if (!uploaded?.id) { + return toast.error("Gagal upload gambar"); + } + + + statePosyandu.edit.form.imageId = uploaded.id; + } + + + await statePosyandu.edit.update(); + toast.success("Posyandu berhasil diperbarui!"); + router.push("/admin/kesehatan/posyandu"); + } catch (error) { + console.error("Error updating posyandu:", error); + toast.error("Terjadi kesalahan saat memperbarui posyandu"); + } + }; + + + return ( + + {/* Tombol Back */} + + + + + + Edit Posyandu + + + + + {/* Card utama */} + + + {/* Upload Gambar */} + + + Gambar Posyandu + + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid, gunakan format gambar')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + radius="md" + p="xl" + > + + + + + + + + + + + + + Seret gambar atau klik untuk memilih file + + + Maksimal 5MB, format gambar wajib + + + + + + + {previewImage && ( + + Preview Gambar + + )} + + + + {/* Input Form */} + setFormData({ ...formData, name: e.target.value })} + required + /> + + + setFormData({ ...formData, nomor: e.target.value })} + required + /> + + + + Deskripsi Posyandu + { + setFormData({ ...formData, deskripsi: htmlContent }); + statePosyandu.edit.form.deskripsi = htmlContent; + }} + /> + + + + + Jadwal Pelayanan + { + setFormData({ ...formData, jadwalPelayanan: htmlContent }); + statePosyandu.edit.form.jadwalPelayanan = htmlContent; + }} + /> + + + + {/* Tombol Submit */} + + + + + + + ); } -export default EditPosyandu; + +export default EditPosyandu; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx index 84f10b68..b47efa34 100644 --- a/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/posyandu/[id]/page.tsx @@ -1,105 +1,175 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Flex, Text, Image, Skeleton } from '@mantine/core'; -import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react'; +import { Box, Button, Paper, Stack, Text, Image, Skeleton, Group, Tooltip } from '@mantine/core'; +import { IconArrowBack, IconTrash, IconEdit } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import React, { useState } from 'react'; import { useProxy } from 'valtio/utils'; -import posyandustate from '../../../_state/kesehatan/posyandu/posyandu'; +import posyanduState from '../../../_state/kesehatan/posyandu/posyandu'; import { useShallowEffect } from '@mantine/hooks'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; -// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; + function DetailPosyandu() { - const statePosyandu = useProxy(posyandustate) - const params = useParams() - const router = useRouter(); - const [modalHapus, setModalHapus] = useState(false); - const [selectedId, setSelectedId] = useState(null) + const statePosyandu = useProxy(posyanduState); + const params = useParams(); + const router = useRouter(); + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); - useShallowEffect(() => { - statePosyandu.findUnique.load(params?.id as string) - }, []) - const handleHapus = () => { - if (selectedId) { - statePosyandu.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/posyandu") - } - } + useShallowEffect(() => { + statePosyandu.findUnique.load(params?.id as string); + }, []); - if (!statePosyandu.findUnique.data) { - return ( - - - - ) - } - return ( - - - - - - - Detail Posyandu - {statePosyandu.findUnique.data ? ( - - - - Nama Posyandu - {statePosyandu.findUnique.data.name} - - - Nomor Posyandu - {statePosyandu.findUnique.data.nomor} - - - Deskripsi Posyandu - - - - Jadwal Pelayanan - - - - Gambar - gambar - - - - - - - - - - ) : null} - - + const handleHapus = () => { + if (selectedId) { + statePosyandu.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push("/admin/kesehatan/posyandu"); + } + }; - setModalHapus(false)} - onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus posyandu ini?" - /> - - ); + + if (!statePosyandu.findUnique.data) { + return ( + + + + ); + } + + + const data = statePosyandu.findUnique.data; + + + return ( + + {/* Tombol kembali */} + + + + {/* Card utama */} + + + + Detail Posyandu + + + + + + + Nama Posyandu + {data.name || '-'} + + + + + Nomor Posyandu + {data.nomor || '-'} + + + + + Deskripsi + + + + + + Jadwal Pelayanan + + + + + + Gambar + {data.image?.link ? ( + {data.name + ) : ( + Tidak ada gambar + )} + + + + {/* Aksi */} + + + + + + + + + + + + + + + + + {/* Modal konfirmasi hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text="Apakah Anda yakin ingin menghapus posyandu ini?" + /> + + ); } -export default DetailPosyandu; + +export default DetailPosyandu; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx index 9ebdd4e0..410c3abc 100644 --- a/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/posyandu/create/page.tsx @@ -1,7 +1,18 @@ -'use client' +'use client'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; @@ -11,159 +22,193 @@ import { useProxy } from 'valtio/utils'; import CreateEditor from '../../../_com/createEditor'; import posyandustate from '../../../_state/kesehatan/posyandu/posyandu'; + function CreatePosyandu() { - const statePosyandu = useProxy(posyandustate) - const router = useRouter(); - const [file, setFile] = useState(null); - const [previewImage, setPreviewImage] = useState(null); - - const resetForm = () => { - statePosyandu.create.form = { - name: "", - nomor: "", - deskripsi: "", - imageId: "", - jadwalPelayanan: "", - }; - - setFile(null); - setPreviewImage(null); - } - - const handleSubmit = async () => { - if (!file) { - return toast.warn("Pilih file gambar terlebih dahulu"); - } - - // Upload gambar dulu - const res = await ApiFetch.api.fileStorage.create.post({ - file, - name: file.name, - }); - - const uploaded = res.data?.data; - if (!uploaded?.id) { - return toast.error("Gagal upload gambar"); - } - - statePosyandu.create.form.imageId = uploaded.id; - - await statePosyandu.create.create(); - - resetForm(); - router.push("/admin/kesehatan/posyandu") - } + const statePosyandu = useProxy(posyandustate); + const router = useRouter(); + const [file, setFile] = useState(null); + const [previewImage, setPreviewImage] = useState(null); + const resetForm = () => { + statePosyandu.create.form = { + name: '', + nomor: '', + deskripsi: '', + imageId: '', + jadwalPelayanan: '', + }; + setFile(null); + setPreviewImage(null); + }; - return ( - - - - - - - Create Posyandu - - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } - }} - onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - + const handleSubmit = async () => { + if (!file) { + return toast.warn('Silakan pilih file gambar terlebih dahulu'); + } -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
- {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} + // Upload gambar dulu + const res = await ApiFetch.api.fileStorage.create.post({ + file, + name: file.name, + }); -
-
- Nama Posyandu} - placeholder='Masukkan nama posyandu' - value={statePosyandu.create.form.name} - onChange={(e) => { - statePosyandu.create.form.name = e.target.value; - }} - /> - Nomor Posyandu} - placeholder='Masukkan nomor posyandu' - value={statePosyandu.create.form.nomor} - onChange={(e) => { - statePosyandu.create.form.nomor = e.target.value; - }} - /> - - Deskripsi Posyandu - { - statePosyandu.create.form.deskripsi = htmlContent; - }} - /> - - - Jadwal Pelayanan - { - statePosyandu.create.form.jadwalPelayanan = htmlContent; - }} - /> - - - - -
-
-
- ); + + const uploaded = res.data?.data; + if (!uploaded?.id) { + return toast.error('Gagal upload gambar'); + } + + + statePosyandu.create.form.imageId = uploaded.id; + + + await statePosyandu.create.create(); + + + resetForm(); + router.push('/admin/kesehatan/posyandu'); + }; + + + return ( + + {/* Header */} + + + + + + Tambah Posyandu + + + + + + + {/* Upload Gambar */} + + + Gambar Posyandu + + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid, gunakan format gambar')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + radius="md" + p="xl" + > + + + + + + + + + + + + + Seret gambar atau klik untuk memilih file (maks 5MB) + + + + + {previewImage && ( + + Preview Gambar + + )} + + + + {/* Input Form */} + (statePosyandu.create.form.name = e.target.value)} + required + /> + (statePosyandu.create.form.nomor = e.target.value)} + required + /> + + + Deskripsi Posyandu + + { + statePosyandu.create.form.deskripsi = htmlContent; + }} + /> + + + + Jadwal Pelayanan + + { + statePosyandu.create.form.jadwalPelayanan = htmlContent; + }} + /> + + + + {/* Button */} + + + + + + + ); } -export default CreatePosyandu; + +export default CreatePosyandu; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/posyandu/page.tsx b/src/app/admin/(dashboard)/kesehatan/posyandu/page.tsx index 6144b417..3e7e6758 100644 --- a/src/app/admin/(dashboard)/kesehatan/posyandu/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/posyandu/page.tsx @@ -1,114 +1,170 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Pagination, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; -import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; -import HeaderSearch from '../../_com/header'; -import JudulList from '../../_com/judulList'; -import { useRouter } from 'next/navigation'; -import { useProxy } from 'valtio/utils'; -import posyandustate from '../../_state/kesehatan/posyandu/posyandu'; +import { + Box, + Button, + Center, + Group, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; +import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../_com/header'; +import posyandustate from '../../_state/kesehatan/posyandu/posyandu'; + function Posyandu() { - const [search, setSearch] = useState(""); - return ( - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - ); + const [search, setSearch] = useState(""); + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); } + function ListPosyandu({ search }: { search: string }) { - const statePosyandu = useProxy(posyandustate) - const router = useRouter(); + const statePosyandu = useProxy(posyandustate) + const router = useRouter(); - const { - data, - page, - totalPages, - loading, - load, - } = statePosyandu.findMany; - useShallowEffect(() => { - load(page, 10, search) - }, [page, search]) + const { + data, + page, + totalPages, + loading, + load, + } = statePosyandu.findMany; - const filteredData = data || []; - if (loading || !data) { - return ( - - - - ) - } + useShallowEffect(() => { + load(page, 10, search) + }, [page, search]) - return ( - - - - - - - - Nama Posyandu - Nomor Posyandu - Deskripsi - Detail - - - - {filteredData.map((item) => ( - - - - {item.name} - - - - - {item.nomor} - - - - - - - - - - - - ))} - -
-
-
-
- load(newPage)} // ini penting! - total={totalPages} - mt="md" - mb="md" - /> -
-
- ); + + const filteredData = data || []; + + + if (loading || !data) { + return ( + + + + ) + } + + + return ( + + + + Daftar Posyandu + + + + + + + + + Nama Posyandu + Nomor Posyandu + Deskripsi + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( + + + + {item.name} + + + + + {item.nomor || '-'} + + + + + + + + + + )) + ) : ( + + +
+ Tidak ada data posyandu yang cocok +
+
+
+ )} +
+
+
+
+
+ { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + mt="md" + mb="md" + color="blue" + radius="md" + /> +
+
+ ); } -export default Posyandu; + +export default Posyandu; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/edit/page.tsx index bf8a9fcb..5adda396 100644 --- a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/edit/page.tsx @@ -4,7 +4,18 @@ import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; import programKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/program-kesehatan/programKesehatan'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; @@ -12,11 +23,10 @@ import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; - function EditProgramKesehatan() { - const programKesehatanState = useProxy(programKesehatan) + const programKesehatanState = useProxy(programKesehatan); const router = useRouter(); - const params = useParams() + const params = useParams(); const [previewImage, setPreviewImage] = useState(null); const [file, setFile] = useState(null); @@ -25,7 +35,7 @@ function EditProgramKesehatan() { deskripsiSingkat: programKesehatanState.edit.form.deskripsiSingkat || '', deskripsi: programKesehatanState.edit.form.deskripsi || '', imageId: programKesehatanState.edit.form.imageId || '', - }) + }); useEffect(() => { const loadProgramKesehatan = async () => { @@ -47,8 +57,8 @@ function EditProgramKesehatan() { } } } catch (error) { - console.error("Error loading program kesehatan:", error); - toast.error("Gagal memuat data program kesehatan"); + console.error('Error loading program kesehatan:', error); + toast.error('Gagal memuat data program kesehatan'); } }; @@ -70,114 +80,143 @@ function EditProgramKesehatan() { const uploaded = res.data?.data; if (!uploaded?.id) { - return toast.error("Gagal upload gambar"); + return toast.error('Gagal upload gambar'); } programKesehatanState.edit.form.imageId = uploaded.id; } await programKesehatanState.edit.update(); - toast.success("Program kesehatan berhasil diperbarui!"); - router.push("/admin/kesehatan/program-kesehatan"); + toast.success('Program kesehatan berhasil diperbarui!'); + router.push('/admin/kesehatan/program-kesehatan'); } catch (error) { - console.error("Error updating program kesehatan:", error); - toast.error("Gagal memuat data program kesehatan"); + console.error('Error updating program kesehatan:', error); + toast.error('Terjadi kesalahan saat memperbarui program kesehatan'); } - } + }; + return ( - - - - - - - - Edit Program Kesehatan - setFormData({ ...formData, name: e.target.value })} - label={Judul} - placeholder="masukkan judul" - /> + + {/* Header dengan tombol back */} + + + + + + Edit Program Kesehatan + + - setFormData({ ...formData, deskripsiSingkat: e.target.value })} - label={Deskripsi Singkat} - placeholder="masukkan deskripsi" - /> + {/* Card Form */} + + + setFormData({ ...formData, name: e.target.value })} + label="Judul" + placeholder="Masukkan judul" + required + /> - - Deskripsi - setFormData({ ...formData, deskripsi: val })} - /> - - - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } + setFormData({ ...formData, deskripsiSingkat: e.target.value })} + label="Deskripsi Singkat" + placeholder="Masukkan deskripsi singkat" + required + /> + + + + Deskripsi + + setFormData({ ...formData, deskripsi: val })} + /> + + + + + Gambar + + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + + + + + + + + + + + +
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+
+
+ + {previewImage && ( + + Preview toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - - -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
- - {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} + />
-
- -
-
-
-
+ + + + ); } diff --git a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/page.tsx index 51452e18..e72cd84a 100644 --- a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/[id]/page.tsx @@ -1,7 +1,7 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Flex, Text, Image, Skeleton } from '@mantine/core'; -import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react'; +import { Box, Button, Group, Paper, Skeleton, Stack, Text, Tooltip, Image } from '@mantine/core'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import React, { useState } from 'react'; import programKesehatan from '../../../_state/kesehatan/program-kesehatan/programKesehatan'; @@ -10,82 +10,116 @@ import { useShallowEffect } from '@mantine/hooks'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; function DetailProgramKesehatan() { - const programKesehatanState = useProxy(programKesehatan) - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) + const state = useProxy(programKesehatan); + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); const router = useRouter(); - const params = useParams() + const params = useParams(); useShallowEffect(() => { - programKesehatanState.findUnique.load(params?.id as string) - }, []) + state.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - programKesehatanState.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/program-kesehatan") + state.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push("/admin/kesehatan/program-kesehatan"); } - } + }; - if (!programKesehatanState.findUnique.data) { + if (!state.findUnique.data) { return ( - + - ) + ); } + const data = state.findUnique.data; + return ( - - - - - - - Detail Program Kesehatan - {programKesehatanState.findUnique.data ? ( - - - - Judul - {programKesehatanState.findUnique.data.name} - - - Deskripsi Singkat - {programKesehatanState.findUnique.data.deskripsiSingkat} - - - Deskripsi - - - - Gambar - gambar - - - - + + + + + Detail Program Kesehatan + + + + + + Judul + {data?.name || '-'} + + + + Deskripsi Singkat + {data?.deskripsiSingkat || '-'} + + + + Deskripsi + + + + + Gambar + {data?.image?.link ? ( + gambar program kesehatan + ) : ( + - + )} + + + + + - - - - - - ) : null} + variant="light" + radius="md" + size="md" + > + + + + + + + + + + @@ -94,7 +128,7 @@ function DetailProgramKesehatan() { opened={modalHapus} onClose={() => setModalHapus(false)} onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus program kesehatan ini?" + text="Apakah Anda yakin ingin menghapus program kesehatan ini?" /> ); diff --git a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/create/page.tsx index 449ff9e2..a5288fe7 100644 --- a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/create/page.tsx @@ -1,7 +1,18 @@ 'use client' import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip +} from '@mantine/core'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; @@ -13,30 +24,32 @@ import { Dropzone } from '@mantine/dropzone'; function CreateProgramKesehatan() { const router = useRouter(); - const programKesehatanState = useProxy(programKesehatan) + const programKesehatanState = useProxy(programKesehatan); const [previewImage, setPreviewImage] = useState(null); const [file, setFile] = useState(null); const resetForm = () => { - // Reset state di valtio programKesehatanState.create.form = { name: "", deskripsiSingkat: "", deskripsi: "", imageId: "", }; - - // Reset state lokal setPreviewImage(null); setFile(null); }; const handleSubmit = async () => { + if (!programKesehatanState.create.form.name) { + return toast.warn("Judul wajib diisi"); + } + if (!programKesehatanState.create.form.deskripsiSingkat) { + return toast.warn("Deskripsi singkat wajib diisi"); + } if (!file) { return toast.warn("Pilih file gambar terlebih dahulu"); } - // Upload gambar dulu const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name, @@ -47,34 +60,45 @@ function CreateProgramKesehatan() { return toast.error("Gagal upload gambar"); } - // Simpan ID gambar ke form programKesehatanState.create.form.imageId = uploaded.id; - - // Submit data berita await programKesehatanState.create.create(); - // Reset form setelah submit resetForm(); - router.push("/admin/kesehatan/program-kesehatan") + router.push("/admin/kesehatan/program-kesehatan"); }; return ( - - - - - - - Create Program Kesehatan + + {/* Header */} + + + + + + Tambah Program Kesehatan + + + + {/* Form Card */} + + { programKesehatanState.create.form.name = val.target.value; }} - label={Judul} - placeholder="masukkan judul" + label="Judul" + placeholder="Masukkan judul" + required /> { programKesehatanState.create.form.deskripsiSingkat = val.target.value; }} - label={Deskripsi Singkat} - placeholder="masukkan deskripsi" + label="Deskripsi Singkat" + placeholder="Masukkan deskripsi singkat" + required /> - Deskripsi + + Deskripsi + { @@ -95,64 +122,76 @@ function CreateProgramKesehatan() { }} /> + - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } - }} - onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - + + Gambar + + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + + + + + + + + + + -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
+
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+
+
- {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} -
+ {previewImage && ( + + Preview + + )}
- + + + +
diff --git a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/page.tsx b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/page.tsx index d74f6f36..ddfa287a 100644 --- a/src/app/admin/(dashboard)/kesehatan/program-kesehatan/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/program-kesehatan/page.tsx @@ -1,8 +1,26 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; -import JudulList from '../../_com/judulList'; +import { + Box, + Button, + Center, + Group, + Image, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; +import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import HeaderSearch from '../../_com/header'; import { useRouter } from 'next/navigation'; import programKesehatan from '../../_state/kesehatan/program-kesehatan/programKesehatan'; @@ -14,9 +32,10 @@ function ProgramKesehatan() { const [search, setSearch] = useState(""); return ( + {/* Header dengan Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} @@ -27,88 +46,112 @@ function ProgramKesehatan() { } function ListProgramKesehatan({ search }: { search: string }) { - const programKesehatanState = useProxy(programKesehatan) - const router = useRouter() + const stateProgram = useProxy(programKesehatan); + const router = useRouter(); - const { - data, - page, - totalPages, - loading, - load, - } = programKesehatanState.findMany; + const { data, page, totalPages, loading, load } = stateProgram.findMany; useShallowEffect(() => { - load(page, 10, search) - }, [page, search]) + load(page, 10, search); + }, [page, search]); - const filteredData = data || [] + const filteredData = data || []; if (loading || !data) { return ( - - - - ) + + + + ); } return ( - - - - - - - - Judul - Deskripsi Singkat - Image - Detail - - - - {filteredData.map((item) => ( + + {/* Header List + Tombol Tambah */} + + Daftar Program Kesehatan + + + + + + {/* Tabel */} + +
+ + + Judul + Deskripsi Singkat + Image + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( - - {item.name} - + + {item.name} + - - - + - + - - ))} - -
-
-
+ )) + ) : ( + + +
+ Tidak ada program kesehatan yang cocok +
+
+
+ )} + + +
+ + {/* Pagination */}
load(newPage)} // ini penting! + onChange={(newPage) => { + load(newPage, 10, search); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} total={totalPages} mt="md" mb="md" + color="blue" + radius="md" />
- ) + ); } export default ProgramKesehatan; diff --git a/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx b/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx index 1168e26d..ff236e3f 100644 --- a/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx @@ -4,7 +4,18 @@ import puskesmasState from '@/app/admin/(dashboard)/_state/kesehatan/puskesmas/puskesmas'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; @@ -85,7 +96,6 @@ function EditPuskesmas() { imageId: form.imageId, }); - // Check if there's an existing image URL in the form data const formWithImage = form as PuskesmasFormData; if (formWithImage.image?.link) { setPreviewImage(formWithImage.image.link); @@ -105,17 +115,8 @@ function EditPuskesmas() { ...statePuskesmas.edit.form, name: formData.name, alamat: formData.alamat, - jam: { - workDays: formData.jam.workDays, - weekDays: formData.jam.weekDays, - holiday: formData.jam.holiday, - }, - kontak: { - kontakPuskesmas: formData.kontak.kontakPuskesmas, - email: formData.kontak.email, - facebook: formData.kontak.facebook, - kontakUGD: formData.kontak.kontakUGD, - }, + jam: { ...formData.jam }, + kontak: { ...formData.kontak }, imageId: formData.imageId, }; @@ -144,166 +145,182 @@ function EditPuskesmas() { const handleInputChange = (e: ChangeEvent) => { const { name, value } = e.target; - setFormData(prev => ({ - ...prev, - [name]: value - })); + setFormData(prev => ({ ...prev, [name]: value })); }; const handleNestedChange = (section: 'jam' | 'kontak', field: string, value: string) => { setFormData(prev => ({ ...prev, - [section]: { - ...prev[section], - [field]: value - } + [section]: { ...prev[section], [field]: value } })); }; return ( - - - - - - - - Edit Puskesmas + + {/* Header dengan tombol back */} + + + + + + Edit Puskesmas + + - Nama Puskesmas} - placeholder="masukkan nama puskesmas" - name="name" - value={formData.name} - onChange={handleInputChange} - /> + {/* Card Form */} + + + - Alamat} - placeholder="masukkan alamat" - name="alamat" - value={formData.alamat} - onChange={handleInputChange} - /> + - Jam Buka} - placeholder="masukkan jam buka" - value={formData.jam.workDays} - onChange={(e) => handleNestedChange('jam', 'workDays', e.target.value)} - /> + handleNestedChange('jam', 'workDays', e.target.value)} + required + /> - Jam Tutup} - placeholder="masukkan jam tutup" - value={formData.jam.weekDays} - onChange={(e) => handleNestedChange('jam', 'weekDays', e.target.value)} - /> + handleNestedChange('jam', 'weekDays', e.target.value)} + required + /> - Jam Libur} - placeholder="masukkan jam libur" - value={formData.jam.holiday} - onChange={(e) => handleNestedChange('jam', 'holiday', e.target.value)} - /> + handleNestedChange('jam', 'holiday', e.target.value)} + required + /> - Kontak Puskesmas} - placeholder="masukkan kontak puskesmas" - value={formData.kontak.kontakPuskesmas} - onChange={(e) => handleNestedChange('kontak', 'kontakPuskesmas', e.target.value)} - /> + handleNestedChange('kontak', 'kontakPuskesmas', e.target.value)} + /> - Email} - placeholder="masukkan email" - value={formData.kontak.email} - onChange={(e) => handleNestedChange('kontak', 'email', e.target.value)} - /> + handleNestedChange('kontak', 'email', e.target.value)} + /> - Facebook} - placeholder="masukkan facebook" - value={formData.kontak.facebook} - onChange={(e) => handleNestedChange('kontak', 'facebook', e.target.value)} - /> + handleNestedChange('kontak', 'facebook', e.target.value)} + /> - Kontak UGD} - placeholder="masukkan kontak UGD" - value={formData.kontak.kontakUGD} - onChange={(e) => handleNestedChange('kontak', 'kontakUGD', e.target.value)} - /> + handleNestedChange('kontak', 'kontakUGD', e.target.value)} + /> - - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } + {/* Upload Gambar */} + + + Gambar + + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + + + + + + + + + + +
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+
+
+ + {previewImage && ( + + Preview toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - - -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
- - {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} - + />
-
+ )} +
+ -
-
-
+ + +
); } diff --git a/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx b/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx index c3edf01c..0747d225 100644 --- a/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx @@ -1,7 +1,7 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Flex, Text, Image, Skeleton } from '@mantine/core'; -import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react'; +import { Box, Button, Paper, Stack, Text, Image, Skeleton, Group, Tooltip } from '@mantine/core'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import React, { useState } from 'react'; import puskesmasState from '../../../_state/kesehatan/puskesmas/puskesmas'; @@ -10,90 +10,128 @@ import { useShallowEffect } from '@mantine/hooks'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; function DetailPuskesmas() { - const params = useParams() + const params = useParams(); const router = useRouter(); - const statePuskesmas = useProxy(puskesmasState) + const statePuskesmas = useProxy(puskesmasState); const [modalHapus, setModalHapus] = useState(false); - const [selectedId, setSelectedId] = useState(null) + const [selectedId, setSelectedId] = useState(null); useShallowEffect(() => { - statePuskesmas.findUnique.load(params?.id as string) - }, []) + statePuskesmas.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - statePuskesmas.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/kesehatan/puskesmas") + statePuskesmas.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push("/admin/kesehatan/puskesmas"); } - } + }; if (!statePuskesmas.findUnique.data) { return ( - + - ) + ); } + const data = statePuskesmas.findUnique.data; return ( - - - - - - - Detail Puskesmas - {statePuskesmas.findUnique.data ? ( - - - - Nama Puskesmas - {statePuskesmas.findUnique.data.name} - - - Alamat - {statePuskesmas.findUnique.data.alamat} - - - Jam Operasional - {statePuskesmas.findUnique.data.jam.workDays} - {statePuskesmas.findUnique.data.jam.weekDays} - {statePuskesmas.findUnique.data.jam.holiday} - - - Gambar - gambar - - - Kontak - {statePuskesmas.findUnique.data.kontak.kontakPuskesmas} - {statePuskesmas.findUnique.data.kontak.email} - {statePuskesmas.findUnique.data.kontak.facebook} - {statePuskesmas.findUnique.data.kontak.kontakUGD} - - - - - - - - - - ) : null} + + {/* Tombol kembali */} + + + + + + Detail Puskesmas + + + + + + Nama Puskesmas + {data?.name || '-'} + + + + Alamat + {data?.alamat || '-'} + + + + Jam Operasional + {data?.jam?.workDays || '-'} + {data?.jam?.weekDays || '-'} + {data?.jam?.holiday || '-'} + + + + Gambar + {data?.image?.link ? ( + gambar + ) : ( + - + )} + + + + Kontak + {data?.kontak?.kontakPuskesmas || '-'} + {data?.kontak?.email || '-'} + {data?.kontak?.facebook || '-'} + {data?.kontak?.kontakUGD || '-'} + + + + + + + + + + + + + @@ -102,7 +140,7 @@ function DetailPuskesmas() { opened={modalHapus} onClose={() => setModalHapus(false)} onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus potensi ini?" + text="Apakah anda yakin ingin menghapus data ini?" /> ); diff --git a/src/app/admin/(dashboard)/kesehatan/puskesmas/create/page.tsx b/src/app/admin/(dashboard)/kesehatan/puskesmas/create/page.tsx index 3d0b762a..7778f58e 100644 --- a/src/app/admin/(dashboard)/kesehatan/puskesmas/create/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/puskesmas/create/page.tsx @@ -1,7 +1,18 @@ 'use client' import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; @@ -11,44 +22,39 @@ import { useProxy } from 'valtio/utils'; import puskesmasState from '../../../_state/kesehatan/puskesmas/puskesmas'; function CreatePuskesmas() { - const statePuskesmas = useProxy(puskesmasState) + const statePuskesmas = useProxy(puskesmasState); const router = useRouter(); const [file, setFile] = useState(null); const [previewImage, setPreviewImage] = useState(null); - const resetForm = () => { statePuskesmas.create.form = { - name: "", - alamat: "", + name: '', + alamat: '', jam: { - workDays: "", - weekDays: "", - holiday: "", + workDays: '', + weekDays: '', + holiday: '', }, kontak: { - kontakPuskesmas: "", - email: "", - facebook: "", - kontakUGD: "", + kontakPuskesmas: '', + email: '', + facebook: '', + kontakUGD: '', }, - imageId: "", + imageId: '', image: undefined, }; - setFile(null); setPreviewImage(null); }; - - const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!file) { - return toast.warn("Pilih file gambar terlebih dahulu"); + return toast.warn('Pilih file gambar terlebih dahulu'); } - // Upload gambar dulu const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name, @@ -56,162 +62,171 @@ function CreatePuskesmas() { const uploaded = res.data?.data; if (!uploaded?.id) { - return toast.error("Gagal upload gambar"); + return toast.error('Gagal upload gambar'); } statePuskesmas.create.form.imageId = uploaded.id; - // State is already being updated directly in the form inputs - await statePuskesmas.create.submit(); - toast.success("Data berhasil disimpan"); + toast.success('Data berhasil disimpan'); resetForm(); - // After successful submission, redirect to the list page router.push('/admin/kesehatan/puskesmas'); - } + }; return ( - - - - + + {/* Header */} + + + + + + Tambah Data Puskesmas + + - - - Create Puskesmas + {/* Form Card */} + + Nama Puskesmas} - placeholder="masukkan nama puskesmas" + label="Nama Puskesmas" + placeholder="Masukkan nama puskesmas" value={statePuskesmas.create.form.name} - onChange={(e) => { - statePuskesmas.create.form.name = e.target.value; - }} + onChange={(e) => (statePuskesmas.create.form.name = e.target.value)} + required /> Alamat} - placeholder="masukkan alamat" + label="Alamat" + placeholder="Masukkan alamat" value={statePuskesmas.create.form.alamat} - onChange={(e) => { - statePuskesmas.create.form.alamat = e.target.value; - }} + onChange={(e) => (statePuskesmas.create.form.alamat = e.target.value)} + required /> Jam Buka} - placeholder="masukkan jam buka" + label="Jam Buka" + placeholder="Masukkan jam buka" value={statePuskesmas.create.form.jam.workDays} - onChange={(e) => { - statePuskesmas.create.form.jam.workDays = e.target.value; - }} + onChange={(e) => (statePuskesmas.create.form.jam.workDays = e.target.value)} /> Jam Tutup} - placeholder="masukkan jam tutup" + label="Jam Tutup" + placeholder="Masukkan jam tutup" value={statePuskesmas.create.form.jam.weekDays} - onChange={(e) => { - statePuskesmas.create.form.jam.weekDays = e.target.value; - }} + onChange={(e) => (statePuskesmas.create.form.jam.weekDays = e.target.value)} /> Holiday} - placeholder="masukkan holiday" + label="Holiday" + placeholder="Masukkan hari libur" value={statePuskesmas.create.form.jam.holiday} - onChange={(e) => { - statePuskesmas.create.form.jam.holiday = e.target.value; - }} + onChange={(e) => (statePuskesmas.create.form.jam.holiday = e.target.value)} /> + Kontak Puskesmas} - placeholder="masukkan kontak puskesmas" + label="Kontak Puskesmas" + placeholder="Masukkan kontak puskesmas" value={statePuskesmas.create.form.kontak.kontakPuskesmas} - onChange={(e) => { - statePuskesmas.create.form.kontak.kontakPuskesmas = e.target.value; - }} + onChange={(e) => + (statePuskesmas.create.form.kontak.kontakPuskesmas = e.target.value) + } /> Email} - placeholder="masukkan email" + label="Email" + placeholder="Masukkan email" value={statePuskesmas.create.form.kontak.email} - onChange={(e) => { - statePuskesmas.create.form.kontak.email = e.target.value; - }} + onChange={(e) => (statePuskesmas.create.form.kontak.email = e.target.value)} /> Facebook} - placeholder="masukkan facebook" + label="Facebook" + placeholder="Masukkan facebook" value={statePuskesmas.create.form.kontak.facebook} - onChange={(e) => { - statePuskesmas.create.form.kontak.facebook = e.target.value; - }} + onChange={(e) => (statePuskesmas.create.form.kontak.facebook = e.target.value)} /> Kontak UGD} - placeholder="masukkan kontak ugd" + label="Kontak UGD" + placeholder="Masukkan kontak UGD" value={statePuskesmas.create.form.kontak.kontakUGD} - onChange={(e) => { - statePuskesmas.create.form.kontak.kontakUGD = e.target.value; - }} + onChange={(e) => (statePuskesmas.create.form.kontak.kontakUGD = e.target.value)} /> - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } - }} - onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} - > - - - - - - - - - - + + Gambar + + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + + + + + + + + + + -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
+
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+
+
- {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview - - )} -
+ {previewImage && ( + + Preview + + )}
- + + {/* Action Button */} + + +
diff --git a/src/app/admin/(dashboard)/kesehatan/puskesmas/page.tsx b/src/app/admin/(dashboard)/kesehatan/puskesmas/page.tsx index 48a6fd72..e9f827e4 100644 --- a/src/app/admin/(dashboard)/kesehatan/puskesmas/page.tsx +++ b/src/app/admin/(dashboard)/kesehatan/puskesmas/page.tsx @@ -1,105 +1,155 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { + Box, + Button, + Center, + Group, + Image, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; +import { useState } from 'react'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../_com/header'; -import JudulList from '../../_com/judulList'; import puskesmasState from '../../_state/kesehatan/puskesmas/puskesmas'; -import { useState } from 'react'; function Puskesmas() { const [search, setSearch] = useState(""); + return ( + {/* Header dengan Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> + ); } function ListPuskesmas({ search }: { search: string }) { - const statePuskesmas = useProxy(puskesmasState) + const statePuskesmas = useProxy(puskesmasState); const router = useRouter(); - const { - data, - page, - totalPages, - loading, - load, - } = statePuskesmas.findMany; + const { data, page, totalPages, loading, load } = statePuskesmas.findMany; useShallowEffect(() => { - load(page, 10, search) - }, [page, search]) + load(page, 10, search); + }, [page, search]); - const filteredData = data || [] + const filteredData = data || []; if (loading || !data) { return ( - - - - ) + + + + ); } + return ( - - - - - - - - Nama Puskesmas - Alamat - Image - Detail - - - - {filteredData.map((item) => ( + + + Daftar Puskesmas + + + + + + +
+ + + Nama Puskesmas + Alamat + Image + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( - {item.name} + + + {item.name} + + {item.alamat} - + - - ))} - -
-
-
+ )) + ) : ( + + +
+ Tidak ada data puskesmas yang cocok +
+
+
+ )} + + +
+ + {/* Pagination */}
load(newPage)} // ini penting! + onChange={(newPage) => { + load(newPage, 10, search); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} total={totalPages} mt="md" mb="md" + color="blue" + radius="md" />
- ) + ); } export default Puskesmas; diff --git a/src/app/admin/(dashboard)/landing-page/profile/pejabat-desa/page.tsx b/src/app/admin/(dashboard)/landing-page/profile/pejabat-desa/page.tsx index 47030974..9446c859 100644 --- a/src/app/admin/(dashboard)/landing-page/profile/pejabat-desa/page.tsx +++ b/src/app/admin/(dashboard)/landing-page/profile/pejabat-desa/page.tsx @@ -66,7 +66,7 @@ function Page() { - +
- Jenjang Pendidikan + {/* } value={search} onChange={(e) => setSearch(e.currentTarget.value)} - /> - + /> */} + ); } -function ListJenjangPendidikan({ search }: { search: string }) { +function ListJenjangPendidikan() { const stateList = useProxy(infoSekolahPaud.jenjangPendidikan) const [modalHapus, setModalHapus] = useState(false) const [selectedId, setSelectedId] = useState(null) @@ -50,8 +50,8 @@ function ListJenjangPendidikan({ search }: { search: string }) { } useShallowEffect(() => { - load(page, 10, search) - }, [page, search]) + load(page, 10) + }, [page]) const filteredData = data || [] diff --git a/src/app/admin/(dashboard)/user&role/user/page.tsx b/src/app/admin/(dashboard)/user&role/user/page.tsx index c9db847a..7590b3fa 100644 --- a/src/app/admin/(dashboard)/user&role/user/page.tsx +++ b/src/app/admin/(dashboard)/user&role/user/page.tsx @@ -2,7 +2,7 @@ import colors from '@/con/colors'; import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconSearch, IconTrash } from '@tabler/icons-react'; +import { IconCheck, IconSearch, IconX } from '@tabler/icons-react'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../_com/header'; @@ -75,9 +75,9 @@ function ListUser({ search }: { search: string }) { Nama User - Email + Nomor Role - Hapus + Aktif / Nonaktif @@ -98,16 +98,19 @@ function ListUser({ search }: { search: string }) { - + diff --git a/src/app/admin/auth/_lib/api_fetch_auth.ts b/src/app/admin/auth/_lib/api_fetch_auth.ts index 09dc4006..55c1e63b 100644 --- a/src/app/admin/auth/_lib/api_fetch_auth.ts +++ b/src/app/admin/auth/_lib/api_fetch_auth.ts @@ -1,9 +1,10 @@ export { - apiFetchLogin + apiFetchLogin, + apiFetchRegister }; const apiFetchLogin = async ({ nomor }: { nomor: string }) => { - const respone = await fetch("/api/auth/login", { + const response = await fetch("/api/auth/login", { method: "POST", body: JSON.stringify({ nomor: nomor }), headers: { @@ -11,5 +12,30 @@ const apiFetchLogin = async ({ nomor }: { nomor: string }) => { }, }); - return await respone.json().catch(() => null); + return await response.json().catch(() => null); +}; + +const apiFetchRegister = async ({ + nomor, + username, +}: { + nomor: string; + username: string; +}) => { + const data = { + username: username, + nomor: nomor, + }; + const respone = await fetch("/api/auth/register", { + method: "POST", + body: JSON.stringify({ data }), + headers: { + "Content-Type": "application/json", + }, + }); + + const result = await respone.json(); + + return result; + // return await respone.json().catch(() => null); }; diff --git a/src/app/admin/auth/login/page.tsx b/src/app/admin/auth/login/page.tsx deleted file mode 100644 index 104fa2a1..00000000 --- a/src/app/admin/auth/login/page.tsx +++ /dev/null @@ -1,81 +0,0 @@ -'use client' -import BackButton from '@/app/darmasaba/(pages)/desa/layanan/_com/BackButto'; -import colors from '@/con/colors'; -import { Box, Button, Center, Flex, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; -import { IconUserFilled } from '@tabler/icons-react'; -import Link from 'next/link'; - - -function Page() { - // const router = useRouter() - // const snap = useSnapshot(userState.userState) - // const handleSubmit = async () => { - // router.push("/darmasaba/pendidikan/perpustakaan-digital") - // await snap.login.submit() - // } - return ( - - - - - -
- -
- - - E-Book Desa Darmasaba - - - Silahkan masukkan akun anda untuk menjelajahi berbagai macam buku di perpustakaan digital - - -
- - - - - - Login - - - - Masuk Untuk Akses Lebih Banyak Buku - { - // userState.userState.login.form.email = e.target.value - // }} - required - /> - { - // userState.userState.login.form.password = e.target.value - // }} - /> - - - - - Belum punya akun? - - - - - - - -
- ); -} - -export default Page; diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx index 15115673..8e08527c 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/layout.tsx @@ -21,7 +21,7 @@ import { useDisclosure } from "@mantine/hooks"; import { IconChevronLeft, IconChevronRight, - IconDoorExit, + IconLogout2 } from "@tabler/icons-react"; import _ from "lodash"; import Link from "next/link"; @@ -107,7 +107,21 @@ export default function Layout({ children }: { children: React.ReactNode }) { variant="gradient" gradient={{ from: colors["blue-button"], to: "#228be6" }} > - + Logo Darmasaba + + + + { + router.push("/login"); + }} + color={colors["blue-button"]} + radius="xl" + size="lg" + variant="gradient" + gradient={{ from: colors["blue-button"], to: "#228be6" }} + > + diff --git a/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/artikel_kesehatan/findMany.ts b/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/artikel_kesehatan/findMany.ts index e48379df..a0fbf8d3 100644 --- a/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/artikel_kesehatan/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/artikel_kesehatan/findMany.ts @@ -1,30 +1,64 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function artikelKesehatanFindMany(context: Context) { + // Ambil parameter dari query + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ""; + const skip = (page - 1) * limit; + + // Buat where clause + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { title: { contains: search, mode: "insensitive" } }, + { introduction: { content: { contains: search, mode: "insensitive" } } }, + { symptom: { title: { contains: search, mode: "insensitive" } } }, + { prevention: { title: { contains: search, mode: "insensitive" } } }, + { firstaid: { title: { contains: search, mode: "insensitive" } } }, + { mythvsfact: { title: { contains: search, mode: "insensitive" } } }, + { doctorsign: { content: { contains: search, mode: "insensitive" } } }, + ]; + } + + try { + // Ambil data dan total count secara paralel + const [data, total] = await Promise.all([ + prisma.artikelKesehatan.findMany({ + where, + include: { + introduction: true, + symptom: true, + prevention: true, + firstaid: true, + mythvsfact: true, + doctorsign: true, + }, + skip, + take: limit, + orderBy: { createdAt: "desc" }, + }), + prisma.artikelKesehatan.count({ where }), + ]); + return { + success: true, + message: "Berhasil ambil keamanan lingkungan dengan pagination", + data, + page, + limit, + total, + totalPages: Math.ceil(total / limit), + }; + } catch (e) { + console.error("Error di findMany paginated:", e); + return { + success: false, + message: "Gagal mengambil data keamanan lingkungan", + }; + } +} -export default async function artikelKesehatanFindMany() { - try { - const data = await prisma.artikelKesehatan.findMany({ - where: { - isActive: true, - }, - include: { - introduction: true, - symptom: true, - prevention: true, - firstaid: true, - mythvsfact: true, - doctorsign: true, - } - }) - return { - success: true, - message: "Success fetch artikel kesehatan", - data, - } - } catch (error) { - console.error("Find many error:", error); - return { - success: false, - message: "Failed fetch artikel kesehatan", - } - } -} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/fasilitas_kesehatan/findMany.ts b/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/fasilitas_kesehatan/findMany.ts index f9004f44..e9691a6a 100644 --- a/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/fasilitas_kesehatan/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/fasilitas_kesehatan/findMany.ts @@ -1,11 +1,35 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// /api/berita/findManyPaginated.ts import prisma from "@/lib/prisma"; +import { Context } from "elysia"; -export default async function findManyFasilitasKesehatan() { - try { - const data = await prisma.fasilitasKesehatan.findMany({ - where: { - isActive: true, - }, +async function fasilitasKesehatanFindMany(context: Context) { + // Ambil parameter dari query + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ''; + const skip = (page - 1) * limit; + + // Buat where clause + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { informasiUmum: { fasilitas: { contains: search, mode: 'insensitive' } } }, + { layananUnggulan: { content: { contains: search, mode: 'insensitive' } } }, + { dokterdanTenagaMedis: { name: { contains: search, mode: 'insensitive' } } }, + { fasilitasPendukung: { content: { contains: search, mode: 'insensitive' } } }, + { prosedurPendaftaran: { content: { contains: search, mode: 'insensitive' } } }, + { tarifdanlayanan: { layanan: { contains: search, mode: 'insensitive' } } }, + ]; + } + + try { + // Ambil data dan total count secara paralel + const [data, total] = await Promise.all([ + prisma.fasilitasKesehatan.findMany({ + where, include: { informasiumum: true, layananunggulan: true, @@ -13,18 +37,29 @@ export default async function findManyFasilitasKesehatan() { fasilitaspendukung: true, prosedurpendaftaran: true, tarifdanlayanan: true, - } - }) - return { - success: true, - message: "Success fetch fasilitas kesehatan", - data, - } - } catch (error) { - console.error("Find many error:", error); - return { - success: false, - message: "Failed fetch fasilitas kesehatan", - } + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + prisma.fasilitasKesehatan.count({ where }), + ]); + + return { + success: true, + message: "Berhasil ambil keamanan lingkungan dengan pagination", + data, + page, + limit, + total, + totalPages: Math.ceil(total / limit), + }; + } catch (e) { + console.error("Error di findMany paginated:", e); + return { + success: false, + message: "Gagal mengambil data keamanan lingkungan", + }; } -} \ No newline at end of file +} +export default fasilitasKesehatanFindMany \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/jadwal_kegiatan/findMany.ts b/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/jadwal_kegiatan/findMany.ts index 76abc694..8fc35a71 100644 --- a/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/jadwal_kegiatan/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/kesehatan/data_kesehatan_warga/jadwal_kegiatan/findMany.ts @@ -1,30 +1,60 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import prisma from "@/lib/prisma"; +import { Context } from "elysia"; -export default async function jadwalKegiatanFindMany() { - try { - const data = await prisma.jadwalKegiatan.findMany({ - where: { - isActive: true, - }, - include: { - informasijadwalkegiatan: true, - deskripsijadwalkegiatan: true, - layananjadwalkegiatan: true, - syaratketentuanjadwalkegiatan: true, - dokumenjadwalkegiatan: true, - pendaftaranjadwalkegiatan: true, - } - }) - return { - success: true, - message: "Success fetch jadwal kegiatan", - data, - } - } catch (error) { - console.error("Find many error:", error); - return { - success: false, - message: "Failed fetch jadwal kegiatan", - } - } -} \ No newline at end of file +export default async function jadwalKegiatanFindMany(context: Context) { + // Ambil parameter dari query + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ""; + const skip = (page - 1) * limit; + + // Buat where clause + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { informasijadwalkegiatan: { name: { contains: search, mode: "insensitive" } } }, + { deskripsijadwalkegiatan: { deskripsi: { contains: search, mode: "insensitive" } } }, + {layananjadwalkegiatan: { content: { contains: search, mode: "insensitive" } } }, + {syaratketentuanjadwalkegiatan: { content: { contains: search, mode: "insensitive" } } }, + {dokumenjadwalkegiatan: { content: { contains: search, mode: "insensitive" } } }, + {pendaftaranjadwalkegiatan: { content: { contains: search, mode: "insensitive" } } }, + ]; + } + try { + const [data, total] = await Promise.all([ + prisma.jadwalKegiatan.findMany({ + where, + include: { + informasijadwalkegiatan: true, + deskripsijadwalkegiatan: true, + layananjadwalkegiatan: true, + syaratketentuanjadwalkegiatan: true, + dokumenjadwalkegiatan: true, + pendaftaranjadwalkegiatan: true, + }, + skip, + take: limit, + orderBy: { createdAt: "desc" }, + }), + prisma.jadwalKegiatan.count({ where }), + ]); + return { + success: true, + message: "Success fetch jadwal kegiatan", + data, + page, + limit, + total, + totalPages: Math.ceil(total / limit), + }; + } catch (error) { + console.error("Find many error:", error); + return { + success: false, + message: "Failed fetch jadwal kegiatan", + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/user/index.ts b/src/app/api/[[...slugs]]/_lib/user/index.ts index 82bb1cff..98dd2d8b 100644 --- a/src/app/api/[[...slugs]]/_lib/user/index.ts +++ b/src/app/api/[[...slugs]]/_lib/user/index.ts @@ -4,6 +4,7 @@ import { Elysia, t } from "elysia"; import userFindMany from "./findMany"; import userFindUnique from "./findUnique"; import userDelete from "./del"; // `delete` nggak boleh jadi nama file JS langsung, jadi biasanya `del.ts` +import userUpdate from "./updt"; const User = new Elysia({ prefix: "/api/user" }) .get("/findMany", userFindMany) @@ -12,6 +13,7 @@ const User = new Elysia({ prefix: "/api/user" }) params: t.Object({ id: t.String(), }), - }); // pakai PUT untuk soft delete + }) // pakai PUT untuk soft delete + .put("/updt", userUpdate); export default User; diff --git a/src/app/api/[[...slugs]]/_lib/user/updt.ts b/src/app/api/[[...slugs]]/_lib/user/updt.ts new file mode 100644 index 00000000..4e99c92c --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/user/updt.ts @@ -0,0 +1,40 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function userUpdate(context: Context) { + try { + const { id, isActive } = await context.body as { id: string, isActive: boolean }; + + if (!id) { + return { + success: false, + message: "ID user wajib ada", + }; + } + + const updatedUser = await prisma.user.update({ + where: { id }, + data: { isActive }, + select: { + id: true, + username: true, + nomor: true, + isActive: true, + updatedAt: true, + } + }); + + return { + success: true, + message: `User berhasil ${isActive ? "diaktifkan" : "dinonaktifkan"}`, + data: updatedUser, + }; + } catch (e: any) { + console.error("Error update user:", e); + return { + success: false, + message: "Gagal mengupdate status user", + }; + } +} diff --git a/src/app/api/auth/_lib/decrypt.ts b/src/app/api/auth/_lib/decrypt.ts new file mode 100644 index 00000000..e36a5342 --- /dev/null +++ b/src/app/api/auth/_lib/decrypt.ts @@ -0,0 +1,50 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { jwtVerify } from "jose"; + +export async function decrypt({ + token, + encodedKey, +}: { + token: string; + encodedKey: string; +}): Promise | null> { + if (!token || !encodedKey) { + console.error("Missing required parameters:", { + hasToken: !!token, + hasEncodedKey: !!encodedKey, + }); + return null; + } + + try { + const enc = new TextEncoder().encode(encodedKey); + const { payload } = await jwtVerify(token, enc, { + algorithms: ["HS256"], + }); + + if (!payload || !payload.user) { + console.error("Invalid payload structure:", { + hasPayload: !!payload, + hasUser: payload ? !!payload.user : false, + }); + return null; + } + + // Logging untuk debug + // console.log("Decrypt successful:", { + // payloadExists: !!payload, + // userExists: !!payload.user, + // tokenPreview: token.substring(0, 10) + "...", + // }); + + return payload.user as Record; + } catch (error) { + console.error("Token verification failed:", { + error, + tokenLength: token?.length, + errorName: error instanceof Error ? error.name : "Unknown error", + errorMessage: error instanceof Error ? error.message : String(error), + }); + return null; + } +} diff --git a/src/app/api/auth/_lib/encrypt.ts b/src/app/api/auth/_lib/encrypt.ts new file mode 100644 index 00000000..cde1ebd9 --- /dev/null +++ b/src/app/api/auth/_lib/encrypt.ts @@ -0,0 +1,26 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { SignJWT } from "jose"; + +export async function encrypt({ + user, + exp = "7 year", + encodedKey, +}: { + user: Record; + exp?: string; + encodedKey: string; +}): Promise { + try { + const enc = new TextEncoder().encode(encodedKey); + return new SignJWT({ user }) + .setProtectedHeader({ alg: "HS256" }) + .setIssuedAt() + .setExpirationTime(exp) + .sign(enc); + } catch (error) { + console.error("Gagal mengenkripsi", error); + return null; + } +} + +// wibu:0.2.82 diff --git a/src/app/api/auth/_lib/get_KodeOtp_By_Id.ts b/src/app/api/auth/_lib/get_KodeOtp_By_Id.ts new file mode 100644 index 00000000..a045508f --- /dev/null +++ b/src/app/api/auth/_lib/get_KodeOtp_By_Id.ts @@ -0,0 +1,13 @@ +"use server"; + +import prisma from "@/lib/prisma"; + +export async function auth_getCodeOtpByNumber({kodeId}: {kodeId: string}) { + const data = await prisma.kodeOtp.findFirst({ + where: { + id: kodeId, + }, + }); + + return data; +} diff --git a/src/app/api/auth/_lib/randomOTP.ts b/src/app/api/auth/_lib/randomOTP.ts new file mode 100644 index 00000000..6f6d2447 --- /dev/null +++ b/src/app/api/auth/_lib/randomOTP.ts @@ -0,0 +1,4 @@ +export function randomOTP() { + const random = Math.floor(Math.random() * (9000 - 1000 )) + 1000 + return random; +} \ No newline at end of file diff --git a/src/app/api/auth/_lib/session_create.ts b/src/app/api/auth/_lib/session_create.ts new file mode 100644 index 00000000..fffd75cc --- /dev/null +++ b/src/app/api/auth/_lib/session_create.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { cookies } from "next/headers"; +import { encrypt } from "./encrypt"; + +export async function sessionCreate({ + sessionKey, + exp = "7 year", + encodedKey, + user, +}: { + sessionKey: string; + exp?: string; + encodedKey: string; + user: Record; +}) { + const token = await encrypt({ + exp, + encodedKey, + user, + }); + + const cookie: any = { + key: sessionKey, + value: token, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + }, + }; + + (await cookies()).set(cookie.key, cookie.value, { ...cookie.options }); + return token; +} + +// wibu:0.2.82 diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts new file mode 100644 index 00000000..dc002998 --- /dev/null +++ b/src/app/api/auth/login/route.ts @@ -0,0 +1,63 @@ +import prisma from "@/lib/prisma"; + +import { NextResponse } from "next/server"; +import { randomOTP } from "../_lib/randomOTP"; + +export async function POST(req: Request) { + if (req.method !== "POST") { + return NextResponse.json( + { success: false, message: "Method Not Allowed" }, + { status: 405 } + ); + } + + try { + const codeOtp = randomOTP(); + const body = await req.json(); + const { nomor } = body; + const res = await fetch( + `https://wa.wibudev.com/code?nom=${nomor}&text=Website Desa Darmasaba - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun Admin lainnya. + \n + >> Kode OTP anda: ${codeOtp}. + ` + ); + + const sendWa = await res.json(); + + if (sendWa.status !== "success") + return NextResponse.json( + { success: false, message: "Nomor Whatsapp Tidak Aktif" }, + { status: 400 } + ); + + const createOtpId = await prisma.kodeOtp.create({ + data: { + nomor: nomor, + otp: codeOtp, + }, + }); + + if (!createOtpId) + return NextResponse.json( + { success: false, message: "Gagal mengirim kode OTP" }, + { status: 400 } + ); + + return NextResponse.json( + { + success: true, + message: "Kode verifikasi terkirim", + kodeId: createOtpId.id, + }, + { status: 200 } + ); + } catch (error) { + console.log("Error Login", error); + return NextResponse.json( + { success: false, message: "Terjadi masalah saat login" , reason: error as Error }, + { status: 500 } + ); + } finally { + await prisma.$disconnect(); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/auth/register/route.ts b/src/app/api/auth/register/route.ts similarity index 100% rename from src/app/api/[[...slugs]]/_lib/auth/register/route.ts rename to src/app/api/auth/register/route.ts diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 00000000..3d6d2bd4 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,10 @@ +import { Box } from "@mantine/core"; +import Login from "../admin/(dashboard)/auth/login-admin/page"; + +export default function Page() { + return ( + + + + ) +} \ No newline at end of file diff --git a/src/app/registrasi/page.tsx b/src/app/registrasi/page.tsx new file mode 100644 index 00000000..fe2bc82c --- /dev/null +++ b/src/app/registrasi/page.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Registrasi from '../admin/(dashboard)/auth/registrasi-admin/page'; + +function Page() { + return ( +
+ +
+ ); +} + +export default Page; diff --git a/src/app/validasi/page.tsx b/src/app/validasi/page.tsx new file mode 100644 index 00000000..ff73bab1 --- /dev/null +++ b/src/app/validasi/page.tsx @@ -0,0 +1,9 @@ +import Validasi from "../admin/(dashboard)/auth/validasi-admin/page"; + +export default function Page() { + return ( +
+ +
+ ); +} \ No newline at end of file