upd: form surat
Deskripsi: - api detail categori list - form awal buat surat No Issues
This commit is contained in:
@@ -28,16 +28,19 @@ export default function Login() {
|
||||
window.location.href = clientRoutes["/scr/dashboard/warga/list-warga"];
|
||||
break;
|
||||
case "credential":
|
||||
window.location.href = clientRoutes["/scr/dashboard/credential/credential"];
|
||||
window.location.href =
|
||||
clientRoutes["/scr/dashboard/credential/credential"];
|
||||
break;
|
||||
case "setting":
|
||||
window.location.href = clientRoutes["/scr/dashboard/setting/detail-setting"];
|
||||
window.location.href =
|
||||
clientRoutes["/scr/dashboard/setting/detail-setting"];
|
||||
break;
|
||||
case "api_key":
|
||||
window.location.href = clientRoutes["/scr/dashboard/apikey/apikey"];
|
||||
break;
|
||||
case "pelayanan":
|
||||
window.location.href = clientRoutes["/scr/dashboard/pelayanan-surat/list-pelayanan"];
|
||||
window.location.href =
|
||||
clientRoutes["/scr/dashboard/pelayanan-surat/list-pelayanan"];
|
||||
break;
|
||||
default:
|
||||
window.location.href = clientRoutes["/scr/dashboard"];
|
||||
|
||||
331
src/pages/darmasaba/surat.tsx
Normal file
331
src/pages/darmasaba/surat.tsx
Normal file
@@ -0,0 +1,331 @@
|
||||
import apiFetch from "@/lib/apiFetch";
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Container,
|
||||
Divider,
|
||||
Grid,
|
||||
Group,
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Textarea,
|
||||
Tooltip
|
||||
} from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import {
|
||||
IconBuildingCommunity,
|
||||
IconInfoCircle,
|
||||
IconMapPin,
|
||||
IconUser
|
||||
} from "@tabler/icons-react";
|
||||
import React from "react";
|
||||
import useSWR from "swr";
|
||||
|
||||
// =========================
|
||||
// Reusable UI components
|
||||
// =========================
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
function FormSection({
|
||||
title,
|
||||
icon,
|
||||
children,
|
||||
description,
|
||||
}: {
|
||||
title: string;
|
||||
icon?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
description?: string;
|
||||
}) {
|
||||
return (
|
||||
<Card radius="md" shadow="sm" withBorder>
|
||||
<Group justify="apart" align="center" mb="xs">
|
||||
<Group align="center" gap="xs">
|
||||
{icon}
|
||||
<Text fw={700}>{title}</Text>
|
||||
</Group>
|
||||
{description && <Badge variant="light">{description}</Badge>}
|
||||
</Group>
|
||||
|
||||
<Divider mb="sm" />
|
||||
<Stack gap="sm">{children}</Stack>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Main form component
|
||||
// =========================
|
||||
|
||||
export default function FormSurat() {
|
||||
const { data, mutate, isLoading } = useSWR("category-pelayanan-list", () =>
|
||||
apiFetch.api.pelayanan.category.get(),
|
||||
);
|
||||
|
||||
const listCategory = data?.data || [];
|
||||
|
||||
|
||||
useShallowEffect(() => {
|
||||
mutate();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container size="md" w={"100%"}>
|
||||
<Box>
|
||||
<Stack gap="lg">
|
||||
<Group justify="apart" align="center">
|
||||
<Group align="center">
|
||||
<IconBuildingCommunity size={28} />
|
||||
<div>
|
||||
<Text fw={800} size="xl">
|
||||
Surat Keterangan Tidak Mampu (SKTM)
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
Blangko resmi untuk pengajuan Surat Keterangan Tidak Mampu —
|
||||
digunakan untuk keperluan pendidikan, kesehatan, atau
|
||||
administrasi.
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
<Group>
|
||||
<Badge radius="sm">Form Length: 3 Sections</Badge>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<form>
|
||||
<Stack gap="lg">
|
||||
{/* Header Section */}
|
||||
<FormSection
|
||||
title="Pemohon"
|
||||
icon={<IconUser size={16} />}
|
||||
description="Informasi identitas pemohon"
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label={
|
||||
<FieldLabel
|
||||
label="Nama Lengkap"
|
||||
hint="Nama lengkap pemohon"
|
||||
/>
|
||||
}
|
||||
placeholder="Budi Setiawan"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label={
|
||||
<FieldLabel
|
||||
label="Nomor Telephone"
|
||||
hint="Nomor telephone yang dapat dihubungi / terhubung dengan whatsapp"
|
||||
/>
|
||||
}
|
||||
placeholder="08123456789"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={12}>
|
||||
<Select
|
||||
label={<FieldLabel label="Jenis Surat" hint="Jenis surat yang ingin diajukan" />}
|
||||
placeholder="Pilih jenis surat"
|
||||
data={listCategory.map((item: any) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
}))}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</FormSection>
|
||||
|
||||
<FormSection
|
||||
title="Syarat Dokumen"
|
||||
description="Syarat dokumen yang diperlukan"
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label={
|
||||
<FieldLabel
|
||||
label="Nama Lengkap"
|
||||
hint="Sesuai KTP"
|
||||
/>
|
||||
}
|
||||
placeholder="Nama lengkap"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label={
|
||||
<FieldLabel
|
||||
label="NIK"
|
||||
hint="16 digit, tanpa spasi"
|
||||
/>
|
||||
}
|
||||
placeholder="3201xxxxxxxxxxxx"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label={
|
||||
<FieldLabel
|
||||
label="Tempat, Tanggal Lahir"
|
||||
hint="Contoh: Denpasar, 31-12-1990"
|
||||
/>
|
||||
}
|
||||
placeholder="Tempat, tanggal lahir"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<Select
|
||||
label={<FieldLabel label="Jenis Kelamin" />}
|
||||
placeholder="Pilih jenis kelamin"
|
||||
data={["Laki-laki", "Perempuan"]}
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<Select
|
||||
label={<FieldLabel label="Agama" />}
|
||||
placeholder="Pilih agama"
|
||||
data={[
|
||||
"Islam",
|
||||
"Kristen",
|
||||
"Katolik",
|
||||
"Hindu",
|
||||
"Buddha",
|
||||
"Konghucu",
|
||||
"Lainnya",
|
||||
]}
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<Select
|
||||
label={<FieldLabel label="Status Perkawinan" />}
|
||||
placeholder="Pilih status"
|
||||
data={[
|
||||
"Belum Kawin",
|
||||
"Kawin",
|
||||
"Cerai Hidup",
|
||||
"Cerai Mati",
|
||||
]}
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label={<FieldLabel label="Pekerjaan" />}
|
||||
placeholder="Pekerjaan"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={12}>
|
||||
<Textarea
|
||||
label={<FieldLabel label="Alamat Lengkap" />}
|
||||
placeholder="Alamat domisili"
|
||||
minRows={2}
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={2}>
|
||||
<TextInput
|
||||
label={<FieldLabel label="RT" />}
|
||||
placeholder="001"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={2}>
|
||||
<TextInput
|
||||
label={<FieldLabel label="RW" />}
|
||||
placeholder="002"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label={<FieldLabel label="Desa / Kelurahan" />}
|
||||
placeholder="Desa"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label={<FieldLabel label="Kecamatan" />}
|
||||
placeholder="Kecamatan"
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label={<FieldLabel label="Kabupaten / Kota" />}
|
||||
placeholder="Kabupaten / Kota"
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</FormSection>
|
||||
|
||||
{/* Keterangan Section */}
|
||||
<FormSection
|
||||
title="Keterangan"
|
||||
icon={<IconMapPin size={18} />}
|
||||
description="Isi pernyataan SKTM"
|
||||
>
|
||||
<Textarea
|
||||
label={
|
||||
<FieldLabel
|
||||
label="Isi Surat"
|
||||
hint="Jelaskan kondisi ekonomi secara singkat"
|
||||
/>
|
||||
}
|
||||
placeholder="Pernyataan resmi bahwa yang bersangkutan benar-benar tergolong keluarga tidak mampu..."
|
||||
minRows={4}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={
|
||||
<FieldLabel
|
||||
label="Keperluan"
|
||||
hint="Contoh: Beasiswa pendidikan / Perawatan kesehatan"
|
||||
/>
|
||||
}
|
||||
placeholder="Keperluan surat"
|
||||
/>
|
||||
</FormSection>
|
||||
|
||||
{/* Actions */}
|
||||
<Group justify="right" mt="md">
|
||||
<Button variant="default" onClick={() => { }}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button type="submit">Kirim / Simpan</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Progress,
|
||||
Stack,
|
||||
Text,
|
||||
Title
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
|
||||
export default function Dashboard() {
|
||||
|
||||
@@ -285,45 +285,47 @@ function NavigationDashboard() {
|
||||
|
||||
return (
|
||||
<Stack gap="xs" p="sm">
|
||||
{navItems.filter((item) => permissions.includes(item.key)).map((item) => (
|
||||
<NavLink
|
||||
key={item.path}
|
||||
active={isActive(item.path as keyof typeof clientRoute)}
|
||||
leftSection={item.icon}
|
||||
label={
|
||||
<Flex align="center" gap={6}>
|
||||
<Text fw={500}>{item.label}</Text>
|
||||
{isActive(item.path as keyof typeof clientRoute) && (
|
||||
<Badge
|
||||
variant="light"
|
||||
color="teal"
|
||||
radius="sm"
|
||||
size="xs"
|
||||
style={{ textTransform: "none" }}
|
||||
>
|
||||
Active
|
||||
</Badge>
|
||||
)}
|
||||
</Flex>
|
||||
}
|
||||
description={item.description}
|
||||
onClick={() =>
|
||||
navigate(clientRoutes[item.path as keyof typeof clientRoute])
|
||||
}
|
||||
style={{
|
||||
backgroundColor: isActive(item.path as keyof typeof clientRoute)
|
||||
? "rgba(0,255,200,0.1)"
|
||||
: "transparent",
|
||||
borderRadius: "8px",
|
||||
transition: "all 0.2s ease",
|
||||
}}
|
||||
styles={{
|
||||
label: { color: "white" },
|
||||
description: { color: "#aaa" },
|
||||
section: { color: "teal" },
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{navItems
|
||||
.filter((item) => permissions.includes(item.key))
|
||||
.map((item) => (
|
||||
<NavLink
|
||||
key={item.path}
|
||||
active={isActive(item.path as keyof typeof clientRoute)}
|
||||
leftSection={item.icon}
|
||||
label={
|
||||
<Flex align="center" gap={6}>
|
||||
<Text fw={500}>{item.label}</Text>
|
||||
{isActive(item.path as keyof typeof clientRoute) && (
|
||||
<Badge
|
||||
variant="light"
|
||||
color="teal"
|
||||
radius="sm"
|
||||
size="xs"
|
||||
style={{ textTransform: "none" }}
|
||||
>
|
||||
Active
|
||||
</Badge>
|
||||
)}
|
||||
</Flex>
|
||||
}
|
||||
description={item.description}
|
||||
onClick={() =>
|
||||
navigate(clientRoutes[item.path as keyof typeof clientRoute])
|
||||
}
|
||||
style={{
|
||||
backgroundColor: isActive(item.path as keyof typeof clientRoute)
|
||||
? "rgba(0,255,200,0.1)"
|
||||
: "transparent",
|
||||
borderRadius: "8px",
|
||||
transition: "all 0.2s ease",
|
||||
}}
|
||||
styles={{
|
||||
label: { color: "white" },
|
||||
description: { color: "#aaa" },
|
||||
section: { color: "teal" },
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Text,
|
||||
Textarea,
|
||||
ThemeIcon,
|
||||
Title
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
|
||||
import {
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
IconFileCheck,
|
||||
IconMessageReport,
|
||||
IconPhone,
|
||||
IconUser
|
||||
IconUser,
|
||||
} from "@tabler/icons-react";
|
||||
import type { User } from "generated/prisma";
|
||||
import type { JsonValue } from "generated/prisma/runtime/library";
|
||||
@@ -59,7 +59,14 @@ export default function DetailPengajuanPage() {
|
||||
<Grid>
|
||||
<Grid.Col span={8}>
|
||||
<Stack gap={"xl"}>
|
||||
<DetailDataPengajuan data={data?.data?.pengajuan} syaratDokumen={data?.data?.syaratDokumen} dataText={data?.data?.dataText} onAction={() => { mutate(); }} />
|
||||
<DetailDataPengajuan
|
||||
data={data?.data?.pengajuan}
|
||||
syaratDokumen={data?.data?.syaratDokumen}
|
||||
dataText={data?.data?.dataText}
|
||||
onAction={() => {
|
||||
mutate();
|
||||
}}
|
||||
/>
|
||||
<DetailDataHistori data={data?.data?.history} />
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
@@ -71,7 +78,17 @@ export default function DetailPengajuanPage() {
|
||||
);
|
||||
}
|
||||
|
||||
function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data: any, syaratDokumen: any, dataText: any, onAction: () => void }) {
|
||||
function DetailDataPengajuan({
|
||||
data,
|
||||
syaratDokumen,
|
||||
dataText,
|
||||
onAction,
|
||||
}: {
|
||||
data: any;
|
||||
syaratDokumen: any;
|
||||
dataText: any;
|
||||
onAction: () => void;
|
||||
}) {
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
const [catModal, setCatModal] = useState<"tolak" | "terima">("tolak");
|
||||
const [keterangan, setKeterangan] = useState("");
|
||||
@@ -88,7 +105,9 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
setHost(data?.user ?? null);
|
||||
|
||||
if (data?.permissions && Array.isArray(data.permissions)) {
|
||||
const onlySetting = data.permissions.filter((p: any) => p.startsWith("pelayanan"));
|
||||
const onlySetting = data.permissions.filter((p: any) =>
|
||||
p.startsWith("pelayanan"),
|
||||
);
|
||||
setPermissions(onlySetting);
|
||||
}
|
||||
}
|
||||
@@ -99,10 +118,15 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
try {
|
||||
const res = await apiFetch.api.pelayanan["update-status"].post({
|
||||
id: data?.id,
|
||||
status: cat == 'tolak' ? 'ditolak' : data.status == 'antrian' ? 'diterima' : 'selesai',
|
||||
status:
|
||||
cat == "tolak"
|
||||
? "ditolak"
|
||||
: data.status == "antrian"
|
||||
? "diterima"
|
||||
: "selesai",
|
||||
keterangan: keterangan,
|
||||
idUser: host?.id ?? "",
|
||||
noSurat: noSurat
|
||||
noSurat: noSurat,
|
||||
});
|
||||
|
||||
if (res?.status === 200) {
|
||||
@@ -120,7 +144,6 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
notification({
|
||||
@@ -129,7 +152,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useShallowEffect(() => {
|
||||
if (viewImg) {
|
||||
@@ -142,7 +165,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
<ModalFile
|
||||
open={openedPreviewFile && !_.isEmpty(viewImg)}
|
||||
onClose={() => {
|
||||
setOpenedPreviewFile(false)
|
||||
setOpenedPreviewFile(false);
|
||||
}}
|
||||
folder="syarat-dokumen"
|
||||
fileName={viewImg}
|
||||
@@ -159,14 +182,25 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
{catModal === "tolak" ? (
|
||||
<>
|
||||
<Text>
|
||||
Anda yakin ingin menolak pengajuan surat ini? Berikan alasan penolakan
|
||||
Anda yakin ingin menolak pengajuan surat ini? Berikan alasan
|
||||
penolakan
|
||||
</Text>
|
||||
<Textarea size="md" minRows={5} value={keterangan} onChange={(e) => setKeterangan(e.target.value)} />
|
||||
<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")}>
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
disabled={keterangan.length < 1}
|
||||
onClick={() => handleKonfirmasi("tolak")}
|
||||
>
|
||||
Tolak
|
||||
</Button>
|
||||
</Group>
|
||||
@@ -174,21 +208,31 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
) : (
|
||||
<>
|
||||
<Text>
|
||||
Anda yakin ingin {data?.status == 'antrian' ? 'menerima' : 'menyetujui'} pengajuan surat ini?
|
||||
{
|
||||
data.status == 'diterima' && 'Masukkan nomer surat yang akan dibuat'
|
||||
}
|
||||
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" />
|
||||
)
|
||||
}
|
||||
{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}>
|
||||
<Button
|
||||
variant="filled"
|
||||
color="green"
|
||||
onClick={() => handleKonfirmasi("terima")}
|
||||
disabled={data.status == "diterima" && noSurat.length < 1}
|
||||
>
|
||||
Ya
|
||||
</Button>
|
||||
</Group>
|
||||
@@ -196,11 +240,13 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
)}
|
||||
</Stack>
|
||||
</Modal>
|
||||
{
|
||||
data?.status == "selesai" &&
|
||||
(<ModalSurat open={openedPreview} onClose={() => setOpenedPreview(false)} surat={data?.idSurat} />)
|
||||
}
|
||||
|
||||
{data?.status == "selesai" && (
|
||||
<ModalSurat
|
||||
open={openedPreview}
|
||||
onClose={() => setOpenedPreview(false)}
|
||||
surat={data?.idSurat}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Card
|
||||
radius="md"
|
||||
@@ -263,7 +309,11 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
>
|
||||
{syaratDokumen?.map((v: any) => (
|
||||
<List.Item key={v.id}>
|
||||
<Anchor onClick={() => { setViewImg(v.value) }}>
|
||||
<Anchor
|
||||
onClick={() => {
|
||||
setViewImg(v.value);
|
||||
}}
|
||||
>
|
||||
{v.jenis}
|
||||
</Anchor>
|
||||
</List.Item>
|
||||
@@ -271,8 +321,6 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
</List>
|
||||
</Flex>
|
||||
|
||||
|
||||
|
||||
<Flex direction={"column"} justify="flex-start">
|
||||
<Group gap="xs">
|
||||
<IconAlignJustified size={20} />
|
||||
@@ -281,82 +329,85 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
|
||||
<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%" }}>{_.upperFirst(item.value)}</Table.Td>
|
||||
</Table.Tr>
|
||||
))
|
||||
}
|
||||
{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%" }}>
|
||||
{_.upperFirst(item.value)}
|
||||
</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" ?
|
||||
(
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="light"
|
||||
onClick={() => setOpenedPreview(!openedPreview)}
|
||||
>
|
||||
Surat
|
||||
</Button>
|
||||
</Group>
|
||||
)
|
||||
: <></>
|
||||
}
|
||||
{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" ? (
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="light"
|
||||
onClick={() => setOpenedPreview(!openedPreview)}
|
||||
>
|
||||
Surat
|
||||
</Button>
|
||||
</Group>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Stack>
|
||||
@@ -395,26 +446,25 @@ function DetailDataHistori({ data }: { data: any }) {
|
||||
</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>
|
||||
))
|
||||
}
|
||||
{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>
|
||||
</Stack>
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
IconClockHour3,
|
||||
IconFileSad,
|
||||
IconSearch,
|
||||
IconUser
|
||||
IconUser,
|
||||
} from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
@@ -123,7 +123,7 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
|
||||
take: "",
|
||||
page: page.toString(),
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
useShallowEffect(() => {
|
||||
@@ -131,12 +131,10 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
|
||||
mutate();
|
||||
}, [status, value]);
|
||||
|
||||
|
||||
useShallowEffect(() => {
|
||||
mutate();
|
||||
}, [page]);
|
||||
|
||||
|
||||
useShallowEffect(() => {
|
||||
const unsubscribe = subscribe(state, () => mutate());
|
||||
return () => unsubscribe();
|
||||
@@ -189,8 +187,16 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
|
||||
</Grid.Col>
|
||||
<Grid.Col span={3}>
|
||||
<Group justify="flex-end">
|
||||
<Text size="sm" c="gray.5">{`${pageSize * (page - 1) + 1} – ${Math.min(total, pageSize * page)} of ${total}`}</Text>
|
||||
<Pagination total={totalPage} value={page} onChange={setPage} withPages={false} />
|
||||
<Text
|
||||
size="sm"
|
||||
c="gray.5"
|
||||
>{`${pageSize * (page - 1) + 1} – ${Math.min(total, pageSize * page)} of ${total}`}</Text>
|
||||
<Pagination
|
||||
total={totalPage}
|
||||
value={page}
|
||||
onChange={setPage}
|
||||
withPages={false}
|
||||
/>
|
||||
</Group>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
@@ -204,7 +210,8 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
|
||||
</Stack>
|
||||
</Flex>
|
||||
) : (
|
||||
Array.isArray(list) && list?.map((v: any) => (
|
||||
Array.isArray(list) &&
|
||||
list?.map((v: any) => (
|
||||
<Card
|
||||
key={v.id}
|
||||
radius="lg"
|
||||
@@ -266,7 +273,13 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
|
||||
Tanggal Ajuan
|
||||
</Text>
|
||||
</Group>
|
||||
<Text size="md">{toDate(v.createdAt).toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" })}</Text>
|
||||
<Text size="md">
|
||||
{toDate(v.createdAt).toLocaleDateString("id-ID", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex direction={"column"} justify="flex-start">
|
||||
<Group gap="xs">
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
Table,
|
||||
Text,
|
||||
Textarea,
|
||||
Title
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
|
||||
import {
|
||||
@@ -58,7 +58,12 @@ export default function DetailPengaduanPage() {
|
||||
<Grid>
|
||||
<Grid.Col span={8}>
|
||||
<Stack gap={"xl"}>
|
||||
<DetailDataPengaduan data={data?.data?.pengaduan} onAction={() => { mutate(); }} />
|
||||
<DetailDataPengaduan
|
||||
data={data?.data?.pengaduan}
|
||||
onAction={() => {
|
||||
mutate();
|
||||
}}
|
||||
/>
|
||||
<DetailDataHistori data={data?.data?.history} />
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
@@ -70,7 +75,13 @@ export default function DetailPengaduanPage() {
|
||||
);
|
||||
}
|
||||
|
||||
function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: () => void }) {
|
||||
function DetailDataPengaduan({
|
||||
data,
|
||||
onAction,
|
||||
}: {
|
||||
data: any | null;
|
||||
onAction: () => void;
|
||||
}) {
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
const [catModal, setCatModal] = useState<"tolak" | "terima">("tolak");
|
||||
const [openedPreview, setOpenedPreview] = useState(false);
|
||||
@@ -84,7 +95,9 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
|
||||
setHost(data?.user ?? null);
|
||||
|
||||
if (data?.permissions && Array.isArray(data.permissions)) {
|
||||
const onlySetting = data.permissions.filter((p: any) => p.startsWith("pengaduan"));
|
||||
const onlySetting = data.permissions.filter((p: any) =>
|
||||
p.startsWith("pengaduan"),
|
||||
);
|
||||
setPermissions(onlySetting);
|
||||
}
|
||||
}
|
||||
@@ -95,9 +108,16 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
|
||||
try {
|
||||
const res = await apiFetch.api.pengaduan["update-status"].post({
|
||||
id: data?.id,
|
||||
status: cat == 'tolak' ? 'ditolak' : data.status == 'antrian' ? 'diterima' : data.status == 'diterima' ? 'dikerjakan' : 'selesai',
|
||||
status:
|
||||
cat == "tolak"
|
||||
? "ditolak"
|
||||
: data.status == "antrian"
|
||||
? "diterima"
|
||||
: data.status == "diterima"
|
||||
? "dikerjakan"
|
||||
: "selesai",
|
||||
keterangan: keterangan,
|
||||
idUser: host?.id ?? ""
|
||||
idUser: host?.id ?? "",
|
||||
});
|
||||
|
||||
if (res?.status === 200) {
|
||||
@@ -115,7 +135,6 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
notification({
|
||||
@@ -124,11 +143,10 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
{/* MODAL KONFIRMASI */}
|
||||
<Modal
|
||||
opened={opened}
|
||||
@@ -143,24 +161,46 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
|
||||
Anda yakin ingin menolak pengaduan ini? Berikan alasan penolakan
|
||||
</Text>
|
||||
|
||||
<Textarea size="md" minRows={5} value={keterangan} onChange={(e) => setKeterangan(e.target.value)} />
|
||||
<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")}>
|
||||
<Button
|
||||
variant="filled"
|
||||
color="red"
|
||||
disabled={keterangan.length < 1}
|
||||
onClick={() => handleKonfirmasi("tolak")}
|
||||
>
|
||||
Tolak
|
||||
</Button>
|
||||
</Group>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Text>Anda yakin ingin {data?.status == 'antrian' ? 'menerima' : data.status == 'diterima' ? 'mengerjakan' : 'menyelesaikan'} pengaduan ini?</Text>
|
||||
<Text>
|
||||
Anda yakin ingin{" "}
|
||||
{data?.status == "antrian"
|
||||
? "menerima"
|
||||
: data.status == "diterima"
|
||||
? "mengerjakan"
|
||||
: "menyelesaikan"}{" "}
|
||||
pengaduan ini?
|
||||
</Text>
|
||||
<Group justify="center" grow>
|
||||
<Button variant="light" onClick={close}>
|
||||
Tidak
|
||||
</Button>
|
||||
<Button variant="filled" color="green" onClick={() => handleKonfirmasi("terima")}>
|
||||
<Button
|
||||
variant="filled"
|
||||
color="green"
|
||||
onClick={() => handleKonfirmasi("terima")}
|
||||
>
|
||||
Ya
|
||||
</Button>
|
||||
</Group>
|
||||
@@ -169,7 +209,6 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
|
||||
</Stack>
|
||||
</Modal>
|
||||
|
||||
|
||||
{/* MODAL GAMBAR */}
|
||||
<ModalFile
|
||||
open={openedPreview && !_.isEmpty(data?.image)}
|
||||
@@ -259,18 +298,20 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
|
||||
<IconPhotoScan size={20} />
|
||||
<Text size="md">Gambar</Text>
|
||||
</Group>
|
||||
{
|
||||
data?.image != null && data?.image != ""
|
||||
?
|
||||
<Anchor href="#" onClick={() => { setOpenedPreview(true) }}>
|
||||
Lihat Gambar
|
||||
</Anchor>
|
||||
:
|
||||
<Text size="md" c="white">
|
||||
-
|
||||
</Text>
|
||||
}
|
||||
|
||||
{data?.image != null && data?.image != "" ? (
|
||||
<Anchor
|
||||
href="#"
|
||||
onClick={() => {
|
||||
setOpenedPreview(true);
|
||||
}}
|
||||
>
|
||||
Lihat Gambar
|
||||
</Anchor>
|
||||
) : (
|
||||
<Text size="md" c="white">
|
||||
-
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
@@ -285,74 +326,76 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
|
||||
{_.upperFirst(data?.detail)}
|
||||
</Text>
|
||||
</Flex>
|
||||
{
|
||||
data?.keterangan && (
|
||||
<Flex direction={"column"} justify="flex-start">
|
||||
<Group gap="xs">
|
||||
<IconInfoTriangle size={20} />
|
||||
<Text size="md">Keterangan</Text>
|
||||
</Group>
|
||||
<Text size="md" c={"white"}>
|
||||
{_.upperFirst(data?.keterangan)}
|
||||
</Text>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
{data?.keterangan && (
|
||||
<Flex direction={"column"} justify="flex-start">
|
||||
<Group gap="xs">
|
||||
<IconInfoTriangle size={20} />
|
||||
<Text size="md">Keterangan</Text>
|
||||
</Group>
|
||||
<Text size="md" c={"white"}>
|
||||
{_.upperFirst(data?.keterangan)}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={12}>
|
||||
{
|
||||
data?.status === "antrian" ? (
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="light"
|
||||
disabled={!permissions.includes("pengaduan.antrian.tolak")}
|
||||
onClick={() => {
|
||||
setCatModal("tolak");
|
||||
open();
|
||||
}}
|
||||
>
|
||||
Tolak
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
disabled={!permissions.includes("pengaduan.antrian.terima")}
|
||||
onClick={() => {
|
||||
setCatModal("terima");
|
||||
open();
|
||||
}}
|
||||
>
|
||||
Terima
|
||||
</Button>
|
||||
</Group>
|
||||
) : data?.status === "diterima" ? (
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="filled"
|
||||
disabled={!permissions.includes("pengaduan.diterima.dikerjakan")}
|
||||
onClick={() => {
|
||||
setCatModal("terima");
|
||||
open();
|
||||
}}
|
||||
>
|
||||
Kerjakan
|
||||
</Button>
|
||||
</Group>
|
||||
) : data?.status === "dikerjakan" ? (
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="filled"
|
||||
disabled={!permissions.includes("pengaduan.dikerjakan.selesai")}
|
||||
onClick={() => {
|
||||
setCatModal("terima");
|
||||
open();
|
||||
}}
|
||||
>
|
||||
Selesai
|
||||
</Button>
|
||||
</Group>
|
||||
) : <></>
|
||||
}
|
||||
{data?.status === "antrian" ? (
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="light"
|
||||
disabled={!permissions.includes("pengaduan.antrian.tolak")}
|
||||
onClick={() => {
|
||||
setCatModal("tolak");
|
||||
open();
|
||||
}}
|
||||
>
|
||||
Tolak
|
||||
</Button>
|
||||
<Button
|
||||
variant="filled"
|
||||
disabled={!permissions.includes("pengaduan.antrian.terima")}
|
||||
onClick={() => {
|
||||
setCatModal("terima");
|
||||
open();
|
||||
}}
|
||||
>
|
||||
Terima
|
||||
</Button>
|
||||
</Group>
|
||||
) : data?.status === "diterima" ? (
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="filled"
|
||||
disabled={
|
||||
!permissions.includes("pengaduan.diterima.dikerjakan")
|
||||
}
|
||||
onClick={() => {
|
||||
setCatModal("terima");
|
||||
open();
|
||||
}}
|
||||
>
|
||||
Kerjakan
|
||||
</Button>
|
||||
</Group>
|
||||
) : data?.status === "dikerjakan" ? (
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="filled"
|
||||
disabled={
|
||||
!permissions.includes("pengaduan.dikerjakan.selesai")
|
||||
}
|
||||
onClick={() => {
|
||||
setCatModal("terima");
|
||||
open();
|
||||
}}
|
||||
>
|
||||
Selesai
|
||||
</Button>
|
||||
</Group>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Stack>
|
||||
@@ -391,25 +434,25 @@ function DetailDataHistori({ data }: { data: any }) {
|
||||
</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>
|
||||
))
|
||||
}
|
||||
{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>
|
||||
</Stack>
|
||||
|
||||
@@ -134,7 +134,7 @@ function ListPengaduan({ status }: { status: StatusKey }) {
|
||||
take: "",
|
||||
page: page.toString(),
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
useShallowEffect(() => {
|
||||
@@ -175,7 +175,6 @@ function ListPengaduan({ status }: { status: StatusKey }) {
|
||||
const pageNow = data?.data?.page || 1;
|
||||
const toDate = (d: any) => new Date(d);
|
||||
|
||||
|
||||
return (
|
||||
<Stack gap="xl">
|
||||
<Grid>
|
||||
@@ -197,8 +196,16 @@ function ListPengaduan({ status }: { status: StatusKey }) {
|
||||
</Grid.Col>
|
||||
<Grid.Col span={3}>
|
||||
<Group justify="flex-end">
|
||||
<Text size="sm" c="gray.5">{`${pageSize * (page - 1) + 1} – ${Math.min(total, pageSize * page)} of ${total}`}</Text>
|
||||
<Pagination total={totalPage} value={page} onChange={setPage} withPages={false} />
|
||||
<Text
|
||||
size="sm"
|
||||
c="gray.5"
|
||||
>{`${pageSize * (page - 1) + 1} – ${Math.min(total, pageSize * page)} of ${total}`}</Text>
|
||||
<Pagination
|
||||
total={totalPage}
|
||||
value={page}
|
||||
onChange={setPage}
|
||||
withPages={false}
|
||||
/>
|
||||
</Group>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
@@ -212,7 +219,8 @@ function ListPengaduan({ status }: { status: StatusKey }) {
|
||||
</Stack>
|
||||
</Flex>
|
||||
) : (
|
||||
Array.isArray(list) && list?.map((v: any) => (
|
||||
Array.isArray(list) &&
|
||||
list?.map((v: any) => (
|
||||
<Card
|
||||
key={v.id}
|
||||
radius="lg"
|
||||
@@ -272,7 +280,13 @@ function ListPengaduan({ status }: { status: StatusKey }) {
|
||||
Tanggal Aduan
|
||||
</Text>
|
||||
</Group>
|
||||
<Text size="md">{toDate(v.createdAt).toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" })}</Text>
|
||||
<Text size="md">
|
||||
{toDate(v.createdAt).toLocaleDateString("id-ID", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex direction={"column"} justify="flex-start">
|
||||
<Group gap="xs">
|
||||
|
||||
@@ -5,19 +5,14 @@ import ProfileUser from "@/components/ProfileUser";
|
||||
import UserRoleSetting from "@/components/UserRoleSetting";
|
||||
import UserSetting from "@/components/UserSetting";
|
||||
import apiFetch from "@/lib/apiFetch";
|
||||
import {
|
||||
Card,
|
||||
Container,
|
||||
Grid,
|
||||
NavLink
|
||||
} from "@mantine/core";
|
||||
import { Card, Container, Grid, NavLink } from "@mantine/core";
|
||||
import {
|
||||
IconBuildingBank,
|
||||
IconCategory2,
|
||||
IconMailSpark,
|
||||
IconUserCog,
|
||||
IconUserScreen,
|
||||
IconUsersGroup
|
||||
IconUsersGroup,
|
||||
} from "@tabler/icons-react";
|
||||
import type { JsonValue } from "generated/prisma/runtime/library";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -33,7 +28,9 @@ export default function DetailSettingPage() {
|
||||
async function fetchPermissions() {
|
||||
const { data } = await apiFetch.api.user.find.get();
|
||||
if (Array.isArray(data?.permissions)) {
|
||||
const onlySetting = data.permissions.filter((p: any) => p.startsWith("setting"));
|
||||
const onlySetting = data.permissions.filter((p: any) =>
|
||||
p.startsWith("setting"),
|
||||
);
|
||||
setPermissions(onlySetting);
|
||||
} else {
|
||||
setPermissions([]);
|
||||
@@ -42,7 +39,6 @@ export default function DetailSettingPage() {
|
||||
fetchPermissions();
|
||||
}, []);
|
||||
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
key: "setting.profile",
|
||||
@@ -85,8 +81,7 @@ export default function DetailSettingPage() {
|
||||
icon: <IconBuildingBank size={20} />,
|
||||
label: "Desa",
|
||||
description: "Manage desa information",
|
||||
}
|
||||
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -104,17 +99,19 @@ export default function DetailSettingPage() {
|
||||
boxShadow: "0 0 20px rgba(0,255,200,0.08)",
|
||||
}}
|
||||
>
|
||||
{
|
||||
navItems.filter((item) => permissions.includes(item.key)).map((item) => (
|
||||
{navItems
|
||||
.filter((item) => permissions.includes(item.key))
|
||||
.map((item) => (
|
||||
<NavLink
|
||||
key={item.key}
|
||||
href={'?type=' + item.path}
|
||||
href={"?type=" + item.path}
|
||||
label={item.label}
|
||||
leftSection={item.icon}
|
||||
active={type === item.path || (!type && item.path === 'profile')}
|
||||
active={
|
||||
type === item.path || (!type && item.path === "profile")
|
||||
}
|
||||
/>
|
||||
))
|
||||
}
|
||||
))}
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
@@ -130,17 +127,47 @@ export default function DetailSettingPage() {
|
||||
}}
|
||||
>
|
||||
{type === "cat-pengaduan" ? (
|
||||
<KategoriPengaduan permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.kategori_pengaduan"))} />
|
||||
<KategoriPengaduan
|
||||
permissions={permissions.filter(
|
||||
(p) =>
|
||||
typeof p === "string" &&
|
||||
p.startsWith("setting.kategori_pengaduan"),
|
||||
)}
|
||||
/>
|
||||
) : type === "cat-pelayanan" ? (
|
||||
<KategoriPelayananSurat permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.kategori_pelayanan"))} />
|
||||
<KategoriPelayananSurat
|
||||
permissions={permissions.filter(
|
||||
(p) =>
|
||||
typeof p === "string" &&
|
||||
p.startsWith("setting.kategori_pelayanan"),
|
||||
)}
|
||||
/>
|
||||
) : type === "desa" ? (
|
||||
<DesaSetting permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.desa"))} />
|
||||
<DesaSetting
|
||||
permissions={permissions.filter(
|
||||
(p) => typeof p === "string" && p.startsWith("setting.desa"),
|
||||
)}
|
||||
/>
|
||||
) : type === "user" ? (
|
||||
<UserSetting permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.user."))} />
|
||||
<UserSetting
|
||||
permissions={permissions.filter(
|
||||
(p) => typeof p === "string" && p.startsWith("setting.user."),
|
||||
)}
|
||||
/>
|
||||
) : type === "role" ? (
|
||||
<UserRoleSetting permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.user_role"))} />
|
||||
<UserRoleSetting
|
||||
permissions={permissions.filter(
|
||||
(p) =>
|
||||
typeof p === "string" && p.startsWith("setting.user_role"),
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<ProfileUser permissions={permissions.filter((p) => typeof p === 'string' && p.startsWith("setting.profile"))} />
|
||||
<ProfileUser
|
||||
permissions={permissions.filter(
|
||||
(p) =>
|
||||
typeof p === "string" && p.startsWith("setting.profile"),
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
|
||||
@@ -37,10 +37,13 @@ export default function DetailWargaPage() {
|
||||
mutate();
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<LoadingOverlay visible={isLoading} zIndex={1000} overlayProps={{ radius: "sm", blur: 2 }} />
|
||||
<LoadingOverlay
|
||||
visible={isLoading}
|
||||
zIndex={1000}
|
||||
overlayProps={{ radius: "sm", blur: 2 }}
|
||||
/>
|
||||
<Container size="xl" py="xl" w={"100%"}>
|
||||
<Grid>
|
||||
<Grid.Col span={4}>
|
||||
@@ -48,18 +51,29 @@ export default function DetailWargaPage() {
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Stack gap={"xl"}>
|
||||
<DetailDataHistori data={data?.data?.pengaduan} kategori="pengaduan" />
|
||||
<DetailDataHistori data={data?.data?.pelayanan} kategori="pelayanan" />
|
||||
<DetailDataHistori
|
||||
data={data?.data?.pengaduan}
|
||||
kategori="pengaduan"
|
||||
/>
|
||||
<DetailDataHistori
|
||||
data={data?.data?.pelayanan}
|
||||
kategori="pelayanan"
|
||||
/>
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Container>
|
||||
</>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
function DetailDataHistori({ data, kategori }: { data: any, kategori: 'pengaduan' | 'pelayanan' }) {
|
||||
function DetailDataHistori({
|
||||
data,
|
||||
kategori,
|
||||
}: {
|
||||
data: any;
|
||||
kategori: "pengaduan" | "pelayanan";
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
@@ -85,43 +99,47 @@ function DetailDataHistori({ data, kategori }: { data: any, kategori: 'pengaduan
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>No {_.upperFirst(kategori)}</Table.Th>
|
||||
<Table.Th>{kategori == "pengaduan" ? "Judul" : "Kategori"}</Table.Th>
|
||||
<Table.Th>
|
||||
{kategori == "pengaduan" ? "Judul" : "Kategori"}
|
||||
</Table.Th>
|
||||
<Table.Th>Status</Table.Th>
|
||||
<Table.Th></Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{
|
||||
data?.length > 0 ? (
|
||||
data?.map((item: any, index: number) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>{item.noPengaduan}</Table.Td>
|
||||
<Table.Td>{kategori == "pengaduan" ? item.title : item.category}</Table.Td>
|
||||
<Table.Td>{item.status}</Table.Td>
|
||||
<Table.Td>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
kategori == "pengaduan" ?
|
||||
navigate(
|
||||
{data?.length > 0 ? (
|
||||
data?.map((item: any, index: number) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>{item.noPengaduan}</Table.Td>
|
||||
<Table.Td>
|
||||
{kategori == "pengaduan" ? item.title : item.category}
|
||||
</Table.Td>
|
||||
<Table.Td>{item.status}</Table.Td>
|
||||
<Table.Td>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
kategori == "pengaduan"
|
||||
? navigate(
|
||||
`/scr/dashboard/pengaduan/detail?id=${item.id}`,
|
||||
) :
|
||||
navigate(
|
||||
`/scr/dashboard/pelayanan-surat/detail-pelayanan?id=${item.id}`,
|
||||
)
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))
|
||||
) : (
|
||||
<Table.Tr>
|
||||
<Table.Td colSpan={4} align="center">Tidak ada data</Table.Td>
|
||||
: navigate(
|
||||
`/scr/dashboard/pelayanan-surat/detail-pelayanan?id=${item.id}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
)
|
||||
}
|
||||
))
|
||||
) : (
|
||||
<Table.Tr>
|
||||
<Table.Td colSpan={4} align="center">
|
||||
Tidak ada data
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
)}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Stack>
|
||||
|
||||
@@ -39,7 +39,6 @@ export default function ListWargaPage() {
|
||||
const pageSize = data?.data?.pageSize || 10;
|
||||
const pageNow = data?.data?.page || 1;
|
||||
|
||||
|
||||
useShallowEffect(() => {
|
||||
setPages(1);
|
||||
mutate();
|
||||
@@ -49,7 +48,6 @@ export default function ListWargaPage() {
|
||||
mutate();
|
||||
}, [pages]);
|
||||
|
||||
|
||||
return (
|
||||
<Container size="xl" py="xl" w={"100%"}>
|
||||
<Card
|
||||
@@ -82,7 +80,12 @@ export default function ListWargaPage() {
|
||||
/>
|
||||
<Group>
|
||||
<Text size="sm">{`${pageSize * (pages - 1) + 1} – ${Math.min(total, pageSize * pages)} of ${total}`}</Text>
|
||||
<Pagination total={totalPage} value={pages} onChange={setPages} withPages={false} />
|
||||
<Pagination
|
||||
total={totalPage}
|
||||
value={pages}
|
||||
onChange={setPages}
|
||||
withPages={false}
|
||||
/>
|
||||
</Group>
|
||||
</Flex>
|
||||
<Divider my={0} />
|
||||
@@ -95,32 +98,33 @@ export default function ListWargaPage() {
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{
|
||||
Array.isArray(list) && list?.length === 0 ? (
|
||||
<Table.Tr>
|
||||
<Table.Td colSpan={3} align="center">Tidak ada data</Table.Td>
|
||||
{Array.isArray(list) && list?.length === 0 ? (
|
||||
<Table.Tr>
|
||||
<Table.Td colSpan={3} align="center">
|
||||
Tidak ada data
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
) : (
|
||||
Array.isArray(list) &&
|
||||
list?.map((item, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<Table.Td>{item.name}</Table.Td>
|
||||
<Table.Td w={250}>{item.phone}</Table.Td>
|
||||
<Table.Td w={150}>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
navigate(
|
||||
`/scr/dashboard/warga/detail-warga?id=${item.id}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
) : (
|
||||
Array.isArray(list) && list?.map((item, i) => (
|
||||
<Table.Tr key={i}>
|
||||
<Table.Td>{item.name}</Table.Td>
|
||||
<Table.Td w={250}>{item.phone}</Table.Td>
|
||||
<Table.Td w={150}>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
navigate(
|
||||
`/scr/dashboard/warga/detail-warga?id=${item.id}`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))
|
||||
)
|
||||
}
|
||||
))
|
||||
)}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user