Deskripsi: - satuan luas tempat usaha - satuan pendapatan perbulan - pada tambah, edit, detail surat No Issues
811 lines
24 KiB
TypeScript
811 lines
24 KiB
TypeScript
import BreadCrumbs from "@/components/BreadCrumbs";
|
|
import FullScreenLoading from "@/components/FullScreenLoading";
|
|
import ModalFile from "@/components/ModalFile";
|
|
import ModalSurat from "@/components/ModalSurat";
|
|
import notification from "@/components/notificationGlobal";
|
|
import apiFetch from "@/lib/apiFetch";
|
|
import { parseTanggalID } from "@/server/lib/stringToDate";
|
|
import {
|
|
ActionIcon,
|
|
Anchor,
|
|
Badge,
|
|
Button,
|
|
Card,
|
|
Container,
|
|
Divider,
|
|
Flex,
|
|
Grid,
|
|
Group,
|
|
List,
|
|
Modal,
|
|
Select,
|
|
Spoiler,
|
|
Stack,
|
|
Table,
|
|
Text,
|
|
Textarea,
|
|
TextInput,
|
|
ThemeIcon,
|
|
Title,
|
|
Tooltip
|
|
} from "@mantine/core";
|
|
import { DateInput } from "@mantine/dates";
|
|
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
|
|
import {
|
|
IconAlignJustified,
|
|
IconCheck,
|
|
IconEdit,
|
|
IconFileCertificate,
|
|
IconFileCheck,
|
|
IconInfoCircle,
|
|
IconMessageReport,
|
|
IconPhone,
|
|
IconUser,
|
|
} from "@tabler/icons-react";
|
|
import dayjs from "dayjs";
|
|
import type { User } from "generated/prisma";
|
|
import type { JsonValue } from "generated/prisma/runtime/library";
|
|
import _ from "lodash";
|
|
import { useEffect, useState } from "react";
|
|
import { useLocation } from "react-router-dom";
|
|
import useSwr from "swr";
|
|
|
|
export default function DetailPengajuanPage() {
|
|
const dataMenu = [
|
|
{ title: "Dashboard", link: "/scr/dashboard/dashboard-home", active: false },
|
|
{ title: "Pelayanan Surat", link: "/scr/dashboard/pelayanan-surat/list-pelayanan", active: false },
|
|
{ title: "Detail Pengajuan Surat", link: "#", active: true },
|
|
];
|
|
const { search } = useLocation();
|
|
const query = new URLSearchParams(search);
|
|
const id = query.get("id");
|
|
const { data, mutate, isLoading } = useSwr("/", () =>
|
|
apiFetch.api.pelayanan.detail.get({
|
|
query: {
|
|
id: id!,
|
|
},
|
|
}),
|
|
);
|
|
|
|
useShallowEffect(() => {
|
|
mutate();
|
|
}, []);
|
|
|
|
return (
|
|
<Container size="xl" py="xl" w={"100%"}>
|
|
<Grid>
|
|
<Grid.Col span={12}>
|
|
<BreadCrumbs dataLink={dataMenu} back />
|
|
</Grid.Col>
|
|
<Grid.Col span={8}>
|
|
<Stack gap={"xl"}>
|
|
<DetailDataPengajuan
|
|
data={data?.data?.pengajuan}
|
|
warga={data && data.data && data.data.warga ? data.data.warga : undefined}
|
|
syaratDokumen={data?.data?.syaratDokumen}
|
|
dataText={data?.data?.dataText}
|
|
onAction={() => {
|
|
mutate();
|
|
}}
|
|
/>
|
|
<DetailDataHistori data={data?.data?.history} />
|
|
</Stack>
|
|
</Grid.Col>
|
|
<Grid.Col span={4}>
|
|
<DetailUserPengajuan data={data?.data?.warga} />
|
|
</Grid.Col>
|
|
</Grid>
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
function DetailDataPengajuan({
|
|
data,
|
|
warga,
|
|
syaratDokumen,
|
|
dataText,
|
|
onAction,
|
|
}: {
|
|
data: any;
|
|
warga?: { phone?: string | null } | null;
|
|
syaratDokumen: any;
|
|
dataText: any;
|
|
onAction: () => void;
|
|
}) {
|
|
|
|
const [opened, { open, close }] = useDisclosure(false);
|
|
const [catModal, setCatModal] = useState<"tolak" | "terima">("tolak");
|
|
const [keterangan, setKeterangan] = useState("");
|
|
const [host, setHost] = useState<User | null>(null);
|
|
const [noSurat, setNoSurat] = useState("");
|
|
const [openedPreview, setOpenedPreview] = useState(false);
|
|
const [openedPreviewFile, setOpenedPreviewFile] = useState(false);
|
|
const [permissions, setPermissions] = useState<JsonValue[]>([]);
|
|
const [viewImg, setViewImg] = useState({ file: "", folder: "" });
|
|
const [uploading, setUploading] = useState({ ok: false, file: "" });
|
|
const [editValue, setEditValue] = useState({ id: "", jenis: "", val: "", satuan: null as string | null, option: null as any, type: "", key: "" })
|
|
const [openEdit, setOpenEdit] = useState(false)
|
|
const [loadingUpdate, setLoadingUpdate] = useState(false)
|
|
const [loadingFS, setLoadingFS] = useState({ value: false, text: "" })
|
|
|
|
useEffect(() => {
|
|
async function fetchHost() {
|
|
const { data } = await apiFetch.api.user.find.get();
|
|
setHost(data?.user ?? null);
|
|
|
|
if (data?.permissions && Array.isArray(data.permissions)) {
|
|
const onlySetting = data.permissions.filter((p: any) =>
|
|
p.startsWith("pelayanan"),
|
|
);
|
|
setPermissions(onlySetting);
|
|
}
|
|
}
|
|
fetchHost();
|
|
}, []);
|
|
|
|
async function sendWA({ status, linkSurat, linkUpdate }: { status: string, linkSurat: string, linkUpdate: string }) {
|
|
try {
|
|
setLoadingFS({ value: true, text: "Sending message to warga" })
|
|
const resWA = await apiFetch.api["send-wa"]["pengajuan-surat"].post({
|
|
noPengajuan: data?.noPengajuan ?? "",
|
|
jenisSurat: data?.category ?? "",
|
|
alasan: keterangan,
|
|
status,
|
|
linkSurat,
|
|
linkUpdate,
|
|
tlp: warga?.phone ?? "",
|
|
})
|
|
|
|
if (resWA?.status === 200) {
|
|
if (resWA.data?.success) {
|
|
notification({
|
|
title: "Success",
|
|
message: "Success send message to warga",
|
|
type: "success",
|
|
});
|
|
|
|
if (status == "selesai") {
|
|
onAction()
|
|
}
|
|
|
|
} else {
|
|
notification({
|
|
title: "Failed",
|
|
message: "Failed send message to warga",
|
|
type: "error",
|
|
});
|
|
}
|
|
} else {
|
|
notification({
|
|
title: "Failed",
|
|
message: "Failed send message to warga",
|
|
type: "error",
|
|
});
|
|
}
|
|
} catch (error) {
|
|
notification({
|
|
title: "Failed",
|
|
message: "Failed send message to warga",
|
|
type: "error",
|
|
});
|
|
}
|
|
finally {
|
|
setLoadingFS({ value: false, text: "" })
|
|
}
|
|
}
|
|
|
|
const handleKonfirmasi = async (cat: "terima" | "tolak") => {
|
|
try {
|
|
setLoadingFS({ value: true, text: "Updating status" })
|
|
const statusFix = cat == "tolak"
|
|
? "ditolak"
|
|
: data.status == "antrian"
|
|
? "diterima"
|
|
: "selesai"
|
|
|
|
const res = await apiFetch.api.pelayanan["update-status"].post({
|
|
id: data?.id,
|
|
status: statusFix,
|
|
keterangan: keterangan,
|
|
idUser: host?.id ?? "",
|
|
noSurat: noSurat,
|
|
});
|
|
|
|
if (res?.status === 200) {
|
|
if (statusFix == "selesai") {
|
|
setTimeout(() => {
|
|
setOpenedPreview(true)
|
|
}, 1000)
|
|
} else {
|
|
sendWA({
|
|
status: statusFix,
|
|
linkSurat: "",
|
|
linkUpdate: statusFix == "ditolak" ? res.data?.linkUpdate ?? '' : '',
|
|
});
|
|
}
|
|
|
|
|
|
onAction();
|
|
close();
|
|
notification({
|
|
title: "Success",
|
|
message: "Success update pengajuan surat",
|
|
type: "success",
|
|
});
|
|
} else {
|
|
notification({
|
|
title: "Error",
|
|
message: "Failed to update pengajuan surat",
|
|
type: "error",
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
notification({
|
|
title: "Error",
|
|
message: "Failed to update pengajuan surat",
|
|
type: "error",
|
|
});
|
|
} finally {
|
|
setLoadingFS({ value: false, text: "" })
|
|
}
|
|
};
|
|
|
|
async function updateDataText() {
|
|
try {
|
|
setLoadingUpdate(true)
|
|
const res = await apiFetch.api.pelayanan["update-data-pelengkap"].post({
|
|
id: editValue.id,
|
|
value: editValue.val,
|
|
jenis: editValue.key,
|
|
idUser: host?.id ?? "",
|
|
})
|
|
|
|
if (res?.status === 200) {
|
|
notification({
|
|
title: "Success",
|
|
message: "Success update data",
|
|
type: "success",
|
|
})
|
|
} else {
|
|
notification({
|
|
title: "Error",
|
|
message: "Failed to update data",
|
|
type: "error",
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
notification({
|
|
title: "Error",
|
|
message: "Failed to update data",
|
|
type: "error",
|
|
})
|
|
} finally {
|
|
setLoadingUpdate(false)
|
|
setOpenEdit(false)
|
|
onAction()
|
|
}
|
|
}
|
|
|
|
useShallowEffect(() => {
|
|
if (viewImg) {
|
|
setOpenedPreviewFile(true);
|
|
}
|
|
}, [viewImg]);
|
|
|
|
useShallowEffect(() => {
|
|
if (uploading.ok && uploading.file) {
|
|
sendWA({
|
|
status: "selesai",
|
|
linkSurat: uploading.file,
|
|
linkUpdate: "",
|
|
});
|
|
}
|
|
}, [uploading]);
|
|
|
|
function FieldLabel({ label, hint }: { label: string; hint?: string }) {
|
|
return (
|
|
<Group justify="apart" gap="xs" align="center">
|
|
<Text fw={600}>{label}</Text>
|
|
{hint && (
|
|
<Tooltip label={hint} withArrow>
|
|
<ActionIcon size={24} variant="subtle">
|
|
<IconInfoCircle size={16} />
|
|
</ActionIcon>
|
|
</Tooltip>
|
|
)}
|
|
</Group>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
|
|
<FullScreenLoading visible={loadingFS.value} text={loadingFS.text} />
|
|
{/* MODAL EDIT DATA PELENGKAP */}
|
|
<Modal
|
|
opened={openEdit}
|
|
onClose={() => setOpenEdit(false)}
|
|
title={"Edit"}
|
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
|
>
|
|
<Stack gap="ld">
|
|
{editValue.type == "enum" ? (
|
|
<Select
|
|
allowDeselect={false}
|
|
label={<FieldLabel label={editValue.jenis} />}
|
|
data={editValue.option ?? []}
|
|
placeholder={editValue.jenis}
|
|
onChange={(e) => { setEditValue({ ...editValue, val: e ?? "" }) }}
|
|
value={editValue.val}
|
|
/>
|
|
) : editValue.type == "date" ? (
|
|
<DateInput
|
|
locale="id"
|
|
valueFormat="DD MMMM YYYY"
|
|
label={<FieldLabel label={editValue.jenis} />}
|
|
placeholder={editValue.jenis}
|
|
onChange={(e) => {
|
|
const formatted = e
|
|
? dayjs(e).locale("id").format("DD MMMM YYYY")
|
|
: "";
|
|
setEditValue({
|
|
...editValue,
|
|
val: formatted
|
|
})
|
|
}}
|
|
value={
|
|
editValue.val
|
|
? parseTanggalID(editValue.val)
|
|
: parseTanggalID(editValue.val)
|
|
}
|
|
/>
|
|
) : (
|
|
<TextInput
|
|
label={<FieldLabel label={editValue.jenis} />}
|
|
placeholder={editValue.jenis}
|
|
type={editValue.type}
|
|
onChange={(e) => { setEditValue({ ...editValue, val: e.target.value }) }}
|
|
value={editValue.val}
|
|
rightSection={
|
|
editValue.satuan != null &&
|
|
<Text mr={"lg"}>{editValue.satuan}</Text>
|
|
}
|
|
/>
|
|
)}
|
|
<Group justify="center" grow>
|
|
<Button variant="light" onClick={() => { setOpenEdit(false) }}>
|
|
Batal
|
|
</Button>
|
|
<Button
|
|
variant="filled"
|
|
onClick={updateDataText}
|
|
disabled={loadingUpdate || !editValue.val}
|
|
loading={loadingUpdate}
|
|
>
|
|
Simpan
|
|
</Button>
|
|
</Group>
|
|
</Stack>
|
|
</Modal>
|
|
|
|
<ModalFile
|
|
open={openedPreviewFile && !_.isEmpty(viewImg.file)}
|
|
onClose={() => {
|
|
setOpenedPreviewFile(false);
|
|
setViewImg({ file: "", folder: "" })
|
|
}}
|
|
folder={viewImg.folder}
|
|
fileName={viewImg.file}
|
|
/>
|
|
|
|
{/* MODAL KONFIRMASI */}
|
|
<Modal
|
|
opened={opened}
|
|
onClose={close}
|
|
title={"Konfirmasi"}
|
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
|
>
|
|
<Stack gap="sm">
|
|
{catModal === "tolak" ? (
|
|
<>
|
|
<Text>
|
|
Anda yakin ingin menolak pengajuan surat ini? Berikan alasan
|
|
penolakan
|
|
</Text>
|
|
<Textarea
|
|
size="md"
|
|
minRows={5}
|
|
value={keterangan}
|
|
onChange={(e) => setKeterangan(e.target.value)}
|
|
/>
|
|
<Group justify="center" grow>
|
|
<Button variant="light" onClick={close}>
|
|
Batal
|
|
</Button>
|
|
<Button
|
|
variant="filled"
|
|
color="red"
|
|
disabled={keterangan.length < 1}
|
|
onClick={() => handleKonfirmasi("tolak")}
|
|
>
|
|
Tolak
|
|
</Button>
|
|
</Group>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Text>
|
|
Anda yakin ingin{" "}
|
|
{data?.status == "antrian" ? "menerima" : "menyetujui"}{" "}
|
|
pengajuan surat ini?
|
|
{data.status == "diterima" &&
|
|
"Masukkan nomer surat yang akan dibuat"}
|
|
</Text>
|
|
{data.status == "diterima" && (
|
|
<Textarea
|
|
size="md"
|
|
minRows={5}
|
|
value={noSurat}
|
|
onChange={(e) => setNoSurat(e.target.value)}
|
|
placeholder="Contoh : 08/D-IV/11/2025"
|
|
/>
|
|
)}
|
|
<Group justify="center" grow>
|
|
<Button variant="light" onClick={close}>
|
|
Tidak
|
|
</Button>
|
|
<Button
|
|
variant="filled"
|
|
color="green"
|
|
onClick={() => handleKonfirmasi("terima")}
|
|
disabled={data.status == "diterima" && noSurat.length < 1}
|
|
>
|
|
Ya
|
|
</Button>
|
|
</Group>
|
|
</>
|
|
)}
|
|
</Stack>
|
|
</Modal>
|
|
|
|
{/* MODAL PREVIEW SURAT */}
|
|
{data?.status == "selesai" && !data?.fileSurat && (
|
|
<ModalSurat
|
|
open={openedPreview}
|
|
onClose={(val) => {
|
|
setOpenedPreview(false)
|
|
setUploading({ ok: val.success, file: val.data })
|
|
}}
|
|
surat={data?.idSurat}
|
|
/>
|
|
)}
|
|
|
|
<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">
|
|
<Group gap="xs">
|
|
<Title order={4} c="gray.2">
|
|
Pengajuan {data?.category}
|
|
</Title>
|
|
<Title order={4} c="dimmed">
|
|
#{data?.noPengajuan}
|
|
</Title>
|
|
</Group>
|
|
<Badge
|
|
size="xl"
|
|
variant="light"
|
|
radius="sm"
|
|
color={
|
|
data?.status === "diterima"
|
|
? "green"
|
|
: data?.status === "ditolak"
|
|
? "red"
|
|
: data?.status === "selesai"
|
|
? "blue"
|
|
: data?.status === "dikerjakan"
|
|
? "gray"
|
|
: "yellow"
|
|
}
|
|
style={{ textTransform: "none" }}
|
|
>
|
|
{data?.status}
|
|
</Badge>
|
|
</Flex>
|
|
<Divider my={0} />
|
|
<Grid>
|
|
<Grid.Col span={12}>
|
|
<Stack gap="lg">
|
|
<Flex direction={"column"} justify="flex-start">
|
|
<Group gap="xs">
|
|
<IconFileCheck size={20} />
|
|
<Text size="md">Syarat Dokumen</Text>
|
|
</Group>
|
|
<List
|
|
spacing="sm"
|
|
pt={10}
|
|
icon={
|
|
<ThemeIcon variant="default" size={20} radius="xl">
|
|
<IconCheck size={13} />
|
|
</ThemeIcon>
|
|
}
|
|
>
|
|
{syaratDokumen?.map((v: any) => (
|
|
<List.Item key={v.id}>
|
|
<Anchor
|
|
onClick={() => {
|
|
setViewImg({ file: v.value, folder: "syarat-dokumen" });
|
|
}}
|
|
>
|
|
{v.jenis}
|
|
</Anchor>
|
|
</List.Item>
|
|
))}
|
|
</List>
|
|
</Flex>
|
|
|
|
<Flex direction={"column"} justify="flex-start">
|
|
<Group gap="xs">
|
|
<IconAlignJustified size={20} />
|
|
<Text size="md">Data Pelengkap</Text>
|
|
</Group>
|
|
|
|
<Table withRowBorders={false}>
|
|
<Table.Tbody>
|
|
{dataText?.map((item: any) => (
|
|
<Table.Tr key={item.id}>
|
|
<Table.Td
|
|
style={{ whiteSpace: "nowrap", width: "10%" }}
|
|
>
|
|
{_.upperFirst(item.jenis)}
|
|
</Table.Td>
|
|
<Table.Td>:</Table.Td>
|
|
<Table.Td style={{ width: "85%" }}>
|
|
<Flex
|
|
gap="md"
|
|
justify="flex-start"
|
|
align="center"
|
|
direction="row"
|
|
>
|
|
<Text>
|
|
{_.upperFirst(item.value)} {item.satuan}
|
|
</Text>
|
|
<ActionIcon
|
|
variant="subtle"
|
|
aria-label="Edit"
|
|
onClick={() => {
|
|
setEditValue({ id: item.id, val: item.value, type: item.type, satuan: item.satuan, option: item.options, jenis: item.jenis, key: item.key })
|
|
setOpenEdit(true)
|
|
}}>
|
|
<IconEdit size={16} />
|
|
</ActionIcon>
|
|
</Flex>
|
|
</Table.Td>
|
|
</Table.Tr>
|
|
))}
|
|
</Table.Tbody>
|
|
</Table>
|
|
</Flex>
|
|
</Stack>
|
|
</Grid.Col>
|
|
<Grid.Col span={12}>
|
|
{data?.status === "antrian" ? (
|
|
<Group justify="center" grow>
|
|
<Button
|
|
disabled={!permissions.includes("pelayanan.antrian.tolak")}
|
|
variant="light"
|
|
onClick={() => {
|
|
setCatModal("tolak");
|
|
open();
|
|
}}
|
|
>
|
|
Tolak
|
|
</Button>
|
|
<Button
|
|
disabled={!permissions.includes("pelayanan.antrian.terima")}
|
|
variant="filled"
|
|
onClick={() => {
|
|
setCatModal("terima");
|
|
open();
|
|
}}
|
|
>
|
|
Terima
|
|
</Button>
|
|
</Group>
|
|
) : data?.status === "diterima" ? (
|
|
<Group justify="center" grow>
|
|
<Button
|
|
disabled={!permissions.includes("pelayanan.diterima.tolak")}
|
|
variant="light"
|
|
onClick={() => {
|
|
setCatModal("tolak");
|
|
open();
|
|
}}
|
|
>
|
|
Tolak
|
|
</Button>
|
|
<Button
|
|
disabled={
|
|
!permissions.includes("pelayanan.diterima.setujui")
|
|
}
|
|
variant="filled"
|
|
onClick={() => {
|
|
setCatModal("terima");
|
|
open();
|
|
}}
|
|
>
|
|
Setujui
|
|
</Button>
|
|
</Group>
|
|
) : data?.status === "selesai" ?
|
|
!data?.fileSurat ?
|
|
(
|
|
<Group justify="center" grow>
|
|
<Button
|
|
variant="light"
|
|
onClick={() => { setOpenedPreview(true) }}
|
|
>
|
|
Kirim Ulang Surat
|
|
</Button>
|
|
</Group>
|
|
)
|
|
:
|
|
(
|
|
<Group justify="center" grow>
|
|
<Button
|
|
variant="light"
|
|
onClick={() => { setViewImg({ file: data?.fileSurat, folder: "surat" }) }}
|
|
>
|
|
Surat
|
|
</Button>
|
|
</Group>
|
|
) : (
|
|
<></>
|
|
)}
|
|
</Grid.Col>
|
|
</Grid>
|
|
</Stack>
|
|
</Card>
|
|
</>
|
|
);
|
|
}
|
|
|
|
function DetailDataHistori({ data }: { data: any }) {
|
|
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">
|
|
Riwayat Pengajuan Surat
|
|
</Title>
|
|
</Flex>
|
|
<Divider my={0} />
|
|
<Spoiler
|
|
maxHeight={200}
|
|
showLabel="Show more"
|
|
hideLabel="Hide"
|
|
transitionDuration={1000}
|
|
>
|
|
<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>
|
|
{data?.map((item: any) => (
|
|
<Table.Tr key={item.id}>
|
|
<Table.Td style={{ whiteSpace: "nowrap" }}>
|
|
{item.createdAt.toLocaleString("id-ID", {
|
|
day: "2-digit",
|
|
month: "short",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
hour12: false,
|
|
})}
|
|
</Table.Td>
|
|
<Table.Td>{item.deskripsi}</Table.Td>
|
|
<Table.Td>{item.status}</Table.Td>
|
|
<Table.Td style={{ whiteSpace: "nowrap" }}>
|
|
{item.nameUser ? item.nameUser : "-"}
|
|
</Table.Td>
|
|
</Table.Tr>
|
|
))}
|
|
</Table.Tbody>
|
|
</Table>
|
|
</Spoiler>
|
|
</Stack>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
function DetailUserPengajuan({ data }: { data: any }) {
|
|
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">
|
|
<Flex direction={"column"}>
|
|
<Title order={4} c="gray.2">
|
|
Warga
|
|
</Title>
|
|
</Flex>
|
|
</Flex>
|
|
<Divider my={0} />
|
|
<Stack gap="md">
|
|
<Group justify="space-between">
|
|
<Group gap="xs">
|
|
<IconUser size={20} />
|
|
<Text size="md">Nama</Text>
|
|
</Group>
|
|
<Text size="md" c={"white"}>
|
|
{data?.name}
|
|
</Text>
|
|
</Group>
|
|
<Group justify="space-between">
|
|
<Group gap="xs">
|
|
<IconPhone size={20} />
|
|
<Text size="md">Telepon</Text>
|
|
</Group>
|
|
<Text size="md" c="white">
|
|
{data?.phone}
|
|
</Text>
|
|
</Group>
|
|
<Group justify="space-between">
|
|
<Group gap="xs">
|
|
<IconMessageReport size={20} />
|
|
<Text size="md">Jumlah Pengaduan</Text>
|
|
</Group>
|
|
<Text size="md" c="white">
|
|
{data?.pengaduan}
|
|
</Text>
|
|
</Group>
|
|
<Group justify="space-between">
|
|
<Group gap="xs">
|
|
<IconFileCertificate size={20} />
|
|
<Text size="md">Jumlah Pelayanan Surat</Text>
|
|
</Group>
|
|
<Text size="md" c="white">
|
|
{data?.pelayanan}
|
|
</Text>
|
|
</Group>
|
|
</Stack>
|
|
</Stack>
|
|
</Card>
|
|
);
|
|
}
|