API & UI Menu Lingkungan Submenu Pengelolaan Sampah

This commit is contained in:
2025-07-18 15:01:43 +08:00
parent 7439eb7687
commit 4025771a4d
25 changed files with 1235 additions and 304 deletions

View File

@@ -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 (
<Box>
<Box mb={10}>
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Edit Keterangan Bank Sampah Terdekat</Title>
<TextInput
value={formData.name}
onChange={(val) => setFormData({ ...formData, name: val.target.value })}
label={<Text fw="bold" fz="sm">Nama Bank Sampah Terdekat</Text>}
placeholder='Masukkan nama Bank Sampah Terdekat'
/>
<TextInput
value={formData.alamat}
onChange={(val) => setFormData({ ...formData, alamat: val.target.value })}
label={<Text fw="bold" fz="sm">Alamat</Text>}
placeholder='Masukkan alamat Bank Sampah'
/>
<TextInput
value={formData.namaTempatMaps}
onChange={(val) => setFormData({ ...formData, namaTempatMaps: val.target.value })}
label={<Text fw="bold" fz="sm">Nama Tempat Maps</Text>}
placeholder='Masukkan nama tempat maps Bank Sampah'
/>
<Box>
<Text fw="bold" fz="sm">Pilih Lokasi di Peta</Text>
<Box style={{ height: 300, width: '100%' }}>
<LeafletMapEdit
key={markerPosition?.lat ?? 'default'}
initialPosition={markerPosition || { lat: -8.65, lng: 115.2 }}
onChange={(pos) => {
setMarkerPosition(pos);
setFormData((prev) => ({
...prev,
lat: pos.lat,
lng: pos.lng,
}));
}}
/>
</Box>
</Box>
<Group>
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
);
}
export default EditKeteranganBankSampahTerdekat;

View File

@@ -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<string | null>(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 (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>Detail Keterangan Bank Sampah Terdekat</Text>
<Paper bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fz={"lg"} fw={"bold"}>Nama Bank Sampah Terdekat</Text>
<Text fz={"lg"}>{keteranganState.findUnique.data?.name}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Alamat</Text>
<Text fz={"lg"}>{keteranganState.findUnique.data?.alamat}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Nama Tempat Maps</Text>
<Text fz={"lg"}>{keteranganState.findUnique.data?.namaTempatMaps}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Peta Lokasi</Text>
{keteranganState.findUnique.data?.lat && keteranganState.findUnique.data?.lng ? (
<Box
style={{
height: "300px",
}}
>
<LeafletMap
defaultCenter={{ lat: keteranganState.findUnique.data.lat, lng: keteranganState.findUnique.data.lng }}
readOnly
/>
</Box>
) : (
<Text c="dimmed" fz="sm">Koordinat belum tersedia</Text>
)}
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Link Petunjuk Arah</Text>
{keteranganState.findUnique.data?.lat && keteranganState.findUnique.data?.lng ? (
<a
href={`https://www.google.com/maps/dir/?api=1&destination=${keteranganState.findUnique.data.lat},${keteranganState.findUnique.data.lng}`}
target="_blank"
rel="noopener noreferrer"
style={{ color: 'black', textDecoration: 'underline' }}
>
Buka Petunjuk Arah di Google Maps
</a>
) : (
<Text c="dimmed" fz="sm">Koordinat belum tersedia</Text>
)}
</Box>
<Box>
<Flex gap={"xs"}>
<Button
onClick={() => {
if (keteranganState.findUnique.data) {
setSelectedId(keteranganState.findUnique.data.id);
setModalHapus(true);
}
}}
disabled={!keteranganState.findUnique.data}
color="red"
>
<IconX size={20} />
</Button>
<Button
onClick={() => router.push(`/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/${keteranganState.findUnique.data?.id}/edit`)}
color="green"
>
<IconEdit size={20} />
</Button>
</Flex>
</Box>
</Stack>
</Paper>
</Stack>
</Paper>
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text="Apakah anda yakin ingin menghapus keterangan bank sampah terdekat ini?"
/>
</Box>
);
}
export default DetailKeteranganBankSampahTerdekat;

View File

