upd: dashboard admin

Deskripsi
- edit upload ttd setting desa

No Issues
This commit is contained in:
2025-11-20 16:09:36 +08:00
parent 03715b7c98
commit d0ff675950
5 changed files with 96 additions and 25 deletions

View File

@@ -1,8 +1,10 @@
import apiFetch from "@/lib/apiFetch";
import {
ActionIcon,
Anchor,
Button,
Divider,
FileInput,
Flex,
Group,
Input,
@@ -10,7 +12,7 @@ import {
Stack,
Table,
Title,
Tooltip,
Tooltip
} from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconEdit } from "@tabler/icons-react";
@@ -22,6 +24,7 @@ export default function DesaSetting() {
const [btnDisable, setBtnDisable] = useState(false);
const [btnLoading, setBtnLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(false);
const [img, setImg] = useState<any>()
const { data, mutate, isLoading } = useSWR("/", () =>
apiFetch.api["configuration-desa"].list.get(),
);
@@ -39,7 +42,31 @@ export default function DesaSetting() {
async function handleEdit() {
try {
setBtnLoading(true);
const res = await apiFetch.api["configuration-desa"].edit.post(dataEdit);
let finalData = { ...dataEdit }; // ← buffer data terbaru
if (dataEdit.name === "TTD") {
const resImg = await apiFetch.api.pengaduan.upload.post({ file: img });
if (resImg.status === 200) {
finalData = {
...finalData,
value: resImg.data?.filename || ""
};
setDataEdit(finalData); // update state
} else {
return notification({
title: "Error",
message: "Failed to upload image",
type: "error",
});
}
}
const res = await apiFetch.api["configuration-desa"].edit.post(finalData);
if (res.status === 200) {
mutate();
close();
@@ -67,6 +94,7 @@ export default function DesaSetting() {
}
}
function chooseEdit({
data,
}: {
@@ -100,18 +128,35 @@ export default function DesaSetting() {
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>
{
dataEdit.name == "TTD"
?
(
<Input.Wrapper label={dataEdit.name}>
<FileInput
clearable
placeholder="Upload TTD"
accept="image/*"
onChange={(e) => { setImg(e) }}
/>
</Input.Wrapper>
)
:
(
<Input.Wrapper label={dataEdit.name}>
<Input
value={dataEdit.value}
onChange={(e) =>
onValidation({ kat: "value", value: e.target.value })
}
/>
</Input.Wrapper>
)
}
<Group justify="center" grow>
<Button variant="light" onClick={close}>
Batal
@@ -119,7 +164,7 @@ export default function DesaSetting() {
<Button
variant="filled"
onClick={handleEdit}
disabled={btnDisable}
disabled={btnDisable || (dataEdit.name == "TTD" && !img)}
loading={btnLoading}
>
Simpan
@@ -127,6 +172,8 @@ export default function DesaSetting() {
</Group>
</Stack>
</Modal>
<Stack gap={"md"}>
<Flex align="center" justify="space-between">
<Title order={4} c="gray.2">
@@ -147,7 +194,17 @@ export default function DesaSetting() {
{list?.map((v: any) => (
<Table.Tr key={v.id}>
<Table.Td>{v.name}</Table.Td>
<Table.Td>{v.value}</Table.Td>
<Table.Td>
{
v.name == "TTD"
?
<Anchor href="https://mantine.dev/" target="_blank" underline="always">
Lihat
</Anchor>
:
v.value
}
</Table.Td>
<Table.Td>
<Tooltip label="Edit Setting">
<ActionIcon

View File

@@ -4,16 +4,10 @@ import KategoriPengaduan from "@/components/KategoriPengaduan";
import ProfileUser from "@/components/ProfileUser";
import UserSetting from "@/components/UserSetting";
import {
Button,
Card,
Container,
Divider,
Flex,
Grid,
NavLink,
Stack,
Table,
Title,
NavLink
} from "@mantine/core";
import {
IconBuildingBank,

View File

@@ -0,0 +1,12 @@
import { v4 as uuidv4 } from "uuid";
import { mimeToExtension } from "./mimetypeToExtension";
export function renameFile({ oldFile, newName }: { oldFile: File; newName: string }) {
const ext = mimeToExtension(oldFile.type)
const nameFix = newName == 'random' ? `${uuidv4()}.${ext}` : newName
return new File([oldFile], nameFix, {
type: oldFile.type,
lastModified: oldFile.lastModified,
});
}

View File

@@ -159,7 +159,7 @@ export async function uploadFile(config: Config, file: File): Promise<string> {
const text = await res.text();
if (!res.ok) throw new Error(`Upload failed: ${text}`);
if (!res.ok) return 'gagal'
return `✅ Uploaded ${file.name} successfully`;
}

View File

@@ -6,6 +6,7 @@ import { mimeToExtension } from "../lib/mimetypeToExtension"
import { generateNoPengaduan } from "../lib/no-pengaduan"
import { normalizePhoneNumber } from "../lib/normalizePhone"
import { prisma } from "../lib/prisma"
import { renameFile } from "../lib/rename-file"
import { catFile, defaultConfigSF, testConnection, uploadFile, uploadFileBase64 } from "../lib/seafile"
const PengaduanRoute = new Elysia({
@@ -523,20 +524,27 @@ Respon:
return { success: false, message: "File tidak ditemukan" };
}
// Rename file
const renamedFile = renameFile({ oldFile: file, newName: 'random' });
// Upload ke Seafile (pastikan uploadFile menerima Blob atau ArrayBuffer)
// const buffer = await file.arrayBuffer();
const result = await uploadFile(defaultConfigSF, file);
const result = await uploadFile(defaultConfigSF, renamedFile);
if (result == 'gagal') {
return { success: false, message: "Upload gagal" };
}
return {
success: true,
message: "Upload berhasil",
filename: file.name,
size: file.size,
filename: renamedFile.name,
size: renamedFile.size,
seafileResult: result
};
}, {
body: t.Object({
file: t.File({ format: "binary" })
file: t.Any()
}),
detail: {
summary: "Upload File",