Profile PPID, Visi Misi, dan Dasar Hukum

This commit is contained in:
2025-05-13 17:54:23 +08:00
parent e5889ca903
commit 2844132ea0
10 changed files with 273 additions and 56 deletions

View File

@@ -0,0 +1,82 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
judul: z.string().min(3, "Judul minimal 3 karakter"),
content: z.string().min(3, "Content minimal 3 karakter"),
});
type DasarHukumForm = Prisma.DasarHukumPPIDGetPayload<{
select: {
id: true;
judul: true;
content: true;
};
}>;
const stateDasarHukumPPID = proxy({
findById: {
data: null as DasarHukumForm | null,
loading: false,
initialize() {
stateDasarHukumPPID.findById.data = {
id: '',
judul: '',
content: '',
} as DasarHukumForm;
},
async load(id: string) {
try {
stateDasarHukumPPID.findById.loading = true;
const res = await ApiFetch.api.ppid.dasarhukumppid["find-by-id"].get({
query: { id },
});
if (res.status === 200) {
stateDasarHukumPPID.findById.data = res.data?.data ?? null;
} else {
toast.error("Gagal mengambil data dasar hukum");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat mengambil data dasar hukum");
} finally {
stateDasarHukumPPID.findById.loading = false;
}
},
},
update: {
loading: false,
async save(data: DasarHukumForm) {
const cek = templateForm.safeParse(data);
if (!cek.success) {
const errors = cek.error.issues
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
.join(", ");
toast.error(`Form tidak valid: ${errors}`);
return;
}
try {
stateDasarHukumPPID.update.loading = true;
const res = await ApiFetch.api.ppid.dasarhukumppid["update"].post(data);
if (res.status === 200) {
toast.success("Data dasar hukum berhasil diubah");
await stateDasarHukumPPID.findById.load(data.id);
} else {
toast.error("Gagal mengubah data dasar hukum");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat mengubah data dasar hukum");
} finally {
stateDasarHukumPPID.update.loading = false;
}
},
},
});
export default stateDasarHukumPPID;

View File

@@ -0,0 +1,64 @@
'use client'
import { Box, Button, Group, Stack, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import stateDasarHukumPPID from '../../../_state/ppid/dasar_hukum/dasarHukum';
import dynamic from 'next/dynamic';
import colors from '@/con/colors';
import { useShallowEffect } from '@mantine/hooks';
const PPIDTextEditor = dynamic(() => import('../../_com/PPIDTextEditor').then(mod => mod.PPIDTextEditor), {
ssr: false,
});
function CreateDasarHukum() {
const dasarHukumState = useProxy(stateDasarHukumPPID)
useShallowEffect(() => {
if (!dasarHukumState.findById.data) {
dasarHukumState.findById.load(""); // biar masuk ke `findFirst` route kamu
}
}, []);
const submit = () => {
if (dasarHukumState.findById.data?.judul && dasarHukumState.findById.data?.content) {
dasarHukumState.update.save(dasarHukumState.findById.data)
}
}
return (
<Box>
<Stack gap={"xs"}>
<Text fw={"bold"}>Judul</Text>
<PPIDTextEditor
showSubmit={false}
key={`judul-${dasarHukumState.findById.data?.id ?? 'new'}`}
onChange={(val) => {
if (dasarHukumState.findById.data) {
dasarHukumState.findById.data.judul = val
}
}}
initialContent={dasarHukumState.findById.data?.judul ?? ''}
/>
<Text fw={"bold"}>Content</Text>
<PPIDTextEditor
showSubmit={false}
key={`content-${dasarHukumState.findById.data?.id ?? 'baru'}`}
onChange={(val) => {
if (dasarHukumState.findById.data) {
dasarHukumState.findById.data.content = val
}
}}
initialContent={dasarHukumState.findById.data?.content ?? ''}
/>
<Group>
<Button
bg={colors['blue-button']}
onClick={submit}
loading={dasarHukumState.update.loading}
>
Submit
</Button>
</Group>
</Stack>
</Box>
);
}
export default CreateDasarHukum;

View File

@@ -0,0 +1,40 @@
'use client'
import React from 'react';
import { useProxy } from 'valtio/utils';
import stateDasarHukumPPID from '../../../_state/ppid/dasar_hukum/dasarHukum';
import { useShallowEffect } from '@mantine/hooks';
import { Box, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
import colors from '@/con/colors';
function ListDataDasarHukum() {
const dataList = useProxy(stateDasarHukumPPID)
useShallowEffect(() => {
dataList.findById.load("")
}, [])
if(!dataList.findById.data) return <Stack>
{Array.from({length: 10}).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
const dataArray = Array.isArray(dataList.findById.data)
? dataList.findById.data
: [dataList.findById.data];
return (
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Stack gap={"xs"}>
<Title order={3}>List Dasar Hukum PPID</Title>
{dataArray.map((item) => (
<Box key={item.id}>
<Text fw={"bold"}>Judul</Text>
<Text dangerouslySetInnerHTML={{ __html: item.judul }}></Text>
<Text fw={"bold"}>Content</Text>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
</Stack>
</Paper>
);
}
export default ListDataDasarHukum;

View File

@@ -1,11 +1,24 @@
import React from 'react';
'use client'
import colors from '@/con/colors';
import { Box, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
import CreateDasarHukum from './create/page';
import ListDataDasarHukum from './listData/page';
function Page() {
return (
<div>
dasar-hukum
</div>
);
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Title order={3}>Dasar Hukum PPID</Title>
<CreateDasarHukum/>
</Paper>
</Box>
<ListDataDasarHukum/>
</SimpleGrid>
</Stack>
)
}
export default Page;

View File

@@ -1,47 +1,38 @@
'use client'
import { Box, Group, Text } from '@mantine/core';
import React from 'react';
import React, { useState } from 'react';
import { useProxy } from 'valtio/utils';
import stateProfilePPID from '../../../_state/ppid/profile_ppid/profile_PPID';
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
import { Dropzone, MIME_TYPES } from '@mantine/dropzone';
import { IconUpload, IconX, IconPhoto } from '@tabler/icons-react';
import ApiFetch from '@/lib/api-fetch';
function Biodata() {
const biodataState = useProxy(stateProfilePPID)
const handleUpload = async (file: File) => {
const formData = new FormData();
formData.append("file", file);
formData.append("id", biodataState.findById.data?.id ?? ''); // pastikan ID-nya sesuai
const res = await fetch("/api/ppid/profileppid/edit-img", {
method: "POST",
body: formData,
});
const result = await res.json();
if (result.success && biodataState.findById.data) {
biodataState.findById.data.imageUrl = result.url;
}
};
const [loading, setLoading] = useState(false);
return (<Box>
<Text fw={"bold"}>Biodata</Text>
<Dropzone
mb={20}
onDrop={(files) => {
const file = files[0];
const currentId = biodataState.findById.data?.id;
if (file && currentId) {
handleUpload(file);
onDrop={async (droppedFiles) => {
setLoading(true);
for (const file of droppedFiles) {
await ApiFetch.api.ppid.profileppid["edit-img"].post({
file: file,
id: biodataState.findById.data?.id,
});
}
setLoading(false);
if (biodataState.findById.data?.id) {
biodataState.findById.load(biodataState.findById.data.id);
}
}}
onReject={() => console.log('File rejected')}
maxSize={5 * 1024 ** 2} // 5MB
accept={[MIME_TYPES.jpeg, MIME_TYPES.png, MIME_TYPES.webp]}
loading={stateProfilePPID.uploadImage.loading}
loading={loading}
>
<Group justify="center" gap="md" mih={150} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>