amalia/12-nov-25 #20
@@ -1,7 +1,7 @@
|
|||||||
import "@mantine/core/styles.css";
|
import "@mantine/core/styles.css";
|
||||||
import "@mantine/notifications/styles.css";
|
|
||||||
import "@mantine/dates/styles.css";
|
import "@mantine/dates/styles.css";
|
||||||
import { Notifications } from "@mantine/notifications";
|
import { Notifications } from "@mantine/notifications";
|
||||||
|
import "@mantine/notifications/styles.css";
|
||||||
|
|
||||||
import { MantineProvider } from "@mantine/core";
|
import { MantineProvider } from "@mantine/core";
|
||||||
import AppRoutes from "./AppRoutes";
|
import AppRoutes from "./AppRoutes";
|
||||||
|
|||||||
157
src/components/DesaSetting.tsx
Normal file
157
src/components/DesaSetting.tsx
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import apiFetch from "@/lib/apiFetch";
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
Flex,
|
||||||
|
Group,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
Stack,
|
||||||
|
Table,
|
||||||
|
Title,
|
||||||
|
Tooltip
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
|
||||||
|
import { IconEdit } from "@tabler/icons-react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import notification from "./notificationGlobal";
|
||||||
|
|
||||||
|
export default function DesaSetting() {
|
||||||
|
const [btnDisable, setBtnDisable] = useState(false);
|
||||||
|
const [btnLoading, setBtnLoading] = useState(false);
|
||||||
|
const [opened, { open, close }] = useDisclosure(false);
|
||||||
|
const { data, mutate, isLoading } = useSWR("/", () =>
|
||||||
|
apiFetch.api["configuration-desa"].list.get(),
|
||||||
|
);
|
||||||
|
const list = data?.data || [];
|
||||||
|
const [dataEdit, setDataEdit] = useState({
|
||||||
|
id: "",
|
||||||
|
value: "",
|
||||||
|
name: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
mutate();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function handleEdit() {
|
||||||
|
try {
|
||||||
|
setBtnLoading(true);
|
||||||
|
const res = await apiFetch.api["configuration-desa"].edit.post(dataEdit);
|
||||||
|
if (res.status === 200) {
|
||||||
|
mutate();
|
||||||
|
close();
|
||||||
|
notification({
|
||||||
|
title: "Success",
|
||||||
|
message: "Your settings have been saved",
|
||||||
|
type: "success",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to edit configuration",
|
||||||
|
type: "error",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
notification({
|
||||||
|
title: "Error",
|
||||||
|
message: "Failed to edit configuration",
|
||||||
|
type: "error",
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setBtnLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseEdit({ data }: { data: { id: string, value: string, name: string } }) {
|
||||||
|
setDataEdit(data);
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onValidation({ kat, value }: { kat: 'value', value: string }) {
|
||||||
|
if (value.length < 1) {
|
||||||
|
setBtnDisable(true);
|
||||||
|
} else {
|
||||||
|
setBtnDisable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kat === 'value') {
|
||||||
|
setDataEdit({ ...dataEdit, value: value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
if (dataEdit.value.length > 0) {
|
||||||
|
setBtnDisable(false);
|
||||||
|
}
|
||||||
|
}, [dataEdit.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
opened={opened}
|
||||||
|
onClose={close}
|
||||||
|
title={"Edit"}
|
||||||
|
centered
|
||||||
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
||||||
|
>
|
||||||
|
<Stack gap="ld">
|
||||||
|
<Input.Wrapper label={dataEdit.name}>
|
||||||
|
<Input value={dataEdit.value} onChange={(e) => onValidation({ kat: 'value', value: e.target.value })} />
|
||||||
|
</Input.Wrapper>
|
||||||
|
<Group justify="center" grow>
|
||||||
|
<Button variant="light" onClick={close}>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button variant="filled" onClick={handleEdit} disabled={btnDisable} loading={btnLoading}>
|
||||||
|
Simpan
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
<Stack gap={"md"}>
|
||||||
|
<Flex align="center" justify="space-between">
|
||||||
|
<Title order={4} c="gray.2">
|
||||||
|
Pengaturan Desa
|
||||||
|
</Title>
|
||||||
|
</Flex>
|
||||||
|
<Divider my={0} />
|
||||||
|
<Stack gap={"md"}>
|
||||||
|
<Table highlightOnHover>
|
||||||
|
<Table.Thead>
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Th>Nama</Table.Th>
|
||||||
|
<Table.Th>Value</Table.Th>
|
||||||
|
<Table.Th>Aksi</Table.Th>
|
||||||
|
</Table.Tr>
|
||||||
|
</Table.Thead>
|
||||||
|
<Table.Tbody>
|
||||||
|
{list?.map((v: any) => (
|
||||||
|
<Table.Tr key={v.id}>
|
||||||
|
<Table.Td>{v.name}</Table.Td>
|
||||||
|
<Table.Td>{v.value}</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Tooltip label="Edit Setting">
|
||||||
|
<ActionIcon
|
||||||
|
variant="light"
|
||||||
|
size="sm"
|
||||||
|
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
|
||||||
|
onClick={() => chooseEdit({ data: v })}
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
))}
|
||||||
|
</Table.Tbody>
|
||||||
|
</Table>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
38
src/components/notificationGlobal.ts
Normal file
38
src/components/notificationGlobal.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { showNotification } from "@mantine/notifications";
|
||||||
|
|
||||||
|
export default function notification({ title, message, type }: { title: string, message: string, type: "success" | "error" | "warning" | "info" }) {
|
||||||
|
switch (type) {
|
||||||
|
case "success":
|
||||||
|
return showNotification({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
color: "green",
|
||||||
|
autoClose: 3000,
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
return showNotification({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
color: "red",
|
||||||
|
autoClose: 3000,
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
return showNotification({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
color: "orange",
|
||||||
|
autoClose: 3000,
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case "info":
|
||||||
|
return showNotification({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
color: "blue",
|
||||||
|
autoClose: 3000,
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import cors from "@elysiajs/cors";
|
||||||
import Swagger from "@elysiajs/swagger";
|
import Swagger from "@elysiajs/swagger";
|
||||||
import Elysia from "elysia";
|
import Elysia from "elysia";
|
||||||
import html from "./index.html";
|
import html from "./index.html";
|
||||||
@@ -14,7 +15,7 @@ import PelayananRoute from "./server/routes/pelayanan_surat_route";
|
|||||||
import PengaduanRoute from "./server/routes/pengaduan_route";
|
import PengaduanRoute from "./server/routes/pengaduan_route";
|
||||||
import TestRoute from "./server/routes/test";
|
import TestRoute from "./server/routes/test";
|
||||||
import UserRoute from "./server/routes/user_route";
|
import UserRoute from "./server/routes/user_route";
|
||||||
import cors from "@elysiajs/cors";
|
import ConfigurationDesaRoute from "./server/routes/configuration_desa_route";
|
||||||
|
|
||||||
const Docs = new Elysia({
|
const Docs = new Elysia({
|
||||||
tags: ["docs"],
|
tags: ["docs"],
|
||||||
@@ -30,6 +31,7 @@ const Api = new Elysia({
|
|||||||
})
|
})
|
||||||
.use(PengaduanRoute)
|
.use(PengaduanRoute)
|
||||||
.use(PelayananRoute)
|
.use(PelayananRoute)
|
||||||
|
.use(ConfigurationDesaRoute)
|
||||||
.use(TestRoute)
|
.use(TestRoute)
|
||||||
.use(apiAuth)
|
.use(apiAuth)
|
||||||
.use(ApiKeyRoute)
|
.use(ApiKeyRoute)
|
||||||
|
|||||||
@@ -1,5 +1,24 @@
|
|||||||
import { Button, Card, Container, Divider, Flex, Grid, Group, Input, NavLink, Stack, Table, Title } from "@mantine/core";
|
import DesaSetting from "@/components/DesaSetting";
|
||||||
import { IconCircleOff, IconGauge, IconHome2 } from "@tabler/icons-react";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Container,
|
||||||
|
Divider,
|
||||||
|
Flex,
|
||||||
|
Grid,
|
||||||
|
Group,
|
||||||
|
Input,
|
||||||
|
NavLink,
|
||||||
|
Stack,
|
||||||
|
Table,
|
||||||
|
Title,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import {
|
||||||
|
IconBuildingBank,
|
||||||
|
IconCategory2,
|
||||||
|
IconMailSpark,
|
||||||
|
IconUserCog,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
export default function DetailSettingPage() {
|
export default function DetailSettingPage() {
|
||||||
@@ -25,25 +44,25 @@ export default function DetailSettingPage() {
|
|||||||
<NavLink
|
<NavLink
|
||||||
href={`?type=profile`}
|
href={`?type=profile`}
|
||||||
label="Profile"
|
label="Profile"
|
||||||
leftSection={<IconHome2 size={16} stroke={1.5} />}
|
leftSection={<IconUserCog size={16} stroke={1.5} />}
|
||||||
active={type === "profile" || !type}
|
active={type === "profile" || !type}
|
||||||
/>
|
/>
|
||||||
<NavLink
|
<NavLink
|
||||||
href={`?type=cat-pengaduan`}
|
href={`?type=cat-pengaduan`}
|
||||||
label="Kategori Pengaduan"
|
label="Kategori Pengaduan"
|
||||||
leftSection={<IconGauge size={16} stroke={1.5} />}
|
leftSection={<IconCategory2 size={16} stroke={1.5} />}
|
||||||
active={type === "cat-pengaduan"}
|
active={type === "cat-pengaduan"}
|
||||||
/>
|
/>
|
||||||
<NavLink
|
<NavLink
|
||||||
href={`?type=cat-pelayanan`}
|
href={`?type=cat-pelayanan`}
|
||||||
label="Kategori Pelayanan Surat"
|
label="Kategori Pelayanan Surat"
|
||||||
leftSection={<IconCircleOff size={16} stroke={1.5} />}
|
leftSection={<IconMailSpark size={16} stroke={1.5} />}
|
||||||
active={type === "cat-pelayanan"}
|
active={type === "cat-pelayanan"}
|
||||||
/>
|
/>
|
||||||
<NavLink
|
<NavLink
|
||||||
href={`?type=desa`}
|
href={`?type=desa`}
|
||||||
label="Desa"
|
label="Desa"
|
||||||
leftSection={<IconCircleOff size={16} stroke={1.5} />}
|
leftSection={<IconBuildingBank size={16} stroke={1.5} />}
|
||||||
active={type === "desa"}
|
active={type === "desa"}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -60,13 +79,15 @@ export default function DetailSettingPage() {
|
|||||||
boxShadow: "0 0 20px rgba(0,255,200,0.08)",
|
boxShadow: "0 0 20px rgba(0,255,200,0.08)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{type === "cat-pengaduan"
|
{type === "cat-pengaduan" ? (
|
||||||
? <KategoriPengaduanPage />
|
<KategoriPengaduanPage />
|
||||||
: type === "cat-pelayanan"
|
) : type === "cat-pelayanan" ? (
|
||||||
? <KategoriPengaduanPage />
|
<KategoriPengaduanPage />
|
||||||
: type === "desa"
|
) : type === "desa" ? (
|
||||||
? <KategoriPengaduanPage />
|
<DesaSetting />
|
||||||
: <ProfilePage />}
|
) : (
|
||||||
|
<ProfilePage />
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -103,7 +124,7 @@ function ProfilePage() {
|
|||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function KategoriPengaduanPage() {
|
function KategoriPengaduanPage() {
|
||||||
@@ -146,5 +167,5 @@ function KategoriPengaduanPage() {
|
|||||||
</Table>
|
</Table>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ export async function uploadFile(config: Config, file: File): Promise<string> {
|
|||||||
if (!res.ok) throw new Error(`Upload failed: ${text}`);
|
if (!res.ok) throw new Error(`Upload failed: ${text}`);
|
||||||
return `✅ Uploaded ${file.name} successfully`;
|
return `✅ Uploaded ${file.name} successfully`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadFileBase64(config: Config, base64File: { name: string; data: string; }): Promise<string> {
|
export async function uploadFileBase64(config: Config, base64File: { name: string; data: string; }): Promise<string> {
|
||||||
const remoteName = path.basename(base64File.name);
|
const remoteName = path.basename(base64File.name);
|
||||||
|
|
||||||
@@ -194,6 +195,38 @@ export async function uploadFileBase64(config: Config, base64File: { name: strin
|
|||||||
return `✅ Uploaded ${base64File.name} successfully`;
|
return `✅ Uploaded ${base64File.name} successfully`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function uploadFileToFolder(config: Config, base64File: { name: string; data: string; }, folder: 'syarat-dokumen' | 'pengaduan'): Promise<string> {
|
||||||
|
const remoteName = path.basename(base64File.name);
|
||||||
|
|
||||||
|
// 1. Dapatkan upload link (pakai Authorization)
|
||||||
|
const uploadUrlResponse = await fetchWithAuth(
|
||||||
|
config,
|
||||||
|
`${config.URL}/${config.REPO}/upload-link/`
|
||||||
|
);
|
||||||
|
const uploadUrl = (await uploadUrlResponse.text()).replace(/"/g, "");
|
||||||
|
|
||||||
|
// 2. Konversi base64 ke Blob
|
||||||
|
const binary = Buffer.from(base64File.data, "base64");
|
||||||
|
const blob = new Blob([binary]);
|
||||||
|
|
||||||
|
// 3. Siapkan form-data
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("parent_dir", "/");
|
||||||
|
formData.append("relative_path", folder); // tanpa slash di akhir
|
||||||
|
formData.append("file", blob, remoteName);
|
||||||
|
|
||||||
|
// 4. Upload file TANPA Authorization header, token di query param
|
||||||
|
const res = await fetch(`${uploadUrl}?token=${config.TOKEN}`, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
const text = await res.text();
|
||||||
|
|
||||||
|
if (!res.ok) throw new Error(`Upload failed: ${text}`);
|
||||||
|
return `✅ Uploaded ${base64File.name} successfully`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
48
src/server/routes/configuration_desa_route.ts
Normal file
48
src/server/routes/configuration_desa_route.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import Elysia, { t } from "elysia";
|
||||||
|
import { prisma } from "../lib/prisma";
|
||||||
|
|
||||||
|
const ConfigurationDesaRoute = new Elysia({
|
||||||
|
prefix: "configuration-desa",
|
||||||
|
tags: ["configuration-desa"],
|
||||||
|
})
|
||||||
|
|
||||||
|
.get("/list", async () => {
|
||||||
|
const data = await prisma.configuration.findMany({
|
||||||
|
orderBy: {
|
||||||
|
name: "asc"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}, {
|
||||||
|
detail: {
|
||||||
|
summary: "List Konfigurasi",
|
||||||
|
description: `tool untuk mendapatkan list konfigurasi`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.post("/edit", async ({ body }) => {
|
||||||
|
const { id, value } = body
|
||||||
|
|
||||||
|
await prisma.configuration.update({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { success: true, message: 'konfigurasi sudah diperbarui' }
|
||||||
|
}, {
|
||||||
|
body: t.Object({
|
||||||
|
id: t.String({ minLength: 1, error: "id harus diisi" }),
|
||||||
|
value: t.String({ minLength: 1, error: "value harus diisi" }),
|
||||||
|
}),
|
||||||
|
detail: {
|
||||||
|
summary: "edit konfigurasi desa",
|
||||||
|
description: `tool untuk edit konfigurasi desa`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
export default ConfigurationDesaRoute
|
||||||
@@ -6,7 +6,7 @@ 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 { catFile, defaultConfigSF, testConnection, uploadFile, uploadFileBase64, uploadFileToFolder } from "../lib/seafile"
|
||||||
|
|
||||||
const PengaduanRoute = new Elysia({
|
const PengaduanRoute = new Elysia({
|
||||||
prefix: "pengaduan",
|
prefix: "pengaduan",
|
||||||
@@ -100,7 +100,8 @@ const PengaduanRoute = new Elysia({
|
|||||||
|
|
||||||
// --- PENGADUAN ---
|
// --- PENGADUAN ---
|
||||||
.post("/create", async ({ body }) => {
|
.post("/create", async ({ body }) => {
|
||||||
const { title, detail, location, image, idCategory, idWarga, phone } = body
|
const { title, detail, location, imageData, imageMime, idCategory, idWarga, phone } = body
|
||||||
|
let imageFix = null
|
||||||
const noPengaduan = await generateNoPengaduan()
|
const noPengaduan = await generateNoPengaduan()
|
||||||
let idCategoryFix = idCategory
|
let idCategoryFix = idCategory
|
||||||
let idWargaFix = idWarga
|
let idWargaFix = idWarga
|
||||||
@@ -110,6 +111,12 @@ const PengaduanRoute = new Elysia({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!imageData && !imageMime) {
|
||||||
|
const ext = mimeToExtension(imageMime)
|
||||||
|
imageFix = `${uuidv4()}.${ext}`
|
||||||
|
await uploadFileToFolder(defaultConfigSF, { name: imageFix, data: imageData }, "pengaduan")
|
||||||
|
}
|
||||||
|
|
||||||
if (!category) {
|
if (!category) {
|
||||||
const cariCategory = await prisma.categoryPengaduan.findFirst({
|
const cariCategory = await prisma.categoryPengaduan.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@@ -163,7 +170,7 @@ const PengaduanRoute = new Elysia({
|
|||||||
idCategory: idCategoryFix,
|
idCategory: idCategoryFix,
|
||||||
idWarga: idWargaFix,
|
idWarga: idWargaFix,
|
||||||
location,
|
location,
|
||||||
image,
|
image: imageFix,
|
||||||
noPengaduan,
|
noPengaduan,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
@@ -172,7 +179,7 @@ const PengaduanRoute = new Elysia({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!pengaduan.id) {
|
if (!pengaduan.id) {
|
||||||
throw new Error("gagal membuat pengaduan")
|
return { success: false, message: 'gagal membuat pengaduan' }
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.historyPengaduan.create({
|
await prisma.historyPengaduan.create({
|
||||||
@@ -188,7 +195,8 @@ const PengaduanRoute = new Elysia({
|
|||||||
title: t.String({ minLength: 1, error: "title harus diisi" }),
|
title: t.String({ minLength: 1, error: "title harus diisi" }),
|
||||||
detail: t.String({ minLength: 1, error: "detail harus diisi" }),
|
detail: t.String({ minLength: 1, error: "detail harus diisi" }),
|
||||||
location: t.String({ minLength: 1, error: "location harus diisi" }),
|
location: t.String({ minLength: 1, error: "location harus diisi" }),
|
||||||
image: t.Any(),
|
imageData: t.String({ optional: true, description: "base64 encoded image data" }),
|
||||||
|
imageMime: t.String({ optional: true, description: "mime type of image" }),
|
||||||
idCategory: t.String({ minLength: 1, error: "idCategory harus diisi" }),
|
idCategory: t.String({ minLength: 1, error: "idCategory harus diisi" }),
|
||||||
idWarga: t.String({ minLength: 1, error: "idWarga harus diisi" }),
|
idWarga: t.String({ minLength: 1, error: "idWarga harus diisi" }),
|
||||||
phone: t.String({ minLength: 1, error: "phone harus diisi" }),
|
phone: t.String({ minLength: 1, error: "phone harus diisi" }),
|
||||||
@@ -622,7 +630,7 @@ const PengaduanRoute = new Elysia({
|
|||||||
const { fileName } = query
|
const { fileName } = query
|
||||||
|
|
||||||
const connect = await testConnection(defaultConfigSF)
|
const connect = await testConnection(defaultConfigSF)
|
||||||
console.log({connect})
|
console.log({ connect })
|
||||||
|
|
||||||
const hasil = await catFile(defaultConfigSF, fileName)
|
const hasil = await catFile(defaultConfigSF, fileName)
|
||||||
console.log('hasilnya', hasil)
|
console.log('hasilnya', hasil)
|
||||||
|
|||||||
Reference in New Issue
Block a user