upd: dashboard

Deskripsi:
- menu dashboard
- tampilan pengaduan list

No Issues
This commit is contained in:
2025-11-06 17:37:21 +08:00
parent df7f93c794
commit 89e83d806e
5 changed files with 253 additions and 32 deletions

View File

@@ -1,27 +1,28 @@
// ⚡ Auto-generated by generateRoutes.ts — DO NOT EDIT MANUALLY
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Login from "./pages/Login";
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 { BrowserRouter, Route, Routes } from "react-router-dom";
import DarmasabaHome from "./pages/darmasaba/darmasaba_home";
import FormKartuTandaPenduduk from "./pages/darmasaba/form_kartu_tanda_penduduk";
import DarmasabaLayout from "./pages/darmasaba/darmasaba_layout";
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 FormKartuTandaPenduduk from "./pages/darmasaba/form_kartu_tanda_penduduk";
import FormKeteranganKelahiran from "./pages/darmasaba/form_keterangan_kelahiran";
import FormSuratKeteranganTempatUsaha from "./pages/darmasaba/form_surat_keterangan_tempat_usaha";
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 Login from "./pages/Login";
import NotFound from "./pages/NotFound";
import ApikeyPage from "./pages/scr/dashboard/apikey/apikey_page";
import CredentialPage from "./pages/scr/dashboard/credential/credential_page";
import DashboardHome from "./pages/scr/dashboard/dashboard_home";
import ApikeyPage from "./pages/scr/dashboard/apikey/apikey_page";
import DashboardLayout from "./pages/scr/dashboard/dashboard_layout";
import ListPage from "./pages/scr/dashboard/pengaduan/list_page";
import ScrLayout from "./pages/scr/scr_layout";
import DirPage from "./pages/dir/dir_page";
import NotFound from "./pages/NotFound";
export default function AppRoutes() {
return (
@@ -92,6 +93,10 @@ export default function AppRoutes() {
path="/scr/dashboard/dashboard-home"
element={<DashboardHome />}
/>
<Route
path="/scr/dashboard/pengaduan/list"
element={<ListPage />}
/>
<Route
path="/scr/dashboard/apikey/apikey"
element={<ApikeyPage />}

View File

@@ -19,6 +19,7 @@ const clientRoutes = {
"/scr/dashboard": "/scr/dashboard",
"/scr/dashboard/credential/credential": "/scr/dashboard/credential/credential",
"/scr/dashboard/dashboard-home": "/scr/dashboard/dashboard-home",
"/scr/dashboard/pengaduan/list": "/scr/dashboard/pengaduan/list",
"/scr/dashboard/apikey/apikey": "/scr/dashboard/apikey/apikey",
"/dir/dir": "/dir/dir",
"/*": "/*"

View File

@@ -2,16 +2,16 @@ import { Tree } from "@mantine/core";
// ✅ Valid data, all values are unique
const data = [
{
value: 'src',
label: 'src',
children: [
{ value: 'src/components', label: 'components' },
{ value: 'src/hooks', label: 'hooks' },
],
},
{ value: 'package.json', label: 'package.json' },
];
{
value: "src",
label: "src",
children: [
{ value: "src/components", label: "components" },
{ value: "src/hooks", label: "hooks" },
],
},
{ value: "package.json", label: "package.json" },
];
export default function DirPage() {
return (

View File

@@ -1,4 +1,8 @@
import { useEffect, useState } from "react";
import {
default as clientRoute,
default as clientRoutes,
} from "@/clientRoutes";
import apiFetch from "@/lib/apiFetch";
import {
ActionIcon,
AppShell,
@@ -22,17 +26,17 @@ import {
IconChevronLeft,
IconChevronRight,
IconDashboard,
IconFileCertificate,
IconKey,
IconLock,
IconMessageReport,
IconSettings,
IconUser,
IconUsersGroup,
} from "@tabler/icons-react";
import type { User } from "generated/prisma";
import { useEffect, useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import {
default as clientRoute,
default as clientRoutes,
} from "@/clientRoutes";
import apiFetch from "@/lib/apiFetch";
function Logout() {
return (
@@ -98,7 +102,7 @@ export default function DashboardLayout() {
size="lg"
style={{
backgroundColor: "rgba(255,255,255,0.05)",
boxShadow: "0 0 6px rgba(0,255,200,0.2)",
boxShadow: "0 0 6px hsla(167, 100%, 50%, 0.20), 0.20)",
}}
>
{opened ? <IconChevronLeft /> : <IconChevronRight />}
@@ -186,7 +190,7 @@ function HostView() {
{host.name}
</Text>
<Text size="sm" c="dimmed">
{host.email}
{host.roleId}
</Text>
</Stack>
</Flex>
@@ -219,6 +223,31 @@ function NavigationDashboard() {
label: "Dashboard Overview",
description: "Quick summary and insights",
},
{
path: "/scr/dashboard/pengaduan/list",
icon: <IconMessageReport size={20} />,
label: "Pengaduan Warga",
description: "Manage pengaduan warga",
},
{
path: "/scr/dashboard/pelayanan",
icon: <IconFileCertificate size={20} />,
label: "Pelayanan Surat",
description: "Manage pelayanan surat",
},
{
path: "/scr/dashboard/user",
icon: <IconUsersGroup size={20} />,
label: "User",
description: "Manage user",
},
{
path: "/scr/dashboard/setting",
icon: <IconSettings size={20} />,
label: "Setting",
description:
"Manage setting (category pengaduan dan pelayanan surat, desa, etc)",
},
{
path: "/scr/dashboard/apikey/apikey",
icon: <IconKey size={20} />,

View File

@@ -0,0 +1,186 @@
import apiFetch from "@/lib/apiFetch";
import {
Badge,
Card,
Container,
Divider,
Flex,
Group,
Stack,
Tabs,
Text,
Title
} from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { showNotification } from "@mantine/notifications";
import { IconAlignJustified, IconClockHour3, IconMapPin } from "@tabler/icons-react";
import { useLocation, useNavigate } from "react-router-dom";
import useSwr from "swr";
import { proxy, subscribe } from "valtio";
const state = proxy({ reload: "" });
function reloadState() {
state.reload = Math.random().toString();
}
export default function PengaduanListPage() {
const { search } = useLocation();
const query = new URLSearchParams(search);
const status = query.get("status");
console.log(status, "status");
return (
<Container
size="xl"
py="xl"
w={"100%"}
>
<Stack gap="xl">
<TabListPengaduan status={status || "all"} />
<ListPengaduan />
</Stack>
</Container>
);
}
function TabListPengaduan({ status }: { status: string }) {
const navigate = useNavigate();
return (
<Tabs defaultValue={status || "all"} color="teal">
<Tabs.List grow>
<Tabs.Tab value="all" onClick={() => { navigate("?status=all") }}>Semua</Tabs.Tab>
<Tabs.Tab value="antrian" onClick={() => { navigate("?status=antrian") }}>Antrian</Tabs.Tab>
<Tabs.Tab value="diterima" onClick={() => { navigate("?status=diterima") }}>Diterima</Tabs.Tab>
<Tabs.Tab value="dikerjakan" onClick={() => { navigate("?status=dikerjakan") }}>Dikerjakan</Tabs.Tab>
<Tabs.Tab value="ditolak" onClick={() => { navigate("?status=ditolak") }}>Ditolak</Tabs.Tab>
<Tabs.Tab value="selesai" onClick={() => { navigate("?status=selesai") }}>Selesai</Tabs.Tab>
</Tabs.List>
</Tabs>
);
}
function ListPengaduan() {
const { data, mutate, isLoading } = useSwr("/", () =>
apiFetch.api.credential.list.get(),
);
useShallowEffect(() => {
const unsubscribe = subscribe(state, () => mutate());
return () => unsubscribe();
}, []);
async function handleRemove(id: string) {
try {
await apiFetch.api.credential.rm.delete({ id });
showNotification({
color: "teal",
title: "Credential Deleted",
message: "The credential was successfully removed.",
});
reloadState();
} catch {
showNotification({
color: "red",
title: "Error",
message: "Failed to delete credential. Please try again.",
});
}
}
if (isLoading)
return (
<Card
radius="lg"
p="xl"
withBorder
style={{
background:
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
}}
>
<Text size="sm" c="dimmed">
Loading credentials...
</Text>
</Card>
);
const list = data?.data?.list || [];
return (
<Card
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">
Dompet Hilang
</Title>
<Group>
<Title order={6} c="gray.5">
#PGD-061125-001
</Title>
<Text size="sm" c="dimmed">
updated 2 minutes ago
</Text>
</Group>
</Flex>
<Badge
size="xl"
variant="light"
radius="sm"
color="gray"
style={{ textTransform: "none" }}
>
Antrian
</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>
</Group>
<Text size="md">
05 November 2025
</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">
Jalan Darmasaba Raya no 77
</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">
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quis, obcaecati. Sint natus culpa temporibus neque quasi expedita ratione, facere optio incidunt quibusdam suscipit nam nemo delectus beatae similique velit obcaecati?
</Text>
</Flex>
</Stack>
</Stack>
</Card>
);
}