upd: form surat
Deskripsi: - api detail categori list - form awal buat surat No Issues
This commit is contained in:
@@ -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 />} />
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -1,98 +1,101 @@
|
|||||||
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(() => {
|
||||||
mutate();
|
mutate();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||||
<MetricCard
|
<MetricCard
|
||||||
icon={<IconMessageReport size={28} />}
|
icon={<IconMessageReport size={28} />}
|
||||||
label="Pengaduan Hari Ini"
|
label="Pengaduan Hari Ini"
|
||||||
value={String(data?.data?.pengaduan?.today)}
|
value={String(data?.data?.pengaduan?.today)}
|
||||||
change={String(data?.data?.pengaduan?.kenaikan) + "%"}
|
change={String(data?.data?.pengaduan?.kenaikan) + "%"}
|
||||||
color={"gray"}
|
color={"gray"}
|
||||||
/>
|
/>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||||
<MetricCard
|
<MetricCard
|
||||||
icon={<IconFileCertificate size={28} />}
|
icon={<IconFileCertificate size={28} />}
|
||||||
label="Pengajuan Surat Hari Ini"
|
label="Pengajuan Surat Hari Ini"
|
||||||
value={String(data?.data?.pelayanan?.today)}
|
value={String(data?.data?.pelayanan?.today)}
|
||||||
change={String(data?.data?.pelayanan?.kenaikan) + "%"}
|
change={String(data?.data?.pelayanan?.kenaikan) + "%"}
|
||||||
color="gray"
|
color="gray"
|
||||||
/>
|
/>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
<Grid.Col span={{ base: 12, sm: 6, md: 4 }}>
|
||||||
<MetricCard
|
<MetricCard
|
||||||
icon={<IconUsers size={28} />}
|
icon={<IconUsers size={28} />}
|
||||||
label="Warga"
|
label="Warga"
|
||||||
value={String(data?.data?.warga)}
|
value={String(data?.data?.warga)}
|
||||||
color="blue"
|
color="blue"
|
||||||
/>
|
/>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function MetricCard({
|
function MetricCard({
|
||||||
icon,
|
icon,
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
change,
|
change,
|
||||||
color,
|
color,
|
||||||
}: {
|
}: {
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
change?: string;
|
change?: string;
|
||||||
color: string;
|
color: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
radius="lg"
|
radius="lg"
|
||||||
p="md"
|
p="md"
|
||||||
withBorder
|
withBorder
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
"linear-gradient(145deg, rgba(30,30,30,0.95), rgba(55,55,55,0.9))",
|
"linear-gradient(145deg, rgba(30,30,30,0.95), rgba(55,55,55,0.9))",
|
||||||
borderColor: "rgba(100,100,100,0.2)",
|
borderColor: "rgba(100,100,100,0.2)",
|
||||||
transition: "transform 0.15s ease, box-shadow 0.15s ease",
|
transition: "transform 0.15s ease, box-shadow 0.15s ease",
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) =>
|
onMouseEnter={(e) =>
|
||||||
(e.currentTarget.style.boxShadow = "0 0 10px rgba(0,255,200,0.2)")
|
(e.currentTarget.style.boxShadow = "0 0 10px rgba(0,255,200,0.2)")
|
||||||
}
|
}
|
||||||
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")}
|
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")}
|
||||||
>
|
>
|
||||||
<Stack gap={6}>
|
<Stack gap={6}>
|
||||||
<Group gap={6}>
|
<Group gap={6}>
|
||||||
{icon}
|
{icon}
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Flex align="center" justify="space-between">
|
<Flex align="center" justify="space-between">
|
||||||
<Text fw={600} size="xl" c="gray.0">
|
<Text fw={600} size="xl" c="gray.0">
|
||||||
{value}
|
{value}
|
||||||
</Text>
|
</Text>
|
||||||
{change && (
|
{change && (
|
||||||
<Text size="sm" c={color}>
|
<Text size="sm" c={color}>
|
||||||
{change}
|
{change}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -7,77 +7,71 @@ import { useEffect, useState } from "react";
|
|||||||
import useSWR from "swr";
|
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;
|
||||||
const option: EChartsOption = {
|
const option: EChartsOption = {
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) loadData();
|
if (data) loadData();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
radius="lg"
|
radius="lg"
|
||||||
p="xl"
|
p="xl"
|
||||||
withBorder
|
withBorder
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
|
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
|
||||||
borderColor: "rgba(100,100,100,0.2)",
|
borderColor: "rgba(100,100,100,0.2)",
|
||||||
boxShadow: "0 0 25px rgba(0,255,200,0.08)",
|
boxShadow: "0 0 25px rgba(0,255,200,0.08)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Flex align="center" justify="space-between">
|
<Flex align="center" justify="space-between">
|
||||||
<Flex direction={"column"}>
|
<Flex direction={"column"}>
|
||||||
<Title order={4} c="gray.0">
|
<Title order={4} c="gray.0">
|
||||||
Grafik Pengaduan dan Pelayanan Surat
|
Grafik Pengaduan dan Pelayanan Surat
|
||||||
</Title>
|
</Title>
|
||||||
<Text size="sm">7 Hari Terakhir</Text>
|
<Text size="sm">7 Hari Terakhir</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<IconChartBar size={20} color="gray" />
|
<IconChartBar size={20} color="gray" />
|
||||||
</Flex>
|
</Flex>
|
||||||
<Divider my="xs" />
|
<Divider my="xs" />
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<EChartsReact style={{ height: 400 }} option={options} />
|
<EChartsReact style={{ height: 400 }} option={options} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
@@ -1,133 +1,210 @@
|
|||||||
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";
|
||||||
|
|
||||||
export default function DashboardLastData() {
|
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
|
radius="lg"
|
||||||
radius="lg"
|
p="xl"
|
||||||
p="xl"
|
withBorder
|
||||||
withBorder
|
w={"50%"}
|
||||||
w={"50%"}
|
style={{
|
||||||
style={{
|
background:
|
||||||
background:
|
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
|
||||||
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
|
borderColor: "rgba(100,100,100,0.2)",
|
||||||
borderColor: "rgba(100,100,100,0.2)",
|
boxShadow: "0 0 25px rgba(0,255,200,0.08)",
|
||||||
boxShadow: "0 0 25px rgba(0,255,200,0.08)",
|
}}
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stack gap="sm">
|
|
||||||
<Flex align="center" pb={"sm"} justify="space-between" style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}>
|
|
||||||
<Title order={4} c="gray.0">
|
|
||||||
Last update pengaduan
|
|
||||||
</Title>
|
|
||||||
<Button variant="subtle" size="xs" radius="md" onClick={() => navigate(`/scr/dashboard/pengaduan/list`)}>View All</Button>
|
|
||||||
</Flex>
|
|
||||||
<Stack gap="sm" mt="md" align="stretch" justify="center">
|
|
||||||
{
|
|
||||||
data && Array.isArray(data.pengaduan) && data.pengaduan.length > 0 ? data.pengaduan.map((item: any, index: number) => (
|
|
||||||
<PengaduanSection
|
|
||||||
key={index}
|
|
||||||
id={item.id}
|
|
||||||
nomer={item.noPengaduan}
|
|
||||||
judul={item.title}
|
|
||||||
status={item.status}
|
|
||||||
updated={item.updatedAt}
|
|
||||||
kategori="pengaduan"
|
|
||||||
/>
|
|
||||||
)) : <Text c="dimmed" ta={"center"} >Tidak ada data</Text>
|
|
||||||
}
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
radius="lg"
|
|
||||||
p="xl"
|
|
||||||
withBorder
|
|
||||||
w={"50%"}
|
|
||||||
style={{
|
|
||||||
background:
|
|
||||||
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
|
|
||||||
borderColor: "rgba(100,100,100,0.2)",
|
|
||||||
boxShadow: "0 0 25px rgba(0,255,200,0.08)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stack gap="sm">
|
|
||||||
<Flex align="center" pb={"sm"} justify="space-between" style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}>
|
|
||||||
<Title order={4} c="gray.0">
|
|
||||||
Last update pelayanan surat
|
|
||||||
</Title>
|
|
||||||
<Button variant="subtle" size="xs" radius="md" onClick={() => navigate(`/scr/dashboard/pelayanan-surat/list-pelayanan`)}>View All</Button>
|
|
||||||
</Flex>
|
|
||||||
<Stack gap="sm" mt="md" align="stretch" justify="center">
|
|
||||||
{
|
|
||||||
data && Array.isArray(data.pelayanan) && data.pelayanan.length > 0 ? data.pelayanan.map((item: any, index: number) => (
|
|
||||||
<PengaduanSection
|
|
||||||
key={index}
|
|
||||||
id={item.id}
|
|
||||||
nomer={item.noPengajuan}
|
|
||||||
judul={item.title}
|
|
||||||
status={item.status}
|
|
||||||
updated={item.updatedAt}
|
|
||||||
kategori="pelayanan"
|
|
||||||
/>
|
|
||||||
)) : <Text c="dimmed" ta={"center"} >Tidak ada data</Text>
|
|
||||||
}
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Card>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function PengaduanSection({ id, nomer, judul, status, updated, kategori }: { id: string, nomer: string, judul: string, status: string, updated: string, kategori: 'pengaduan' | 'pelayanan' }) {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack
|
|
||||||
gap="xs"
|
|
||||||
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)" }}>
|
<Stack gap="sm">
|
||||||
<Flex direction={"column"}>
|
<Flex
|
||||||
<Text size="md" c="gray.2" lineClamp={1}>
|
align="center"
|
||||||
{judul}
|
pb={"sm"}
|
||||||
</Text>
|
justify="space-between"
|
||||||
<Group>
|
style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}
|
||||||
<Text size="sm" c="dimmed">
|
>
|
||||||
#{nomer} ∙ {updated}
|
<Title order={4} c="gray.0">
|
||||||
</Text>
|
Last update pengaduan
|
||||||
</Group>
|
</Title>
|
||||||
</Flex>
|
<Button
|
||||||
<Tooltip label={status}>
|
variant="subtle"
|
||||||
<Badge size="xs" circle color={
|
size="xs"
|
||||||
status === "diterima"
|
radius="md"
|
||||||
? "green"
|
onClick={() => navigate(`/scr/dashboard/pengaduan/list`)}
|
||||||
: status === "ditolak"
|
>
|
||||||
? "red"
|
View All
|
||||||
: status === "selesai"
|
</Button>
|
||||||
? "blue"
|
</Flex>
|
||||||
: status === "dikerjakan"
|
<Stack gap="sm" mt="md" align="stretch" justify="center">
|
||||||
? "gray"
|
{data &&
|
||||||
: "yellow"
|
Array.isArray(data.pengaduan) &&
|
||||||
} />
|
data.pengaduan.length > 0 ? (
|
||||||
</Tooltip>
|
data.pengaduan.map((item: any, index: number) => (
|
||||||
</Flex>
|
<PengaduanSection
|
||||||
</Stack>
|
key={index}
|
||||||
)
|
id={item.id}
|
||||||
|
nomer={item.noPengaduan}
|
||||||
|
judul={item.title}
|
||||||
|
status={item.status}
|
||||||
|
updated={item.updatedAt}
|
||||||
|
kategori="pengaduan"
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text c="dimmed" ta={"center"}>
|
||||||
|
Tidak ada data
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
radius="lg"
|
||||||
|
p="xl"
|
||||||
|
withBorder
|
||||||
|
w={"50%"}
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
"linear-gradient(145deg, rgba(25,25,25,0.95), rgba(45,45,45,0.85))",
|
||||||
|
borderColor: "rgba(100,100,100,0.2)",
|
||||||
|
boxShadow: "0 0 25px rgba(0,255,200,0.08)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Flex
|
||||||
|
align="center"
|
||||||
|
pb={"sm"}
|
||||||
|
justify="space-between"
|
||||||
|
style={{ borderBottom: "1px solid rgba(255,255,255,0.1)" }}
|
||||||
|
>
|
||||||
|
<Title order={4} c="gray.0">
|
||||||
|
Last update pelayanan surat
|
||||||
|
</Title>
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
size="xs"
|
||||||
|
radius="md"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(`/scr/dashboard/pelayanan-surat/list-pelayanan`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
View All
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
<Stack gap="sm" mt="md" align="stretch" justify="center">
|
||||||
|
{data &&
|
||||||
|
Array.isArray(data.pelayanan) &&
|
||||||
|
data.pelayanan.length > 0 ? (
|
||||||
|
data.pelayanan.map((item: any, index: number) => (
|
||||||
|
<PengaduanSection
|
||||||
|
key={index}
|
||||||
|
id={item.id}
|
||||||
|
nomer={item.noPengajuan}
|
||||||
|
judul={item.title}
|
||||||
|
status={item.status}
|
||||||
|
updated={item.updatedAt}
|
||||||
|
kategori="pelayanan"
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text c="dimmed" ta={"center"}>
|
||||||
|
Tidak ada data
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PengaduanSection({
|
||||||
|
id,
|
||||||
|
nomer,
|
||||||
|
judul,
|
||||||
|
status,
|
||||||
|
updated,
|
||||||
|
kategori,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
nomer: string;
|
||||||
|
judul: string;
|
||||||
|
status: string;
|
||||||
|
updated: string;
|
||||||
|
kategori: "pengaduan" | "pelayanan";
|
||||||
|
}) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
gap="xs"
|
||||||
|
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 direction={"column"}>
|
||||||
|
<Text size="md" c="gray.2" lineClamp={1}>
|
||||||
|
{judul}
|
||||||
|
</Text>
|
||||||
|
<Group>
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
#{nomer} ∙ {updated}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Flex>
|
||||||
|
<Tooltip label={status}>
|
||||||
|
<Badge
|
||||||
|
size="xs"
|
||||||
|
circle
|
||||||
|
color={
|
||||||
|
status === "diterima"
|
||||||
|
? "green"
|
||||||
|
: status === "ditolak"
|
||||||
|
? "red"
|
||||||
|
: status === "selesai"
|
||||||
|
? "blue"
|
||||||
|
: status === "dikerjakan"
|
||||||
|
? "gray"
|
||||||
|
: "yellow"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -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,31 +145,27 @@ 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}>
|
||||||
?
|
<FileInput
|
||||||
(
|
clearable
|
||||||
<Input.Wrapper label={dataEdit.name}>
|
placeholder="Upload TTD"
|
||||||
<FileInput
|
accept="image/*"
|
||||||
clearable
|
onChange={(e) => {
|
||||||
placeholder="Upload TTD"
|
setImg(e);
|
||||||
accept="image/*"
|
}}
|
||||||
onChange={(e) => { setImg(e) }}
|
/>
|
||||||
/>
|
</Input.Wrapper>
|
||||||
</Input.Wrapper>
|
) : (
|
||||||
)
|
<Input.Wrapper label={dataEdit.name}>
|
||||||
:
|
<Input
|
||||||
(
|
value={dataEdit.value}
|
||||||
<Input.Wrapper label={dataEdit.name}>
|
onChange={(e) =>
|
||||||
<Input
|
onValidation({ kat: "value", value: e.target.value })
|
||||||
value={dataEdit.value}
|
}
|
||||||
onChange={(e) =>
|
/>
|
||||||
onValidation({ kat: "value", value: e.target.value })
|
</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={() => {
|
||||||
Lihat
|
setViewImg(v.value);
|
||||||
</Anchor>
|
setOpenedPreview(true);
|
||||||
:
|
}}
|
||||||
"-"
|
underline="always"
|
||||||
:
|
>
|
||||||
v.value
|
Lihat
|
||||||
}
|
</Anchor>
|
||||||
|
) : (
|
||||||
|
"-"
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
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"
|
||||||
|
|||||||
@@ -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,19 +538,17 @@ 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"
|
leftSection={<IconPlus size={20} />}
|
||||||
leftSection={<IconPlus size={20} />}
|
onClick={openTambah}
|
||||||
onClick={openTambah}
|
>
|
||||||
>
|
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>
|
||||||
|
|||||||
@@ -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,19 +298,17 @@ 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"
|
leftSection={<IconPlus size={20} />}
|
||||||
leftSection={<IconPlus size={20} />}
|
onClick={openTambah}
|
||||||
onClick={openTambah}
|
>
|
||||||
>
|
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>
|
||||||
|
|||||||
@@ -3,92 +3,100 @@ 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({
|
||||||
const [viewFile, setViewFile] = useState<string>("");
|
open,
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
onClose,
|
||||||
const [typeFile, setTypeFile] = useState<string>("");
|
folder,
|
||||||
const [error, setError] = useState<boolean>(false);
|
fileName,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
folder: string;
|
||||||
|
fileName: string;
|
||||||
|
}) {
|
||||||
|
const [viewFile, setViewFile] = useState<string>("");
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [typeFile, setTypeFile] = useState<string>("");
|
||||||
|
const [error, setError] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && fileName) {
|
if (open && fileName) {
|
||||||
loadImage();
|
loadImage();
|
||||||
|
}
|
||||||
|
}, [open, fileName]);
|
||||||
|
|
||||||
|
const loadImage = async () => {
|
||||||
|
try {
|
||||||
|
setViewFile("");
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
// detect type of file
|
||||||
|
const { ext, type } = detectFileType(fileName);
|
||||||
|
setTypeFile(type || "");
|
||||||
|
|
||||||
|
// load file
|
||||||
|
const urlApi =
|
||||||
|
"/api/pengaduan/image?folder=" + folder + "&fileName=" + fileName;
|
||||||
|
const res = await fetch(urlApi);
|
||||||
|
if (!res.ok) {
|
||||||
|
setError(true);
|
||||||
|
return notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to load image",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [open, fileName]);
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
setViewFile(url);
|
||||||
|
} catch (err) {
|
||||||
|
setError(true);
|
||||||
|
notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to load image",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const loadImage = async () => {
|
useEffect(() => {
|
||||||
try {
|
if (error) {
|
||||||
setViewFile("");
|
onClose();
|
||||||
setLoading(true);
|
}
|
||||||
|
}, [error]);
|
||||||
|
|
||||||
// detect type of file
|
return (
|
||||||
const { ext, type } = detectFileType(fileName);
|
<Modal
|
||||||
setTypeFile(type || "");
|
opened={open}
|
||||||
|
onClose={onClose}
|
||||||
// load file
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
const urlApi = '/api/pengaduan/image?folder=' + folder + '&fileName=' + fileName;
|
size="xl"
|
||||||
const res = await fetch(urlApi);
|
withCloseButton
|
||||||
if (!res.ok) {
|
removeScrollProps={{ allowPinchZoom: true }}
|
||||||
setError(true);
|
title="File"
|
||||||
return notification({
|
>
|
||||||
title: "Error",
|
{loading && (
|
||||||
message: "Failed to load image",
|
<Flex justify="center" align="center" h={200}>
|
||||||
type: "error",
|
<Loader />
|
||||||
});
|
</Flex>
|
||||||
}
|
)}
|
||||||
const blob = await res.blob();
|
{viewFile && (
|
||||||
const url = URL.createObjectURL(blob);
|
<>
|
||||||
|
{typeFile == "pdf" ? (
|
||||||
setViewFile(url);
|
<embed
|
||||||
} catch (err) {
|
src={viewFile}
|
||||||
setError(true);
|
type="application/pdf"
|
||||||
notification({
|
width="100%"
|
||||||
title: "Error",
|
height="950"
|
||||||
message: "Failed to load image",
|
/>
|
||||||
type: "error",
|
) : (
|
||||||
});
|
<Image radius="md" h={300} fit="contain" src={viewFile} />
|
||||||
} finally {
|
)}
|
||||||
setLoading(false);
|
</>
|
||||||
}
|
)}
|
||||||
};
|
</Modal>
|
||||||
|
);
|
||||||
useEffect(() => {
|
|
||||||
if (error) {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
}, [error]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
opened={open}
|
|
||||||
onClose={onClose}
|
|
||||||
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
|
||||||
size="xl"
|
|
||||||
withCloseButton
|
|
||||||
removeScrollProps={{ allowPinchZoom: true }}
|
|
||||||
title="File"
|
|
||||||
>
|
|
||||||
{loading && (
|
|
||||||
<Flex justify="center" align="center" h={200}>
|
|
||||||
<Loader />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
{viewFile && (
|
|
||||||
<>
|
|
||||||
{typeFile == "pdf" ? (
|
|
||||||
<embed src={viewFile} type="application/pdf" width="100%" height="950" />
|
|
||||||
) : (
|
|
||||||
<Image
|
|
||||||
radius="md"
|
|
||||||
h={300}
|
|
||||||
fit="contain"
|
|
||||||
src={viewFile}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,122 +18,129 @@ 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({
|
||||||
const A4Style = {
|
open,
|
||||||
width: "210mm",
|
onClose,
|
||||||
height: "297mm",
|
surat,
|
||||||
padding: "20mm",
|
}: {
|
||||||
background: "#fff",
|
open: boolean;
|
||||||
color: "#000",
|
onClose: () => void;
|
||||||
fontSize: "14px",
|
surat: string;
|
||||||
fontFamily: "Times New Roman",
|
}) {
|
||||||
};
|
const A4Style = {
|
||||||
const hiddenRef = useRef<any>(null);
|
width: "210mm",
|
||||||
const { data, mutate, isLoading } = useSWR("surat", () =>
|
height: "297mm",
|
||||||
apiFetch.api.surat.detail.get({
|
padding: "20mm",
|
||||||
query: {
|
background: "#fff",
|
||||||
id: surat,
|
color: "#000",
|
||||||
},
|
fontSize: "14px",
|
||||||
}),
|
fontFamily: "Times New Roman",
|
||||||
);
|
};
|
||||||
|
const hiddenRef = useRef<any>(null);
|
||||||
|
const { data, mutate, isLoading } = useSWR("surat", () =>
|
||||||
|
apiFetch.api.surat.detail.get({
|
||||||
|
query: {
|
||||||
|
id: surat,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
mutate();
|
mutate();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const downloadPDF = async () => {
|
||||||
|
const element = hiddenRef.current;
|
||||||
|
const canvas = await html2canvas(element, {
|
||||||
|
scale: 2,
|
||||||
|
useCORS: true,
|
||||||
|
allowTaint: true,
|
||||||
|
width: element.offsetWidth,
|
||||||
|
height: element.offsetHeight,
|
||||||
|
});
|
||||||
|
|
||||||
const downloadPDF = async () => {
|
const imgData = canvas.toDataURL("image/jpeg", 1.0);
|
||||||
const element = hiddenRef.current;
|
|
||||||
const canvas = await html2canvas(element, {
|
|
||||||
scale: 2,
|
|
||||||
useCORS: true,
|
|
||||||
allowTaint: true,
|
|
||||||
width: element.offsetWidth,
|
|
||||||
height: element.offsetHeight,
|
|
||||||
});
|
|
||||||
|
|
||||||
const imgData = canvas.toDataURL("image/jpeg", 1.0);
|
const pdf = new jsPDF("p", "mm", "a4");
|
||||||
|
const pageWidth = 210; // A4 width mm
|
||||||
|
const pageHeight = 297; // A4 height mm
|
||||||
|
|
||||||
const pdf = new jsPDF("p", "mm", "a4");
|
const imgWidth = pageWidth;
|
||||||
const pageWidth = 210; // A4 width mm
|
const imgHeight = (canvas.height * pageWidth) / canvas.width;
|
||||||
const pageHeight = 297; // A4 height mm
|
|
||||||
|
|
||||||
const imgWidth = pageWidth;
|
pdf.addImage(imgData, "JPEG", 0, 0, imgWidth, imgHeight);
|
||||||
const imgHeight = (canvas.height * pageWidth) / canvas.width;
|
|
||||||
|
|
||||||
pdf.addImage(imgData, "JPEG", 0, 0, imgWidth, imgHeight);
|
pdf.save(`${data?.data?.surat?.nameCategory}.pdf`);
|
||||||
|
};
|
||||||
|
|
||||||
pdf.save(`${data?.data?.surat?.nameCategory}.pdf`);
|
return (
|
||||||
};
|
<>
|
||||||
|
<Modal
|
||||||
|
opened={open}
|
||||||
|
onClose={() => onClose()}
|
||||||
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
|
size="auto"
|
||||||
|
withCloseButton={false}
|
||||||
|
removeScrollProps={{ allowPinchZoom: true }}
|
||||||
|
styles={{
|
||||||
|
header: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
padding: "12px 16px",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
<Flex justify="space-between" align="center" w="100%">
|
||||||
|
<div style={{ fontSize: 18, fontWeight: 600 }}>Preview Surat</div>
|
||||||
|
|
||||||
return (
|
<Flex gap={8}>
|
||||||
<>
|
<ActionIcon size={32} variant="default">
|
||||||
<Modal
|
<IconDownload size={20} onClick={downloadPDF} />
|
||||||
opened={open}
|
</ActionIcon>
|
||||||
onClose={() => onClose()}
|
|
||||||
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
|
||||||
size="auto"
|
|
||||||
withCloseButton={false}
|
|
||||||
removeScrollProps={{ allowPinchZoom: true }}
|
|
||||||
styles={{
|
|
||||||
header: {
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
padding: "12px 16px",
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
width: "100%",
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
title={
|
|
||||||
<Flex justify="space-between" align="center" w="100%">
|
|
||||||
<div style={{ fontSize: 18, fontWeight: 600 }}>
|
|
||||||
Preview Surat
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Flex gap={8}>
|
<ActionIcon size={32} variant="default" onClick={onClose}>
|
||||||
<ActionIcon size={32} variant="default">
|
<IconX size={20} />
|
||||||
<IconDownload size={20} onClick={downloadPDF} />
|
</ActionIcon>
|
||||||
</ActionIcon>
|
</Flex>
|
||||||
|
</Flex>
|
||||||
<ActionIcon size={32} variant="default" onClick={onClose}>
|
}
|
||||||
<IconX size={20} />
|
>
|
||||||
</ActionIcon>
|
<div ref={hiddenRef} style={A4Style}>
|
||||||
</Flex>
|
{data && data.data ? (
|
||||||
</Flex>
|
data.data.surat.idCategory == "skusaha" ? (
|
||||||
}
|
<SKUsaha data={data.data} />
|
||||||
>
|
) : data.data.surat.idCategory == "skkelahiran" ? (
|
||||||
<div ref={hiddenRef} style={A4Style}>
|
<SKKelahiran data={data.data} />
|
||||||
{
|
) : data.data.surat.idCategory == "skkelakuanbaik" ? (
|
||||||
data && data.data
|
<SKKelakuanBaik data={data.data} />
|
||||||
? data.data.surat.idCategory == "skusaha"
|
) : data.data.surat.idCategory == "skpenghasilan" ? (
|
||||||
? <SKUsaha data={data.data} />
|
<SKPenghasilan data={data.data} />
|
||||||
: data.data.surat.idCategory == "skkelahiran"
|
) : data.data.surat.idCategory == "sktidakmampu" ? (
|
||||||
? <SKKelahiran data={data.data} />
|
<SKTidakMampu data={data.data} />
|
||||||
: data.data.surat.idCategory == "skkelakuanbaik"
|
) : data.data.surat.idCategory == "skyatimpiatu" ? (
|
||||||
? <SKKelakuanBaik data={data.data} />
|
<SKYatim data={data.data} />
|
||||||
: data.data.surat.idCategory == "skpenghasilan"
|
) : data.data.surat.idCategory == "skdomisiliorganisasi" ? (
|
||||||
? <SKPenghasilan data={data.data} />
|
<SKDomisiliOrganisasi data={data.data} />
|
||||||
: data.data.surat.idCategory == "sktidakmampu"
|
) : data.data.surat.idCategory == "skbedabiodata" ? (
|
||||||
? <SKTidakMampu data={data.data} />
|
<SKBedaBiodataDiri data={data.data} />
|
||||||
: data.data.surat.idCategory == "skyatimpiatu"
|
) : data.data.surat.idCategory == "sktempatusaha" ? (
|
||||||
? <SKYatim data={data.data} />
|
<SKTempatUsaha data={data.data} />
|
||||||
: data.data.surat.idCategory == "skdomisiliorganisasi"
|
) : data.data.surat.idCategory == "skbelumkawin" ? (
|
||||||
? <SKDomisiliOrganisasi data={data.data} />
|
<SKBelumKawin data={data.data} />
|
||||||
: data.data.surat.idCategory == "skbedabiodata"
|
) : data.data.surat.idCategory == "skkematian" ? (
|
||||||
? <SKBedaBiodataDiri data={data.data} />
|
<SKKematian data={data.data} />
|
||||||
: data.data.surat.idCategory == "sktempatusaha"
|
) : (
|
||||||
? <SKTempatUsaha data={data.data} />
|
<></>
|
||||||
: data.data.surat.idCategory == "skbelumkawin"
|
)
|
||||||
? <SKBelumKawin data={data.data} />
|
) : (
|
||||||
: data.data.surat.idCategory == "skkematian"
|
<></>
|
||||||
? <SKKematian data={data.data} />
|
)}
|
||||||
: <></>
|
</div>
|
||||||
: <></>
|
</Modal>
|
||||||
}
|
</>
|
||||||
</div>
|
);
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@@ -3,65 +3,66 @@ import { Anchor, Flex, Stack, Text } from "@mantine/core";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
interface Node {
|
interface Node {
|
||||||
label: string;
|
label: string;
|
||||||
children: any;
|
children: any;
|
||||||
actions: string[];
|
actions: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function RenderNode({ node }: { node: Node }) {
|
function RenderNode({ node }: { node: Node }) {
|
||||||
const sub = Object.values(node.children || {});
|
const sub = Object.values(node.children || {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pl="md" gap={6}>
|
<Stack pl="md" gap={6}>
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<Text size="sm">- {node.label}</Text>
|
<Text size="sm">- {node.label}</Text>
|
||||||
|
|
||||||
{/* Children */}
|
{/* Children */}
|
||||||
{sub.map((child: any, i) => (
|
{sub.map((child: any, i) => (
|
||||||
<RenderNode key={i} node={child} />
|
<RenderNode key={i} node={child} />
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RenderNode2({ node }: { node: Node }) {
|
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>
|
||||||
|
|
||||||
{/* Children */}
|
{/* Children */}
|
||||||
{sub.map((child: any, i) => (
|
{sub.map((child: any, i) => (
|
||||||
<RenderNode2 key={i} node={child} />
|
<RenderNode2 key={i} node={child} />
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PermissionRole({ permissions }: { permissions: string[] }) {
|
export default function PermissionRole({
|
||||||
const [showAll, setShowAll] = useState(false);
|
permissions,
|
||||||
if (!permissions?.length) return <Text c="dimmed">-</Text>;
|
}: {
|
||||||
|
permissions: string[];
|
||||||
|
}) {
|
||||||
|
const [showAll, setShowAll] = useState(false);
|
||||||
|
if (!permissions?.length) return <Text c="dimmed">-</Text>;
|
||||||
|
|
||||||
const groups = groupPermissions(permissions);
|
const groups = groupPermissions(permissions);
|
||||||
const rootNodes = Object.values(groups);
|
const rootNodes = Object.values(groups);
|
||||||
|
|
||||||
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
|
||||||
:
|
.slice(0, 2)
|
||||||
rootNodes.slice(0, 2).map((node: any, idx) => (
|
.map((node: any, idx) => <RenderNode2 key={idx} node={node} />)}
|
||||||
<RenderNode2 key={idx} node={node} />
|
<Anchor size="xs" onClick={() => setShowAll(!showAll)}>
|
||||||
))
|
{showAll ? "View less" : "View more"}
|
||||||
}
|
</Anchor>
|
||||||
<Anchor size="xs" onClick={() => setShowAll(!showAll)} >
|
</Stack>
|
||||||
{showAll ? "View less" : "View more"}
|
);
|
||||||
</Anchor>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,177 +1,183 @@
|
|||||||
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";
|
||||||
|
|
||||||
interface Node {
|
interface Node {
|
||||||
label: string;
|
label: string;
|
||||||
key: string;
|
key: string;
|
||||||
children?: Node[];
|
children?: Node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PermissionTree({
|
export default function PermissionTree({
|
||||||
selected,
|
selected,
|
||||||
onChange,
|
onChange,
|
||||||
}: {
|
}: {
|
||||||
selected: string[];
|
selected: string[];
|
||||||
onChange: (val: string[]) => void;
|
onChange: (val: string[]) => void;
|
||||||
}) {
|
}) {
|
||||||
// Ambil semua child dari node
|
// Ambil semua child dari node
|
||||||
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[] {
|
||||||
let result: string[] = [];
|
let result: string[] = [];
|
||||||
if (node.children) {
|
if (node.children) {
|
||||||
node.children.forEach((c) => {
|
node.children.forEach((c) => {
|
||||||
result.push(c.key);
|
result.push(c.key);
|
||||||
result = [...result, ...getAllChildKeys(c)];
|
result = [...result, ...getAllChildKeys(c)];
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dapatkan parentKey, jika ada
|
||||||
|
function getParentKey(key: string) {
|
||||||
|
const split = key.split(".");
|
||||||
|
if (split.length <= 1) return null;
|
||||||
|
split.pop();
|
||||||
|
return split.join(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update parent ke atas secara rekursif
|
||||||
|
function updateParent(next: string[], parentKey: string | null): string[] {
|
||||||
|
if (!parentKey) return next;
|
||||||
|
|
||||||
|
const allChildKeys = findAllChildKeysFromKey(parentKey);
|
||||||
|
|
||||||
|
const selectedChild = allChildKeys.filter((c) => next.includes(c));
|
||||||
|
|
||||||
|
if (selectedChild.length === 0) {
|
||||||
|
// Semua child uncheck → parent uncheck
|
||||||
|
next = next.filter((x) => x !== parentKey);
|
||||||
|
} else if (selectedChild.length === allChildKeys.length) {
|
||||||
|
// Semua child check → parent check
|
||||||
|
if (!next.includes(parentKey)) {
|
||||||
|
next.push(parentKey);
|
||||||
}
|
}
|
||||||
return result;
|
} else {
|
||||||
}
|
// Sebagian child check → parent intermediate (checked = true, rendered sebagai indeterminate)
|
||||||
|
if (!next.includes(parentKey)) {
|
||||||
// Dapatkan parentKey, jika ada
|
next.push(parentKey);
|
||||||
function getParentKey(key: string) {
|
|
||||||
const split = key.split(".");
|
|
||||||
if (split.length <= 1) return null;
|
|
||||||
split.pop();
|
|
||||||
return split.join(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Update parent ke atas secara rekursif
|
|
||||||
function updateParent(next: string[], parentKey: string | null): string[] {
|
|
||||||
if (!parentKey) return next;
|
|
||||||
|
|
||||||
const allChildKeys = findAllChildKeysFromKey(parentKey);
|
|
||||||
|
|
||||||
const selectedChild = allChildKeys.filter((c) => next.includes(c));
|
|
||||||
|
|
||||||
if (selectedChild.length === 0) {
|
|
||||||
// Semua child uncheck → parent uncheck
|
|
||||||
next = next.filter((x) => x !== parentKey);
|
|
||||||
} else if (selectedChild.length === allChildKeys.length) {
|
|
||||||
// Semua child check → parent check
|
|
||||||
if (!next.includes(parentKey)) {
|
|
||||||
next.push(parentKey);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Sebagian child check → parent intermediate (checked = true, rendered sebagai indeterminate)
|
|
||||||
if (!next.includes(parentKey)) {
|
|
||||||
next.push(parentKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Rekursif naik ke atas
|
// Rekursif naik ke atas
|
||||||
return updateParent(next, getParentKey(parentKey));
|
return updateParent(next, getParentKey(parentKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
// dapatkan child dari string key
|
// dapatkan child dari string key
|
||||||
function findAllChildKeysFromKey(parentKey: string) {
|
function findAllChildKeysFromKey(parentKey: string) {
|
||||||
const list: string[] = [];
|
const list: string[] = [];
|
||||||
|
|
||||||
function traverse(nodes: Node[]) {
|
function traverse(nodes: Node[]) {
|
||||||
nodes.forEach((n) => {
|
nodes.forEach((n) => {
|
||||||
if (n.key.startsWith(parentKey + ".") && n.key !== parentKey) {
|
if (n.key.startsWith(parentKey + ".") && n.key !== parentKey) {
|
||||||
list.push(n.key);
|
list.push(n.key);
|
||||||
}
|
}
|
||||||
if (n.children) traverse(n.children);
|
if (n.children) traverse(n.children);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse(permissionConfig.menus);
|
traverse(permissionConfig.menus);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RenderMenu = ({ menu }: { menu: Node }) => {
|
const RenderMenu = ({ menu }: { menu: Node }) => {
|
||||||
const hasChild = menu.children && menu.children.length > 0;
|
const hasChild = menu.children && menu.children.length > 0;
|
||||||
const open = openNodes[menu.label] ?? false;
|
const open = openNodes[menu.label] ?? false;
|
||||||
const childKeys = getAllChildKeys(menu);
|
const childKeys = getAllChildKeys(menu);
|
||||||
const isChecked = selected.includes(menu.key);
|
const isChecked = selected.includes(menu.key);
|
||||||
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() {
|
|
||||||
let next = [...selected];
|
|
||||||
|
|
||||||
if (childKeys.length > 0) {
|
|
||||||
// klik parent
|
|
||||||
if (!isChecked) {
|
|
||||||
next = [...new Set([...next, menu.key, ...childKeys])];
|
|
||||||
} else {
|
|
||||||
next = next.filter((x) => x !== menu.key && !childKeys.includes(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
next = updateParent(next, getParentKey(menu.key));
|
|
||||||
onChange(next);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// klik child
|
|
||||||
if (isChecked) {
|
|
||||||
next = next.filter((x) => x !== menu.key);
|
|
||||||
} else {
|
|
||||||
next.push(menu.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
next = updateParent(next, getParentKey(menu.key));
|
|
||||||
onChange(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack gap={4}>
|
|
||||||
<Group gap="xs">
|
|
||||||
{menu.children && menu.children.length > 0 ? (
|
|
||||||
<ActionIcon
|
|
||||||
variant="subtle"
|
|
||||||
onClick={() => toggleNode(menu.label)}
|
|
||||||
>
|
|
||||||
{openNodes[menu.label] ? (
|
|
||||||
<IconChevronDown size={16} />
|
|
||||||
) : (
|
|
||||||
<IconChevronRight size={16} />
|
|
||||||
)}
|
|
||||||
</ActionIcon>
|
|
||||||
) : (
|
|
||||||
<div style={{ width: 28 }} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Checkbox
|
|
||||||
label={menu.label}
|
|
||||||
checked={isChecked}
|
|
||||||
indeterminate={isIndeterminate}
|
|
||||||
onChange={handleCheck}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
{menu.children && (
|
|
||||||
<Collapse in={open}>
|
|
||||||
<Stack gap={4} pl="md">
|
|
||||||
{menu.children.map((child) => (
|
|
||||||
<RenderMenu key={child.key} menu={child} />
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
</Collapse>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
function handleCheck() {
|
||||||
<Stack>
|
let next = [...selected];
|
||||||
<Text size="sm">Hak Akses</Text>
|
|
||||||
{permissionConfig.menus.filter((menu: Node) => !menu.key.startsWith("api") && !menu.key.startsWith("credential")).map((menu: Node) => (
|
if (childKeys.length > 0) {
|
||||||
<RenderMenu key={menu.key} menu={menu} />
|
// klik parent
|
||||||
))}
|
if (!isChecked) {
|
||||||
|
next = [...new Set([...next, menu.key, ...childKeys])];
|
||||||
|
} else {
|
||||||
|
next = next.filter((x) => x !== menu.key && !childKeys.includes(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
next = updateParent(next, getParentKey(menu.key));
|
||||||
|
onChange(next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// klik child
|
||||||
|
if (isChecked) {
|
||||||
|
next = next.filter((x) => x !== menu.key);
|
||||||
|
} else {
|
||||||
|
next.push(menu.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
next = updateParent(next, getParentKey(menu.key));
|
||||||
|
onChange(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap={4}>
|
||||||
|
<Group gap="xs">
|
||||||
|
{menu.children && menu.children.length > 0 ? (
|
||||||
|
<ActionIcon variant="subtle" onClick={() => toggleNode(menu.label)}>
|
||||||
|
{openNodes[menu.label] ? (
|
||||||
|
<IconChevronDown size={16} />
|
||||||
|
) : (
|
||||||
|
<IconChevronRight size={16} />
|
||||||
|
)}
|
||||||
|
</ActionIcon>
|
||||||
|
) : (
|
||||||
|
<div style={{ width: 28 }} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
label={menu.label}
|
||||||
|
checked={isChecked}
|
||||||
|
indeterminate={isIndeterminate}
|
||||||
|
onChange={handleCheck}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{menu.children && (
|
||||||
|
<Collapse in={open}>
|
||||||
|
<Stack gap={4} pl="md">
|
||||||
|
{menu.children.map((child) => (
|
||||||
|
<RenderMenu key={child.key} menu={child} />
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Collapse>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<Text size="sm">Hak Akses</Text>
|
||||||
|
{permissionConfig.menus
|
||||||
|
.filter(
|
||||||
|
(menu: Node) =>
|
||||||
|
!menu.key.startsWith("api") && !menu.key.startsWith("credential"),
|
||||||
|
)
|
||||||
|
.map((menu: Node) => (
|
||||||
|
<RenderMenu key={menu.key} menu={menu} />
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import apiFetch from "@/lib/apiFetch";
|
import apiFetch from "@/lib/apiFetch";
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
Flex,
|
Flex,
|
||||||
Group,
|
Group,
|
||||||
Input,
|
Input,
|
||||||
Modal,
|
Modal,
|
||||||
Stack,
|
Stack,
|
||||||
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";
|
||||||
@@ -24,404 +24,449 @@ import PermissionRole from "./PermissionRole";
|
|||||||
import PermissionTree from "./PermissionTree";
|
import PermissionTree from "./PermissionTree";
|
||||||
|
|
||||||
interface MenuNode {
|
interface MenuNode {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
default: boolean;
|
default: boolean;
|
||||||
children?: MenuNode[];
|
children?: MenuNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function UserRoleSetting({ permissions }: { permissions: JsonValue[] }) {
|
export default function UserRoleSetting({
|
||||||
const [btnDisable, setBtnDisable] = useState(true);
|
permissions,
|
||||||
const [btnLoading, setBtnLoading] = useState(false);
|
}: {
|
||||||
const [opened, { open, close }] = useDisclosure(false);
|
permissions: JsonValue[];
|
||||||
const [openedDelete, { open: openDelete, close: closeDelete }] =
|
}) {
|
||||||
useDisclosure(false);
|
const [btnDisable, setBtnDisable] = useState(true);
|
||||||
const [dataDelete, setDataDelete] = useState("");
|
const [btnLoading, setBtnLoading] = useState(false);
|
||||||
const {
|
const [opened, { open, close }] = useDisclosure(false);
|
||||||
data: dataRole,
|
const [openedDelete, { open: openDelete, close: closeDelete }] =
|
||||||
mutate: mutateRole,
|
useDisclosure(false);
|
||||||
isLoading: isLoadingRole,
|
const [dataDelete, setDataDelete] = useState("");
|
||||||
} = useSWR("user-role", () => apiFetch.api.user.role.get());
|
const {
|
||||||
const [openedTambah, { open: openTambah, close: closeTambah }] =
|
data: dataRole,
|
||||||
useDisclosure(false);
|
mutate: mutateRole,
|
||||||
const { data, mutate, isLoading } = useSWR("role-list", () =>
|
isLoading: isLoadingRole,
|
||||||
apiFetch.api.user.role.get(),
|
} = useSWR("user-role", () => apiFetch.api.user.role.get());
|
||||||
);
|
const [openedTambah, { open: openTambah, close: closeTambah }] =
|
||||||
const list = data?.data || [];
|
useDisclosure(false);
|
||||||
const listRole = dataRole?.data || [];
|
const { data, mutate, isLoading } = useSWR("role-list", () =>
|
||||||
const [dataEdit, setDataEdit] = useState({
|
apiFetch.api.user.role.get(),
|
||||||
id: "",
|
);
|
||||||
name: "",
|
const list = data?.data || [];
|
||||||
permissions: [],
|
const listRole = dataRole?.data || [];
|
||||||
});
|
const [dataEdit, setDataEdit] = useState({
|
||||||
const [dataTambah, setDataTambah] = useState({
|
id: "",
|
||||||
name: "",
|
name: "",
|
||||||
permissions: [],
|
permissions: [],
|
||||||
});
|
});
|
||||||
const [error, setError] = useState({
|
const [dataTambah, setDataTambah] = useState({
|
||||||
name: false,
|
name: "",
|
||||||
permissions: false,
|
permissions: [],
|
||||||
});
|
});
|
||||||
|
const [error, setError] = useState({
|
||||||
|
name: false,
|
||||||
|
permissions: false,
|
||||||
|
});
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
mutate();
|
mutate();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
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(
|
||||||
if (res.status === 200) {
|
dataTambah as any,
|
||||||
mutate();
|
);
|
||||||
closeTambah();
|
if (res.status === 200) {
|
||||||
setDataTambah({
|
mutate();
|
||||||
name: "",
|
closeTambah();
|
||||||
permissions: [],
|
setDataTambah({
|
||||||
});
|
name: "",
|
||||||
notification({
|
permissions: [],
|
||||||
title: "Success",
|
});
|
||||||
message: "Your role have been saved",
|
notification({
|
||||||
type: "success",
|
title: "Success",
|
||||||
});
|
message: "Your role have been saved",
|
||||||
} else {
|
type: "success",
|
||||||
notification({
|
});
|
||||||
title: "Error",
|
|
||||||
message: "Failed to create role",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
notification({
|
|
||||||
title: "Error",
|
|
||||||
message: "Failed to create role",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setBtnLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleEdit() {
|
|
||||||
try {
|
|
||||||
setBtnLoading(true);
|
|
||||||
const res = await apiFetch.api.user["role-update"].post(dataEdit as any);
|
|
||||||
if (res.status === 200) {
|
|
||||||
mutate();
|
|
||||||
close();
|
|
||||||
notification({
|
|
||||||
title: "Success",
|
|
||||||
message: "Your role have been saved",
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
notification({
|
|
||||||
title: "Error",
|
|
||||||
message: "Failed to edit role",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
notification({
|
|
||||||
title: "Error",
|
|
||||||
message: "Failed to edit role",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setBtnLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDelete() {
|
|
||||||
try {
|
|
||||||
setBtnLoading(true);
|
|
||||||
const res = await apiFetch.api.user["role-delete"].post({ id: dataDelete });
|
|
||||||
if (res.status === 200) {
|
|
||||||
mutate();
|
|
||||||
closeDelete();
|
|
||||||
notification({
|
|
||||||
title: "Success",
|
|
||||||
message: "Your role have been deleted",
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
notification({
|
|
||||||
title: "Error",
|
|
||||||
message: "Failed to delete role",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
notification({
|
|
||||||
title: "Error",
|
|
||||||
message: "Failed to delete role",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setBtnLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function chooseEdit({ data }: { data: { id: string; name: string; permissions: []; }; }) {
|
|
||||||
setDataEdit({
|
|
||||||
id: data.id, name: data.name, permissions: data.permissions ? data.permissions : []
|
|
||||||
});
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onValidation({ kat, value, aksi, }: { kat: "name" | "permission"; value: string | null; aksi: "edit" | "tambah"; }) {
|
|
||||||
if (value == null || value.length < 1) {
|
|
||||||
setBtnDisable(true);
|
|
||||||
setError({ ...error, [kat]: true });
|
|
||||||
} else {
|
} else {
|
||||||
setBtnDisable(false);
|
notification({
|
||||||
setError({ ...error, [kat]: false });
|
title: "Error",
|
||||||
|
message: "Failed to create role",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
if (aksi === "edit") {
|
console.error(error);
|
||||||
setDataEdit({ ...dataEdit, [kat]: value });
|
notification({
|
||||||
} else {
|
title: "Error",
|
||||||
setDataTambah({ ...dataTambah, [kat]: value });
|
message: "Failed to create role",
|
||||||
}
|
type: "error",
|
||||||
}
|
|
||||||
|
|
||||||
function buildOrderList(menus: MenuNode[]): string[] {
|
|
||||||
const list: string[] = [];
|
|
||||||
|
|
||||||
const traverse = (nodes: MenuNode[]) => {
|
|
||||||
nodes.forEach((node) => {
|
|
||||||
list.push(node.key);
|
|
||||||
if (node.children) traverse(node.children);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
traverse(menus);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortByJsonOrder(arrayData: string[]): string[] {
|
|
||||||
const orderList = buildOrderList(listMenu.menus);
|
|
||||||
|
|
||||||
return arrayData.sort((a, b) => {
|
|
||||||
return orderList.indexOf(a) - orderList.indexOf(b);
|
|
||||||
});
|
});
|
||||||
}
|
} finally {
|
||||||
|
setBtnLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useShallowEffect(() => {
|
async function handleEdit() {
|
||||||
if (dataEdit.name.length > 0) {
|
try {
|
||||||
setBtnDisable(false);
|
setBtnLoading(true);
|
||||||
|
const res = await apiFetch.api.user["role-update"].post(dataEdit as any);
|
||||||
|
if (res.status === 200) {
|
||||||
|
mutate();
|
||||||
|
close();
|
||||||
|
notification({
|
||||||
|
title: "Success",
|
||||||
|
message: "Your role have been saved",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to edit role",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [dataEdit.id]);
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to edit role",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setBtnLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
async function handleDelete() {
|
||||||
<>
|
try {
|
||||||
{/* Modal Edit */}
|
setBtnLoading(true);
|
||||||
<Modal
|
const res = await apiFetch.api.user["role-delete"].post({
|
||||||
opened={opened}
|
id: dataDelete,
|
||||||
onClose={close}
|
});
|
||||||
title={"Edit"}
|
if (res.status === 200) {
|
||||||
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
mutate();
|
||||||
size={"lg"}
|
closeDelete();
|
||||||
>
|
notification({
|
||||||
<Stack gap="ld">
|
title: "Success",
|
||||||
<Input.Wrapper label="Nama Role">
|
message: "Your role have been deleted",
|
||||||
<Input
|
type: "success",
|
||||||
value={dataEdit.name}
|
});
|
||||||
onChange={(e) =>
|
} else {
|
||||||
onValidation({
|
notification({
|
||||||
kat: "name",
|
title: "Error",
|
||||||
value: e.target.value,
|
message: "Failed to delete role",
|
||||||
aksi: "edit",
|
type: "error",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
/>
|
} catch (error) {
|
||||||
</Input.Wrapper>
|
console.error(error);
|
||||||
<PermissionTree
|
notification({
|
||||||
selected={dataEdit.permissions}
|
title: "Error",
|
||||||
onChange={(permissions) => {
|
message: "Failed to delete role",
|
||||||
setDataEdit({ ...dataEdit, permissions: sortByJsonOrder(permissions) as never[] });
|
type: "error",
|
||||||
}}
|
});
|
||||||
/>
|
} finally {
|
||||||
<Group justify="center" grow>
|
setBtnLoading(false);
|
||||||
<Button variant="light" onClick={close}>
|
}
|
||||||
Batal
|
}
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="filled"
|
|
||||||
onClick={handleEdit}
|
|
||||||
disabled={
|
|
||||||
btnDisable ||
|
|
||||||
dataEdit.name.length < 1 ||
|
|
||||||
dataEdit.permissions?.length < 1
|
|
||||||
}
|
|
||||||
loading={btnLoading}
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
{/* Modal Tambah */}
|
function chooseEdit({
|
||||||
<Modal
|
data,
|
||||||
opened={openedTambah}
|
}: {
|
||||||
onClose={closeTambah}
|
data: { id: string; name: string; permissions: [] };
|
||||||
title={"Tambah"}
|
}) {
|
||||||
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
setDataEdit({
|
||||||
size={"lg"}
|
id: data.id,
|
||||||
>
|
name: data.name,
|
||||||
<Stack gap="ld">
|
permissions: data.permissions ? data.permissions : [],
|
||||||
<Input.Wrapper
|
});
|
||||||
label="Nama Role"
|
open();
|
||||||
description=""
|
}
|
||||||
error={error.name ? "Field is required" : ""}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
value={dataTambah.name}
|
|
||||||
onChange={(e) =>
|
|
||||||
onValidation({
|
|
||||||
kat: "name",
|
|
||||||
value: e.target.value,
|
|
||||||
aksi: "tambah",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Input.Wrapper>
|
|
||||||
<PermissionTree
|
|
||||||
selected={dataTambah.permissions}
|
|
||||||
onChange={(permissions) => {
|
|
||||||
setDataTambah({ ...dataTambah, permissions: sortByJsonOrder(permissions) as never[] });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Group justify="center" grow>
|
|
||||||
<Button variant="light" onClick={closeTambah}>
|
|
||||||
Batal
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="filled"
|
|
||||||
onClick={handleCreate}
|
|
||||||
disabled={
|
|
||||||
btnDisable ||
|
|
||||||
dataTambah.name.length < 1 ||
|
|
||||||
dataTambah.permissions.length < 1
|
|
||||||
}
|
|
||||||
loading={btnLoading}
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
{/* Modal Delete */}
|
function onValidation({
|
||||||
<Modal
|
kat,
|
||||||
opened={openedDelete}
|
value,
|
||||||
onClose={closeDelete}
|
aksi,
|
||||||
title={"Delete"}
|
}: {
|
||||||
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
kat: "name" | "permission";
|
||||||
>
|
value: string | null;
|
||||||
<Stack gap="md">
|
aksi: "edit" | "tambah";
|
||||||
<Text size="md" color="gray.6">
|
}) {
|
||||||
Apakah anda yakin ingin menghapus role ini?
|
if (value == null || value.length < 1) {
|
||||||
</Text>
|
setBtnDisable(true);
|
||||||
<Group justify="center" grow>
|
setError({ ...error, [kat]: true });
|
||||||
<Button variant="light" onClick={closeDelete}>
|
} else {
|
||||||
Batal
|
setBtnDisable(false);
|
||||||
</Button>
|
setError({ ...error, [kat]: false });
|
||||||
<Button
|
}
|
||||||
variant="filled"
|
|
||||||
color="red"
|
|
||||||
onClick={handleDelete}
|
|
||||||
loading={btnLoading}
|
|
||||||
>
|
|
||||||
Hapus
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Stack gap={"md"}>
|
if (aksi === "edit") {
|
||||||
<Flex align="center" justify="space-between">
|
setDataEdit({ ...dataEdit, [kat]: value });
|
||||||
<Title order={4} c="gray.2">
|
} else {
|
||||||
Daftar Role
|
setDataTambah({ ...dataTambah, [kat]: value });
|
||||||
</Title>
|
}
|
||||||
{
|
}
|
||||||
permissions.includes('setting.user_role.tambah') && (
|
|
||||||
<Tooltip label="Tambah Role">
|
function buildOrderList(menus: MenuNode[]): string[] {
|
||||||
<Button
|
const list: string[] = [];
|
||||||
variant="light"
|
|
||||||
leftSection={<IconPlus size={20} />}
|
const traverse = (nodes: MenuNode[]) => {
|
||||||
onClick={openTambah}
|
nodes.forEach((node) => {
|
||||||
|
list.push(node.key);
|
||||||
|
if (node.children) traverse(node.children);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
traverse(menus);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortByJsonOrder(arrayData: string[]): string[] {
|
||||||
|
const orderList = buildOrderList(listMenu.menus);
|
||||||
|
|
||||||
|
return arrayData.sort((a, b) => {
|
||||||
|
return orderList.indexOf(a) - orderList.indexOf(b);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
if (dataEdit.name.length > 0) {
|
||||||
|
setBtnDisable(false);
|
||||||
|
}
|
||||||
|
}, [dataEdit.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Modal Edit */}
|
||||||
|
<Modal
|
||||||
|
opened={opened}
|
||||||
|
onClose={close}
|
||||||
|
title={"Edit"}
|
||||||
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
|
size={"lg"}
|
||||||
|
>
|
||||||
|
<Stack gap="ld">
|
||||||
|
<Input.Wrapper label="Nama Role">
|
||||||
|
<Input
|
||||||
|
value={dataEdit.name}
|
||||||
|
onChange={(e) =>
|
||||||
|
onValidation({
|
||||||
|
kat: "name",
|
||||||
|
value: e.target.value,
|
||||||
|
aksi: "edit",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Input.Wrapper>
|
||||||
|
<PermissionTree
|
||||||
|
selected={dataEdit.permissions}
|
||||||
|
onChange={(permissions) => {
|
||||||
|
setDataEdit({
|
||||||
|
...dataEdit,
|
||||||
|
permissions: sortByJsonOrder(permissions) as never[],
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Group justify="center" grow>
|
||||||
|
<Button variant="light" onClick={close}>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="filled"
|
||||||
|
onClick={handleEdit}
|
||||||
|
disabled={
|
||||||
|
btnDisable ||
|
||||||
|
dataEdit.name.length < 1 ||
|
||||||
|
dataEdit.permissions?.length < 1
|
||||||
|
}
|
||||||
|
loading={btnLoading}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{/* Modal Tambah */}
|
||||||
|
<Modal
|
||||||
|
opened={openedTambah}
|
||||||
|
onClose={closeTambah}
|
||||||
|
title={"Tambah"}
|
||||||
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
|
size={"lg"}
|
||||||
|
>
|
||||||
|
<Stack gap="ld">
|
||||||
|
<Input.Wrapper
|
||||||
|
label="Nama Role"
|
||||||
|
description=""
|
||||||
|
error={error.name ? "Field is required" : ""}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={dataTambah.name}
|
||||||
|
onChange={(e) =>
|
||||||
|
onValidation({
|
||||||
|
kat: "name",
|
||||||
|
value: e.target.value,
|
||||||
|
aksi: "tambah",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Input.Wrapper>
|
||||||
|
<PermissionTree
|
||||||
|
selected={dataTambah.permissions}
|
||||||
|
onChange={(permissions) => {
|
||||||
|
setDataTambah({
|
||||||
|
...dataTambah,
|
||||||
|
permissions: sortByJsonOrder(permissions) as never[],
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Group justify="center" grow>
|
||||||
|
<Button variant="light" onClick={closeTambah}>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="filled"
|
||||||
|
onClick={handleCreate}
|
||||||
|
disabled={
|
||||||
|
btnDisable ||
|
||||||
|
dataTambah.name.length < 1 ||
|
||||||
|
dataTambah.permissions.length < 1
|
||||||
|
}
|
||||||
|
loading={btnLoading}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{/* Modal Delete */}
|
||||||
|
<Modal
|
||||||
|
opened={openedDelete}
|
||||||
|
onClose={closeDelete}
|
||||||
|
title={"Delete"}
|
||||||
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
<Text size="md" color="gray.6">
|
||||||
|
Apakah anda yakin ingin menghapus role ini?
|
||||||
|
</Text>
|
||||||
|
<Group justify="center" grow>
|
||||||
|
<Button variant="light" onClick={closeDelete}>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="filled"
|
||||||
|
color="red"
|
||||||
|
onClick={handleDelete}
|
||||||
|
loading={btnLoading}
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Stack gap={"md"}>
|
||||||
|
<Flex align="center" justify="space-between">
|
||||||
|
<Title order={4} c="gray.2">
|
||||||
|
Daftar Role
|
||||||
|
</Title>
|
||||||
|
{permissions.includes("setting.user_role.tambah") && (
|
||||||
|
<Tooltip label="Tambah Role">
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
leftSection={<IconPlus size={20} />}
|
||||||
|
onClick={openTambah}
|
||||||
|
>
|
||||||
|
Tambah
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
<Divider my={0} />
|
||||||
|
<Stack gap={"md"}>
|
||||||
|
<Table highlightOnHover>
|
||||||
|
<Table.Thead>
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Th>Role</Table.Th>
|
||||||
|
<Table.Th>Permission</Table.Th>
|
||||||
|
<Table.Th>Aksi</Table.Th>
|
||||||
|
</Table.Tr>
|
||||||
|
</Table.Thead>
|
||||||
|
<Table.Tbody>
|
||||||
|
{list.length > 0 ? (
|
||||||
|
list?.map((v: any) => (
|
||||||
|
<Table.Tr key={v.id}>
|
||||||
|
<Table.Td w={"150"}>{v.name}</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<PermissionRole permissions={v.permissions} />
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td w={"100"}>
|
||||||
|
<Group>
|
||||||
|
<Tooltip
|
||||||
|
label={
|
||||||
|
permissions.includes("setting.user_role.edit")
|
||||||
|
? "Edit Role"
|
||||||
|
: "Edit Role - Anda tidak memiliki akses"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Tambah
|
<ActionIcon
|
||||||
</Button>
|
variant="light"
|
||||||
</Tooltip>
|
size="sm"
|
||||||
)
|
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
|
||||||
}
|
onClick={() => chooseEdit({ data: v })}
|
||||||
</Flex>
|
disabled={
|
||||||
<Divider my={0} />
|
!permissions.includes("setting.user_role.edit") ||
|
||||||
<Stack gap={"md"}>
|
v.id == "developer"
|
||||||
<Table highlightOnHover>
|
}
|
||||||
<Table.Thead>
|
>
|
||||||
<Table.Tr>
|
<IconEdit size={20} />
|
||||||
<Table.Th>Role</Table.Th>
|
</ActionIcon>
|
||||||
<Table.Th>Permission</Table.Th>
|
</Tooltip>
|
||||||
<Table.Th>Aksi</Table.Th>
|
<Tooltip
|
||||||
</Table.Tr>
|
label={
|
||||||
</Table.Thead>
|
permissions.includes("setting.user_role.delete")
|
||||||
<Table.Tbody>
|
? "Delete Role"
|
||||||
{list.length > 0 ? (
|
: "Delete Role - Anda tidak memiliki akses"
|
||||||
list?.map((v: any) => (
|
}
|
||||||
<Table.Tr key={v.id}>
|
>
|
||||||
<Table.Td w={"150"}>{v.name}</Table.Td>
|
<ActionIcon
|
||||||
<Table.Td>
|
variant="light"
|
||||||
<PermissionRole permissions={v.permissions} />
|
size="sm"
|
||||||
</Table.Td>
|
color="red"
|
||||||
<Table.Td w={"100"}>
|
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
|
||||||
<Group>
|
onClick={() => {
|
||||||
<Tooltip label={permissions.includes('setting.user_role.edit') ? "Edit Role" : "Edit Role - Anda tidak memiliki akses"}>
|
setDataDelete(v.id);
|
||||||
<ActionIcon
|
openDelete();
|
||||||
variant="light"
|
}}
|
||||||
size="sm"
|
disabled={
|
||||||
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
|
!permissions.includes(
|
||||||
onClick={() => chooseEdit({ data: v })}
|
"setting.user_role.delete",
|
||||||
disabled={!permissions.includes('setting.user_role.edit') || v.id == "developer"}
|
) || v.id == "developer"
|
||||||
>
|
}
|
||||||
<IconEdit size={20} />
|
>
|
||||||
</ActionIcon>
|
<IconTrash size={20} />
|
||||||
</Tooltip>
|
</ActionIcon>
|
||||||
<Tooltip label={permissions.includes('setting.user_role.delete') ? "Delete Role" : "Delete Role - Anda tidak memiliki akses"}>
|
</Tooltip>
|
||||||
<ActionIcon
|
</Group>
|
||||||
variant="light"
|
</Table.Td>
|
||||||
size="sm"
|
</Table.Tr>
|
||||||
color="red"
|
))
|
||||||
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
|
) : (
|
||||||
onClick={() => {
|
<Table.Tr>
|
||||||
setDataDelete(v.id);
|
<Table.Td colSpan={5} align="center">
|
||||||
openDelete();
|
Data Role Tidak Ditemukan
|
||||||
}}
|
</Table.Td>
|
||||||
disabled={!permissions.includes('setting.user_role.delete') || v.id == "developer"}
|
</Table.Tr>
|
||||||
>
|
)}
|
||||||
<IconTrash size={20} />
|
</Table.Tbody>
|
||||||
</ActionIcon>
|
</Table>
|
||||||
</Tooltip>
|
</Stack>
|
||||||
</Group>
|
</Stack>
|
||||||
</Table.Td>
|
</>
|
||||||
</Table.Tr>
|
);
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td colSpan={5} align="center">
|
|
||||||
Data Role Tidak Ditemukan
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
)}
|
|
||||||
</Table.Tbody>
|
|
||||||
</Table>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,20 +441,17 @@ 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"
|
leftSection={<IconPlus size={20} />}
|
||||||
leftSection={<IconPlus size={20} />}
|
onClick={openTambah}
|
||||||
onClick={openTambah}
|
>
|
||||||
>
|
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>
|
||||||
|
|||||||
@@ -3,157 +3,242 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKBedaBiodataDiri({ data }: { data: any }) {
|
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 () => {
|
||||||
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ lineHeight: "1.25" }}>
|
|
||||||
{/* HEADER */}
|
|
||||||
<div style={{ textAlign: "center", marginBottom: "15px" }}>
|
|
||||||
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br />
|
|
||||||
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br />
|
|
||||||
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br />
|
|
||||||
Alamat: {data.setting.desaAlamat}<br />
|
|
||||||
Kode Pos: {data.setting.desaPos}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* JUDUL */}
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<b><u>SURAT KETERANGAN BEDA BIODATA DIRI</u></b><br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* YANG BERTANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
Yang bertanda tangan di bawah ini:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "160px" }}>Nama</td>
|
|
||||||
<td style={{ width: "10px" }}>:</td>
|
|
||||||
<td>{data.setting.perbekelNama}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Jabatan</td>
|
|
||||||
<td>:</td>
|
|
||||||
<td>{data.setting.perbekelJabatan + " " + 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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* IDENTITAS ORANG YG MEMINTA SURAT */}
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
Dengan ini menerangkan bahwa berdasarkan keterangan dari yang bersangkutan:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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:
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>1. Nama</td><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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>2. Tempat/Tanggal Lahir</td><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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>3. Nama Orang Tua</td><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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
Perbedaan tersebut terjadi karena <b>kesalahan penulisan/pencatatan administratif</b>, namun yang bersangkutan adalah <b>orang yang sama</b>.
|
|
||||||
<br />
|
|
||||||
Dengan surat keterangan ini dibuat dengan sebenar-benarnya untuk dipergunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
Dikeluarkan di {data.setting.desaNama} <br />
|
|
||||||
Pada tanggal {data.surat.createdAt}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "0px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
|
||||||
<br /><br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.25" }}>
|
||||||
|
{/* HEADER */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "15px" }}>
|
||||||
|
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
|
||||||
|
<br />
|
||||||
|
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
|
||||||
|
<br />
|
||||||
|
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
|
||||||
|
<br />
|
||||||
|
Alamat: {data.setting.desaAlamat}
|
||||||
|
<br />
|
||||||
|
Kode Pos: {data.setting.desaPos}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* JUDUL */}
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<b>
|
||||||
|
<u>SURAT KETERANGAN BEDA BIODATA DIRI</u>
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
Nomor: {data.surat.noSurat}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* YANG BERTANDA TANGAN */}
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
Yang bertanda tangan di bawah ini:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{data.setting.perbekelNama}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Jabatan</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>
|
||||||
|
{data.setting.perbekelJabatan + " " + 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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* IDENTITAS ORANG YG MEMINTA SURAT */}
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
Dengan ini menerangkan bahwa berdasarkan keterangan dari yang
|
||||||
|
bersangkutan:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{getValue("nama")}</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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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:
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>1. Nama</td>
|
||||||
|
<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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>2. Tempat/Tanggal Lahir</td>
|
||||||
|
<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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>3. Nama Orang Tua</td>
|
||||||
|
<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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
Perbedaan tersebut terjadi karena{" "}
|
||||||
|
<b>kesalahan penulisan/pencatatan administratif</b>, namun yang
|
||||||
|
bersangkutan adalah <b>orang yang sama</b>.
|
||||||
|
<br />
|
||||||
|
Dengan surat keterangan ini dibuat dengan sebenar-benarnya untuk
|
||||||
|
dipergunakan sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
Dikeluarkan di {data.setting.desaNama} <br />
|
||||||
|
Pada tanggal {data.surat.createdAt}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: "0px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,108 +3,166 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKBelumKawin({ data }: { data: any }) {
|
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 () => {
|
||||||
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ lineHeight: "1.3" }}>
|
|
||||||
{/* HEADER */}
|
|
||||||
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
|
||||||
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br />
|
|
||||||
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br />
|
|
||||||
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br />
|
|
||||||
Alamat: {data.setting.desaAlamat}<br />
|
|
||||||
Kode Pos: {data.setting.desaPos}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* JUDUL */}
|
|
||||||
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
|
||||||
<b><u>SURAT KETERANGAN BELUM KAWIN</u></b><br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* YANG BERTANDA TANGAN */}
|
|
||||||
<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:
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* IDENTITAS ORANG YG MEMINTA SURAT */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></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>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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Berdasarkan keterangan dari yang bersangkutan dan data administrasi 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 style={{ marginTop: "20px" }}>
|
|
||||||
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dikeluarkan di {data.setting.desaNama} <br />
|
|
||||||
Pada tanggal {data.surat.createdAt}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "40px", display: "flex", justifyContent: "space-between", width: "100%" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<br /><br />
|
|
||||||
Pemohon
|
|
||||||
<br /><br /><br /><br /><br /><br />
|
|
||||||
<u>{getValue("nama")}</u> <br />
|
|
||||||
</div>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<br /><br />
|
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
|
||||||
<br /><br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.3" }}>
|
||||||
|
{/* HEADER */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
||||||
|
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
|
||||||
|
<br />
|
||||||
|
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
|
||||||
|
<br />
|
||||||
|
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
|
||||||
|
<br />
|
||||||
|
Alamat: {data.setting.desaAlamat}
|
||||||
|
<br />
|
||||||
|
Kode Pos: {data.setting.desaPos}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* JUDUL */}
|
||||||
|
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
||||||
|
<b>
|
||||||
|
<u>SURAT KETERANGAN BELUM KAWIN</u>
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
Nomor: {data.surat.noSurat}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* YANG BERTANDA TANGAN */}
|
||||||
|
<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:
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* IDENTITAS ORANG YG MEMINTA SURAT */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{getValue("nama")}</td>
|
||||||
|
</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>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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Berdasarkan keterangan dari yang bersangkutan dan data administrasi
|
||||||
|
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 style={{ marginTop: "20px" }}>
|
||||||
|
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat
|
||||||
|
digunakan sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dikeluarkan di {data.setting.desaNama} <br />
|
||||||
|
Pada tanggal {data.surat.createdAt}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: "40px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Pemohon
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<u>{getValue("nama")}</u> <br />
|
||||||
|
</div>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,121 +3,163 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKDomisiliOrganisasi({ data }: { data: any }) {
|
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 () => {
|
||||||
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ lineHeight: "1.3" }}>
|
|
||||||
{/* HEADER */}
|
|
||||||
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
|
||||||
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br />
|
|
||||||
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br />
|
|
||||||
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br />
|
|
||||||
Alamat: {data.setting.desaAlamat}<br />
|
|
||||||
Kode Pos: {data.setting.desaPos}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* JUDUL */}
|
|
||||||
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
|
||||||
<b><u>SURAT KETERANGAN DOMISILI ORGANISASI</u></b><br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* YANG BERTANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
Yang bertanda tangan di bawah ini:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "160px" }}>Nama</td>
|
|
||||||
<td style={{ width: "10px" }}>:</td>
|
|
||||||
<td>{data.setting.perbekelNama}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Jabatan</td>
|
|
||||||
<td>:</td>
|
|
||||||
<td>{data.setting.perbekelJabatan}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Alamat Kantor</td>
|
|
||||||
<td>:</td>
|
|
||||||
<td>{data.setting.desaAlamat}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* IDENTITAS ORANG YG MEMINTA SURAT */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dengan ini menerangkan bahwa:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Nama Organisasi</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Benar bahwa organisasi tersebut berdomisili di wilayah Desa / Kelurahan {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")}.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dikeluarkan di {data.setting.desaNama} <br />
|
|
||||||
Pada tanggal {data.surat.createdAt}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<br />
|
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
|
||||||
<br /><br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.3" }}>
|
||||||
|
{/* HEADER */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
||||||
|
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
|
||||||
|
<br />
|
||||||
|
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
|
||||||
|
<br />
|
||||||
|
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
|
||||||
|
<br />
|
||||||
|
Alamat: {data.setting.desaAlamat}
|
||||||
|
<br />
|
||||||
|
Kode Pos: {data.setting.desaPos}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* JUDUL */}
|
||||||
|
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
||||||
|
<b>
|
||||||
|
<u>SURAT KETERANGAN DOMISILI ORGANISASI</u>
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
Nomor: {data.surat.noSurat}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* YANG BERTANDA TANGAN */}
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
Yang bertanda tangan di bawah ini:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{data.setting.perbekelNama}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Jabatan</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{data.setting.perbekelJabatan}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Alamat Kantor</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{data.setting.desaAlamat}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* IDENTITAS ORANG YG MEMINTA SURAT */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dengan ini menerangkan bahwa:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama Organisasi</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{getValue("nama")}</td>
|
||||||
|
</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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Benar bahwa organisasi tersebut berdomisili di wilayah Desa / Kelurahan{" "}
|
||||||
|
{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")}.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat
|
||||||
|
digunakan sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dikeluarkan di {data.setting.desaNama} <br />
|
||||||
|
Pada tanggal {data.surat.createdAt}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: "40px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br />
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,142 +3,244 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKKelahiran({ data }: { data: any }) {
|
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 () => {
|
||||||
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ lineHeight: "1.2" }}>
|
|
||||||
|
|
||||||
{/* HEADER */}
|
|
||||||
<div style={{ textAlign: "center", marginBottom: "20px" }}>
|
|
||||||
<b>PEMERINTAH KABUPATEN/KOTA {_.upperCase(data.setting.desaKabupaten)}</b><br />
|
|
||||||
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br />
|
|
||||||
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br />
|
|
||||||
Alamat: {data.setting.desaAlamat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* JUDUL */}
|
|
||||||
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
|
||||||
<b><u>SURAT KETERANGAN KELAHIRAN</u></b><br />
|
|
||||||
Nomor : {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* PEMBUKA */}
|
|
||||||
<div>
|
|
||||||
Yang bertanda tangan di bawah ini, {data.setting.perbekelJabatan}
|
|
||||||
{` ${data.setting.desaNama}, Kecamatan ${data.setting.desaKecamatan}, Kabupaten/Kota ${data.setting.desaKabupaten}`}
|
|
||||||
, dengan ini menerangkan bahwa:
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* DATA KELAHIRAN ANAK */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Telah lahir seorang anak pada:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "200px" }}>Tanggal Lahir</td><td>:</td><td>{getValue("tanggal lahir 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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* DATA IBU */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dari seorang ibu bernama:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "200px" }}>Nama Lengkap Ibu</td><td>:</td><td>{getValue("nama ibu")}</td></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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* DATA AYAH */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dan seorang ayah bernama:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "200px" }}>Nama Lengkap Ayah</td><td>:</td><td>{getValue("nama ayah")}</td></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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* DATA PELAPOR */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Berdasarkan laporan dari:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "200px" }}>Nama Pelapor</td><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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* PENUTUP */}
|
|
||||||
<div style={{ marginTop: "20px", textAlign: "justify" }}>
|
|
||||||
Demikian Surat Keterangan Kelahiran ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TEMPAT TANGGAL */}
|
|
||||||
<table style={{ width: "100%", marginTop: "20px" }}>
|
|
||||||
<tbody>
|
|
||||||
<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>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "40px", width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
|
||||||
<br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.2" }}>
|
||||||
|
{/* HEADER */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "20px" }}>
|
||||||
|
<b>
|
||||||
|
PEMERINTAH KABUPATEN/KOTA {_.upperCase(data.setting.desaKabupaten)}
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
|
||||||
|
<br />
|
||||||
|
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
|
||||||
|
<br />
|
||||||
|
Alamat: {data.setting.desaAlamat}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* JUDUL */}
|
||||||
|
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
||||||
|
<b>
|
||||||
|
<u>SURAT KETERANGAN KELAHIRAN</u>
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
Nomor : {data.surat.noSurat}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* PEMBUKA */}
|
||||||
|
<div>
|
||||||
|
Yang bertanda tangan di bawah ini, {data.setting.perbekelJabatan}
|
||||||
|
{` ${data.setting.desaNama}, Kecamatan ${data.setting.desaKecamatan}, Kabupaten/Kota ${data.setting.desaKabupaten}`}
|
||||||
|
, dengan ini menerangkan bahwa:
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DATA KELAHIRAN ANAK */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Telah lahir seorang anak pada:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "200px" }}>Tanggal Lahir</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{getValue("tanggal lahir 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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DATA IBU */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dari seorang ibu bernama:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "200px" }}>Nama Lengkap Ibu</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{getValue("nama ibu")}</td>
|
||||||
|
</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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DATA AYAH */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dan seorang ayah bernama:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "200px" }}>Nama Lengkap Ayah</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{getValue("nama ayah")}</td>
|
||||||
|
</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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DATA PELAPOR */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Berdasarkan laporan dari:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "200px" }}>Nama Pelapor</td>
|
||||||
|
<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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* PENUTUP */}
|
||||||
|
<div style={{ marginTop: "20px", textAlign: "justify" }}>
|
||||||
|
Demikian Surat Keterangan Kelahiran ini dibuat dengan sebenarnya agar
|
||||||
|
dapat digunakan sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TEMPAT TANGGAL */}
|
||||||
|
<table style={{ width: "100%", marginTop: "20px" }}>
|
||||||
|
<tbody>
|
||||||
|
<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>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: "40px",
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,143 +3,153 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKKelakuanBaik({ data }: { data: any }) {
|
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 () => {
|
||||||
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ lineHeight: "1.3" }}>
|
|
||||||
|
|
||||||
{/* HEADER */}
|
|
||||||
<div style={{ textAlign: "center", marginBottom: "30px" }}>
|
|
||||||
<b style={{ fontSize: "18px" }}>SURAT KETERANGAN KELAKUAN BAIK</b><br />
|
|
||||||
(PENGANTAR SKCK)<br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* PEMBUKA */}
|
|
||||||
<div style={{ marginBottom: "15px" }}>
|
|
||||||
Yang bertanda tangan di bawah ini menerangkan dengan sebenarnya bahwa:
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* IDENTITAS PENDUDUK */}
|
|
||||||
<table style={{ width: "100%", marginBottom: "15px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "180px" }}>Nama lengkap</td>
|
|
||||||
<td style={{ width: "10px" }}>:</td>
|
|
||||||
<td>{getValue("nama")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>NIK</td>
|
|
||||||
<td>:</td>
|
|
||||||
<td>{getValue("nik")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Tempat/Tgl 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>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{/* ISI */}
|
|
||||||
<div style={{ textAlign: "justify", marginBottom: "15px" }}>
|
|
||||||
Adalah benar penduduk yang berdomisili di wilayah kami dan selama tinggal di lingkungan
|
|
||||||
Desa {data.setting.desaNama}, berkelakuan baik, tidak pernah terlibat perbuatan melanggar hukum,
|
|
||||||
serta dikenal sopan dan aktif dalam kegiatan kemasyarakatan.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ textAlign: "justify", marginBottom: "15px" }}>
|
|
||||||
Surat keterangan ini diberikan sebagai pengantar permohonan penerbitan Surat Keterangan
|
|
||||||
Catatan Kepolisian (SKCK) ke Polsek/Polres {getValue("polsek")}.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ textAlign: "justify", marginBottom: "15px" }}>
|
|
||||||
Surat ini berlaku selama 6 (enam) bulan sejak tanggal diterbitkan, kecuali terdapat perubahan
|
|
||||||
data yang mendasar.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ textAlign: "justify", marginBottom: "20px" }}>
|
|
||||||
Demikian surat keterangan ini dibuat dengan sebenarnya untuk dipergunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TANGGAL */}
|
|
||||||
<table style={{ width: "100%", marginBottom: "40px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "180px" }}>Dikeluarkan di</td>
|
|
||||||
<td style={{ width: "10px" }}>:</td>
|
|
||||||
<td>{data.setting.desaNama}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Pada tanggal</td>
|
|
||||||
<td>:</td>
|
|
||||||
<td>{data.surat.createdAt}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
Kepala Desa {data.setting.desaNama}
|
|
||||||
<br /> <br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
|
||||||
<u>{data.setting.perbekelNama}</u><br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.3" }}>
|
||||||
|
{/* HEADER */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "30px" }}>
|
||||||
|
<b style={{ fontSize: "18px" }}>SURAT KETERANGAN KELAKUAN BAIK</b>
|
||||||
|
<br />
|
||||||
|
(PENGANTAR SKCK)
|
||||||
|
<br />
|
||||||
|
Nomor: {data.surat.noSurat}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* PEMBUKA */}
|
||||||
|
<div style={{ marginBottom: "15px" }}>
|
||||||
|
Yang bertanda tangan di bawah ini menerangkan dengan sebenarnya bahwa:
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* IDENTITAS PENDUDUK */}
|
||||||
|
<table style={{ width: "100%", marginBottom: "15px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "180px" }}>Nama lengkap</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{getValue("nama")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>NIK</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{getValue("nik")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tempat/Tgl 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>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{/* ISI */}
|
||||||
|
<div style={{ textAlign: "justify", marginBottom: "15px" }}>
|
||||||
|
Adalah benar penduduk yang berdomisili di wilayah kami dan selama
|
||||||
|
tinggal di lingkungan Desa {data.setting.desaNama}, berkelakuan baik,
|
||||||
|
tidak pernah terlibat perbuatan melanggar hukum, serta dikenal sopan dan
|
||||||
|
aktif dalam kegiatan kemasyarakatan.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ textAlign: "justify", marginBottom: "15px" }}>
|
||||||
|
Surat keterangan ini diberikan sebagai pengantar permohonan penerbitan
|
||||||
|
Surat Keterangan Catatan Kepolisian (SKCK) ke Polsek/Polres{" "}
|
||||||
|
{getValue("polsek")}.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ textAlign: "justify", marginBottom: "15px" }}>
|
||||||
|
Surat ini berlaku selama 6 (enam) bulan sejak tanggal diterbitkan,
|
||||||
|
kecuali terdapat perubahan data yang mendasar.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ textAlign: "justify", marginBottom: "20px" }}>
|
||||||
|
Demikian surat keterangan ini dibuat dengan sebenarnya untuk
|
||||||
|
dipergunakan sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TANGGAL */}
|
||||||
|
<table style={{ width: "100%", marginBottom: "40px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "180px" }}>Dikeluarkan di</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{data.setting.desaNama}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Pada tanggal</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{data.surat.createdAt}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
Kepala Desa {data.setting.desaNama}
|
||||||
|
<br /> <br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u>
|
||||||
|
<br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,118 +3,201 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKKematian({ data }: { data: any }) {
|
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 () => {
|
||||||
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ lineHeight: "1.3" }}>
|
|
||||||
{/* HEADER */}
|
|
||||||
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
|
||||||
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br />
|
|
||||||
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br />
|
|
||||||
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br />
|
|
||||||
Alamat: {data.setting.desaAlamat}<br />
|
|
||||||
Kode Pos: {data.setting.desaPos}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* JUDUL */}
|
|
||||||
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
|
||||||
<b><u>SURAT KETERANGAN KEMATIAN</u></b><br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* YANG BERTANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
Yang bertanda tangan di bawah ini:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</td></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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Melaporkan bahwa:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Telah meninggal dunia pada:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Tanggal Kematian</td><td style={{ width: "10px" }}>:</td><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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat digunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "40px", display: "flex", justifyContent: "space-between", width: "100%" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<br /><br />
|
|
||||||
Pemohon
|
|
||||||
<br /><br /><br /><br /> <br />
|
|
||||||
<u>{getValue("nama")}</u> <br />
|
|
||||||
</div>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<br />
|
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
|
||||||
<br /><br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br />
|
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.3" }}>
|
||||||
|
{/* HEADER */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
||||||
|
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
|
||||||
|
<br />
|
||||||
|
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
|
||||||
|
<br />
|
||||||
|
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
|
||||||
|
<br />
|
||||||
|
Alamat: {data.setting.desaAlamat}
|
||||||
|
<br />
|
||||||
|
Kode Pos: {data.setting.desaPos}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* JUDUL */}
|
||||||
|
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
||||||
|
<b>
|
||||||
|
<u>SURAT KETERANGAN KEMATIAN</u>
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
Nomor: {data.surat.noSurat}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* YANG BERTANDA TANGAN */}
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
Yang bertanda tangan di bawah ini:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{getValue("nama")}</td>
|
||||||
|
</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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Melaporkan bahwa:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{getValue("nama")}</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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Telah meninggal dunia pada:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Tanggal Kematian</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Demikian surat keterangan ini dibuat dengan sebenarnya agar dapat
|
||||||
|
digunakan sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: "40px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Pemohon
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br /> <br />
|
||||||
|
<u>{getValue("nama")}</u> <br />
|
||||||
|
</div>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br />
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,141 +3,180 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKPenghasilan({ data }: { data: any }) {
|
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 () => {
|
||||||
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
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 / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br />
|
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
|
||||||
Alamat: {data.setting.desaAlamat}<br />
|
<br />
|
||||||
Kode Pos: {data.setting.desaPos}
|
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
|
||||||
</div>
|
<br />
|
||||||
|
Alamat: {data.setting.desaAlamat}
|
||||||
{/* JUDUL */}
|
<br />
|
||||||
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
Kode Pos: {data.setting.desaPos}
|
||||||
<b><u>SURAT KETERANGAN PENGHASILAN</u></b><br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* YANG BERTANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
Yang bertanda tangan di bawah ini:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "160px" }}>Nama</td>
|
|
||||||
<td style={{ width: "10px" }}>:</td>
|
|
||||||
<td>{data.setting.perbekelNama}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Jabatan</td>
|
|
||||||
<td>:</td>
|
|
||||||
<td>{data.setting.perbekelJabatan}</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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* IDENTITAS */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dengan ini menerangkan bahwa:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Nama</td><td>:</td><td>{getValue("nama")}</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>Pekerjaan</td><td>:</td><td>{getValue("pekerjaan")}</td></tr>
|
|
||||||
<tr><td>Alamat</td><td>:</td><td>{getValue("alamat")}</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* PENGHASILAN */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Berdasarkan keterangan yang bersangkutan, orang tersebut memiliki penghasilan rata-rata:
|
|
||||||
<table style={{ width: "100%", marginTop: "10px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "160px" }}>Penghasilan</td>
|
|
||||||
<td style={{ width: "10px" }}>:</td>
|
|
||||||
<td>
|
|
||||||
Rp {getValue("penghasilan")}
|
|
||||||
{" "}
|
|
||||||
({getValue("penghasilan terbilang")}) per bulan
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* KEPERLUAN */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Surat keterangan ini dibuat untuk keperluan: <b>{getValue("alasan permohonan")}</b>.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat dipergunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TANGGAL & TANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dikeluarkan di {data.setting.desaNama} <br />
|
|
||||||
Pada tanggal {data.surat.createdAt}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
|
||||||
<br /> <br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br />
|
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* JUDUL */}
|
||||||
|
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
||||||
|
<b>
|
||||||
|
<u>SURAT KETERANGAN PENGHASILAN</u>
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
Nomor: {data.surat.noSurat}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* YANG BERTANDA TANGAN */}
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
Yang bertanda tangan di bawah ini:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{data.setting.perbekelNama}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Jabatan</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{data.setting.perbekelJabatan}</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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* IDENTITAS */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dengan ini menerangkan bahwa:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{getValue("nama")}</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>Pekerjaan</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{getValue("pekerjaan")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Alamat</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{getValue("alamat")}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* PENGHASILAN */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Berdasarkan keterangan yang bersangkutan, orang tersebut memiliki
|
||||||
|
penghasilan rata-rata:
|
||||||
|
<table style={{ width: "100%", marginTop: "10px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Penghasilan</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>
|
||||||
|
Rp {getValue("penghasilan")} (
|
||||||
|
{getValue("penghasilan terbilang")}) per bulan
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* KEPERLUAN */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Surat keterangan ini dibuat untuk keperluan:{" "}
|
||||||
|
<b>{getValue("alasan permohonan")}</b>.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat
|
||||||
|
dipergunakan sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TANGGAL & TANDA TANGAN */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dikeluarkan di {data.setting.desaNama} <br />
|
||||||
|
Pada tanggal {data.surat.createdAt}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: "40px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br /> <br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,119 +3,142 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
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>
|
||||||
<b style={{ fontSize: "16px" }}>SURAT KETERANGAN TEMPAT USAHA</b><br />
|
<br />
|
||||||
Nomor: {data.surat.noSurat}
|
Nomor: {data.surat.noSurat}
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ISI */}
|
|
||||||
<div>
|
|
||||||
<div style={{ marginBottom: "10px" }}>
|
|
||||||
Yang bertanda tangan dibawah ini, saya:
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* DATA PEJABAT */}
|
|
||||||
<div>
|
|
||||||
<Row label="Nama" value={data.setting.perbekelNama} />
|
|
||||||
<Row label="Jabatan" value={data.setting.perbekelJabatan} />
|
|
||||||
<Row label="Alamat" value={data.setting.desaAlamat} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div>Dengan ini menerangkan bahwa:</div>
|
|
||||||
|
|
||||||
{/* DATA WARGA */}
|
|
||||||
<div>
|
|
||||||
<Row label="Nama Pemilik Usaha" value={getValue("nama")} />
|
|
||||||
<Row label="Tempat/Tanggal Lahir" value={getValue("tempat tanggal lahir")} />
|
|
||||||
<Row label="Alamat Pemilik Usaha" value={getValue("alamat")} />
|
|
||||||
<Row label="Nomor KTP" value={getValue("nik")} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div>Benar yang bersangkutan memiliki tempat usaha dengan keterangan seperti berikut:</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Row label="Nama Usaha" value={getValue("nama usaha")} />
|
|
||||||
<Row label="Bidang Usaha" value={getValue("bidang usaha")} />
|
|
||||||
<Row label="Alamat Usaha" value={getValue("alamat usaha")} />
|
|
||||||
<Row 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")} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p style={{ textAlign: "justify" }}>
|
|
||||||
Surat keterangan ini dibuat untuk keperluan <b>{getValue("alasan permohonan")}.</b>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p style={{ textAlign: "justify" }}>
|
|
||||||
Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat dipergunakan sebagaimana mestinya.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Row label="Dikeluarkan di" value={data.setting.desaNama} />
|
|
||||||
<Row label="Pada tanggal" value={data.surat.createdAt} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
{data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br />
|
|
||||||
<u>{data.setting.perbekelNama}</u><br />
|
|
||||||
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* ISI */}
|
||||||
|
<div>
|
||||||
|
<div style={{ marginBottom: "10px" }}>
|
||||||
|
Yang bertanda tangan dibawah ini, saya:
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DATA PEJABAT */}
|
||||||
|
<div>
|
||||||
|
<Row label="Nama" value={data.setting.perbekelNama} />
|
||||||
|
<Row label="Jabatan" value={data.setting.perbekelJabatan} />
|
||||||
|
<Row label="Alamat" value={data.setting.desaAlamat} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div>Dengan ini menerangkan bahwa:</div>
|
||||||
|
|
||||||
|
{/* DATA WARGA */}
|
||||||
|
<div>
|
||||||
|
<Row label="Nama Pemilik Usaha" value={getValue("nama")} />
|
||||||
|
<Row
|
||||||
|
label="Tempat/Tanggal Lahir"
|
||||||
|
value={getValue("tempat tanggal lahir")}
|
||||||
|
/>
|
||||||
|
<Row label="Alamat Pemilik Usaha" value={getValue("alamat")} />
|
||||||
|
<Row label="Nomor KTP" value={getValue("nik")} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
Benar yang bersangkutan memiliki tempat usaha dengan keterangan
|
||||||
|
seperti berikut:
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Row label="Nama Usaha" value={getValue("nama usaha")} />
|
||||||
|
<Row label="Bidang Usaha" value={getValue("bidang usaha")} />
|
||||||
|
<Row label="Alamat Usaha" value={getValue("alamat usaha")} />
|
||||||
|
<Row
|
||||||
|
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")} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style={{ textAlign: "justify" }}>
|
||||||
|
Surat keterangan ini dibuat untuk keperluan{" "}
|
||||||
|
<b>{getValue("alasan permohonan")}.</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style={{ textAlign: "justify" }}>
|
||||||
|
Demikian surat keterangan ini dibuat dengan sebenarnya untuk dapat
|
||||||
|
dipergunakan sebagaimana mestinya.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Row label="Dikeluarkan di" value={data.setting.desaNama} />
|
||||||
|
<Row label="Pada tanggal" value={data.surat.createdAt} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
{data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u>
|
||||||
|
<br />
|
||||||
|
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
<div style={{ width: "10px" }}>:</div>
|
<div style={{ width: "10px" }}>:</div>
|
||||||
<div>{value}</div>
|
<div>{value}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,110 +3,118 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
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 () => {
|
||||||
|
try {
|
||||||
|
setViewImg("");
|
||||||
|
if (!data.setting.perbekelTTD) return;
|
||||||
|
|
||||||
const loadImage = async () => {
|
const urlApi =
|
||||||
try {
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
setViewImg("");
|
data.setting.perbekelTTD;
|
||||||
if (!data.setting.perbekelTTD) return;
|
// Fetch manual agar mendapatkan Response asli
|
||||||
|
const res = await fetch(urlApi);
|
||||||
|
if (!res.ok)
|
||||||
|
return notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to load image sign",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD;
|
setViewImg(url);
|
||||||
// Fetch manual agar mendapatkan Response asli
|
} catch (err) {
|
||||||
const res = await fetch(urlApi);
|
console.error("Gagal load gambar:", err);
|
||||||
if (!res.ok)
|
}
|
||||||
return notification({
|
};
|
||||||
title: "Error",
|
|
||||||
message: "Failed to load image sign",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
const blob = await res.blob();
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
setViewImg(url);
|
useEffect(() => {
|
||||||
} catch (err) {
|
loadImage();
|
||||||
console.error("Gagal load gambar:", err);
|
}, [data]);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
return (
|
||||||
loadImage();
|
<div style={{ lineHeight: "1.5" }}>
|
||||||
}, [data]);
|
{/* TITLE */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "20px" }}>
|
||||||
return (
|
<b style={{ fontSize: "16px" }}>SURAT KETERANGAN TIDAK MAMPU</b>
|
||||||
<div style={{ lineHeight: "1.5" }}>
|
<br />
|
||||||
{/* TITLE */}
|
Nomor: {data.surat.noSurat}
|
||||||
<div style={{ textAlign: "center", marginBottom: "20px" }}>
|
|
||||||
<b style={{ fontSize: "16px" }}>SURAT KETERANGAN TIDAK MAMPU</b><br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ISI */}
|
|
||||||
<div>
|
|
||||||
<div style={{ marginBottom: "10px" }}>
|
|
||||||
Yang bertanda tangan dibawah ini, saya
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* DATA PEJABAT */}
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<Row label="Nama" value={data.setting.perbekelNama} />
|
|
||||||
<Row label="Alamat" value={data.setting.desaAlamat} />
|
|
||||||
<Row label="Jabatan" value={data.setting.perbekelJabatan} />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div>Dengan ini menerangkan bahwa:</div>
|
|
||||||
|
|
||||||
{/* DATA WARGA */}
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<Row label="Nama" value={getValue("nama")} />
|
|
||||||
<Row label="Tempat Tgl Lahir" value={getValue("tempat tanggal lahir")} />
|
|
||||||
<Row label="Alamat" value={getValue("alamat")} />
|
|
||||||
<Row label="NIK" value={getValue("nik")} />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<p style={{ textAlign: "justify" }}>
|
|
||||||
Orang tersebut benar-benar penduduk desa {data.setting.desaNama} dan termasuk keluarga tidak mampu.
|
|
||||||
Surat keterangan ini dipergunakan untuk
|
|
||||||
<b>{getValue("alasan permohonan")}.</b>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p style={{ textAlign: "justify" }}>
|
|
||||||
Demikian surat keterangan ini kami buat dengan sebenar-benarnya untuk dapat dipergunakan
|
|
||||||
sebagaimana mestinya.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
{data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br />
|
|
||||||
<u>{data.setting.perbekelNama}</u><br />
|
|
||||||
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* ISI */}
|
||||||
|
<div>
|
||||||
|
<div style={{ marginBottom: "10px" }}>
|
||||||
|
Yang bertanda tangan dibawah ini, saya
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DATA PEJABAT */}
|
||||||
|
<div>
|
||||||
|
<Row label="Nama" value={data.setting.perbekelNama} />
|
||||||
|
<Row label="Alamat" value={data.setting.desaAlamat} />
|
||||||
|
<Row label="Jabatan" value={data.setting.perbekelJabatan} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div>Dengan ini menerangkan bahwa:</div>
|
||||||
|
|
||||||
|
{/* DATA WARGA */}
|
||||||
|
<div>
|
||||||
|
<Row label="Nama" value={getValue("nama")} />
|
||||||
|
<Row
|
||||||
|
label="Tempat Tgl Lahir"
|
||||||
|
value={getValue("tempat tanggal lahir")}
|
||||||
|
/>
|
||||||
|
<Row label="Alamat" value={getValue("alamat")} />
|
||||||
|
<Row label="NIK" value={getValue("nik")} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<p style={{ textAlign: "justify" }}>
|
||||||
|
Orang tersebut benar-benar penduduk desa {data.setting.desaNama} dan
|
||||||
|
termasuk keluarga tidak mampu. Surat keterangan ini dipergunakan untuk
|
||||||
|
<b>{getValue("alasan permohonan")}.</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style={{ textAlign: "justify" }}>
|
||||||
|
Demikian surat keterangan ini kami buat dengan sebenar-benarnya untuk
|
||||||
|
dapat dipergunakan sebagaimana mestinya.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
{data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u>
|
||||||
|
<br />
|
||||||
|
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
<div style={{ width: "10px" }}>:</div>
|
<div style={{ width: "10px" }}>:</div>
|
||||||
<div>{value}</div>
|
<div>{value}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,147 +3,217 @@ import { useEffect, useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKUsaha({ data }: { data: any }) {
|
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 () => {
|
||||||
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ lineHeight: "1.3" }}>
|
|
||||||
{/* HEADER */}
|
|
||||||
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
|
||||||
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b><br />
|
|
||||||
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b><br />
|
|
||||||
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b><br />
|
|
||||||
Alamat: {data.setting.desaAlamat}<br />
|
|
||||||
Kode Pos: {data.setting.desaPos}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* JUDUL */}
|
|
||||||
<div style={{ textAlign: "center", margin: "15px 0" }}>
|
|
||||||
<b><u>SURAT KETERANGAN USAHA</u></b><br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* YANG BERTANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "15px" }}>
|
|
||||||
Yang bertanda tangan di bawah ini:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "160px" }}>Nama</td>
|
|
||||||
<td style={{ width: "10px" }}>:</td>
|
|
||||||
<td>{data.setting.perbekelNama}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Jabatan</td>
|
|
||||||
<td>:</td>
|
|
||||||
<td>{data.setting.perbekelJabatan}</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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* IDENTITAS ORANG YG MEMINTA SURAT */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dengan ini menerangkan dengan sesungguhnya bahwa:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Nama</td><td style={{ width: "10px" }}>:</td><td>{getValue("nama")}</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>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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* DOMISILI */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Bahwa orang tersebut di atas benar-benar penduduk:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr><td style={{ width: "160px" }}>Desa / Kelurahan</td><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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* USAHA */}
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dan yang bersangkutan benar memiliki usaha:
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<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>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Surat keterangan ini dibuat dengan sebenarnya untuk dipergunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "20px" }}>
|
|
||||||
Dikeluarkan di {data.setting.desaNama} <br />
|
|
||||||
Pada tanggal {data.surat.createdAt}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
|
||||||
<div style={{ marginTop: "10px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<br />
|
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
|
||||||
<br /><br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />
|
|
||||||
<br />
|
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.3" }}>
|
||||||
|
{/* HEADER */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
||||||
|
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</b>
|
||||||
|
<br />
|
||||||
|
<b>KECAMATAN {_.upperCase(data.setting.desaKecamatan)}</b>
|
||||||
|
<br />
|
||||||
|
<b>DESA / KELURAHAN {_.upperCase(data.setting.desaNama)}</b>
|
||||||
|
<br />
|
||||||
|
Alamat: {data.setting.desaAlamat}
|
||||||
|
<br />
|
||||||
|
Kode Pos: {data.setting.desaPos}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
{/* JUDUL */}
|
||||||
|
<div style={{ textAlign: "center", margin: "15px 0" }}>
|
||||||
|
<b>
|
||||||
|
<u>SURAT KETERANGAN USAHA</u>
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
Nomor: {data.surat.noSurat}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* YANG BERTANDA TANGAN */}
|
||||||
|
<div style={{ marginTop: "15px" }}>
|
||||||
|
Yang bertanda tangan di bawah ini:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{data.setting.perbekelNama}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Jabatan</td>
|
||||||
|
<td>:</td>
|
||||||
|
<td>{data.setting.perbekelJabatan}</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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* IDENTITAS ORANG YG MEMINTA SURAT */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dengan ini menerangkan dengan sesungguhnya bahwa:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Nama</td>
|
||||||
|
<td style={{ width: "10px" }}>:</td>
|
||||||
|
<td>{getValue("nama")}</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>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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* DOMISILI */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Bahwa orang tersebut di atas benar-benar penduduk:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "160px" }}>Desa / Kelurahan</td>
|
||||||
|
<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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* USAHA */}
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dan yang bersangkutan benar memiliki usaha:
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<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>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Surat keterangan ini dibuat dengan sebenarnya untuk dipergunakan
|
||||||
|
sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
Dikeluarkan di {data.setting.desaNama} <br />
|
||||||
|
Pada tanggal {data.surat.createdAt}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TANDA TANGAN */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: "10px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br />
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,191 +4,209 @@ import { useState } from "react";
|
|||||||
import notification from "../notificationGlobal";
|
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 =
|
||||||
// Fetch manual agar mendapatkan Response asli
|
"/api/pengaduan/image?folder=lainnya&fileName=" +
|
||||||
const res = await fetch(urlApi);
|
data.setting.perbekelTTD;
|
||||||
if (!res.ok)
|
// Fetch manual agar mendapatkan Response asli
|
||||||
return notification({
|
const res = await fetch(urlApi);
|
||||||
title: "Error",
|
if (!res.ok)
|
||||||
message: "Failed to load image sign",
|
return notification({
|
||||||
type: "error",
|
title: "Error",
|
||||||
});
|
message: "Failed to load image sign",
|
||||||
const blob = await res.blob();
|
type: "error",
|
||||||
const url = URL.createObjectURL(blob);
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewImg(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
loadImage();
|
loadImage();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ lineHeight: "1.3" }}>
|
|
||||||
|
|
||||||
{/* HEADER */}
|
|
||||||
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
|
||||||
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</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}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ textAlign: "center", marginTop: "15px" }}>
|
|
||||||
<b><u>SURAT KETERANGAN YATIM / PIATU / YATIM PIATU</u></b><br />
|
|
||||||
Nomor: {data.surat.noSurat}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
{/* BAGIAN PENANDATANGAN */}
|
|
||||||
<div>Yang bertanda tangan di bawah ini:</div>
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "180px" }}>Nama</td>
|
|
||||||
<td>: {data.setting.perbekelNama}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Jabatan</td>
|
|
||||||
<td>: {data.setting.perbekelJabatan}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Alamat Kantor</td>
|
|
||||||
<td>: {data.setting.desaAlamat}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
{/* BAGIAN IDENTITAS ANAK */}
|
|
||||||
<div>Dengan ini menerangkan bahwa:</div>
|
|
||||||
|
|
||||||
<table style={{ width: "100%", marginTop: "5px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "180px" }}>Nama</td>
|
|
||||||
<td>: {getValue("nama")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Tempat/Tanggal Lahir</td>
|
|
||||||
<td>: {getValue("tempat tanggal lahir")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Jenis Kelamin</td>
|
|
||||||
<td>: {getValue("jenis kelamin")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Alamat</td>
|
|
||||||
<td>: {getValue("alamat")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>NIK</td>
|
|
||||||
<td>: {getValue("nik")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Pekerjaan</td>
|
|
||||||
<td>: {getValue("pekerjaan")}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
{/* KETERANGAN ORANG TUA */}
|
|
||||||
<div>
|
|
||||||
Benar bahwa yang bersangkutan adalah <b>anak (Yatim / Piatu / Yatim Piatu)</b>,
|
|
||||||
dengan keterangan sebagai berikut:
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div><b>1. Nama Ayah</b></div>
|
|
||||||
<table style={{ width: "100%" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "180px" }}>Nama Ayah</td>
|
|
||||||
<td>: {getValue("nama ayah")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Status</td>
|
|
||||||
<td>: {getValue("status ayah")}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div><b>2. Nama Ibu</b></div>
|
|
||||||
<table style={{ width: "100%" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "180px" }}>Nama Ibu</td>
|
|
||||||
<td>: {getValue("nama ibu")}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Status</td>
|
|
||||||
<td>: {getValue("status ibu")}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Dengan demikian, berdasarkan keterangan pihak keluarga dan data di Kantor Desa,
|
|
||||||
maka benar bahwa yang bersangkutan adalah
|
|
||||||
<b> anak (Yatim / Piatu / Yatim Piatu).</b>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
Surat keterangan ini dibuat dengan sebenar-benarnya untuk dipergunakan sebagaimana mestinya.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
{/* TANGGAL & TEMPAT */}
|
|
||||||
<table style={{ width: "100%", marginTop: "10px" }}>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td style={{ width: "180px" }}>Dikeluarkan di</td>
|
|
||||||
<td>: {data.setting.desaNama}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Pada tanggal</td>
|
|
||||||
<td>: {data.surat.createdAt}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
{/* TTD */}
|
|
||||||
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
Kepala Desa {data.setting.desaNama}
|
|
||||||
<br /><br />
|
|
||||||
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
|
||||||
NIP. {data.setting.perbekelNIP}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.3" }}>
|
||||||
|
{/* HEADER */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "10px" }}>
|
||||||
|
<b>PEMERINTAH KABUPATEN {_.upperCase(data.setting.desaKabupaten)}</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}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
<div style={{ textAlign: "center", marginTop: "15px" }}>
|
||||||
|
<b>
|
||||||
|
<u>SURAT KETERANGAN YATIM / PIATU / YATIM PIATU</u>
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
Nomor: {data.surat.noSurat}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* BAGIAN PENANDATANGAN */}
|
||||||
|
<div>Yang bertanda tangan di bawah ini:</div>
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "180px" }}>Nama</td>
|
||||||
|
<td>: {data.setting.perbekelNama}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Jabatan</td>
|
||||||
|
<td>: {data.setting.perbekelJabatan}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Alamat Kantor</td>
|
||||||
|
<td>: {data.setting.desaAlamat}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* BAGIAN IDENTITAS ANAK */}
|
||||||
|
<div>Dengan ini menerangkan bahwa:</div>
|
||||||
|
|
||||||
|
<table style={{ width: "100%", marginTop: "5px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "180px" }}>Nama</td>
|
||||||
|
<td>: {getValue("nama")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tempat/Tanggal Lahir</td>
|
||||||
|
<td>: {getValue("tempat tanggal lahir")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Jenis Kelamin</td>
|
||||||
|
<td>: {getValue("jenis kelamin")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Alamat</td>
|
||||||
|
<td>: {getValue("alamat")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>NIK</td>
|
||||||
|
<td>: {getValue("nik")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Pekerjaan</td>
|
||||||
|
<td>: {getValue("pekerjaan")}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* KETERANGAN ORANG TUA */}
|
||||||
|
<div>
|
||||||
|
Benar bahwa yang bersangkutan adalah{" "}
|
||||||
|
<b>anak (Yatim / Piatu / Yatim Piatu)</b>, dengan keterangan sebagai
|
||||||
|
berikut:
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>1. Nama Ayah</b>
|
||||||
|
</div>
|
||||||
|
<table style={{ width: "100%" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "180px" }}>Nama Ayah</td>
|
||||||
|
<td>: {getValue("nama ayah")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Status</td>
|
||||||
|
<td>: {getValue("status ayah")}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>2. Nama Ibu</b>
|
||||||
|
</div>
|
||||||
|
<table style={{ width: "100%" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "180px" }}>Nama Ibu</td>
|
||||||
|
<td>: {getValue("nama ibu")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Status</td>
|
||||||
|
<td>: {getValue("status ibu")}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
Dengan demikian, berdasarkan keterangan pihak keluarga dan data di
|
||||||
|
Kantor Desa, maka benar bahwa yang bersangkutan adalah
|
||||||
|
<b> anak (Yatim / Piatu / Yatim Piatu).</b>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
Surat keterangan ini dibuat dengan sebenar-benarnya untuk dipergunakan
|
||||||
|
sebagaimana mestinya.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* TANGGAL & TEMPAT */}
|
||||||
|
<table style={{ width: "100%", marginTop: "10px" }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style={{ width: "180px" }}>Dikeluarkan di</td>
|
||||||
|
<td>: {data.setting.desaNama}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Pada tanggal</td>
|
||||||
|
<td>: {data.surat.createdAt}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* TTD */}
|
||||||
|
<div
|
||||||
|
style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
Kepala Desa {data.setting.desaNama}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />{" "}
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"];
|
||||||
|
|||||||
331
src/pages/darmasaba/surat.tsx
Normal file
331
src/pages/darmasaba/surat.tsx
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
import apiFetch from "@/lib/apiFetch";
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Badge,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Container,
|
||||||
|
Divider,
|
||||||
|
Grid,
|
||||||
|
Group,
|
||||||
|
Select,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Textarea,
|
||||||
|
Tooltip
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useShallowEffect } from "@mantine/hooks";
|
||||||
|
import {
|
||||||
|
IconBuildingCommunity,
|
||||||
|
IconInfoCircle,
|
||||||
|
IconMapPin,
|
||||||
|
IconUser
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
import React from "react";
|
||||||
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Reusable UI components
|
||||||
|
// =========================
|
||||||
|
|
||||||
|
function FieldLabel({ label, hint }: { label: string; hint?: string }) {
|
||||||
|
return (
|
||||||
|
<Group justify="apart" gap="xs" align="center">
|
||||||
|
<Text fw={600}>{label}</Text>
|
||||||
|
{hint && (
|
||||||
|
<Tooltip label={hint} withArrow>
|
||||||
|
<ActionIcon size={24} variant="subtle">
|
||||||
|
<IconInfoCircle size={16} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormSection({
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
children,
|
||||||
|
description,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
children: React.ReactNode;
|
||||||
|
description?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Card radius="md" shadow="sm" withBorder>
|
||||||
|
<Group justify="apart" align="center" mb="xs">
|
||||||
|
<Group align="center" gap="xs">
|
||||||
|
{icon}
|
||||||
|
<Text fw={700}>{title}</Text>
|
||||||
|
</Group>
|
||||||
|
{description && <Badge variant="light">{description}</Badge>}
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Divider mb="sm" />
|
||||||
|
<Stack gap="sm">{children}</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Main form component
|
||||||
|
// =========================
|
||||||
|
|
||||||
|
export default function FormSurat() {
|
||||||
|
const { data, mutate, isLoading } = useSWR("category-pelayanan-list", () =>
|
||||||
|
apiFetch.api.pelayanan.category.get(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const listCategory = data?.data || [];
|
||||||
|
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
mutate();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container size="md" w={"100%"}>
|
||||||
|
<Box>
|
||||||
|
<Stack gap="lg">
|
||||||
|
<Group justify="apart" align="center">
|
||||||
|
<Group align="center">
|
||||||
|
<IconBuildingCommunity size={28} />
|
||||||
|
<div>
|
||||||
|
<Text fw={800} size="xl">
|
||||||
|
Surat Keterangan Tidak Mampu (SKTM)
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
Blangko resmi untuk pengajuan Surat Keterangan Tidak Mampu —
|
||||||
|
digunakan untuk keperluan pendidikan, kesehatan, atau
|
||||||
|
administrasi.
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Badge radius="sm">Form Length: 3 Sections</Badge>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<Stack gap="lg">
|
||||||
|
{/* Header Section */}
|
||||||
|
<FormSection
|
||||||
|
title="Pemohon"
|
||||||
|
icon={<IconUser size={16} />}
|
||||||
|
description="Informasi identitas pemohon"
|
||||||
|
>
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<TextInput
|
||||||
|
label={
|
||||||
|
<FieldLabel
|
||||||
|
label="Nama Lengkap"
|
||||||
|
hint="Nama lengkap pemohon"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder="Budi Setiawan"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<TextInput
|
||||||
|
label={
|
||||||
|
<FieldLabel
|
||||||
|
label="Nomor Telephone"
|
||||||
|
hint="Nomor telephone yang dapat dihubungi / terhubung dengan whatsapp"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder="08123456789"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={12}>
|
||||||
|
<Select
|
||||||
|
label={<FieldLabel label="Jenis Surat" hint="Jenis surat yang ingin diajukan" />}
|
||||||
|
placeholder="Pilih jenis surat"
|
||||||
|
data={listCategory.map((item: any) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</FormSection>
|
||||||
|
|
||||||
|
<FormSection
|
||||||
|
title="Syarat Dokumen"
|
||||||
|
description="Syarat dokumen yang diperlukan"
|
||||||
|
>
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<TextInput
|
||||||
|
label={
|
||||||
|
<FieldLabel
|
||||||
|
label="Nama Lengkap"
|
||||||
|
hint="Sesuai KTP"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder="Nama lengkap"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<TextInput
|
||||||
|
label={
|
||||||
|
<FieldLabel
|
||||||
|
label="NIK"
|
||||||
|
hint="16 digit, tanpa spasi"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder="3201xxxxxxxxxxxx"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<TextInput
|
||||||
|
label={
|
||||||
|
<FieldLabel
|
||||||
|
label="Tempat, Tanggal Lahir"
|
||||||
|
hint="Contoh: Denpasar, 31-12-1990"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder="Tempat, tanggal lahir"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<Select
|
||||||
|
label={<FieldLabel label="Jenis Kelamin" />}
|
||||||
|
placeholder="Pilih jenis kelamin"
|
||||||
|
data={["Laki-laki", "Perempuan"]}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<Select
|
||||||
|
label={<FieldLabel label="Agama" />}
|
||||||
|
placeholder="Pilih agama"
|
||||||
|
data={[
|
||||||
|
"Islam",
|
||||||
|
"Kristen",
|
||||||
|
"Katolik",
|
||||||
|
"Hindu",
|
||||||
|
"Buddha",
|
||||||
|
"Konghucu",
|
||||||
|
"Lainnya",
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<Select
|
||||||
|
label={<FieldLabel label="Status Perkawinan" />}
|
||||||
|
placeholder="Pilih status"
|
||||||
|
data={[
|
||||||
|
"Belum Kawin",
|
||||||
|
"Kawin",
|
||||||
|
"Cerai Hidup",
|
||||||
|
"Cerai Mati",
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<TextInput
|
||||||
|
label={<FieldLabel label="Pekerjaan" />}
|
||||||
|
placeholder="Pekerjaan"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={12}>
|
||||||
|
<Textarea
|
||||||
|
label={<FieldLabel label="Alamat Lengkap" />}
|
||||||
|
placeholder="Alamat domisili"
|
||||||
|
minRows={2}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={2}>
|
||||||
|
<TextInput
|
||||||
|
label={<FieldLabel label="RT" />}
|
||||||
|
placeholder="001"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={2}>
|
||||||
|
<TextInput
|
||||||
|
label={<FieldLabel label="RW" />}
|
||||||
|
placeholder="002"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<TextInput
|
||||||
|
label={<FieldLabel label="Desa / Kelurahan" />}
|
||||||
|
placeholder="Desa"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<TextInput
|
||||||
|
label={<FieldLabel label="Kecamatan" />}
|
||||||
|
placeholder="Kecamatan"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<TextInput
|
||||||
|
label={<FieldLabel label="Kabupaten / Kota" />}
|
||||||
|
placeholder="Kabupaten / Kota"
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</FormSection>
|
||||||
|
|
||||||
|
{/* Keterangan Section */}
|
||||||
|
<FormSection
|
||||||
|
title="Keterangan"
|
||||||
|
icon={<IconMapPin size={18} />}
|
||||||
|
description="Isi pernyataan SKTM"
|
||||||
|
>
|
||||||
|
<Textarea
|
||||||
|
label={
|
||||||
|
<FieldLabel
|
||||||
|
label="Isi Surat"
|
||||||
|
hint="Jelaskan kondisi ekonomi secara singkat"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder="Pernyataan resmi bahwa yang bersangkutan benar-benar tergolong keluarga tidak mampu..."
|
||||||
|
minRows={4}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
label={
|
||||||
|
<FieldLabel
|
||||||
|
label="Keperluan"
|
||||||
|
hint="Contoh: Beasiswa pendidikan / Perawatan kesehatan"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder="Keperluan surat"
|
||||||
|
/>
|
||||||
|
</FormSection>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<Group justify="right" mt="md">
|
||||||
|
<Button variant="default" onClick={() => { }}>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
<Button type="submit">Kirim / Simpan</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Progress,
|
Progress,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
Title
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
|
|||||||
@@ -285,45 +285,47 @@ function NavigationDashboard() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap="xs" p="sm">
|
<Stack gap="xs" p="sm">
|
||||||
{navItems.filter((item) => permissions.includes(item.key)).map((item) => (
|
{navItems
|
||||||
<NavLink
|
.filter((item) => permissions.includes(item.key))
|
||||||
key={item.path}
|
.map((item) => (
|
||||||
active={isActive(item.path as keyof typeof clientRoute)}
|
<NavLink
|
||||||
leftSection={item.icon}
|
key={item.path}
|
||||||
label={
|
active={isActive(item.path as keyof typeof clientRoute)}
|
||||||
<Flex align="center" gap={6}>
|
leftSection={item.icon}
|
||||||
<Text fw={500}>{item.label}</Text>
|
label={
|
||||||
{isActive(item.path as keyof typeof clientRoute) && (
|
<Flex align="center" gap={6}>
|
||||||
<Badge
|
<Text fw={500}>{item.label}</Text>
|
||||||
variant="light"
|
{isActive(item.path as keyof typeof clientRoute) && (
|
||||||
color="teal"
|
<Badge
|
||||||
radius="sm"
|
variant="light"
|
||||||
size="xs"
|
color="teal"
|
||||||
style={{ textTransform: "none" }}
|
radius="sm"
|
||||||
>
|
size="xs"
|
||||||
Active
|
style={{ textTransform: "none" }}
|
||||||
</Badge>
|
>
|
||||||
)}
|
Active
|
||||||
</Flex>
|
</Badge>
|
||||||
}
|
)}
|
||||||
description={item.description}
|
</Flex>
|
||||||
onClick={() =>
|
}
|
||||||
navigate(clientRoutes[item.path as keyof typeof clientRoute])
|
description={item.description}
|
||||||
}
|
onClick={() =>
|
||||||
style={{
|
navigate(clientRoutes[item.path as keyof typeof clientRoute])
|
||||||
backgroundColor: isActive(item.path as keyof typeof clientRoute)
|
}
|
||||||
? "rgba(0,255,200,0.1)"
|
style={{
|
||||||
: "transparent",
|
backgroundColor: isActive(item.path as keyof typeof clientRoute)
|
||||||
borderRadius: "8px",
|
? "rgba(0,255,200,0.1)"
|
||||||
transition: "all 0.2s ease",
|
: "transparent",
|
||||||
}}
|
borderRadius: "8px",
|
||||||
styles={{
|
transition: "all 0.2s ease",
|
||||||
label: { color: "white" },
|
}}
|
||||||
description: { color: "#aaa" },
|
styles={{
|
||||||
section: { color: "teal" },
|
label: { color: "white" },
|
||||||
}}
|
description: { color: "#aaa" },
|
||||||
/>
|
section: { color: "teal" },
|
||||||
))}
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,82 +329,85 @@ 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
|
||||||
<Table.Td style={{ whiteSpace: "nowrap", width: "10%" }}>{_.upperFirst(item.jenis)}</Table.Td>
|
style={{ whiteSpace: "nowrap", width: "10%" }}
|
||||||
<Table.Td>:</Table.Td>
|
>
|
||||||
<Table.Td style={{ width: "85%" }}>{_.upperFirst(item.value)}</Table.Td>
|
{_.upperFirst(item.jenis)}
|
||||||
</Table.Tr>
|
</Table.Td>
|
||||||
))
|
<Table.Td>:</Table.Td>
|
||||||
}
|
<Table.Td style={{ width: "85%" }}>
|
||||||
|
{_.upperFirst(item.value)}
|
||||||
|
</Table.Td>
|
||||||
|
</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")}
|
variant="light"
|
||||||
variant="light"
|
onClick={() => {
|
||||||
onClick={() => {
|
setCatModal("tolak");
|
||||||
setCatModal("tolak");
|
open();
|
||||||
open();
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Tolak
|
||||||
Tolak
|
</Button>
|
||||||
</Button>
|
<Button
|
||||||
<Button
|
disabled={!permissions.includes("pelayanan.antrian.terima")}
|
||||||
disabled={!permissions.includes("pelayanan.antrian.terima")}
|
variant="filled"
|
||||||
variant="filled"
|
onClick={() => {
|
||||||
onClick={() => {
|
setCatModal("terima");
|
||||||
setCatModal("terima");
|
open();
|
||||||
open();
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Terima
|
||||||
Terima
|
</Button>
|
||||||
</Button>
|
</Group>
|
||||||
</Group>
|
) : data?.status === "diterima" ? (
|
||||||
) : data?.status === "diterima" ? (
|
<Group justify="center" grow>
|
||||||
<Group justify="center" grow>
|
<Button
|
||||||
<Button
|
disabled={!permissions.includes("pelayanan.diterima.tolak")}
|
||||||
disabled={!permissions.includes("pelayanan.diterima.tolak")}
|
variant="light"
|
||||||
variant="light"
|
onClick={() => {
|
||||||
onClick={() => {
|
setCatModal("tolak");
|
||||||
setCatModal("tolak");
|
open();
|
||||||
open();
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Tolak
|
||||||
Tolak
|
</Button>
|
||||||
</Button>
|
<Button
|
||||||
<Button
|
disabled={
|
||||||
disabled={!permissions.includes("pelayanan.diterima.setujui")}
|
!permissions.includes("pelayanan.diterima.setujui")
|
||||||
variant="filled"
|
}
|
||||||
onClick={() => {
|
variant="filled"
|
||||||
setCatModal("terima");
|
onClick={() => {
|
||||||
open();
|
setCatModal("terima");
|
||||||
}}
|
open();
|
||||||
>
|
}}
|
||||||
Setujui
|
>
|
||||||
</Button>
|
Setujui
|
||||||
</Group>
|
</Button>
|
||||||
) :
|
</Group>
|
||||||
data?.status === "selesai" ?
|
) : data?.status === "selesai" ? (
|
||||||
(
|
<Group justify="center" grow>
|
||||||
<Group justify="center" grow>
|
<Button
|
||||||
<Button
|
variant="light"
|
||||||
variant="light"
|
onClick={() => setOpenedPreview(!openedPreview)}
|
||||||
onClick={() => setOpenedPreview(!openedPreview)}
|
>
|
||||||
>
|
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", {
|
||||||
{
|
day: "2-digit",
|
||||||
item.createdAt.toLocaleString("id-ID", {
|
month: "short",
|
||||||
day: "2-digit",
|
year: "numeric",
|
||||||
month: "short",
|
hour: "2-digit",
|
||||||
year: "numeric",
|
minute: "2-digit",
|
||||||
hour: "2-digit",
|
hour12: false,
|
||||||
minute: "2-digit",
|
})}
|
||||||
hour12: false
|
</Table.Td>
|
||||||
})
|
<Table.Td>{item.deskripsi}</Table.Td>
|
||||||
}</Table.Td>
|
<Table.Td>{item.status}</Table.Td>
|
||||||
<Table.Td>{item.deskripsi}</Table.Td>
|
<Table.Td style={{ whiteSpace: "nowrap" }}>
|
||||||
<Table.Td>{item.status}</Table.Td>
|
{item.nameUser ? item.nameUser : "-"}
|
||||||
<Table.Td style={{ whiteSpace: "nowrap" }}>{item.nameUser ? item.nameUser : "-"}</Table.Td>
|
</Table.Td>
|
||||||
</Table.Tr>
|
</Table.Tr>
|
||||||
))
|
))}
|
||||||
}
|
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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={() => {
|
||||||
Lihat Gambar
|
setOpenedPreview(true);
|
||||||
</Anchor>
|
}}
|
||||||
:
|
>
|
||||||
<Text size="md" c="white">
|
Lihat Gambar
|
||||||
-
|
</Anchor>
|
||||||
</Text>
|
) : (
|
||||||
}
|
<Text size="md" c="white">
|
||||||
|
-
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
@@ -285,74 +326,76 @@ 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} />
|
<Text size="md">Keterangan</Text>
|
||||||
<Text size="md">Keterangan</Text>
|
</Group>
|
||||||
</Group>
|
<Text size="md" c={"white"}>
|
||||||
<Text size="md" c={"white"}>
|
{_.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"
|
disabled={!permissions.includes("pengaduan.antrian.tolak")}
|
||||||
disabled={!permissions.includes("pengaduan.antrian.tolak")}
|
onClick={() => {
|
||||||
onClick={() => {
|
setCatModal("tolak");
|
||||||
setCatModal("tolak");
|
open();
|
||||||
open();
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Tolak
|
||||||
Tolak
|
</Button>
|
||||||
</Button>
|
<Button
|
||||||
<Button
|
variant="filled"
|
||||||
variant="filled"
|
disabled={!permissions.includes("pengaduan.antrian.terima")}
|
||||||
disabled={!permissions.includes("pengaduan.antrian.terima")}
|
onClick={() => {
|
||||||
onClick={() => {
|
setCatModal("terima");
|
||||||
setCatModal("terima");
|
open();
|
||||||
open();
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Terima
|
||||||
Terima
|
</Button>
|
||||||
</Button>
|
</Group>
|
||||||
</Group>
|
) : data?.status === "diterima" ? (
|
||||||
) : data?.status === "diterima" ? (
|
<Group justify="center" grow>
|
||||||
<Group justify="center" grow>
|
<Button
|
||||||
<Button
|
variant="filled"
|
||||||
variant="filled"
|
disabled={
|
||||||
disabled={!permissions.includes("pengaduan.diterima.dikerjakan")}
|
!permissions.includes("pengaduan.diterima.dikerjakan")
|
||||||
onClick={() => {
|
}
|
||||||
setCatModal("terima");
|
onClick={() => {
|
||||||
open();
|
setCatModal("terima");
|
||||||
}}
|
open();
|
||||||
>
|
}}
|
||||||
Kerjakan
|
>
|
||||||
</Button>
|
Kerjakan
|
||||||
</Group>
|
</Button>
|
||||||
) : data?.status === "dikerjakan" ? (
|
</Group>
|
||||||
<Group justify="center" grow>
|
) : data?.status === "dikerjakan" ? (
|
||||||
<Button
|
<Group justify="center" grow>
|
||||||
variant="filled"
|
<Button
|
||||||
disabled={!permissions.includes("pengaduan.dikerjakan.selesai")}
|
variant="filled"
|
||||||
onClick={() => {
|
disabled={
|
||||||
setCatModal("terima");
|
!permissions.includes("pengaduan.dikerjakan.selesai")
|
||||||
open();
|
}
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
setCatModal("terima");
|
||||||
Selesai
|
open();
|
||||||
</Button>
|
}}
|
||||||
</Group>
|
>
|
||||||
) : <></>
|
Selesai
|
||||||
}
|
</Button>
|
||||||
|
</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" }}>
|
||||||
<Table.Td style={{ whiteSpace: "nowrap" }}>{item.nameUser ? item.nameUser : "-"}</Table.Td>
|
{item.nameUser ? item.nameUser : "-"}
|
||||||
</Table.Tr>
|
</Table.Td>
|
||||||
))
|
</Table.Tr>
|
||||||
}
|
))}
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,43 +99,47 @@ 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>
|
||||||
<Table.Td>{kategori == "pengaduan" ? item.title : item.category}</Table.Td>
|
{kategori == "pengaduan" ? item.title : item.category}
|
||||||
<Table.Td>{item.status}</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>{item.status}</Table.Td>
|
||||||
<Button
|
<Table.Td>
|
||||||
variant="outline"
|
<Button
|
||||||
onClick={() => {
|
variant="outline"
|
||||||
kategori == "pengaduan" ?
|
onClick={() => {
|
||||||
navigate(
|
kategori == "pengaduan"
|
||||||
|
? 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
|
);
|
||||||
</Button>
|
}}
|
||||||
</Table.Td>
|
>
|
||||||
</Table.Tr>
|
Detail
|
||||||
))
|
</Button>
|
||||||
) : (
|
</Table.Td>
|
||||||
<Table.Tr>
|
|
||||||
<Table.Td colSpan={4} align="center">Tidak ada data</Table.Td>
|
|
||||||
</Table.Tr>
|
</Table.Tr>
|
||||||
)
|
))
|
||||||
}
|
) : (
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td colSpan={4} align="center">
|
||||||
|
Tidak ada data
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
)}
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -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,32 +98,33 @@ 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">
|
||||||
<Table.Td colSpan={3} align="center">Tidak ada data</Table.Td>
|
Tidak ada data
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
) : (
|
||||||
|
Array.isArray(list) &&
|
||||||
|
list?.map((item, i) => (
|
||||||
|
<Table.Tr key={i}>
|
||||||
|
<Table.Td>{item.name}</Table.Td>
|
||||||
|
<Table.Td w={250}>{item.phone}</Table.Td>
|
||||||
|
<Table.Td w={150}>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(
|
||||||
|
`/scr/dashboard/warga/detail-warga?id=${item.id}`,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</Button>
|
||||||
|
</Table.Td>
|
||||||
</Table.Tr>
|
</Table.Tr>
|
||||||
) : (
|
))
|
||||||
Array.isArray(list) && list?.map((item, i) => (
|
)}
|
||||||
<Table.Tr key={i}>
|
|
||||||
<Table.Td>{item.name}</Table.Td>
|
|
||||||
<Table.Td w={250}>{item.phone}</Table.Td>
|
|
||||||
<Table.Td w={150}>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
navigate(
|
|
||||||
`/scr/dashboard/warga/detail-warga?id=${item.id}`,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Detail
|
|
||||||
</Button>
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -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 ---
|
||||||
|
|||||||
Reference in New Issue
Block a user