diff --git a/bun.lockb b/bun.lockb
index 6da03f4d..a4438762 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index a737af3f..f347056c 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"@tiptap/react": "^2.11.7",
"@tiptap/starter-kit": "^2.11.7",
"@types/bun": "^1.2.2",
+ "@types/leaflet": "^1.9.20",
"@types/lodash": "^4.17.16",
"add": "^2.0.6",
"animate.css": "^4.1.1",
@@ -53,6 +54,7 @@
"framer-motion": "^12.23.5",
"get-port": "^7.1.0",
"jotai": "^2.12.3",
+ "leaflet": "^1.9.4",
"list": "^2.0.19",
"lodash": "^4.17.21",
"motion": "^12.4.1",
@@ -64,6 +66,7 @@
"prisma": "^6.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
+ "react-leaflet": "^5.0.0",
"react-simple-toasts": "^6.1.0",
"react-toastify": "^11.0.5",
"readdirp": "^4.1.1",
diff --git a/prisma/migrations/20250718031959_nico_18_jul_25/migration.sql b/prisma/migrations/20250718031959_nico_18_jul_25/migration.sql
new file mode 100644
index 00000000..f1569287
--- /dev/null
+++ b/prisma/migrations/20250718031959_nico_18_jul_25/migration.sql
@@ -0,0 +1,67 @@
+-- CreateTable
+CREATE TABLE "PengaduanMasyarakat" (
+ "id" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "email" TEXT NOT NULL,
+ "nomorTelepon" TEXT NOT NULL,
+ "nik" TEXT NOT NULL,
+ "judulPengaduan" TEXT NOT NULL,
+ "lokasiKejadian" TEXT NOT NULL,
+ "imageId" TEXT NOT NULL,
+ "deskripsiPengaduan" TEXT NOT NULL,
+ "jenisPengaduanId" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
+
+ CONSTRAINT "PengaduanMasyarakat_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "JenisPengaduan" (
+ "id" TEXT NOT NULL,
+ "nama" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
+
+ CONSTRAINT "JenisPengaduan_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "PengelolaanSampah" (
+ "id" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "icon" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
+
+ CONSTRAINT "PengelolaanSampah_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "KeteranganBankSampahTerdekat" (
+ "id" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "alamat" TEXT NOT NULL,
+ "namaTempatMaps" TEXT NOT NULL,
+ "linkPetunjukArah" TEXT NOT NULL,
+ "lat" DOUBLE PRECISION NOT NULL,
+ "lng" DOUBLE PRECISION NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
+
+ CONSTRAINT "KeteranganBankSampahTerdekat_pkey" PRIMARY KEY ("id")
+);
+
+-- AddForeignKey
+ALTER TABLE "PengaduanMasyarakat" ADD CONSTRAINT "PengaduanMasyarakat_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "PengaduanMasyarakat" ADD CONSTRAINT "PengaduanMasyarakat_jenisPengaduanId_fkey" FOREIGN KEY ("jenisPengaduanId") REFERENCES "JenisPengaduan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 6bcd7c19..d0152d76 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -1457,3 +1457,18 @@ model PengelolaanSampah {
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
+
+model KeteranganBankSampahTerdekat {
+ id String @id @default(cuid())
+ name String
+ alamat String
+ namaTempatMaps String
+ linkPetunjukArah String
+ lat Float
+ lng Float
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime @default(now())
+ isActive Boolean @default(true)
+}
+
diff --git a/src/app/admin/(dashboard)/_com/leafletMapCreate.tsx b/src/app/admin/(dashboard)/_com/leafletMapCreate.tsx
new file mode 100644
index 00000000..4b4282ab
--- /dev/null
+++ b/src/app/admin/(dashboard)/_com/leafletMapCreate.tsx
@@ -0,0 +1,59 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+'use client';
+
+import { MapContainer, TileLayer, Marker, useMapEvents } from 'react-leaflet';
+import { useEffect, useState } from 'react';
+import 'leaflet/dist/leaflet.css';
+import L, { LeafletMouseEvent } from 'leaflet';
+
+type Props = {
+ defaultCenter: { lat: number; lng: number };
+ onSelect?: (pos: { lat: number; lng: number }) => void;
+ readOnly?: boolean;
+};
+
+export default function LeafletMap({ defaultCenter, onSelect, readOnly = false }: Props) {
+ const [markerPos, setMarkerPos] = useState(defaultCenter);
+
+ useEffect(() => {
+ // Aman di sini, karena ini hanya jalan di client
+ delete (L.Icon.Default.prototype as any)._getIconUrl;
+ L.Icon.Default.mergeOptions({
+ iconRetinaUrl:
+ 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
+ iconUrl:
+ 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
+ shadowUrl:
+ 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png',
+ });
+ }, []);
+
+ function LocationMarker() {
+ useMapEvents({
+ click(e: LeafletMouseEvent) {
+ if (readOnly) return;
+
+ const { lat, lng } = e.latlng;
+ setMarkerPos({ lat, lng });
+ onSelect?.({ lat, lng });
+ },
+ });
+
+ return ;
+ }
+
+ return (
+
+
+
+
+ );
+}
diff --git a/src/app/admin/(dashboard)/_com/leafletMapEdit.tsx b/src/app/admin/(dashboard)/_com/leafletMapEdit.tsx
new file mode 100644
index 00000000..bc5580bd
--- /dev/null
+++ b/src/app/admin/(dashboard)/_com/leafletMapEdit.tsx
@@ -0,0 +1,57 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+'use client';
+
+import { MapContainer, TileLayer, Marker, useMapEvents } from 'react-leaflet';
+import { useState, useEffect } from 'react';
+import 'leaflet/dist/leaflet.css';
+import L, { LeafletMouseEvent } from 'leaflet';
+
+delete (L.Icon.Default.prototype as any)._getIconUrl;
+L.Icon.Default.mergeOptions({
+ iconRetinaUrl:
+ 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
+ iconUrl:
+ 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
+ shadowUrl:
+ 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png',
+});
+
+type Props = {
+ initialPosition: { lat: number; lng: number };
+ onChange: (pos: { lat: number; lng: number }) => void;
+};
+
+export default function LeafletMapEdit({ initialPosition, onChange }: Props) {
+ const [markerPos, setMarkerPos] = useState(initialPosition);
+
+ useEffect(() => {
+ setMarkerPos(initialPosition);
+ }, [initialPosition]);
+
+ function LocationMarker() {
+ useMapEvents({
+ click(e: LeafletMouseEvent) {
+ const { lat, lng } = e.latlng;
+ setMarkerPos({ lat, lng });
+ onChange({ lat, lng });
+ },
+ });
+
+ return ;
+ }
+
+ return (
+
+
+
+
+ );
+}
diff --git a/src/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah.ts b/src/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah.ts
index 20a6f041..78d9d5c6 100644
--- a/src/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah.ts
+++ b/src/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah.ts
@@ -15,12 +15,12 @@ const defaultForm = {
icon: "",
};
-const pengelolaanSampahState = proxy({
+const pengelolaanSampah = proxy({
create: {
form: { ...defaultForm },
loading: false,
async create() {
- const cek = templateForm.safeParse(pengelolaanSampahState.create.form);
+ const cek = templateForm.safeParse(pengelolaanSampah.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
@@ -29,12 +29,12 @@ const pengelolaanSampahState = proxy({
}
try {
- pengelolaanSampahState.create.loading = true;
- const res = await ApiFetch.api.lingkungan.pengelolaansampah["create"].post(
- pengelolaanSampahState.create.form
- );
+ pengelolaanSampah.create.loading = true;
+ const res = await ApiFetch.api.lingkungan.pengelolaansampah[
+ "create"
+ ].post(pengelolaanSampah.create.form);
if (res.status === 200) {
- pengelolaanSampahState.findMany.load();
+ pengelolaanSampah.findMany.load();
return toast.success("success create");
}
console.log(res);
@@ -42,7 +42,7 @@ const pengelolaanSampahState = proxy({
} catch (error) {
console.log((error as Error).message);
} finally {
- pengelolaanSampahState.create.loading = false;
+ pengelolaanSampah.create.loading = false;
}
},
},
@@ -54,33 +54,35 @@ const pengelolaanSampahState = proxy({
loading: false,
load: async (page = 1, limit = 10) => {
// Change to arrow function
- pengelolaanSampahState.findMany.loading = true; // Use the full path to access the property
- pengelolaanSampahState.findMany.page = page;
+ pengelolaanSampah.findMany.loading = true; // Use the full path to access the property
+ pengelolaanSampah.findMany.page = page;
try {
- const res = await ApiFetch.api.lingkungan.pengelolaansampah["find-many"].get({
+ const res = await ApiFetch.api.lingkungan.pengelolaansampah[
+ "find-many"
+ ].get({
query: { page, limit },
});
if (res.status === 200 && res.data?.success) {
- pengelolaanSampahState.findMany.data = res.data.data || [];
- pengelolaanSampahState.findMany.total = res.data.total || 0;
- pengelolaanSampahState.findMany.totalPages = res.data.totalPages || 1;
+ pengelolaanSampah.findMany.data = res.data.data || [];
+ pengelolaanSampah.findMany.total = res.data.total || 0;
+ pengelolaanSampah.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error(
"Failed to load pengelolaan sampah:",
res.data?.message
);
- pengelolaanSampahState.findMany.data = [];
- pengelolaanSampahState.findMany.total = 0;
- pengelolaanSampahState.findMany.totalPages = 1;
+ pengelolaanSampah.findMany.data = [];
+ pengelolaanSampah.findMany.total = 0;
+ pengelolaanSampah.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading pengelolaan sampah:", error);
- pengelolaanSampahState.findMany.data = [];
- pengelolaanSampahState.findMany.total = 0;
- pengelolaanSampahState.findMany.totalPages = 1;
+ pengelolaanSampah.findMany.data = [];
+ pengelolaanSampah.findMany.total = 0;
+ pengelolaanSampah.findMany.totalPages = 1;
} finally {
- pengelolaanSampahState.findMany.loading = false;
+ pengelolaanSampah.findMany.loading = false;
}
},
},
@@ -95,12 +97,15 @@ const pengelolaanSampahState = proxy({
}
try {
- const response = await fetch(`/api/lingkungan/pengelolaansampah/${id}`, {
- method: "GET",
- headers: {
- "Content-Type": "application/json",
- },
- });
+ const response = await fetch(
+ `/api/lingkungan/pengelolaansampah/${id}`,
+ {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ );
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
@@ -143,19 +148,22 @@ const pengelolaanSampahState = proxy({
}
this.loading = true;
try {
- const response = await fetch(`/api/lingkungan/pengelolaansampah/${id}`, {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(this.form),
- });
+ const response = await fetch(
+ `/api/lingkungan/pengelolaansampah/${id}`,
+ {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(this.form),
+ }
+ );
const result = await response.json();
if (!response.ok || !result?.success) {
throw new Error(result?.message || "Gagal update data");
}
toast.success("Berhasil update data!");
- await pengelolaanSampahState.findMany.load();
+ await pengelolaanSampah.findMany.load();
return result.data;
} catch (error) {
console.error("Error update data:", error);
@@ -174,14 +182,14 @@ const pengelolaanSampahState = proxy({
const res = await fetch(`/api/lingkungan/pengelolaansampah/${id}`);
if (res.ok) {
const data = await res.json();
- pengelolaanSampahState.findUnique.data = data.data ?? null;
+ pengelolaanSampah.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch data", res.status, res.statusText);
- pengelolaanSampahState.findUnique.data = null;
+ pengelolaanSampah.findUnique.data = null;
}
} catch (error) {
console.error("Error loading pengelolaan sampah:", error);
- pengelolaanSampahState.findUnique.data = null;
+ pengelolaanSampah.findUnique.data = null;
}
},
},
@@ -191,20 +199,25 @@ const pengelolaanSampahState = proxy({
if (!id) return toast.warn("ID tidak valid");
try {
- pengelolaanSampahState.delete.loading = true;
+ pengelolaanSampah.delete.loading = true;
- const response = await fetch(`/api/lingkungan/pengelolaansampah/del/${id}`, {
- method: "DELETE",
- headers: {
- "Content-Type": "application/json",
- },
- });
+ const response = await fetch(
+ `/api/lingkungan/pengelolaansampah/del/${id}`,
+ {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ );
const result = await response.json();
if (response.ok && result?.success) {
- toast.success(result.message || "pengelolaan sampah berhasil dihapus");
- await pengelolaanSampahState.findMany.load(); // refresh list
+ toast.success(
+ result.message || "pengelolaan sampah berhasil dihapus"
+ );
+ await pengelolaanSampah.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus pengelolaan sampah");
}
@@ -212,10 +225,236 @@ const pengelolaanSampahState = proxy({
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus pengelolaan sampah");
} finally {
- pengelolaanSampahState.delete.loading = false;
+ pengelolaanSampah.delete.loading = false;
}
},
},
});
+const templateKeteranganSampahForm = z.object({
+ name: z.string().min(1, "Nama minimal 1 karakter"),
+ alamat: z.string().min(1, "Alamat minimal 1 karakter"),
+ namaTempatMaps: z.string().min(1, "Nama Tempat Maps minimal 1 karakter"),
+ lat: z.number(),
+ lng: z.number(),
+});
+
+const defaultKeteranganSampahForm = {
+ name: "",
+ alamat: "",
+ namaTempatMaps: "",
+ lat: 0,
+ lng: 0,
+};
+
+
+const keteranganSampah = proxy({
+ create: {
+ form: { ...defaultKeteranganSampahForm },
+ loading: false,
+ async create() {
+ const cek = templateKeteranganSampahForm.safeParse(
+ keteranganSampah.create.form
+ );
+ if (!cek.success) {
+ const err = `[${cek.error.issues
+ .map((v) => `${v.path.join(".")}`)
+ .join("\n")}] required`;
+ return toast.error(err);
+ }
+ try {
+ keteranganSampah.create.loading = true;
+ const res =
+ await ApiFetch.api.lingkungan.pengelolaansampah.keteranganbankterdekat[
+ "create"
+ ].post(keteranganSampah.create.form);
+ if (res.status === 200) {
+ keteranganSampah.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 {
+ keteranganSampah.create.loading = false;
+ }
+ },
+ },
+ findMany: {
+ data: null as
+ | Prisma.KeteranganBankSampahTerdekatGetPayload<{
+ omit: { isActive: true };
+ }>[]
+ | null,
+ async load() {
+ const res = await ApiFetch.api.lingkungan.pengelolaansampah.keteranganbankterdekat[
+ "find-many"
+ ].get();
+ if (res.status === 200) {
+ keteranganSampah.findMany.data = res.data?.data ?? [];
+ }
+ },
+ },
+ findUnique: {
+ data: null as Prisma.KeteranganBankSampahTerdekatGetPayload<{
+ omit: { isActive: true };
+ }> | null,
+ async load(id: string) {
+ try {
+ const res = await fetch(`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${id}`);
+ if (res.ok) {
+ const data = await res.json();
+ keteranganSampah.findUnique.data = data.data ?? null;
+ } else {
+ console.error("Failed to fetch data", res.status, res.statusText);
+ keteranganSampah.findUnique.data = null;
+ }
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ keteranganSampah.findUnique.data = null;
+ }
+ },
+ },
+ delete: {
+ loading: false,
+ async byId(id: string) {
+ if (!id) return toast.warn("ID tidak valid");
+
+ try {
+ keteranganSampah.delete.loading = true;
+
+ const response = await fetch(`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/del/${id}`, {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+
+ const result = await response.json();
+
+ if (response.ok && result?.success) {
+ toast.success(result.message || "Keterangan sampah berhasil dihapus");
+ await keteranganSampah.findMany.load(); // refresh list
+ } else {
+ toast.error(result?.message || "Gagal menghapus keterangan sampah");
+ }
+ } catch (error) {
+ console.error("Gagal delete:", error);
+ toast.error("Terjadi kesalahan saat menghapus keterangan sampah");
+ } finally {
+ keteranganSampah.delete.loading = false;
+ }
+ },
+ },
+ edit: {
+ id: "",
+ form: { ...defaultKeteranganSampahForm },
+ loading: false,
+
+ async load(id: string) {
+ if (!id) {
+ toast.warn("ID tidak valid");
+ return null;
+ }
+
+ try {
+ const response = await fetch(`/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${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,
+ alamat: data.alamat,
+ namaTempatMaps: data.namaTempatMaps,
+ lat: data.lat,
+ lng: data.lng,
+ };
+ return data;
+ } else {
+ throw new Error(result?.message || "Gagal memuat data");
+ }
+ } catch (error) {
+ console.error("Error loading keterangan sampah:", error);
+ toast.error(
+ error instanceof Error ? error.message : "Gagal memuat data"
+ );
+ return null;
+ }
+ },
+
+ async update() {
+ const cek = templateKeteranganSampahForm.safeParse(keteranganSampah.edit.form);
+ if (!cek.success) {
+ const err = `[${cek.error.issues
+ .map((v) => `${v.path.join(".")}`)
+ .join("\n")}] required`;
+ return toast.error(err);
+ }
+
+ try {
+ keteranganSampah.edit.loading = true;
+ const response = await fetch(
+ `/api/lingkungan/pengelolaansampah/keteranganbankterdekat/${this.id}`,
+ {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ name: this.form.name,
+ alamat: this.form.alamat,
+ namaTempatMaps: this.form.namaTempatMaps,
+ lat: this.form.lat,
+ lng: this.form.lng,
+ }),
+ }
+ );
+ 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 keterangan sampah");
+ await keteranganSampah.findMany.load(); // refresh list
+ return true;
+ } else {
+ throw new Error(result.message || "Gagal mengupdate keterangan sampah");
+ }
+ } catch (error) {
+ console.error("Error updating keterangan sampah:", error);
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Gagal mengupdate keterangan sampah"
+ );
+ return false;
+ } finally {
+ keteranganSampah.edit.loading = false;
+ }
+ },
+ reset() {
+ keteranganSampah.edit.id = "";
+ keteranganSampah.edit.form = { ...defaultKeteranganSampahForm };
+ },
+ },
+});
+
+const pengelolaanSampahState = proxy({
+ pengelolaanSampah,
+ keteranganSampah,
+});
+
export default pengelolaanSampahState;
diff --git a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx
index 11ee3621..dfee28d2 100644
--- a/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx
+++ b/src/app/admin/(dashboard)/inovasi/layanan-online-desa/_lib/layoutTabs.tsx
@@ -28,13 +28,7 @@ function LayoutTabsLayananOnlineDesa({ children }: { children: React.ReactNode }
label: "Jenis Pengaduan",
value: "jenispengaduan",
href: "/admin/inovasi/layanan-online-desa/jenis-pengaduan"
- },
- {
- label: "Informasi Desa",
- value: "informasidesa",
- href: "/admin/inovasi/layanan-online-desa/informasi-desa"
}
-
];
const curentTab = tabs.find(tab => tab.href === pathname)
const [activeTab, setActiveTab] = useState(curentTab?.value || tabs[0].value);
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/edit/page.tsx
new file mode 100644
index 00000000..004fe1ea
--- /dev/null
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/edit/page.tsx
@@ -0,0 +1,141 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
+import pengelolaanSampahState from '@/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah';
+import colors from '@/con/colors';
+import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
+import { IconArrowBack } from '@tabler/icons-react';
+import dynamic from 'next/dynamic';
+import { useParams, useRouter } from 'next/navigation';
+import { useEffect, useState } from 'react';
+import { toast } from 'react-toastify';
+import { useProxy } from 'valtio/utils';
+
+const LeafletMapEdit = dynamic(() => import('@/app/admin/(dashboard)/_com/leafletMapEdit'), { ssr: false });
+
+function EditKeteranganBankSampahTerdekat() {
+ const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah)
+ const router = useRouter();
+ const params = useParams()
+ const [markerPosition, setMarkerPosition] = useState<{ lat: number; lng: number } | null>(null);
+
+ const [formData, setFormData] = useState({
+ name: '',
+ alamat: '',
+ namaTempatMaps: '',
+ lat: 0,
+ lng: 0,
+ })
+
+ useEffect(() => {
+ const loadKeteranganBankSampahTerdekat = async () => {
+ const id = params?.id as string;
+ if (!id) return;
+
+ try {
+ const data = await keteranganState.edit.load(id);
+ if (data) {
+ keteranganState.edit.id = id;
+ keteranganState.edit.form = {
+ name: data.name,
+ alamat: data.alamat,
+ namaTempatMaps: data.namaTempatMaps,
+ lat: data.lat,
+ lng: data.lng,
+ };
+
+ setFormData({
+ name: data.name,
+ alamat: data.alamat,
+ namaTempatMaps: data.namaTempatMaps,
+ lat: data.lat,
+ lng: data.lng,
+ });
+
+ setMarkerPosition({ lat: data.lat, lng: data.lng });
+ }
+ } catch (error) {
+ console.error("Error loading pengelolaan sampah:", error);
+ toast.error("Gagal memuat data pengelolaan sampah");
+ }
+ }
+
+ loadKeteranganBankSampahTerdekat();
+ }, [params?.id]);
+
+ const handleSubmit = async () => {
+ try {
+ keteranganState.edit.form = {
+ ...keteranganState.edit.form,
+ name: formData.name.trim(),
+ alamat: formData.alamat.trim(),
+ namaTempatMaps: formData.namaTempatMaps.trim(),
+ lat: formData.lat,
+ lng: formData.lng,
+ }
+ await keteranganState.edit.update();
+ keteranganState.findUnique.data = null;
+ router.push("/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat");
+ } catch (error) {
+ console.error("Error updating pengelolaan sampah:", error);
+ toast.error("Gagal memuat data pengelolaan sampah");
+ }
+ }
+ return (
+
+
+
+
+
+
+
+ Edit Keterangan Bank Sampah Terdekat
+ setFormData({ ...formData, name: val.target.value })}
+ label={Nama Bank Sampah Terdekat}
+ placeholder='Masukkan nama Bank Sampah Terdekat'
+ />
+
+ setFormData({ ...formData, alamat: val.target.value })}
+ label={Alamat}
+ placeholder='Masukkan alamat Bank Sampah'
+ />
+
+ setFormData({ ...formData, namaTempatMaps: val.target.value })}
+ label={Nama Tempat Maps}
+ placeholder='Masukkan nama tempat maps Bank Sampah'
+ />
+
+
+ Pilih Lokasi di Peta
+
+ {
+ setMarkerPosition(pos);
+ setFormData((prev) => ({
+ ...prev,
+ lat: pos.lat,
+ lng: pos.lng,
+ }));
+ }}
+ />
+
+
+
+
+
+
+
+
+ );
+}
+
+export default EditKeteranganBankSampahTerdekat;
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/page.tsx
new file mode 100644
index 00000000..0daa10e0
--- /dev/null
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/[id]/page.tsx
@@ -0,0 +1,148 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+'use client'
+import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
+import pengelolaanSampahState from '@/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah';
+import colors from '@/con/colors';
+import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
+import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
+import { useParams, useRouter } from 'next/navigation';
+import { useEffect, useState } from 'react';
+import { useProxy } from 'valtio/utils';
+
+import dynamic from 'next/dynamic'
+
+const LeafletMap = dynamic(() => import('@/app/admin/(dashboard)/_com/leafletMapCreate'), {
+ ssr: false
+})
+
+
+function DetailKeteranganBankSampahTerdekat() {
+ const router = useRouter();
+ const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah)
+ const [selectedId, setSelectedId] = useState(null)
+ const [modalHapus, setModalHapus] = useState(false)
+ const params = useParams()
+
+ useEffect(() => {
+ keteranganState.findUnique.load(params?.id as string)
+ }, [])
+
+ const handleHapus = () => {
+ if (selectedId) {
+ keteranganState.delete.byId(selectedId)
+ setModalHapus(false)
+ setSelectedId(null)
+ router.push("/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat")
+ }
+ }
+
+ if (!keteranganState.findUnique.data) {
+ return (
+
+
+
+ )
+ }
+ return (
+
+
+
+
+
+
+
+ Detail Keterangan Bank Sampah Terdekat
+
+
+
+
+ Nama Bank Sampah Terdekat
+ {keteranganState.findUnique.data?.name}
+
+
+
+ Alamat
+ {keteranganState.findUnique.data?.alamat}
+
+
+
+ Nama Tempat Maps
+ {keteranganState.findUnique.data?.namaTempatMaps}
+
+
+
+ Peta Lokasi
+ {keteranganState.findUnique.data?.lat && keteranganState.findUnique.data?.lng ? (
+
+
+
+ ) : (
+ Koordinat belum tersedia
+ )}
+
+
+
+
+ Link Petunjuk Arah
+ {keteranganState.findUnique.data?.lat && keteranganState.findUnique.data?.lng ? (
+
+ Buka Petunjuk Arah di Google Maps
+
+ ) : (
+ Koordinat belum tersedia
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setModalHapus(false)}
+ onConfirm={handleHapus}
+ text="Apakah anda yakin ingin menghapus keterangan bank sampah terdekat ini?"
+ />
+
+
+ );
+}
+
+export default DetailKeteranganBankSampahTerdekat;
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create/page.tsx
index 84db20f6..7405dcad 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create/page.tsx
@@ -1,14 +1,42 @@
'use client'
-import { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor';
+
+import pengelolaanSampahState from '@/app/admin/(dashboard)/_state/lingkungan/pengelolaan-sampah';
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 { IconArrowBack } from '@tabler/icons-react';
+import dynamic from 'next/dynamic';
import { useRouter } from 'next/navigation';
+import { useState } from 'react';
+import { useProxy } from 'valtio/utils';
-
+const LeafletMap = dynamic(() => import('@/app/admin/(dashboard)/_com/leafletMapCreate'), { ssr: false });
function CreateKeteranganBankSampahTerdekat() {
+ const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah)
const router = useRouter();
+
+ const [markerPosition, setMarkerPosition] = useState<{ lat: number; lng: number } | null>(null);
+
+ const resetForm = () => {
+ keteranganState.create.form = {
+ name: "",
+ alamat: "",
+ namaTempatMaps: "",
+ lat: 0,
+ lng: 0,
+ }
+ setMarkerPosition(null)
+ }
+ const handleSubmit = async () => {
+ if (markerPosition) {
+ keteranganState.create.form.lat = markerPosition.lat
+ keteranganState.create.form.lng = markerPosition.lng
+ }
+ await keteranganState.create.create()
+ resetForm()
+ router.push("/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat")
+ }
+
return (
@@ -20,22 +48,38 @@ function CreateKeteranganBankSampahTerdekat() {
Create Keterangan Bank Sampah Terdekat
-
- Masukkan Image
-
-
Nama Bank Sampah Terdekat}
- placeholder='Masukkan nama bank sampah terdekat'
+ value={keteranganState.create.form.name}
+ onChange={(val) => keteranganState.create.form.name = val.target.value}
+ label={Nama Bank Sampah Terdekat}
+ placeholder='Masukkan nama Bank Sampah Terdekat'
/>
+
+ keteranganState.create.form.alamat = val.target.value}
+ label={Alamat}
+ placeholder='Masukkan alamat Bank Sampah'
+ />
+
+ keteranganState.create.form.namaTempatMaps = val.target.value}
+ label={Nama Tempat Maps}
+ placeholder='Masukkan nama tempat maps Bank Sampah'
+ />
+
- Deskripsi Bank Sampah Terdekat
-
+ Pilih Lokasi di Peta
+
+ setMarkerPosition(pos)}
+ defaultCenter={{ lat: -8.65, lng: 115.2 }}
+ />
+
-
+
@@ -44,3 +88,4 @@ function CreateKeteranganBankSampahTerdekat() {
}
export default CreateKeteranganBankSampahTerdekat;
+
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/detail/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/detail/page.tsx
deleted file mode 100644
index 9d90797e..00000000
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/detail/page.tsx
+++ /dev/null
@@ -1,62 +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 DetailKeteranganBankSampahTerdekat() {
- const router = useRouter();
- return (
-
-
-
-
-
-
- Detail Keterangan Bank Sampah Terdekat
-
-
-
-
- Nama Bank Sampah Terdekat
- Test Judul
-
-
- Gambar
-
-
-
- Deskripsi
- Test Deskripsi
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Modal Hapus
- setModalHapus(false)}
- onConfirm={handleHapus}
- text="Apakah anda yakin ingin menghapus potensi ini?"
- /> */}
-
- );
-}
-
-export default DetailKeteranganBankSampahTerdekat;
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/edit/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/edit/page.tsx
deleted file mode 100644
index 3ef09d42..00000000
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/edit/page.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-'use client'
-import { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor';
-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';
-
-
-
-function EditKeteranganBankSampahTerdekat() {
- const router = useRouter();
- return (
-
-
-
-
-
-
-
- Edit Keterangan Bank Sampah Terdekat
-
- Masukkan Image
-
-
- Nama Bank Sampah Terdekat}
- placeholder='Masukkan nama bank sampah terdekat'
- />
-
- Deskripsi Bank Sampah Terdekat
-
-
-
-
-
-
-
-
- );
-}
-
-export default EditKeteranganBankSampahTerdekat;
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/page.tsx
index 60f9150e..5928838d 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/page.tsx
@@ -1,55 +1,90 @@
+/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
-import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
-import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
+import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
+import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
-import JudulListTab from '../../../_com/judulListTab';
+import { useProxy } from 'valtio/utils';
+
+import { useEffect, useState } from 'react';
+import HeaderSearch from '../../../_com/header';
+import JudulList from '../../../_com/judulList';
+import pengelolaanSampahState from '../../../_state/lingkungan/pengelolaan-sampah';
function KeteranganBankSampahTerdekat() {
+ const [search, setSearch] = useState("");
+ return (
+
+ }
+ value={search}
+ onChange={(e) => setSearch(e.currentTarget.value)}
+ />
+
+
+ );
+}
+
+function ListKeteranganBankSampahTerdekat({ search }: { search: string }) {
+ const keteranganState = useProxy(pengelolaanSampahState.keteranganSampah)
const router = useRouter();
+
+ useEffect(() => {
+ keteranganState.findMany.load()
+ }, [])
+
+ const filteredData = (keteranganState.findMany.data || []).filter(item => {
+ const keyword = search.toLowerCase();
+ return (
+ item.name.toLowerCase().includes(keyword) ||
+ item.alamat.toLowerCase().includes(keyword) ||
+ item.namaTempatMaps.toLowerCase().includes(keyword)
+ );
+ });
+
+ if (!keteranganState.findMany.data) {
+ return (
+
+
+
+ )
+ }
return (
-
- }
- />
- List Keterangan Bank Sampah Terdekat
-
-
-
-
- Nama Bank Sampah Terdekat
- Gambar
- Detail
-
-
-
-
-
-
- Bank Sampah Sarana Gathi
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Nama Bank Sampah Terdekat
+ Alamat
+ Nama Tempat Maps
+ Detail
+
+
+
+ {filteredData.map((item) => (
+
+ {item.name}
+ {item.alamat}
+ {item.namaTempatMaps}
+
+
+
+
+ ))}
+
+
- )
+ );
}
export default KeteranganBankSampahTerdekat;
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/[id]/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/[id]/page.tsx
index 816fdc03..a39f4a62 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/[id]/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/[id]/page.tsx
@@ -19,7 +19,7 @@ type IconKey = 'ekowisata' | 'kompetisi' | 'wisata' | 'ekonomi' | 'sampah' | 'tr
function EditProgramKreatifDesa() {
- const stateSampah = useProxy(pengelolaanSampahState)
+ const stateSampah = useProxy(pengelolaanSampahState.pengelolaanSampah)
const params = useParams()
const router = useRouter();
const [formData, setFormData] = useState({
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create/page.tsx
index 95acc819..99058152 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/create/page.tsx
@@ -11,7 +11,7 @@ import { useProxy } from 'valtio/utils';
function CreatePengelolaanSampahBankSampah() {
- const stateCreate = useProxy(pengelolaanSampahState)
+ const stateCreate = useProxy(pengelolaanSampahState.pengelolaanSampah)
const router = useRouter();
const resetForm = () => {
diff --git a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/page.tsx b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/page.tsx
index 2792b45c..e9f107eb 100644
--- a/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/page.tsx
+++ b/src/app/admin/(dashboard)/lingkungan/pengelolaan-sampah-bank-sampah/list-pengelolaan-sampah-bank-sampah/page.tsx
@@ -31,7 +31,7 @@ function PengelolaanSampahBankSampah() {
}
function ListPengelolaanSampahBankSampah({ search }: { search: string }) {
- const stateList = useProxy(pengelolaanSampahState)
+ const stateList = useProxy(pengelolaanSampahState.pengelolaanSampah)
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState(null)
const router = useRouter()
diff --git a/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/index.ts b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/index.ts
index ba1bd47e..568e0e6c 100644
--- a/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/index.ts
@@ -4,6 +4,7 @@ import pengelolaanSampahDelete from "./del";
import pengelolaanSampahFindMany from "./findMany";
import pengelolaanSampahFindUnique from "./findUnique";
import pengelolaanSampahUpdate from "./updt";
+import KeteranganBankSampahTerdekat from "./keterangan-bank-sampah";
const PengelolaanSampah = new Elysia({
prefix: "/pengelolaansampah",
@@ -33,5 +34,6 @@ const PengelolaanSampah = new Elysia({
}),
}
)
- .delete("/del/:id", pengelolaanSampahDelete);
+ .delete("/del/:id", pengelolaanSampahDelete)
+ .use(KeteranganBankSampahTerdekat);
export default PengelolaanSampah;
diff --git a/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/create.ts b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/create.ts
new file mode 100644
index 00000000..51950fdb
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/create.ts
@@ -0,0 +1,33 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+type FormCreateKeteranganBankSampahTerdekat = {
+ name: string;
+ alamat: string;
+ namaTempatMaps: string;
+ lat: number;
+ lng: number;
+}
+
+export default async function keteranganBankSampahTerdekatCreate(context: Context) {
+ const body = context.body as FormCreateKeteranganBankSampahTerdekat;
+
+ const linkPetunjukArah = `https://www.google.com/maps/dir/?api=1&destination=${body.lat},${body.lng}`;
+
+ const created = await prisma.keteranganBankSampahTerdekat.create({
+ data: {
+ name: body.name,
+ alamat: body.alamat,
+ namaTempatMaps: body.namaTempatMaps,
+ lat: body.lat,
+ lng: body.lng,
+ linkPetunjukArah,
+ },
+ });
+
+ return {
+ success: true,
+ message: "Success create keterangan bank sampah terdekat",
+ data: created,
+ };
+}
diff --git a/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/del.ts b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/del.ts
new file mode 100644
index 00000000..c0f7da2d
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/del.ts
@@ -0,0 +1,36 @@
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+const keteranganBankSampahTerdekatDelete = async (context: Context) => {
+ const id = context.params?.id as string;
+
+ if (!id) {
+ return {
+ status: 400,
+ body: "ID tidak diberikan",
+ };
+ }
+
+ const keteranganBankSampahTerdekat = await prisma.keteranganBankSampahTerdekat.findUnique({
+ where: { id },
+ });
+
+ if (!keteranganBankSampahTerdekat) {
+ return {
+ status: 404,
+ body: "Keterangan bank sampah terdekat tidak ditemukan",
+ };
+ }
+
+ await prisma.keteranganBankSampahTerdekat.delete({
+ where: { id },
+ });
+
+ return {
+ success: true,
+ status: 200,
+ message: "Keterangan bank sampah terdekat berhasil dihapus",
+ };
+};
+
+export default keteranganBankSampahTerdekatDelete;
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/findMany.ts b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/findMany.ts
new file mode 100644
index 00000000..5928f238
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/findMany.ts
@@ -0,0 +1,21 @@
+import prisma from "@/lib/prisma";
+
+export default async function keteranganBankSampahTerdekatFindMany() {
+ try {
+ const data = await prisma.keteranganBankSampahTerdekat.findMany({
+ where: { isActive: true },
+ });
+
+ return {
+ success: true,
+ message: "Success fetch keterangan bank sampah terdekat",
+ data,
+ };
+ } catch (e) {
+ console.error("Find many error:", e);
+ return {
+ success: false,
+ message: "Failed fetch keterangan bank sampah terdekat",
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/findUnique.ts b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/findUnique.ts
new file mode 100644
index 00000000..2b24cef7
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/findUnique.ts
@@ -0,0 +1,39 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+export default async function keteranganBankSampahTerdekatFindUnique(context: Context) {
+ const { id } = context.params as { id: string };
+
+ if (!id) {
+ return {
+ success: false,
+ message: "ID keterangan bank sampah terdekat diperlukan",
+ };
+ }
+
+ try {
+ const keteranganBankSampahTerdekat = await prisma.keteranganBankSampahTerdekat.findUnique({
+ where: { id },
+ });
+
+ if (!keteranganBankSampahTerdekat) {
+ return {
+ success: false,
+ message: "Keterangan bank sampah terdekat tidak ditemukan",
+ };
+ }
+
+ return {
+ success: true,
+ data: keteranganBankSampahTerdekat,
+ };
+ } catch (error: any) {
+ console.error("Error findUnique keterangan bank sampah terdekat:", error);
+ return {
+ success: false,
+ message: "Gagal mengambil data keterangan bank sampah terdekat",
+ error: error.message,
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/index.ts b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/index.ts
new file mode 100644
index 00000000..42cb1f82
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/index.ts
@@ -0,0 +1,44 @@
+import Elysia, { t } from "elysia";
+import keteranganBankSampahTerdekatCreate from "./create";
+import keteranganBankSampahTerdekatDelete from "./del";
+import keteranganBankSampahTerdekatFindMany from "./findMany";
+import keteranganBankSampahTerdekatFindUnique from "./findUnique";
+import keteranganBankSampahTerdekatUpdate from "./updt";
+
+const KeteranganBankSampahTerdekat = new Elysia({
+ prefix: "/keteranganbankterdekat",
+ tags: ["Lingkungan/Pengelolaan Sampah/Keterangan Bank Sampah Terdekat"],
+})
+ .get("/find-many", keteranganBankSampahTerdekatFindMany)
+ .get("/:id", async (context) => {
+ const response = await keteranganBankSampahTerdekatFindUnique(context);
+ return response;
+ })
+ .post("/create", keteranganBankSampahTerdekatCreate, {
+ body: t.Object({
+ name: t.String(),
+ alamat: t.String(),
+ namaTempatMaps: t.String(),
+ lat: t.Number(),
+ lng: t.Number(),
+ }),
+ })
+ .put(
+ "/:id",
+ async (context) => {
+ const response = await keteranganBankSampahTerdekatUpdate(context);
+ return response;
+ },
+ {
+ body: t.Object({
+ name: t.String(),
+ alamat: t.String(),
+ namaTempatMaps: t.String(),
+ lat: t.Number(),
+ lng: t.Number(),
+ }),
+ }
+ )
+ .delete("/del/:id", keteranganBankSampahTerdekatDelete);
+
+export default KeteranganBankSampahTerdekat;
diff --git a/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/updt.ts b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/updt.ts
new file mode 100644
index 00000000..ee1bdec0
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/lingkungan/pengelolaan-sampah/keterangan-bank-sampah/updt.ts
@@ -0,0 +1,55 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import prisma from "@/lib/prisma";
+import { Context } from "elysia";
+
+type FormUpdateKeteranganBankSampahTerdekat = {
+ id: string;
+ name?: string;
+ alamat?: string;
+ namaTempatMaps?: string;
+ lat?: number;
+ lng?: number;
+};
+
+export default async function keteranganBankSampahTerdekatUpdate(context: Context) {
+ const body = context.body as FormUpdateKeteranganBankSampahTerdekat;
+ const id = context.params?.id;
+
+ if (!id) {
+ return {
+ success: false,
+ message: "ID keterangan bank sampah terdekat wajib diisi",
+ };
+ }
+
+ try {
+ const updateData: any = {
+ name: body.name,
+ alamat: body.alamat,
+ namaTempatMaps: body.namaTempatMaps,
+ };
+
+ if (body.lat !== undefined && body.lng !== undefined) {
+ updateData.lat = body.lat;
+ updateData.lng = body.lng;
+ updateData.linkPetunjukArah = `https://www.google.com/maps/dir/?api=1&destination=${body.lat},${body.lng}`;
+ }
+
+ const updated = await prisma.keteranganBankSampahTerdekat.update({
+ where: { id },
+ data: updateData,
+ });
+
+ return {
+ success: true,
+ message: "Success update keterangan bank sampah terdekat",
+ data: updated,
+ };
+ } catch (error) {
+ console.error("Update error:", error);
+ return {
+ success: false,
+ message: "Gagal mengupdate keterangan bank sampah terdekat",
+ };
+ }
+}
diff --git a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx
index 4ac2d9c8..6f822525 100644
--- a/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/inovasi/layanan-online-desa/informasi-desa/page.tsx
@@ -39,96 +39,102 @@ function InformasiDesa() {
- {dataBerita && (
-
+ {dataBerita && (
+
+
+
+
+
+
+
+ {dataBerita.kategoriBerita?.name} • {dayjs(dataBerita.createdAt).fromNow()}
+ {dataBerita.judul}
+
+
+
+
+
+ )}
+
+ Berita Terbaru
-
-
-
-
-
- {dataBerita.kategoriBerita?.name} • {dayjs(dataBerita.createdAt).fromNow()}
- {dataBerita.judul}
-
-
-
+ {stateBerita.findRecent.data.map((item) => (
+
+
+
+
+
+
+
+ {item.judul}
+
+
+ {item.deskripsi}
+
+
+ {dayjs(item.createdAt).fromNow()}
+
+
+
+
+ ))}
-
- )}
-
- Berita Terbaru
-
- {stateBerita.findRecent.data.map((item) => (
-
-
-
-
-
-
-
- {item.judul}
-
-
- {item.deskripsi}
-
-
- {dayjs(item.createdAt).fromNow()}
-
-
-
-
- ))}
-
-
-
- {dataPengumuman && (
-
-
- {dataPengumuman.judul}
- {dataPengumuman.CategoryPengumuman?.name} • {dayjs(dataPengumuman.createdAt).fromNow()}
-
-
-
-
-
- )}
-
- Pengumuman Terbaru
+
+
- {statePengumuman.findRecent.data.map((item) => (
-
-
-
-
- {item.judul}
-
-
- {item.deskripsi}
-
-
- {dayjs(item.createdAt).fromNow()}
-
+
+ {dataPengumuman && (
+
+
+ {dataPengumuman.judul}
+ {dataPengumuman.CategoryPengumuman?.name} • {dayjs(dataPengumuman.createdAt).fromNow()}
+
+
+
-
-
- ))}
-
+
+ )}
+
+
+
+ Pengumuman Terbaru
+
+ {statePengumuman.findRecent.data.map((item) => (
+
+
+
+
+ {item.judul}
+
+
+ {item.deskripsi}
+
+
+ {dayjs(item.createdAt).fromNow()}
+
+
+
+
+ ))}
+
+
+
+
-
);