diff --git a/src/components/KategoriPelayananSurat.tsx b/src/components/KategoriPelayananSurat.tsx index da0752b..f39ba3e 100644 --- a/src/components/KategoriPelayananSurat.tsx +++ b/src/components/KategoriPelayananSurat.tsx @@ -13,7 +13,7 @@ import { Table, Text, Title, - Tooltip + Tooltip, } from "@mantine/core"; import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { IconEdit, IconEye, IconPlus, IconTrash } from "@tabler/icons-react"; @@ -190,7 +190,10 @@ export default function KategoriPelayananSurat({ function handleAddSyarat() { setDataChoose({ ...dataChoose, - syaratDokumen: [...dataChoose.syaratDokumen, { key: "", name: "", desc: "" }], + syaratDokumen: [ + ...dataChoose.syaratDokumen, + { key: "", name: "", desc: "" }, + ], }); } diff --git a/src/components/NotFoundPengajuanSurat.tsx b/src/components/NotFoundPengajuanSurat.tsx index e63ea48..95252b2 100644 --- a/src/components/NotFoundPengajuanSurat.tsx +++ b/src/components/NotFoundPengajuanSurat.tsx @@ -1,33 +1,27 @@ -import { - Button, - Center, - Group, - Stack, - Text, - Title -} from "@mantine/core" -import { IconSearch } from "@tabler/icons-react" +import { Button, Center, Group, Stack, Text, Title } from "@mantine/core"; +import { IconSearch } from "@tabler/icons-react"; export function DataNotFound({ - onRetry, - backTo + onRetry, + backTo, }: { - onRetry?: () => void - backTo?: () => void + onRetry?: () => void; + backTo?: () => void; }) { - return ( -
- - + return ( +
+ + - Data Pengajuan Tidak Ditemukan + Data Pengajuan Tidak Ditemukan - - Kami tidak dapat menemukan data pengajuan dengan nomor pengajuan yg diinputkan. Silakan periksa kembali data Anda. - + + Kami tidak dapat menemukan data pengajuan dengan nomor pengajuan yg + diinputkan. Silakan periksa kembali data Anda. + - - {/* {onRetry && ( + + {/* {onRetry && ( )} */} - - - -
- ) + + +
+
+ ); } diff --git a/src/components/SuccessPengajuanSurat.tsx b/src/components/SuccessPengajuanSurat.tsx index 9a21128..355b00c 100644 --- a/src/components/SuccessPengajuanSurat.tsx +++ b/src/components/SuccessPengajuanSurat.tsx @@ -4,13 +4,13 @@ import { IconCheck } from "@tabler/icons-react"; type SuccessPengajuanProps = { noPengajuan: string; onClose?: () => void; - category?: 'create' | 'update'; + category?: "create" | "update"; }; export default function SuccessPengajuan({ noPengajuan, onClose, - category + category, }: SuccessPengajuanProps) { return (
@@ -19,11 +19,15 @@ export default function SuccessPengajuan({ - {category == 'create' ? 'Pengajuan Berhasil Dibuat' : 'Pengajuan Berhasil Diupdate'} + {category == "create" + ? "Pengajuan Berhasil Dibuat" + : "Pengajuan Berhasil Diupdate"} - {category == 'create' ? 'Pengajuan layanan surat sudah dibuat dengan nomor:' : 'Pengajuan layanan surat sudah diupdate dengan nomor:'} + {category == "create" + ? "Pengajuan layanan surat sudah dibuat dengan nomor:" + : "Pengajuan layanan surat sudah diupdate dengan nomor:"} diff --git a/src/components/surat/SKBedaBiodataDiri.tsx b/src/components/surat/SKBedaBiodataDiri.tsx index 59efc34..241937f 100644 --- a/src/components/surat/SKBedaBiodataDiri.tsx +++ b/src/components/surat/SKBedaBiodataDiri.tsx @@ -7,7 +7,7 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) { const getValue = (jenis: string) => _.upperFirst( data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || - "", + "", ); const loadImage = async () => { @@ -146,9 +146,7 @@ export default function SKBedaBiodataDiri({ data }: { data: any }) { 1. {getValue("data_dokumen")} - - {/* {getValue("nama")} */} - + {/* {getValue("nama")} */} Tertulis pada dokumen A diff --git a/src/components/surat/SKBelumKawin.tsx b/src/components/surat/SKBelumKawin.tsx index 9033893..66640ef 100644 --- a/src/components/surat/SKBelumKawin.tsx +++ b/src/components/surat/SKBelumKawin.tsx @@ -7,7 +7,7 @@ export default function SKBelumKawin({ data }: { data: any }) { const getValue = (jenis: string) => _.upperFirst( data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || - "", + "", ); const loadImage = async () => { diff --git a/src/components/surat/SKKematian.tsx b/src/components/surat/SKKematian.tsx index ad0654a..740e135 100644 --- a/src/components/surat/SKKematian.tsx +++ b/src/components/surat/SKKematian.tsx @@ -7,7 +7,7 @@ export default function SKKematian({ data }: { data: any }) { const getValue = (jenis: string) => _.upperFirst( data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || - "", + "", ); const loadImage = async () => { diff --git a/src/components/surat/SKPenghasilan.tsx b/src/components/surat/SKPenghasilan.tsx index 72c07fe..173a7ce 100644 --- a/src/components/surat/SKPenghasilan.tsx +++ b/src/components/surat/SKPenghasilan.tsx @@ -135,9 +135,7 @@ export default function SKPenghasilan({ data }: { data: any }) { Penghasilan : - - Rp. {getValue("penghasilan")} per bulan - + Rp. {getValue("penghasilan")} per bulan @@ -145,8 +143,8 @@ export default function SKPenghasilan({ data }: { data: any }) { {/* KEPERLUAN */}
- Surat keterangan ini dibuat untuk keperluan:{" "} - {getValue("alasan")}. + Surat keterangan ini dibuat untuk keperluan: {getValue("alasan")} + .
diff --git a/src/components/surat/SKTempatUsaha.tsx b/src/components/surat/SKTempatUsaha.tsx index fd20642..e0bb245 100644 --- a/src/components/surat/SKTempatUsaha.tsx +++ b/src/components/surat/SKTempatUsaha.tsx @@ -71,7 +71,10 @@ export default function SKTempatUsaha({ data }: { data: any }) { label="Tempat/Tanggal Lahir" value={`${getValue("tempat_lahir")}, ${getValue("tanggal_lahir")}`} /> - +
@@ -86,14 +89,8 @@ export default function SKTempatUsaha({ data }: { data: any }) { - - + + diff --git a/src/components/surat/SKUsaha.tsx b/src/components/surat/SKUsaha.tsx index 4d604c7..25ab268 100644 --- a/src/components/surat/SKUsaha.tsx +++ b/src/components/surat/SKUsaha.tsx @@ -7,7 +7,7 @@ export default function SKUsaha({ data }: { data: any }) { const getValue = (jenis: string) => _.upperFirst( data.surat.dataText.find((item: any) => item.jenis === jenis)?.value || - "", + "", ); const loadImage = async () => { diff --git a/src/components/surat/SKYatimPiatu.tsx b/src/components/surat/SKYatimPiatu.tsx index 69e74d0..46c621c 100644 --- a/src/components/surat/SKYatimPiatu.tsx +++ b/src/components/surat/SKYatimPiatu.tsx @@ -98,7 +98,9 @@ export default function SKYatim({ data }: { data: any }) { Tempat/Tanggal Lahir - : {`${getValue("tempat_lahir")}, ${getValue("tanggal_lahir")}`} + + : {`${getValue("tempat_lahir")}, ${getValue("tanggal_lahir")}`} + Jenis Kelamin diff --git a/src/index.tsx b/src/index.tsx index 1622581..cc5738b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -15,6 +15,7 @@ import LayananRoute from "./server/routes/layanan_route"; import { MCPRoute } from "./server/routes/mcp_route"; import PelayananRoute from "./server/routes/pelayanan_surat_route"; import PengaduanRoute from "./server/routes/pengaduan_route"; +import SendWaRoute from "./server/routes/send_wa_route"; import SuratRoute from "./server/routes/surat_route"; import TestPengaduanRoute from "./server/routes/test_pengaduan"; import UserRoute from "./server/routes/user_route"; @@ -45,7 +46,8 @@ const Api = new Elysia({ .use(CredentialRoute) .use(UserRoute) .use(LayananRoute) - .use(AduanRoute); + .use(AduanRoute) + .use(SendWaRoute); const app = new Elysia() .use(Api) diff --git a/src/pages/darmasaba/surat.tsx b/src/pages/darmasaba/surat.tsx index 0b6d33b..66223e2 100644 --- a/src/pages/darmasaba/surat.tsx +++ b/src/pages/darmasaba/surat.tsx @@ -31,7 +31,7 @@ import { IconInfoCircle, IconNotes, IconPhone, - IconUpload + IconUpload, } from "@tabler/icons-react"; import dayjs from "dayjs"; import "dayjs/locale/id"; @@ -268,9 +268,7 @@ export default function FormSurat() { overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} > - - Apakah anda yakin ingin mengirim pengajuan surat ini? - + Apakah anda yakin ingin mengirim pengajuan surat ini? */} - - - - )} + + + + )} - } - + )} ); } diff --git a/src/pages/darmasaba/update_data_surat.tsx b/src/pages/darmasaba/update_data_surat.tsx index 43b4aae..aaa4b28 100644 --- a/src/pages/darmasaba/update_data_surat.tsx +++ b/src/pages/darmasaba/update_data_surat.tsx @@ -6,34 +6,34 @@ import SuccessPengajuan from "@/components/SuccessPengajuanSurat"; import apiFetch from "@/lib/apiFetch"; import { parseTanggalID } from "@/server/lib/stringToDate"; import { - ActionIcon, - Alert, - Anchor, - Badge, - Box, - Button, - Card, - Container, - Divider, - FileInput, - Flex, - Grid, - Group, - Modal, - Select, - Stack, - Text, - TextInput, - Tooltip + ActionIcon, + Alert, + Anchor, + Badge, + Box, + Button, + Card, + Container, + Divider, + FileInput, + Flex, + Grid, + Group, + Modal, + Select, + Stack, + Text, + TextInput, + Tooltip, } from "@mantine/core"; import { DateInput } from "@mantine/dates"; import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { - IconBuildingCommunity, - IconFiles, - IconInfoCircle, - IconNotes, - IconUpload + IconBuildingCommunity, + IconFiles, + IconInfoCircle, + IconNotes, + IconUpload, } from "@tabler/icons-react"; import dayjs from "dayjs"; import "dayjs/locale/id"; @@ -42,645 +42,689 @@ import React, { useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; type DataItem = { - key: string; - value: string; + key: string; + value: string; }; type UpdateDataItem = { - id: string; - key: string; - value: any; + id: string; + key: string; + value: any; }; type FormSurat = { - kategoriId: string; - nama: string; - phone: string; - dataPelengkap: DataItem[]; - syaratDokumen: DataItem[]; + kategoriId: string; + nama: string; + phone: string; + dataPelengkap: DataItem[]; + syaratDokumen: DataItem[]; }; type FormUpdateSurat = { - dataPelengkap: UpdateDataItem[]; - syaratDokumen: UpdateDataItem[]; + dataPelengkap: UpdateDataItem[]; + syaratDokumen: UpdateDataItem[]; }; type DataPengajuan = { - id: string; - noPengajuan: string; - category: string; - status: "antrian" | "diproses" | "selesai" | "ditolak"; - createdAt: Date; - updatedAt: Date; - idSurat: string | undefined; -} + id: string; + noPengajuan: string; + category: string; + status: "antrian" | "diproses" | "selesai" | "ditolak"; + createdAt: Date; + updatedAt: Date; + idSurat: string | undefined; +}; export default function UpdateDataSurat() { - const navigate = useNavigate(); - const { search } = useLocation(); - const query = new URLSearchParams(search); - const noPengajuan = query.get("pengajuan"); - const [found, setFound] = useState(true); + const navigate = useNavigate(); + const { search } = useLocation(); + const query = new URLSearchParams(search); + const noPengajuan = query.get("pengajuan"); + const [found, setFound] = useState(true); - - return ( - - - - { - found && - - - -
- - Update Data Pengajuan Surat Administrasi - - - Formulir ini digunakan untuk memperbarui data pengajuan surat administrasi yang telah diajukan sebelumnya. - -
-
-
- } - - { - !noPengajuan ? ( - - ) - : - found ? ( - { setFound(e) }} /> - ) : ( - navigate("/darmasaba/update-data-surat")} /> - ) - } - -
-
-
- ); + return ( + + + + {found && ( + + + +
+ + Update Data Pengajuan Surat Administrasi + + + Formulir ini digunakan untuk memperbarui data pengajuan + surat administrasi yang telah diajukan sebelumnya. + +
+
+
+ )} + + {!noPengajuan ? ( + + ) : found ? ( + { + setFound(e); + }} + /> + ) : ( + navigate("/darmasaba/update-data-surat")} + /> + )} + +
+
+
+ ); } function FieldLabel({ label, hint }: { label: string; hint?: string }) { - return ( - - {label} - {hint && ( - - - - - - )} - - ); + return ( + + {label} + {hint && ( + + + + + + )} + + ); } function FormSection({ - title, - icon, - children, - description, - info, + title, + icon, + children, + description, + info, }: { - title?: string; - icon?: React.ReactNode; - children: React.ReactNode; - description?: string; - info?: string; + title?: string; + icon?: React.ReactNode; + children: React.ReactNode; + description?: string; + info?: string; }) { - return ( - - - - - {icon} - {title && {title}} - - {description && {description}} - - {info && {info}} - - { - title && - } - {children} - - ); + return ( + + + + + {icon} + {title && {title}} + + {description && {description}} + + {info && ( + + {info} + + )} + + {title && } + {children} + + ); } function FileInputWrapper({ - label, - placeholder, - accept, - onChange, - preview, - name, - description, - linkView, - disabled + label, + placeholder, + accept, + onChange, + preview, + name, + description, + linkView, + disabled, }: { - label: string; - placeholder?: string; - accept?: string; - linkView?: string; - onChange: (file: File | null) => void; - preview?: string | null; - name: string; - description?: string; - disabled?: boolean; + label: string; + placeholder?: string; + accept?: string; + linkView?: string; + onChange: (file: File | null) => void; + preview?: string | null; + name: string; + description?: string; + disabled?: boolean; }) { - const [viewImg, setViewImg] = useState(""); - const [openedPreviewFile, setOpenedPreviewFile] = useState(false); + const [viewImg, setViewImg] = useState(""); + const [openedPreviewFile, setOpenedPreviewFile] = useState(false); + useShallowEffect(() => { + if (viewImg) { + setOpenedPreviewFile(true); + } + }, [viewImg]); - useShallowEffect(() => { - if (viewImg) { - setOpenedPreviewFile(true); - } - }, [viewImg]); + return ( + <> + { + setOpenedPreviewFile(false); + }} + folder="syarat-dokumen" + fileName={viewImg} + /> - return ( - <> - { - setOpenedPreviewFile(false); - }} - folder="syarat-dokumen" - fileName={viewImg} - /> + + + + {label} + + {description && ( + + {description} + + )} + {linkView && ( + setViewImg(linkView)} size="sm"> + Lihat dokumen sebelumnya + + )} + - - - - {label} - - {description && ( - - {description} - - )} - { - linkView && ( - setViewImg(linkView)} size="sm"> - Lihat dokumen sebelumnya - - ) - } - + onChange(f)} + leftSection={} + aria-label={label} + name={name} + disabled={disabled} + /> - onChange(f)} - leftSection={} - aria-label={label} - name={name} - disabled={disabled} - /> - - {preview ? ( -
- - Preview: - -
- {`${label} -
-
- ) : null} -
- - - ); + {preview ? ( +
+ + Preview: + +
+ {`${label} +
+
+ ) : null} +
+ + ); } function SearchData() { - const [submitLoading, setSubmitLoading] = useState(false); - const [searchPengajuan, setSearchPengajuan] = useState(""); - const [searchPengajuanPhone, setSearchPengajuanPhone] = useState(""); - const navigate = useNavigate(); + const [submitLoading, setSubmitLoading] = useState(false); + const [searchPengajuan, setSearchPengajuan] = useState(""); + const [searchPengajuanPhone, setSearchPengajuanPhone] = useState(""); + const navigate = useNavigate(); - async function handleSearch() { - try { - setSubmitLoading(true); - if (searchPengajuan == "" || searchPengajuanPhone == "") { - notification({ - title: "Peringatan", - message: "Silakan isi nomor pengajuan atau nomor telephone", - type: "warning" - }); - return; - } - - const response = await apiFetch.api.pelayanan["get-no-pengajuan"].post({ - phone: searchPengajuanPhone, - noPengajuan: searchPengajuan - }); - - if (response.status === 200) { - if (response.data?.success) { - navigate(`/darmasaba/update-data-surat?pengajuan=${response.data.nomer}`); - } else { - notification({ - title: "Peringatan", - message: response.data?.message || "Data pengajuan tidak valid", - type: "warning" - }); - } - } else { - notification({ - title: "Error", - message: "Pengajuan tidak ditemukan atau gagal memuat data", - type: "error" - }); - } - - } catch (error) { - console.error("Error searching:", error); - notification({ - title: "Error", - message: "Gagal mencari data pengajuan", - type: "error" - }); - } finally { - setSubmitLoading(false); - } - } - - return ( - - - - } - placeholder="PS-2025-000123" - onChange={(e) => { setSearchPengajuan(e.target.value) }} - /> - - - - - } - placeholder="08123456789" - type="number" - onChange={(e) => { setSearchPengajuanPhone(e.target.value) }} - /> - - - - - - - - ) -} - - -function DataUpdate({ noPengajuan, onValidate }: { noPengajuan: string, onValidate: (e: boolean) => void }) { - const [opened, { open, close }] = useDisclosure(false) - const navigate = useNavigate() - const [sukses, setSukses] = useState(false) - const [submitLoading, setSubmitLoading] = useState(false) - const [dataPelengkap, setDataPelengkap] = useState([]) - const [dataSyaratDokumen, setDataSyaratDokumen] = useState([]) - const [dataPengajuan, setDataPengajuan] = useState({}) - const [status, setStatus] = useState("") - const [formSurat, setFormSurat] = useState({ - dataPelengkap: [], - syaratDokumen: [], - }); - - async function fetchData() { - try { - const res = await apiFetch.api.pelayanan["detail-data"].post({ nomerPengajuan: noPengajuan }); - if (res.data && res.data.success === true) { - onValidate(true) - setDataPelengkap(res.data.dataPelengkap || []); - setDataSyaratDokumen(res.data.syaratDokumen || []); - setDataPengajuan(res.data.pengajuan || {}); - - setStatus(res.data.pengajuan && 'status' in res.data.pengajuan ? res.data.pengajuan.status : ""); - } else { - // notification({ - // title: "Error", - // message: res.data?.message || "Gagal memuat data", - // type: "error", - // }); - onValidate(false) - setDataPelengkap([]); - setDataSyaratDokumen([]); - setDataPengajuan({}); - - } - } catch (error) { - console.error('Error fetching data:', error); - } - } - - useShallowEffect(() => { - fetchData(); - }, []); - - function upsertById( - array: T[], - item: T - ): T[] { - const index = array.findIndex((v) => v.id === item.id) - - // ➕ insert - if (index === -1) { - return [...array, item] + async function handleSearch() { + try { + setSubmitLoading(true); + if (searchPengajuan == "" || searchPengajuanPhone == "") { + notification({ + title: "Peringatan", + message: "Silakan isi nomor pengajuan atau nomor telephone", + type: "warning", + }); + return; } - // ✏️ update - return array.map((v, i) => (i === index ? { ...v, ...item } : v)) - } - - - function validationForm({ - kategori, - value, - }: { - kategori: "dataPelengkap" | "syaratDokumen"; - value: UpdateDataItem; - }) { - setFormSurat((prev) => ({ - ...prev, - [kategori]: upsertById(prev[kategori], { - id: value.id, - key: value.key, - value: value.value - }) - })); - } - - function updateArrayByKey( - list: UpdateDataItem[], - id: string, - value: any, - ): UpdateDataItem[] { - return list.map((item) => - item.id === id ? { ...item, value } : item, - ); - } - - function onChecking() { - if (formSurat.dataPelengkap.length == 0 && formSurat.syaratDokumen.length == 0) - return notification({ - title: "Peringatan", - message: "Tidak ada data yang diupdate", - type: "warning", - }); - const isFormKosong = Object.values(formSurat).some((value: UpdateDataItem[] | string) => { - if (Array.isArray(value)) { - return ( - value.some( - (item) => - typeof item.value === "string" && item.value.trim() === "", - ) - ); - } - - if (typeof value === "string") { - return value.trim() === ""; - } - - return false; + const response = await apiFetch.api.pelayanan["get-no-pengajuan"].post({ + phone: searchPengajuanPhone, + noPengajuan: searchPengajuan, }); - if (isFormKosong) { - return notification({ - title: "Gagal", - message: "Silahkan lengkapi form surat", - type: "error", - }); + if (response.status === 200) { + if (response.data?.success) { + navigate( + `/darmasaba/update-data-surat?pengajuan=${response.data.nomer}`, + ); + } else { + notification({ + title: "Peringatan", + message: response.data?.message || "Data pengajuan tidak valid", + type: "warning", + }); + } } else { - open() + notification({ + title: "Error", + message: "Pengajuan tidak ditemukan atau gagal memuat data", + type: "error", + }); } - } + } catch (error) { + console.error("Error searching:", error); + notification({ + title: "Error", + message: "Gagal mencari data pengajuan", + type: "error", + }); + } finally { + setSubmitLoading(false); + } + } - async function onSubmit() { - try { - setSubmitLoading(true); - // 🔥 CLONE state SEKALI - let finalFormSurat = structuredClone(formSurat); - - // 2️⃣ Upload satu per satu - for (const itemUpload of finalFormSurat.syaratDokumen) { - const updImg = await apiFetch.api.pengaduan.upload.post({ - file: itemUpload.value, - folder: "syarat-dokumen", - }); - - if (updImg.status === 200) { - // 🔥 UPDATE OBJECT LOKAL (BUKAN STATE) - finalFormSurat.syaratDokumen = updateArrayByKey( - finalFormSurat.syaratDokumen, - itemUpload.id, - updImg.data?.filename || "", - ); + return ( + + + + } - } + placeholder="PS-2025-000123" + onChange={(e) => { + setSearchPengajuan(e.target.value); + }} + /> + - // 3️⃣ SET STATE SEKALI (optional, untuk UI) - setFormSurat(finalFormSurat); + + + } + placeholder="08123456789" + type="number" + onChange={(e) => { + setSearchPengajuanPhone(e.target.value); + }} + /> + - // 4️⃣ SUBMIT KE API - const res = await apiFetch.api.pelayanan.update.post({ - id: dataPengajuan && ('id' in dataPengajuan) ? dataPengajuan.id : "", - dataPelengkap: finalFormSurat.dataPelengkap, - syaratDokumen: finalFormSurat.syaratDokumen, - }); - - if (res.status === 200) { - setSukses(true); - } else { - notification({ - title: "Gagal", - message: - "Pengajuan surat gagal dibuat, silahkan coba beberapa saat lagi", - type: "error", - }); - } - } catch (error) { - notification({ - title: "Gagal", - message: "Server Error", - type: "error", - }); - } finally { - setSubmitLoading(false); - } - } - - - return ( - <> - - - - - Apakah anda yakin ingin mengupdate pengajuan surat ini? - - - - - - - - { - sukses ? - { - navigate("/darmasaba/update-data-surat"); - }} - category="update" - /> - : - <> - { - (status != "ditolak" && status != "antrian") - && ⚠} /> - } - } - > - - {dataPelengkap.map((item: any, index: number) => ( - - { - item.type == "enum" - ? - } + data={item.options ?? []} + placeholder={item.name} + onChange={(e) => { + validationForm({ + kategori: "dataPelengkap", + value: { id: item.id, key: item.key, value: e }, + }); + }} + value={ + formSurat.dataPelengkap.find( + (n: any) => n.key == item.key, + )?.value || + dataPelengkap.find((n: any) => n.key == item.key)?.value + } + /> + ) : item.type == "date" ? ( + } + placeholder={item.name} + onChange={(e) => { + const formatted = e + ? dayjs(e).locale("id").format("DD MMMM YYYY") + : ""; + validationForm({ + kategori: "dataPelengkap", + value: { + id: item.id, + key: item.key, + value: formatted, + }, + }); + }} + value={ + formSurat.dataPelengkap.find( + (n: any) => n.key === item.key, + )?.value + ? parseTanggalID( + formSurat.dataPelengkap.find( + (n: any) => n.key === item.key, + )?.value, + ) + : parseTanggalID(item.value) + } + /> + ) : ( + } + placeholder={item.name} + onChange={(e) => + validationForm({ + kategori: "dataPelengkap", + value: { + id: item.id, + key: item.key, + value: e.target.value, + }, + }) + } + value={ + formSurat.dataPelengkap.find((n) => n.id === item.id) + ?.value ?? + dataPelengkap.find((n: any) => n.key == item.key)?.value + } + disabled={status != "ditolak" && status != "antrian"} + /> + )} + + ))} + + + + } + > + + {dataSyaratDokumen.map((item: any, index: number) => ( + + + validationForm({ + kategori: "syaratDokumen", + value: { id: item.id, key: item.key, value: file }, + }) + } + name={item.name} + disabled={status != "ditolak" && status != "antrian"} + /> + + ))} + + + + + + + + )} + + ); } diff --git a/src/pages/scr/dashboard/pelayanan-surat/detail_pelayanan_page.tsx b/src/pages/scr/dashboard/pelayanan-surat/detail_pelayanan_page.tsx index 2327872..c834e2f 100644 --- a/src/pages/scr/dashboard/pelayanan-surat/detail_pelayanan_page.tsx +++ b/src/pages/scr/dashboard/pelayanan-surat/detail_pelayanan_page.tsx @@ -437,7 +437,12 @@ function DetailDataHistori({ data }: { data: any }) { - + diff --git a/src/pages/scr/dashboard/pengaduan/detail_page.tsx b/src/pages/scr/dashboard/pengaduan/detail_page.tsx index 0e13810..2bfa9d8 100644 --- a/src/pages/scr/dashboard/pengaduan/detail_page.tsx +++ b/src/pages/scr/dashboard/pengaduan/detail_page.tsx @@ -38,6 +38,7 @@ import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; import useSwr from "swr"; + export default function DetailPengaduanPage() { const { search } = useLocation(); const query = new URLSearchParams(search); @@ -61,6 +62,7 @@ export default function DetailPengaduanPage() { { mutate(); }} @@ -78,9 +80,11 @@ export default function DetailPengaduanPage() { function DetailDataPengaduan({ data, + phone, onAction, }: { data: any | null; + phone?: string | null; onAction: () => void; }) { const [opened, { open, close }] = useDisclosure(false); @@ -122,6 +126,21 @@ function DetailDataPengaduan({ }); if (res?.status === 200) { + const resWA = await apiFetch.api["send-wa"].pengaduan.post({ + noPengaduan: data?.noPengaduan, + judulPengaduan: data?.title, + status: + cat == "tolak" + ? "ditolak" + : data.status == "antrian" + ? "diterima" + : data.status == "diterima" + ? "dikerjakan" + : "selesai", + alasan: keterangan, + tlp: String(phone), + }) + onAction(); close(); notification({ @@ -129,6 +148,28 @@ function DetailDataPengaduan({ message: "Success update pengaduan", type: "success", }); + + if (resWA?.status === 200) { + if (resWA.data?.success) { + notification({ + title: "Success", + message: "Success send message to warga", + type: "success", + }); + } else { + notification({ + title: "Failed", + message: "Failed send message to warga", + type: "error", + }); + } + } else { + notification({ + title: "Failed", + message: "Failed send message to warga", + type: "error", + }); + } } else { notification({ title: "Error", @@ -425,7 +466,12 @@ function DetailDataHistori({ data }: { data: any }) { - +
diff --git a/src/server/routes/pengaduan_route.ts b/src/server/routes/pengaduan_route.ts index fb60b14..8fb9ade 100644 --- a/src/server/routes/pengaduan_route.ts +++ b/src/server/routes/pengaduan_route.ts @@ -415,7 +415,7 @@ const PengaduanRoute = new Elysia({ const datafix = { pengaduan: {}, history: [], - warga: {}, + warga: null, } return datafix diff --git a/src/server/routes/send_wa_route.ts b/src/server/routes/send_wa_route.ts new file mode 100644 index 0000000..16b2322 --- /dev/null +++ b/src/server/routes/send_wa_route.ts @@ -0,0 +1,81 @@ +import Elysia, { t } from "elysia"; + +const SendWaRoute = new Elysia({ + prefix: "send-wa", + tags: ["send-wa"], +}) + + // --- KATEGORI PENGADUAN --- + .post("/pengaduan", async ({ body }) => { + const { noPengaduan, judulPengaduan, status, alasan, tlp } = body + + let text = "" + + if (status === "ditolak") { + text = `Pemberitahuan Aduan + +Aduan dengan Nomor Pengaduan: ${noPengaduan} +Judul Pengaduan: ${judulPengaduan} +Kami informasikan bahwa aduan tersebut tidak dapat ditindaklanjuti (ditolak). +Alasan penolakan:${alasan} + +Terima kasih atas pengertian Bapak/Ibu.` + } else if (status == "diterima") { + text = `Pemberitahuan Aduan + +Aduan dengan Nomor Pengaduan: ${noPengaduan} +Judul Pengaduan: ${judulPengaduan} +Telah kami terima dan akan segera diproses sesuai ketentuan yang berlaku. + +Terima kasih atas laporan Bapak/Ibu.` + } else if (status == "dikerjakan") { + text = `Pemberitahuan Aduan + +Aduan dengan Nomor Pengaduan: ${noPengaduan} +Judul Pengaduan: ${judulPengaduan} +Saat ini sedang dalam proses penanganan oleh petugas terkait. + +Mohon menunggu informasi selanjutnya.` + } else if (status == "selesai") { + text = `Pemberitahuan Aduan + +Aduan dengan Nomor Pengaduan: ${noPengaduan} +Judul Pengaduan: ${judulPengaduan} +Telah selesai ditindaklanjuti. + +Terima kasih atas partisipasi dan kepercayaan Bapak/Ibu.` + } + + const textFix = encodeURIComponent(text) + + const res = await fetch( + `https://cld-dkr-prod-wajs-server.wibudev.com/api/wa/code?nom=${tlp}&text=${textFix}`, + { + cache: "no-cache", + headers: { + Authorization: `Bearer ${process.env.WA_SERVER_TOKEN}`, + }, + } + ); + + if (res.status !== 200) + return { success: false, message: "Nomor Whatsapp Tidak Aktif" } + + + return { success: true, message: 'Pemberitahuan berhasil dikirim ke warga' } + }, { + body: t.Object({ + noPengaduan: t.String({ minLength: 1, error: "nomer pengaduan harus diisi" }), + judulPengaduan: t.String({ minLength: 1, error: "judul pengaduan harus diisi" }), + status: t.String({ minLength: 1, error: "status harus diisi" }), + alasan: t.String({ optional: true }), + tlp: t.String({ minLength: 1, error: "nomor telepon harus diisi" }), + }), + detail: { + summary: "Send pemberitahuan pengaduan lewat WA", + description: `tool untuk send pemberitahuan pengaduan lewat WA` + } + }) + ; + +export default SendWaRoute