Profile PPID, Visi Misi, dan Dasar Hukum
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
"@cubejs-client/core": "^0.31.0",
|
"@cubejs-client/core": "^0.31.0",
|
||||||
"@elysiajs/cors": "^1.2.0",
|
"@elysiajs/cors": "^1.2.0",
|
||||||
"@elysiajs/eden": "^1.2.0",
|
"@elysiajs/eden": "^1.2.0",
|
||||||
|
"@elysiajs/static": "^1.3.0",
|
||||||
"@elysiajs/stream": "^1.1.0",
|
"@elysiajs/stream": "^1.1.0",
|
||||||
"@elysiajs/swagger": "^1.2.0",
|
"@elysiajs/swagger": "^1.2.0",
|
||||||
"@mantine/carousel": "^7.16.2",
|
"@mantine/carousel": "^7.16.2",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 275 KiB After Width: | Height: | Size: 275 KiB |
@@ -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;
|
||||||
64
src/app/admin/(dashboard)/ppid/dasar-hukum/create/page.tsx
Normal file
64
src/app/admin/(dashboard)/ppid/dasar-hukum/create/page.tsx
Normal 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;
|
||||||
40
src/app/admin/(dashboard)/ppid/dasar-hukum/listData/page.tsx
Normal file
40
src/app/admin/(dashboard)/ppid/dasar-hukum/listData/page.tsx
Normal 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;
|
||||||
@@ -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() {
|
function Page() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack gap={"xs"}>
|
||||||
dasar-hukum
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
</div>
|
<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;
|
export default Page;
|
||||||
|
|||||||
@@ -1,47 +1,38 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Group, Text } from '@mantine/core';
|
import { Box, Group, Text } from '@mantine/core';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import stateProfilePPID from '../../../_state/ppid/profile_ppid/profile_PPID';
|
import stateProfilePPID from '../../../_state/ppid/profile_ppid/profile_PPID';
|
||||||
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
|
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
|
||||||
import { Dropzone, MIME_TYPES } from '@mantine/dropzone';
|
import { Dropzone, MIME_TYPES } from '@mantine/dropzone';
|
||||||
import { IconUpload, IconX, IconPhoto } from '@tabler/icons-react';
|
import { IconUpload, IconX, IconPhoto } from '@tabler/icons-react';
|
||||||
|
import ApiFetch from '@/lib/api-fetch';
|
||||||
|
|
||||||
|
|
||||||
function Biodata() {
|
function Biodata() {
|
||||||
const biodataState = useProxy(stateProfilePPID)
|
const biodataState = useProxy(stateProfilePPID)
|
||||||
const handleUpload = async (file: File) => {
|
const [loading, setLoading] = useState(false);
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (<Box>
|
return (<Box>
|
||||||
<Text fw={"bold"}>Biodata</Text>
|
<Text fw={"bold"}>Biodata</Text>
|
||||||
<Dropzone
|
<Dropzone
|
||||||
mb={20}
|
mb={20}
|
||||||
onDrop={(files) => {
|
onDrop={async (droppedFiles) => {
|
||||||
const file = files[0];
|
setLoading(true);
|
||||||
const currentId = biodataState.findById.data?.id;
|
for (const file of droppedFiles) {
|
||||||
if (file && currentId) {
|
await ApiFetch.api.ppid.profileppid["edit-img"].post({
|
||||||
handleUpload(file);
|
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]}
|
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' }}>
|
<Group justify="center" gap="md" mih={150} style={{ pointerEvents: 'none' }}>
|
||||||
<Dropzone.Accept>
|
<Dropzone.Accept>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Context } from "elysia";
|
|||||||
import { writeFile, unlink } from "fs/promises";
|
import { writeFile, unlink } from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
import { mkdir } from "fs/promises";
|
||||||
|
|
||||||
interface ProfilePPIDBody {
|
interface ProfilePPIDBody {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -31,31 +32,51 @@ export default async function editImageProfilePPID(context: Context<{ body: Prof
|
|||||||
const bytes = await file.arrayBuffer();
|
const bytes = await file.arrayBuffer();
|
||||||
const buffer = Buffer.from(bytes);
|
const buffer = Buffer.from(bytes);
|
||||||
const filename = `${id}_${Date.now()}_${file.name}`;
|
const filename = `${id}_${Date.now()}_${file.name}`;
|
||||||
const filePath = path.resolve("public", "assets", "images", "ppid", "profile-ppid", filename);
|
const folderPath = path.resolve("public", "assets", "images", "ppid", "profile-ppid");
|
||||||
|
|
||||||
// Simpan file baru
|
try {
|
||||||
await writeFile(filePath, buffer);
|
await mkdir(folderPath, { recursive: true });
|
||||||
|
|
||||||
const imageUrl = `/assets/images/ppid/profile-ppid/${filename}`;
|
console.log('Folder path:', folderPath);
|
||||||
|
const filePath = path.join(folderPath, filename);
|
||||||
// Hapus file lama (opsional, kalau mau bersih)
|
console.log('File path:', filePath);
|
||||||
const oldData = await prisma.profilePPID.findUnique({ where: { id } });
|
|
||||||
if (oldData?.imageUrl) {
|
await writeFile(filePath, buffer);
|
||||||
const oldPath = path.resolve("public", oldData.imageUrl.replace(/^\//, ""));
|
|
||||||
if (fs.existsSync(oldPath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
await unlink(oldPath).catch(() => {});
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Failed to write file to disk",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const imageUrl = `/assets/images/ppid/profile-ppid/${filename}`;
|
||||||
|
|
||||||
|
// Hapus file lama (opsional, kalau mau bersih)
|
||||||
|
const oldData = await prisma.profilePPID.findUnique({ where: { id } });
|
||||||
|
if (oldData?.imageUrl) {
|
||||||
|
const oldPath = path.resolve("public", oldData.imageUrl.replace(/^\//, ""));
|
||||||
|
if (fs.existsSync(oldPath)) {
|
||||||
|
await unlink(oldPath).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update DB
|
||||||
|
await prisma.profilePPID.update({
|
||||||
|
where: { id },
|
||||||
|
data: { imageUrl },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Gambar berhasil diunggah",
|
||||||
|
url: imageUrl,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error uploading file:', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Gagal mengunggah gambar",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update DB
|
|
||||||
await prisma.profilePPID.update({
|
|
||||||
where: { id },
|
|
||||||
data: { imageUrl },
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
message: "Gambar berhasil diunggah",
|
|
||||||
url: imageUrl,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
|
import { staticPlugin } from '@elysiajs/static'
|
||||||
import cors, { HTTPMethod } from "@elysiajs/cors";
|
import cors, { HTTPMethod } from "@elysiajs/cors";
|
||||||
import swagger from "@elysiajs/swagger";
|
import swagger from "@elysiajs/swagger";
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
@@ -22,7 +23,7 @@ if (!process.env.WIBU_UPLOAD_DIR)
|
|||||||
throw new Error("WIBU_UPLOAD_DIR is not defined");
|
throw new Error("WIBU_UPLOAD_DIR is not defined");
|
||||||
|
|
||||||
const UPLOAD_DIR = path.join(ROOT, process.env.WIBU_UPLOAD_DIR);
|
const UPLOAD_DIR = path.join(ROOT, process.env.WIBU_UPLOAD_DIR);
|
||||||
const UPLOAD_DIR_IMAGE = path.join(UPLOAD_DIR, "image");
|
const UPLOAD_DIR_IMAGE = path.join(UPLOAD_DIR, "public", "assets", "images", "ppid", "profile-ppid");
|
||||||
|
|
||||||
// create uploads dir
|
// create uploads dir
|
||||||
fs.mkdir(UPLOAD_DIR, {
|
fs.mkdir(UPLOAD_DIR, {
|
||||||
@@ -62,6 +63,10 @@ const Utils = new Elysia({
|
|||||||
|
|
||||||
const ApiServer = new Elysia()
|
const ApiServer = new Elysia()
|
||||||
.use(swagger({ path: "/api/docs" }))
|
.use(swagger({ path: "/api/docs" }))
|
||||||
|
.use(staticPlugin({
|
||||||
|
prefix: '/', // biar bisa akses dari root URL
|
||||||
|
assets: './public'
|
||||||
|
}))
|
||||||
.use(cors(corsConfig))
|
.use(cors(corsConfig))
|
||||||
.use(PPID)
|
.use(PPID)
|
||||||
.use(Kesehatan)
|
.use(Kesehatan)
|
||||||
|
|||||||
Reference in New Issue
Block a user