upd: dashboard admin

Deskripsi:
- tampilan profile;2A
- tampilan list category pengaduan
- tampilan list category pelayanan surat
- tampilan list configurasi desa

No Issues
This commit is contained in:
2025-11-11 15:31:00 +08:00
parent 5b0f9b06d8
commit 715a929e13
5 changed files with 313 additions and 166 deletions

View File

@@ -25,6 +25,7 @@ import ListPage from "./pages/scr/dashboard/pengaduan/list_page";
import DetailPage from "./pages/scr/dashboard/pengaduan/detail_page";
import ApikeyPage from "./pages/scr/dashboard/apikey/apikey_page";
import DashboardLayout from "./pages/scr/dashboard/dashboard_layout";
import DetailSettingPage from "./pages/scr/dashboard/setting/detail_setting_page";
import ScrLayout from "./pages/scr/scr_layout";
import DirPage from "./pages/dir/dir_page";
import NotFound from "./pages/NotFound";
@@ -126,6 +127,10 @@ export default function AppRoutes() {
path="/scr/dashboard/apikey/apikey"
element={<ApikeyPage />}
/>
<Route
path="/scr/dashboard/setting/detail-setting"
element={<DetailSettingPage />}
/>
</Route>
</Route>
<Route path="/dir/dir" element={<DirPage />} />

View File

@@ -26,6 +26,7 @@ const clientRoutes = {
"/scr/dashboard/pengaduan/list": "/scr/dashboard/pengaduan/list",
"/scr/dashboard/pengaduan/detail": "/scr/dashboard/pengaduan/detail",
"/scr/dashboard/apikey/apikey": "/scr/dashboard/apikey/apikey",
"/scr/dashboard/setting/detail-setting": "/scr/dashboard/setting/detail-setting",
"/dir/dir": "/dir/dir",
"/*": "/*"
} as const;

View File

@@ -242,7 +242,7 @@ function NavigationDashboard() {
description: "Manage warga",
},
{
path: "/scr/dashboard/setting",
path: "/scr/dashboard/setting/detail-setting",
icon: <IconSettings size={20} />,
label: "Setting",
description:

View File

@@ -0,0 +1,150 @@
import { Button, Card, Container, Divider, Flex, Grid, Group, Input, NavLink, Stack, Table, Title } from "@mantine/core";
import { IconCircleOff, IconGauge, IconHome2 } from "@tabler/icons-react";
import { useLocation } from "react-router-dom";
export default function DetailSettingPage() {
const { search } = useLocation();
const query = new URLSearchParams(search);
const type = query.get("type");
return (
<Container size="xl" py="xl" w={"100%"}>
<Grid>
<Grid.Col span={3}>
<Card
radius="md"
p="lg"
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)",
}}
>
<NavLink
href={`?type=profile`}
label="Profile"
leftSection={<IconHome2 size={16} stroke={1.5} />}
active={type === "profile" || !type}
/>
<NavLink
href={`?type=cat-pengaduan`}
label="Kategori Pengaduan"
leftSection={<IconGauge size={16} stroke={1.5} />}
active={type === "cat-pengaduan"}
/>
<NavLink
href={`?type=cat-pelayanan`}
label="Kategori Pelayanan Surat"
leftSection={<IconCircleOff size={16} stroke={1.5} />}
active={type === "cat-pelayanan"}
/>
<NavLink
href={`?type=desa`}
label="Desa"
leftSection={<IconCircleOff size={16} stroke={1.5} />}
active={type === "desa"}
/>
</Card>
</Grid.Col>
<Grid.Col span={9}>
<Card
radius="md"
p="lg"
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)",
}}
>
{type === "cat-pengaduan"
? <KategoriPengaduanPage />
: type === "cat-pelayanan"
? <KategoriPengaduanPage />
: type === "desa"
? <KategoriPengaduanPage />
: <ProfilePage />}
</Card>
</Grid.Col>
</Grid>
</Container>
);
}
function ProfilePage() {
return (
<Stack gap={"md"}>
<Flex align="center" justify="space-between">
<Title order={4} c="gray.2">
Profile Pengguna
</Title>
<Button variant="light">Edit</Button>
</Flex>
<Divider my={0} />
<Stack gap={"md"}>
<Group gap="xl" grow>
<Input.Wrapper label="Nama" description="" error="">
<Input value={"Amalia Dwi Yustiani"} readOnly />
</Input.Wrapper>
<Input.Wrapper label="Phone" description="" error="">
<Input value={"08123456789"} readOnly />
</Input.Wrapper>
</Group>
<Group gap="xl" grow>
<Input.Wrapper label="Email" description="" error="">
<Input value={"amaliadwiyustiani@gmail.com"} readOnly />
</Input.Wrapper>
<Input.Wrapper label="Role" description="" error="">
<Input value={"Admin"} readOnly />
</Input.Wrapper>
</Group>
</Stack>
</Stack>
)
}
function KategoriPengaduanPage() {
const elements = [
{ position: 6, mass: 12.011, symbol: "C", name: "Carbon" },
{ position: 7, mass: 14.007, symbol: "N", name: "Nitrogen" },
{ position: 39, mass: 88.906, symbol: "Y", name: "Yttrium" },
{ position: 56, mass: 137.33, symbol: "Ba", name: "Barium" },
{ position: 58, mass: 140.12, symbol: "Ce", name: "Cerium" },
];
const rows = elements.map((element) => (
<Table.Tr key={element.name}>
<Table.Td>{element.position}</Table.Td>
<Table.Td>{element.name}</Table.Td>
<Table.Td>{element.symbol}</Table.Td>
<Table.Td>{element.mass}</Table.Td>
</Table.Tr>
));
return (
<Stack gap={"md"}>
<Flex align="center" justify="space-between">
<Title order={4} c="gray.2">
Kategori Pengaduan
</Title>
<Button variant="light">Tambah</Button>
</Flex>
<Divider my={0} />
<Stack gap={"md"}>
<Table>
<Table.Thead>
<Table.Tr>
<Table.Th>Tanggal</Table.Th>
<Table.Th>Deskripsi</Table.Th>
<Table.Th>Status</Table.Th>
<Table.Th>User</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>{rows}</Table.Tbody>
</Table>
</Stack>
</Stack>
)
}

