diff --git a/prisma/migrations/20250630081247_fix_layanan_polsek_deletedat/migration.sql b/prisma/migrations/20250630081247_fix_layanan_polsek_deletedat/migration.sql new file mode 100644 index 00000000..3767383e --- /dev/null +++ b/prisma/migrations/20250630081247_fix_layanan_polsek_deletedat/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "LayananPolsek" ALTER COLUMN "deletedAt" DROP NOT NULL, +ALTER COLUMN "deletedAt" DROP DEFAULT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e976a222..1126f317 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -919,11 +919,13 @@ model LayananPolsek { nama String // contoh: "Pelayanan SKCK", "Laporan Kriminal" createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - deletedAt DateTime @default(now()) + deletedAt DateTime? isActive Boolean @default(true) PolsekTerdekat PolsekTerdekat[] } + + // ========================================= KONTAK DARURAT ========================================= // model KontakDaruratKeamanan { id String @id @default(cuid()) @@ -984,22 +986,22 @@ model VideoKeamanan { // ========================================= LAPORAN PUBLIK ========================================= // model LaporanPublik { - id String @id @default(cuid()) - judul String - lokasi String - tanggalWaktu DateTime - status StatusLaporan - penanganan PenangananLaporanPublik[] - kronologi String? // Optional, bisa diisi detail kronologi - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(cuid()) + judul String + lokasi String + tanggalWaktu DateTime + status StatusLaporan + penanganan PenangananLaporanPublik[] + kronologi String? // Optional, bisa diisi detail kronologi + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model PenangananLaporanPublik { - id String @id @default(cuid()) - laporanId String - deskripsi String - laporan LaporanPublik @relation(fields: [laporanId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + laporanId String + deskripsi String + laporan LaporanPublik @relation(fields: [laporanId], references: [id], onDelete: Cascade) } enum StatusLaporan { @@ -1009,13 +1011,14 @@ enum StatusLaporan { } model Pelapor { - id String @id @default(cuid()) - nama String - alamat String - nomorTelepon String - image FileStorage @relation(fields: [imageId], references: [id]) - imageId String + id String @id @default(cuid()) + nama String + alamat String + nomorTelepon String + image FileStorage @relation(fields: [imageId], references: [id]) + imageId String } + // ========================================= TIPS KEAMANAN ========================================= // model MenuTipsKeamanan { id String @id @default(cuid()) diff --git a/src/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan.ts b/src/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan.ts new file mode 100644 index 00000000..3775153a --- /dev/null +++ b/src/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan.ts @@ -0,0 +1,229 @@ +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 templateForm = z.object({ + name: z.string().min(3, "Nama minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), + imageId: z.string().nonempty(), +}); + +const defaultForm = { + name: "", + deskripsi: "", + imageId: "", +}; + +const keamananLingkunganState = proxy({ + create: { + form: { ...defaultForm }, + loading: false, + async create() { + const cek = templateForm.safeParse(keamananLingkunganState.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + + try { + keamananLingkunganState.create.loading = true; + const res = await ApiFetch.api.keamanan.keamananlingkungan[ + "create" + ].post(keamananLingkunganState.create.form); + if (res.status === 200) { + keamananLingkunganState.findMany.load(); + return toast.success("success create"); + } + console.log(res); + return toast.error("failed create"); + } catch (error) { + console.log((error as Error).message); + } finally { + keamananLingkunganState.create.loading = false; + } + }, + resetForm() { + keamananLingkunganState.create.form = { ...defaultForm }; + }, + }, + findMany: { + data: null as + | Prisma.KeamananLingkunganGetPayload<{ + include: { image: true }; + }>[] + | null, + async load() { + const res = await ApiFetch.api.keamanan.keamananlingkungan[ + "find-many" + ].get(); + if (res.status === 200) { + keamananLingkunganState.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.KeamananLingkunganGetPayload<{ + include: { image: true }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/keamanan/keamananlingkungan/${id}`); + if (res.ok) { + const data = await res.json(); + keamananLingkunganState.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + keamananLingkunganState.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + keamananLingkunganState.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + keamananLingkunganState.delete.loading = true; + + const response = await fetch( + `/api/keamanan/keamananlingkungan/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "Keamanan ingkungan berhasil dihapus" + ); + await keamananLingkunganState.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus keamanan ingkungan"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus keamanan ingkungan"); + } finally { + keamananLingkunganState.delete.loading = false; + } + }, + }, + edit: { + id: "", + form: { ...defaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch(`/api/keamanan/keamananlingkungan/${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 = { + name: data.name, + deskripsi: data.deskripsi, + imageId: data.imageId || "", + }; + return data; // Return the loaded data + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading keamanan lingkungan:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = templateForm.safeParse(keamananLingkunganState.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + keamananLingkunganState.edit.loading = true; + + const response = await fetch(`/api/keamanan/keamananlingkungan/${this.id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + name: this.form.name, + deskripsi: this.form.deskripsi, + imageId: this.form.imageId, + }), + }); + + 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 keamanan lingkungan"); + await keamananLingkunganState.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal update keamanan lingkungan"); + } + } catch (error) { + console.error("Error updating keamanan lingkungan:", error); + toast.error( + error instanceof Error + ? error.message + : "Terjadi kesalahan saat update keamanan lingkungan" + ); + return false; + } finally { + keamananLingkunganState.edit.loading = false; + } + }, + reset() { + keamananLingkunganState.edit.id = ""; + keamananLingkunganState.edit.form = { ...defaultForm }; + }, + }, +}); + +export default keamananLingkunganState; diff --git a/src/app/admin/(dashboard)/_state/keamanan/polsek-terdekat.ts b/src/app/admin/(dashboard)/_state/keamanan/polsek-terdekat.ts new file mode 100644 index 00000000..9f490984 --- /dev/null +++ b/src/app/admin/(dashboard)/_state/keamanan/polsek-terdekat.ts @@ -0,0 +1,242 @@ +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 templateForm = z.object({ + nama: z.string().min(3, "Nama minimal 3 karakter"), + jarakKeDesa: z.string().min(3, "Jarak minimal 3 karakter"), + alamat: z.string().min(3, "Alamat minimal 3 karakter"), + nomorTelepon: z.string().min(3, "Nomor Telepon minimal 3 karakter"), + jamOperasional: z.string().min(3, "Jam Operasional minimal 3 karakter"), + embedMapUrl: z.string().min(3, "Embed Map Url minimal 3 karakter"), + namaTempatMaps: z.string().min(3, "Nama Tempat Maps minimal 3 karakter"), + alamatMaps: z.string().min(3, "Alamat Maps minimal 3 karakter"), + linkPetunjukArah: z.string().min(3, "Link Petunjuk Arah minimal 3 karakter"), + layananPolsekId: z.string().min(3, "Layanan Polsek Id minimal 3 karakter"), +}); + +const defaultForm = { + nama: "", + jarakKeDesa: "", + alamat: "", + nomorTelepon: "", + jamOperasional: "", + embedMapUrl: "", + namaTempatMaps: "", + alamatMaps: "", + linkPetunjukArah: "", + layananPolsekId: "", +}; + +const polsekTerdekatState = proxy({ + create: { + form: { ...defaultForm }, + loading: false, + async create() { + const cek = templateForm.safeParse(polsekTerdekatState.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + polsekTerdekatState.create.loading = true; + const res = await ApiFetch.api.keamanan.polsekterdekat["create"].post( + polsekTerdekatState.create.form + ); + if (res.status === 200) { + polsekTerdekatState.findMany.load(); + return toast.success("Data berhasil ditambahkan"); + } + return toast.error("Gagal menambahkan data"); + } catch (error) { + console.log(error); + toast.error("Gagal menambahkan data"); + } finally { + polsekTerdekatState.create.loading = false; + } + }, + }, + findMany: { + data: null as + | Prisma.PolsekTerdekatGetPayload<{ + include: { layananPolsek: true }; + }>[] + | null, + async load() { + const res = await ApiFetch.api.keamanan.polsekterdekat["find-many"].get(); + if (res.status === 200) { + polsekTerdekatState.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.PolsekTerdekatGetPayload<{ + include: { layananPolsek: true }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/keamanan/polsekterdekat/${id}`); + if (res.ok) { + const data = await res.json(); + polsekTerdekatState.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + polsekTerdekatState.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + polsekTerdekatState.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + polsekTerdekatState.delete.loading = true; + + const response = await fetch(`/api/keamanan/polsekterdekat/del/${id}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Polsek terdekat berhasil dihapus"); + await polsekTerdekatState.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus polsek terdekat"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus polsek terdekat"); + } finally { + polsekTerdekatState.delete.loading = false; + } + }, + }, + edit: { + id: "", + form: { ...defaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch(`/api/keamanan/polsekterdekat/${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 = { + nama: data.nama, + jarakKeDesa: data.jarakKeDesa, + alamat: data.alamat, + nomorTelepon: data.nomorTelepon, + jamOperasional: data.jamOperasional, + embedMapUrl: data.embedMapUrl, + namaTempatMaps: data.namaTempatMaps, + alamatMaps: data.alamatMaps, + linkPetunjukArah: data.linkPetunjukArah, + layananPolsekId: data.layananPolsekId, + }; + return data; + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading polsek terdekat:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = templateForm.safeParse(polsekTerdekatState.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + + try { + polsekTerdekatState.edit.loading = true; + const response = await fetch( + `/api/keamanan/polsekterdekat/${this.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nama: this.form.nama, + jarakKeDesa: this.form.jarakKeDesa, + alamat: this.form.alamat, + nomorTelepon: this.form.nomorTelepon, + jamOperasional: this.form.jamOperasional, + embedMapUrl: this.form.embedMapUrl, + namaTempatMaps: this.form.namaTempatMaps, + alamatMaps: this.form.alamatMaps, + linkPetunjukArah: this.form.linkPetunjukArah, + layananPolsekId: this.form.layananPolsekId, + }), + } + ); + 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 polsek terdekat"); + await polsekTerdekatState.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal mengupdate polsek terdekat"); + } + } catch (error) { + console.error("Error updating polsek terdekat:", error); + toast.error( + error instanceof Error + ? error.message + : "Gagal mengupdate polsek terdekat" + ); + return false; + } finally { + polsekTerdekatState.edit.loading = false; + } + }, + reset() { + polsekTerdekatState.edit.id = ""; + polsekTerdekatState.edit.form = { ...defaultForm }; + }, + }, +}); + +export default polsekTerdekatState; diff --git a/src/app/admin/(dashboard)/_state/keamanan/tips-keamanan.ts b/src/app/admin/(dashboard)/_state/keamanan/tips-keamanan.ts new file mode 100644 index 00000000..3925ae61 --- /dev/null +++ b/src/app/admin/(dashboard)/_state/keamanan/tips-keamanan.ts @@ -0,0 +1,212 @@ +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 templateForm = z.object({ + judul: z.string().min(3, "Nama minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), + imageId: z.string().nonempty(), +}); + +const defaultForm = { + judul: "", + deskripsi: "", + imageId: "", +}; + +const tipsKeamananState = proxy({ + create: { + form: { ...defaultForm }, + loading: false, + async create() { + const cek = templateForm.safeParse(tipsKeamananState.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + + try { + tipsKeamananState.create.loading = true; + const res = await ApiFetch.api.keamanan.menutipskeamanan["create"].post( + tipsKeamananState.create.form + ); + if (res.status === 200) { + tipsKeamananState.findMany.load(); + return toast.success("success create"); + } + console.log(res); + return toast.error("failed create"); + } catch (error) { + console.log((error as Error).message); + } finally { + tipsKeamananState.create.loading = false; + } + }, + resetForm() { + tipsKeamananState.create.form = { ...defaultForm }; + }, + }, + findMany: { + data: null as + | Prisma.MenuTipsKeamananGetPayload<{ + include: { image: true }; + }>[] + | null, + async load() { + const res = await ApiFetch.api.keamanan.menutipskeamanan[ + "find-many" + ].get(); + if (res.status === 200) { + tipsKeamananState.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.MenuTipsKeamananGetPayload<{ + include: { image: true }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/keamanan/menutipskeamanan/${id}`); + if (res.ok) { + const data = await res.json(); + tipsKeamananState.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + tipsKeamananState.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + tipsKeamananState.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + try { + tipsKeamananState.delete.loading = true; + const response = await fetch( + `/api/keamanan/menutipskeamanan/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + const result = await response.json(); + if (response.ok && result?.success) { + toast.success(result.message || "Tips keamanan berhasil dihapus"); + await tipsKeamananState.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus tips keamanan"); + } + } catch (error) { + toast.error("Terjadi kesalahan saat menghapus tips keamanan"); + console.error("Gagal delete:", error); + } finally { + tipsKeamananState.delete.loading = false; + } + }, + }, + update: { + id: "", + loading: false, + form: { ...defaultForm }, + + async load(id: string) { + if (!id) return toast.warn("ID tidak valid"); + try { + tipsKeamananState.update.loading = true; + const response = await fetch(`/api/keamanan/menutipskeamanan/${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, + imageId: data.imageId || "", + }; + return data; // Return the loaded data + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error fetching data:", error); + toast.error("Gagal memuat data"); + return null; + } + }, + async update() { + const cek = templateForm.safeParse(tipsKeamananState.update.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + + try { + tipsKeamananState.update.loading = true; + const response = await fetch( + `/api/keamanan/menutipskeamanan/${tipsKeamananState.update.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + judul: this.form.judul, + deskripsi: this.form.deskripsi, + imageId: this.form.imageId, + }), + } + ); + 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 tips keamanan"); + await tipsKeamananState.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal update tips keamanan"); + } + } catch (error) { + console.error("Error updating data:", error); + toast.error("Gagal update data"); + return false; + } finally { + tipsKeamananState.update.loading = false; + } + }, + reset() { + tipsKeamananState.update.id = ""; + tipsKeamananState.update.form = { ...defaultForm }; + }, + }, +}); +export default tipsKeamananState; diff --git a/src/app/admin/(dashboard)/desa/berita/[id]/page.tsx b/src/app/admin/(dashboard)/desa/berita/[id]/page.tsx index a991e6ba..6d7b076b 100644 --- a/src/app/admin/(dashboard)/desa/berita/[id]/page.tsx +++ b/src/app/admin/(dashboard)/desa/berita/[id]/page.tsx @@ -35,9 +35,7 @@ function DetailBerita() { if (!beritaState.berita.findUnique.data) { return ( - {Array.from({ length: 10 }).map((_, k) => ( - - ))} + ) } diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/edit/page.tsx new file mode 100644 index 00000000..df2c9245 --- /dev/null +++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/edit/page.tsx @@ -0,0 +1,179 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +"use client"; + +import { + Box, + Button, + Center, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title +} from "@mantine/core"; +import { IconArrowBack, IconImageInPicture, IconPhoto, IconUpload, IconX } 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"; + + +import EditEditor from "@/app/admin/(dashboard)/_com/editEditor"; +import colors from "@/con/colors"; +import ApiFetch from "@/lib/api-fetch"; +import { Dropzone } from "@mantine/dropzone"; +import keamananLingkunganState from "../../../../_state/keamanan/keamanan-lingkungan"; + +function EditKeamananLingkungan() { + const keamananState = useProxy(keamananLingkunganState); + const router = useRouter(); + const params = useParams(); + + const [previewImage, setPreviewImage] = useState(null); + const [file, setFile] = useState(null); + const [formData, setFormData] = useState({ + name: keamananState.edit.form.name || '', + deskripsi: keamananState.edit.form.deskripsi || '', + imageId: keamananState.edit.form.imageId || '' + }); + + // Load berita by id saat pertama kali + useEffect(() => { + const loadBerita = async () => { + const id = params?.id as string; + if (!id) return; + + try { + const data = await keamananState.edit.load(id); // akses langsung, bukan dari proxy + if (data) { + setFormData({ + name: data.name || '', + deskripsi: data.deskripsi || '', + imageId: data.imageId || '', + }); + + if (data?.image?.link) { + setPreviewImage(data.image.link); + } + } + } catch (error) { + console.error("Error loading keamananLingkungan:", error); + toast.error("Gagal memuat data keamananLingkungan"); + } + }; + + loadBerita(); + }, [params?.id]); // ✅ hapus beritaState dari dependency + + const handleSubmit = async () => { + + try { + // Update global state with form data + keamananState.edit.form = { + ...keamananState.edit.form, + name: formData.name, + deskripsi: formData.deskripsi, + imageId: formData.imageId // Keep existing imageId if not changed + }; + + // Jika ada file baru, upload + if (file) { + 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"); + } + + // Update imageId in global state + keamananState.edit.form.imageId = uploaded.id; + } + + await keamananState.edit.update(); + toast.success("Keamanan Lingkungan berhasil diperbarui!"); + router.push("/admin/keamanan/keamanan-lingkungan-pecalang-patwal"); + } catch (error) { + console.error("Error updating keamananLingkungan:", error); + toast.error("Terjadi kesalahan saat memperbarui keamananLingkungan"); + } + }; + + return ( + + + + + + + Edit Keamanan Lingkungan + { + 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 + +
+
+
+ + {previewImage ? ( + + ) : ( +
+ +
+ )} + setFormData({ ...formData, name: e.target.value })} + label={Judul Keamanan Lingkungan} + placeholder="masukkan judul" + /> + + + Deskripsi + { + setFormData((prev) => ({ ...prev, deskripsi: htmlContent })); + keamananState.edit.form.deskripsi = htmlContent; + }} + /> + + + +
+
+
+ ); +} + +export default EditKeamananLingkungan; diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/page.tsx new file mode 100644 index 00000000..e0118dff --- /dev/null +++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/[id]/page.tsx @@ -0,0 +1,111 @@ +'use client' +import { useProxy } from 'valtio/utils'; + +import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; + +import colors from '@/con/colors'; +import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; +import keamananLingkunganState from '../../../_state/keamanan/keamanan-lingkungan'; + + +function DetailKeamananLingkungan() { + const keamananState = useProxy(keamananLingkunganState) + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) + const params = useParams() + const router = useRouter() + + useShallowEffect(() => { + keamananState.findUnique.load(params?.id as string) + }, []) + + + const handleHapus = () => { + if (selectedId) { + keamananState.delete.byId(selectedId) + setModalHapus(false) + setSelectedId(null) + router.push("/admin/keamanan/keamanan-lingkungan-pecalang-patwal") + } + } + + if (!keamananState.findUnique.data) { + return ( + + + + ) + } + + return ( + + + + + + + Detail Keamanan Lingkungan + {keamananState.findUnique.data ? ( + + + + Judul Keamanan Lingkungan + {keamananState.findUnique.data?.name} + + + Deskripsi + + + + Gambar + gambar + + + + + + + + ) : null} + + + + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text='Apakah anda yakin ingin menghapus keamanan lingkungan ini?' + /> + + ); +} + +export default DetailKeamananLingkungan; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/create/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/create/page.tsx index 224b81fe..d24bc719 100644 --- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/create/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/create/page.tsx @@ -1,43 +1,147 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; -import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react'; +import ApiFetch from '@/lib/api-fetch'; +import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; +import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import { KeamananEditor } from '../../_com/keamananEditor'; +import { useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; +import CreateEditor from '../../../_com/createEditor'; +import keamananLingkunganState from '../../../_state/keamanan/keamanan-lingkungan'; + function CreateKeamananLingkungan() { + const keamananState = useProxy(keamananLingkunganState) + const [previewImage, setPreviewImage] = useState(null); + const [file, setFile] = useState(null); const router = useRouter(); + + const resetForm = () => { + keamananState.create.form = { + name: "", + deskripsi: "", + imageId: "", + } + setPreviewImage(null); + setFile(null); + } + + const handleSubmit = async () => { + if (!file) { + return toast.warn("Pilih file gambar terlebih dahulu"); + } + + const res = await ApiFetch.api.fileStorage.create.post({ + file, + name: file.name, + }) + + const uploaded = res.data?.data; + + if (!uploaded?.id) { + return toast.error("Gagal mengupload file"); + } + + keamananState.create.form.imageId = uploaded.id; + + await keamananState.create.create(); + + resetForm(); + router.push("/admin/keamanan/keamanan-lingkungan-pecalang-patwal") + } + return ( - + Create Keamanan Lingkungan - Masukkan Image - + 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 + +
+
+
+ + {/* Tampilkan preview kalau ada */} + {previewImage && ( + + Preview + + )} + +
Nama Keamanan Lingkungan} - placeholder='Masukkan nama KeamananLingkungan' + value={keamananState.create.form.name} + onChange={(val) => { + keamananState.create.form.name = val.target.value; + }} + label={Nama Keamanan Lingkungan} + placeholder='Masukkan nama Keamanan Lingkungan' /> - Deskripsi KeamananLingkungan - Deskripsi Keamanan Lingkungan + { + keamananState.create.form.deskripsi = val; + }} /> - + -
+
-
+ ); } diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/detail/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/detail/page.tsx deleted file mode 100644 index e7d52a38..00000000 --- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/detail/page.tsx +++ /dev/null @@ -1,70 +0,0 @@ -'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 React from 'react'; -// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; - -function DetailKeamananLingkungan() { - const router = useRouter(); - return ( - - - - - - - Detail Keamanan Lingkungan - - - - - Nama Keamanan Lingkungan - Test Judul - - - Nomor Keamanan Lingkungan - Test Kategori - - - Deskripsi - Test Deskripsi - - - Gambar - gambar - - - Konten - Test Konten - - - - - - - - - - - - - {/* Modal Hapus - setModalHapus(false)} - onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus potensi ini?" - /> */} - - ); -} - -export default DetailKeamananLingkungan; diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/edit/page.tsx deleted file mode 100644 index 35be3435..00000000 --- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/edit/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; -import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { KeamananEditor } from '../../_com/keamananEditor'; - -function EditKeamananLingkungan() { - const router = useRouter(); - return ( - - - - - - - - Edit Keamanan Lingkungan - - Masukkan Image - - - Nama Keamanan Lingkungan} - placeholder='Masukkan nama Keamanan Lingkungan' - /> - - Deskripsi Keamanan Lingkungan - - - - - - - - - ); -} - -export default EditKeamananLingkungan; diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx index 7a6cefe7..34a39351 100644 --- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx @@ -1,10 +1,13 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { Box, Button, Paper, Skeleton, Stack, 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 keamananLingkunganState from '../../_state/keamanan/keamanan-lingkungan'; +import { useShallowEffect } from '@mantine/hooks'; function KeamananLingkungan() { return ( @@ -14,13 +17,26 @@ function KeamananLingkungan() { placeholder='pencarian' searchIcon={} /> - + ); } function ListKeamananLingkungan() { + const keamananState = useProxy(keamananLingkunganState) const router = useRouter(); + + useShallowEffect(() => { + keamananState.findMany.load() + }, []) + + if (!keamananState.findMany.data) { + return ( + + + + ) + } return ( @@ -32,24 +48,26 @@ function ListKeamananLingkungan() { Nama Keamanan Lingkungan - Nomor Keamanan Lingkungan Deskripsi Detail - + - - Keamanan Lingkungan 1 - 0896232831883 - Keamanan Lingkungan 1 - - - - + {keamananState.findMany.data?.map((item) => ( + + {item.name} + + + + + + + + ))} - + ); diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx new file mode 100644 index 00000000..39778f11 --- /dev/null +++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/[id]/edit/page.tsx @@ -0,0 +1,372 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Card, Group, Modal, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useProxy } from 'valtio/utils'; + +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import polsekTerdekat from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat'; + +function CreatePolsekTerdekat() { + const polsekState = useProxy(polsekTerdekat) + const params = useParams(); + const router = useRouter(); + const [layananOptions, setLayananOptions] = useState<{ value: string; label: string }[]>([]); + const [modalOpen, setModalOpen] = useState(false); + const [modalUpdateOpen, setModalUpdateOpen] = useState(false); + const [namaLayananBaru, setNamaLayananBaru] = useState(""); + const [selectedLayananId, setSelectedLayananId] = useState(null); + const [namaLayananUpdate, setNamaLayananUpdate] = useState(""); + const [formData, setFormData] = useState({ + nama: "", + jarakKeDesa: "", + alamat: "", + nomorTelepon: "", + jamOperasional: "", + embedMapUrl: "", + namaTempatMaps: "", + alamatMaps: "", + linkPetunjukArah: "", + layananPolsekId: "", + }) + + useEffect(() => { + const loadPolsekTerdekat = async () => { + const id = params?.id as string; + if (!id) return; + + try { + const data = await polsekState.edit.load(id); + if (data) { + setFormData({ + nama: data.nama || '', + jarakKeDesa: data.jarakKeDesa || '', + alamat: data.alamat || '', + nomorTelepon: data.nomorTelepon || '', + jamOperasional: data.jamOperasional || '', + embedMapUrl: data.embedMapUrl || '', + namaTempatMaps: data.namaTempatMaps || '', + alamatMaps: data.alamatMaps || '', + linkPetunjukArah: data.linkPetunjukArah || '', + layananPolsekId: data.layananPolsekId || '', + }); + } + } catch (error) { + console.error("Error loading polsek terdekat:", error); + toast.error("Gagal memuat data polsek terdekat"); + } + }; + + loadPolsekTerdekat(); + }, [params?.id]); + + + const handleSubmit = async () => { + try { + polsekState.edit.form = { + ...polsekState.edit.form, + nama: formData.nama, + jarakKeDesa: formData.jarakKeDesa, + alamat: formData.alamat, + nomorTelepon: formData.nomorTelepon, + jamOperasional: formData.jamOperasional, + embedMapUrl: formData.embedMapUrl, + namaTempatMaps: formData.namaTempatMaps, + alamatMaps: formData.alamatMaps, + linkPetunjukArah: formData.linkPetunjukArah, + layananPolsekId: formData.layananPolsekId, + } + await polsekState.edit.update() + toast.success("Polsek terdekat berhasil diperbarui!") + router.push("/admin/keamanan/polsek-terdekat") + } catch (error) { + console.error("Error updating polsek terdekat:", error); + toast.error("Gagal memuat data polsek terdekat"); + } + } + + const fetchLayanan = async () => { + try { + const res = await fetch("/api/keamanan/layanan-polsek/find-many") + const data = await res.json() + + if (data.success) { + const options = data.data.map((item: any) => { + return { + value: item.id, + label: item.nama + } + }) + setLayananOptions(options) + } + } catch { + toast.error("Gagal memuat layanan polsek") + } + } + + + const handleTambahLayanan = async () => { + if (!namaLayananBaru.trim()) return toast.warn("Nama layanan tidak boleh kosong"); + + try { + const res = await fetch("/api/keamanan/layanan-polsek/create", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ nama: namaLayananBaru }), + }); + const data = await res.json(); + console.log("data setelah create:", data); + + if (data.success) { + const newLayanan = { + value: data.data.id, + label: data.data.nama, + }; + setLayananOptions((prev) => [...prev, newLayanan]); + await fetchLayanan(); + polsekState.create.form.layananPolsekId = data.data.id; + toast.success("Layanan baru ditambahkan!"); + setModalOpen(false); + setNamaLayananBaru(""); + } else { + toast.error(data.message || "Gagal menambah layanan"); + } + } catch { + toast.error("Error menambah layanan"); + } + }; + + const handleUpdateLayanan = async (id: string, namaBaru: string) => { + if (!namaBaru.trim()) return toast.warn("Nama layanan tidak boleh kosong"); + + try { + const res = await fetch(`/api/keamanan/layanan-polsek/update/${id}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ nama: namaBaru }), + }); + const data = await res.json(); + console.log("data setelah update:", data); + + if (data.success) { + await fetchLayanan(); // Refresh list + toast.success("Layanan berhasil diupdate!"); + setModalUpdateOpen(false); + setNamaLayananUpdate(""); + } else { + toast.error(data.message || "Gagal mengupdate layanan"); + } + } catch { + toast.error("Error mengupdate layanan"); + } + }; + + const handleDeleteLayanan = async (id: string) => { + const confirmDelete = confirm("Yakin ingin menghapus layanan ini?"); + if (!confirmDelete) return; + + try { + const res = await fetch(`/api/keamanan/layanan-polsek/del/${id}`, { + method: "DELETE", + }); + const data = await res.json(); + console.log("data setelah delete:", data); + + if (data.success) { + await fetchLayanan(); // Refresh list + setLayananOptions((prev) => prev.filter((layanan) => layanan.value !== id)); + toast.success("Layanan berhasil dihapus!"); + } else { + toast.error(data.message || "Gagal menghapus layanan"); + } + } catch { + toast.error("Error menghapus layanan"); + } + }; + + + useEffect(() => { + fetchLayanan(); + }, []); + + return ( + + setModalOpen(false)} + title="Tambah Layanan Polsek" + centered + > + + setNamaLayananBaru(e.currentTarget.value)} + /> + + + + + setModalUpdateOpen(false)} + title="Tambah Layanan Polsek" + centered + > + + setNamaLayananUpdate(e.currentTarget.value)} + /> + + + + + + + + + + + Create Polsek Terdekat + { + formData.nama = val.target.value + }} + label={Nama Polsek Terdekat} + placeholder='Masukkan nama Polsek Terdekat' + /> + { + formData.jarakKeDesa = val.target.value + }} + label={Jarak Polsek Terdekat} + placeholder='Masukkan jarak Polsek Terdekat' + /> + { + formData.alamat = val.target.value + }} + label={Alamat Polsek Terdekat} + placeholder='Masukkan alamat Polsek Terdekat' + /> + { + formData.nomorTelepon = val.target.value + }} + label={Nomor Telepon Polsek Terdekat} + placeholder='Masukkan nomor telepon Polsek Terdekat' + /> + { + formData.jamOperasional = val.target.value + }} + label={Jam Operasional Polsek Terdekat} + placeholder='Masukkan jam operasional Polsek Terdekat' + /> + { + formData.embedMapUrl = val.target.value + }} + label={Embed Map URL Polsek Terdekat} + placeholder='Masukkan embed map url Polsek Terdekat' + /> + { + formData.namaTempatMaps = val.target.value + }} + label={Nama Tempat Maps Polsek Terdekat} + placeholder='Masukkan nama tempat maps Polsek Terdekat' + /> + { + formData.alamatMaps = val.target.value + }} + label={Alamat Maps Polsek Terdekat} + placeholder='Masukkan alamat maps Polsek Terdekat' + /> + { + formData.linkPetunjukArah = val.target.value + }} + label={Link Petunjuk Arah Polsek Terdekat} + placeholder='Masukkan link petunjuk arah Polsek Terdekat' + /> + {/* Tambah Dropdown Select */} + { + polsekState.create.form.layananPolsekId = val || ""; + }} + /> + + + {/* Tambah field lainnya di sini... */} - + ); + } export default CreatePolsekTerdekat; diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/detail/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/detail/page.tsx deleted file mode 100644 index b981cb6f..00000000 --- a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/detail/page.tsx +++ /dev/null @@ -1,70 +0,0 @@ -'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 React from 'react'; -// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; - -function DetailPolsekTerdekat() { - const router = useRouter(); - return ( - - - - - - - Detail Polsek Terdekat - - - - - Nama Polsek Terdekat - Test Judul - - - Nomor Polsek Terdekat - Test Kategori - - - Deskripsi - Test Deskripsi - - - Gambar - gambar - - - Konten - Test Konten - - - - - - - - - - - - - {/* Modal Hapus - setModalHapus(false)} - onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus potensi ini?" - /> */} - - ); -} - -export default DetailPolsekTerdekat; diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/edit/page.tsx deleted file mode 100644 index ef12dd43..00000000 --- a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/edit/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; -import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { KeamananEditor } from '../../_com/keamananEditor'; - -function EditPolsekTerdekat() { - const router = useRouter(); - return ( - - - - - - - - Edit Polsek Terdekat - - Masukkan Image - - - Nama Polsek Terdekat} - placeholder='Masukkan nama Polsek Terdekat' - /> - - Deskripsi Polsek Terdekat - - - - - - - - - ); -} - -export default EditPolsekTerdekat; diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/page.tsx index 7d8d8e51..043e74ef 100644 --- a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/page.tsx @@ -1,10 +1,13 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; import { IconDeviceImac, 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 { useRouter } from 'next/navigation'; +import polsekTerdekat from '../../_state/keamanan/polsek-terdekat'; function PolsekTerdekat() { return ( @@ -20,7 +23,20 @@ function PolsekTerdekat() { } function ListPolsekTerdekat() { + const polsekState = useProxy(polsekTerdekat) const router = useRouter(); + + useShallowEffect(() => { + polsekState.findMany.load() + }, []) + + if (!polsekState.findMany.data) { + return ( + + + + ) + } return ( @@ -32,22 +48,24 @@ function ListPolsekTerdekat() { Nama Polsek Terdekat - Nomor Polsek Terdekat - Deskripsi + Jarak Polsek + Alamat Detail - - Polsek Terdekat 1 - 0896232831883 - Polsek Terdekat 1 - - + ))} diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/edit/page.tsx new file mode 100644 index 00000000..8b7cde15 --- /dev/null +++ b/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/edit/page.tsx @@ -0,0 +1,179 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +"use client"; + +import { + Box, + Button, + Center, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title +} from "@mantine/core"; +import { IconArrowBack, IconImageInPicture, IconPhoto, IconUpload, IconX } 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"; + + +import EditEditor from "@/app/admin/(dashboard)/_com/editEditor"; +import colors from "@/con/colors"; +import ApiFetch from "@/lib/api-fetch"; +import { Dropzone } from "@mantine/dropzone"; +import tipsKeamananState from "../../../../_state/keamanan/tips-keamanan"; + +function EditTipsKeamanan() { + const keamananState = useProxy(tipsKeamananState); + const router = useRouter(); + const params = useParams(); + + const [previewImage, setPreviewImage] = useState(null); + const [file, setFile] = useState(null); + const [formData, setFormData] = useState({ + judul: keamananState.update.form.judul || '', + deskripsi: keamananState.update.form.deskripsi || '', + imageId: keamananState.update.form.imageId || '' + }); + + // Load berita by id saat pertama kali + useEffect(() => { + const loadBerita = async () => { + const id = params?.id as string; + if (!id) return; + + try { + const data = await keamananState.update.load(id); // akses langsung, bukan dari proxy + if (data) { + setFormData({ + judul: data.judul || '', + deskripsi: data.deskripsi || '', + imageId: data.imageId || '', + }); + + if (data?.image?.link) { + setPreviewImage(data.image.link); + } + } + } catch (error) { + console.error("Error loading keamananLingkungan:", error); + toast.error("Gagal memuat data keamananLingkungan"); + } + }; + + loadBerita(); + }, [params?.id]); // ✅ hapus beritaState dari dependency + + const handleSubmit = async () => { + + try { + // Update global state with form data + keamananState.update.form = { + ...keamananState.update.form, + judul: formData.judul, + deskripsi: formData.deskripsi, + imageId: formData.imageId // Keep existing imageId if not changed + }; + + // Jika ada file baru, upload + if (file) { + 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"); + } + + // Update imageId in global state + keamananState.update.form.imageId = uploaded.id; + } + + await keamananState.update.update(); + toast.success("Tips Keamanan berhasil diperbarui!"); + router.push("/admin/keamanan/tips-keamanan"); + } catch (error) { + console.error("Error updating keamananLingkungan:", error); + toast.error("Terjadi kesalahan saat memperbarui keamananLingkungan"); + } + }; + + return ( + + + + + + + Edit Tips Keamanan + { + 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 + +
+
+
+ + {previewImage ? ( + + ) : ( +
+ +
+ )} + setFormData({ ...formData, judul: e.target.value })} + label={Nama Tips Keamanan} + placeholder="masukkan nama tips keamanan" + /> + + + Deskripsi + { + setFormData((prev) => ({ ...prev, deskripsi: htmlContent })); + keamananState.update.form.deskripsi = htmlContent; + }} + /> + + + +
+
+
+ ); +} + +export default EditTipsKeamanan; diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/page.tsx new file mode 100644 index 00000000..4f9fe631 --- /dev/null +++ b/src/app/admin/(dashboard)/keamanan/tips-keamanan/[id]/page.tsx @@ -0,0 +1,111 @@ +'use client' +import { useProxy } from 'valtio/utils'; + +import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; + +import colors from '@/con/colors'; +import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; +import tipsKeamananState from '../../../_state/keamanan/tips-keamanan'; + + +function DetailTipsKeamanan() { + const stateKeamanan = useProxy(tipsKeamananState) + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) + const params = useParams() + const router = useRouter() + + useShallowEffect(() => { + stateKeamanan.findUnique.load(params?.id as string) + }, []) + + + const handleHapus = () => { + if (selectedId) { + stateKeamanan.delete.byId(selectedId) + setModalHapus(false) + setSelectedId(null) + router.push("/admin/keamanan/tips-keamanan") + } + } + + if (!stateKeamanan.findUnique.data) { + return ( + + + + ) + } + + return ( + + + + + + + Detail Tips Keamanan + {stateKeamanan.findUnique.data ? ( + + + + Nama Tips Keamanan + {stateKeamanan.findUnique.data?.judul} + + + Deskripsi + + + + Gambar + gambar + + + + + + + + ) : null} + + + + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text='Apakah anda yakin ingin menghapus tips keamanan ini?' + /> + + ); +} + +export default DetailTipsKeamanan; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/create/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/create/page.tsx index 7012f30a..023d1b01 100644 --- a/src/app/admin/(dashboard)/keamanan/tips-keamanan/create/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/tips-keamanan/create/page.tsx @@ -1,44 +1,148 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; -import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react'; +import ApiFetch from '@/lib/api-fetch'; +import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; +import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import { KeamananEditor } from '../../_com/keamananEditor'; +import { useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; +import CreateEditor from '../../../_com/createEditor'; +import tipsKeamananState from '../../../_state/keamanan/tips-keamanan'; -function CreateTipsKeamanan() { + +function CreateKeamananLingkungan() { + const stateKeamanan = useProxy(tipsKeamananState) + const [previewImage, setPreviewImage] = useState(null); + const [file, setFile] = useState(null); const router = useRouter(); + + const resetForm = () => { + stateKeamanan.create.form = { + judul: "", + deskripsi: "", + imageId: "", + } + setPreviewImage(null); + setFile(null); + } + + const handleSubmit = async () => { + if (!file) { + return toast.warn("Pilih file gambar terlebih dahulu"); + } + + const res = await ApiFetch.api.fileStorage.create.post({ + file, + name: file.name, + }) + + const uploaded = res.data?.data; + + if (!uploaded?.id) { + return toast.error("Gagal mengupload file"); + } + + stateKeamanan.create.form.imageId = uploaded.id; + + await stateKeamanan.create.create(); + + resetForm(); + router.push("/admin/keamanan/tips-keamanan") + } + return ( - + Create Tips Keamanan - Masukkan Image - + 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 + +
+
+
+ + {/* Tampilkan preview kalau ada */} + {previewImage && ( + + Preview + + )} + +
Nama Tips Keamanan} - placeholder='Masukkan nama tips keamanan' + value={stateKeamanan.create.form.judul} + onChange={(val) => { + stateKeamanan.create.form.judul = val.target.value; + }} + label={Nama Tips Keamanan} + placeholder='Masukkan nama Tips Keamanan' /> Deskripsi Tips Keamanan - { + stateKeamanan.create.form.deskripsi = val; + }} /> - + -
+
-
+
); } -export default CreateTipsKeamanan; +export default CreateKeamananLingkungan; diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/detail/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/detail/page.tsx deleted file mode 100644 index 194c46aa..00000000 --- a/src/app/admin/(dashboard)/keamanan/tips-keamanan/detail/page.tsx +++ /dev/null @@ -1,70 +0,0 @@ -'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 React from 'react'; -// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; - -function DetailTipsKeamanan() { - const router = useRouter(); - return ( - - - - - - - Detail Tips Keamanan - - - - - Nama Tips Keamanan - Test Judul - - - Nomor Tips Keamanan - Test Kategori - - - Deskripsi - Test Deskripsi - - - Gambar - gambar - - - Konten - Test Konten - - - - - - - - - - - - - {/* Modal Hapus - setModalHapus(false)} - onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus potensi ini?" - /> */} - - ); -} - -export default DetailTipsKeamanan; diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/edit/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/edit/page.tsx deleted file mode 100644 index fe6d6a9f..00000000 --- a/src/app/admin/(dashboard)/keamanan/tips-keamanan/edit/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; -import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { KeamananEditor } from '../../_com/keamananEditor'; - -function EditTipsKeamanan() { - const router = useRouter(); - return ( - - - - - - - - Edit Tips Keamanan - - Masukkan Image - - - Nama Tips Keamanan} - placeholder='Masukkan nama Tips Keamanan' - /> - - Deskripsi Tips Keamanan - - - - - - - - - ); -} - -export default EditTipsKeamanan; diff --git a/src/app/admin/(dashboard)/keamanan/tips-keamanan/page.tsx b/src/app/admin/(dashboard)/keamanan/tips-keamanan/page.tsx index 995fba9d..7169aafe 100644 --- a/src/app/admin/(dashboard)/keamanan/tips-keamanan/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/tips-keamanan/page.tsx @@ -1,10 +1,13 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { Box, Button, Paper, Skeleton, Stack, 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 tipsKeamananState from '../../_state/keamanan/tips-keamanan'; +import { useProxy } from 'valtio/utils'; +import { useShallowEffect } from '@mantine/hooks'; function TipsKeamanan() { return ( @@ -20,7 +23,20 @@ function TipsKeamanan() { } function ListTipsKeamanan() { + const stateKeamanan = useProxy(tipsKeamananState) const router = useRouter(); + + useShallowEffect(() => { + stateKeamanan.findMany.load() + }, []) + + if (!stateKeamanan.findMany.data) { + return ( + + + + ) + } return ( @@ -32,22 +48,24 @@ function ListTipsKeamanan() { Nama Tips Keamanan - Nomor Tips Keamanan Deskripsi Detail - - Tips Keamanan 1 - 0896232831883 - Tips Keamanan 1 - - + ))} diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/index.ts index b6446c29..60d3bea7 100644 --- a/src/app/api/[[...slugs]]/_lib/keamanan/index.ts +++ b/src/app/api/[[...slugs]]/_lib/keamanan/index.ts @@ -5,6 +5,7 @@ import KontakDarurat from "./kontak-darurat"; import PencegahanKriminalitas from "./pencegahan-kriminalitas"; import MenuTipsKeamanan from "./tips-keamanan"; import LaporanPublik from "./laporan-publik"; +import LayananPolsek from "./layanan-polsek"; const Keamanan = new Elysia({ prefix: "/api/keamanan", tags: ["Keamanan"] }) .use(KeamananLingkungan) @@ -13,4 +14,5 @@ const Keamanan = new Elysia({ prefix: "/api/keamanan", tags: ["Keamanan"] }) .use(PencegahanKriminalitas) .use(MenuTipsKeamanan) .use(LaporanPublik) +.use(LayananPolsek) export default Keamanan; diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/del.ts b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/del.ts index a9f5d3aa..a7a5a7fa 100644 --- a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/del.ts +++ b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/del.ts @@ -47,6 +47,8 @@ const keamananLingkunganDelete = async (context: Context) => { return { status: 200, body: deleted, + success: true, + message: "Keamanan lingkungan berhasil dihapus", }; }; export default keamananLingkunganDelete; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/updt.ts b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/updt.ts index 8418e7d9..b60cbdf0 100644 --- a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/updt.ts +++ b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/updt.ts @@ -5,93 +5,106 @@ import fs from "fs/promises"; import { Context } from "elysia"; type FormUpdate = Prisma.KeamananLingkunganGetPayload<{ - select: { - name: true; - deskripsi: true; - imageId: true; - }; + select: { + id: true; + name: true; + deskripsi: true; + imageId: true; + }; }>; export default async function updateKeamananLingkungan(context: Context) { - try { - const id = context.params?.id; - const body = (await context.body) as Omit; + try { + const id = context.params?.id; + const body = (await context.body) as Omit; - const { name, deskripsi, imageId } = body; + const { name, deskripsi, imageId } = body; - if (!id) { - return new Response(JSON.stringify({ - success: false, - message: "ID tidak diberikan", - }), { - status: 400, - headers: { - "Content-Type": "application/json", - }, - }); + if (!id) { + return new Response( + JSON.stringify({ + success: false, + message: "ID tidak diberikan", + }), + { + status: 400, + headers: { + "Content-Type": "application/json", + }, } - const existing = await prisma.keamananLingkungan.findUnique({ - where: { id }, - include: { - image: true, - } - }); - - if (!existing) { - return new Response(JSON.stringify({ - success: false, - message: "Keamanan lingkungan tidak ditemukan", - }), { - status: 404, - headers: { - "Content-Type": "application/json", - }, - }); - } - - if (existing.imageId && existing.imageId !== imageId) { - const oldImage = existing.image; - if (oldImage) { - try { - const filePath = path.join(oldImage.path, oldImage.name); - await fs.unlink(filePath); - await prisma.fileStorage.delete({ - where: { id: oldImage.id }, - }); - } catch (err) { - console.error("Gagal hapus gambar lama:", err); - } - } - } - - const updated = await prisma.keamananLingkungan.update({ - where: { id }, - data: { - name, - deskripsi, - imageId, - }, - }) - - return new Response(JSON.stringify({ - success: true, - message: "Success update keamanan lingkungan", - data: updated, - }), { - status: 200, - headers: { - "Content-Type": "application/json", - }, - }); - } catch (error) { - console.error("Error updating keamanan lingkungan:", error); - return new Response(JSON.stringify({ - success: false, - message: "Terjadi kesalahan saat mengupdate keamanan lingkungan", - }), { - status: 500, - headers: { - "Content-Type": "application/json", - }, - }); + ); } -} \ No newline at end of file + const existing = await prisma.keamananLingkungan.findUnique({ + where: { id }, + include: { + image: true, + }, + }); + + if (!existing) { + return new Response( + JSON.stringify({ + success: false, + message: "Keamanan lingkungan tidak ditemukan", + }), + { + status: 404, + headers: { + "Content-Type": "application/json", + }, + } + ); + } + + if (existing.imageId && existing.imageId !== imageId) { + const oldImage = existing.image; + if (oldImage) { + try { + const filePath = path.join(oldImage.path, oldImage.name); + await fs.unlink(filePath); + await prisma.fileStorage.delete({ + where: { id: oldImage.id }, + }); + } catch (err) { + console.error("Gagal hapus gambar lama:", err); + } + } + } + + const updated = await prisma.keamananLingkungan.update({ + where: { id }, + data: { + name, + deskripsi, + imageId, + }, + }); + + return new Response( + JSON.stringify({ + success: true, + message: "Success update keamanan lingkungan", + data: updated, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); + } catch (error) { + console.error("Error updating keamanan lingkungan:", error); + return new Response( + JSON.stringify({ + success: false, + message: "Terjadi kesalahan saat mengupdate keamanan lingkungan", + }), + { + status: 500, + headers: { + "Content-Type": "application/json", + }, + } + ); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/create.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/create.ts new file mode 100644 index 00000000..009121f9 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/create.ts @@ -0,0 +1,26 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function layananPolsekCreate(context: Context) { + const body = context.body as { nama: string }; + + if (!body.nama) { + return { + success: false, + message: "Nama is required", + }; + } + + const layanan = await prisma.layananPolsek.create({ + data: { + nama: body.nama, + deletedAt: null, // pastikan ini ditambahkan kalau field-mu optional + }, + }); + + return { + success: true, + message: "Success create layanan polsek", + data: layanan, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/del.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/del.ts new file mode 100644 index 00000000..55a5ff51 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/del.ts @@ -0,0 +1,31 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export const layananPolsekDelete = async (context: Context) => { + const id = context.params.id; + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + const layanan = await prisma.layananPolsek.delete({ + where: { + id: id, + }, + }) + + if(!layanan) { + return { + success: false, + message: "Layanan polsek tidak ditemukan", + } + } + + return { + success: true, + message: "Success delete layanan polsek", + data: layanan, + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findMany.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findMany.ts new file mode 100644 index 00000000..a5028d6e --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findMany.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; + + async function layananPolsekFindMany() { + const data = await prisma.layananPolsek.findMany(); + return { + success: true, + data: data.map((item: any) => { + return { + id: item.id, + nama: item.nama, + } + }), + }; +} + +export default layananPolsekFindMany + diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findUnique.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findUnique.ts new file mode 100644 index 00000000..343e2aae --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/findUnique.ts @@ -0,0 +1,48 @@ +import prisma from "@/lib/prisma"; + +export default async function layananPolsekFindUnique(request: Request){ + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if(!id){ + return Response.json({ + success: false, + message: "ID tidak boleh kosong", + }, { status: 400 }); + } + + try { + if (typeof id !== 'string') { + return Response.json({ + success: false, + message: "ID tidak valid", + }, { status: 400 }); + } + + const data = await prisma.layananPolsek.findUnique({ + where: { id }, + }); + + if (!data) { + return Response.json({ + success: false, + message: "Layanan polsek tidak ditemukan", + }, { status: 404 }); + } + + return Response.json({ + success: true, + message: "Success fetch layanan polsek by ID", + data, + }, { status: 200 }); + } catch (e) { + console.error("Find by ID error:", e); + return Response.json({ + success: false, + message: "Gagal mengambil layanan polsek: " + (e instanceof Error ? e.message : 'Unknown error'), + }, { + status: 500, + }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/index.ts new file mode 100644 index 00000000..efbd270c --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/index.ts @@ -0,0 +1,32 @@ +import Elysia from "elysia"; +import layananPolsekCreate from "./create"; +import layananPolsekFindMany from "./findMany"; +import { t } from "elysia"; +import layananPolsekUpdate from "./updt"; +import layananPolsekFindUnique from "./findUnique"; +import { layananPolsekDelete } from "./del"; + +const LayananPolsek = new Elysia({ + prefix: "/layanan-polsek", + tags: ["Keamanan/Polsek Terdekat/Layanan Polsek"], +}) + .get("/find-many", layananPolsekFindMany) + .get("/:id", async (context) => { + const response = await layananPolsekFindUnique( + new Request(context.request) + ); + return response; + }) + .delete("/del/:id", layananPolsekDelete) + .post("/create", layananPolsekCreate, { + body: t.Object({ + nama: t.String(), + }), + }) + .put("/:id", layananPolsekUpdate, { + body: t.Object({ + nama: t.String(), + }), + }); + +export default LayananPolsek; diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/updt.ts b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/updt.ts new file mode 100644 index 00000000..b6eab8d0 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/layanan-polsek/updt.ts @@ -0,0 +1,29 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + + +export default async function layananPolsekUpdate(context: Context) { + const body = await context.request.json() + + if (!body.nama) { + return { + success: false, + message: "Nama is required", + } + } + + const layanan = await prisma.layananPolsek.update({ + where: { + id: body.id, + }, + data: { + nama: body.nama, + }, + }) + + return { + success: true, + message: "Success update layanan polsek", + data: layanan, + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts index e060ae1f..42fc6c92 100644 --- a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts +++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts @@ -30,8 +30,9 @@ const polsekTerdekatDelete = async (context: Context) => { }); return { + success: true, status: 200, - body: "Polsek terdekat berhasil dihapus", + message: "Polsek terdekat berhasil dihapus", }; }; diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts index 2b2ff147..23382476 100644 --- a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts +++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts @@ -1,10 +1,11 @@ import Elysia, { t } from "elysia"; +import polsekTerdekatCreate from "./create"; +import polsekTerdekatDelete from "./del"; import polsekTerdekatFindMany from "./findMany"; import polsekTerdekatFindUnique from "./findUnique"; -import polsekTerdekatDelete from "./del"; -import polsekTerdekatCreate from "./create"; import polsekTerdekatUpdate from "./updt"; + const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/Polsek Terdekat"] }) .get("/find-many", polsekTerdekatFindMany) .get("/:id", async (context) => { @@ -44,4 +45,5 @@ const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/ layananPolsekId: t.String(), }), }) + export default PolsekTerdekat; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/tips-keamanan/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/tips-keamanan/index.ts index 6b68157f..023de508 100644 --- a/src/app/api/[[...slugs]]/_lib/keamanan/tips-keamanan/index.ts +++ b/src/app/api/[[...slugs]]/_lib/keamanan/tips-keamanan/index.ts @@ -18,7 +18,7 @@ const MenuTipsKeamanan = new Elysia({ }) .post("/create", menuTipsKeamananCreate, { body: t.Object({ - name: t.String(), + judul: t.String(), deskripsi: t.String(), imageId: t.String(), }), @@ -32,7 +32,7 @@ const MenuTipsKeamanan = new Elysia({ }, { body: t.Object({ - name: t.String(), + judul: t.String(), deskripsi: t.String(), imageId: t.String(), }),