nico/27-okt-25 #1
@@ -4,7 +4,7 @@ import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
// Schema validasi
|
||||
// Validasi form
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1),
|
||||
alamat: z.string().min(1),
|
||||
@@ -37,7 +37,8 @@ const defaultForm = {
|
||||
email: "",
|
||||
facebook: "",
|
||||
kontakUGD: "",
|
||||
}
|
||||
},
|
||||
image: undefined,
|
||||
};
|
||||
|
||||
const puskesmasState = proxy({
|
||||
@@ -53,23 +54,105 @@ const puskesmasState = proxy({
|
||||
|
||||
try {
|
||||
puskesmasState.create.loading = true;
|
||||
const formData = {
|
||||
...puskesmasState.create.form,
|
||||
kontak: {
|
||||
...puskesmasState.create.form.kontak,
|
||||
jam: puskesmasState.create.form.jam
|
||||
}
|
||||
};
|
||||
const res = await ApiFetch.api.kesehatan.puskesmas.create.post(formData);
|
||||
if (res.status === 200) {
|
||||
puskesmasState.findMany.load();
|
||||
toast.success("Berhasil menambahkan puskesmas");
|
||||
} else {
|
||||
toast.error("Gagal tambah puskesmas");
|
||||
|
||||
console.log('Form data:', puskesmasState.create.form);
|
||||
interface ErrorResponse {
|
||||
message?: string;
|
||||
error?: string;
|
||||
errors?: Record<string, string[]>;
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error("Terjadi kesalahan saat tambah");
|
||||
console.error(err);
|
||||
|
||||
const payload = {
|
||||
name: puskesmasState.create.form.name,
|
||||
alamat: puskesmasState.create.form.alamat,
|
||||
imageId: puskesmasState.create.form.imageId,
|
||||
jam: {
|
||||
workDays: puskesmasState.create.form.jam.workDays,
|
||||
weekDays: puskesmasState.create.form.jam.weekDays,
|
||||
holiday: puskesmasState.create.form.jam.holiday,
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: puskesmasState.create.form.kontak.kontakPuskesmas,
|
||||
email: puskesmasState.create.form.kontak.email,
|
||||
facebook: puskesmasState.create.form.kontak.facebook,
|
||||
kontakUGD: puskesmasState.create.form.kontak.kontakUGD,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
console.log('Sending payload:', JSON.stringify(payload, null, 2));
|
||||
|
||||
try {
|
||||
const res = await ApiFetch.api.kesehatan.puskesmas.create.post(payload);
|
||||
console.log('API Response:', res);
|
||||
|
||||
if (res.status === 200) {
|
||||
await puskesmasState.findMany.load();
|
||||
toast.success("Berhasil menambahkan puskesmas");
|
||||
return res;
|
||||
} else {
|
||||
console.error('API Error Response:', {
|
||||
status: res.status,
|
||||
data: res.data
|
||||
});
|
||||
|
||||
const errorData = res.data as ErrorResponse;
|
||||
let errorMessage = 'Gagal menambahkan puskesmas';
|
||||
|
||||
if (errorData?.message) {
|
||||
errorMessage = errorData.message;
|
||||
} else if (errorData?.error) {
|
||||
errorMessage = errorData.error;
|
||||
} else if (errorData?.errors) {
|
||||
errorMessage = Object.entries(errorData.errors)
|
||||
.map(([field, messages]) => `${field}: ${Array.isArray(messages) ? messages.join(', ') : messages}`)
|
||||
.join('; ');
|
||||
}
|
||||
|
||||
console.error('Extracted error message:', errorMessage);
|
||||
toast.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in API call:', {
|
||||
error,
|
||||
errorString: String(error),
|
||||
jsonError: error instanceof Error ? {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack
|
||||
} : 'Not an Error instance'
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in puskesmas submit:", {
|
||||
error,
|
||||
errorString: String(error),
|
||||
errorType: typeof error,
|
||||
isErrorInstance: error instanceof Error,
|
||||
errorDetails: error instanceof Error ? {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause
|
||||
} : null
|
||||
});
|
||||
|
||||
let errorMessage = "Terjadi kesalahan saat menambahkan puskesmas";
|
||||
if (error instanceof Error) {
|
||||
errorMessage = error.message || errorMessage;
|
||||
} else if (error && typeof error === 'object' && 'message' in error) {
|
||||
errorMessage = String((error as { message: unknown }).message);
|
||||
} else if (typeof error === 'string') {
|
||||
errorMessage = error;
|
||||
}
|
||||
|
||||
console.error('Displaying error to user:', errorMessage);
|
||||
toast.error(errorMessage);
|
||||
throw error;
|
||||
} finally {
|
||||
puskesmasState.create.loading = false;
|
||||
}
|
||||
@@ -80,11 +163,9 @@ const puskesmasState = proxy({
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PuskesmasGetPayload<{
|
||||
include: { image: true; jam: true; kontak: true };
|
||||
}>[]
|
||||
| null,
|
||||
data: null as Prisma.PuskesmasGetPayload<{
|
||||
include: { image: true; jam: true; kontak: true };
|
||||
}>[] | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.puskesmas["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
@@ -94,11 +175,9 @@ const puskesmasState = proxy({
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as
|
||||
| Prisma.PuskesmasGetPayload<{
|
||||
include: { image: true; jam: true; kontak: true };
|
||||
}>
|
||||
| null,
|
||||
data: null as Prisma.PuskesmasGetPayload<{
|
||||
include: { image: true; jam: true; kontak: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
const res = await fetch(`/api/kesehatan/puskesmas/${id}`);
|
||||
if (res.ok) {
|
||||
@@ -140,7 +219,8 @@ const puskesmasState = proxy({
|
||||
email: data.kontak.email,
|
||||
facebook: data.kontak.facebook,
|
||||
kontakUGD: data.kontak.kontakUGD,
|
||||
}
|
||||
},
|
||||
image: data.image,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -154,17 +234,18 @@ const puskesmasState = proxy({
|
||||
|
||||
try {
|
||||
puskesmasState.edit.loading = true;
|
||||
const formData = {
|
||||
...puskesmasState.edit.form,
|
||||
kontak: {
|
||||
...puskesmasState.edit.form.kontak,
|
||||
jam: puskesmasState.edit.form.jam
|
||||
}
|
||||
const payload = {
|
||||
name: puskesmasState.edit.form.name,
|
||||
alamat: puskesmasState.edit.form.alamat,
|
||||
imageId: puskesmasState.edit.form.imageId,
|
||||
jam: { ...puskesmasState.edit.form.jam },
|
||||
kontak: { ...puskesmasState.edit.form.kontak }
|
||||
};
|
||||
|
||||
const res = await fetch(`/api/kesehatan/puskesmas/${puskesmasState.edit.id}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(formData),
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
|
||||
286
src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx
Normal file
286
src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/edit/page.tsx
Normal file
@@ -0,0 +1,286 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import puskesmasState from '@/app/admin/(dashboard)/_state/kesehatan/puskesmas/puskesmas';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
interface PuskesmasFormBase {
|
||||
name: string;
|
||||
alamat: string;
|
||||
jam: {
|
||||
workDays: string;
|
||||
weekDays: string;
|
||||
holiday: string;
|
||||
};
|
||||
kontak: {
|
||||
kontakPuskesmas: string;
|
||||
email: string;
|
||||
facebook: string;
|
||||
kontakUGD: string;
|
||||
};
|
||||
imageId: string;
|
||||
}
|
||||
|
||||
interface PuskesmasFormData extends PuskesmasFormBase {
|
||||
image?: {
|
||||
link: string;
|
||||
};
|
||||
}
|
||||
|
||||
function EditPuskesmas() {
|
||||
const statePuskesmas = useProxy(puskesmasState);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [formData, setFormData] = useState<PuskesmasFormData>({
|
||||
name: statePuskesmas.edit.form.name || '',
|
||||
alamat: statePuskesmas.edit.form.alamat || '',
|
||||
jam: {
|
||||
workDays: statePuskesmas.edit.form.jam?.workDays || '',
|
||||
weekDays: statePuskesmas.edit.form.jam?.weekDays || '',
|
||||
holiday: statePuskesmas.edit.form.jam?.holiday || '',
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: statePuskesmas.edit.form.kontak?.kontakPuskesmas || '',
|
||||
email: statePuskesmas.edit.form.kontak?.email || '',
|
||||
facebook: statePuskesmas.edit.form.kontak?.facebook || '',
|
||||
kontakUGD: statePuskesmas.edit.form.kontak?.kontakUGD || '',
|
||||
},
|
||||
imageId: statePuskesmas.edit.form.imageId || '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadPuskesmas = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
await statePuskesmas.edit.load(id);
|
||||
const { form } = statePuskesmas.edit;
|
||||
if (form) {
|
||||
setFormData({
|
||||
name: form.name,
|
||||
alamat: form.alamat,
|
||||
jam: {
|
||||
workDays: form.jam.workDays,
|
||||
weekDays: form.jam.weekDays,
|
||||
holiday: form.jam.holiday,
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: form.kontak.kontakPuskesmas,
|
||||
email: form.kontak.email,
|
||||
facebook: form.kontak.facebook,
|
||||
kontakUGD: form.kontak.kontakUGD,
|
||||
},
|
||||
imageId: form.imageId,
|
||||
});
|
||||
|
||||
// Check if there's an existing image URL in the form data
|
||||
const formWithImage = form as PuskesmasFormData;
|
||||
if (formWithImage.image?.link) {
|
||||
setPreviewImage(formWithImage.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading puskesmas:", error);
|
||||
toast.error("Gagal memuat data puskesmas");
|
||||
}
|
||||
};
|
||||
loadPuskesmas();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
statePuskesmas.edit.form = {
|
||||
...statePuskesmas.edit.form,
|
||||
name: formData.name,
|
||||
alamat: formData.alamat,
|
||||
jam: {
|
||||
workDays: formData.jam.workDays,
|
||||
weekDays: formData.jam.weekDays,
|
||||
holiday: formData.jam.holiday,
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: formData.kontak.kontakPuskesmas,
|
||||
email: formData.kontak.email,
|
||||
facebook: formData.kontak.facebook,
|
||||
kontakUGD: formData.kontak.kontakUGD,
|
||||
},
|
||||
imageId: formData.imageId,
|
||||
};
|
||||
|
||||
if (file) {
|
||||
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||
const uploaded = res.data?.data;
|
||||
|
||||
if (!uploaded?.id) {
|
||||
toast.error("Gagal upload gambar");
|
||||
return;
|
||||
}
|
||||
|
||||
statePuskesmas.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
const success = await statePuskesmas.edit.submit();
|
||||
if (success) {
|
||||
toast.success("Puskesmas berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/puskesmas");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating puskesmas:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data puskesmas");
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (selectedFile: File | null) => {
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
if (e.target?.result) {
|
||||
setPreviewImage(e.target.result as string);
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(selectedFile);
|
||||
} else {
|
||||
setFile(null);
|
||||
setPreviewImage(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleNestedChange = (section: 'jam' | 'kontak', field: string, value: string) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[section]: {
|
||||
...prev[section],
|
||||
[field]: value
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button onClick={() => router.back()} variant="subtle" color="blue">
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Stack gap="xs">
|
||||
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>Edit Puskesmas</Title>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Nama Puskesmas</Text>}
|
||||
placeholder="masukkan nama puskesmas"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Alamat</Text>}
|
||||
placeholder="masukkan alamat"
|
||||
name="alamat"
|
||||
value={formData.alamat}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Jam Buka</Text>}
|
||||
placeholder="masukkan jam buka"
|
||||
value={formData.jam.workDays}
|
||||
onChange={(e) => handleNestedChange('jam', 'workDays', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Jam Tutup</Text>}
|
||||
placeholder="masukkan jam tutup"
|
||||
value={formData.jam.weekDays}
|
||||
onChange={(e) => handleNestedChange('jam', 'weekDays', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Jam Libur</Text>}
|
||||
placeholder="masukkan jam libur"
|
||||
value={formData.jam.holiday}
|
||||
onChange={(e) => handleNestedChange('jam', 'holiday', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Kontak Puskesmas</Text>}
|
||||
placeholder="masukkan kontak puskesmas"
|
||||
value={formData.kontak.kontakPuskesmas}
|
||||
onChange={(e) => handleNestedChange('kontak', 'kontakPuskesmas', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Email</Text>}
|
||||
placeholder="masukkan email"
|
||||
value={formData.kontak.email}
|
||||
onChange={(e) => handleNestedChange('kontak', 'email', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Facebook</Text>}
|
||||
placeholder="masukkan facebook"
|
||||
value={formData.kontak.facebook}
|
||||
onChange={(e) => handleNestedChange('kontak', 'facebook', e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Kontak UGD</Text>}
|
||||
placeholder="masukkan kontak UGD"
|
||||
value={formData.kontak.kontakUGD}
|
||||
onChange={(e) => handleNestedChange('kontak', 'kontakUGD', e.target.value)}
|
||||
/>
|
||||
|
||||
<FileInput
|
||||
placeholder="Pilih gambar"
|
||||
label="Gambar"
|
||||
accept="image/*"
|
||||
leftSection={<IconImageInPicture size={16} />}
|
||||
value={file}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
|
||||
{previewImage ? (
|
||||
<Image alt="Preview" src={previewImage} w={200} h={200} />
|
||||
) : (
|
||||
<Center w={200} h={200} bg="gray">
|
||||
<IconImageInPicture />
|
||||
</Center>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
bg={colors['blue-button']}
|
||||
loading={statePuskesmas.edit.loading}
|
||||
>
|
||||
Simpan Perubahan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPuskesmas;
|
||||
111
src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx
Normal file
111
src/app/admin/(dashboard)/kesehatan/puskesmas/[id]/page.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, Flex, Text, Image, Skeleton } from '@mantine/core';
|
||||
import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import React, { useState } from 'react';
|
||||
import puskesmasState from '../../../_state/kesehatan/puskesmas/puskesmas';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
|
||||
function DetailPuskesmas() {
|
||||
const params = useParams()
|
||||
const router = useRouter();
|
||||
const statePuskesmas = useProxy(puskesmasState)
|
||||
const [modalHapus, setModalHapus] = useState(false);
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
|
||||
useShallowEffect(() => {
|
||||
statePuskesmas.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
statePuskesmas.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/kesehatan/puskesmas")
|
||||
}
|
||||
}
|
||||
|
||||
if (!statePuskesmas.findUnique.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail Puskesmas</Text>
|
||||
{statePuskesmas.findUnique.data ? (
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Nama Puskesmas</Text>
|
||||
<Text fz={"lg"}>{statePuskesmas.findUnique.data.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Alamat</Text>
|
||||
<Text fz={"lg"}>{statePuskesmas.findUnique.data.alamat}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Jam Operasional</Text>
|
||||
<Text fz={"lg"}>{statePuskesmas.findUnique.data.jam.workDays}</Text>
|
||||
<Text fz={"lg"}>{statePuskesmas.findUnique.data.jam.weekDays}</Text>
|
||||
<Text fz={"lg"}>{statePuskesmas.findUnique.data.jam.holiday}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
|
||||
<Image src={statePuskesmas.findUnique.data.image?.link} alt="gambar" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Kontak</Text>
|
||||
<Text fz={"lg"} >{statePuskesmas.findUnique.data.kontak.kontakPuskesmas}</Text>
|
||||
<Text fz={"lg"} >{statePuskesmas.findUnique.data.kontak.email}</Text>
|
||||
<Text fz={"lg"} >{statePuskesmas.findUnique.data.kontak.facebook}</Text>
|
||||
<Text fz={"lg"} >{statePuskesmas.findUnique.data.kontak.kontakUGD}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={"xs"}>
|
||||
<Button color="red" onClick={() => {
|
||||
if (statePuskesmas.findUnique.data) {
|
||||
setSelectedId(statePuskesmas.findUnique.data.id)
|
||||
setModalHapus(true)
|
||||
}
|
||||
}}>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button onClick={() => router.push(`/admin/kesehatan/puskesmas/${statePuskesmas.findUnique.data?.id}/edit`)} color="green">
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailPuskesmas;
|
||||
@@ -1,14 +1,76 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import puskesmasState from '../../../_state/kesehatan/puskesmas/puskesmas';
|
||||
|
||||
function CreatePuskesmas() {
|
||||
const statePuskesmas = useProxy(puskesmasState)
|
||||
const router = useRouter();
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
|
||||
|
||||
const resetForm = () => {
|
||||
statePuskesmas.create.form = {
|
||||
name: "",
|
||||
alamat: "",
|
||||
jam: {
|
||||
workDays: "",
|
||||
weekDays: "",
|
||||
holiday: "",
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: "",
|
||||
email: "",
|
||||
facebook: "",
|
||||
kontakUGD: "",
|
||||
},
|
||||
imageId: "",
|
||||
image: undefined,
|
||||
};
|
||||
|
||||
setFile(null);
|
||||
setPreviewImage(null);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!file) {
|
||||
return toast.warn("Pilih file gambar terlebih dahulu");
|
||||
}
|
||||
|
||||
// Upload gambar dulu
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
name: file.name,
|
||||
});
|
||||
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) {
|
||||
return toast.error("Gagal upload gambar");
|
||||
}
|
||||
|
||||
statePuskesmas.create.form.imageId = uploaded.id;
|
||||
// State is already being updated directly in the form inputs
|
||||
|
||||
await statePuskesmas.create.submit();
|
||||
|
||||
toast.success("Data berhasil disimpan");
|
||||
resetForm();
|
||||
// After successful submission, redirect to the list page
|
||||
router.push('/admin/kesehatan/puskesmas');
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box component="form" onSubmit={handleSubmit}>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
@@ -18,26 +80,80 @@ function CreatePuskesmas() {
|
||||
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>Create Puskesmas</Title>
|
||||
|
||||
<TextInput
|
||||
|
||||
label={<Text fz="sm" fw="bold">Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
label={<Text fz="sm" fw="bold">Nama Puskesmas</Text>}
|
||||
placeholder="masukkan nama puskesmas"
|
||||
value={statePuskesmas.create.form.name}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.name = e.target.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Alamat</Text>}
|
||||
placeholder="masukkan alamat"
|
||||
value={statePuskesmas.create.form.alamat}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.alamat = e.target.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Jam Buka</Text>}
|
||||
placeholder="masukkan jam buka"
|
||||
value={statePuskesmas.create.form.jam.workDays}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.jam.workDays = e.target.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Jam Tutup</Text>}
|
||||
placeholder="masukkan jam tutup"
|
||||
value={statePuskesmas.create.form.jam.weekDays}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.jam.weekDays = e.target.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Holiday</Text>}
|
||||
placeholder="masukkan holiday"
|
||||
value={statePuskesmas.create.form.jam.holiday}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.jam.holiday = e.target.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Kontak Puskesmas</Text>}
|
||||
placeholder="masukkan kontak puskesmas"
|
||||
value={statePuskesmas.create.form.kontak.kontakPuskesmas}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.kontak.kontakPuskesmas = e.target.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Email</Text>}
|
||||
placeholder="masukkan email"
|
||||
value={statePuskesmas.create.form.kontak.email}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.kontak.email = e.target.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Facebook</Text>}
|
||||
placeholder="masukkan facebook"
|
||||
value={statePuskesmas.create.form.kontak.facebook}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.kontak.facebook = e.target.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Kontak UGD</Text>}
|
||||
placeholder="masukkan kontak ugd"
|
||||
value={statePuskesmas.create.form.kontak.kontakUGD}
|
||||
onChange={(e) => {
|
||||
statePuskesmas.create.form.kontak.kontakUGD = e.target.value;
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
|
||||
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
|
||||
placeholder="masukkan deskripsi"
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
|
||||
label={<Text fz="sm" fw="bold">Kategori</Text>}
|
||||
placeholder="masukkan kategori"
|
||||
/>
|
||||
|
||||
{/* <FileInput
|
||||
<FileInput
|
||||
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
|
||||
value={file}
|
||||
onChange={async (e) => {
|
||||
@@ -48,25 +164,18 @@ function CreatePuskesmas() {
|
||||
);
|
||||
setPreviewImage(base64);
|
||||
}}
|
||||
/> */}
|
||||
/>
|
||||
|
||||
{/* {previewImage ? (
|
||||
{previewImage ? (
|
||||
<Image alt="" src={previewImage} w={200} h={200} />
|
||||
) : (
|
||||
<Center w={200} h={200} bg="gray">
|
||||
<IconImageInPicture />
|
||||
</Center>
|
||||
)} */}
|
||||
)}
|
||||
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold">Konten</Text>
|
||||
<KesehatanEditor
|
||||
showSubmit={false}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Button bg={colors['blue-button']}>
|
||||
Simpan Potensi
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>
|
||||
Simpan Puskesmas
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, Flex, Text, Image } from '@mantine/core';
|
||||
import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React from 'react';
|
||||
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
|
||||
function DetailPuskesmas() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail Potensi</Text>
|
||||
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Judul</Text>
|
||||
<Text fz={"lg"}>Test Judul</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Kategori</Text>
|
||||
<Text fz={"lg"}>Test Kategori</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
|
||||
<Text fz={"lg"}>Test Deskripsi</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
|
||||
<Image src={"/"} alt="gambar" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Konten</Text>
|
||||
<Text fz={"lg"} >Test Konten</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={"xs"}>
|
||||
<Button color="red">
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button onClick={() => router.push('/admin/kesehatan/puskesmas/edit')} color="green">
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Hapus
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||
/> */}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailPuskesmas;
|
||||
@@ -1,10 +1,13 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import puskesmasState from '../../_state/kesehatan/puskesmas/puskesmas';
|
||||
|
||||
function Puskesmas() {
|
||||
return (
|
||||
@@ -20,7 +23,20 @@ function Puskesmas() {
|
||||
}
|
||||
|
||||
function ListPuskesmas() {
|
||||
const statePuskesmas = useProxy(puskesmasState)
|
||||
const router = useRouter();
|
||||
|
||||
useShallowEffect(() => {
|
||||
statePuskesmas.findMany.load()
|
||||
}, [])
|
||||
|
||||
if (!statePuskesmas.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Skeleton h={500}/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
@@ -33,28 +49,27 @@ function ListPuskesmas() {
|
||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Judul</TableTh>
|
||||
<TableTh>Kategori</TableTh>
|
||||
<TableTh>Nama Puskesmas</TableTh>
|
||||
<TableTh>Alamat</TableTh>
|
||||
<TableTh>Image</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
<TableTr>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>Test</Text>
|
||||
</Box></TableTd>
|
||||
<TableTd>Test</TableTd>
|
||||
<TableTd>
|
||||
<Image w={100} src={"/"} alt="image" />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push('/admin/kesehatan/puskesmas/detail')}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
{statePuskesmas.findMany.data?.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{item.alamat}</TableTd>
|
||||
<TableTd>
|
||||
<Image w={100} src={item.image.link} alt="image" />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/kesehatan/puskesmas/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
|
||||
@@ -9,27 +9,27 @@ const Puskesmas = new Elysia({
|
||||
prefix: "puskesmas",
|
||||
tags: ["Kesehatan/Puskesmas"],
|
||||
})
|
||||
.post("/create", puskesmasCreate, {
|
||||
body: t.Object({
|
||||
name: t.String(),
|
||||
alamat: t.String(),
|
||||
imageId: t.String(),
|
||||
kontak: t.Object({
|
||||
jam: t.Object({
|
||||
workDays: t.String(),
|
||||
weekDays: t.String(),
|
||||
holiday: t.String(),
|
||||
}),
|
||||
kontakPuskesmas: t.String(),
|
||||
email: t.String(),
|
||||
facebook: t.String(),
|
||||
kontakUGD: t.String(),
|
||||
}),
|
||||
.post("/create", puskesmasCreate, {
|
||||
body: t.Object({
|
||||
name: t.String(),
|
||||
alamat: t.String(),
|
||||
imageId: t.String(),
|
||||
jam: t.Object({
|
||||
workDays: t.String(),
|
||||
weekDays: t.String(),
|
||||
holiday: t.String(),
|
||||
}),
|
||||
})
|
||||
.get("/find-many", puskesmasFindMany)
|
||||
.delete("/del/:id", puskesmasDelete)
|
||||
.get("/:id", async (context) => {
|
||||
kontak: t.Object({
|
||||
kontakPuskesmas: t.String(),
|
||||
email: t.String(),
|
||||
facebook: t.String(),
|
||||
kontakUGD: t.String(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
.get("/find-many", puskesmasFindMany)
|
||||
.delete("/del/:id", puskesmasDelete)
|
||||
.get("/:id", async (context) => {
|
||||
const response = await findUniquePuskesmas(new Request(context.request));
|
||||
return response;
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user