Merge pull request 'amalia/21-nov-25' (#34) from amalia/21-nov-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/34
This commit is contained in:
@@ -16,11 +16,11 @@ import {
|
|||||||
} 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 _ from "lodash";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import ModalFile from "./ModalFile";
|
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);
|
||||||
@@ -50,7 +50,8 @@ export default function DesaSetting() {
|
|||||||
let finalData = { ...dataEdit }; // ← buffer data terbaru
|
let finalData = { ...dataEdit }; // ← buffer data terbaru
|
||||||
|
|
||||||
if (dataEdit.name === "TTD") {
|
if (dataEdit.name === "TTD") {
|
||||||
const resImg = await apiFetch.api.pengaduan.upload.post({ file: img });
|
const oldImg = await apiFetch.api.pengaduan["delete-image"].post({ 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 = {
|
||||||
@@ -176,7 +177,7 @@ export default function DesaSetting() {
|
|||||||
<ModalFile
|
<ModalFile
|
||||||
open={openedPreview && !_.isEmpty(viewImg)}
|
open={openedPreview && !_.isEmpty(viewImg)}
|
||||||
onClose={() => setOpenedPreview(false)}
|
onClose={() => setOpenedPreview(false)}
|
||||||
folder="syarat-dokumen"
|
folder="lainnya"
|
||||||
fileName={viewImg}
|
fileName={viewImg}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
import { detectFileType } from "@/server/lib/detect-type-of-file";
|
||||||
import { Flex, Image, Loader, Modal } from "@mantine/core";
|
import { Flex, Image, Loader, Modal } from "@mantine/core";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import notification from "./notificationGlobal";
|
import notification from "./notificationGlobal";
|
||||||
|
|
||||||
export default function ModalFile({ open, onClose, folder, fileName }: { open: boolean, onClose: () => void, folder: string, fileName: string }) {
|
export default function ModalFile({ open, onClose, folder, fileName }: { open: boolean, onClose: () => void, folder: string, fileName: string }) {
|
||||||
const [viewImg, setViewImg] = useState<string>("");
|
const [viewFile, setViewFile] = useState<string>("");
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [typeFile, setTypeFile] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && fileName) {
|
if (open && fileName) {
|
||||||
@@ -12,12 +14,18 @@ export default function ModalFile({ open, onClose, folder, fileName }: { open: b
|
|||||||
}
|
}
|
||||||
}, [open, fileName]);
|
}, [open, fileName]);
|
||||||
|
|
||||||
|
|
||||||
const loadImage = async () => {
|
const loadImage = async () => {
|
||||||
try {
|
try {
|
||||||
setViewImg("");
|
setViewFile("");
|
||||||
setLoading(true);
|
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 urlApi = '/api/pengaduan/image?folder=' + folder + '&fileName=' + fileName;
|
||||||
// Fetch manual agar mendapatkan Response asli
|
|
||||||
const res = await fetch(urlApi);
|
const res = await fetch(urlApi);
|
||||||
if (!res.ok)
|
if (!res.ok)
|
||||||
return notification({
|
return notification({
|
||||||
@@ -28,7 +36,7 @@ export default function ModalFile({ open, onClose, folder, fileName }: { open: b
|
|||||||
const blob = await res.blob();
|
const blob = await res.blob();
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
setViewImg(url);
|
setViewFile(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal load gambar:", err);
|
console.error("Gagal load gambar:", err);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -43,7 +51,7 @@ export default function ModalFile({ open, onClose, folder, fileName }: { open: b
|
|||||||
opened={open}
|
opened={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
size="lg"
|
size="xl"
|
||||||
withCloseButton
|
withCloseButton
|
||||||
removeScrollProps={{ allowPinchZoom: true }}
|
removeScrollProps={{ allowPinchZoom: true }}
|
||||||
title="File"
|
title="File"
|
||||||
@@ -53,13 +61,19 @@ export default function ModalFile({ open, onClose, folder, fileName }: { open: b
|
|||||||
<Loader />
|
<Loader />
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
{viewImg && (
|
{viewFile && (
|
||||||
<Image
|
<>
|
||||||
radius="md"
|
{typeFile == "pdf" ? (
|
||||||
h={300}
|
<embed src={viewFile} type="application/pdf" width="100%" height="100%" />
|
||||||
fit="contain"
|
) : (
|
||||||
src={viewImg}
|
<Image
|
||||||
/>
|
radius="md"
|
||||||
|
h={300}
|
||||||
|
fit="contain"
|
||||||
|
src={viewFile}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ export default function ModalSurat({ open, onClose, surat }: { open: boolean, on
|
|||||||
|
|
||||||
const downloadPDF = async () => {
|
const downloadPDF = async () => {
|
||||||
const element = hiddenRef.current;
|
const element = hiddenRef.current;
|
||||||
|
|
||||||
const canvas = await html2canvas(element, {
|
const canvas = await html2canvas(element, {
|
||||||
scale: 2,
|
scale: 2,
|
||||||
useCORS: true,
|
useCORS: true,
|
||||||
@@ -64,7 +63,7 @@ export default function ModalSurat({ open, onClose, surat }: { open: boolean, on
|
|||||||
|
|
||||||
pdf.addImage(imgData, "JPEG", 0, 0, imgWidth, imgHeight);
|
pdf.addImage(imgData, "JPEG", 0, 0, imgWidth, imgHeight);
|
||||||
|
|
||||||
pdf.save("surat-keterangan-usaha.pdf");
|
pdf.save(`${data?.data?.surat?.nameCategory}.pdf`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,11 +1,42 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKBedaBiodataDiri({ data }: { data: any }) {
|
export default function SKBedaBiodataDiri({ 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=lainnya&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.25" }}>
|
<div style={{ lineHeight: "1.25" }}>
|
||||||
{/* HEADER */}
|
{/* HEADER */}
|
||||||
@@ -113,13 +144,12 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
{/* TANDA TANGAN */}
|
||||||
<div style={{ marginTop: "30px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
<div style={{ marginTop: "0px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
|
|
||||||
<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 || undefined} alt="ttd perbekel" width={100} /> <br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</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 SKBelumKawin({ data }: { data: any }) {
|
export default function SKBelumKawin({ 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=lainnya&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 */}
|
||||||
@@ -62,13 +92,14 @@ export default function SKBelumKawin({ data }: { data: any }) {
|
|||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
Pemohon
|
Pemohon
|
||||||
<br /><br /><br /><br />
|
<br /><br /><br /><br /><br /><br />
|
||||||
<u>{getValue("nama")}</u> <br />
|
<u>{getValue("nama")}</u> <br />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
<br /><br /><br /><br />
|
<br /><br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,41 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKDomisiliOrganisasi({ data }: { data: any }) {
|
export default function SKDomisiliOrganisasi({ 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=lainnya&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 */}
|
||||||
@@ -79,11 +109,11 @@ export default function SKDomisiliOrganisasi({ data }: { data: any }) {
|
|||||||
{/* TANDA TANGAN */}
|
{/* TANDA TANGAN */}
|
||||||
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br />
|
||||||
<br /><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 || undefined} alt="ttd perbekel" width={100} /> <br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</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 SKKelahiran({ data }: { data: any }) {
|
export default function SKKelahiran({ 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=lainnya&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.2" }}>
|
<div style={{ lineHeight: "1.2" }}>
|
||||||
|
|
||||||
@@ -102,8 +132,9 @@ export default function SKKelahiran({ data }: { data: any }) {
|
|||||||
<div style={{ marginTop: "40px", width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
<div style={{ marginTop: "40px", width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
<br /><br /><br /><br />
|
<br />
|
||||||
{data.setting.perbekelNama} <br />
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</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 SKKelakuanBaik({ data }: { data: any }) {
|
export default function SKKelakuanBaik({ 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=lainnya&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" }}>
|
||||||
|
|
||||||
@@ -103,8 +133,9 @@ export default function SKKelakuanBaik({ data }: { data: any }) {
|
|||||||
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
Kepala Desa {data.setting.desaNama}
|
Kepala Desa {data.setting.desaNama}
|
||||||
<br /><br /><br /><br />
|
<br /> <br />
|
||||||
{data.setting.perbekelNama}<br />
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
||||||
|
<u>{data.setting.perbekelNama}</u><br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</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 SKKematian({ data }: { data: any }) {
|
export default function SKKematian({ 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=lainnya&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 */}
|
||||||
@@ -72,13 +102,14 @@ export default function SKKematian({ data }: { data: any }) {
|
|||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
Pemohon
|
Pemohon
|
||||||
<br /><br /><br /><br />
|
<br /><br /><br /><br /> <br />
|
||||||
<u>{getValue("nama")}</u> <br />
|
<u>{getValue("nama")}</u> <br />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
<br /><br />
|
<br />
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
<br /><br /><br /><br />
|
<br /><br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br />
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,41 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKPenghasilan({ data }: { data: any }) {
|
export default function SKPenghasilan({ 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=lainnya&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 */}
|
||||||
@@ -102,8 +132,9 @@ export default function SKPenghasilan({ data }: { data: any }) {
|
|||||||
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end" }}>
|
<div style={{ marginTop: "40px", display: "flex", justifyContent: "flex-end" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
<br /><br /><br /><br />
|
<br /> <br />
|
||||||
{data.setting.perbekelNama} <br />
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,40 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKTempatUsaha({ data }: { data: any }) {
|
export default function SKTempatUsaha({ data }: { data: any }) {
|
||||||
|
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 urlApi = '/api/pengaduan/image?folder=lainnya&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.5" }}>
|
<div style={{ lineHeight: "1.5" }}>
|
||||||
{/* TITLE */}
|
{/* TITLE */}
|
||||||
@@ -68,8 +99,8 @@ export default function SKTempatUsaha({ data }: { data: any }) {
|
|||||||
{/* TANDA TANGAN */}
|
{/* TANDA TANGAN */}
|
||||||
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
{data.setting.desaNama}, {data.surat.createdAt} <br /><br /><br />
|
{data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br />
|
||||||
<u>{data.setting.perbekelNama}</u><br />
|
<u>{data.setting.perbekelNama}</u><br />
|
||||||
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,40 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKTidakMampu({ data }: { data: any }) {
|
export default function SKTidakMampu({ data }: { data: any }) {
|
||||||
|
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 urlApi = '/api/pengaduan/image?folder=lainnya&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.5" }}>
|
<div style={{ lineHeight: "1.5" }}>
|
||||||
{/* TITLE */}
|
{/* TITLE */}
|
||||||
@@ -59,8 +90,8 @@ export default function SKTidakMampu({ data }: { data: any }) {
|
|||||||
{/* TANDA TANGAN */}
|
{/* TANDA TANGAN */}
|
||||||
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
{data.setting.desaNama}, {data.surat.createdAt} <br /><br /><br />
|
{data.setting.desaKabupaten}, {data.surat.createdAt} <br /> <br />
|
||||||
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /><br />
|
||||||
<u>{data.setting.perbekelNama}</u><br />
|
<u>{data.setting.perbekelNama}</u><br />
|
||||||
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
{data.setting.perbekelJabatan + " " + data.setting.desaNama}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function SKUsaha({ data }: { data: any }) {
|
|||||||
setViewImg("");
|
setViewImg("");
|
||||||
if (!data.setting.perbekelTTD) return;
|
if (!data.setting.perbekelTTD) return;
|
||||||
|
|
||||||
const urlApi = '/api/pengaduan/image?folder=syarat-dokumen&fileName=' + data.setting.perbekelTTD;
|
const urlApi = '/api/pengaduan/image?folder=lainnya&fileName=' + data.setting.perbekelTTD;
|
||||||
// Fetch manual agar mendapatkan Response asli
|
// Fetch manual agar mendapatkan Response asli
|
||||||
const res = await fetch(urlApi);
|
const res = await fetch(urlApi);
|
||||||
if (!res.ok)
|
if (!res.ok)
|
||||||
@@ -132,13 +132,12 @@ export default function SKUsaha({ data }: { data: any }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* TANDA TANGAN */}
|
{/* TANDA TANGAN */}
|
||||||
<div style={{ marginTop: "20px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
<div style={{ marginTop: "10px", display: "flex", justifyContent: "flex-end", width: "100%" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<br />
|
||||||
<br /><br />
|
|
||||||
Kepala Desa / Lurah {data.setting.desaNama}
|
Kepala Desa / Lurah {data.setting.desaNama}
|
||||||
<br /><br />
|
<br /><br />
|
||||||
<img src={viewImg} alt="ttd perbekel" width={100} />
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} />
|
||||||
<br />
|
<br />
|
||||||
<u>{data.setting.perbekelNama}</u> <br />
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
|
|||||||
@@ -1,10 +1,40 @@
|
|||||||
|
import { useShallowEffect } from "@mantine/hooks";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { useState } from "react";
|
||||||
|
import notification from "../notificationGlobal";
|
||||||
|
|
||||||
export default function SKYatim({ data }: { data: any }) {
|
export default function SKYatim({ data }: { data: any }) {
|
||||||
|
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 urlApi = '/api/pengaduan/image?folder=lainnya&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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
loadImage();
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ lineHeight: "1.3" }}>
|
<div style={{ lineHeight: "1.3" }}>
|
||||||
|
|
||||||
@@ -146,14 +176,15 @@ export default function SKYatim({ data }: { data: any }) {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<br /><br />
|
<br />
|
||||||
|
|
||||||
{/* TTD */}
|
{/* TTD */}
|
||||||
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
<div style={{ width: "100%", display: "flex", justifyContent: "flex-end" }}>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
Kepala Desa {data.setting.desaNama}
|
Kepala Desa {data.setting.desaNama}
|
||||||
<br /><br /><br /><br />
|
<br /><br />
|
||||||
{data.setting.perbekelNama} <br />
|
<img src={viewImg || undefined} alt="ttd perbekel" width={100} /> <br />
|
||||||
|
<u>{data.setting.perbekelNama}</u> <br />
|
||||||
NIP. {data.setting.perbekelNIP}
|
NIP. {data.setting.perbekelNIP}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
25
src/server/lib/detect-type-of-file.ts
Normal file
25
src/server/lib/detect-type-of-file.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
function getExtension(fileName: string): string | null {
|
||||||
|
if (!fileName || typeof fileName !== "string") return null;
|
||||||
|
|
||||||
|
const parts = fileName.split(".");
|
||||||
|
if (parts.length <= 1) return null;
|
||||||
|
|
||||||
|
return parts.pop()?.toLowerCase() || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function detectFileType(fileName: string) {
|
||||||
|
const ext = getExtension(fileName);
|
||||||
|
|
||||||
|
if (!ext) return { ext: null, type: "unknown" };
|
||||||
|
|
||||||
|
if (["jpg", "jpeg", "png", "gif", "webp", "bmp"].includes(ext)) {
|
||||||
|
return { ext, type: "image" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext === "pdf") {
|
||||||
|
return { ext, type: "pdf" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ext, type: "other" };
|
||||||
|
}
|
||||||
@@ -94,7 +94,6 @@ export async function fetchWithAuth(config: Config, url: string, options: Reques
|
|||||||
} catch {
|
} catch {
|
||||||
console.error('🔍 Could not read response body');
|
console.error('🔍 Could not read response body');
|
||||||
}
|
}
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -139,7 +138,7 @@ export async function catFile(config: Config, folder: string, fileName: string):
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadFile(config: Config, file: File): Promise<string> {
|
export async function uploadFile(config: Config, file: File, folder: string): Promise<string> {
|
||||||
const remoteName = path.basename(file.name);
|
const remoteName = path.basename(file.name);
|
||||||
|
|
||||||
// 1. Dapatkan upload link (pakai Authorization)
|
// 1. Dapatkan upload link (pakai Authorization)
|
||||||
@@ -152,7 +151,7 @@ export async function uploadFile(config: Config, file: File): Promise<string> {
|
|||||||
// 2. Siapkan form-data
|
// 2. Siapkan form-data
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("parent_dir", "/");
|
formData.append("parent_dir", "/");
|
||||||
formData.append("relative_path", "syarat-dokumen"); // tanpa slash di akhir
|
formData.append("relative_path", folder); // tanpa slash di akhir
|
||||||
formData.append("file", file, remoteName); // file langsung, jangan pakai Blob
|
formData.append("file", file, remoteName); // file langsung, jangan pakai Blob
|
||||||
|
|
||||||
// 3. Upload file TANPA Authorization header, token di query param
|
// 3. Upload file TANPA Authorization header, token di query param
|
||||||
@@ -232,10 +231,10 @@ export async function uploadFileToFolder(config: Config, base64File: { name: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function removeFile(config: Config, fileName: string, folder: string): Promise<string> {
|
||||||
|
const res = await fetchWithAuth(config, `${config.URL}/${config.REPO}/file/?p=/${folder}/${fileName}`, { method: 'DELETE' });
|
||||||
|
|
||||||
|
if (!res.ok) return 'gagal menghapus file';
|
||||||
export async function removeFile(config: Config, fileName: string): Promise<string> {
|
|
||||||
await fetchWithAuth(config, `${config.URL}/${config.REPO}/file/?p=/${fileName}`, { method: 'DELETE' });
|
|
||||||
return `🗑️ Removed ${fileName}`
|
return `🗑️ Removed ${fileName}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ 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 { renameFile } from "../lib/rename-file"
|
import { renameFile } from "../lib/rename-file"
|
||||||
import { catFile, defaultConfigSF, uploadFile, uploadFileBase64 } from "../lib/seafile"
|
import { catFile, defaultConfigSF, removeFile, uploadFile, uploadFileBase64 } from "../lib/seafile"
|
||||||
|
|
||||||
const PengaduanRoute = new Elysia({
|
const PengaduanRoute = new Elysia({
|
||||||
prefix: "pengaduan",
|
prefix: "pengaduan",
|
||||||
@@ -176,7 +176,7 @@ const PengaduanRoute = new Elysia({
|
|||||||
title: judulPengaduan,
|
title: judulPengaduan,
|
||||||
detail: detailPengaduan,
|
detail: detailPengaduan,
|
||||||
idCategory: idCategoryFix,
|
idCategory: idCategoryFix,
|
||||||
idWarga: idWargaFix,
|
idWarga: idWargaFix || "",
|
||||||
location: lokasi,
|
location: lokasi,
|
||||||
image: imageFix,
|
image: imageFix,
|
||||||
noPengaduan,
|
noPengaduan,
|
||||||
@@ -218,23 +218,20 @@ const PengaduanRoute = new Elysia({
|
|||||||
description: "Alamat atau titik lokasi pengaduan"
|
description: "Alamat atau titik lokasi pengaduan"
|
||||||
}),
|
}),
|
||||||
|
|
||||||
namaGambar: t.String({
|
namaGambar: t.Optional(t.String({
|
||||||
optional: true,
|
|
||||||
examples: ["sampah.jpg"],
|
examples: ["sampah.jpg"],
|
||||||
description: "Nama file gambar yang telah diupload (opsional)"
|
description: "Nama file gambar yang telah diupload (opsional)"
|
||||||
}),
|
})),
|
||||||
|
|
||||||
kategoriId: t.String({
|
kategoriId: t.Optional(t.String({
|
||||||
optional: true,
|
|
||||||
examples: ["kebersihan"],
|
examples: ["kebersihan"],
|
||||||
description: "ID atau nama kategori pengaduan (contoh: kebersihan, keamanan, lainnya)"
|
description: "ID atau nama kategori pengaduan (contoh: kebersihan, keamanan, lainnya)"
|
||||||
}),
|
})),
|
||||||
|
|
||||||
wargaId: t.String({
|
wargaId: t.Optional(t.String({
|
||||||
optional: true,
|
|
||||||
examples: ["budiman"],
|
examples: ["budiman"],
|
||||||
description: "ID unik warga yang melapor (jika sudah terdaftar)"
|
description: "ID unik warga yang melapor (jika sudah terdaftar)"
|
||||||
}),
|
})),
|
||||||
|
|
||||||
noTelepon: t.String({
|
noTelepon: t.String({
|
||||||
error: "Nomor telepon harus diisi",
|
error: "Nomor telepon harus diisi",
|
||||||
@@ -517,7 +514,7 @@ Respon:
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.post("/upload", async ({ body }) => {
|
.post("/upload", async ({ body }) => {
|
||||||
const { file } = body;
|
const { file, folder } = body;
|
||||||
|
|
||||||
// Validasi file
|
// Validasi file
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@@ -530,7 +527,7 @@ Respon:
|
|||||||
|
|
||||||
// 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, renamedFile);
|
const result = await uploadFile(defaultConfigSF, renamedFile, folder);
|
||||||
if (result == 'gagal') {
|
if (result == 'gagal') {
|
||||||
return { success: false, message: "Upload gagal" };
|
return { success: false, message: "Upload gagal" };
|
||||||
}
|
}
|
||||||
@@ -544,7 +541,8 @@ Respon:
|
|||||||
};
|
};
|
||||||
}, {
|
}, {
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
file: t.Any()
|
file: t.Any(),
|
||||||
|
folder: t.String(),
|
||||||
}),
|
}),
|
||||||
detail: {
|
detail: {
|
||||||
summary: "Upload File",
|
summary: "Upload File",
|
||||||
@@ -728,12 +726,14 @@ Respon:
|
|||||||
const hasil = await catFile(defaultConfigSF, folder, fileName);
|
const hasil = await catFile(defaultConfigSF, folder, fileName);
|
||||||
|
|
||||||
const ext = fileName.split(".").pop()?.toLowerCase();
|
const ext = fileName.split(".").pop()?.toLowerCase();
|
||||||
const mime =
|
let mime = "application/octet-stream"; // default
|
||||||
ext === "jpg" || ext === "jpeg"
|
|
||||||
? "image/jpeg"
|
if (["jpg", "jpeg"].includes(ext!)) mime = "image/jpeg";
|
||||||
: ext === "png"
|
if (["png"].includes(ext!)) mime = "image/png";
|
||||||
? "image/png"
|
if (["gif"].includes(ext!)) mime = "image/gif";
|
||||||
: "application/octet-stream";
|
if (["webp"].includes(ext!)) mime = "image/webp";
|
||||||
|
if (["svg"].includes(ext!)) mime = "image/svg+xml";
|
||||||
|
if (["pdf"].includes(ext!)) mime = "application/pdf";
|
||||||
|
|
||||||
set.headers["Content-Type"] = mime;
|
set.headers["Content-Type"] = mime;
|
||||||
set.headers["Content-Length"] = hasil.byteLength.toString();
|
set.headers["Content-Length"] = hasil.byteLength.toString();
|
||||||
@@ -749,6 +749,33 @@ Respon:
|
|||||||
description: "tool untuk mendapatkan gambar",
|
description: "tool untuk mendapatkan gambar",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.post("/delete-image", async ({ body }) => {
|
||||||
|
const { file, folder } = body;
|
||||||
|
|
||||||
|
// Validasi file
|
||||||
|
if (!file) {
|
||||||
|
return { success: false, message: "File tidak ditemukan" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await removeFile(defaultConfigSF, file, folder);
|
||||||
|
if (result == 'gagal') {
|
||||||
|
return { success: false, message: "Delete gagal" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Delete berhasil",
|
||||||
|
};
|
||||||
|
}, {
|
||||||
|
body: t.Object({
|
||||||
|
file: t.String(),
|
||||||
|
folder: t.String(),
|
||||||
|
}),
|
||||||
|
detail: {
|
||||||
|
summary: "Delete File",
|
||||||
|
description: "Tool untuk delete file Seafile",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ const SuratRoute = new Elysia({
|
|||||||
select: {
|
select: {
|
||||||
DataTextPelayanan: true,
|
DataTextPelayanan: true,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
CategoryPelayanan: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -37,6 +42,7 @@ const SuratRoute = new Elysia({
|
|||||||
surat: {
|
surat: {
|
||||||
id: dataSurat?.id,
|
id: dataSurat?.id,
|
||||||
idCategory: dataSurat?.idCategory,
|
idCategory: dataSurat?.idCategory,
|
||||||
|
nameCategory: dataSurat?.CategoryPelayanan?.name,
|
||||||
noSurat: dataSurat?.noSurat,
|
noSurat: dataSurat?.noSurat,
|
||||||
dataText: dataSurat?.PelayananAjuan?.DataTextPelayanan,
|
dataText: dataSurat?.PelayananAjuan?.DataTextPelayanan,
|
||||||
createdAt: dataSurat?.createdAt.toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" }),
|
createdAt: dataSurat?.createdAt.toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" }),
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ const WargaRoute = new Elysia({
|
|||||||
phone: t.String({ minLength: 1 })
|
phone: t.String({ minLength: 1 })
|
||||||
}),
|
}),
|
||||||
detail: {
|
detail: {
|
||||||
summary: "edit konfigurasi desa",
|
summary: "Edit Warga",
|
||||||
description: `tool untuk edit konfigurasi desa`
|
description: `tool untuk edit warga`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.get("/detail", async ({ query }) => {
|
.get("/detail", async ({ query }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user