diff --git a/public/uploads/seeded-images/profile-ppid/2513dd3c-256d-4f15-9e35-75e9819acdf4_perbekel.png b/public/uploads/seeded-images/profile-ppid/2513dd3c-256d-4f15-9e35-75e9819acdf4_perbekel.png new file mode 100644 index 00000000..ed1cbd10 Binary files /dev/null and b/public/uploads/seeded-images/profile-ppid/2513dd3c-256d-4f15-9e35-75e9819acdf4_perbekel.png differ diff --git a/src/app/admin/(dashboard)/_state/desa/pengumuman.ts b/src/app/admin/(dashboard)/_state/desa/pengumuman.ts index 31987b33..91b4e311 100644 --- a/src/app/admin/(dashboard)/_state/desa/pengumuman.ts +++ b/src/app/admin/(dashboard)/_state/desa/pengumuman.ts @@ -1,83 +1,246 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import ApiFetch from "@/lib/api-fetch" -import { Prisma } from "@prisma/client" -import { toast } from "react-toastify" -import { proxy } from "valtio" -import { z } from "zod" +import ApiFetch from "@/lib/api-fetch"; +import { Prisma } from "@prisma/client"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; +import { z } from "zod"; const templateFormPengumuman = z.object({ - judul: z.string().min(3, "Judul minimal 3 karakter"), - deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), - content: z.string().min(3, "Content minimal 3 karakter"), - categoryPengumumanId: z.string().nonempty(), -}) + judul: z.string().min(3, "Judul minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), + content: z.string().min(3, "Content minimal 3 karakter"), + categoryPengumumanId: z.string().nonempty(), +}); -const category = proxy ({ - findMany: { - data: null as - | null - | Prisma.CategoryPengumumanGetPayload<{ omit: { isActive: true } }>[], - async load() { - const res = await ApiFetch.api.desa.pengumuman.category["find-many"].get(); - if (res.status === 200) { - category.findMany.data = res.data?.data as any ?? []; - } - } - } -}) +const category = proxy({ + findMany: { + data: null as + | null + | Prisma.CategoryPengumumanGetPayload<{ omit: { isActive: true } }>[], + async load() { + const res = await ApiFetch.api.desa.pengumuman.category[ + "find-many" + ].get(); + if (res.status === 200) { + category.findMany.data = (res.data?.data as any) ?? []; + } + }, + }, +}); type PengumumanForm = Prisma.PengumumanGetPayload<{ - select: { - judul: true; - deskripsi: true; - content: true; - categoryPengumumanId: true; - } -}> + select: { + judul: true; + deskripsi: true; + content: true; + categoryPengumumanId: true; + }; +}>; const pengumuman = proxy({ -create: { + create: { form: {} as PengumumanForm, loading: false, async create() { - const cek = templateFormPengumuman.safeParse(pengumuman.create.form); - if (!cek.success) { + const cek = templateFormPengumuman.safeParse(pengumuman.create.form); + if (!cek.success) { const err = `[${cek.error.issues - .map((v) => `${v.path.join(".")}`) - .join("\n")}] required`; + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; return toast.error(err); - } - try { + } + try { pengumuman.create.loading = true; - const res = await ApiFetch.api.desa.pengumuman["create"].post(pengumuman.create.form) + const res = await ApiFetch.api.desa.pengumuman["create"].post( + pengumuman.create.form + ); if (res.status === 200) { - pengumuman.findMany.load(); - return toast.success("success create"); + pengumuman.findMany.load(); + return toast.success("success create"); } - console.log(res) + console.log(res); return toast.error("failed create"); - } catch (error) { + } catch (error) { console.log((error as Error).message); - } finally{ + } finally { pengumuman.create.loading = false; - } - } -}, -findMany: { + } + }, + }, + findMany: { data: null as - | Prisma.PengumumanGetPayload<{omit: {isActive: true}}>[] - | null, - async load () { - const res = await ApiFetch.api.desa.pengumuman["find-many"].get(); - console.log(res) - if (res.status === 200) { - pengumuman.findMany.data = res.data?.data ?? []; + | Prisma.PengumumanGetPayload<{ + include: { + CategoryPengumuman: true; } - } -} -}) + }>[] + | null, + async load() { + const res = await ApiFetch.api.desa.pengumuman["find-many"].get(); + console.log(res); + if (res.status === 200) { + pengumuman.findMany.data = res.data?.data ?? []; + } + }, + }, + // findUnique: { + // data: null as + // | Prisma.PengumumanGetPayload<{ + // include: { + // CategoryPengumuman: true; + // } + // }> + // | null, + // async load(id: string) { + // try { + // const res = await fetch(`/api/desa/pengumuman/${id}`); + // if (res.ok) { + // const data = await res.json(); + // pengumuman.findUnique.data = data.data ?? null; + // } else { + // console.error('Failed to fetch pengumuman:', res.statusText); + // pengumuman.findUnique.data = null; + // } + // } catch (error) { + // console.error('Error fetching pengumuman:', error); + // pengumuman.findUnique.data = null; + // } + // }, + // }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + pengumuman.delete.loading = true; + + const response = await fetch(`/api/desa/pengumuman/delete/${id}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Pengumuman berhasil dihapus"); + await pengumuman.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus pengumuman"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus pengumuman"); + } finally { + pengumuman.delete.loading = false; + } + }, + }, + update: { + id: "", + form: {} as PengumumanForm, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch(`/api/desa/pengumuman/${id}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + judul: data.judul, + deskripsi: data.deskripsi, + content: data.content, + categoryPengumumanId: data.categoryPengumumanId || "", + }; + return data; + } else { + throw new Error(result?.message || "Gagal mengambil data pengumuman"); + } + } catch (error) { + console.error((error as Error).message); + toast.error("Terjadi kesalahan saat mengambil data pengumuman"); + } finally { + pengumuman.update.loading = false; + } + }, + + async update() { + const cek = templateFormPengumuman.safeParse(pengumuman.update.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + pengumuman.update.loading = true; + + const response = await fetch(`/api/desa/pengumuman/${this.id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + judul: this.form.judul, + deskripsi: this.form.deskripsi, + content: this.form.content, + categoryPengumumanId: this.form.categoryPengumumanId, + }), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.message || `HTTP error! status: ${response.status}` + ); + } + + const result = await response.json(); + + if (result.success) { + toast.success("Berhasil update pengumuman"); + await pengumuman.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal update pengumuman"); + } + } catch (error) { + console.error("Error updating pengumuman:", error); + toast.error( + error instanceof Error + ? error.message + : "Terjadi kesalahan saat update pengumuman" + ); + return false; + } finally { + pengumuman.update.loading = false; + } + }, + }, +}); const stateDesaPengumuman = proxy({ - category, - pengumuman -}) -export default stateDesaPengumuman \ No newline at end of file + category, + pengumuman, +}); +export default stateDesaPengumuman; diff --git a/src/app/admin/(dashboard)/desa/pengumuman/edit/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/[id]/edit/page.tsx similarity index 94% rename from src/app/admin/(dashboard)/desa/pengumuman/edit/page.tsx rename to src/app/admin/(dashboard)/desa/pengumuman/[id]/edit/page.tsx index 506ef58f..ccbb4eb3 100644 --- a/src/app/admin/(dashboard)/desa/pengumuman/edit/page.tsx +++ b/src/app/admin/(dashboard)/desa/pengumuman/[id]/edit/page.tsx @@ -3,7 +3,7 @@ import colors from '@/con/colors'; import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; import { IconArrowBack } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import { KeamananEditor } from '../../../keamanan/_com/keamananEditor'; +import { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor'; function EditPengumuman() { const router = useRouter(); diff --git a/src/app/admin/(dashboard)/desa/pengumuman/detail/page.tsx b/src/app/admin/(dashboard)/desa/pengumuman/[id]/page.tsx similarity index 58% rename from src/app/admin/(dashboard)/desa/pengumuman/detail/page.tsx rename to src/app/admin/(dashboard)/desa/pengumuman/[id]/page.tsx index 2cb0d7e1..5090a523 100644 --- a/src/app/admin/(dashboard)/desa/pengumuman/detail/page.tsx +++ b/src/app/admin/(dashboard)/desa/pengumuman/[id]/page.tsx @@ -1,15 +1,14 @@ 'use client' -import { Box, Button, Flex, Image, Paper, Stack, Text } from '@mantine/core'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { Box, Button, Paper } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import colors from '@/con/colors'; -// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; -// import stateDashboardBerita from '../../../_state/desa/berita'; + function DetailPengumuman() { - // const pengumumanState = useProxy(stateDashboardpengumuman) + // const pengumumanState = useProxy(stateDesaPengumuman) // const [modalHapus, setModalHapus] = useState(false) // const [selectedId, setSelectedId] = useState(null) // const params = useParams() @@ -32,9 +31,7 @@ function DetailPengumuman() { // if (!pengumumanState.pengumuman.findUnique.data) { // return ( // - // {Array.from({ length: 10 }).map((_, k) => ( - // - // ))} + // // // ) // } @@ -47,51 +44,47 @@ function DetailPengumuman() { - + {/* Detail Pengumuman - + {pengumumanState.pengumuman.findUnique.data ? ( + Kategori - Pendidikan + {pengumumanState.pengumuman.findUnique.data?.CategoryPengumuman?.name} Judul - Pengumuman Pendidikan - - - Deskripsi Singkat - Pengumuman Pendidikan + {pengumumanState.pengumuman.findUnique.data?.judul} Deskripsi - Pengumuman Pendidikan - - - Tanggal - 2025-06-04 - - - Waktu - 08:00 - 17:00 - - - Gambar - gambar + {pengumumanState.pengumuman.findUnique.data?.deskripsi} Konten - Pengumuman Pendidikan + - + Create Pengumuman Judul} - placeholder='Masukkan judul' + label={Judul} + placeholder='Masukkan judul' + onChange={(val) => { + pengumumanState.pengumuman.create.form.judul = val.target.value + }} + /> + { + pengumumanState.pengumuman.create.form.categoryPengumumanId = val.id; + }} /> Deskripsi Singkat} - placeholder='Masukkan deskripsi singkat' - /> - Tanggal} - placeholder='Masukkan tanggal' - /> - Waktu} - placeholder='Masukkan waktu' + label={Deskripsi Singkat} + placeholder='Masukkan deskripsi singkat' + onChange={(val) => { + pengumumanState.pengumuman.create.form.deskripsi = val.target.value + }} /> - Deskripsi - + Deskripsi + { + pengumumanState.pengumuman.create.form.content = htmlContent; + }} + /> - + - + - + + ); +} + +function SelectCategory({ + onChange, +}: { + onChange: (value: Prisma.CategoryPengumumanGetPayload<{ select: { name: true; id: true; } }>) => void; +}) { + const categoryState = useProxy(stateDesaPengumuman.category); + + useShallowEffect(() => { + categoryState.findMany.load(); + }, []); + + return ( +