upd: dashboard admin

Deskripsi:
- tampilan list kategori pengaduan
- tambah kategori pengaduan
- edit kategori pengaduan

No Issues
This commit is contained in:
2025-11-12 17:20:14 +08:00
parent a4167cfc8b
commit 503c3e330d
2 changed files with 236 additions and 1 deletions

View File

@@ -0,0 +1,234 @@
import apiFetch from "@/lib/apiFetch";
import {
ActionIcon,
Button,
Divider,
Flex,
Group,
Input,
Modal,
Stack,
Table,
Title,
Tooltip
} from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit, IconPlus } from "@tabler/icons-react";
import { useState } from "react";
import useSWR from "swr";
import notification from "./notificationGlobal";
export default function KategoriPengaduan() {
const [btnDisable, setBtnDisable] = useState(true);
const [btnLoading, setBtnLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(false);
const [openedTambah, { open: openTambah, close: closeTambah }] = useDisclosure(false);
const { data, mutate, isLoading } = useSWR("/", () =>
apiFetch.api.pengaduan.category.get(),
);
const list = data?.data || [];
const [dataEdit, setDataEdit] = useState({
id: "",
name: "",
});
const [dataTambah, setDataTambah] = useState("")
useShallowEffect(() => {
mutate();
}, []);
async function handleCreate() {
try {
setBtnLoading(true);
const res = await apiFetch.api.pengaduan.category.create.post({ name: dataTambah });
if (res.status === 200) {
mutate();
closeTambah();
setDataTambah("");
notification({
title: "Success",
message: "Your category have been saved",
type: "success",
})
} else {
notification({
title: "Error",
message: "Failed to create category",
type: "error",
})
}
} catch (error) {
console.log(error);
notification({
title: "Error",
message: "Failed to create category",
type: "error",
})
} finally {
setBtnLoading(false);
}
}
async function handleEdit() {
try {
setBtnLoading(true);
const res = await apiFetch.api.pengaduan.category.update.post(dataEdit);
if (res.status === 200) {
mutate();
close();
notification({
title: "Success",
message: "Your category have been saved",
type: "success",
})
} else {
notification({
title: "Error",
message: "Failed to edit category",
type: "error",
})
}
} catch (error) {
console.log(error);
notification({
title: "Error",
message: "Failed to edit category",
type: "error",
})
} finally {
setBtnLoading(false);
}
}
function chooseEdit({ data }: { data: { id: string, value: string, name: string } }) {
setDataEdit(data);
open();
}
function onValidation({ kat, value, aksi }: { kat: 'name', value: string, aksi: 'edit' | 'tambah' }) {
if (value.length < 1) {
setBtnDisable(true);
} else {
setBtnDisable(false);
}
if (kat === 'name') {
if (aksi === 'edit') {
setDataEdit({ ...dataEdit, name: value });
} else {
setDataTambah(value);
}
}
}
useShallowEffect(() => {
if (dataEdit.name.length > 0) {
setBtnDisable(false);
}
}, [dataEdit.id]);
useShallowEffect(() => {
if (dataTambah.length > 0) {
setBtnDisable(false);
}
}, [dataTambah]);
return (
<>
{/* Modal Edit */}
<Modal
opened={opened}
onClose={close}
title={"Edit"}
centered
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
>
<Stack gap="ld">
<Input.Wrapper label="Edit Kategori">
<Input value={dataEdit.name} onChange={(e) => onValidation({ kat: 'name', value: e.target.value, aksi: 'edit' })} />
</Input.Wrapper>
<Group justify="center" grow>
<Button variant="light" onClick={close}>
Batal
</Button>
<Button variant="filled" onClick={handleEdit} disabled={btnDisable} loading={btnLoading}>
Simpan
</Button>
</Group>
</Stack>
</Modal>
{/* Modal Tambah */}
<Modal
opened={openedTambah}
onClose={closeTambah}
title={"Tambah"}
centered
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
>
<Stack gap="ld">
<Input.Wrapper label="Tambah Kategori">
<Input value={dataTambah} onChange={(e) => onValidation({ kat: 'name', value: e.target.value, aksi: 'tambah' })} />
</Input.Wrapper>
<Group justify="center" grow>
<Button variant="light" onClick={closeTambah}>
Batal
</Button>
<Button variant="filled" onClick={handleCreate} disabled={btnDisable} loading={btnLoading}>
Simpan
</Button>
</Group>
</Stack>
</Modal>
<Stack gap={"md"}>
<Flex align="center" justify="space-between">
<Title order={4} c="gray.2">
Kategori Pengaduan
</Title>
<Tooltip label="Tambah Kategori Pengaduan">
<Button
variant="light"
leftSection={<IconPlus size={20} />}
onClick={openTambah}
>
Tambah
</Button>
</Tooltip>
</Flex>
<Divider my={0} />
<Stack gap={"md"}>
<Table highlightOnHover>
<Table.Thead>
<Table.Tr>
<Table.Th>Kategori</Table.Th>
<Table.Th>Aksi</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{list?.map((v: any) => (
<Table.Tr key={v.id}>
<Table.Td>{v.name}</Table.Td>
<Table.Td>
<Tooltip label="Edit Setting">
<ActionIcon
variant="light"
size="sm"
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
onClick={() => chooseEdit({ data: v })}
>
<IconEdit size={20} />
</ActionIcon>
</Tooltip>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Stack>
</Stack>
</>
);
}

View File

@@ -1,4 +1,5 @@
import DesaSetting from "@/components/DesaSetting";
import KategoriPengaduan from "@/components/KategoriPengaduan";
import {
Button,
Card,
@@ -80,7 +81,7 @@ export default function DetailSettingPage() {
}}
>
{type === "cat-pengaduan" ? (
<KategoriPengaduanPage />
<KategoriPengaduan />
) : type === "cat-pelayanan" ? (
<KategoriPengaduanPage />
) : type === "desa" ? (