From b39800a475cdee0674140b586406d8d84d300867 Mon Sep 17 00:00:00 2001 From: nico Date: Mon, 8 Sep 2025 15:45:56 +0800 Subject: [PATCH] Fix UI Admin Keamanan Lingkungan --- .../[id]/edit/page.tsx | 146 +++++++---- .../[id]/page.tsx | 127 ++++++---- .../create/page.tsx | 227 ++++++++++++------ .../page.tsx | 150 ++++++++---- .../keamanan/polsek-terdekat/page.tsx | 145 +++++++---- 5 files changed, 532 insertions(+), 263 deletions(-) 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 index df2c9245..2a8a3684 100644 --- 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 @@ -11,15 +11,21 @@ import { Stack, Text, TextInput, - Title + Title, + Tooltip, } from "@mantine/core"; -import { IconArrowBack, IconImageInPicture, IconPhoto, IconUpload, IconX } from "@tabler/icons-react"; +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"; @@ -34,24 +40,24 @@ function EditKeamananLingkungan() { 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 || '' + name: keamananState.edit.form.name || "", + deskripsi: keamananState.edit.form.deskripsi || "", + imageId: keamananState.edit.form.imageId || "", }); - // Load berita by id saat pertama kali + // Load data by id useEffect(() => { - const loadBerita = async () => { + const loadData = async () => { const id = params?.id as string; if (!id) return; try { - const data = await keamananState.edit.load(id); // akses langsung, bukan dari proxy + const data = await keamananState.edit.load(id); if (data) { setFormData({ - name: data.name || '', - deskripsi: data.deskripsi || '', - imageId: data.imageId || '', + name: data.name || "", + deskripsi: data.deskripsi || "", + imageId: data.imageId || "", }); if (data?.image?.link) { @@ -64,30 +70,29 @@ function EditKeamananLingkungan() { } }; - loadBerita(); - }, [params?.id]); // ✅ hapus beritaState dari dependency + loadData(); + }, [params?.id]); 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 + imageId: formData.imageId, }; - // Jika ada file baru, upload if (file) { - const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name }); + 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; } @@ -101,36 +106,72 @@ function EditKeamananLingkungan() { }; return ( - - - - - - - Edit Keamanan Lingkungan + + {/* Header */} + + + + + + Edit Keamanan Lingkungan + + + + {/* Form */} + + { - const selectedFile = files[0]; // Ambil file pertama + const selectedFile = files[0]; if (selectedFile) { setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview + setPreviewImage(URL.createObjectURL(selectedFile)); } }} - onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} + onReject={() => toast.error("File tidak valid.")} + maxSize={5 * 1024 ** 2} + accept={{ "image/*": [] }} > - + - + - + - +
@@ -151,15 +192,21 @@ function EditKeamananLingkungan() { )} + setFormData({ ...formData, name: e.target.value })} - label={Judul Keamanan Lingkungan} - placeholder="masukkan judul" + onChange={(e) => + setFormData({ ...formData, name: e.target.value }) + } + label="Judul Keamanan Lingkungan" + placeholder="Masukkan judul" + required /> - Deskripsi + + Deskripsi + { @@ -169,7 +216,20 @@ function 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 index c6683c1e..6477b69f 100644 --- 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 @@ -1,9 +1,9 @@ 'use client' import { useProxy } from 'valtio/utils'; -import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { Box, Button, Group, Image, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; @@ -36,64 +36,95 @@ function DetailKeamananLingkungan() { if (!keamananState.findUnique.data) { return ( - + ) } + const data = keamananState.findUnique.data + return ( - - - - - - - Detail Keamanan Lingkungan - {keamananState.findUnique.data ? ( - - - - Gambar - gambar - - - Judul Keamanan Lingkungan - {keamananState.findUnique.data?.name} - - - Deskripsi - - - + + {/* Tombol Back */} + + + {/* Wrapper Detail */} + + + + Detail Keamanan Lingkungan + + + + + + Gambar + gambar keamanan lingkungan + + + + Judul Keamanan Lingkungan + {data?.name || '-'} + + + + Deskripsi + + + + {/* Aksi */} + + + + + - - - - ) : null} + + + + @@ -102,10 +133,10 @@ function DetailKeamananLingkungan() { opened={modalHapus} onClose={() => setModalHapus(false)} onConfirm={handleHapus} - text='Apakah anda yakin ingin menghapus keamanan lingkungan ini?' + text="Apakah anda yakin ingin menghapus keamanan lingkungan ini?" /> ); } -export default DetailKeamananLingkungan; \ No newline at end of file +export default DetailKeamananLingkungan; 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 d24bc719..31a30c79 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,9 +1,25 @@ 'use client' import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { + Box, + Button, + Group, + Image, + Paper, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; -import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; +import { + IconArrowBack, + IconPhoto, + IconUpload, + IconX, +} from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { toast } from 'react-toastify'; @@ -11,37 +27,36 @@ import { useProxy } from 'valtio/utils'; import CreateEditor from '../../../_com/createEditor'; import keamananLingkunganState from '../../../_state/keamanan/keamanan-lingkungan'; - function CreateKeamananLingkungan() { - const keamananState = useProxy(keamananLingkunganState) + 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: "", - } + name: '', + deskripsi: '', + imageId: '', + }; setPreviewImage(null); setFile(null); - } + }; const handleSubmit = async () => { if (!file) { - return toast.warn("Pilih file gambar terlebih dahulu"); + return toast.warn('Pilih file gambar terlebih dahulu'); } const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name, - }) + }); const uploaded = res.data?.data; if (!uploaded?.id) { - return toast.error("Gagal mengupload file"); + return toast.error('Gagal mengupload file'); } keamananState.create.form.imageId = uploaded.id; @@ -49,86 +64,127 @@ function CreateKeamananLingkungan() { await keamananState.create.create(); resetForm(); - router.push("/admin/keamanan/keamanan-lingkungan-pecalang-patwal") - } + router.push('/admin/keamanan/keamanan-lingkungan-pecalang-patwal'); + }; return ( - - - - + + {/* Header */} + + + + + + Tambah Data Keamanan Lingkungan + + - - - Create Keamanan Lingkungan + {/* Form */} + + + {/* Upload Gambar */} - Gambar - - { - const selectedFile = files[0]; // Ambil file pertama - if (selectedFile) { - setFile(selectedFile); - setPreviewImage(URL.createObjectURL(selectedFile)); // Buat preview - } - }} - onReject={() => toast.error('File tidak valid.')} - maxSize={5 * 1024 ** 2} // Maks 5MB - accept={{ 'image/*': [] }} + + Gambar + + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid.')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': [] }} + > + - - - - - - - - - - - -
- - Drag gambar ke sini atau klik untuk pilih file - - - Maksimal 5MB dan harus format gambar - -
-
-
- - {/* Tampilkan preview kalau ada */} - {previewImage && ( - - Preview + - - )} + + + + + + + -
+
+ + Drag gambar ke sini atau klik untuk pilih file + + + Maksimal 5MB dan harus format gambar + +
+ + + + {previewImage && ( + + Preview + + )}
+ + {/* Input Nama */} { keamananState.create.form.name = val.target.value; }} - label={Nama Keamanan Lingkungan} - placeholder='Masukkan nama Keamanan Lingkungan' + label={Nama Keamanan Lingkungan} + placeholder="Masukkan nama Keamanan Lingkungan" + required /> + + {/* Input Deskripsi */} - Deskripsi Keamanan Lingkungan + + Deskripsi Keamanan Lingkungan + { @@ -136,8 +192,21 @@ function CreateKeamananLingkungan() { }} /> - - + + {/* Tombol Submit */} + +
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 6cfde30e..f4bef5c6 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,26 +1,46 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Pagination, 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 { + Box, + Button, + Center, + Group, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; +import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../_com/header'; +import keamananLingkunganState from '../../_state/keamanan/keamanan-lingkungan'; function KeamananLingkungan() { const [search, setSearch] = useState(""); + return ( + {/* Header Search */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> + ); @@ -47,54 +67,94 @@ function ListKeamananLingkungan({ search }: { search: string }) { if (loading || !data) { return ( - + ) } + return ( - - - - - - Nama Keamanan Lingkungan - Deskripsi - Detail - - - - {filteredData.map((item) => ( - - - - {item.name} - - - - - - - - - - + + {/* Judul + Tombol Tambah */} + + Daftar Keamanan Lingkungan + + + + + + {/* Tabel */} + +
+ + + Nama + Deskripsi + Aksi - ))} - -
+ + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( + + + + {item.name} + + + + + + + + + + )) + ) : ( + + +
+ + Tidak ada data keamanan lingkungan yang cocok + +
+
+
+ )} +
+ +
+ + {/* Pagination */}
load(newPage)} + onChange={(newPage) => { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} total={totalPages} - my={"md"} + mt="md" + mb="md" + color="blue" + radius="md" />
diff --git a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/page.tsx b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/page.tsx index 2bcc88f3..8ea7b60d 100644 --- a/src/app/admin/(dashboard)/keamanan/polsek-terdekat/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/polsek-terdekat/page.tsx @@ -1,26 +1,45 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; +import { + Box, + Button, + Center, + Group, + Pagination, + Paper, + Skeleton, + Stack, + Table, + TableTbody, + TableTd, + TableTh, + TableThead, + TableTr, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; +import { IconDeviceImac, IconPlus, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; +import { useState } from 'react'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../_com/header'; -import JudulList from '../../_com/judulList'; import polsekTerdekat from '../../_state/keamanan/polsek-terdekat'; -import { useState } from 'react'; function PolsekTerdekat() { const [search, setSearch] = useState(""); + return ( } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> + ); @@ -47,60 +66,90 @@ function ListPolsekTerdekat({ search }: { search: string }) { if (loading || !data) { return ( - + ) } + return ( - - - - - - Nama Polsek Terdekat - Jarak Polsek - Alamat - Detail - - - - {filteredData.map((item) => ( - - - - {item.nama} - - - - - {item.jarakKeDesa} - - - - - {item.alamat} - - - - - + + + Daftar Polsek Terdekat + + + + + + +
+ + + Nama Polsek + Jarak + Alamat + Aksi - ))} - -
+ + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( + + + + {item.nama} + + + {item.jarakKeDesa} + {item.alamat} + + + + + )) + ) : ( + + +
+ + Tidak ada data Polsek yang cocok + +
+
+
+ )} +
+ +
+ + {/* Pagination */}
load(newPage)} + onChange={(newPage) => { + load(newPage, 10, search); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} total={totalPages} - my="md" + mt="md" + mb="md" + color="blue" + radius="md" />