upd: list pengaduan

Deskripsi:
- pencarian data list pengaduan

No Issues
This commit is contained in:
2025-11-07 15:19:23 +08:00
parent e0456b2dba
commit 928ecb4c76
4 changed files with 206 additions and 131 deletions

View File

@@ -1,28 +1,28 @@
// ⚡ Auto-generated by generateRoutes.ts — DO NOT EDIT MANUALLY
import { BrowserRouter, Route, Routes } from "react-router-dom";
import DarmasabaHome from "./pages/darmasaba/darmasaba_home";
import DarmasabaLayout from "./pages/darmasaba/darmasaba_layout";
import FormKartuKeluarga from "./pages/darmasaba/form_kartu_keluarga";
import FormKartuTandaPenduduk from "./pages/darmasaba/form_kartu_tanda_penduduk";
import FormKeteranganKelahiran from "./pages/darmasaba/form_keterangan_kelahiran";
import FormLaporanSampah from "./pages/darmasaba/form_laporan_sampah";
import FormSuratKeteranganBelumKawin from "./pages/darmasaba/form_surat_keterangan_belum_kawin";
import FormSuratKeteranganDomisiliOrganisasi from "./pages/darmasaba/form_surat_keterangan_domisili_organisasi";
import FormSuratKeteranganKelakuanBaik from "./pages/darmasaba/form_surat_keterangan_kelakuan_baik";
import FormSuratKeteranganPenghasilan from "./pages/darmasaba/form_surat_keterangan_penghasilan";
import FormSuratKeteranganTempatUsaha from "./pages/darmasaba/form_surat_keterangan_tempat_usaha";
import FormSuratKeteranganTidakMampu from "./pages/darmasaba/form_surat_keterangan_tidak_mampu";
import FormSuratKeteranganUsaha from "./pages/darmasaba/form_surat_keterangan_usaha";
import DirPage from "./pages/dir/dir_page";
import Home from "./pages/Home";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Login from "./pages/Login";
import NotFound from "./pages/NotFound";
import ApikeyPage from "./pages/scr/dashboard/apikey/apikey_page";
import DarmasabaLayout from "./pages/darmasaba/darmasaba_layout";
import FormSuratKeteranganUsaha from "./pages/darmasaba/form_surat_keterangan_usaha";
import FormSuratKeteranganTidakMampu from "./pages/darmasaba/form_surat_keterangan_tidak_mampu";
import DarmasabaHome from "./pages/darmasaba/darmasaba_home";
import FormKartuTandaPenduduk from "./pages/darmasaba/form_kartu_tanda_penduduk";
import FormKartuKeluarga from "./pages/darmasaba/form_kartu_keluarga";
import FormLaporanSampah from "./pages/darmasaba/form_laporan_sampah";
import FormSuratKeteranganPenghasilan from "./pages/darmasaba/form_surat_keterangan_penghasilan";
import FormSuratKeteranganDomisiliOrganisasi from "./pages/darmasaba/form_surat_keterangan_domisili_organisasi";
import FormSuratKeteranganBelumKawin from "./pages/darmasaba/form_surat_keterangan_belum_kawin";
import FormKeteranganKelahiran from "./pages/darmasaba/form_keterangan_kelahiran";
import FormSuratKeteranganTempatUsaha from "./pages/darmasaba/form_surat_keterangan_tempat_usaha";
import FormSuratKeteranganKelakuanBaik from "./pages/darmasaba/form_surat_keterangan_kelakuan_baik";
import Home from "./pages/Home";
import CredentialPage from "./pages/scr/dashboard/credential/credential_page";
import DashboardHome from "./pages/scr/dashboard/dashboard_home";
import DashboardLayout from "./pages/scr/dashboard/dashboard_layout";
import ListPage from "./pages/scr/dashboard/pengaduan/list_page";
import ApikeyPage from "./pages/scr/dashboard/apikey/apikey_page";
import DashboardLayout from "./pages/scr/dashboard/dashboard_layout";
import ScrLayout from "./pages/scr/scr_layout";
import DirPage from "./pages/dir/dir_page";
import NotFound from "./pages/NotFound";
export default function AppRoutes() {
return (

View File

@@ -13,7 +13,7 @@ import { MCPRoute } from "./server/routes/mcp_route";
import PelayananRoute from "./server/routes/pelayanan_surat_route";
import PengaduanRoute from "./server/routes/pengaduan_route";
import UserRoute from "./server/routes/user_route";
import cors from "@elysiajs/cors"
import cors from "@elysiajs/cors";
const Docs = new Elysia({
tags: ["docs"],
@@ -41,11 +41,13 @@ const app = new Elysia()
.use(Api)
.use(Docs)
.use(Auth)
.use(cors({
origin: "*",
methods: ["GET", "POST", "OPTIONS"],
allowedHeaders: ["Content-Type"],
}))
.use(
cors({
origin: "*",
methods: ["GET", "POST", "OPTIONS"],
allowedHeaders: ["Content-Type"],
}),
)
.get(
"/.well-known/mcp.json",
async () => {

View File

@@ -2,17 +2,26 @@ import apiFetch from "@/lib/apiFetch";
import {
Badge,
Card,
CloseButton,
Container,
Divider,
Flex,
Group,
Input,
Stack,
Tabs,
Text,
Title
} from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { IconAlignJustified, IconClockHour3, IconFileSad, IconMapPin } from "@tabler/icons-react";
import {
IconAlignJustified,
IconClockHour3,
IconFileSad,
IconMapPin,
IconSearch,
} from "@tabler/icons-react";
import { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import useSwr from "swr";
import { proxy } from "valtio";
@@ -25,17 +34,13 @@ function reloadState() {
export default function PengaduanListPage() {
const { search } = useLocation();
const query = new URLSearchParams(search);
const status = query.get("status");
const status = query.get("status") as StatusKey;
return (
<Container
size="xl"
py="xl"
w={"100%"}
>
<Container size="xl" py="xl" w={"100%"}>
<Stack gap="xl">
<TabListPengaduan status={status || "all"} />
<ListPengaduan status={status || "all"} />
<TabListPengaduan status={status || "semua"} />
<ListPengaduan status={status || "semua"} />
</Stack>
</Container>
);
@@ -44,39 +49,83 @@ export default function PengaduanListPage() {
function TabListPengaduan({ status }: { status: string }) {
const navigate = useNavigate();
const dataCount = useSwr("/pengaduan/count", () =>
apiFetch.api.pengaduan.count.get().then((res) => res.data)
apiFetch.api.pengaduan.count.get().then((res) => res.data),
);
return (
<Tabs defaultValue={status || "all"} color="teal">
<Tabs defaultValue={status || "semua"} color="teal">
<Tabs.List grow>
<Tabs.Tab value="all" onClick={() => { navigate("?status=all") }}>Semua ({dataCount?.data?.semua || 0})</Tabs.Tab>
<Tabs.Tab value="antrian" onClick={() => { navigate("?status=antrian") }}>Antrian ({dataCount?.data?.antrian || 0})</Tabs.Tab>
<Tabs.Tab value="diterima" onClick={() => { navigate("?status=diterima") }}>Diterima ({dataCount?.data?.diterima || 0})</Tabs.Tab>
<Tabs.Tab value="dikerjakan" onClick={() => { navigate("?status=dikerjakan") }}>Dikerjakan ({dataCount?.data?.dikerjakan || 0})</Tabs.Tab>
<Tabs.Tab value="selesai" onClick={() => { navigate("?status=selesai") }}>Selesai ({dataCount?.data?.selesai || 0})</Tabs.Tab>
<Tabs.Tab value="ditolak" onClick={() => { navigate("?status=ditolak") }}>Ditolak ({dataCount?.data?.ditolak || 0})</Tabs.Tab>
<Tabs.Tab
value="all"
onClick={() => {
navigate("?status=semua");
}}
>
Semua ({dataCount?.data?.semua || 0})
</Tabs.Tab>
<Tabs.Tab
value="antrian"
onClick={() => {
navigate("?status=antrian");
}}
>
Antrian ({dataCount?.data?.antrian || 0})
</Tabs.Tab>
<Tabs.Tab
value="diterima"
onClick={() => {
navigate("?status=diterima");
}}
>
Diterima ({dataCount?.data?.diterima || 0})
</Tabs.Tab>
<Tabs.Tab
value="dikerjakan"
onClick={() => {
navigate("?status=dikerjakan");
}}
>
Dikerjakan ({dataCount?.data?.dikerjakan || 0})
</Tabs.Tab>
<Tabs.Tab
value="selesai"
onClick={() => {
navigate("?status=selesai");
}}
>
Selesai ({dataCount?.data?.selesai || 0})
</Tabs.Tab>
<Tabs.Tab
value="ditolak"
onClick={() => {
navigate("?status=ditolak");
}}
>
Ditolak ({dataCount?.data?.ditolak || 0})
</Tabs.Tab>
</Tabs.List>
</Tabs>
);
}
function ListPengaduan({ status }: { status: string }) {
type StatusKey = "antrian" | "diterima" | "dikerjakan" | "ditolak" | "selesai" | "semua";
function ListPengaduan({ status }: { status: StatusKey }) {
const [page, setPage] = useState(1);
const [value, setValue] = useState("");
const { data, mutate, isLoading } = useSwr("/", () =>
apiFetch.api.pengaduan.list.get({
query: {
status,
search: "",
search: value,
take: "",
page: ""
page: "",
},
}),
);
useShallowEffect(() => {
mutate();
}, [status]);
}, [status, value]);
if (isLoading)
return (
@@ -99,94 +148,118 @@ function ListPengaduan({ status }: { status: string }) {
return (
<Stack gap="xl">
{
list.length === 0 ? (
<Flex justify="center" align="center" py={"xl"}>
<Stack gap={4} align="center">
<IconFileSad size={32} color="gray" />
<Text c="dimmed" size="sm">
No pengaduan have been added yet.
</Text>
</Stack>
</Flex>
) :
list.map((v: any) => (
<Card
key={v.id}
radius="lg"
p="xl"
withBorder
style={{
background:
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
borderColor: "rgba(100,100,100,0.2)",
boxShadow: "0 0 20px rgba(0,255,200,0.08)",
}}
>
<Stack gap="md">
<Flex align="center" justify="space-between">
<Flex direction={"column"}>
<Title order={3} c="gray.2">
{v.title}
<Group grow>
<Input
value={value}
placeholder="Cari pengaduan..."
onChange={(event) => setValue(event.currentTarget.value)}
leftSection={<IconSearch size={16} />}
rightSectionPointerEvents="all"
rightSection={
<CloseButton
aria-label="Clear input"
onClick={() => setValue('')}
style={{ display: value ? undefined : 'none' }}
/>
}
/>
{/* <Group justify="flex-end">
<Text size="sm">Menampilkan {Number(data?.data?.length) * (page - 1) + 1} {Math.min(10, Number(data?.data?.length) * page)} dari {Number(data?.data?.length)}</Text>
<Pagination total={Number(data?.data?.length)} value={page} onChange={setPage} withPages={false} />
</Group> */}
</Group>
{list.length === 0 ? (
<Flex justify="center" align="center" py={"xl"}>
<Stack gap={4} align="center">
<IconFileSad size={32} color="gray" />
<Text c="dimmed" size="sm">
No pengaduan have been added yet.
</Text>
</Stack>
</Flex>
) : (
list.map((v: any) => (
<Card
key={v.id}
radius="lg"
p="xl"
withBorder
style={{
background:
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
borderColor: "rgba(100,100,100,0.2)",
boxShadow: "0 0 20px rgba(0,255,200,0.08)",
}}
>
<Stack gap="md">
<Flex align="center" justify="space-between">
<Flex direction={"column"}>
<Title order={3} c="gray.2">
{v.title}
</Title>
<Group>
<Title order={6} c="gray.5">
#{v.noPengaduan}
</Title>
<Group>
<Title order={6} c="gray.5">
#{v.noPengaduan}
</Title>
<Text size="sm" c="dimmed">
{v.updatedAt}
</Text>
</Group>
</Flex>
<Badge
size="xl"
variant="light"
radius="sm"
color={v.status === "diterima" ? "green" : v.status === "ditolak" ? "red" : v.status === "selesai" ? "blue" : v.status === "dikerjakan" ? "purple" : "yellow"}
style={{ textTransform: "none" }}
>
{v.status}
</Badge>
<Text size="sm" c="dimmed">
{v.updatedAt}
</Text>
</Group>
</Flex>
<Divider my={0} />
<Stack gap="sm">
<Flex direction={"column"} justify="flex-start">
<Group gap="xs">
<IconClockHour3 size={20} color="white" />
<Text size="md" c="white">
Tanggal Aduan
</Text>
</Group>
<Text size="md">
{v.createdAt}
<Badge
size="xl"
variant="light"
radius="sm"
color={
v.status === "diterima"
? "green"
: v.status === "ditolak"
? "red"
: v.status === "selesai"
? "blue"
: v.status === "dikerjakan"
? "purple"
: "yellow"
}
style={{ textTransform: "none" }}
>
{v.status}
</Badge>
</Flex>
<Divider my={0} />
<Stack gap="sm">
<Flex direction={"column"} justify="flex-start">
<Group gap="xs">
<IconClockHour3 size={20} color="white" />
<Text size="md" c="white">
Tanggal Aduan
</Text>
</Flex>
<Flex direction={"column"} justify="flex-start">
<Group gap="xs">
<IconMapPin size={20} color="white" />
<Text size="md" c="white">
Lokasi
</Text>
</Group>
<Text size="md">
{v.location}
</Group>
<Text size="md">{v.createdAt}</Text>
</Flex>
<Flex direction={"column"} justify="flex-start">
<Group gap="xs">
<IconMapPin size={20} color="white" />
<Text size="md" c="white">
Lokasi
</Text>
</Flex>
<Flex direction={"column"} justify="flex-start">
<Group gap="xs">
<IconAlignJustified size={20} color="white" />
<Text size="md" c="white">
Detail
</Text>
</Group>
<Text size="md">
{v.detail}
</Group>
<Text size="md">{v.location}</Text>
</Flex>
<Flex direction={"column"} justify="flex-start">
<Group gap="xs">
<IconAlignJustified size={20} color="white" />
<Text size="md" c="white">
Detail
</Text>
</Flex>
</Stack>
</Group>
<Text size="md">{v.detail}</Text>
</Flex>
</Stack>
</Card>
))}
</Stack>
</Card>
))
)}
</Stack>
);
}

View File

@@ -500,7 +500,7 @@ const PengaduanRoute = new Elysia({
]
}
if (status && status !== "all") {
if (status && status !== "semua") {
where = {
...where,
status: status
@@ -511,7 +511,7 @@ const PengaduanRoute = new Elysia({
skip,
take: !take ? 10 : Number(take),
orderBy: {
createdAt: "asc"
createdAt: "desc"
},
where,
select: {