@@ -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 (
<Box>
<Box mb={10}>
@@ -20,22 +48,38 @@ function CreateKeteranganBankSampahTerdekat() {
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Create Keterangan Bank Sampah Terdekat</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={50} />
</Box>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Bank Sampah Terdekat</Text>}
placeholder='Masukkan nama bank sampah terdekat'
value={keteranganState.create.form.name}
onChange={(val) => keteranganState.create.form.name = val.target.value}
label={<Text fw="bold" fz="sm">Nama Bank Sampah Terdekat</Text>}
placeholder='Masukkan nama Bank Sampah Terdekat'
/>
<TextInput
value={keteranganState.create.form.alamat}
onChange={(val) => keteranganState.create.form.alamat = val.target.value}
label={<Text fw="bold" fz="sm">Alamat</Text>}
placeholder='Masukkan alamat Bank Sampah'
/>
<TextInput
value={keteranganState.create.form.namaTempatMaps}
onChange={(val) => keteranganState.create.form.namaTempatMaps = val.target.value}
label={<Text fw="bold" fz="sm">Nama Tempat Maps</Text>}
placeholder='Masukkan nama tempat maps Bank Sampah'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Bank Sampah Terdekat</Text>
<KeamananEditor
showSubmit={false}
/>
<Text fw="bold" fz="sm">Pilih Lokasi di Peta</Text>
<Box style={{ height: 300, width: '100%' }}>
<LeafletMap
onSelect={(pos) => setMarkerPosition(pos)}
defaultCenter={{ lat: -8.65, lng: 115.2 }}
/>
</Box>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
@@ -44,3 +88,4 @@ function CreateKeteranganBankSampahTerdekat() {
}
export default CreateKeteranganBankSampahTerdekat;

View File

@@ -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 (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>Detail Keterangan Bank Sampah Terdekat</Text>
<Paper bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fz={"lg"} fw={"bold"}>Nama Bank Sampah Terdekat</Text>
<Text fz={"lg"}>Test Judul</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
<Image src={"/"} alt="gambar" />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
<Text fz={"lg"} >Test Deskripsi</Text>
</Box>
<Box>
<Flex gap={"xs"}>
<Button color="red">
<IconX size={20} />
</Button>
<Button onClick={() => router.push('/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan_bank_sampah_terdekat/edit')} color="green">
<IconEdit size={20} />
</Button>
</Flex>
</Box>
</Stack>
</Paper>
</Stack>
</Paper>
{/* Modal Hapus
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text="Apakah anda yakin ingin menghapus potensi ini?"
/> */}
</Box>
);
}
export default DetailKeteranganBankSampahTerdekat;

View File

@@ -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 (
<Box>
<Box mb={10}>
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Edit Keterangan Bank Sampah Terdekat</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={50} />
</Box>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Bank Sampah Terdekat</Text>}
placeholder='Masukkan nama bank sampah terdekat'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Bank Sampah Terdekat</Text>
<KeamananEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
);
}
export default EditKeteranganBankSampahTerdekat;

View File

@@ -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 (
<Box>
<HeaderSearch
title='Keterangan Bank Sampah Terdekat'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListKeteranganBankSampahTerdekat search={search}/>
</Box>
);
}
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 (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<JudulListTab
title='Keterangan Bank Sampah Terdekat'
href='/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan_bank_sampah_terdekat/create'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<Title order={4}>List Keterangan Bank Sampah Terdekat</Title>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Nama Bank Sampah Terdekat</TableTh>
<TableTh>Gambar</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Bank Sampah Sarana Gathi</Text>
</Box>
</TableTd>
<TableTd>
<Image w={100} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push('/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan_bank_sampah_terdekat/detail')}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
<JudulList
title='List Keterangan Bank Sampah Terdekat'
href='/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama Bank Sampah Terdekat</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Nama Tempat Maps</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.alamat}</TableTd>
<TableTd>{item.namaTempatMaps}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/lingkungan/pengelolaan-sampah-bank-sampah/keterangan-bank-sampah-terdekat/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
</Box>
)
);
}
export default KeteranganBankSampahTerdekat;

View File

@@ -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<FormProgramKreatif>({

View File

@@ -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 = () => {

View File

@@ -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<string | null>(null)
const router = useRouter()