261 lines
6.9 KiB
TypeScript
261 lines
6.9 KiB
TypeScript
import apiFetch from "@/lib/apiFetch";
|
|
import {
|
|
ActionIcon,
|
|
Anchor,
|
|
Button,
|
|
Divider,
|
|
FileInput,
|
|
Flex,
|
|
Group,
|
|
Input,
|
|
Modal,
|
|
Stack,
|
|
Table,
|
|
Title,
|
|
Tooltip,
|
|
} from "@mantine/core";
|
|
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
|
|
import { IconEdit } from "@tabler/icons-react";
|
|
import type { JsonValue } from "generated/prisma/runtime/library";
|
|
import _ from "lodash";
|
|
import { useState } from "react";
|
|
import useSWR from "swr";
|
|
import ModalFile from "./ModalFile";
|
|
import notification from "./notificationGlobal";
|
|
|
|
export default function DesaSetting({
|
|
permissions,
|
|
}: {
|
|
permissions: JsonValue[];
|
|
}) {
|
|
const [btnDisable, setBtnDisable] = useState(false);
|
|
const [btnLoading, setBtnLoading] = useState(false);
|
|
const [opened, { open, close }] = useDisclosure(false);
|
|
const [img, setImg] = useState<any>();
|
|
const [openedPreview, setOpenedPreview] = useState(false);
|
|
const [viewImg, setViewImg] = useState("");
|
|
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);
|
|
|
|
let finalData = { ...dataEdit }; // ← buffer data terbaru
|
|
|
|
if (dataEdit.name === "TTD") {
|
|
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) {
|
|
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();
|
|
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.error(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"}
|
|
overlayProps={{ backgroundOpacity: 0.55, blur: 3 }}
|
|
>
|
|
<Stack gap="ld">
|
|
{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
|
|
</Button>
|
|
<Button
|
|
variant="filled"
|
|
onClick={handleEdit}
|
|
disabled={btnDisable || (dataEdit.name == "TTD" && !img)}
|
|
loading={btnLoading}
|
|
>
|
|
Simpan
|
|
</Button>
|
|
</Group>
|
|
</Stack>
|
|
</Modal>
|
|
|
|
<ModalFile
|
|
open={openedPreview && !_.isEmpty(viewImg)}
|
|
onClose={() => setOpenedPreview(false)}
|
|
folder="lainnya"
|
|
fileName={viewImg}
|
|
/>
|
|
|
|
<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.length > 0 && list?.map((v: any) => (
|
|
<Table.Tr key={v.id}>
|
|
<Table.Td>{v.name}</Table.Td>
|
|
<Table.Td>
|
|
{v.name == "TTD" ? (
|
|
v.value ? (
|
|
<Anchor
|
|
href="#"
|
|
onClick={() => {
|
|
setViewImg(v.value);
|
|
setOpenedPreview(true);
|
|
}}
|
|
underline="always"
|
|
>
|
|
Lihat
|
|
</Anchor>
|
|
) : (
|
|
"-"
|
|
)
|
|
) : (
|
|
v.value
|
|
)}
|
|
</Table.Td>
|
|
<Table.Td>
|
|
<Tooltip
|
|
label={
|
|
permissions.includes("setting.desa.edit")
|
|
? "Edit Setting"
|
|
: "Edit Setting - Anda tidak memiliki akses"
|
|
}
|
|
>
|
|
<ActionIcon
|
|
variant="light"
|
|
size="sm"
|
|
style={{ boxShadow: "0 0 8px rgba(0,255,200,0.2)" }}
|
|
onClick={() => chooseEdit({ data: v })}
|
|
disabled={!permissions.includes("setting.desa.edit")}
|
|
>
|
|
<IconEdit size={20} />
|
|
</ActionIcon>
|
|
</Tooltip>
|
|
</Table.Td>
|
|
</Table.Tr>
|
|
))}
|
|
</Table.Tbody>
|
|
</Table>
|
|
</Stack>
|
|
</Stack>
|
|
</>
|
|
);
|
|
}
|