upd: form surat

Deskripsi:
- api detail categori list
- form awal buat surat

No Issues
This commit is contained in:
2025-12-16 17:38:13 +08:00
parent a13e51a724
commit 7c6e4ac9eb
38 changed files with 4236 additions and 2941 deletions

View File

@@ -14,6 +14,7 @@ import FormSuratKeteranganBelumKawin from "./pages/darmasaba/form_surat_keterang
import FormKeteranganKelahiran from "./pages/darmasaba/form_keterangan_kelahiran"; import FormKeteranganKelahiran from "./pages/darmasaba/form_keterangan_kelahiran";
import FormSuratKeteranganTempatUsaha from "./pages/darmasaba/form_surat_keterangan_tempat_usaha"; import FormSuratKeteranganTempatUsaha from "./pages/darmasaba/form_surat_keterangan_tempat_usaha";
import FormSuratKeteranganKelakuanBaik from "./pages/darmasaba/form_surat_keterangan_kelakuan_baik"; import FormSuratKeteranganKelakuanBaik from "./pages/darmasaba/form_surat_keterangan_kelakuan_baik";
import Surat from "./pages/darmasaba/surat";
import Home from "./pages/Home"; import Home from "./pages/Home";
import CredentialPage from "./pages/scr/dashboard/credential/credential_page"; import CredentialPage from "./pages/scr/dashboard/credential/credential_page";
import DashboardHome from "./pages/scr/dashboard/dashboard_home"; import DashboardHome from "./pages/scr/dashboard/dashboard_home";
@@ -84,6 +85,7 @@ export default function AppRoutes() {
path="/darmasaba/surat-keterangan-kelakuan-baik" path="/darmasaba/surat-keterangan-kelakuan-baik"
element={<FormSuratKeteranganKelakuanBaik />} element={<FormSuratKeteranganKelakuanBaik />}
/> />
<Route path="/darmasaba/surat" element={<Surat />} />
</Route> </Route>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />

View File

@@ -14,6 +14,7 @@ const clientRoutes = {
"/darmasaba/keterangan-kelahiran": "/darmasaba/keterangan-kelahiran", "/darmasaba/keterangan-kelahiran": "/darmasaba/keterangan-kelahiran",
"/darmasaba/surat-keterangan-tempat-usaha": "/darmasaba/surat-keterangan-tempat-usaha", "/darmasaba/surat-keterangan-tempat-usaha": "/darmasaba/surat-keterangan-tempat-usaha",
"/darmasaba/surat-keterangan-kelakuan-baik": "/darmasaba/surat-keterangan-kelakuan-baik", "/darmasaba/surat-keterangan-kelakuan-baik": "/darmasaba/surat-keterangan-kelakuan-baik",
"/darmasaba/surat": "/darmasaba/surat",
"/": "/", "/": "/",
"/scr": "/scr", "/scr": "/scr",
"/scr/dashboard": "/scr/dashboard", "/scr/dashboard": "/scr/dashboard",

View File

@@ -1,12 +1,16 @@
import apiFetch from "@/lib/apiFetch"; import apiFetch from "@/lib/apiFetch";
import { Card, Flex, Grid, Group, Stack, Text } from "@mantine/core"; import { Card, Flex, Grid, Group, Stack, Text } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks"; import { useShallowEffect } from "@mantine/hooks";
import { IconFileCertificate, IconMessageReport, IconUsers } from "@tabler/icons-react"; import {
IconFileCertificate,
IconMessageReport,
IconUsers,
} from "@tabler/icons-react";
import useSWR from "swr"; import useSWR from "swr";
export default function DashboardCountData() { export default function DashboardCountData() {
const { data, mutate, isLoading } = useSWR("/", () => const { data, mutate, isLoading } = useSWR("/", () =>
apiFetch.api.dashboard.count.get() apiFetch.api.dashboard.count.get(),
); );
useShallowEffect(() => { useShallowEffect(() => {
@@ -45,7 +49,6 @@ export default function DashboardCountData() {
); );
} }
function MetricCard({ function MetricCard({
icon, icon,
label, label,

View File

@@ -8,12 +8,9 @@ import useSWR from "swr";
export default function DashboardGrafik() { export default function DashboardGrafik() {
const [options, setOptions] = useState<EChartsOption>({}); const [options, setOptions] = useState<EChartsOption>({});
const { data, mutate, isLoading } = useSWR( const { data, mutate, isLoading } = useSWR("grafik-dashboard", async () => {
"grafik-dashboard", return apiFetch.api.dashboard.grafik.get().then((res) => res.data);
async () => { });
return apiFetch.api.dashboard.grafik.get().then(res => res.data);
}
);
const loadData = () => { const loadData = () => {
if (!data) return; if (!data) return;
@@ -21,27 +18,24 @@ export default function DashboardGrafik() {
darkMode: true, darkMode: true,
animation: true, animation: true,
legend: { legend: {
textStyle: { color: "#fff" } textStyle: { color: "#fff" },
}, },
tooltip: {}, tooltip: {},
dataset: { dataset: {
dimensions: data.dimensions, dimensions: data.dimensions,
source: data.source source: data.source,
}, },
xAxis: { xAxis: {
type: "category", type: "category",
axisLabel: { color: "#fff" } axisLabel: { color: "#fff" },
}, },
yAxis: { yAxis: {
type: "value", type: "value",
minInterval: 1, minInterval: 1,
axisLabel: { color: "#fff" } axisLabel: { color: "#fff" },
}, },
color: ["#1abc9c", "#10816aff"], color: ["#1abc9c", "#10816aff"],
series: [ series: [{ type: "bar" }, { type: "bar" }],
{ type: "bar" },
{ type: "bar" }
]
}; };
setOptions(option); setOptions(option);
@@ -79,5 +73,5 @@ export default function DashboardGrafik() {
</Stack> </Stack>
</Stack> </Stack>
</Card> </Card>
) );
} }

View File

@@ -1,5 +1,15 @@
import apiFetch from "@/lib/apiFetch"; import apiFetch from "@/lib/apiFetch";
import { Badge, Button, Card, Flex, Group, Stack, Text, Title, Tooltip } from "@mantine/core"; import {
Badge,
Button,
Card,
Flex,
Group,
Stack,
Text,
Title,
Tooltip,
} from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks"; import { useShallowEffect } from "@mantine/hooks";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import useSWR from "swr"; import useSWR from "swr";
@@ -8,14 +18,13 @@ export default function DashboardLastData() {
const navigate = useNavigate(); const navigate = useNavigate();
const { data, mutate, isLoading } = useSWR("last-update", async () => { const { data, mutate, isLoading } = useSWR("last-update", async () => {
const res = await apiFetch.api.dashboard["last-update"].get(); const res = await apiFetch.api.dashboard["last-update"].get();
return res.data return res.data;
}); });
useShallowEffect(() => { useShallowEffect(() => {
mutate(); mutate();
}, []); }, []);
return ( return (
<Flex justify="flex-start" gap="md"> <Flex justify="flex-start" gap="md">
<Card <Card
@@ -31,15 +40,29 @@ export default function DashboardLastData() {
}} }}
> >
<Stack gap="sm"> <Stack gap="sm">
<Flex align="center" pb={"sm"} justify="space-between" style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}> <Flex
align="center"
pb={"sm"}
justify="space-between"
style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}
>
<Title order={4} c="gray.0"> <Title order={4} c="gray.0">
Last update pengaduan Last update pengaduan
</Title> </Title>
<Button variant="subtle" size="xs" radius="md" onClick={() => navigate(`/scr/dashboard/pengaduan/list`)}>View All</Button> <Button
variant="subtle"
size="xs"
radius="md"
onClick={() => navigate(`/scr/dashboard/pengaduan/list`)}
>
View All
</Button>
</Flex> </Flex>
<Stack gap="sm" mt="md" align="stretch" justify="center"> <Stack gap="sm" mt="md" align="stretch" justify="center">
{ {data &&
data && Array.isArray(data.pengaduan) && data.pengaduan.length > 0 ? data.pengaduan.map((item: any, index: number) => ( Array.isArray(data.pengaduan) &&
data.pengaduan.length > 0 ? (
data.pengaduan.map((item: any, index: number) => (
<PengaduanSection <PengaduanSection
key={index} key={index}
id={item.id} id={item.id}
@@ -49,8 +72,12 @@ export default function DashboardLastData() {
updated={item.updatedAt} updated={item.updatedAt}
kategori="pengaduan" kategori="pengaduan"
/> />
)) : <Text c="dimmed" ta={"center"} >Tidak ada data</Text> ))
} ) : (
<Text c="dimmed" ta={"center"}>
Tidak ada data
</Text>
)}
</Stack> </Stack>
</Stack> </Stack>
</Card> </Card>
@@ -68,15 +95,31 @@ export default function DashboardLastData() {
}} }}
> >
<Stack gap="sm"> <Stack gap="sm">
<Flex align="center" pb={"sm"} justify="space-between" style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}> <Flex
align="center"
pb={"sm"}
justify="space-between"
style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}
>
<Title order={4} c="gray.0"> <Title order={4} c="gray.0">
Last update pelayanan surat Last update pelayanan surat
</Title> </Title>
<Button variant="subtle" size="xs" radius="md" onClick={() => navigate(`/scr/dashboard/pelayanan-surat/list-pelayanan`)}>View All</Button> <Button
variant="subtle"
size="xs"
radius="md"
onClick={() =>
navigate(`/scr/dashboard/pelayanan-surat/list-pelayanan`)
}
>
View All
</Button>
</Flex> </Flex>
<Stack gap="sm" mt="md" align="stretch" justify="center"> <Stack gap="sm" mt="md" align="stretch" justify="center">
{ {data &&
data && Array.isArray(data.pelayanan) && data.pelayanan.length > 0 ? data.pelayanan.map((item: any, index: number) => ( Array.isArray(data.pelayanan) &&
data.pelayanan.length > 0 ? (
data.pelayanan.map((item: any, index: number) => (
<PengaduanSection <PengaduanSection
key={index} key={index}
id={item.id} id={item.id}
@@ -86,8 +129,12 @@ export default function DashboardLastData() {
updated={item.updatedAt} updated={item.updatedAt}
kategori="pelayanan" kategori="pelayanan"
/> />
)) : <Text c="dimmed" ta={"center"} >Tidak ada data</Text> ))
} ) : (
<Text c="dimmed" ta={"center"}>
Tidak ada data
</Text>
)}
</Stack> </Stack>
</Stack> </Stack>
</Card> </Card>
@@ -95,15 +142,41 @@ export default function DashboardLastData() {
); );
} }
function PengaduanSection({ id, nomer, judul, status, updated, kategori }: { id: string, nomer: string, judul: string, status: string, updated: string, kategori: 'pengaduan' | 'pelayanan' }) { function PengaduanSection({
id,
nomer,
judul,
status,
updated,
kategori,
}: {
id: string;
nomer: string;
judul: string;
status: string;
updated: string;
kategori: "pengaduan" | "pelayanan";
}) {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<Stack <Stack
gap="xs" gap="xs"
onClick={() => navigate(kategori == "pelayanan" ? `/scr/dashboard/pelayanan-surat/detail-pelayanan?id=${id}` : `/scr/dashboard/pengaduan/detail?id=${id}`)} onClick={() =>
navigate(
kategori == "pelayanan"
? `/scr/dashboard/pelayanan-surat/detail-pelayanan?id=${id}`
: `/scr/dashboard/pengaduan/detail?id=${id}`,
)
}
>
<Flex
align="center"
pb={"sm"}
justify="space-between"
gap="md"
style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}
> >
<Flex align="center" pb={"sm"} justify="space-between" gap="md" style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}>
<Flex direction={"column"}> <Flex direction={"column"}>
<Text size="md" c="gray.2" lineClamp={1}> <Text size="md" c="gray.2" lineClamp={1}>
{judul} {judul}
@@ -115,7 +188,10 @@ function PengaduanSection({ id, nomer, judul, status, updated, kategori }: { id:
</Group> </Group>
</Flex> </Flex>
<Tooltip label={status}> <Tooltip label={status}>
<Badge size="xs" circle color={ <Badge
size="xs"
circle
color={
status === "diterima" status === "diterima"
? "green" ? "green"
: status === "ditolak" : status === "ditolak"
@@ -125,9 +201,10 @@ function PengaduanSection({ id, nomer, judul, status, updated, kategori }: { id:
: status === "dikerjakan" : status === "dikerjakan"
? "gray" ? "gray"
: "yellow" : "yellow"
} /> }
/>
</Tooltip> </Tooltip>
</Flex> </Flex>
</Stack> </Stack>
) );
} }

View File

@@ -12,7 +12,7 @@ import {
Stack, Stack,
Table, Table,
Title, Title,
Tooltip Tooltip,
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit } from "@tabler/icons-react"; import { IconEdit } from "@tabler/icons-react";
@@ -23,11 +23,15 @@ import useSWR from "swr";
import ModalFile from "./ModalFile"; import ModalFile from "./ModalFile";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function DesaSetting({ permissions }: { permissions: JsonValue[] }) { export default function DesaSetting({
permissions,
}: {
permissions: JsonValue[];
}) {
const [btnDisable, setBtnDisable] = useState(false); const [btnDisable, setBtnDisable] = useState(false);
const [btnLoading, setBtnLoading] = useState(false); const [btnLoading, setBtnLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
const [img, setImg] = useState<any>() const [img, setImg] = useState<any>();
const [openedPreview, setOpenedPreview] = useState(false); const [openedPreview, setOpenedPreview] = useState(false);
const [viewImg, setViewImg] = useState(""); const [viewImg, setViewImg] = useState("");
const { data, mutate, isLoading } = useSWR("/", () => const { data, mutate, isLoading } = useSWR("/", () =>
@@ -51,13 +55,19 @@ export default function DesaSetting({ permissions }: { permissions: JsonValue[]
let finalData = { ...dataEdit }; // ← buffer data terbaru let finalData = { ...dataEdit }; // ← buffer data terbaru
if (dataEdit.name === "TTD") { if (dataEdit.name === "TTD") {
const oldImg = await apiFetch.api.pengaduan["delete-image"].post({ file: dataEdit.value, folder: "lainnya" }); const oldImg = await apiFetch.api.pengaduan["delete-image"].post({
const resImg = await apiFetch.api.pengaduan.upload.post({ file: img, folder: "lainnya" }); file: dataEdit.value,
folder: "lainnya",
});
const resImg = await apiFetch.api.pengaduan.upload.post({
file: img,
folder: "lainnya",
});
if (resImg.status === 200) { if (resImg.status === 200) {
finalData = { finalData = {
...finalData, ...finalData,
value: resImg.data?.filename || "" value: resImg.data?.filename || "",
}; };
setDataEdit(finalData); // update state setDataEdit(finalData); // update state
@@ -70,7 +80,6 @@ export default function DesaSetting({ permissions }: { permissions: JsonValue[]
} }
} }
const res = await apiFetch.api["configuration-desa"].edit.post(finalData); const res = await apiFetch.api["configuration-desa"].edit.post(finalData);
if (res.status === 200) { if (res.status === 200) {
@@ -100,8 +109,11 @@ export default function DesaSetting({ permissions }: { permissions: JsonValue[]
} }
} }
function chooseEdit({
function chooseEdit({ data }: { data: { id: string; value: string; name: string }; }) { data,
}: {
data: { id: string; value: string; name: string };
}) {
setDataEdit(data); setDataEdit(data);
open(); open();
} }
@@ -133,21 +145,18 @@ export default function DesaSetting({ permissions }: { permissions: JsonValue[]
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
> >
<Stack gap="ld"> <Stack gap="ld">
{ {dataEdit.name == "TTD" ? (
dataEdit.name == "TTD"
?
(
<Input.Wrapper label={dataEdit.name}> <Input.Wrapper label={dataEdit.name}>
<FileInput <FileInput
clearable clearable
placeholder="Upload TTD" placeholder="Upload TTD"
accept="image/*" accept="image/*"
onChange={(e) => { setImg(e) }} onChange={(e) => {
setImg(e);
}}
/> />
</Input.Wrapper> </Input.Wrapper>
) ) : (
:
(
<Input.Wrapper label={dataEdit.name}> <Input.Wrapper label={dataEdit.name}>
<Input <Input
value={dataEdit.value} value={dataEdit.value}
@@ -156,8 +165,7 @@ export default function DesaSetting({ permissions }: { permissions: JsonValue[]
} }
/> />
</Input.Wrapper> </Input.Wrapper>
) )}
}
<Group justify="center" grow> <Group justify="center" grow>
<Button variant="light" onClick={close}> <Button variant="light" onClick={close}>
@@ -203,21 +211,33 @@ export default function DesaSetting({ permissions }: { permissions: JsonValue[]
<Table.Tr key={v.id}> <Table.Tr key={v.id}>
<Table.Td>{v.name}</Table.Td> <Table.Td>{v.name}</Table.Td>
<Table.Td> <Table.Td>
{ {v.name == "TTD" ? (
v.name == "TTD" v.value ? (
? <Anchor
v.value ? href="#"
<Anchor href="#" onClick={() => { setViewImg(v.value); setOpenedPreview(true); }} underline="always"> onClick={() => {
setViewImg(v.value);
setOpenedPreview(true);
}}
underline="always"
>
Lihat Lihat
</Anchor> </Anchor>
: ) : (
"-" "-"
: )
) : (
v.value v.value
} )}
</Table.Td> </Table.Td>
<Table.Td> <Table.Td>
<Tooltip label={permissions.includes("setting.desa.edit") ? "Edit Setting" : "Edit Setting - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes("setting.desa.edit")
? "Edit Setting"
: "Edit Setting - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"

View File

@@ -23,7 +23,11 @@ import { useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function KategoriPelayananSurat({ permissions }: { permissions: JsonValue[] }) { export default function KategoriPelayananSurat({
permissions,
}: {
permissions: JsonValue[];
}) {
const [openedDelete, { open: openDelete, close: closeDelete }] = const [openedDelete, { open: openDelete, close: closeDelete }] =
useDisclosure(false); useDisclosure(false);
const [openedDetail, { open: openDetail, close: closeDetail }] = const [openedDetail, { open: openDetail, close: closeDetail }] =
@@ -53,7 +57,6 @@ export default function KategoriPelayananSurat({ permissions }: { permissions: J
mutate(); mutate();
}, []); }, []);
async function handleCreate() { async function handleCreate() {
try { try {
setBtnLoading(true); setBtnLoading(true);
@@ -535,8 +538,7 @@ export default function KategoriPelayananSurat({ permissions }: { permissions: J
<Title order={4} c="gray.2"> <Title order={4} c="gray.2">
Kategori Pelayanan Surat Kategori Pelayanan Surat
</Title> </Title>
{ {permissions.includes("setting.kategori_pelayanan.tambah") && (
permissions.includes("setting.kategori_pelayanan.tambah") && (
<Tooltip label="Tambah Kategori Pelayanan Surat"> <Tooltip label="Tambah Kategori Pelayanan Surat">
<Button <Button
variant="light" variant="light"
@@ -546,8 +548,7 @@ export default function KategoriPelayananSurat({ permissions }: { permissions: J
Tambah Tambah
</Button> </Button>
</Tooltip> </Tooltip>
) )}
}
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
<Stack gap={"md"}> <Stack gap={"md"}>
@@ -578,7 +579,15 @@ export default function KategoriPelayananSurat({ permissions }: { permissions: J
<IconEye size={20} /> <IconEye size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label={permissions.includes("setting.kategori_pelayanan.edit") ? "Edit Kategori" : "Edit Kategori - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes(
"setting.kategori_pelayanan.edit",
)
? "Edit Kategori"
: "Edit Kategori - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -587,12 +596,24 @@ export default function KategoriPelayananSurat({ permissions }: { permissions: J
setDataChoose(v); setDataChoose(v);
open(); open();
}} }}
disabled={!permissions.includes("setting.kategori_pelayanan.edit")} disabled={
!permissions.includes(
"setting.kategori_pelayanan.edit",
)
}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label={permissions.includes("setting.kategori_pelayanan.delete") ? "Hapus Kategori" : "Hapus Kategori - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes(
"setting.kategori_pelayanan.delete",
)
? "Hapus Kategori"
: "Hapus Kategori - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -602,7 +623,11 @@ export default function KategoriPelayananSurat({ permissions }: { permissions: J
setDataDelete(v.id); setDataDelete(v.id);
openDelete(); openDelete();
}} }}
disabled={!permissions.includes("setting.kategori_pelayanan.delete")} disabled={
!permissions.includes(
"setting.kategori_pelayanan.delete",
)
}
> >
<IconTrash size={20} /> <IconTrash size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -20,7 +20,11 @@ import { useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function KategoriPengaduan({ permissions }: { permissions: JsonValue[] }) { export default function KategoriPengaduan({
permissions,
}: {
permissions: JsonValue[];
}) {
const [openedDelete, { open: openDelete, close: closeDelete }] = const [openedDelete, { open: openDelete, close: closeDelete }] =
useDisclosure(false); useDisclosure(false);
const [btnDisable, setBtnDisable] = useState(true); const [btnDisable, setBtnDisable] = useState(true);
@@ -294,8 +298,7 @@ export default function KategoriPengaduan({ permissions }: { permissions: JsonVa
<Title order={4} c="gray.2"> <Title order={4} c="gray.2">
Kategori Pengaduan Kategori Pengaduan
</Title> </Title>
{ {permissions.includes("setting.kategori_pengaduan.tambah") && (
permissions.includes("setting.kategori_pengaduan.tambah") && (
<Tooltip label="Tambah Kategori Pengaduan"> <Tooltip label="Tambah Kategori Pengaduan">
<Button <Button
variant="light" variant="light"
@@ -305,8 +308,7 @@ export default function KategoriPengaduan({ permissions }: { permissions: JsonVa
Tambah Tambah
</Button> </Button>
</Tooltip> </Tooltip>
) )}
}
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
<Stack gap={"md"}> <Stack gap={"md"}>
@@ -323,18 +325,38 @@ export default function KategoriPengaduan({ permissions }: { permissions: JsonVa
<Table.Td>{v.name}</Table.Td> <Table.Td>{v.name}</Table.Td>
<Table.Td> <Table.Td>
<Group> <Group>
<Tooltip label={permissions.includes("setting.kategori_pengaduan.edit") ? "Edit Kategori" : "Edit Kategori - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes(
"setting.kategori_pengaduan.edit",
)
? "Edit Kategori"
: "Edit Kategori - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }} style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
onClick={() => chooseEdit({ data: v })} onClick={() => chooseEdit({ data: v })}
disabled={!permissions.includes("setting.kategori_pengaduan.edit") || v.id == "lainnya"} disabled={
!permissions.includes(
"setting.kategori_pengaduan.edit",
) || v.id == "lainnya"
}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label={permissions.includes("setting.kategori_pengaduan.delete") ? "Hapus Kategori" : "Hapus Kategori - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes(
"setting.kategori_pengaduan.delete",
)
? "Hapus Kategori"
: "Hapus Kategori - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -344,7 +366,11 @@ export default function KategoriPengaduan({ permissions }: { permissions: JsonVa
setDataDelete(v.id); setDataDelete(v.id);
openDelete(); openDelete();
}} }}
disabled={!permissions.includes("setting.kategori_pengaduan.delete") || v.id == "lainnya"} disabled={
!permissions.includes(
"setting.kategori_pengaduan.delete",
) || v.id == "lainnya"
}
> >
<IconTrash size={20} /> <IconTrash size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -3,7 +3,17 @@ import { Flex, Image, Loader, Modal } from "@mantine/core";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function ModalFile({ open, onClose, folder, fileName }: { open: boolean, onClose: () => void, folder: string, fileName: string }) { export default function ModalFile({
open,
onClose,
folder,
fileName,
}: {
open: boolean;
onClose: () => void;
folder: string;
fileName: string;
}) {
const [viewFile, setViewFile] = useState<string>(""); const [viewFile, setViewFile] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [typeFile, setTypeFile] = useState<string>(""); const [typeFile, setTypeFile] = useState<string>("");
@@ -15,7 +25,6 @@ export default function ModalFile({ open, onClose, folder, fileName }: { open: b
} }
}, [open, fileName]); }, [open, fileName]);
const loadImage = async () => { const loadImage = async () => {
try { try {
setViewFile(""); setViewFile("");
@@ -26,7 +35,8 @@ export default function ModalFile({ open, onClose, folder, fileName }: { open: b
setTypeFile(type || ""); setTypeFile(type || "");
// load file // load file
const urlApi = '/api/pengaduan/image?folder=' + folder + '&fileName=' + fileName; const urlApi =
"/api/pengaduan/image?folder=" + folder + "&fileName=" + fileName;
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) { if (!res.ok) {
setError(true); setError(true);
@@ -58,8 +68,6 @@ export default function ModalFile({ open, onClose, folder, fileName }: { open: b
} }
}, [error]); }, [error]);
return ( return (
<Modal <Modal
opened={open} opened={open}
@@ -78,14 +86,14 @@ export default function ModalFile({ open, onClose, folder, fileName }: { open: b
{viewFile && ( {viewFile && (
<> <>
{typeFile == "pdf" ? ( {typeFile == "pdf" ? (
<embed src={viewFile} type="application/pdf" width="100%" height="950" /> <embed
) : (
<Image
radius="md"
h={300}
fit="contain"
src={viewFile} src={viewFile}
type="application/pdf"
width="100%"
height="950"
/> />
) : (
<Image radius="md" h={300} fit="contain" src={viewFile} />
)} )}
</> </>
)} )}

View File

@@ -18,7 +18,15 @@ import SKTidakMampu from "./surat/SKTidakMampu";
import SKUsaha from "./surat/SKUsaha"; import SKUsaha from "./surat/SKUsaha";
import SKYatim from "./surat/SKYatimPiatu"; import SKYatim from "./surat/SKYatimPiatu";
export default function ModalSurat({ open, onClose, surat }: { open: boolean, onClose: () => void, surat: string }) { export default function ModalSurat({
open,
onClose,
surat,
}: {
open: boolean;
onClose: () => void;
surat: string;
}) {
const A4Style = { const A4Style = {
width: "210mm", width: "210mm",
height: "297mm", height: "297mm",
@@ -41,7 +49,6 @@ export default function ModalSurat({ open, onClose, surat }: { open: boolean, on
mutate(); mutate();
}, []); }, []);
const downloadPDF = async () => { const downloadPDF = async () => {
const element = hiddenRef.current; const element = hiddenRef.current;
const canvas = await html2canvas(element, { const canvas = await html2canvas(element, {
@@ -84,13 +91,11 @@ export default function ModalSurat({ open, onClose, surat }: { open: boolean, on
}, },
title: { title: {
width: "100%", width: "100%",
} },
}} }}
title={ title={
<Flex justify="space-between" align="center" w="100%"> <Flex justify="space-between" align="center" w="100%">
<div style={{ fontSize: 18, fontWeight: 600 }}> <div style={{ fontSize: 18, fontWeight: 600 }}>Preview Surat</div>
Preview Surat
</div>
<Flex gap={8}> <Flex gap={8}>
<ActionIcon size={32} variant="default"> <ActionIcon size={32} variant="default">
@@ -105,35 +110,37 @@ export default function ModalSurat({ open, onClose, surat }: { open: boolean, on
} }
> >
<div ref={hiddenRef} style={A4Style}> <div ref={hiddenRef} style={A4Style}>
{ {data && data.data ? (
data && data.data data.data.surat.idCategory == "skusaha" ? (
? data.data.surat.idCategory == "skusaha" <SKUsaha data={data.data} />
? <SKUsaha data={data.data} /> ) : data.data.surat.idCategory == "skkelahiran" ? (
: data.data.surat.idCategory == "skkelahiran" <SKKelahiran data={data.data} />
? <SKKelahiran data={data.data} /> ) : data.data.surat.idCategory == "skkelakuanbaik" ? (
: data.data.surat.idCategory == "skkelakuanbaik" <SKKelakuanBaik data={data.data} />
? <SKKelakuanBaik data={data.data} /> ) : data.data.surat.idCategory == "skpenghasilan" ? (
: data.data.surat.idCategory == "skpenghasilan" <SKPenghasilan data={data.data} />
? <SKPenghasilan data={data.data} /> ) : data.data.surat.idCategory == "sktidakmampu" ? (
: data.data.surat.idCategory == "sktidakmampu" <SKTidakMampu data={data.data} />
? <SKTidakMampu data={data.data} /> ) : data.data.surat.idCategory == "skyatimpiatu" ? (
: data.data.surat.idCategory == "skyatimpiatu" <SKYatim data={data.data} />
? <SKYatim data={data.data} /> ) : data.data.surat.idCategory == "skdomisiliorganisasi" ? (
: data.data.surat.idCategory == "skdomisiliorganisasi" <SKDomisiliOrganisasi data={data.data} />
? <SKDomisiliOrganisasi data={data.data} /> ) : data.data.surat.idCategory == "skbedabiodata" ? (
: data.data.surat.idCategory == "skbedabiodata" <SKBedaBiodataDiri data={data.data} />
? <SKBedaBiodataDiri data={data.data} /> ) : data.data.surat.idCategory == "sktempatusaha" ? (
: data.data.surat.idCategory == "sktempatusaha" <SKTempatUsaha data={data.data} />
? <SKTempatUsaha data={data.data} /> ) : data.data.surat.idCategory == "skbelumkawin" ? (
: data.data.surat.idCategory == "skbelumkawin" <SKBelumKawin data={data.data} />
? <SKBelumKawin data={data.data} /> ) : data.data.surat.idCategory == "skkematian" ? (
: data.data.surat.idCategory == "skkematian" <SKKematian data={data.data} />
? <SKKematian data={data.data} /> ) : (
: <></> <></>
: <></> )
} ) : (
<></>
)}
</div> </div>
</Modal> </Modal>
</> </>
) );
} }

View File

@@ -28,7 +28,7 @@ function RenderNode2({ node }: { node: Node }) {
const sub = Object.values(node.children || {}); const sub = Object.values(node.children || {});
return ( return (
<Flex direction={"row"} wrap={'wrap'} gap={6}> <Flex direction={"row"} wrap={"wrap"} gap={6}>
{/* Title */} {/* Title */}
<Text size="sm">{node.label},</Text> <Text size="sm">{node.label},</Text>
@@ -40,7 +40,11 @@ function RenderNode2({ node }: { node: Node }) {
); );
} }
export default function PermissionRole({ permissions }: { permissions: string[] }) { export default function PermissionRole({
permissions,
}: {
permissions: string[];
}) {
const [showAll, setShowAll] = useState(false); const [showAll, setShowAll] = useState(false);
if (!permissions?.length) return <Text c="dimmed">-</Text>; if (!permissions?.length) return <Text c="dimmed">-</Text>;
@@ -49,16 +53,13 @@ export default function PermissionRole({ permissions }: { permissions: string[]
return ( return (
<Stack gap="sm"> <Stack gap="sm">
{ {showAll
showAll ? ? rootNodes.map((node: any, idx) => (
rootNodes.map((node: any, idx) => (
<RenderNode key={idx} node={node} /> <RenderNode key={idx} node={node} />
)) ))
: : rootNodes
rootNodes.slice(0, 2).map((node: any, idx) => ( .slice(0, 2)
<RenderNode2 key={idx} node={node} /> .map((node: any, idx) => <RenderNode2 key={idx} node={node} />)}
))
}
<Anchor size="xs" onClick={() => setShowAll(!showAll)}> <Anchor size="xs" onClick={() => setShowAll(!showAll)}>
{showAll ? "View less" : "View more"} {showAll ? "View less" : "View more"}
</Anchor> </Anchor>

View File

@@ -1,5 +1,12 @@
import permissionConfig from "@/lib/listPermission.json"; import permissionConfig from "@/lib/listPermission.json";
import { ActionIcon, Checkbox, Collapse, Group, Stack, Text } from "@mantine/core"; import {
ActionIcon,
Checkbox,
Collapse,
Group,
Stack,
Text,
} from "@mantine/core";
import { IconChevronDown, IconChevronRight } from "@tabler/icons-react"; import { IconChevronDown, IconChevronRight } from "@tabler/icons-react";
import { useState } from "react"; import { useState } from "react";
@@ -20,7 +27,7 @@ export default function PermissionTree({
const [openNodes, setOpenNodes] = useState<Record<string, boolean>>({}); const [openNodes, setOpenNodes] = useState<Record<string, boolean>>({});
function toggleNode(label: string) { function toggleNode(label: string) {
setOpenNodes(prev => ({ ...prev, [label]: !prev[label] })); setOpenNodes((prev) => ({ ...prev, [label]: !prev[label] }));
} }
function getAllChildKeys(node: Node): string[] { function getAllChildKeys(node: Node): string[] {
@@ -42,7 +49,6 @@ export default function PermissionTree({
return split.join("."); return split.join(".");
} }
// Update parent ke atas secara rekursif // Update parent ke atas secara rekursif
function updateParent(next: string[], parentKey: string | null): string[] { function updateParent(next: string[], parentKey: string | null): string[] {
if (!parentKey) return next; if (!parentKey) return next;
@@ -95,9 +101,7 @@ export default function PermissionTree({
const isIndeterminate = const isIndeterminate =
!isChecked && !isChecked &&
selected.some( selected.some(
(x) => (x) => typeof x === "string" && x.startsWith(menu.key + "."),
typeof x === "string" &&
x.startsWith(menu.key + ".")
); );
function handleCheck() { function handleCheck() {
@@ -131,10 +135,7 @@ export default function PermissionTree({
<Stack gap={4}> <Stack gap={4}>
<Group gap="xs"> <Group gap="xs">
{menu.children && menu.children.length > 0 ? ( {menu.children && menu.children.length > 0 ? (
<ActionIcon <ActionIcon variant="subtle" onClick={() => toggleNode(menu.label)}>
variant="subtle"
onClick={() => toggleNode(menu.label)}
>
{openNodes[menu.label] ? ( {openNodes[menu.label] ? (
<IconChevronDown size={16} /> <IconChevronDown size={16} />
) : ( ) : (
@@ -169,7 +170,12 @@ export default function PermissionTree({
return ( return (
<Stack> <Stack>
<Text size="sm">Hak Akses</Text> <Text size="sm">Hak Akses</Text>
{permissionConfig.menus.filter((menu: Node) => !menu.key.startsWith("api") && !menu.key.startsWith("credential")).map((menu: Node) => ( {permissionConfig.menus
.filter(
(menu: Node) =>
!menu.key.startsWith("api") && !menu.key.startsWith("credential"),
)
.map((menu: Node) => (
<RenderMenu key={menu.key} menu={menu} /> <RenderMenu key={menu.key} menu={menu} />
))} ))}
</Stack> </Stack>

View File

@@ -13,7 +13,11 @@ import type { JsonValue } from "generated/prisma/runtime/library";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function ProfileUser({ permissions }: { permissions: JsonValue[] }) { export default function ProfileUser({
permissions,
}: {
permissions: JsonValue[];
}) {
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
const [openedPassword, setOpenedPassword] = useState(false); const [openedPassword, setOpenedPassword] = useState(false);
const [pwdBaru, setPwdBaru] = useState(""); const [pwdBaru, setPwdBaru] = useState("");
@@ -127,21 +131,17 @@ export default function ProfileUser({ permissions }: { permissions: JsonValue[]
Profile Pengguna Profile Pengguna
</Title> </Title>
<Group gap="md"> <Group gap="md">
{ {permissions.includes("setting.profile.edit") && (
permissions.includes("setting.profile.edit") && (
<Button variant="light" onClick={() => setOpened(true)}> <Button variant="light" onClick={() => setOpened(true)}>
Edit Edit
</Button> </Button>
) )}
}
{ {permissions.includes("setting.profile.password") && (
permissions.includes("setting.profile.password") && (
<Button variant="light" onClick={() => setOpenedPassword(true)}> <Button variant="light" onClick={() => setOpenedPassword(true)}>
Ubah Password Ubah Password
</Button> </Button>
) )}
}
</Group> </Group>
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />

View File

@@ -11,7 +11,7 @@ import {
Table, Table,
Text, Text,
Title, Title,
Tooltip Tooltip,
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react"; import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react";
@@ -30,7 +30,11 @@ interface MenuNode {
children?: MenuNode[]; children?: MenuNode[];
} }
export default function UserRoleSetting({ permissions }: { permissions: JsonValue[] }) { export default function UserRoleSetting({
permissions,
}: {
permissions: JsonValue[];
}) {
const [btnDisable, setBtnDisable] = useState(true); const [btnDisable, setBtnDisable] = useState(true);
const [btnLoading, setBtnLoading] = useState(false); const [btnLoading, setBtnLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
@@ -70,7 +74,9 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
async function handleCreate() { async function handleCreate() {
try { try {
setBtnLoading(true); setBtnLoading(true);
const res = await apiFetch.api.user["role-create"].post(dataTambah as any); const res = await apiFetch.api.user["role-create"].post(
dataTambah as any,
);
if (res.status === 200) { if (res.status === 200) {
mutate(); mutate();
closeTambah(); closeTambah();
@@ -136,7 +142,9 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
async function handleDelete() { async function handleDelete() {
try { try {
setBtnLoading(true); setBtnLoading(true);
const res = await apiFetch.api.user["role-delete"].post({ id: dataDelete }); const res = await apiFetch.api.user["role-delete"].post({
id: dataDelete,
});
if (res.status === 200) { if (res.status === 200) {
mutate(); mutate();
closeDelete(); closeDelete();
@@ -164,14 +172,28 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
} }
} }
function chooseEdit({ data }: { data: { id: string; name: string; permissions: []; }; }) { function chooseEdit({
data,
}: {
data: { id: string; name: string; permissions: [] };
}) {
setDataEdit({ setDataEdit({
id: data.id, name: data.name, permissions: data.permissions ? data.permissions : [] id: data.id,
name: data.name,
permissions: data.permissions ? data.permissions : [],
}); });
open(); open();
} }
function onValidation({ kat, value, aksi, }: { kat: "name" | "permission"; value: string | null; aksi: "edit" | "tambah"; }) { function onValidation({
kat,
value,
aksi,
}: {
kat: "name" | "permission";
value: string | null;
aksi: "edit" | "tambah";
}) {
if (value == null || value.length < 1) { if (value == null || value.length < 1) {
setBtnDisable(true); setBtnDisable(true);
setError({ ...error, [kat]: true }); setError({ ...error, [kat]: true });
@@ -241,7 +263,10 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
<PermissionTree <PermissionTree
selected={dataEdit.permissions} selected={dataEdit.permissions}
onChange={(permissions) => { onChange={(permissions) => {
setDataEdit({ ...dataEdit, permissions: sortByJsonOrder(permissions) as never[] }); setDataEdit({
...dataEdit,
permissions: sortByJsonOrder(permissions) as never[],
});
}} }}
/> />
<Group justify="center" grow> <Group justify="center" grow>
@@ -292,7 +317,10 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
<PermissionTree <PermissionTree
selected={dataTambah.permissions} selected={dataTambah.permissions}
onChange={(permissions) => { onChange={(permissions) => {
setDataTambah({ ...dataTambah, permissions: sortByJsonOrder(permissions) as never[] }); setDataTambah({
...dataTambah,
permissions: sortByJsonOrder(permissions) as never[],
});
}} }}
/> />
<Group justify="center" grow> <Group justify="center" grow>
@@ -347,8 +375,7 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
<Title order={4} c="gray.2"> <Title order={4} c="gray.2">
Daftar Role Daftar Role
</Title> </Title>
{ {permissions.includes("setting.user_role.tambah") && (
permissions.includes('setting.user_role.tambah') && (
<Tooltip label="Tambah Role"> <Tooltip label="Tambah Role">
<Button <Button
variant="light" variant="light"
@@ -358,8 +385,7 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
Tambah Tambah
</Button> </Button>
</Tooltip> </Tooltip>
) )}
}
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
<Stack gap={"md"}> <Stack gap={"md"}>
@@ -381,18 +407,33 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
</Table.Td> </Table.Td>
<Table.Td w={"100"}> <Table.Td w={"100"}>
<Group> <Group>
<Tooltip label={permissions.includes('setting.user_role.edit') ? "Edit Role" : "Edit Role - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes("setting.user_role.edit")
? "Edit Role"
: "Edit Role - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }} style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
onClick={() => chooseEdit({ data: v })} onClick={() => chooseEdit({ data: v })}
disabled={!permissions.includes('setting.user_role.edit') || v.id == "developer"} disabled={
!permissions.includes("setting.user_role.edit") ||
v.id == "developer"
}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label={permissions.includes('setting.user_role.delete') ? "Delete Role" : "Delete Role - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes("setting.user_role.delete")
? "Delete Role"
: "Delete Role - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -402,7 +443,11 @@ export default function UserRoleSetting({ permissions }: { permissions: JsonValu
setDataDelete(v.id); setDataDelete(v.id);
openDelete(); openDelete();
}} }}
disabled={!permissions.includes('setting.user_role.delete') || v.id == "developer"} disabled={
!permissions.includes(
"setting.user_role.delete",
) || v.id == "developer"
}
> >
<IconTrash size={20} /> <IconTrash size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -21,7 +21,11 @@ import { useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import notification from "./notificationGlobal"; import notification from "./notificationGlobal";
export default function UserSetting({ permissions }: { permissions: JsonValue[] }) { export default function UserSetting({
permissions,
}: {
permissions: JsonValue[];
}) {
const [btnDisable, setBtnDisable] = useState(true); const [btnDisable, setBtnDisable] = useState(true);
const [btnLoading, setBtnLoading] = useState(false); const [btnLoading, setBtnLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
@@ -437,8 +441,7 @@ export default function UserSetting({ permissions }: { permissions: JsonValue[]
<Title order={4} c="gray.2"> <Title order={4} c="gray.2">
Daftar User Daftar User
</Title> </Title>
{ {permissions.includes("setting.user.tambah") && (
permissions.includes('setting.user.tambah') && (
<Tooltip label="Tambah User"> <Tooltip label="Tambah User">
<Button <Button
variant="light" variant="light"
@@ -448,9 +451,7 @@ export default function UserSetting({ permissions }: { permissions: JsonValue[]
Tambah Tambah
</Button> </Button>
</Tooltip> </Tooltip>
) )}
}
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
<Stack gap={"md"}> <Stack gap={"md"}>
@@ -474,18 +475,33 @@ export default function UserSetting({ permissions }: { permissions: JsonValue[]
<Table.Td>{v.nameRole}</Table.Td> <Table.Td>{v.nameRole}</Table.Td>
<Table.Td> <Table.Td>
<Group> <Group>
<Tooltip label={permissions.includes('setting.user.edit') ? "Edit User" : "Edit User - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes("setting.user.edit")
? "Edit User"
: "Edit User - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }} style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
onClick={() => chooseEdit({ data: v })} onClick={() => chooseEdit({ data: v })}
disabled={!permissions.includes('setting.user.edit') || v.roleId == "developer"} disabled={
!permissions.includes("setting.user.edit") ||
v.roleId == "developer"
}
> >
<IconEdit size={20} /> <IconEdit size={20} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip label={permissions.includes('setting.user.delete') ? "Delete User" : "Delete User - Anda tidak memiliki akses"}> <Tooltip
label={
permissions.includes("setting.user.delete")
? "Delete User"
: "Delete User - Anda tidak memiliki akses"
}
>
<ActionIcon <ActionIcon
variant="light" variant="light"
size="sm" size="sm"
@@ -495,7 +511,10 @@ export default function UserSetting({ permissions }: { permissions: JsonValue[]
setDataDelete(v.id); setDataDelete(v.id);
openDelete(); openDelete();
}} }}
disabled={!permissions.includes('setting.user.delete') || v.roleId == "developer"} disabled={
!permissions.includes("setting.user.delete") ||
v.roleId == "developer"
}
> >
<IconTrash size={20} /> <IconTrash size={20} />
</ActionIcon> </ActionIcon>

View File

@@ -6,7 +6,8 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(); const [viewImg, setViewImg] = useState<string>();
const getValue = (jenis: string) => const getValue = (jenis: string) =>
_.upperFirst( _.upperFirst(
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" data.surat.dataText.find((item: any) => item.jenis === jenis)?.value ||
"",
); );
const loadImage = async () => { const loadImage = async () => {
@@ -14,7 +15,9 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -36,21 +39,27 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
loadImage(); loadImage();
}, [data]); }, [data]);
return ( return (
<div style={{ lineHeight: "1.25" }}> <div style={{ lineHeight: "1.25" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "15px" }}> <div style={{ textAlign: "center", marginBottom: "15px" }}>
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br /> <b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br /> <b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
Alamat: {data.setting.desaAlamat}<br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
<br />
Alamat: {data.setting.desaAlamat}
<br />
Kode Pos: {data.setting.desaPos} Kode Pos: {data.setting.desaPos}
</div> </div>
{/* JUDUL */} {/* JUDUL */}
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<b><u>SURAT KETERANGAN BEDA BIODATA DIRI</u></b><br /> <b>
<u>SURAT KETERANGAN BEDA BIODATA DIRI</u>
</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -67,7 +76,9 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
<tr> <tr>
<td>Jabatan</td> <td>Jabatan</td>
<td>:</td> <td>:</td>
<td>{data.setting.perbekelJabatan + " " + data.setting.desaNama}</td> <td>
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
</td>
</tr> </tr>
<tr> <tr>
<td>Kecamatan</td> <td>Kecamatan</td>
@@ -85,29 +96,68 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
{/* IDENTITAS ORANG YG MEMINTA SURAT */} {/* IDENTITAS ORANG YG MEMINTA SURAT */}
<div style={{ marginTop: "15px" }}> <div style={{ marginTop: "15px" }}>
Dengan ini menerangkan bahwa berdasarkan keterangan dari yang bersangkutan: Dengan ini menerangkan bahwa berdasarkan keterangan dari yang
bersangkutan:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></tr> <tr>
<tr><td>Tempat/Tanggal Lahir</td><td>:</td><td>{getValue("tempat tanggal lahir")}</td></tr> <td style={{ width: "160px" }}>Nama</td>
<tr><td>Jenis Kelamin</td><td>:</td><td>{getValue("jenis kelamin")}</td></tr> <td style={{ width: "10px" }}>:</td>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat")}</td></tr> <td>{getValue("nama")}</td>
<tr><td>Pekerjaan</td><td>:</td><td>{getValue("pekerjaan")}</td></tr> </tr>
<tr><td>NIK</td><td>:</td><td>{getValue("nik")}</td></tr> <tr>
<td>Tempat/Tanggal Lahir</td>
<td>:</td>
<td>{getValue("tempat tanggal lahir")}</td>
</tr>
<tr>
<td>Jenis Kelamin</td>
<td>:</td>
<td>{getValue("jenis kelamin")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat")}</td>
</tr>
<tr>
<td>Pekerjaan</td>
<td>:</td>
<td>{getValue("pekerjaan")}</td>
</tr>
<tr>
<td>NIK</td>
<td>:</td>
<td>{getValue("nik")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div style={{ marginTop: "15px" }}> <div style={{ marginTop: "15px" }}>
Bahwa orang tersebut di atas <b>benar merupakan orang yang sama</b>, meskipun terdapat <b>perbedaan data pribadi (biodata)</b> pada beberapa dokumen, sebagai berikut: Bahwa orang tersebut di atas <b>benar merupakan orang yang sama</b>,
meskipun terdapat <b>perbedaan data pribadi (biodata)</b> pada beberapa
dokumen, sebagai berikut:
</div> </div>
<div style={{ marginTop: "15px" }}> <div style={{ marginTop: "15px" }}>
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>1. Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></tr> <tr>
<tr><td>Tertulis pada dokumen A</td><td>:</td><td>{getValue("tertulis pada dokumen a")}</td></tr> <td style={{ width: "160px" }}>1. Nama</td>
<tr><td>Tertulis pada dokumen B</td><td>:</td><td>{getValue("tertulis pada dokumen b")}</td></tr> <td style={{ width: "10px" }}>:</td>
<td>{getValue("nama")}</td>
</tr>
<tr>
<td>Tertulis pada dokumen A</td>
<td>:</td>
<td>{getValue("tertulis pada dokumen a")}</td>
</tr>
<tr>
<td>Tertulis pada dokumen B</td>
<td>:</td>
<td>{getValue("tertulis pada dokumen b")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -115,9 +165,21 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
<div style={{ marginTop: "15px" }}> <div style={{ marginTop: "15px" }}>
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>2. Tempat/Tanggal Lahir</td><td style={{ width: "10px" }}>:</td><td>{getValue("tempat tanggal lahir")}</td></tr> <tr>
<tr><td>Tertulis pada dokumen A</td><td>:</td><td>{getValue("tertulis pada dokumen a")}</td></tr> <td style={{ width: "160px" }}>2. Tempat/Tanggal Lahir</td>
<tr><td>Tertulis pada dokumen B</td><td>:</td><td>{getValue("tertulis pada dokumen b")}</td></tr> <td style={{ width: "10px" }}>:</td>
<td>{getValue("tempat tanggal lahir")}</td>
</tr>
<tr>
<td>Tertulis pada dokumen A</td>
<td>:</td>
<td>{getValue("tertulis pada dokumen a")}</td>
</tr>
<tr>
<td>Tertulis pada dokumen B</td>
<td>:</td>
<td>{getValue("tertulis pada dokumen b")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -125,17 +187,32 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
<div style={{ marginTop: "15px" }}> <div style={{ marginTop: "15px" }}>
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>3. Nama Orang Tua</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama orang tua")}</td></tr> <tr>
<tr><td>Tertulis pada dokumen A</td><td>:</td><td>{getValue("tertulis pada dokumen a")}</td></tr> <td style={{ width: "160px" }}>3. Nama Orang Tua</td>
<tr><td>Tertulis pada dokumen B</td><td>:</td><td>{getValue("tertulis pada dokumen b")}</td></tr> <td style={{ width: "10px" }}>:</td>
<td>{getValue("nama orang tua")}</td>
</tr>
<tr>
<td>Tertulis pada dokumen A</td>
<td>:</td>
<td>{getValue("tertulis pada dokumen a")}</td>
</tr>
<tr>
<td>Tertulis pada dokumen B</td>
<td>:</td>
<td>{getValue("tertulis pada dokumen b")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div style={{ marginTop: "15px" }}> <div style={{ marginTop: "15px" }}>
Perbedaan tersebut terjadi karena <b>kesalahan penulisan/pencatatan administratif</b>, namun yang bersangkutan adalah <b>orang yang sama</b>. Perbedaan tersebut terjadi karena{" "}
<b>kesalahan penulisan/pencatatan administratif</b>, namun yang
bersangkutan adalah <b>orang yang sama</b>.
<br /> <br />
Dengan surat keterangan ini dibuat dengan sebenar-benarnya untuk dipergunakan sebagaimana mestinya. Dengan surat keterangan ini dibuat dengan sebenar-benarnya untuk
dipergunakan sebagaimana mestinya.
</div> </div>
<div style={{ marginTop: "15px" }}> <div style={{ marginTop: "15px" }}>
@@ -144,16 +221,24 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
</div> </div>
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ marginTop: "0px", display: "flex", justifyContent: "flex-end", width: "100%" }}> <div
style={{
marginTop: "0px",
display: "flex",
justifyContent: "flex-end",
width: "100%",
}}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
Kepala Desa / Lurah {data.setting.desaNama} Kepala Desa / Lurah {data.setting.desaNama}
<br /><br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
<br />
<u>{data.setting.perbekelNama}</u> <br /> <u>{data.setting.perbekelNama}</u> <br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -6,7 +6,8 @@ export default function SKBelumKawin({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(); const [viewImg, setViewImg] = useState<string>();
const getValue = (jenis: string) => const getValue = (jenis: string) =>
_.upperFirst( _.upperFirst(
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" data.surat.dataText.find((item: any) => item.jenis === jenis)?.value ||
"",
); );
const loadImage = async () => { const loadImage = async () => {
@@ -14,7 +15,9 @@ export default function SKBelumKawin({ data }: { data: any }) {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -40,46 +43,86 @@ export default function SKBelumKawin({ data }: { data: any }) {
<div style={{ lineHeight: "1.3" }}> <div style={{ lineHeight: "1.3" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "10px" }}> <div style={{ textAlign: "center", marginBottom: "10px" }}>
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br /> <b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br /> <b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
Alamat: {data.setting.desaAlamat}<br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
<br />
Alamat: {data.setting.desaAlamat}
<br />
Kode Pos: {data.setting.desaPos} Kode Pos: {data.setting.desaPos}
</div> </div>
{/* JUDUL */} {/* JUDUL */}
<div style={{ textAlign: "center", margin: "20px 0" }}> <div style={{ textAlign: "center", margin: "20px 0" }}>
<b><u>SURAT KETERANGAN BELUM KAWIN</u></b><br /> <b>
<u>SURAT KETERANGAN BELUM KAWIN</u>
</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
{/* YANG BERTANDA TANGAN */} {/* YANG BERTANDA TANGAN */}
<div style={{ marginTop: "15px" }}> <div style={{ marginTop: "15px" }}>
Yang bertanda tangan di bawah ini {data.setting.perbekelJabatan} {data.setting.desaNama}, Kecamatan {data.setting.desaKecamatan}, Kabupaten {data.setting.desaKabupaten}, dengan ini menerangkan bahwa: Yang bertanda tangan di bawah ini {data.setting.perbekelJabatan}{" "}
{data.setting.desaNama}, Kecamatan {data.setting.desaKecamatan},
Kabupaten {data.setting.desaKabupaten}, dengan ini menerangkan bahwa:
</div> </div>
{/* IDENTITAS ORANG YG MEMINTA SURAT */} {/* IDENTITAS ORANG YG MEMINTA SURAT */}
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></tr> <tr>
<tr><td>NIK</td><td>:</td><td>{getValue("nik")}</td></tr> <td style={{ width: "160px" }}>Nama</td>
<tr><td>Tempat/Tanggal Lahir</td><td>:</td><td>{getValue("tempat tanggal lahir")}</td></tr> <td style={{ width: "10px" }}>:</td>
<tr><td>Jenis Kelamin</td><td>:</td><td>{getValue("jenis kelamin")}</td></tr> <td>{getValue("nama")}</td>
<tr><td>Agama</td><td>:</td><td>{getValue("agama")}</td></tr> </tr>
<tr><td>Pekerjaan</td><td>:</td><td>{getValue("pekerjaan")}</td></tr> <tr>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat")}</td></tr> <td>NIK</td>
<td>:</td>
<td>{getValue("nik")}</td>
</tr>
<tr>
<td>Tempat/Tanggal Lahir</td>
<td>:</td>
<td>{getValue("tempat tanggal lahir")}</td>
</tr>
<tr>
<td>Jenis Kelamin</td>
<td>:</td>
<td>{getValue("jenis kelamin")}</td>
</tr>
<tr>
<td>Agama</td>
<td>:</td>
<td>{getValue("agama")}</td>
</tr>
<tr>
<td>Pekerjaan</td>
<td>:</td>
<td>{getValue("pekerjaan")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Berdasarkan keterangan dari yang bersangkutan dan data administrasi kependudukan yang ada di Desa {data.setting.desaNama}, Berdasarkan keterangan dari yang bersangkutan dan data administrasi
yang bersangkutan benar sampai saat ini belum pernah menikah, baik secara adat, agama, maupun hukum negara. kependudukan yang ada di Desa {data.setting.desaNama}, yang bersangkutan
benar sampai saat ini belum pernah menikah, baik secara adat, agama,
maupun hukum negara.
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya. Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat
digunakan sebagaimana mestinya.
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
@@ -88,23 +131,38 @@ export default function SKBelumKawin({ data }: { data: any }) {
</div> </div>
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ marginTop: "40px", display: "flex", justifyContent: "space-between", width: "100%" }}> <div
style={{
marginTop: "40px",
display: "flex",
justifyContent: "space-between",
width: "100%",
}}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<br /><br /> <br />
<br />
Pemohon Pemohon
<br /><br /><br /><br /><br /><br /> <br />
<br />
<br />
<br />
<br />
<br />
<u>{getValue("nama")}</u> <br /> <u>{getValue("nama")}</u> <br />
</div> </div>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<br /><br /> <br />
<br />
Kepala Desa / Lurah {data.setting.desaNama} Kepala Desa / Lurah {data.setting.desaNama}
<br /><br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
<br />
<u>{data.setting.perbekelNama}</u> <br /> <u>{data.setting.perbekelNama}</u> <br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -6,7 +6,8 @@ export default function SKDomisiliOrganisasi({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (jenis: string) => const getValue = (jenis: string) =>
_.upperFirst( _.upperFirst(
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" data.surat.dataText.find((item: any) => item.jenis === jenis)?.value ||
"",
); );
const loadImage = async () => { const loadImage = async () => {
@@ -14,7 +15,9 @@ export default function SKDomisiliOrganisasi({ data }: { data: any }) {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -40,16 +43,23 @@ export default function SKDomisiliOrganisasi({ data }: { data: any }) {
<div style={{ lineHeight: "1.3" }}> <div style={{ lineHeight: "1.3" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "10px" }}> <div style={{ textAlign: "center", marginBottom: "10px" }}>
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br /> <b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br /> <b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
Alamat: {data.setting.desaAlamat}<br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
<br />
Alamat: {data.setting.desaAlamat}
<br />
Kode Pos: {data.setting.desaPos} Kode Pos: {data.setting.desaPos}
</div> </div>
{/* JUDUL */} {/* JUDUL */}
<div style={{ textAlign: "center", margin: "20px 0" }}> <div style={{ textAlign: "center", margin: "20px 0" }}>
<b><u>SURAT KETERANGAN DOMISILI ORGANISASI</u></b><br /> <b>
<u>SURAT KETERANGAN DOMISILI ORGANISASI</u>
</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -82,23 +92,47 @@ export default function SKDomisiliOrganisasi({ data }: { data: any }) {
Dengan ini menerangkan bahwa: Dengan ini menerangkan bahwa:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Nama Organisasi</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></tr> <tr>
<tr><td>Jenis Organisasi</td><td>:</td><td>{getValue("jenis kelamin")}</td></tr> <td style={{ width: "160px" }}>Nama Organisasi</td>
<tr><td>Alamat</td><td>:</td><td>{getValue("tempat tanggal lahir")}</td></tr> <td style={{ width: "10px" }}>:</td>
<tr><td>Nomor Telepon</td><td>:</td><td>{getValue("negara")}</td></tr> <td>{getValue("nama")}</td>
<tr><td>Nama Pimpinan</td><td>:</td><td>{getValue("agama")}</td></tr> </tr>
<tr>
<td>Jenis Organisasi</td>
<td>:</td>
<td>{getValue("jenis kelamin")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("tempat tanggal lahir")}</td>
</tr>
<tr>
<td>Nomor Telepon</td>
<td>:</td>
<td>{getValue("negara")}</td>
</tr>
<tr>
<td>Nama Pimpinan</td>
<td>:</td>
<td>{getValue("agama")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Benar bahwa organisasi tersebut berdomisili di wilayah Desa / Kelurahan {data.setting.desaNama}, Kecamatan {data.setting.desaKecamatan}, Kabupaten {data.setting.desaKabupaten}. Benar bahwa organisasi tersebut berdomisili di wilayah Desa / Kelurahan{" "}
Dan sampai saat ini masih aktif melakukan kegiatan sesuai dengan bidangnya.<br /> {data.setting.desaNama}, Kecamatan {data.setting.desaKecamatan},
Kabupaten {data.setting.desaKabupaten}. Dan sampai saat ini masih aktif
melakukan kegiatan sesuai dengan bidangnya.
<br />
Surat keterangan ini dibuat untuk keperluan {getValue("keperluan")}. Surat keterangan ini dibuat untuk keperluan {getValue("keperluan")}.
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya. Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat
digunakan sebagaimana mestinya.
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
@@ -107,17 +141,25 @@ export default function SKDomisiliOrganisasi({ data }: { data: any }) {
</div> </div>
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end", width: "100%" }}> <div
style={{
marginTop: "40px",
display: "flex",
justifyContent: "flex-end",
width: "100%",
}}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<br /> <br />
Kepala Desa / Lurah {data.setting.desaNama} Kepala Desa / Lurah {data.setting.desaNama}
<br /><br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
<br />
<u>{data.setting.perbekelNama}</u> <br /> <u>{data.setting.perbekelNama}</u> <br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -6,7 +6,8 @@ export default function SKKelahiran({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (jenis: string) => const getValue = (jenis: string) =>
_.upperFirst( _.upperFirst(
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" data.surat.dataText.find((item: any) => item.jenis === jenis)?.value ||
"",
); );
const loadImage = async () => { const loadImage = async () => {
@@ -14,7 +15,9 @@ export default function SKKelahiran({ data }: { data: any }) {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -38,18 +41,25 @@ export default function SKKelahiran({ data }: { data: any }) {
return ( return (
<div style={{ lineHeight: "1.2" }}> <div style={{ lineHeight: "1.2" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "20px" }}> <div style={{ textAlign: "center", marginBottom: "20px" }}>
<b>PEMERINTAH KABUPATEN/KOTA {_.upperCase(data.setting.desaKabupaten)}</b><br /> <b>
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br /> PEMERINTAH KABUPATEN/KOTA {_.upperCase(data.setting.desaKabupaten)}
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br /> </b>
<br />
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
<br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
<br />
Alamat: {data.setting.desaAlamat} Alamat: {data.setting.desaAlamat}
</div> </div>
{/* JUDUL */} {/* JUDUL */}
<div style={{ textAlign: "center", margin: "20px 0" }}> <div style={{ textAlign: "center", margin: "20px 0" }}>
<b><u>SURAT KETERANGAN KELAHIRAN</u></b><br /> <b>
<u>SURAT KETERANGAN KELAHIRAN</u>
</b>
<br />
Nomor : {data.surat.noSurat} Nomor : {data.surat.noSurat}
</div> </div>
@@ -65,12 +75,36 @@ export default function SKKelahiran({ data }: { data: any }) {
Telah lahir seorang anak pada: Telah lahir seorang anak pada:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "200px" }}>Tanggal Lahir</td><td>:</td><td>{getValue("tanggal lahir anak")}</td></tr> <tr>
<tr><td>Pukul</td><td>:</td><td>{getValue("pukul lahir anak")}</td></tr> <td style={{ width: "200px" }}>Tanggal Lahir</td>
<tr><td>Tempat Kelahiran</td><td>:</td><td>{getValue("tempat lahir anak")}</td></tr> <td>:</td>
<tr><td>Jenis Kelamin</td><td>:</td><td>{getValue("jenis kelamin anak")}</td></tr> <td>{getValue("tanggal lahir anak")}</td>
<tr><td>Anak ke</td><td>:</td><td>{getValue("anak ke")}</td></tr> </tr>
<tr><td>Nama Anak</td><td>:</td><td>{getValue("nama anak")}</td></tr> <tr>
<td>Pukul</td>
<td>:</td>
<td>{getValue("pukul lahir anak")}</td>
</tr>
<tr>
<td>Tempat Kelahiran</td>
<td>:</td>
<td>{getValue("tempat lahir anak")}</td>
</tr>
<tr>
<td>Jenis Kelamin</td>
<td>:</td>
<td>{getValue("jenis kelamin anak")}</td>
</tr>
<tr>
<td>Anak ke</td>
<td>:</td>
<td>{getValue("anak ke")}</td>
</tr>
<tr>
<td>Nama Anak</td>
<td>:</td>
<td>{getValue("nama anak")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -80,11 +114,31 @@ export default function SKKelahiran({ data }: { data: any }) {
Dari seorang ibu bernama: Dari seorang ibu bernama:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "200px" }}>Nama Lengkap Ibu</td><td>:</td><td>{getValue("nama ibu")}</td></tr> <tr>
<tr><td>NIK</td><td>:</td><td>{getValue("nik ibu")}</td></tr> <td style={{ width: "200px" }}>Nama Lengkap Ibu</td>
<tr><td>Tempat & Tanggal Lahir</td><td>:</td><td>{getValue("tempat tanggal lahir ibu")}</td></tr> <td>:</td>
<tr><td>Pekerjaan</td><td>:</td><td>{getValue("pekerjaan ibu")}</td></tr> <td>{getValue("nama ibu")}</td>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat ibu")}</td></tr> </tr>
<tr>
<td>NIK</td>
<td>:</td>
<td>{getValue("nik ibu")}</td>
</tr>
<tr>
<td>Tempat & Tanggal Lahir</td>
<td>:</td>
<td>{getValue("tempat tanggal lahir ibu")}</td>
</tr>
<tr>
<td>Pekerjaan</td>
<td>:</td>
<td>{getValue("pekerjaan ibu")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat ibu")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -94,11 +148,31 @@ export default function SKKelahiran({ data }: { data: any }) {
Dan seorang ayah bernama: Dan seorang ayah bernama:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "200px" }}>Nama Lengkap Ayah</td><td>:</td><td>{getValue("nama ayah")}</td></tr> <tr>
<tr><td>NIK</td><td>:</td><td>{getValue("nik ayah")}</td></tr> <td style={{ width: "200px" }}>Nama Lengkap Ayah</td>
<tr><td>Tempat & Tanggal Lahir</td><td>:</td><td>{getValue("tempat tanggal lahir ayah")}</td></tr> <td>:</td>
<tr><td>Pekerjaan</td><td>:</td><td>{getValue("pekerjaan ayah")}</td></tr> <td>{getValue("nama ayah")}</td>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat ayah")}</td></tr> </tr>
<tr>
<td>NIK</td>
<td>:</td>
<td>{getValue("nik ayah")}</td>
</tr>
<tr>
<td>Tempat & Tanggal Lahir</td>
<td>:</td>
<td>{getValue("tempat tanggal lahir ayah")}</td>
</tr>
<tr>
<td>Pekerjaan</td>
<td>:</td>
<td>{getValue("pekerjaan ayah")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat ayah")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -108,37 +182,65 @@ export default function SKKelahiran({ data }: { data: any }) {
Berdasarkan laporan dari: Berdasarkan laporan dari:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "200px" }}>Nama Pelapor</td><td>:</td><td>{getValue("nama pelapor")}</td></tr> <tr>
<tr><td>Hubungan dengan Anak</td><td>:</td><td>{getValue("hubungan pelapor")}</td></tr> <td style={{ width: "200px" }}>Nama Pelapor</td>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat pelapor")}</td></tr> <td>:</td>
<td>{getValue("nama pelapor")}</td>
</tr>
<tr>
<td>Hubungan dengan Anak</td>
<td>:</td>
<td>{getValue("hubungan pelapor")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat pelapor")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
{/* PENUTUP */} {/* PENUTUP */}
<div style={{ marginTop: "20px", textAlign: "justify" }}> <div style={{ marginTop: "20px", textAlign: "justify" }}>
Demikian Surat Keterangan Kelahiran ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya. Demikian Surat Keterangan Kelahiran ini dibuat dengan sebenarnya agar
dapat digunakan sebagaimana mestinya.
</div> </div>
{/* TEMPAT TANGGAL */} {/* TEMPAT TANGGAL */}
<table style={{ width: "100%", marginTop: "20px" }}> <table style={{ width: "100%", marginTop: "20px" }}>
<tbody> <tbody>
<tr><td style={{ width: "200px" }}>Dikeluarkan di</td><td>:</td><td>{data.setting.desaNama}</td></tr> <tr>
<tr><td>Pada tanggal</td><td>:</td><td>{data.surat.createdAt}</td></tr> <td style={{ width: "200px" }}>Dikeluarkan di</td>
<td>:</td>
<td>{data.setting.desaNama}</td>
</tr>
<tr>
<td>Pada tanggal</td>
<td>:</td>
<td>{data.surat.createdAt}</td>
</tr>
</tbody> </tbody>
</table> </table>
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ marginTop: "40px", width: "100%", display: "flex", justifyContent: "flex-end" }}> <div
style={{
marginTop: "40px",
width: "100%",
display: "flex",
justifyContent: "flex-end",
}}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
Kepala Desa / Lurah {data.setting.desaNama} Kepala Desa / Lurah {data.setting.desaNama}
<br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br /> <img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
<br />
<u>{data.setting.perbekelNama}</u> <br /> <u>{data.setting.perbekelNama}</u> <br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -6,7 +6,8 @@ export default function SKKelakuanBaik({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (jenis: string) => const getValue = (jenis: string) =>
_.upperFirst( _.upperFirst(
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" data.surat.dataText.find((item: any) => item.jenis === jenis)?.value ||
"",
); );
const loadImage = async () => { const loadImage = async () => {
@@ -14,7 +15,9 @@ export default function SKKelakuanBaik({ data }: { data: any }) {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -38,11 +41,12 @@ export default function SKKelakuanBaik({ data }: { data: any }) {
return ( return (
<div style={{ lineHeight: "1.3" }}> <div style={{ lineHeight: "1.3" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "30px" }}> <div style={{ textAlign: "center", marginBottom: "30px" }}>
<b style={{ fontSize: "18px" }}>SURAT KETERANGAN KELAKUAN BAIK</b><br /> <b style={{ fontSize: "18px" }}>SURAT KETERANGAN KELAKUAN BAIK</b>
(PENGANTAR SKCK)<br /> <br />
(PENGANTAR SKCK)
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -94,23 +98,26 @@ export default function SKKelakuanBaik({ data }: { data: any }) {
{/* ISI */} {/* ISI */}
<div style={{ textAlign: "justify", marginBottom: "15px" }}> <div style={{ textAlign: "justify", marginBottom: "15px" }}>
Adalah benar penduduk yang berdomisili di wilayah kami dan selama tinggal di lingkungan Adalah benar penduduk yang berdomisili di wilayah kami dan selama
Desa {data.setting.desaNama}, berkelakuan baik, tidak pernah terlibat perbuatan melanggar hukum, tinggal di lingkungan Desa {data.setting.desaNama}, berkelakuan baik,
serta dikenal sopan dan aktif dalam kegiatan kemasyarakatan. tidak pernah terlibat perbuatan melanggar hukum, serta dikenal sopan dan
aktif dalam kegiatan kemasyarakatan.
</div> </div>
<div style={{ textAlign: "justify", marginBottom: "15px" }}> <div style={{ textAlign: "justify", marginBottom: "15px" }}>
Surat keterangan ini diberikan sebagai pengantar permohonan penerbitan Surat Keterangan Surat keterangan ini diberikan sebagai pengantar permohonan penerbitan
Catatan Kepolisian (SKCK) ke Polsek/Polres {getValue("polsek")}. Surat Keterangan Catatan Kepolisian (SKCK) ke Polsek/Polres{" "}
{getValue("polsek")}.
</div> </div>
<div style={{ textAlign: "justify", marginBottom: "15px" }}> <div style={{ textAlign: "justify", marginBottom: "15px" }}>
Surat ini berlaku selama 6 (enam) bulan sejak tanggal diterbitkan, kecuali terdapat perubahan Surat ini berlaku selama 6 (enam) bulan sejak tanggal diterbitkan,
data yang mendasar. kecuali terdapat perubahan data yang mendasar.
</div> </div>
<div style={{ textAlign: "justify", marginBottom: "20px" }}> <div style={{ textAlign: "justify", marginBottom: "20px" }}>
Demikian surat keterangan ini dibuat dengan sebenarnya untuk dipergunakan sebagaimana mestinya. Demikian surat keterangan ini dibuat dengan sebenarnya untuk
dipergunakan sebagaimana mestinya.
</div> </div>
{/* TANGGAL */} {/* TANGGAL */}
@@ -130,16 +137,19 @@ export default function SKKelakuanBaik({ data }: { data: any }) {
</table> </table>
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}> <div
style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
Kepala Desa {data.setting.desaNama} Kepala Desa {data.setting.desaNama}
<br /> <br /> <br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br /> <img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
<u>{data.setting.perbekelNama}</u><br /> <br />
<u>{data.setting.perbekelNama}</u>
<br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -6,7 +6,8 @@ export default function SKKematian({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (jenis: string) => const getValue = (jenis: string) =>
_.upperFirst( _.upperFirst(
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" data.surat.dataText.find((item: any) => item.jenis === jenis)?.value ||
"",
); );
const loadImage = async () => { const loadImage = async () => {
@@ -14,7 +15,9 @@ export default function SKKematian({ data }: { data: any }) {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -40,16 +43,23 @@ export default function SKKematian({ data }: { data: any }) {
<div style={{ lineHeight: "1.3" }}> <div style={{ lineHeight: "1.3" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "10px" }}> <div style={{ textAlign: "center", marginBottom: "10px" }}>
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br /> <b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br /> <b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
Alamat: {data.setting.desaAlamat}<br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
<br />
Alamat: {data.setting.desaAlamat}
<br />
Kode Pos: {data.setting.desaPos} Kode Pos: {data.setting.desaPos}
</div> </div>
{/* JUDUL */} {/* JUDUL */}
<div style={{ textAlign: "center", margin: "20px 0" }}> <div style={{ textAlign: "center", margin: "20px 0" }}>
<b><u>SURAT KETERANGAN KEMATIAN</u></b><br /> <b>
<u>SURAT KETERANGAN KEMATIAN</u>
</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -58,11 +68,31 @@ export default function SKKematian({ data }: { data: any }) {
Yang bertanda tangan di bawah ini: Yang bertanda tangan di bawah ini:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></tr> <tr>
<tr><td>NIK</td><td>:</td><td>{getValue("nik")}</td></tr> <td style={{ width: "160px" }}>Nama</td>
<tr><td>Pekerjaan</td><td>:</td><td>{getValue("pekerjaan")}</td></tr> <td style={{ width: "10px" }}>:</td>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat")}</td></tr> <td>{getValue("nama")}</td>
<tr><td>Hubungan dengan almarhum/almarhumah</td><td>:</td><td>{getValue("hubungan dengan almarhum")}</td></tr> </tr>
<tr>
<td>NIK</td>
<td>:</td>
<td>{getValue("nik")}</td>
</tr>
<tr>
<td>Pekerjaan</td>
<td>:</td>
<td>{getValue("pekerjaan")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat")}</td>
</tr>
<tr>
<td>Hubungan dengan almarhum/almarhumah</td>
<td>:</td>
<td>{getValue("hubungan dengan almarhum")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -71,12 +101,36 @@ export default function SKKematian({ data }: { data: any }) {
Melaporkan bahwa: Melaporkan bahwa:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></tr> <tr>
<tr><td>NIK</td><td>:</td><td>{getValue("nik")}</td></tr> <td style={{ width: "160px" }}>Nama</td>
<tr><td>Jenis Kelamin</td><td>:</td><td>{getValue("jenis kelamin")}</td></tr> <td style={{ width: "10px" }}>:</td>
<tr><td>Tempat/Tanggal Lahir</td><td>:</td><td>{getValue("tempat tanggal lahir")}</td></tr> <td>{getValue("nama")}</td>
<tr><td>Agama</td><td>:</td><td>{getValue("agama")}</td></tr> </tr>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat")}</td></tr> <tr>
<td>NIK</td>
<td>:</td>
<td>{getValue("nik")}</td>
</tr>
<tr>
<td>Jenis Kelamin</td>
<td>:</td>
<td>{getValue("jenis kelamin")}</td>
</tr>
<tr>
<td>Tempat/Tanggal Lahir</td>
<td>:</td>
<td>{getValue("tempat tanggal lahir")}</td>
</tr>
<tr>
<td>Agama</td>
<td>:</td>
<td>{getValue("agama")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -85,36 +139,65 @@ export default function SKKematian({ data }: { data: any }) {
Telah meninggal dunia pada: Telah meninggal dunia pada:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Tanggal Kematian</td><td style={{ width: "10px" }}>:</td><td>{getValue("tanggal kematian")}</td></tr> <tr>
<tr><td>Waktu Kematian</td><td>:</td><td>{getValue("waktu kematian")}</td></tr> <td style={{ width: "160px" }}>Tanggal Kematian</td>
<tr><td>Tempat Kematian</td><td>:</td><td>{getValue("tempat kematian")}</td></tr> <td style={{ width: "10px" }}>:</td>
<tr><td>Penyebab Kematian</td><td>:</td><td>{getValue("penyebab kematian")}</td></tr> <td>{getValue("tanggal kematian")}</td>
</tr>
<tr>
<td>Waktu Kematian</td>
<td>:</td>
<td>{getValue("waktu kematian")}</td>
</tr>
<tr>
<td>Tempat Kematian</td>
<td>:</td>
<td>{getValue("tempat kematian")}</td>
</tr>
<tr>
<td>Penyebab Kematian</td>
<td>:</td>
<td>{getValue("penyebab kematian")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya. Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat
digunakan sebagaimana mestinya.
</div> </div>
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ marginTop: "40px", display: "flex", justifyContent: "space-between", width: "100%" }}> <div
style={{
marginTop: "40px",
display: "flex",
justifyContent: "space-between",
width: "100%",
}}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<br /><br /> <br />
<br />
Pemohon Pemohon
<br /><br /><br /><br /> <br /> <br />
<br />
<br />
<br /> <br />
<u>{getValue("nama")}</u> <br /> <u>{getValue("nama")}</u> <br />
</div> </div>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<br /> <br />
Kepala Desa / Lurah {data.setting.desaNama} Kepala Desa / Lurah {data.setting.desaNama}
<br /><br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />
<br />
<u>{data.setting.perbekelNama}</u> <br /> <u>{data.setting.perbekelNama}</u> <br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -6,7 +6,8 @@ export default function SKPenghasilan({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (jenis: string) => const getValue = (jenis: string) =>
_.upperFirst( _.upperFirst(
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" data.surat.dataText.find((item: any) => item.jenis === jenis)?.value ||
"",
); );
const loadImage = async () => { const loadImage = async () => {
@@ -14,7 +15,9 @@ export default function SKPenghasilan({ data }: { data: any }) {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -40,16 +43,23 @@ export default function SKPenghasilan({ data }: { data: any }) {
<div style={{ lineHeight: "1.3" }}> <div style={{ lineHeight: "1.3" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "10px" }}> <div style={{ textAlign: "center", marginBottom: "10px" }}>
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br /> <b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br /> <b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
Alamat: {data.setting.desaAlamat}<br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
<br />
Alamat: {data.setting.desaAlamat}
<br />
Kode Pos: {data.setting.desaPos} Kode Pos: {data.setting.desaPos}
</div> </div>
{/* JUDUL */} {/* JUDUL */}
<div style={{ textAlign: "center", margin: "20px 0" }}> <div style={{ textAlign: "center", margin: "20px 0" }}>
<b><u>SURAT KETERANGAN PENGHASILAN</u></b><br /> <b>
<u>SURAT KETERANGAN PENGHASILAN</u>
</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -87,27 +97,47 @@ export default function SKPenghasilan({ data }: { data: any }) {
Dengan ini menerangkan bahwa: Dengan ini menerangkan bahwa:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Nama</td><td>:</td><td>{getValue("nama")}</td></tr> <tr>
<tr><td>Jenis Kelamin</td><td>:</td><td>{getValue("jenis kelamin")}</td></tr> <td style={{ width: "160px" }}>Nama</td>
<tr><td>Tempat / Tanggal Lahir</td><td>:</td><td>{getValue("tempat tanggal lahir")}</td></tr> <td>:</td>
<tr><td>Pekerjaan</td><td>:</td><td>{getValue("pekerjaan")}</td></tr> <td>{getValue("nama")}</td>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat")}</td></tr> </tr>
<tr>
<td>Jenis Kelamin</td>
<td>:</td>
<td>{getValue("jenis kelamin")}</td>
</tr>
<tr>
<td>Tempat / Tanggal Lahir</td>
<td>:</td>
<td>{getValue("tempat tanggal lahir")}</td>
</tr>
<tr>
<td>Pekerjaan</td>
<td>:</td>
<td>{getValue("pekerjaan")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
{/* PENGHASILAN */} {/* PENGHASILAN */}
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Berdasarkan keterangan yang bersangkutan, orang tersebut memiliki penghasilan rata-rata: Berdasarkan keterangan yang bersangkutan, orang tersebut memiliki
penghasilan rata-rata:
<table style={{ width: "100%", marginTop: "10px" }}> <table style={{ width: "100%", marginTop: "10px" }}>
<tbody> <tbody>
<tr> <tr>
<td style={{ width: "160px" }}>Penghasilan</td> <td style={{ width: "160px" }}>Penghasilan</td>
<td style={{ width: "10px" }}>:</td> <td style={{ width: "10px" }}>:</td>
<td> <td>
Rp {getValue("penghasilan")} Rp {getValue("penghasilan")} (
{" "} {getValue("penghasilan terbilang")}) per bulan
({getValue("penghasilan terbilang")}) per bulan
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -116,11 +146,13 @@ export default function SKPenghasilan({ data }: { data: any }) {
{/* KEPERLUAN */} {/* KEPERLUAN */}
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Surat keterangan ini dibuat untuk keperluan: <b>{getValue("alasan permohonan")}</b>. Surat keterangan ini dibuat untuk keperluan:{" "}
<b>{getValue("alasan permohonan")}</b>.
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat dipergunakan sebagaimana mestinya. Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat
dipergunakan sebagaimana mestinya.
</div> </div>
{/* TANGGAL & TANDA TANGAN */} {/* TANGGAL & TANDA TANGAN */}
@@ -129,11 +161,18 @@ export default function SKPenghasilan({ data }: { data: any }) {
Pada tanggal {data.surat.createdAt} Pada tanggal {data.surat.createdAt}
</div> </div>
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end" }}> <div
style={{
marginTop: "40px",
display: "flex",
justifyContent: "flex-end",
}}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
Kepala Desa / Lurah {data.setting.desaNama} Kepala Desa / Lurah {data.setting.desaNama}
<br /> <br /> <br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br /> <img src={viewImg || undefined} alt="ttd perbekel" width={100} />
<br />
<u>{data.setting.perbekelNama}</u> <br /> <u>{data.setting.perbekelNama}</u> <br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>

View File

@@ -5,14 +5,18 @@ import notification from "../notificationGlobal";
export default function SKTempatUsaha({ data }: { data: any }) { export default function SKTempatUsaha({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (key: string) => const getValue = (key: string) =>
_.upperFirst(data.surat.dataText.find((i: any) => i.jenis === key)?.value || ""); _.upperFirst(
data.surat.dataText.find((i: any) => i.jenis === key)?.value || "",
);
const loadImage = async () => { const loadImage = async () => {
try { try {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -34,12 +38,12 @@ export default function SKTempatUsaha({ data }: { data: any }) {
loadImage(); loadImage();
}, [data]); }, [data]);
return ( return (
<div style={{ lineHeight: "1.5" }}> <div style={{ lineHeight: "1.5" }}>
{/* TITLE */} {/* TITLE */}
<div style={{ textAlign: "center", marginBottom: "20px" }}> <div style={{ textAlign: "center", marginBottom: "20px" }}>
<b style={{ fontSize: "16px" }}>SURAT KETERANGAN TEMPAT USAHA</b><br /> <b style={{ fontSize: "16px" }}>SURAT KETERANGAN TEMPAT USAHA</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -63,30 +67,44 @@ export default function SKTempatUsaha({ data }: { data: any }) {
{/* DATA WARGA */} {/* DATA WARGA */}
<div> <div>
<Row label="Nama Pemilik Usaha" value={getValue("nama")} /> <Row label="Nama Pemilik Usaha" value={getValue("nama")} />
<Row label="Tempat/Tanggal Lahir" value={getValue("tempat tanggal lahir")} /> <Row
label="Tempat/Tanggal Lahir"
value={getValue("tempat tanggal lahir")}
/>
<Row label="Alamat Pemilik Usaha" value={getValue("alamat")} /> <Row label="Alamat Pemilik Usaha" value={getValue("alamat")} />
<Row label="Nomor KTP" value={getValue("nik")} /> <Row label="Nomor KTP" value={getValue("nik")} />
</div> </div>
<br /> <br />
<div>Benar yang bersangkutan memiliki tempat usaha dengan keterangan seperti berikut:</div> <div>
Benar yang bersangkutan memiliki tempat usaha dengan keterangan
seperti berikut:
</div>
<div> <div>
<Row label="Nama Usaha" value={getValue("nama usaha")} /> <Row label="Nama Usaha" value={getValue("nama usaha")} />
<Row label="Bidang Usaha" value={getValue("bidang usaha")} /> <Row label="Bidang Usaha" value={getValue("bidang usaha")} />
<Row label="Alamat Usaha" value={getValue("alamat usaha")} /> <Row label="Alamat Usaha" value={getValue("alamat usaha")} />
<Row label="Status Tempat Usaha" value={getValue("status tempat usaha")} /> <Row
<Row label="Luas Tempat Usaha" value={getValue("luas tempat usaha")} /> label="Status Tempat Usaha"
value={getValue("status tempat usaha")}
/>
<Row
label="Luas Tempat Usaha"
value={getValue("luas tempat usaha")}
/>
<Row label="Jumlah Karyawan" value={getValue("jumlah karyawan")} /> <Row label="Jumlah Karyawan" value={getValue("jumlah karyawan")} />
</div> </div>
<p style={{ textAlign: "justify" }}> <p style={{ textAlign: "justify" }}>
Surat keterangan ini dibuat untuk keperluan <b>{getValue("alasan permohonan")}.</b> Surat keterangan ini dibuat untuk keperluan{" "}
<b>{getValue("alasan permohonan")}.</b>
</p> </p>
<p style={{ textAlign: "justify" }}> <p style={{ textAlign: "justify" }}>
Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat dipergunakan sebagaimana mestinya. Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat
dipergunakan sebagaimana mestinya.
</p> </p>
<div> <div>
@@ -94,14 +112,19 @@ export default function SKTempatUsaha({ data }: { data: any }) {
<Row label="Pada tanggal" value={data.surat.createdAt} /> <Row label="Pada tanggal" value={data.surat.createdAt} />
</div> </div>
<br /><br /> <br />
<br />
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}> <div
style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
{data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br /> {data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br /> <img src={viewImg || undefined} alt="ttd perbekel" width={100} />
<u>{data.setting.perbekelNama}</u><br /> <br />
<u>{data.setting.perbekelNama}</u>
<br />
{data.setting.perbekelJabatan + " " + data.setting.desaNama} {data.setting.perbekelJabatan + " " + data.setting.desaNama}
</div> </div>
</div> </div>
@@ -110,7 +133,7 @@ export default function SKTempatUsaha({ data }: { data: any }) {
); );
} }
function Row({ label, value }: { label: string, value: string }) { function Row({ label, value }: { label: string; value: string }) {
return ( return (
<div style={{ display: "flex", marginBottom: "4px" }}> <div style={{ display: "flex", marginBottom: "4px" }}>
<div style={{ width: "180px" }}>{label}</div> <div style={{ width: "180px" }}>{label}</div>

View File

@@ -5,15 +5,18 @@ import notification from "../notificationGlobal";
export default function SKTidakMampu({ data }: { data: any }) { export default function SKTidakMampu({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (key: string) => const getValue = (key: string) =>
_.upperFirst(data.surat.dataText.find((i: any) => i.jenis === key)?.value || ""); _.upperFirst(
data.surat.dataText.find((i: any) => i.jenis === key)?.value || "",
);
const loadImage = async () => { const loadImage = async () => {
try { try {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -39,7 +42,8 @@ export default function SKTidakMampu({ data }: { data: any }) {
<div style={{ lineHeight: "1.5" }}> <div style={{ lineHeight: "1.5" }}>
{/* TITLE */} {/* TITLE */}
<div style={{ textAlign: "center", marginBottom: "20px" }}> <div style={{ textAlign: "center", marginBottom: "20px" }}>
<b style={{ fontSize: "16px" }}>SURAT KETERANGAN TIDAK MAMPU</b><br /> <b style={{ fontSize: "16px" }}>SURAT KETERANGAN TIDAK MAMPU</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -51,11 +55,9 @@ export default function SKTidakMampu({ data }: { data: any }) {
{/* DATA PEJABAT */} {/* DATA PEJABAT */}
<div> <div>
<Row label="Nama" value={data.setting.perbekelNama} /> <Row label="Nama" value={data.setting.perbekelNama} />
<Row label="Alamat" value={data.setting.desaAlamat} /> <Row label="Alamat" value={data.setting.desaAlamat} />
<Row label="Jabatan" value={data.setting.perbekelJabatan} /> <Row label="Jabatan" value={data.setting.perbekelJabatan} />
</div> </div>
<br /> <br />
@@ -64,35 +66,41 @@ export default function SKTidakMampu({ data }: { data: any }) {
{/* DATA WARGA */} {/* DATA WARGA */}
<div> <div>
<Row label="Nama" value={getValue("nama")} /> <Row label="Nama" value={getValue("nama")} />
<Row label="Tempat Tgl Lahir" value={getValue("tempat tanggal lahir")} /> <Row
label="Tempat Tgl Lahir"
value={getValue("tempat tanggal lahir")}
/>
<Row label="Alamat" value={getValue("alamat")} /> <Row label="Alamat" value={getValue("alamat")} />
<Row label="NIK" value={getValue("nik")} /> <Row label="NIK" value={getValue("nik")} />
</div> </div>
<br /> <br />
<p style={{ textAlign: "justify" }}> <p style={{ textAlign: "justify" }}>
Orang tersebut benar-benar penduduk desa {data.setting.desaNama} dan termasuk keluarga tidak mampu. Orang tersebut benar-benar penduduk desa {data.setting.desaNama} dan
Surat keterangan ini dipergunakan untuk termasuk keluarga tidak mampu. Surat keterangan ini dipergunakan untuk
<b>{getValue("alasan permohonan")}.</b> <b>{getValue("alasan permohonan")}.</b>
</p> </p>
<p style={{ textAlign: "justify" }}> <p style={{ textAlign: "justify" }}>
Demikian surat keterangan ini kami buat dengan sebenar-benarnya untuk dapat dipergunakan Demikian surat keterangan ini kami buat dengan sebenar-benarnya untuk
sebagaimana mestinya. dapat dipergunakan sebagaimana mestinya.
</p> </p>
<br /><br /> <br />
<br />
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}> <div
style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
{data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br /> {data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br /> <img src={viewImg || undefined} alt="ttd perbekel" width={100} />
<u>{data.setting.perbekelNama}</u><br /> <br />
<u>{data.setting.perbekelNama}</u>
<br />
{data.setting.perbekelJabatan + " " + data.setting.desaNama} {data.setting.perbekelJabatan + " " + data.setting.desaNama}
</div> </div>
</div> </div>
@@ -101,7 +109,7 @@ export default function SKTidakMampu({ data }: { data: any }) {
); );
} }
function Row({ label, value }: { label: string, value: string }) { function Row({ label, value }: { label: string; value: string }) {
return ( return (
<div style={{ display: "flex", marginBottom: "4px" }}> <div style={{ display: "flex", marginBottom: "4px" }}>
<div style={{ width: "180px" }}>{label}</div> <div style={{ width: "180px" }}>{label}</div>

View File

@@ -6,7 +6,8 @@ export default function SKUsaha({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (jenis: string) => const getValue = (jenis: string) =>
_.upperFirst( _.upperFirst(
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || "" data.surat.dataText.find((item: any) => item.jenis === jenis)?.value ||
"",
); );
const loadImage = async () => { const loadImage = async () => {
@@ -14,7 +15,9 @@ export default function SKUsaha({ data }: { data: any }) {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -40,16 +43,23 @@ export default function SKUsaha({ data }: { data: any }) {
<div style={{ lineHeight: "1.3" }}> <div style={{ lineHeight: "1.3" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "10px" }}> <div style={{ textAlign: "center", marginBottom: "10px" }}>
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br /> <b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br /> <b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
Alamat: {data.setting.desaAlamat}<br /> <br />
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
<br />
Alamat: {data.setting.desaAlamat}
<br />
Kode Pos: {data.setting.desaPos} Kode Pos: {data.setting.desaPos}
</div> </div>
{/* JUDUL */} {/* JUDUL */}
<div style={{ textAlign: "center", margin: "15px 0" }}> <div style={{ textAlign: "center", margin: "15px 0" }}>
<b><u>SURAT KETERANGAN USAHA</u></b><br /> <b>
<u>SURAT KETERANGAN USAHA</u>
</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -87,14 +97,46 @@ export default function SKUsaha({ data }: { data: any }) {
Dengan ini menerangkan dengan sesungguhnya bahwa: Dengan ini menerangkan dengan sesungguhnya bahwa:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></tr> <tr>
<tr><td>Jenis Kelamin</td><td>:</td><td>{getValue("jenis kelamin")}</td></tr> <td style={{ width: "160px" }}>Nama</td>
<tr><td>Tempat / Tanggal Lahir</td><td>:</td><td>{getValue("tempat tanggal lahir")}</td></tr> <td style={{ width: "10px" }}>:</td>
<tr><td>Warga Negara</td><td>:</td><td>{getValue("negara")}</td></tr> <td>{getValue("nama")}</td>
<tr><td>Agama</td><td>:</td><td>{getValue("agama")}</td></tr> </tr>
<tr><td>Status</td><td>:</td><td>{getValue("status perkawinan")}</td></tr> <tr>
<tr><td>Pekerjaan</td><td>:</td><td>{getValue("pekerjaan")}</td></tr> <td>Jenis Kelamin</td>
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat")}</td></tr> <td>:</td>
<td>{getValue("jenis kelamin")}</td>
</tr>
<tr>
<td>Tempat / Tanggal Lahir</td>
<td>:</td>
<td>{getValue("tempat tanggal lahir")}</td>
</tr>
<tr>
<td>Warga Negara</td>
<td>:</td>
<td>{getValue("negara")}</td>
</tr>
<tr>
<td>Agama</td>
<td>:</td>
<td>{getValue("agama")}</td>
</tr>
<tr>
<td>Status</td>
<td>:</td>
<td>{getValue("status perkawinan")}</td>
</tr>
<tr>
<td>Pekerjaan</td>
<td>:</td>
<td>{getValue("pekerjaan")}</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
<td>{getValue("alamat")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -104,9 +146,21 @@ export default function SKUsaha({ data }: { data: any }) {
Bahwa orang tersebut di atas benar-benar penduduk: Bahwa orang tersebut di atas benar-benar penduduk:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Desa / Kelurahan</td><td style={{ width: "10px" }}>:</td><td>{data.setting.desaNama}</td></tr> <tr>
<tr><td>Kecamatan</td><td>:</td><td>{data.setting.desaKecamatan}</td></tr> <td style={{ width: "160px" }}>Desa / Kelurahan</td>
<tr><td>Kabupaten</td><td>:</td><td>{data.setting.desaKabupaten}</td></tr> <td style={{ width: "10px" }}>:</td>
<td>{data.setting.desaNama}</td>
</tr>
<tr>
<td>Kecamatan</td>
<td>:</td>
<td>{data.setting.desaKecamatan}</td>
</tr>
<tr>
<td>Kabupaten</td>
<td>:</td>
<td>{data.setting.desaKabupaten}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -116,14 +170,23 @@ export default function SKUsaha({ data }: { data: any }) {
Dan yang bersangkutan benar memiliki usaha: Dan yang bersangkutan benar memiliki usaha:
<table style={{ width: "100%", marginTop: "5px" }}> <table style={{ width: "100%", marginTop: "5px" }}>
<tbody> <tbody>
<tr><td style={{ width: "160px" }}>Jenis Usaha</td><td style={{ width: "10px" }}>:</td><td>{getValue("jenis usaha")}</td></tr> <tr>
<tr><td>Alamat Usaha</td><td>:</td><td>{getValue("alamat usaha")}</td></tr> <td style={{ width: "160px" }}>Jenis Usaha</td>
<td style={{ width: "10px" }}>:</td>
<td>{getValue("jenis usaha")}</td>
</tr>
<tr>
<td>Alamat Usaha</td>
<td>:</td>
<td>{getValue("alamat usaha")}</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
Surat keterangan ini dibuat dengan sebenarnya untuk dipergunakan sebagaimana mestinya. Surat keterangan ini dibuat dengan sebenarnya untuk dipergunakan
sebagaimana mestinya.
</div> </div>
<div style={{ marginTop: "20px" }}> <div style={{ marginTop: "20px" }}>
@@ -132,18 +195,25 @@ export default function SKUsaha({ data }: { data: any }) {
</div> </div>
{/* TANDA TANGAN */} {/* TANDA TANGAN */}
<div style={{ marginTop: "10px", display: "flex", justifyContent: "flex-end", width: "100%" }}> <div
style={{
marginTop: "10px",
display: "flex",
justifyContent: "flex-end",
width: "100%",
}}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<br /> <br />
Kepala Desa / Lurah {data.setting.desaNama} Kepala Desa / Lurah {data.setting.desaNama}
<br /><br /> <br />
<br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <img src={viewImg || undefined} alt="ttd perbekel" width={100} />
<br /> <br />
<u>{data.setting.perbekelNama}</u> <br /> <u>{data.setting.perbekelNama}</u> <br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -6,14 +6,18 @@ import notification from "../notificationGlobal";
export default function SKYatim({ data }: { data: any }) { export default function SKYatim({ data }: { data: any }) {
const [viewImg, setViewImg] = useState<string>(""); const [viewImg, setViewImg] = useState<string>("");
const getValue = (key: string) => const getValue = (key: string) =>
_.upperFirst(data.surat.dataText.find((i: any) => i.jenis === key)?.value || ""); _.upperFirst(
data.surat.dataText.find((i: any) => i.jenis === key)?.value || "",
);
const loadImage = async () => { const loadImage = async () => {
try { try {
setViewImg(""); setViewImg("");
if (!data.setting.perbekelTTD) return; if (!data.setting.perbekelTTD) return;
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD; const urlApi =
"/api/pengaduan/image?folder=lainnya&fileName=" +
data.setting.perbekelTTD;
// Fetch manual agar mendapatkan Response asli // Fetch manual agar mendapatkan Response asli
const res = await fetch(urlApi); const res = await fetch(urlApi);
if (!res.ok) if (!res.ok)
@@ -37,17 +41,22 @@ export default function SKYatim({ data }: { data: any }) {
return ( return (
<div style={{ lineHeight: "1.3" }}> <div style={{ lineHeight: "1.3" }}>
{/* HEADER */} {/* HEADER */}
<div style={{ textAlign: "center", marginBottom: "10px" }}> <div style={{ textAlign: "center", marginBottom: "10px" }}>
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br /> <b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br /> <br />
<b>DESA {_.upperCase(data.setting.desaNama)}</b><br /> <b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
<br />
<b>DESA {_.upperCase(data.setting.desaNama)}</b>
<br />
Alamat: {data.setting.desaAlamat}. Kode Pos: {data.setting.desaPos} Alamat: {data.setting.desaAlamat}. Kode Pos: {data.setting.desaPos}
</div> </div>
<div style={{ textAlign: "center", marginTop: "15px" }}> <div style={{ textAlign: "center", marginTop: "15px" }}>
<b><u>SURAT KETERANGAN YATIM / PIATU / YATIM PIATU</u></b><br /> <b>
<u>SURAT KETERANGAN YATIM / PIATU / YATIM PIATU</u>
</b>
<br />
Nomor: {data.surat.noSurat} Nomor: {data.surat.noSurat}
</div> </div>
@@ -110,13 +119,16 @@ export default function SKYatim({ data }: { data: any }) {
{/* KETERANGAN ORANG TUA */} {/* KETERANGAN ORANG TUA */}
<div> <div>
Benar bahwa yang bersangkutan adalah <b>anak (Yatim / Piatu / Yatim Piatu)</b>, Benar bahwa yang bersangkutan adalah{" "}
dengan keterangan sebagai berikut: <b>anak (Yatim / Piatu / Yatim Piatu)</b>, dengan keterangan sebagai
berikut:
</div> </div>
<br /> <br />
<div><b>1. Nama Ayah</b></div> <div>
<b>1. Nama Ayah</b>
</div>
<table style={{ width: "100%" }}> <table style={{ width: "100%" }}>
<tbody> <tbody>
<tr> <tr>
@@ -132,7 +144,9 @@ export default function SKYatim({ data }: { data: any }) {
<br /> <br />
<div><b>2. Nama Ibu</b></div> <div>
<b>2. Nama Ibu</b>
</div>
<table style={{ width: "100%" }}> <table style={{ width: "100%" }}>
<tbody> <tbody>
<tr> <tr>
@@ -149,15 +163,16 @@ export default function SKYatim({ data }: { data: any }) {
<br /> <br />
<div> <div>
Dengan demikian, berdasarkan keterangan pihak keluarga dan data di Kantor Desa, Dengan demikian, berdasarkan keterangan pihak keluarga dan data di
maka benar bahwa yang bersangkutan adalah Kantor Desa, maka benar bahwa yang bersangkutan adalah
<b> anak (Yatim / Piatu / Yatim Piatu).</b> <b> anak (Yatim / Piatu / Yatim Piatu).</b>
</div> </div>
<br /> <br />
<div> <div>
Surat keterangan ini dibuat dengan sebenar-benarnya untuk dipergunakan sebagaimana mestinya. Surat keterangan ini dibuat dengan sebenar-benarnya untuk dipergunakan
sebagaimana mestinya.
</div> </div>
<br /> <br />
@@ -179,16 +194,19 @@ export default function SKYatim({ data }: { data: any }) {
<br /> <br />
{/* TTD */} {/* TTD */}
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}> <div
style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}
>
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
Kepala Desa {data.setting.desaNama} Kepala Desa {data.setting.desaNama}
<br /><br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br /> <br />
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
<br />
<u>{data.setting.perbekelNama}</u> <br /> <u>{data.setting.perbekelNama}</u> <br />
NIP. {data.setting.perbekelNIP} NIP. {data.setting.perbekelNIP}
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -28,16 +28,19 @@ export default function Login() {
window.location.href = clientRoutes["/scr/dashboard/warga/list-warga"]; window.location.href = clientRoutes["/scr/dashboard/warga/list-warga"];
break; break;
case "credential": case "credential":
window.location.href = clientRoutes["/scr/dashboard/credential/credential"]; window.location.href =
clientRoutes["/scr/dashboard/credential/credential"];
break; break;
case "setting": case "setting":
window.location.href = clientRoutes["/scr/dashboard/setting/detail-setting"]; window.location.href =
clientRoutes["/scr/dashboard/setting/detail-setting"];
break; break;
case "api_key": case "api_key":
window.location.href = clientRoutes["/scr/dashboard/apikey/apikey"]; window.location.href = clientRoutes["/scr/dashboard/apikey/apikey"];
break; break;
case "pelayanan": case "pelayanan":
window.location.href = clientRoutes["/scr/dashboard/pelayanan-surat/list-pelayanan"]; window.location.href =
clientRoutes["/scr/dashboard/pelayanan-surat/list-pelayanan"];
break; break;
default: default:
window.location.href = clientRoutes["/scr/dashboard"]; window.location.href = clientRoutes["/scr/dashboard"];

View 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>
);
}

View File

@@ -9,7 +9,7 @@ import {
Progress, Progress,
Stack, Stack,
Text, Text,
Title Title,
} from "@mantine/core"; } from "@mantine/core";
export default function Dashboard() { export default function Dashboard() {

View File

@@ -285,7 +285,9 @@ function NavigationDashboard() {
return ( return (
<Stack gap="xs" p="sm"> <Stack gap="xs" p="sm">
{navItems.filter((item) => permissions.includes(item.key)).map((item) => ( {navItems
.filter((item) => permissions.includes(item.key))
.map((item) => (
<NavLink <NavLink
key={item.path} key={item.path}
active={isActive(item.path as keyof typeof clientRoute)} active={isActive(item.path as keyof typeof clientRoute)}

View File

@@ -19,7 +19,7 @@ import {
Text, Text,
Textarea, Textarea,
ThemeIcon, ThemeIcon,
Title Title,
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { import {
@@ -29,7 +29,7 @@ import {
IconFileCheck, IconFileCheck,
IconMessageReport, IconMessageReport,
IconPhone, IconPhone,
IconUser IconUser,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import type { User } from "generated/prisma"; import type { User } from "generated/prisma";
import type { JsonValue } from "generated/prisma/runtime/library"; import type { JsonValue } from "generated/prisma/runtime/library";
@@ -59,7 +59,14 @@ export default function DetailPengajuanPage() {
<Grid> <Grid>
<Grid.Col span={8}> <Grid.Col span={8}>
<Stack gap={"xl"}> <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} /> <DetailDataHistori data={data?.data?.history} />
</Stack> </Stack>
</Grid.Col> </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 [opened, { open, close }] = useDisclosure(false);
const [catModal, setCatModal] = useState<"tolak" | "terima">("tolak"); const [catModal, setCatModal] = useState<"tolak" | "terima">("tolak");
const [keterangan, setKeterangan] = useState(""); const [keterangan, setKeterangan] = useState("");
@@ -88,7 +105,9 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
setHost(data?.user ?? null); setHost(data?.user ?? null);
if (data?.permissions && Array.isArray(data.permissions)) { 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); setPermissions(onlySetting);
} }
} }
@@ -99,10 +118,15 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
try { try {
const res = await apiFetch.api.pelayanan["update-status"].post({ const res = await apiFetch.api.pelayanan["update-status"].post({
id: data?.id, id: data?.id,
status: cat == 'tolak' ? 'ditolak' : data.status == 'antrian' ? 'diterima' : 'selesai', status:
cat == "tolak"
? "ditolak"
: data.status == "antrian"
? "diterima"
: "selesai",
keterangan: keterangan, keterangan: keterangan,
idUser: host?.id ?? "", idUser: host?.id ?? "",
noSurat: noSurat noSurat: noSurat,
}); });
if (res?.status === 200) { if (res?.status === 200) {
@@ -120,7 +144,6 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
type: "error", type: "error",
}); });
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
notification({ notification({
@@ -129,7 +152,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
type: "error", type: "error",
}); });
} }
} };
useShallowEffect(() => { useShallowEffect(() => {
if (viewImg) { if (viewImg) {
@@ -142,7 +165,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
<ModalFile <ModalFile
open={openedPreviewFile && !_.isEmpty(viewImg)} open={openedPreviewFile && !_.isEmpty(viewImg)}
onClose={() => { onClose={() => {
setOpenedPreviewFile(false) setOpenedPreviewFile(false);
}} }}
folder="syarat-dokumen" folder="syarat-dokumen"
fileName={viewImg} fileName={viewImg}
@@ -159,14 +182,25 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
{catModal === "tolak" ? ( {catModal === "tolak" ? (
<> <>
<Text> <Text>
Anda yakin ingin menolak pengajuan surat ini? Berikan alasan penolakan Anda yakin ingin menolak pengajuan surat ini? Berikan alasan
penolakan
</Text> </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> <Group justify="center" grow>
<Button variant="light" onClick={close}> <Button variant="light" onClick={close}>
Batal Batal
</Button> </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 Tolak
</Button> </Button>
</Group> </Group>
@@ -174,21 +208,31 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
) : ( ) : (
<> <>
<Text> <Text>
Anda yakin ingin {data?.status == 'antrian' ? 'menerima' : 'menyetujui'} pengajuan surat ini? Anda yakin ingin{" "}
{ {data?.status == "antrian" ? "menerima" : "menyetujui"}{" "}
data.status == 'diterima' && 'Masukkan nomer surat yang akan dibuat' pengajuan surat ini?
} {data.status == "diterima" &&
"Masukkan nomer surat yang akan dibuat"}
</Text> </Text>
{ {data.status == "diterima" && (
data.status == 'diterima' && ( <Textarea
<Textarea size="md" minRows={5} value={noSurat} onChange={(e) => setNoSurat(e.target.value)} placeholder="Contoh : 08/D-IV/11/2025" /> size="md"
) minRows={5}
} value={noSurat}
onChange={(e) => setNoSurat(e.target.value)}
placeholder="Contoh : 08/D-IV/11/2025"
/>
)}
<Group justify="center" grow> <Group justify="center" grow>
<Button variant="light" onClick={close}> <Button variant="light" onClick={close}>
Tidak Tidak
</Button> </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 Ya
</Button> </Button>
</Group> </Group>
@@ -196,11 +240,13 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
)} )}
</Stack> </Stack>
</Modal> </Modal>
{ {data?.status == "selesai" && (
data?.status == "selesai" && <ModalSurat
(<ModalSurat open={openedPreview} onClose={() => setOpenedPreview(false)} surat={data?.idSurat} />) open={openedPreview}
} onClose={() => setOpenedPreview(false)}
surat={data?.idSurat}
/>
)}
<Card <Card
radius="md" radius="md"
@@ -263,7 +309,11 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
> >
{syaratDokumen?.map((v: any) => ( {syaratDokumen?.map((v: any) => (
<List.Item key={v.id}> <List.Item key={v.id}>
<Anchor onClick={() => { setViewImg(v.value) }}> <Anchor
onClick={() => {
setViewImg(v.value);
}}
>
{v.jenis} {v.jenis}
</Anchor> </Anchor>
</List.Item> </List.Item>
@@ -271,8 +321,6 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
</List> </List>
</Flex> </Flex>
<Flex direction={"column"} justify="flex-start"> <Flex direction={"column"} justify="flex-start">
<Group gap="xs"> <Group gap="xs">
<IconAlignJustified size={20} /> <IconAlignJustified size={20} />
@@ -281,23 +329,26 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
<Table withRowBorders={false}> <Table withRowBorders={false}>
<Table.Tbody> <Table.Tbody>
{ {dataText?.map((item: any) => (
dataText?.map((item: any) => (
<Table.Tr key={item.id}> <Table.Tr key={item.id}>
<Table.Td style={{ whiteSpace: "nowrap", width: "10%" }}>{_.upperFirst(item.jenis)}</Table.Td> <Table.Td
style={{ whiteSpace: "nowrap", width: "10%" }}
>
{_.upperFirst(item.jenis)}
</Table.Td>
<Table.Td>:</Table.Td> <Table.Td>:</Table.Td>
<Table.Td style={{ width: "85%" }}>{_.upperFirst(item.value)}</Table.Td> <Table.Td style={{ width: "85%" }}>
{_.upperFirst(item.value)}
</Table.Td>
</Table.Tr> </Table.Tr>
)) ))}
}
</Table.Tbody> </Table.Tbody>
</Table> </Table>
</Flex> </Flex>
</Stack> </Stack>
</Grid.Col> </Grid.Col>
<Grid.Col span={12}> <Grid.Col span={12}>
{ {data?.status === "antrian" ? (
data?.status === "antrian" ? (
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
disabled={!permissions.includes("pelayanan.antrian.tolak")} disabled={!permissions.includes("pelayanan.antrian.tolak")}
@@ -333,7 +384,9 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
Tolak Tolak
</Button> </Button>
<Button <Button
disabled={!permissions.includes("pelayanan.diterima.setujui")} disabled={
!permissions.includes("pelayanan.diterima.setujui")
}
variant="filled" variant="filled"
onClick={() => { onClick={() => {
setCatModal("terima"); setCatModal("terima");
@@ -343,9 +396,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
Setujui Setujui
</Button> </Button>
</Group> </Group>
) : ) : data?.status === "selesai" ? (
data?.status === "selesai" ?
(
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
variant="light" variant="light"
@@ -354,9 +405,9 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
Surat Surat
</Button> </Button>
</Group> </Group>
) ) : (
: <></> <></>
} )}
</Grid.Col> </Grid.Col>
</Grid> </Grid>
</Stack> </Stack>
@@ -395,26 +446,25 @@ function DetailDataHistori({ data }: { data: any }) {
</Table.Tr> </Table.Tr>
</Table.Thead> </Table.Thead>
<Table.Tbody> <Table.Tbody>
{ {data?.map((item: any) => (
data?.map((item: any) => (
<Table.Tr key={item.id}> <Table.Tr key={item.id}>
<Table.Td style={{ whiteSpace: "nowrap" }}> <Table.Td style={{ whiteSpace: "nowrap" }}>
{ {item.createdAt.toLocaleString("id-ID", {
item.createdAt.toLocaleString("id-ID", {
day: "2-digit", day: "2-digit",
month: "short", month: "short",
year: "numeric", year: "numeric",
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
hour12: false hour12: false,
}) })}
}</Table.Td> </Table.Td>
<Table.Td>{item.deskripsi}</Table.Td> <Table.Td>{item.deskripsi}</Table.Td>
<Table.Td>{item.status}</Table.Td> <Table.Td>{item.status}</Table.Td>
<Table.Td style={{ whiteSpace: "nowrap" }}>{item.nameUser ? item.nameUser : "-"}</Table.Td> <Table.Td style={{ whiteSpace: "nowrap" }}>
{item.nameUser ? item.nameUser : "-"}
</Table.Td>
</Table.Tr> </Table.Tr>
)) ))}
}
</Table.Tbody> </Table.Tbody>
</Table> </Table>
</Stack> </Stack>

View File

@@ -20,7 +20,7 @@ import {
IconClockHour3, IconClockHour3,
IconFileSad, IconFileSad,
IconSearch, IconSearch,
IconUser IconUser,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import { useState } from "react"; import { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
@@ -123,7 +123,7 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
take: "", take: "",
page: page.toString(), page: page.toString(),
}, },
}) }),
); );
useShallowEffect(() => { useShallowEffect(() => {
@@ -131,12 +131,10 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
mutate(); mutate();
}, [status, value]); }, [status, value]);
useShallowEffect(() => { useShallowEffect(() => {
mutate(); mutate();
}, [page]); }, [page]);
useShallowEffect(() => { useShallowEffect(() => {
const unsubscribe = subscribe(state, () => mutate()); const unsubscribe = subscribe(state, () => mutate());
return () => unsubscribe(); return () => unsubscribe();
@@ -189,8 +187,16 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
</Grid.Col> </Grid.Col>
<Grid.Col span={3}> <Grid.Col span={3}>
<Group justify="flex-end"> <Group justify="flex-end">
<Text size="sm" c="gray.5">{`${pageSize * (page - 1) + 1} ${Math.min(total, pageSize * page)} of ${total}`}</Text> <Text
<Pagination total={totalPage} value={page} onChange={setPage} withPages={false} /> 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> </Group>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
@@ -204,7 +210,8 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
</Stack> </Stack>
</Flex> </Flex>
) : ( ) : (
Array.isArray(list) && list?.map((v: any) => ( Array.isArray(list) &&
list?.map((v: any) => (
<Card <Card
key={v.id} key={v.id}
radius="lg" radius="lg"
@@ -266,7 +273,13 @@ function ListPelayananSurat({ status }: { status: StatusKey }) {
Tanggal Ajuan Tanggal Ajuan
</Text> </Text>
</Group> </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>
<Flex direction={"column"} justify="flex-start"> <Flex direction={"column"} justify="flex-start">
<Group gap="xs"> <Group gap="xs">

View File

@@ -16,7 +16,7 @@ import {
Table, Table,
Text, Text,
Textarea, Textarea,
Title Title,
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { import {
@@ -58,7 +58,12 @@ export default function DetailPengaduanPage() {
<Grid> <Grid>
<Grid.Col span={8}> <Grid.Col span={8}>
<Stack gap={"xl"}> <Stack gap={"xl"}>
<DetailDataPengaduan data={data?.data?.pengaduan} onAction={() => { mutate(); }} /> <DetailDataPengaduan
data={data?.data?.pengaduan}
onAction={() => {
mutate();
}}
/>
<DetailDataHistori data={data?.data?.history} /> <DetailDataHistori data={data?.data?.history} />
</Stack> </Stack>
</Grid.Col> </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 [opened, { open, close }] = useDisclosure(false);
const [catModal, setCatModal] = useState<"tolak" | "terima">("tolak"); const [catModal, setCatModal] = useState<"tolak" | "terima">("tolak");
const [openedPreview, setOpenedPreview] = useState(false); const [openedPreview, setOpenedPreview] = useState(false);
@@ -84,7 +95,9 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
setHost(data?.user ?? null); setHost(data?.user ?? null);
if (data?.permissions && Array.isArray(data.permissions)) { 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); setPermissions(onlySetting);
} }
} }
@@ -95,9 +108,16 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
try { try {
const res = await apiFetch.api.pengaduan["update-status"].post({ const res = await apiFetch.api.pengaduan["update-status"].post({
id: data?.id, 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, keterangan: keterangan,
idUser: host?.id ?? "" idUser: host?.id ?? "",
}); });
if (res?.status === 200) { if (res?.status === 200) {
@@ -115,7 +135,6 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
type: "error", type: "error",
}); });
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
notification({ notification({
@@ -124,11 +143,10 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
type: "error", type: "error",
}); });
} }
} };
return ( return (
<> <>
{/* MODAL KONFIRMASI */} {/* MODAL KONFIRMASI */}
<Modal <Modal
opened={opened} opened={opened}
@@ -143,24 +161,46 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
Anda yakin ingin menolak pengaduan ini? Berikan alasan penolakan Anda yakin ingin menolak pengaduan ini? Berikan alasan penolakan
</Text> </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> <Group justify="center" grow>
<Button variant="light" onClick={close}> <Button variant="light" onClick={close}>
Batal Batal
</Button> </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 Tolak
</Button> </Button>
</Group> </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> <Group justify="center" grow>
<Button variant="light" onClick={close}> <Button variant="light" onClick={close}>
Tidak Tidak
</Button> </Button>
<Button variant="filled" color="green" onClick={() => handleKonfirmasi("terima")}> <Button
variant="filled"
color="green"
onClick={() => handleKonfirmasi("terima")}
>
Ya Ya
</Button> </Button>
</Group> </Group>
@@ -169,7 +209,6 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
</Stack> </Stack>
</Modal> </Modal>
{/* MODAL GAMBAR */} {/* MODAL GAMBAR */}
<ModalFile <ModalFile
open={openedPreview && !_.isEmpty(data?.image)} open={openedPreview && !_.isEmpty(data?.image)}
@@ -259,18 +298,20 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
<IconPhotoScan size={20} /> <IconPhotoScan size={20} />
<Text size="md">Gambar</Text> <Text size="md">Gambar</Text>
</Group> </Group>
{ {data?.image != null && data?.image != "" ? (
data?.image != null && data?.image != "" <Anchor
? href="#"
<Anchor href="#" onClick={() => { setOpenedPreview(true) }}> onClick={() => {
setOpenedPreview(true);
}}
>
Lihat Gambar Lihat Gambar
</Anchor> </Anchor>
: ) : (
<Text size="md" c="white"> <Text size="md" c="white">
- -
</Text> </Text>
} )}
</Flex> </Flex>
</Stack> </Stack>
</Grid.Col> </Grid.Col>
@@ -285,8 +326,7 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
{_.upperFirst(data?.detail)} {_.upperFirst(data?.detail)}
</Text> </Text>
</Flex> </Flex>
{ {data?.keterangan && (
data?.keterangan && (
<Flex direction={"column"} justify="flex-start"> <Flex direction={"column"} justify="flex-start">
<Group gap="xs"> <Group gap="xs">
<IconInfoTriangle size={20} /> <IconInfoTriangle size={20} />
@@ -296,13 +336,11 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
{_.upperFirst(data?.keterangan)} {_.upperFirst(data?.keterangan)}
</Text> </Text>
</Flex> </Flex>
) )}
}
</Stack> </Stack>
</Grid.Col> </Grid.Col>
<Grid.Col span={12}> <Grid.Col span={12}>
{ {data?.status === "antrian" ? (
data?.status === "antrian" ? (
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
variant="light" variant="light"
@@ -329,7 +367,9 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
variant="filled" variant="filled"
disabled={!permissions.includes("pengaduan.diterima.dikerjakan")} disabled={
!permissions.includes("pengaduan.diterima.dikerjakan")
}
onClick={() => { onClick={() => {
setCatModal("terima"); setCatModal("terima");
open(); open();
@@ -342,7 +382,9 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
<Group justify="center" grow> <Group justify="center" grow>
<Button <Button
variant="filled" variant="filled"
disabled={!permissions.includes("pengaduan.dikerjakan.selesai")} disabled={
!permissions.includes("pengaduan.dikerjakan.selesai")
}
onClick={() => { onClick={() => {
setCatModal("terima"); setCatModal("terima");
open(); open();
@@ -351,8 +393,9 @@ function DetailDataPengaduan({ data, onAction }: { data: any | null, onAction: (
Selesai Selesai
</Button> </Button>
</Group> </Group>
) : <></> ) : (
} <></>
)}
</Grid.Col> </Grid.Col>
</Grid> </Grid>
</Stack> </Stack>
@@ -391,25 +434,25 @@ function DetailDataHistori({ data }: { data: any }) {
</Table.Tr> </Table.Tr>
</Table.Thead> </Table.Thead>
<Table.Tbody> <Table.Tbody>
{ {data?.map((item: any) => (
data?.map((item: any) => (
<Table.Tr key={item.id}> <Table.Tr key={item.id}>
<Table.Td style={{ whiteSpace: "nowrap" }}>{ <Table.Td style={{ whiteSpace: "nowrap" }}>
item.createdAt.toLocaleString("id-ID", { {item.createdAt.toLocaleString("id-ID", {
day: "2-digit", day: "2-digit",
month: "short", month: "short",
year: "numeric", year: "numeric",
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
hour12: false hour12: false,
}) })}
}</Table.Td> </Table.Td>
<Table.Td>{item.deskripsi}</Table.Td> <Table.Td>{item.deskripsi}</Table.Td>
<Table.Td>{item.status}</Table.Td> <Table.Td>{item.status}</Table.Td>
<Table.Td style={{ whiteSpace: "nowrap" }}>{item.nameUser ? item.nameUser : "-"}</Table.Td> <Table.Td style={{ whiteSpace: "nowrap" }}>
{item.nameUser ? item.nameUser : "-"}
</Table.Td>
</Table.Tr> </Table.Tr>
)) ))}
}
</Table.Tbody> </Table.Tbody>
</Table> </Table>
</Stack> </Stack>

View File

@@ -134,7 +134,7 @@ function ListPengaduan({ status }: { status: StatusKey }) {
take: "", take: "",
page: page.toString(), page: page.toString(),
}, },
}) }),
); );
useShallowEffect(() => { useShallowEffect(() => {
@@ -175,7 +175,6 @@ function ListPengaduan({ status }: { status: StatusKey }) {
const pageNow = data?.data?.page || 1; const pageNow = data?.data?.page || 1;
const toDate = (d: any) => new Date(d); const toDate = (d: any) => new Date(d);
return ( return (
<Stack gap="xl"> <Stack gap="xl">
<Grid> <Grid>
@@ -197,8 +196,16 @@ function ListPengaduan({ status }: { status: StatusKey }) {
</Grid.Col> </Grid.Col>
<Grid.Col span={3}> <Grid.Col span={3}>
<Group justify="flex-end"> <Group justify="flex-end">
<Text size="sm" c="gray.5">{`${pageSize * (page - 1) + 1} ${Math.min(total, pageSize * page)} of ${total}`}</Text> <Text
<Pagination total={totalPage} value={page} onChange={setPage} withPages={false} /> 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> </Group>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
@@ -212,7 +219,8 @@ function ListPengaduan({ status }: { status: StatusKey }) {
</Stack> </Stack>
</Flex> </Flex>
) : ( ) : (
Array.isArray(list) && list?.map((v: any) => ( Array.isArray(list) &&
list?.map((v: any) => (
<Card <Card
key={v.id} key={v.id}
radius="lg" radius="lg"
@@ -272,7 +280,13 @@ function ListPengaduan({ status }: { status: StatusKey }) {
Tanggal Aduan Tanggal Aduan
</Text> </Text>
</Group> </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>
<Flex direction={"column"} justify="flex-start"> <Flex direction={"column"} justify="flex-start">
<Group gap="xs"> <Group gap="xs">

View File

@@ -5,19 +5,14 @@ import ProfileUser from "@/components/ProfileUser";
import UserRoleSetting from "@/components/UserRoleSetting"; import UserRoleSetting from "@/components/UserRoleSetting";
import UserSetting from "@/components/UserSetting"; import UserSetting from "@/components/UserSetting";
import apiFetch from "@/lib/apiFetch"; import apiFetch from "@/lib/apiFetch";
import { import { Card, Container, Grid, NavLink } from "@mantine/core";
Card,
Container,
Grid,
NavLink
} from "@mantine/core";
import { import {
IconBuildingBank, IconBuildingBank,
IconCategory2, IconCategory2,
IconMailSpark, IconMailSpark,
IconUserCog, IconUserCog,
IconUserScreen, IconUserScreen,
IconUsersGroup IconUsersGroup,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import type { JsonValue } from "generated/prisma/runtime/library"; import type { JsonValue } from "generated/prisma/runtime/library";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@@ -33,7 +28,9 @@ export default function DetailSettingPage() {
async function fetchPermissions() { async function fetchPermissions() {
const { data } = await apiFetch.api.user.find.get(); const { data } = await apiFetch.api.user.find.get();
if (Array.isArray(data?.permissions)) { 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); setPermissions(onlySetting);
} else { } else {
setPermissions([]); setPermissions([]);
@@ -42,7 +39,6 @@ export default function DetailSettingPage() {
fetchPermissions(); fetchPermissions();
}, []); }, []);
const navItems = [ const navItems = [
{ {
key: "setting.profile", key: "setting.profile",
@@ -85,8 +81,7 @@ export default function DetailSettingPage() {
icon: <IconBuildingBank size={20} />, icon: <IconBuildingBank size={20} />,
label: "Desa", label: "Desa",
description: "Manage desa information", description: "Manage desa information",
} },
]; ];
return ( return (
@@ -104,17 +99,19 @@ export default function DetailSettingPage() {
boxShadow: "0 0 20px rgba(0,255,200,0.08)", boxShadow: "0 0 20px rgba(0,255,200,0.08)",
}} }}
> >
{ {navItems
navItems.filter((item) => permissions.includes(item.key)).map((item) => ( .filter((item) => permissions.includes(item.key))
.map((item) => (
<NavLink <NavLink
key={item.key} key={item.key}
href={'?type=' + item.path} href={"?type=" + item.path}
label={item.label} label={item.label}
leftSection={item.icon} leftSection={item.icon}
active={type === item.path || (!type && item.path === 'profile')} active={
/> type === item.path || (!type && item.path === "profile")
))
} }
/>
))}
</Card> </Card>
</Grid.Col> </Grid.Col>
<Grid.Col span={9}> <Grid.Col span={9}>
@@ -130,17 +127,47 @@ export default function DetailSettingPage() {
}} }}
> >
{type === "cat-pengaduan" ? ( {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" ? ( ) : 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" ? ( ) : 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" ? ( ) : 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" ? ( ) : 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> </Card>
</Grid.Col> </Grid.Col>

View File

@@ -37,10 +37,13 @@ export default function DetailWargaPage() {
mutate(); mutate();
}, []); }, []);
return ( 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%"}> <Container size="xl" py="xl" w={"100%"}>
<Grid> <Grid>
<Grid.Col span={4}> <Grid.Col span={4}>
@@ -48,18 +51,29 @@ export default function DetailWargaPage() {
</Grid.Col> </Grid.Col>
<Grid.Col span={8}> <Grid.Col span={8}>
<Stack gap={"xl"}> <Stack gap={"xl"}>
<DetailDataHistori data={data?.data?.pengaduan} kategori="pengaduan" /> <DetailDataHistori
<DetailDataHistori data={data?.data?.pelayanan} kategori="pelayanan" /> data={data?.data?.pengaduan}
kategori="pengaduan"
/>
<DetailDataHistori
data={data?.data?.pelayanan}
kategori="pelayanan"
/>
</Stack> </Stack>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
</Container> </Container>
</> </>
); );
} }
function DetailDataHistori({ data, kategori }: { data: any, kategori: 'pengaduan' | 'pelayanan' }) { function DetailDataHistori({
data,
kategori,
}: {
data: any;
kategori: "pengaduan" | "pelayanan";
}) {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
@@ -85,30 +99,33 @@ function DetailDataHistori({ data, kategori }: { data: any, kategori: 'pengaduan
<Table.Thead> <Table.Thead>
<Table.Tr> <Table.Tr>
<Table.Th>No {_.upperFirst(kategori)}</Table.Th> <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>Status</Table.Th>
<Table.Th></Table.Th> <Table.Th></Table.Th>
</Table.Tr> </Table.Tr>
</Table.Thead> </Table.Thead>
<Table.Tbody> <Table.Tbody>
{ {data?.length > 0 ? (
data?.length > 0 ? (
data?.map((item: any, index: number) => ( data?.map((item: any, index: number) => (
<Table.Tr key={index}> <Table.Tr key={index}>
<Table.Td>{item.noPengaduan}</Table.Td> <Table.Td>{item.noPengaduan}</Table.Td>
<Table.Td>{kategori == "pengaduan" ? item.title : item.category}</Table.Td> <Table.Td>
{kategori == "pengaduan" ? item.title : item.category}
</Table.Td>
<Table.Td>{item.status}</Table.Td> <Table.Td>{item.status}</Table.Td>
<Table.Td> <Table.Td>
<Button <Button
variant="outline" variant="outline"
onClick={() => { onClick={() => {
kategori == "pengaduan" ? kategori == "pengaduan"
navigate( ? navigate(
`/scr/dashboard/pengaduan/detail?id=${item.id}`, `/scr/dashboard/pengaduan/detail?id=${item.id}`,
) :
navigate(
`/scr/dashboard/pelayanan-surat/detail-pelayanan?id=${item.id}`,
) )
: navigate(
`/scr/dashboard/pelayanan-surat/detail-pelayanan?id=${item.id}`,
);
}} }}
> >
Detail Detail
@@ -118,10 +135,11 @@ function DetailDataHistori({ data, kategori }: { data: any, kategori: 'pengaduan
)) ))
) : ( ) : (
<Table.Tr> <Table.Tr>
<Table.Td colSpan={4} align="center">Tidak ada data</Table.Td> <Table.Td colSpan={4} align="center">
Tidak ada data
</Table.Td>
</Table.Tr> </Table.Tr>
) )}
}
</Table.Tbody> </Table.Tbody>
</Table> </Table>
</Stack> </Stack>

View File

@@ -39,7 +39,6 @@ export default function ListWargaPage() {
const pageSize = data?.data?.pageSize || 10; const pageSize = data?.data?.pageSize || 10;
const pageNow = data?.data?.page || 1; const pageNow = data?.data?.page || 1;
useShallowEffect(() => { useShallowEffect(() => {
setPages(1); setPages(1);
mutate(); mutate();
@@ -49,7 +48,6 @@ export default function ListWargaPage() {
mutate(); mutate();
}, [pages]); }, [pages]);
return ( return (
<Container size="xl" py="xl" w={"100%"}> <Container size="xl" py="xl" w={"100%"}>
<Card <Card
@@ -82,7 +80,12 @@ export default function ListWargaPage() {
/> />
<Group> <Group>
<Text size="sm">{`${pageSize * (pages - 1) + 1} ${Math.min(total, pageSize * pages)} of ${total}`}</Text> <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> </Group>
</Flex> </Flex>
<Divider my={0} /> <Divider my={0} />
@@ -95,13 +98,15 @@ export default function ListWargaPage() {
</Table.Tr> </Table.Tr>
</Table.Thead> </Table.Thead>
<Table.Tbody> <Table.Tbody>
{ {Array.isArray(list) && list?.length === 0 ? (
Array.isArray(list) && list?.length === 0 ? (
<Table.Tr> <Table.Tr>
<Table.Td colSpan={3} align="center">Tidak ada data</Table.Td> <Table.Td colSpan={3} align="center">
Tidak ada data
</Table.Td>
</Table.Tr> </Table.Tr>
) : ( ) : (
Array.isArray(list) && list?.map((item, i) => ( Array.isArray(list) &&
list?.map((item, i) => (
<Table.Tr key={i}> <Table.Tr key={i}>
<Table.Td>{item.name}</Table.Td> <Table.Td>{item.name}</Table.Td>
<Table.Td w={250}>{item.phone}</Table.Td> <Table.Td w={250}>{item.phone}</Table.Td>
@@ -119,8 +124,7 @@ export default function ListWargaPage() {
</Table.Td> </Table.Td>
</Table.Tr> </Table.Tr>
)) ))
) )}
}
</Table.Tbody> </Table.Tbody>
</Table> </Table>
</Stack> </Stack>

View File

@@ -102,6 +102,24 @@ const PelayananRoute = new Elysia({
description: `tool untuk delete kategori pelayanan surat` description: `tool untuk delete kategori pelayanan surat`
} }
}) })
.get("/category/detail", async ({ query }) => {
const { id } = query
const data = await prisma.categoryPelayanan.findUnique({
where:{
id
}
})
return data
}, {
query: t.Object({
id: t.String({ minLength: 1, error: "id harus diisi" }),
}),
detail: {
summary: "Detail Kategori Pelayanan Surat by ID",
description: `tool untuk mendapatkan detail kategori pelayanan surat berdasarkan id`,
}
})
// --- PELAYANAN SURAT --- // --- PELAYANAN SURAT ---