upd: dashboard admin
Deskripsi: - update modal surat - api surat - dowload surat No Issues
This commit is contained in:
108
src/components/ModalSurat.tsx
Normal file
108
src/components/ModalSurat.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import apiFetch from "@/lib/apiFetch";
|
||||
import { ActionIcon, Flex, Modal } from "@mantine/core";
|
||||
import { useShallowEffect } from "@mantine/hooks";
|
||||
import { IconDownload, IconX } from "@tabler/icons-react";
|
||||
import html2canvas from "html2canvas";
|
||||
import jsPDF from "jspdf";
|
||||
import { useRef } from "react";
|
||||
import useSWR from "swr";
|
||||
import SKUsaha from "./surat/SKUsaha";
|
||||
|
||||
export default function ModalSurat({ open, onClose, surat }: { open: boolean, onClose: () => void, surat: string }) {
|
||||
const A4Style = {
|
||||
width: "210mm",
|
||||
height: "297mm",
|
||||
padding: "20mm",
|
||||
background: "#fff",
|
||||
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(() => {
|
||||
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 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 imgWidth = pageWidth;
|
||||
const imgHeight = (canvas.height * pageWidth) / canvas.width;
|
||||
|
||||
pdf.addImage(imgData, "JPEG", 0, 0, imgWidth, imgHeight);
|
||||
|
||||
pdf.save("surat-keterangan-usaha.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>
|
||||
|
||||
<Flex gap={8}>
|
||||
<ActionIcon size={32} variant="default">
|
||||
<IconDownload size={20} onClick={downloadPDF} />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon size={32} variant="default" onClick={onClose}>
|
||||
<IconX size={20} />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
<div ref={hiddenRef} style={A4Style}>
|
||||
{
|
||||
data && data.data && data.data.surat.idCategory == "skusaha"
|
||||
? <SKUsaha data={data.data} />
|
||||
: <></>
|
||||
}
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
118
src/components/surat/SKUsaha.tsx
Normal file
118
src/components/surat/SKUsaha.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import _ from "lodash";
|
||||
|
||||
export default function SKUsaha({ 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 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: "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>
|
||||
);
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import PengaduanRoute from "./server/routes/pengaduan_route";
|
||||
import TestPengaduanRoute from "./server/routes/test_pengaduan";
|
||||
import UserRoute from "./server/routes/user_route";
|
||||
import WargaRoute from "./server/routes/warga_route";
|
||||
import SuratRoute from "./server/routes/surat_route";
|
||||
|
||||
const Docs = new Elysia({
|
||||
tags: ["docs"],
|
||||
@@ -34,6 +35,7 @@ const Api = new Elysia({
|
||||
.use(PelayananRoute)
|
||||
.use(ConfigurationDesaRoute)
|
||||
.use(WargaRoute)
|
||||
.use(SuratRoute)
|
||||
.use(TestPengaduanRoute)
|
||||
.use(apiAuth)
|
||||
.use(ApiKeyRoute)
|
||||
|
||||
@@ -25,7 +25,7 @@ export const categoryPelayananSurat = [
|
||||
syaratDokumen: [
|
||||
{ name: "pengantar kelian", desc: "Surat Pengantar Kelian Banjar Dinas" },
|
||||
{ name: "skt organisasi", desc: "Fotokopi Surat Keterangan Terdaftar (SKT) Organisasi atau Pengukuhan Kelompok" },
|
||||
{name: "susunan pengurus", desc: "Jika Pengajuan baru pembuatan SKT maka melengkapi Susunan Pengurus lengkap denganKop Organisasi"}
|
||||
{ name: "susunan pengurus", desc: "Jika Pengajuan baru pembuatan SKT maka melengkapi Susunan Pengurus lengkap denganKop Organisasi" }
|
||||
],
|
||||
dataText: ["nama organisasi", "alamat organisasi", "nama pemohon", "jabatan pemohon", "kontak", "penanggung jawab", "tanggal berdiri"]
|
||||
},
|
||||
@@ -95,7 +95,7 @@ export const categoryPelayananSurat = [
|
||||
{ name: "ktp/kk", desc: "Fotokopi KTP atau Kartu Keluarga" },
|
||||
{ name: "foto lokasi", desc: "Foto lokasi usaha dicetak dalam selembar kertas, diparaf dan distempel oleh Kelian" }
|
||||
],
|
||||
dataText: ["jenis usaha", "alamat usaha"]
|
||||
dataText: ["nama", "jenis kelamin", "tempat tanggal lahir", "negara", "agama", "status perkawinan", "alamat", "pekerjaan", "jenis usaha", "alamat usaha"]
|
||||
},
|
||||
{
|
||||
id: "skyatimpiatu",
|
||||
|
||||
@@ -47,7 +47,7 @@ export const confDesa = [
|
||||
{
|
||||
id: "perbekelNIP",
|
||||
name: "NIP",
|
||||
value: ""
|
||||
value: "1122334455"
|
||||
},
|
||||
{
|
||||
id: "perbekelTTD",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import ModalSurat from "@/components/ModalSurat";
|
||||
import notification from "@/components/notificationGlobal";
|
||||
import apiFetch from "@/lib/apiFetch";
|
||||
import {
|
||||
Anchor,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
@@ -73,6 +75,7 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
const [keterangan, setKeterangan] = useState("");
|
||||
const [host, setHost] = useState<User | null>(null);
|
||||
const [noSurat, setNoSurat] = useState("");
|
||||
const [openedPreview, setOpenedPreview] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchHost() {
|
||||
@@ -169,6 +172,11 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
)}
|
||||
</Stack>
|
||||
</Modal>
|
||||
{
|
||||
data?.status == "selesai" &&
|
||||
(<ModalSurat open={openedPreview} onClose={() => setOpenedPreview(false)} surat={data?.idSurat} />)
|
||||
}
|
||||
|
||||
|
||||
<Card
|
||||
radius="md"
|
||||
@@ -224,13 +232,17 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
spacing="sm"
|
||||
pt={10}
|
||||
icon={
|
||||
<ThemeIcon color="green" size={20} radius="xl">
|
||||
<ThemeIcon variant="default" size={20} radius="xl">
|
||||
<IconCheck size={13} />
|
||||
</ThemeIcon>
|
||||
}
|
||||
>
|
||||
{syaratDokumen?.map((v: any) => (
|
||||
<List.Item key={v.id}>{v.jenis}</List.Item>
|
||||
<List.Item key={v.id}>
|
||||
<Anchor href="https://mantine.dev/" target="_blank">
|
||||
{v.jenis}
|
||||
</Anchor>
|
||||
</List.Item>
|
||||
))}
|
||||
</List>
|
||||
</Flex>
|
||||
@@ -307,15 +319,9 @@ function DetailDataPengajuan({ data, syaratDokumen, dataText, onAction }: { data
|
||||
<Group justify="center" grow>
|
||||
<Button
|
||||
variant="light"
|
||||
onClick={() => { }}
|
||||
onClick={() => setOpenedPreview(!openedPreview)}
|
||||
>
|
||||
Lihat Surat
|
||||
</Button>
|
||||
<Button
|
||||
variant="light"
|
||||
onClick={() => { }}
|
||||
>
|
||||
Download
|
||||
Surat
|
||||
</Button>
|
||||
</Group>
|
||||
)
|
||||
|
||||
@@ -170,6 +170,17 @@ const PelayananRoute = new Elysia({
|
||||
}
|
||||
})
|
||||
|
||||
const dataSurat = await prisma.suratPelayanan.findFirst({
|
||||
where: {
|
||||
idPengajuanLayanan: data?.id,
|
||||
isActive: true
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
idCategory: true,
|
||||
}
|
||||
})
|
||||
|
||||
const dataSyarat = await prisma.syaratDokumenPelayanan.findMany({
|
||||
where: {
|
||||
idPengajuanLayanan: data?.id,
|
||||
@@ -266,6 +277,7 @@ const PelayananRoute = new Elysia({
|
||||
status: data?.status,
|
||||
createdAt: data?.createdAt,
|
||||
updatedAt: data?.updatedAt,
|
||||
idSurat: dataSurat?.id,
|
||||
}
|
||||
|
||||
const datafix = {
|
||||
@@ -275,7 +287,6 @@ const PelayananRoute = new Elysia({
|
||||
syaratDokumen: dataSyaratFix,
|
||||
dataText: dataTextFix,
|
||||
}
|
||||
|
||||
return datafix
|
||||
}, {
|
||||
query: t.Object({
|
||||
|
||||
59
src/server/routes/surat_route.ts
Normal file
59
src/server/routes/surat_route.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import { prisma } from "../lib/prisma";
|
||||
|
||||
const SuratRoute = new Elysia({
|
||||
prefix: "surat",
|
||||
tags: ["surat"],
|
||||
})
|
||||
.get("/detail", async ({ query }) => {
|
||||
const { id } = query
|
||||
|
||||
const dataSurat = await prisma.suratPelayanan.findUnique({
|
||||
where: {
|
||||
id
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
noSurat: true,
|
||||
idCategory: true,
|
||||
createdAt: true,
|
||||
PelayananAjuan: {
|
||||
select: {
|
||||
DataTextPelayanan: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const dataSetting = await prisma.configuration.findMany()
|
||||
|
||||
const toObject = (arr: any[]) =>
|
||||
dataSetting.reduce((acc: any, item: any) => {
|
||||
acc[item.id] = item.value;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
surat: {
|
||||
id: dataSurat?.id,
|
||||
idCategory: dataSurat?.idCategory,
|
||||
noSurat: dataSurat?.noSurat,
|
||||
dataText: dataSurat?.PelayananAjuan?.DataTextPelayanan,
|
||||
createdAt: dataSurat?.createdAt.toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" }),
|
||||
},
|
||||
setting: toObject(dataSetting)
|
||||
}
|
||||
|
||||
}, {
|
||||
query: t.Object({
|
||||
id: t.String({ minLength: 1, error: "id harus diisi" })
|
||||
}),
|
||||
detail: {
|
||||
summary: "Detail Surat",
|
||||
description: `tool untuk mendapatkan detail surat`,
|
||||
}
|
||||
|
||||
})
|
||||
;
|
||||
|
||||
export default SuratRoute
|
||||
Reference in New Issue
Block a user