View File

@@ -1,191 +1,182 @@
import apiFetch from "@/lib/apiFetch";
import {
Avatar,
Box,
Card,
Container,
Divider,
Flex,
Grid,
Group,
Stack,
Table,
Text,
Title
Avatar,
Box,
Card,
Container,
Divider,
Flex,
Grid,
Group,
Stack,
Table,
Text,
Title,
} from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import {
IconMail,
IconMapPin,
IconPhone
} from "@tabler/icons-react";
import { IconMail, IconMapPin, IconPhone } from "@tabler/icons-react";
import { useState } from "react";
import { useLocation } from "react-router-dom";
import useSwr from "swr";
export default function DetailWargaPage() {
const { search } = useLocation();
const query = new URLSearchParams(search);
const id = query.get("id");
const { search } = useLocation();
const query = new URLSearchParams(search);
const id = query.get("id");
return (
<Container size="xl" py="xl" w={"100%"}>
<Grid>
<Grid.Col span={4}>
<DetailWarga />
</Grid.Col>
<Grid.Col span={8}>
<Stack gap={"xl"}>
<DetailDataHistori />
<DetailDataHistori />
</Stack>
</Grid.Col>
</Grid>
</Container>
);
return (
<Container size="xl" py="xl" w={"100%"}>
<Grid>
<Grid.Col span={4}>
<DetailWarga />
</Grid.Col>
<Grid.Col span={8}>
<Stack gap={"xl"}>
<DetailDataHistori />
<DetailDataHistori />
</Stack>
</Grid.Col>
</Grid>
</Container>
);
}
function DetailDataHistori() {
const elements = [
{ position: 6, mass: 12.011, symbol: "C", name: "Carbon" },
{ position: 7, mass: 14.007, symbol: "N", name: "Nitrogen" },
{ position: 39, mass: 88.906, symbol: "Y", name: "Yttrium" },
{ position: 56, mass: 137.33, symbol: "Ba", name: "Barium" },
{ position: 58, mass: 140.12, symbol: "Ce", name: "Cerium" },
];
const elements = [
{ position: 6, mass: 12.011, symbol: "C", name: "Carbon" },
{ position: 7, mass: 14.007, symbol: "N", name: "Nitrogen" },
{ position: 39, mass: 88.906, symbol: "Y", name: "Yttrium" },
{ position: 56, mass: 137.33, symbol: "Ba", name: "Barium" },
{ position: 58, mass: 140.12, symbol: "Ce", name: "Cerium" },
];
const rows = elements.map((element) => (
<Table.Tr key={element.name}>
<Table.Td>{element.position}</Table.Td>
<Table.Td>{element.name}</Table.Td>
<Table.Td>{element.symbol}</Table.Td>
<Table.Td>{element.mass}</Table.Td>
</Table.Tr>
));
return (
<Card
radius="md"
p="lg"
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">
<Title order={4} c="gray.2">
Histori Pengaduan
</Title>
</Flex>
<Divider my={0} />
<Table>
<Table.Thead>
<Table.Tr>
<Table.Th>Tanggal</Table.Th>
<Table.Th>Deskripsi</Table.Th>
<Table.Th>Status</Table.Th>
<Table.Th>User</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>{rows}</Table.Tbody>
</Table>
</Stack>
</Card>
);
const rows = elements.map((element) => (
<Table.Tr key={element.name}>
<Table.Td>{element.position}</Table.Td>
<Table.Td>{element.name}</Table.Td>
<Table.Td>{element.symbol}</Table.Td>
<Table.Td>{element.mass}</Table.Td>
</Table.Tr>
));
return (
<Card
radius="md"
p="lg"
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">
<Title order={4} c="gray.2">
Histori Pengaduan
</Title>
</Flex>
<Divider my={0} />
<Table>
<Table.Thead>
<Table.Tr>
<Table.Th>Tanggal</Table.Th>
<Table.Th>Deskripsi</Table.Th>
<Table.Th>Status</Table.Th>
<Table.Th>User</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>{rows}</Table.Tbody>
</Table>
</Stack>
</Card>
);
}
function DetailWarga() {
const [page, setPage] = useState(1);
const [value, setValue] = useState("");
const { data, mutate, isLoading } = useSwr("/", () =>
apiFetch.api.pengaduan.list.get({
query: {
status,
search: value,
take: "",
page: "",
},
}),
);
const [page, setPage] = useState(1);
const [value, setValue] = useState("");
const { data, mutate, isLoading } = useSwr("/", () =>
apiFetch.api.pengaduan.list.get({
query: {
status,
search: value,
take: "",
page: "",
},
}),
);
useShallowEffect(() => {
mutate();
}, [status, value]);
useShallowEffect(() => {
mutate();
}, [status, value]);
const list = data?.data || [];
const list = data?.data || [];
return (
<Card
radius="md"
p="lg"
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)",
}}
>
<Box
style={{
backgroundColor: "#f7d86c",
height: 100,
borderRadius: "12px",
position: "relative",
}}
/>
<Group>
{/* Profile image */}
<Avatar
src="https://i.pravatar.cc/150?img=32"
radius={100}
size={90}
style={{
position: "absolute",
top: 80,
left: 30,
border: "4px solid white",
}}
/>
return (
<Card
radius="md"
p="lg"
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)",
}}
>
<Box
style={{
backgroundColor: "#f7d86c",
height: 100,
borderRadius: "12px",
position: "relative",
}}
/>
<Group>
{/* Profile image */}
<Avatar
src="https://i.pravatar.cc/150?img=32"
radius={100}
size={90}
style={{
position: "absolute",
top: 80,
left: 30,
border: "4px solid white",
}}
/>
{/* Main content */}
<Stack ml={115} gap={4}>
<Text fw={700} fz="lg">
Lizbeth Moore
</Text>
<Text fz="sm" c="dimmed">
Social Media Strategies
</Text>
</Stack>
</Group>
{/* Main content */}
<Stack ml={115} gap={4}>
<Text fw={700} fz="lg">
Lizbeth Moore
</Text>
<Text fz="sm" c="dimmed">
Social Media Strategies
</Text>
</Stack>
</Group>
{/* Contact info */}
<Card
radius="md"
mt="md"
p="md"
withBorder={false}
>
<Stack gap="xs">
<Group gap="xs">
<IconMail size={18} />
<Text size="sm">lizbeth.moore@email.com</Text>
</Group>
{/* Contact info */}
<Card radius="md" mt="md" p="md" withBorder={false}>
<Stack gap="xs">
<Group gap="xs">
<IconMail size={18} />
<Text size="sm">lizbeth.moore@email.com</Text>
</Group>
<Group gap="xs">
<IconPhone size={18} />
<Text size="sm">+1 555-7788</Text>
</Group>
<Group gap="xs">
<IconPhone size={18} />
<Text size="sm">+1 555-7788</Text>
</Group>
<Group gap="xs">
<IconMapPin size={18} />
<Text size="sm">Greenway Ave, Los Angeles, CA, USA</Text>
</Group>
</Stack>
</Card>
<Group gap="xs">
<IconMapPin size={18} />
<Text size="sm">Greenway Ave, Los Angeles, CA, USA</Text>
</Group>
</Stack>
</Card>
);
</Card>
);
}