Merge pull request 'amalia/20-nov-25' (#33) from amalia/20-nov-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/33
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
import apiFetch from "@/lib/apiFetch";
|
import apiFetch from "@/lib/apiFetch";
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
|
Anchor,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
|
FileInput,
|
||||||
Flex,
|
Flex,
|
||||||
Group,
|
Group,
|
||||||
Input,
|
Input,
|
||||||
@@ -10,18 +12,23 @@ 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";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import ModalFile from "./ModalFile";
|
||||||
import notification from "./notificationGlobal";
|
import notification from "./notificationGlobal";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
export default function DesaSetting() {
|
export default function DesaSetting() {
|
||||||
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 [openedPreview, setOpenedPreview] = useState(false);
|
||||||
|
const [viewImg, setViewImg] = useState("");
|
||||||
const { data, mutate, isLoading } = useSWR("/", () =>
|
const { data, mutate, isLoading } = useSWR("/", () =>
|
||||||
apiFetch.api["configuration-desa"].list.get(),
|
apiFetch.api["configuration-desa"].list.get(),
|
||||||
);
|
);
|
||||||
@@ -39,7 +46,31 @@ export default function DesaSetting() {
|
|||||||
async function handleEdit() {
|
async function handleEdit() {
|
||||||
try {
|
try {
|
||||||
setBtnLoading(true);
|
setBtnLoading(true);
|
||||||
const res = await apiFetch.api["configuration-desa"].edit.post(dataEdit);
|
|
||||||
|
let finalData = { ...dataEdit }; // ← buffer data terbaru
|
||||||
|
|
||||||
|
if (dataEdit.name === "TTD") {
|
||||||
|
const resImg = await apiFetch.api.pengaduan.upload.post({ file: img });
|
||||||
|
|
||||||
|
if (resImg.status === 200) {
|
||||||
|
finalData = {
|
||||||
|
...finalData,
|
||||||
|
value: resImg.data?.filename || ""
|
||||||
|
};
|
||||||
|
|
||||||
|
setDataEdit(finalData); // update state
|
||||||
|
} else {
|
||||||
|
return notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to upload image",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const res = await apiFetch.api["configuration-desa"].edit.post(finalData);
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
mutate();
|
mutate();
|
||||||
close();
|
close();
|
||||||
@@ -67,11 +98,8 @@ export default function DesaSetting() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseEdit({
|
|
||||||
data,
|
function chooseEdit({ data }: { data: { id: string; value: string; name: string }; }) {
|
||||||
}: {
|
|
||||||
data: { id: string; value: string; name: string };
|
|
||||||
}) {
|
|
||||||
setDataEdit(data);
|
setDataEdit(data);
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
@@ -100,18 +128,35 @@ export default function DesaSetting() {
|
|||||||
opened={opened}
|
opened={opened}
|
||||||
onClose={close}
|
onClose={close}
|
||||||
title={"Edit"}
|
title={"Edit"}
|
||||||
centered
|
|
||||||
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
>
|
>
|
||||||
<Stack gap="ld">
|
<Stack gap="ld">
|
||||||
<Input.Wrapper label={dataEdit.name}>
|
{
|
||||||
<Input
|
dataEdit.name == "TTD"
|
||||||
value={dataEdit.value}
|
?
|
||||||
onChange={(e) =>
|
(
|
||||||
onValidation({ kat: "value", value: e.target.value })
|
<Input.Wrapper label={dataEdit.name}>
|
||||||
}
|
<FileInput
|
||||||
/>
|
clearable
|
||||||
</Input.Wrapper>
|
placeholder="Upload TTD"
|
||||||
|
accept="image/*"
|
||||||
|
onChange={(e) => { setImg(e) }}
|
||||||
|
/>
|
||||||
|
</Input.Wrapper>
|
||||||
|
)
|
||||||
|
:
|
||||||
|
(
|
||||||
|
<Input.Wrapper label={dataEdit.name}>
|
||||||
|
<Input
|
||||||
|
value={dataEdit.value}
|
||||||
|
onChange={(e) =>
|
||||||
|
onValidation({ kat: "value", value: e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Input.Wrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
<Group justify="center" grow>
|
<Group justify="center" grow>
|
||||||
<Button variant="light" onClick={close}>
|
<Button variant="light" onClick={close}>
|
||||||
Batal
|
Batal
|
||||||
@@ -119,7 +164,7 @@ export default function DesaSetting() {
|
|||||||
<Button
|
<Button
|
||||||
variant="filled"
|
variant="filled"
|
||||||
onClick={handleEdit}
|
onClick={handleEdit}
|
||||||
disabled={btnDisable}
|
disabled={btnDisable || (dataEdit.name == "TTD" && !img)}
|
||||||
loading={btnLoading}
|
loading={btnLoading}
|
||||||
>
|
>
|
||||||
Simpan
|
Simpan
|
||||||
@@ -127,6 +172,14 @@ export default function DesaSetting() {
|
|||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<ModalFile
|
||||||
|
open={openedPreview && !_.isEmpty(viewImg)}
|
||||||
|
onClose={() => setOpenedPreview(false)}
|
||||||
|
folder="syarat-dokumen"
|
||||||
|
fileName={viewImg}
|
||||||
|
/>
|
||||||
|
|
||||||
<Stack gap={"md"}>
|
<Stack gap={"md"}>
|
||||||
<Flex align="center" justify="space-between">
|
<Flex align="center" justify="space-between">
|
||||||
<Title order={4} c="gray.2">
|
<Title order={4} c="gray.2">
|
||||||
@@ -147,7 +200,17 @@ export default function DesaSetting() {
|
|||||||
{list?.map((v: any) => (
|
{list?.map((v: any) => (
|
||||||
<Table.Tr key={v.id}>
|
<Table.Tr key={v.id}>
|
||||||
<Table.Td>{v.name}</Table.Td>
|
<Table.Td>{v.name}</Table.Td>
|
||||||
<Table.Td>{v.value}</Table.Td>
|
<Table.Td>
|
||||||
|
{
|
||||||
|
v.name == "TTD"
|
||||||
|
?
|
||||||
|
<Anchor href="#" onClick={() => { setViewImg(v.value); setOpenedPreview(true); }} underline="always">
|
||||||
|
Lihat
|
||||||
|
</Anchor>
|
||||||
|
:
|
||||||
|
v.value
|
||||||
|
}
|
||||||
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Tooltip label="Edit Setting">
|
<Tooltip label="Edit Setting">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
|||||||
66
src/components/ModalFile.tsx
Normal file
66
src/components/ModalFile.tsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Flex, Image, Loader, Modal } from "@mantine/core";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import notification from "./notificationGlobal";
|
||||||
|
|
||||||
|
export default function ModalFile({ open, onClose, folder, fileName }: { open: boolean, onClose: () => void, folder: string, fileName: string }) {
|
||||||
|
const [viewImg, setViewImg] = useState<string>("");
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && fileName) {
|
||||||
|
loadImage();
|
||||||
|
}
|
||||||
|
}, [open, fileName]);
|
||||||
|
|
||||||
|
const loadImage = async () => {
|
||||||
|
try {
|
||||||
|
setViewImg("");
|
||||||
|
setLoading(true);
|
||||||
|
const urlApi = '/api/pengaduan/image?folder=' + folder + '&fileName=' + fileName;
|
||||||
|
// Fetch manual agar mendapatkan Response asli
|
||||||
|
const res = await fetch(urlApi);
|
||||||
|
if (!res.ok)
|
||||||
|
return notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to load image",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
const blob = await res.blob();
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
setViewImg(url);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal load gambar:", err);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
opened={open}
|
||||||
|
onClose={onClose}
|
||||||
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
|
size="lg"
|
||||||
|
withCloseButton
|
||||||
|
removeScrollProps={{ allowPinchZoom: true }}
|
||||||
|
title="File"
|
||||||
|
>
|
||||||
|
{loading && (
|
||||||
|
<Flex justify="center" align="center" h={200}>
|
||||||
|
<Loader />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
{viewImg && (
|
||||||
|
<Image
|
||||||
|
radius="md"
|
||||||
|
h={300}
|
||||||
|
fit="contain"
|
||||||
|
src={viewImg}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,9 +6,14 @@ import html2canvas from "html2canvas";
|
|||||||
import jsPDF from "jspdf";
|
import jsPDF from "jspdf";
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import SKBedaBiodataDiri from "./surat/SKBedaBiodataDiri";
|
||||||
|
import SKBelumKawin from "./surat/SKBelumKawin";
|
||||||
|
import SKDomisiliOrganisasi from "./surat/SKDomisiliOrganisasi";
|
||||||
import SKKelahiran from "./surat/SKKelahiran";
|
import SKKelahiran from "./surat/SKKelahiran";
|
||||||
import SKKelakuanBaik from "./surat/SKKelakuanBaik";
|
import SKKelakuanBaik from "./surat/SKKelakuanBaik";
|
||||||
|
import SKKematian from "./surat/SKKematian";
|
||||||
import SKPenghasilan from "./surat/SKPenghasilan";
|
import SKPenghasilan from "./surat/SKPenghasilan";
|
||||||
|
import SKTempatUsaha from "./surat/SKTempatUsaha";
|
||||||
import SKTidakMampu from "./surat/SKTidakMampu";
|
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";
|
||||||
@@ -115,8 +120,17 @@ export default function ModalSurat({ open, onClose, surat }: { open: boolean, on
|
|||||||
? <SKTidakMampu data={data.data} />
|
? <SKTidakMampu data={data.data} />
|
||||||
: data.data.surat.idCategory == "skyatimpiatu"
|
: data.data.surat.idCategory == "skyatimpiatu"
|
||||||
? <SKYatim data={data.data} />
|
? <SKYatim data={data.data} />
|
||||||
: <></>
|
: data.data.surat.idCategory == "skdomisiliorganisasi"
|
||||||
|
? <SKDomisiliOrganisasi data={data.data} />
|
||||||
|
: data.data.surat.idCategory == "skbedabiodata"
|
||||||
|
? <SKBedaBiodataDiri 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>
|
</div>
|
||||||
|
|||||||
129
src/components/surat/SKBedaBiodataDiri.tsx
Normal file
129
src/components/surat/SKBedaBiodataDiri.tsx
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
export default function SKBedaBiodataDiri({ data }: { data: any }) {
|
||||||
|
const getValue = (jenis: string) =>
|
||||||
|
_.upperFirst(
|
||||||
|
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || ""
|
||||||
|
);
|
||||||
|
|
||||||
|
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: "30px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br /><br /><br /><br />
|
||||||
|
{data.setting.perbekelNama} <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
79
src/components/surat/SKBelumKawin.tsx
Normal file
79
src/components/surat/SKBelumKawin.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
export default function SKBelumKawin({ data }: { data: any }) {
|
||||||
|
const getValue = (jenis: string) =>
|
||||||
|
_.upperFirst(
|
||||||
|
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || ""
|
||||||
|
);
|
||||||
|
|
||||||
|
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 />
|
||||||
|
<u>{getValue("nama")}</u> <br />
|
||||||
|
</div>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br /><br />
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br /><br /><br /><br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
93
src/components/surat/SKDomisiliOrganisasi.tsx
Normal file
93
src/components/surat/SKDomisiliOrganisasi.tsx
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
export default function SKDomisiliOrganisasi({ data }: { data: any }) {
|
||||||
|
const getValue = (jenis: string) =>
|
||||||
|
_.upperFirst(
|
||||||
|
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || ""
|
||||||
|
);
|
||||||
|
|
||||||
|
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 /><br />
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br /><br /><br /><br />
|
||||||
|
{data.setting.perbekelNama} <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
89
src/components/surat/SKKematian.tsx
Normal file
89
src/components/surat/SKKematian.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
export default function SKKematian({ data }: { data: any }) {
|
||||||
|
const getValue = (jenis: string) =>
|
||||||
|
_.upperFirst(
|
||||||
|
data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || ""
|
||||||
|
);
|
||||||
|
|
||||||
|
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 />
|
||||||
|
<u>{getValue("nama")}</u> <br />
|
||||||
|
</div>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br /><br />
|
||||||
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
|
<br /><br /><br /><br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
90
src/components/surat/SKTempatUsaha.tsx
Normal file
90
src/components/surat/SKTempatUsaha.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
export default function SKTempatUsaha({ data }: { data: any }) {
|
||||||
|
const getValue = (key: string) =>
|
||||||
|
_.upperFirst(data.surat.dataText.find((i: any) => i.jenis === key)?.value || "");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ lineHeight: "1.5" }}>
|
||||||
|
{/* TITLE */}
|
||||||
|
<div style={{ textAlign: "center", marginBottom: "20px" }}>
|
||||||
|
<b style={{ fontSize: "16px" }}>SURAT KETERANGAN TEMPAT USAHA</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="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.desaNama}, {data.surat.createdAt} <br /><br /><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 }) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: "flex", marginBottom: "4px" }}>
|
||||||
|
<div style={{ width: "180px" }}>{label}</div>
|
||||||
|
<div style={{ width: "10px" }}>:</div>
|
||||||
|
<div>{value}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ export default function SKTidakMampu({ data }: { data: any }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* DATA PEJABAT */}
|
{/* DATA PEJABAT */}
|
||||||
<div style={{ marginLeft: "20px" }}>
|
<div>
|
||||||
|
|
||||||
<Row label="Nama" value={data.setting.perbekelNama} />
|
<Row label="Nama" value={data.setting.perbekelNama} />
|
||||||
<Row label="Alamat" value={data.setting.desaAlamat} />
|
<Row label="Alamat" value={data.setting.desaAlamat} />
|
||||||
@@ -32,7 +32,7 @@ export default function SKTidakMampu({ data }: { data: any }) {
|
|||||||
<div>Dengan ini menerangkan bahwa:</div>
|
<div>Dengan ini menerangkan bahwa:</div>
|
||||||
|
|
||||||
{/* DATA WARGA */}
|
{/* DATA WARGA */}
|
||||||
<div style={{ marginLeft: "20px" }}>
|
<div>
|
||||||
|
|
||||||
<Row label="Nama" value={getValue("nama")} />
|
<Row label="Nama" value={getValue("nama")} />
|
||||||
<Row label="Tempat Tgl Lahir" value={getValue("tempat tanggal lahir")} />
|
<Row label="Tempat Tgl Lahir" value={getValue("tempat tanggal lahir")} />
|
||||||
@@ -61,7 +61,7 @@ export default function SKTidakMampu({ data }: { data: any }) {
|
|||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
{data.setting.desaNama}, {data.surat.createdAt} <br /><br /><br />
|
{data.setting.desaNama}, {data.surat.createdAt} <br /><br /><br />
|
||||||
|
|
||||||
<b><u>{data.setting.perbekelNama}</u></b><br />
|
<u>{data.setting.perbekelNama}</u><br />
|
||||||
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,41 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKUsaha({ data }: { data: any }) {
|
export default function SKUsaha({ data }: { data: any }) {
|
||||||
|
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 () => {
|
||||||
|
try {
|
||||||
|
setViewImg("");
|
||||||
|
if (!data.setting.perbekelTTD) return;
|
||||||
|
|
||||||
|
const urlApi = '/api/pengaduan/image?folder=syarat-dokumen&fileName=' + data.setting.perbekelTTD;
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
setViewImg(url);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal load gambar:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadImage();
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ lineHeight: "1.3" }}>
|
<div style={{ lineHeight: "1.3" }}>
|
||||||
{/* HEADER */}
|
{/* HEADER */}
|
||||||
@@ -18,7 +48,7 @@ export default function SKUsaha({ data }: { data: any }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* JUDUL */}
|
{/* JUDUL */}
|
||||||
<div style={{ textAlign: "center", margin: "20px 0" }}>
|
<div style={{ textAlign: "center", margin: "15px 0" }}>
|
||||||
<b><u>SURAT KETERANGAN USAHA</u></b><br />
|
<b><u>SURAT KETERANGAN USAHA</u></b><br />
|
||||||
Nomor: {data.surat.noSurat}
|
Nomor: {data.surat.noSurat}
|
||||||
</div>
|
</div>
|
||||||
@@ -102,13 +132,15 @@ export default function SKUsaha({ data }: { data: any }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
{/* TANDA TANGAN */}
|
||||||
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
<div style={{ marginTop: "20px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
<br /><br /><br /><br />
|
<br /><br />
|
||||||
{data.setting.perbekelNama} <br />
|
<img src={viewImg} alt="ttd perbekel" width={100} />
|
||||||
|
<br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,16 +4,10 @@ import KategoriPengaduan from "@/components/KategoriPengaduan";
|
|||||||
import ProfileUser from "@/components/ProfileUser";
|
import ProfileUser from "@/components/ProfileUser";
|
||||||
import UserSetting from "@/components/UserSetting";
|
import UserSetting from "@/components/UserSetting";
|
||||||
import {
|
import {
|
||||||
Button,
|
|
||||||
Card,
|
Card,
|
||||||
Container,
|
Container,
|
||||||
Divider,
|
|
||||||
Flex,
|
|
||||||
Grid,
|
Grid,
|
||||||
NavLink,
|
NavLink
|
||||||
Stack,
|
|
||||||
Table,
|
|
||||||
Title,
|
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import {
|
import {
|
||||||
IconBuildingBank,
|
IconBuildingBank,
|
||||||
|
|||||||
12
src/server/lib/rename-file.ts
Normal file
12
src/server/lib/rename-file.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { mimeToExtension } from "./mimetypeToExtension";
|
||||||
|
|
||||||
|
export function renameFile({ oldFile, newName }: { oldFile: File; newName: string }) {
|
||||||
|
const ext = mimeToExtension(oldFile.type)
|
||||||
|
const nameFix = newName == 'random' ? `${uuidv4()}.${ext}` : newName
|
||||||
|
|
||||||
|
return new File([oldFile], nameFix, {
|
||||||
|
type: oldFile.type,
|
||||||
|
lastModified: oldFile.lastModified,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -128,11 +128,15 @@ export async function listFiles(config: Config): Promise<{ name: string }[]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function catFile(config: Config, fileName: string): Promise<string> {
|
export async function catFile(config: Config, folder: string, fileName: string): Promise<ArrayBuffer> {
|
||||||
const downloadUrlResponse = await fetchWithAuth(config, `${config.URL}/${config.REPO}/file/?p=/${fileName}`);
|
const downloadUrlResponse = await fetchWithAuth(config, `${config.URL}/${config.REPO}/file/?p=/${folder}/${fileName}`);
|
||||||
const downloadUrl = (await downloadUrlResponse.text()).replace(/"/g, '');
|
const downloadUrl = (await downloadUrlResponse.text()).replace(/"/g, '');
|
||||||
const content = await (await fetchWithAuth(config, downloadUrl)).text();
|
|
||||||
return content
|
// Download file sebagai binary, BUKAN text
|
||||||
|
const fileResponse = await fetchWithAuth(config, downloadUrl);
|
||||||
|
const buffer = await fileResponse.arrayBuffer();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadFile(config: Config, file: File): Promise<string> {
|
export async function uploadFile(config: Config, file: File): Promise<string> {
|
||||||
@@ -159,7 +163,7 @@ export async function uploadFile(config: Config, file: File): Promise<string> {
|
|||||||
|
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
|
|
||||||
if (!res.ok) throw new Error(`Upload failed: ${text}`);
|
if (!res.ok) return 'gagal'
|
||||||
return `✅ Uploaded ${file.name} successfully`;
|
return `✅ Uploaded ${file.name} successfully`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { mimeToExtension } from "../lib/mimetypeToExtension"
|
|||||||
import { generateNoPengaduan } from "../lib/no-pengaduan"
|
import { generateNoPengaduan } from "../lib/no-pengaduan"
|
||||||
import { normalizePhoneNumber } from "../lib/normalizePhone"
|
import { normalizePhoneNumber } from "../lib/normalizePhone"
|
||||||
import { prisma } from "../lib/prisma"
|
import { prisma } from "../lib/prisma"
|
||||||
import { catFile, defaultConfigSF, testConnection, uploadFile, uploadFileBase64 } from "../lib/seafile"
|
import { renameFile } from "../lib/rename-file"
|
||||||
|
import { catFile, defaultConfigSF, uploadFile, uploadFileBase64 } from "../lib/seafile"
|
||||||
|
|
||||||
const PengaduanRoute = new Elysia({
|
const PengaduanRoute = new Elysia({
|
||||||
prefix: "pengaduan",
|
prefix: "pengaduan",
|
||||||
@@ -523,20 +524,27 @@ Respon:
|
|||||||
return { success: false, message: "File tidak ditemukan" };
|
return { success: false, message: "File tidak ditemukan" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rename file
|
||||||
|
const renamedFile = renameFile({ oldFile: file, newName: 'random' });
|
||||||
|
|
||||||
|
|
||||||
// Upload ke Seafile (pastikan uploadFile menerima Blob atau ArrayBuffer)
|
// Upload ke Seafile (pastikan uploadFile menerima Blob atau ArrayBuffer)
|
||||||
// const buffer = await file.arrayBuffer();
|
// const buffer = await file.arrayBuffer();
|
||||||
const result = await uploadFile(defaultConfigSF, file);
|
const result = await uploadFile(defaultConfigSF, renamedFile);
|
||||||
|
if (result == 'gagal') {
|
||||||
|
return { success: false, message: "Upload gagal" };
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: "Upload berhasil",
|
message: "Upload berhasil",
|
||||||
filename: file.name,
|
filename: renamedFile.name,
|
||||||
size: file.size,
|
size: renamedFile.size,
|
||||||
seafileResult: result
|
seafileResult: result
|
||||||
};
|
};
|
||||||
}, {
|
}, {
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
file: t.File({ format: "binary" })
|
file: t.Any()
|
||||||
}),
|
}),
|
||||||
detail: {
|
detail: {
|
||||||
summary: "Upload File",
|
summary: "Upload File",
|
||||||
@@ -715,14 +723,10 @@ Respon:
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.get("/image", async ({ query, set }) => {
|
.get("/image", async ({ query, set }) => {
|
||||||
const { fileName } = query
|
const { fileName, folder } = query;
|
||||||
|
|
||||||
const connect = await testConnection(defaultConfigSF)
|
const hasil = await catFile(defaultConfigSF, folder, fileName);
|
||||||
console.log({ connect })
|
|
||||||
|
|
||||||
const hasil = await catFile(defaultConfigSF, fileName)
|
|
||||||
console.log('hasilnya', hasil)
|
|
||||||
// Tentukan tipe MIME berdasarkan ekstensi
|
|
||||||
const ext = fileName.split(".").pop()?.toLowerCase();
|
const ext = fileName.split(".").pop()?.toLowerCase();
|
||||||
const mime =
|
const mime =
|
||||||
ext === "jpg" || ext === "jpeg"
|
ext === "jpg" || ext === "jpeg"
|
||||||
@@ -732,16 +736,21 @@ Respon:
|
|||||||
: "application/octet-stream";
|
: "application/octet-stream";
|
||||||
|
|
||||||
set.headers["Content-Type"] = mime;
|
set.headers["Content-Type"] = mime;
|
||||||
|
set.headers["Content-Length"] = hasil.byteLength.toString();
|
||||||
|
|
||||||
return new Response(hasil);
|
return new Response(hasil);
|
||||||
}, {
|
}, {
|
||||||
query: t.Object({
|
query: t.Object({
|
||||||
fileName: t.String(),
|
fileName: t.String(),
|
||||||
|
folder: t.String()
|
||||||
}),
|
}),
|
||||||
detail: {
|
detail: {
|
||||||
summary: "Gambar Pengaduan Warga",
|
summary: "View Gambar",
|
||||||
description: `tool untuk mendapatkan gambar pengaduan warga`,
|
description: "tool untuk mendapatkan gambar",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
export default PengaduanRoute
|
export default PengaduanRoute
|
||||||
|
|||||||
Reference in New Issue
Block a user