Fix QC Kak Inno Admin, Fix QC Keano UI User, Fix QC Pak jun tabel apbdes
This commit is contained in:
@@ -4,10 +4,12 @@ import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -26,6 +28,7 @@ function CreateArtikelKesehatan() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stateArtikelKesehatan.create.form = {
|
||||
@@ -62,25 +65,32 @@ function CreateArtikelKesehatan() {
|
||||
|
||||
const handleSubmit = async (e?: React.FormEvent) => {
|
||||
e?.preventDefault();
|
||||
if (!file) {
|
||||
return toast.warn('Silakan pilih file gambar terlebih dahulu');
|
||||
try {
|
||||
if (!file) {
|
||||
return toast.warn('Silakan pilih file gambar terlebih dahulu');
|
||||
}
|
||||
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
name: file.name,
|
||||
});
|
||||
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) {
|
||||
return toast.error('Gagal mengunggah gambar, silakan coba lagi');
|
||||
}
|
||||
|
||||
stateArtikelKesehatan.create.form.imageId = uploaded.id;
|
||||
await stateArtikelKesehatan.create.submit();
|
||||
toast.success('Data berhasil disimpan');
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan');
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
toast.error('Gagal menyimpan data, silakan coba lagi');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file,
|
||||
name: file.name,
|
||||
});
|
||||
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) {
|
||||
return toast.error('Gagal mengunggah gambar, silakan coba lagi');
|
||||
}
|
||||
|
||||
stateArtikelKesehatan.create.form.imageId = uploaded.id;
|
||||
await stateArtikelKesehatan.create.submit();
|
||||
toast.success('Data berhasil disimpan');
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -124,7 +134,7 @@ function CreateArtikelKesehatan() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid, gunakan format gambar')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
radius="md"
|
||||
p="xl"
|
||||
>
|
||||
@@ -145,7 +155,7 @@ function CreateArtikelKesehatan() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ textAlign: 'center' }}>
|
||||
<Box pos={"relative"} mt="sm" style={{ textAlign: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
@@ -157,6 +167,24 @@ function CreateArtikelKesehatan() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@@ -164,7 +192,7 @@ function CreateArtikelKesehatan() {
|
||||
<TextInput
|
||||
label={"Judul"}
|
||||
placeholder="Masukkan judul"
|
||||
defaultValue={stateArtikelKesehatan.create.form.title}
|
||||
value={stateArtikelKesehatan.create.form.title}
|
||||
onChange={(e) => {
|
||||
stateArtikelKesehatan.create.form.title = e.target.value;
|
||||
}}
|
||||
@@ -173,7 +201,7 @@ function CreateArtikelKesehatan() {
|
||||
<TextInput
|
||||
label={"Deskripsi"}
|
||||
placeholder="Masukkan deskripsi"
|
||||
defaultValue={stateArtikelKesehatan.create.form.content}
|
||||
value={stateArtikelKesehatan.create.form.content}
|
||||
onChange={(e) => {
|
||||
stateArtikelKesehatan.create.form.content = e.target.value;
|
||||
}}
|
||||
@@ -196,7 +224,7 @@ function CreateArtikelKesehatan() {
|
||||
label={"Judul Gejala"}
|
||||
required
|
||||
placeholder="Masukkan judul gejala penyakit"
|
||||
defaultValue={stateArtikelKesehatan.create.form.symptom.title}
|
||||
value={stateArtikelKesehatan.create.form.symptom.title}
|
||||
onChange={(e) => {
|
||||
stateArtikelKesehatan.create.form.symptom.title = e.target.value;
|
||||
}}
|
||||
@@ -220,7 +248,7 @@ function CreateArtikelKesehatan() {
|
||||
label={"Judul Pencegahan"}
|
||||
required
|
||||
placeholder="Masukkan judul"
|
||||
defaultValue={stateArtikelKesehatan.create.form.prevention.title}
|
||||
value={stateArtikelKesehatan.create.form.prevention.title}
|
||||
onChange={(e) => {
|
||||
stateArtikelKesehatan.create.form.prevention.title = e.target.value;
|
||||
}}
|
||||
@@ -241,7 +269,7 @@ function CreateArtikelKesehatan() {
|
||||
label={"Judul Pertolongan Pertama"}
|
||||
required
|
||||
placeholder="Masukkan judul"
|
||||
defaultValue={stateArtikelKesehatan.create.form.firstAid.title}
|
||||
value={stateArtikelKesehatan.create.form.firstAid.title}
|
||||
onChange={(e) => {
|
||||
stateArtikelKesehatan.create.form.firstAid.title = e.target.value;
|
||||
}}
|
||||
@@ -262,7 +290,7 @@ function CreateArtikelKesehatan() {
|
||||
label={"Judul Mitos dan Fakta"}
|
||||
required
|
||||
placeholder="Masukkan judul"
|
||||
defaultValue={stateArtikelKesehatan.create.form.mythVsFact.title}
|
||||
value={stateArtikelKesehatan.create.form.mythVsFact.title}
|
||||
onChange={(e) => {
|
||||
stateArtikelKesehatan.create.form.mythVsFact.title = e.target.value;
|
||||
}}
|
||||
@@ -300,8 +328,20 @@ function CreateArtikelKesehatan() {
|
||||
|
||||
{/* Submit Button */}
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
type="submit"
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
@@ -310,7 +350,7 @@ function CreateArtikelKesehatan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client';
|
||||
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -45,6 +47,7 @@ function EditFasilitasKesehatan() {
|
||||
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState<FasilitasKesehatanFormBase>({
|
||||
name: '',
|
||||
@@ -56,6 +59,16 @@ function EditFasilitasKesehatan() {
|
||||
tarifDanLayanan: { layanan: '', tarif: '' },
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState<FasilitasKesehatanFormBase>({
|
||||
name: '',
|
||||
informasiUmum: { fasilitas: '', alamat: '', jamOperasional: '' },
|
||||
layananUnggulan: { content: '' },
|
||||
dokterdanTenagaMedis: { name: '', specialist: '', jadwal: '' },
|
||||
fasilitasPendukung: { content: '' },
|
||||
prosedurPendaftaran: { content: '' },
|
||||
tarifDanLayanan: { layanan: '', tarif: '' },
|
||||
});
|
||||
|
||||
// Helper untuk update nested state
|
||||
const updateForm = <K extends keyof FasilitasKesehatanFormBase>(
|
||||
key: K,
|
||||
@@ -71,26 +84,73 @@ function EditFasilitasKesehatan() {
|
||||
[key]: { ...prev[key] as object, [nestedKey]: value },
|
||||
}));
|
||||
|
||||
const deepClone = (obj: any): any => {
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
} catch (error) {
|
||||
console.warn('Gagal deep clone dengan JSON fallback:', error);
|
||||
return obj; // fallback (berisiko shared reference)
|
||||
}
|
||||
};
|
||||
|
||||
// Load data
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
await state.edit.load(id);
|
||||
const form = state.edit.form;
|
||||
if (form) setFormData(form as FasilitasKesehatanFormBase);
|
||||
const loadedData = state.edit.form;
|
||||
|
||||
if (!loadedData) {
|
||||
toast.error('Data tidak ditemukan');
|
||||
return;
|
||||
}
|
||||
|
||||
// Gunakan JSON fallback untuk deep clone
|
||||
const clonedData = deepClone(loadedData) as FasilitasKesehatanFormBase;
|
||||
|
||||
setFormData(clonedData);
|
||||
setOriginalData(clonedData);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Gagal memuat data fasilitas kesehatan');
|
||||
}
|
||||
};
|
||||
|
||||
load();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
informasiUmum:
|
||||
{
|
||||
fasilitas: originalData.informasiUmum.fasilitas,
|
||||
alamat: originalData.informasiUmum.alamat,
|
||||
jamOperasional: originalData.informasiUmum.jamOperasional
|
||||
},
|
||||
layananUnggulan: { content: originalData.layananUnggulan.content },
|
||||
dokterdanTenagaMedis: {
|
||||
name: originalData.dokterdanTenagaMedis.name,
|
||||
specialist: originalData.dokterdanTenagaMedis.specialist,
|
||||
jadwal: originalData.dokterdanTenagaMedis.jadwal
|
||||
},
|
||||
fasilitasPendukung: { content: originalData.fasilitasPendukung.content },
|
||||
prosedurPendaftaran: { content: originalData.prosedurPendaftaran.content },
|
||||
tarifDanLayanan: {
|
||||
layanan: originalData.tarifDanLayanan.layanan,
|
||||
tarif: originalData.tarifDanLayanan.tarif
|
||||
},
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
// Submit
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
state.edit.form = { ...state.edit.form, ...formData };
|
||||
const success = await state.edit.submit();
|
||||
if (success) {
|
||||
@@ -100,6 +160,8 @@ function EditFasilitasKesehatan() {
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data fasilitas kesehatan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -230,19 +292,30 @@ function EditFasilitasKesehatan() {
|
||||
</Box>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Group justify="flex-end">
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
loading={state.edit.loading}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
@@ -21,6 +23,7 @@ import { useProxy } from 'valtio/utils';
|
||||
function CreateFasilitasKesehatan() {
|
||||
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stateFasilitasKesehatan.create.form = {
|
||||
@@ -53,10 +56,18 @@ function CreateFasilitasKesehatan() {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
await stateFasilitasKesehatan.create.submit();
|
||||
toast.success('Data berhasil disimpan');
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await stateFasilitasKesehatan.create.submit();
|
||||
toast.success('Data berhasil disimpan');
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Gagal menyimpan data');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -89,7 +100,7 @@ function CreateFasilitasKesehatan() {
|
||||
<TextInput
|
||||
label={"Nama Fasilitas Kesehatan"}
|
||||
placeholder="Masukkan nama fasilitas kesehatan"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.name}
|
||||
value={stateFasilitasKesehatan.create.form.name}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.name = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -100,21 +111,21 @@ function CreateFasilitasKesehatan() {
|
||||
<TextInput
|
||||
label="Fasilitas"
|
||||
placeholder="Masukkan fasilitas"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.informasiUmum.fasilitas}
|
||||
value={stateFasilitasKesehatan.create.form.informasiUmum.fasilitas}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.informasiUmum.fasilitas = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.informasiUmum.alamat}
|
||||
value={stateFasilitasKesehatan.create.form.informasiUmum.alamat}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.informasiUmum.alamat = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Jam Operasional"
|
||||
placeholder="Masukkan jam operasional"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional}
|
||||
value={stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -135,21 +146,21 @@ function CreateFasilitasKesehatan() {
|
||||
<TextInput
|
||||
label="Nama Dokter"
|
||||
placeholder="Masukkan nama dokter"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name}
|
||||
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Spesialis"
|
||||
placeholder="Masukkan spesialis"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist}
|
||||
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Jadwal"
|
||||
placeholder="Masukkan jadwal"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal}
|
||||
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -179,23 +190,35 @@ function CreateFasilitasKesehatan() {
|
||||
<TextInput
|
||||
label="Tarif"
|
||||
placeholder="Masukkan tarif"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif}
|
||||
value={stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Layanan"
|
||||
placeholder="Masukkan layanan"
|
||||
defaultValue={stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan}
|
||||
value={stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan}
|
||||
onChange={(e) => (stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan = e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Submit */}
|
||||
<Group justify="right" mt="md">
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
type="submit"
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
@@ -204,7 +227,7 @@ function CreateFasilitasKesehatan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -40,7 +40,7 @@ function CreateDokter() {
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Nama Dokter</Text>}
|
||||
placeholder="masukkan nama dokter"
|
||||
defaultValue={createState.create.create.form.name}
|
||||
value={createState.create.create.form.name}
|
||||
onChange={(e) => {
|
||||
createState.create.create.form.name = e.target.value;
|
||||
}}
|
||||
@@ -49,7 +49,7 @@ function CreateDokter() {
|
||||
<TextInput
|
||||
label={<Text fz="sm" fw="bold">Specialist</Text>}
|
||||
placeholder="masukkan specialist"
|
||||
defaultValue={createState.create.create.form.specialist}
|
||||
value={createState.create.create.form.specialist}
|
||||
onChange={(e) => {
|
||||
createState.create.create.form.specialist = e.target.value;
|
||||
}}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconArrowBack, IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
@@ -28,18 +28,10 @@ import fasilitasKesehatanState from '../../../_state/kesehatan/data_kesehatan_wa
|
||||
|
||||
|
||||
function FasilitasKesehatan() {
|
||||
const router = useRouter();
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* Tombol Back */}
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors["blue-button"]} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Header Search */}
|
||||
<HeaderSearch
|
||||
title='Fasilitas Kesehatan'
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
@@ -17,11 +18,13 @@ import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { convertToISODate } from '../../../persentase_data_kelahiran_kematian/lib/dateUtils';
|
||||
|
||||
function EditGrafikHasilKepuasan() {
|
||||
const editState = useProxy(grafikkepuasan);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: '',
|
||||
@@ -31,6 +34,14 @@ function EditGrafikHasilKepuasan() {
|
||||
penyakit: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
nama: '',
|
||||
tanggal: '',
|
||||
jenisKelamin: '',
|
||||
alamat: '',
|
||||
penyakit: '',
|
||||
});
|
||||
|
||||
// Load data once
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
@@ -39,13 +50,25 @@ function EditGrafikHasilKepuasan() {
|
||||
|
||||
try {
|
||||
const data = await editState.update.load(id);
|
||||
if (data) setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyakit: data.penyakit || '',
|
||||
});
|
||||
if (data) {
|
||||
const formattedTanggal = convertToISODate(data.tanggal);
|
||||
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: formattedTanggal,
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyakit: data.penyakit || '',
|
||||
});
|
||||
|
||||
setOriginalData({
|
||||
nama: data.nama || '',
|
||||
tanggal: formattedTanggal,
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyakit: data.penyakit || '',
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error loading grafik hasil kepuasan:", err);
|
||||
toast.error("Gagal memuat data grafik hasil kepuasan");
|
||||
@@ -60,8 +83,20 @@ function EditGrafikHasilKepuasan() {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
nama: originalData.nama,
|
||||
tanggal: originalData.tanggal,
|
||||
jenisKelamin: originalData.jenisKelamin,
|
||||
alamat: originalData.alamat,
|
||||
penyakit: originalData.penyakit,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
editState.update.form = { ...editState.update.form, ...formData };
|
||||
await editState.update.submit();
|
||||
toast.success('Grafik hasil kepuasan berhasil diperbarui!');
|
||||
@@ -69,6 +104,8 @@ function EditGrafikHasilKepuasan() {
|
||||
} catch (err) {
|
||||
console.error('Error updating grafik hasil kepuasan:', err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui grafik hasil kepuasan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,6 +149,17 @@ function EditGrafikHasilKepuasan() {
|
||||
))}
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -122,7 +170,7 @@ function EditGrafikHasilKepuasan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
@@ -15,12 +16,14 @@ import {
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function CreateGrafikHasilKepuasanMasyarakat() {
|
||||
const stateGrafikKepuasan = useProxy(grafikkepuasan);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stateGrafikKepuasan.create.form = {
|
||||
@@ -33,9 +36,17 @@ function CreateGrafikHasilKepuasanMasyarakat() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await stateGrafikKepuasan.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan");
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await stateGrafikKepuasan.create.create();
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan");
|
||||
} catch (error) {
|
||||
console.error("Error creating grafik kepuasan:", error);
|
||||
toast.error("Terjadi kesalahan saat membuat grafik kepuasan");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -68,7 +79,7 @@ function CreateGrafikHasilKepuasanMasyarakat() {
|
||||
<TextInput
|
||||
label="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
defaultValue={stateGrafikKepuasan.create.form.nama}
|
||||
value={stateGrafikKepuasan.create.form.nama}
|
||||
onChange={(e) => (stateGrafikKepuasan.create.form.nama = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -76,33 +87,44 @@ function CreateGrafikHasilKepuasanMasyarakat() {
|
||||
type="date"
|
||||
label="Tanggal"
|
||||
placeholder="Masukkan tanggal"
|
||||
defaultValue={stateGrafikKepuasan.create.form.tanggal}
|
||||
value={stateGrafikKepuasan.create.form.tanggal}
|
||||
onChange={(e) => (stateGrafikKepuasan.create.form.tanggal = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Masukkan jenis kelamin"
|
||||
defaultValue={stateGrafikKepuasan.create.form.jenisKelamin}
|
||||
value={stateGrafikKepuasan.create.form.jenisKelamin}
|
||||
onChange={(e) => (stateGrafikKepuasan.create.form.jenisKelamin = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
defaultValue={stateGrafikKepuasan.create.form.alamat}
|
||||
value={stateGrafikKepuasan.create.form.alamat}
|
||||
onChange={(e) => (stateGrafikKepuasan.create.form.alamat = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Penyakit"
|
||||
placeholder="Masukkan penyakit"
|
||||
defaultValue={stateGrafikKepuasan.create.form.penyakit}
|
||||
value={stateGrafikKepuasan.create.form.penyakit}
|
||||
onChange={(e) => (stateGrafikKepuasan.create.form.penyakit = e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -113,7 +135,7 @@ function CreateGrafikHasilKepuasanMasyarakat() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Group, Loader, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -39,7 +39,10 @@ function EditJadwalKegiatan() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<JadwalKegiatanFormBase>(emptyForm());
|
||||
const [originalData, setOriginalData] = useState<JadwalKegiatanFormBase>(emptyForm());
|
||||
|
||||
|
||||
// Helper untuk update nested state
|
||||
const updateNested = <
|
||||
@@ -85,6 +88,19 @@ function EditJadwalKegiatan() {
|
||||
syaratKetentuanJadwalKegiatan: { content: form.syaratKetentuanJadwalKegiatan?.content || '' },
|
||||
dokumenJadwalKegiatan: { content: form.dokumenJadwalKegiatan?.content || '' },
|
||||
});
|
||||
setOriginalData({
|
||||
content: form.content || '',
|
||||
informasiJadwalKegiatan: {
|
||||
name: form.informasiJadwalKegiatan?.name || '',
|
||||
tanggal: form.informasiJadwalKegiatan?.tanggal || '',
|
||||
waktu: form.informasiJadwalKegiatan?.waktu || '',
|
||||
lokasi: form.informasiJadwalKegiatan?.lokasi || '',
|
||||
},
|
||||
deskripsiJadwalKegiatan: { deskripsi: form.deskripsiJadwalKegiatan?.deskripsi || '' },
|
||||
layananJadwalKegiatan: { content: form.layananJadwalKegiatan?.content || '' },
|
||||
syaratKetentuanJadwalKegiatan: { content: form.syaratKetentuanJadwalKegiatan?.content || '' },
|
||||
dokumenJadwalKegiatan: { content: form.dokumenJadwalKegiatan?.content || '' },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading jadwal kegiatan:", error);
|
||||
@@ -94,8 +110,26 @@ function EditJadwalKegiatan() {
|
||||
loadJadwalKegiatan();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
content: originalData.content || '',
|
||||
informasiJadwalKegiatan: {
|
||||
name: originalData.informasiJadwalKegiatan?.name || '',
|
||||
tanggal: originalData.informasiJadwalKegiatan?.tanggal || '',
|
||||
waktu: originalData.informasiJadwalKegiatan?.waktu || '',
|
||||
lokasi: originalData.informasiJadwalKegiatan?.lokasi || '',
|
||||
},
|
||||
deskripsiJadwalKegiatan: { deskripsi: originalData.deskripsiJadwalKegiatan?.deskripsi || '' },
|
||||
layananJadwalKegiatan: { content: originalData.layananJadwalKegiatan?.content || '' },
|
||||
syaratKetentuanJadwalKegiatan: { content: originalData.syaratKetentuanJadwalKegiatan?.content || '' },
|
||||
dokumenJadwalKegiatan: { content: originalData.dokumenJadwalKegiatan?.content || '' },
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
stateJadwalKegiatan.edit.form = { ...stateJadwalKegiatan.edit.form, ...formData };
|
||||
const success = await stateJadwalKegiatan.edit.submit();
|
||||
if (success) {
|
||||
@@ -105,6 +139,8 @@ function EditJadwalKegiatan() {
|
||||
} catch (error) {
|
||||
console.error("Error updating jadwal kegiatan:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data jadwal kegiatan");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -190,6 +226,17 @@ function EditJadwalKegiatan() {
|
||||
|
||||
{/* Submit */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -200,7 +247,7 @@ function EditJadwalKegiatan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -14,12 +15,14 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function CreateJadwalKegiatan() {
|
||||
const stateJadwalKegiatan = useProxy(jadwalKegiatanState);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stateJadwalKegiatan.create.form = {
|
||||
@@ -47,11 +50,18 @@ function CreateJadwalKegiatan() {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
await stateJadwalKegiatan.create.submit();
|
||||
|
||||
toast.success('Data berhasil disimpan');
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await stateJadwalKegiatan.create.submit();
|
||||
toast.success('Data berhasil disimpan');
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Gagal menyimpan data jadwal kegiatan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -84,7 +94,7 @@ function CreateJadwalKegiatan() {
|
||||
<TextInput
|
||||
label="Nama Jadwal Kegiatan"
|
||||
placeholder="Masukkan nama jadwal kegiatan"
|
||||
defaultValue={stateJadwalKegiatan.create.form.content}
|
||||
value={stateJadwalKegiatan.create.form.content}
|
||||
onChange={(e) => {
|
||||
stateJadwalKegiatan.create.form.content = e.target.value;
|
||||
}}
|
||||
@@ -107,7 +117,7 @@ function CreateJadwalKegiatan() {
|
||||
label="Nama"
|
||||
required
|
||||
placeholder="Masukkan nama"
|
||||
defaultValue={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name}
|
||||
value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name}
|
||||
onChange={(e) => {
|
||||
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name = e.target.value;
|
||||
}}
|
||||
@@ -116,7 +126,7 @@ function CreateJadwalKegiatan() {
|
||||
type="date"
|
||||
required
|
||||
label="Tanggal"
|
||||
defaultValue={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal}
|
||||
value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal}
|
||||
onChange={(e) => {
|
||||
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal = e.target.value;
|
||||
}}
|
||||
@@ -125,7 +135,7 @@ function CreateJadwalKegiatan() {
|
||||
label="Waktu"
|
||||
required
|
||||
placeholder="Masukkan waktu"
|
||||
defaultValue={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu}
|
||||
value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu}
|
||||
onChange={(e) => {
|
||||
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu = e.target.value;
|
||||
}}
|
||||
@@ -134,7 +144,7 @@ function CreateJadwalKegiatan() {
|
||||
label="Lokasi"
|
||||
required
|
||||
placeholder="Masukkan lokasi"
|
||||
defaultValue={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi}
|
||||
value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi}
|
||||
onChange={(e) => {
|
||||
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi = e.target.value;
|
||||
}}
|
||||
@@ -172,8 +182,20 @@ function CreateJadwalKegiatan() {
|
||||
</Box>
|
||||
{/* Save Button */}
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
type="submit"
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
@@ -182,7 +204,7 @@ function CreateJadwalKegiatan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Title
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconArrowBack, IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
@@ -27,18 +27,10 @@ import HeaderSearch from '../../../_com/header';
|
||||
import jadwalKegiatanState from '../../../_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
|
||||
function JadwalKegiatan() {
|
||||
const router = useRouter();
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* Tombol Back */}
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors["blue-button"]} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Header Search */}
|
||||
<HeaderSearch
|
||||
title="Jadwal Kegiatan"
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
@@ -17,11 +18,13 @@ import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { convertToISODate } from '../../../lib/dateUtils';
|
||||
|
||||
function EditKelahiran() {
|
||||
const editState = useProxy(persentaseKelahiranKematian.kelahiran);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: '',
|
||||
@@ -30,26 +33,44 @@ function EditKelahiran() {
|
||||
alamat: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
nama: '',
|
||||
tanggal: '',
|
||||
jenisKelamin: '',
|
||||
alamat: '',
|
||||
});
|
||||
|
||||
// Load data saat mount atau params.id berubah
|
||||
useEffect(() => {
|
||||
const loadKelahiran = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
|
||||
try {
|
||||
const data = await editState.edit.load(id);
|
||||
if (data) setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || ''
|
||||
});
|
||||
if (data) {
|
||||
const formattedTanggal = convertToISODate(data.tanggal);
|
||||
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: formattedTanggal,
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || ''
|
||||
});
|
||||
|
||||
setOriginalData({
|
||||
nama: data.nama || '',
|
||||
tanggal: formattedTanggal,
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || ''
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading data kelahiran:', error);
|
||||
toast.error('Gagal memuat data kelahiran');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
loadKelahiran();
|
||||
}, [params?.id]);
|
||||
|
||||
@@ -57,8 +78,20 @@ function EditKelahiran() {
|
||||
setFormData((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
nama: originalData.nama,
|
||||
tanggal: originalData.tanggal,
|
||||
jenisKelamin: originalData.jenisKelamin,
|
||||
alamat: originalData.alamat,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Update global state hanya saat submit
|
||||
editState.edit.form = { ...editState.edit.form, ...formData };
|
||||
await editState.edit.update();
|
||||
@@ -67,6 +100,8 @@ function EditKelahiran() {
|
||||
} catch (error) {
|
||||
console.error('Error updating data kelahiran:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data kelahiran');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,6 +158,17 @@ function EditKelahiran() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -133,7 +179,7 @@ function EditKelahiran() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -13,13 +14,15 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function CreateKelahiran() {
|
||||
const createState = useProxy(persentaseKelahiranKematian.kelahiran);
|
||||
const router = useRouter();
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
createState.create.form = {
|
||||
@@ -32,11 +35,19 @@ function CreateKelahiran() {
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await createState.create.create();
|
||||
resetForm();
|
||||
router.push(
|
||||
'/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran'
|
||||
);
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await createState.create.create();
|
||||
resetForm();
|
||||
router.push(
|
||||
'/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error creating kelahiran:', error);
|
||||
toast.error('Gagal menambahkan data kelahiran');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -71,7 +82,7 @@ function CreateKelahiran() {
|
||||
<TextInput
|
||||
label={<Text fw="bold" fz="sm">Nama</Text>}
|
||||
placeholder="Masukkan nama"
|
||||
defaultValue={createState.create.form.nama}
|
||||
value={createState.create.form.nama}
|
||||
onChange={(e) => (createState.create.form.nama = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -79,27 +90,38 @@ function CreateKelahiran() {
|
||||
type="date"
|
||||
label={<Text fw="bold" fz="sm">Tanggal</Text>}
|
||||
placeholder="Masukkan tanggal"
|
||||
defaultValue={createState.create.form.tanggal}
|
||||
value={createState.create.form.tanggal}
|
||||
onChange={(e) => (createState.create.form.tanggal = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw="bold" fz="sm">Jenis Kelamin</Text>}
|
||||
placeholder="Masukkan jenis kelamin"
|
||||
defaultValue={createState.create.form.jenisKelamin}
|
||||
value={createState.create.form.jenisKelamin}
|
||||
onChange={(e) => (createState.create.form.jenisKelamin = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw="bold" fz="sm">Alamat</Text>}
|
||||
placeholder="Masukkan alamat"
|
||||
defaultValue={createState.create.form.alamat}
|
||||
value={createState.create.form.alamat}
|
||||
onChange={(e) => (createState.create.form.alamat = e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -110,7 +132,7 @@ function CreateKelahiran() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -19,11 +20,13 @@ import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { convertToISODate } from '../../../lib/dateUtils';
|
||||
|
||||
function EditKematian() {
|
||||
const editState = useProxy(persentaseKelahiranKematian.kematian);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: '',
|
||||
@@ -33,6 +36,14 @@ function EditKematian() {
|
||||
penyebab: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
nama: '',
|
||||
tanggal: '',
|
||||
jenisKelamin: '',
|
||||
alamat: '',
|
||||
penyebab: '',
|
||||
});
|
||||
|
||||
// Load data saat mount
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
@@ -42,12 +53,22 @@ function EditKematian() {
|
||||
try {
|
||||
const data = await editState.edit.load(id);
|
||||
if (data) {
|
||||
const formattedTanggal = convertToISODate(data.tanggal);
|
||||
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
tanggal: data.tanggal || '',
|
||||
tanggal: formattedTanggal,
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyebab: data.penyebab || '',
|
||||
penyebab: data.penyebab || ''
|
||||
});
|
||||
|
||||
setOriginalData({
|
||||
nama: data.nama || '',
|
||||
tanggal: formattedTanggal,
|
||||
jenisKelamin: data.jenisKelamin || '',
|
||||
alamat: data.alamat || '',
|
||||
penyebab: data.penyebab || ''
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -63,8 +84,20 @@ function EditKematian() {
|
||||
setFormData(prev => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
nama: originalData.nama,
|
||||
tanggal: originalData.tanggal,
|
||||
jenisKelamin: originalData.jenisKelamin,
|
||||
alamat: originalData.alamat,
|
||||
penyebab: originalData.penyebab,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Update global state saat submit
|
||||
editState.edit.form = { ...editState.edit.form, ...formData };
|
||||
await editState.edit.update();
|
||||
@@ -75,6 +108,8 @@ function EditKematian() {
|
||||
} catch (error) {
|
||||
console.error('Error updating data kematian:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data kematian');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,6 +179,17 @@ function EditKematian() {
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -154,7 +200,7 @@ function EditKematian() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
@@ -13,18 +14,14 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function CreateKematian() {
|
||||
const createState = useProxy(persentaseKelahiranKematian.kematian);
|
||||
const router = useRouter();
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
createState.create.form = {
|
||||
@@ -38,19 +35,27 @@ function CreateKematian() {
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!createState.create.form.nama) {
|
||||
return toast.warn('Nama wajib diisi');
|
||||
}
|
||||
if (!createState.create.form.tanggal) {
|
||||
return toast.warn('Tanggal wajib diisi');
|
||||
}
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!createState.create.form.nama) {
|
||||
return toast.warn('Nama wajib diisi');
|
||||
}
|
||||
if (!createState.create.form.tanggal) {
|
||||
return toast.warn('Tanggal wajib diisi');
|
||||
}
|
||||
|
||||
|
||||
await createState.create.create();
|
||||
resetForm();
|
||||
router.push(
|
||||
'/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian'
|
||||
);
|
||||
await createState.create.create();
|
||||
resetForm();
|
||||
router.push(
|
||||
'/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kematian'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error creating data kematian:', error);
|
||||
toast.error('Gagal menambahkan data kematian');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -80,7 +85,7 @@ function CreateKematian() {
|
||||
<TextInput
|
||||
label="Nama"
|
||||
placeholder="Masukkan nama"
|
||||
defaultValue={createState.create.form.nama}
|
||||
value={createState.create.form.nama}
|
||||
onChange={(e) => (createState.create.form.nama = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -88,21 +93,21 @@ function CreateKematian() {
|
||||
type="date"
|
||||
label="Tanggal"
|
||||
placeholder="Masukkan tanggal"
|
||||
defaultValue={createState.create.form.tanggal}
|
||||
value={createState.create.form.tanggal}
|
||||
onChange={(e) => (createState.create.form.tanggal = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Jenis Kelamin"
|
||||
placeholder="Masukkan jenis kelamin"
|
||||
defaultValue={createState.create.form.jenisKelamin}
|
||||
value={createState.create.form.jenisKelamin}
|
||||
onChange={(e) => (createState.create.form.jenisKelamin = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
defaultValue={createState.create.form.alamat}
|
||||
value={createState.create.form.alamat}
|
||||
onChange={(e) => (createState.create.form.alamat = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -120,6 +125,17 @@ function CreateKematian() {
|
||||
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -130,7 +146,7 @@ function CreateKematian() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export const convertToISODate = (dateString: string): string => {
|
||||
if (!dateString) return '';
|
||||
|
||||
// Jika format dd/mm/yyyy
|
||||
const parts = dateString.split('/');
|
||||
if (parts.length === 3 && parts[0].length === 2 && parts[1].length === 2 && parts[2].length === 4) {
|
||||
const [day, month, year] = parts;
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
// Jika sudah format YYYY-MM-DD, biarkan
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
|
||||
return dateString;
|
||||
}
|
||||
|
||||
// Jika format lain, coba parse dengan Date
|
||||
const date = new Date(dateString);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toISOString().split('T')[0]; // YYYY-MM-DD
|
||||
}
|
||||
|
||||
console.warn(`Format tanggal tidak dikenali: ${dateString}`);
|
||||
return '';
|
||||
};
|
||||
@@ -5,10 +5,12 @@ import infoWabahPenyakit from '@/app/admin/(dashboard)/_state/kesehatan/info-wab
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -26,13 +28,22 @@ function EditInfoWabahPenyakit() {
|
||||
const infoWabahPenyakitState = useProxy(infoWabahPenyakit);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
deskripsiSingkat: '',
|
||||
deskripsi: '',
|
||||
deskripsiLengkap: '',
|
||||
imageId: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
deskripsiSingkat: '',
|
||||
deskripsiLengkap: '',
|
||||
imageId: '',
|
||||
imageUrl: ''
|
||||
});
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
@@ -53,9 +64,16 @@ function EditInfoWabahPenyakit() {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
deskripsiSingkat: data.deskripsiSingkat || '',
|
||||
deskripsi: data.deskripsiLengkap || '',
|
||||
deskripsiLengkap: data.deskripsiLengkap || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
deskripsiSingkat: data.deskripsiSingkat || '',
|
||||
deskripsiLengkap: data.deskripsiLengkap || '',
|
||||
imageId: data.imageId || '',
|
||||
imageUrl: data.image?.link || '',
|
||||
});
|
||||
|
||||
if (data.image?.link) setPreviewImage(data.image.link);
|
||||
}
|
||||
@@ -70,6 +88,7 @@ function EditInfoWabahPenyakit() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
let uploadedImageId = formData.imageId;
|
||||
|
||||
// Upload file kalau ada
|
||||
@@ -86,7 +105,7 @@ function EditInfoWabahPenyakit() {
|
||||
...infoWabahPenyakitState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsiSingkat: formData.deskripsiSingkat,
|
||||
deskripsiLengkap: formData.deskripsi,
|
||||
deskripsiLengkap: formData.deskripsiLengkap,
|
||||
imageId: uploadedImageId,
|
||||
};
|
||||
|
||||
@@ -96,9 +115,23 @@ function EditInfoWabahPenyakit() {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui info wabah penyakit');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
deskripsiSingkat: originalData.deskripsiSingkat,
|
||||
deskripsiLengkap: originalData.deskripsiLengkap,
|
||||
imageId: originalData.imageId,
|
||||
});
|
||||
setPreviewImage(originalData.imageUrl || null);
|
||||
setFile(null);
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleDrop = (files: File[]) => {
|
||||
const selectedFile = files[0];
|
||||
if (selectedFile) {
|
||||
@@ -149,11 +182,11 @@ function EditInfoWabahPenyakit() {
|
||||
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold">
|
||||
Deskripsi
|
||||
Deskripsi Lengkap
|
||||
</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => updateField('deskripsi', val)}
|
||||
value={formData.deskripsiLengkap}
|
||||
onChange={(val) => updateField('deskripsiLengkap', val)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -165,7 +198,7 @@ function EditInfoWabahPenyakit() {
|
||||
onDrop={handleDrop}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={200} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
@@ -190,7 +223,7 @@ function EditInfoWabahPenyakit() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -203,11 +236,40 @@ function EditInfoWabahPenyakit() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -218,7 +280,7 @@ function EditInfoWabahPenyakit() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -26,6 +28,7 @@ function CreateInfoWabahPenyakit() {
|
||||
const infoWabahPenyakitState = useProxy(infoWabahPenyakit)
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
infoWabahPenyakitState.create.form = {
|
||||
@@ -39,25 +42,33 @@ function CreateInfoWabahPenyakit() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!file) {
|
||||
return toast.warn("Pilih file gambar terlebih dahulu");
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
return toast.warn("Pilih file gambar terlebih dahulu");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
infoWabahPenyakitState.create.form.imageId = uploaded.id;
|
||||
await infoWabahPenyakitState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/info-wabah-penyakit")
|
||||
} catch (error) {
|
||||
console.error("Error creating info wabah penyakit:", error);
|
||||
toast.error("Gagal menambahkan info wabah penyakit");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
infoWabahPenyakitState.create.form.imageId = uploaded.id;
|
||||
await infoWabahPenyakitState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/info-wabah-penyakit")
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -88,7 +99,7 @@ function CreateInfoWabahPenyakit() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={infoWabahPenyakitState.create.form.name}
|
||||
value={infoWabahPenyakitState.create.form.name}
|
||||
onChange={(val) => {
|
||||
infoWabahPenyakitState.create.form.name = val.target.value;
|
||||
}}
|
||||
@@ -129,7 +140,7 @@ function CreateInfoWabahPenyakit() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
@@ -154,7 +165,7 @@ function CreateInfoWabahPenyakit() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -167,11 +178,40 @@ function CreateInfoWabahPenyakit() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -182,7 +222,7 @@ function CreateInfoWabahPenyakit() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client';
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import kontakDarurat from '@/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -28,12 +31,20 @@ function EditKontakDarurat() {
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
whatsapp: '',
|
||||
});
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
whatsapp: '',
|
||||
imageUrl: '',
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Load data sekali saat mount
|
||||
@@ -51,6 +62,13 @@ function EditKontakDarurat() {
|
||||
imageId: data.imageId || '',
|
||||
whatsapp: data.whatsapp || '',
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
whatsapp: data.whatsapp || '',
|
||||
imageUrl: data.image?.link || '',
|
||||
});
|
||||
if (data?.image?.link) setPreviewImage(data.image.link);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -62,10 +80,11 @@ function EditKontakDarurat() {
|
||||
};
|
||||
|
||||
loadKontakDarurat();
|
||||
}, [params?.id, kontakDaruratState.edit]);
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
let imageId = formData.imageId;
|
||||
|
||||
// Upload file baru jika ada
|
||||
@@ -89,9 +108,23 @@ function EditKontakDarurat() {
|
||||
} catch (error) {
|
||||
console.error("Error updating kontak darurat:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui kontak darurat");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
deskripsi: originalData.deskripsi,
|
||||
imageId: originalData.imageId,
|
||||
whatsapp: originalData.whatsapp,
|
||||
});
|
||||
setPreviewImage(originalData.imageUrl || null);
|
||||
setFile(null);
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
if (loading) return <Text>Loading...</Text>;
|
||||
|
||||
return (
|
||||
@@ -151,7 +184,7 @@ function EditKontakDarurat() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={200} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
@@ -172,7 +205,7 @@ function EditKontakDarurat() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -185,11 +218,40 @@ function EditKontakDarurat() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -200,7 +262,7 @@ function EditKontakDarurat() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -31,6 +33,7 @@ function CreateKontakDarurat() {
|
||||
const kontakDaruratState = useProxy(kontakDarurat);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
kontakDaruratState.create.form = {
|
||||
@@ -44,26 +47,34 @@ function CreateKontakDarurat() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!file) {
|
||||
return toast.warn('Pilih file gambar terlebih dahulu');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
return toast.warn('Pilih file gambar terlebih dahulu');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
kontakDaruratState.create.form.imageId = uploaded.id;
|
||||
|
||||
await kontakDaruratState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/kontak-darurat');
|
||||
} catch (error) {
|
||||
console.error('Error creating kontak darurat:', error);
|
||||
toast.error('Gagal menambahkan kontak darurat');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
kontakDaruratState.create.form.imageId = uploaded.id;
|
||||
|
||||
await kontakDaruratState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/kontak-darurat');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -94,7 +105,7 @@ function CreateKontakDarurat() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={kontakDaruratState.create.form.name}
|
||||
value={kontakDaruratState.create.form.name}
|
||||
onChange={(val) => {
|
||||
kontakDaruratState.create.form.name = val.target.value;
|
||||
}}
|
||||
@@ -105,7 +116,7 @@ function CreateKontakDarurat() {
|
||||
|
||||
<TextInput
|
||||
type='number'
|
||||
defaultValue={kontakDaruratState.create.form.whatsapp}
|
||||
value={kontakDaruratState.create.form.whatsapp}
|
||||
onChange={(val) => {
|
||||
kontakDaruratState.create.form.whatsapp = val.target.value;
|
||||
}}
|
||||
@@ -136,7 +147,7 @@ function CreateKontakDarurat() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group
|
||||
justify="center"
|
||||
@@ -178,7 +189,7 @@ function CreateKontakDarurat() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -191,11 +202,40 @@ function CreateKontakDarurat() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -206,7 +246,7 @@ function CreateKontakDarurat() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -6,10 +6,12 @@ import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penangan
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -27,6 +29,7 @@ function EditPenangananDarurat() {
|
||||
const penangananDaruratState = useProxy(penangananDarurat)
|
||||
const router = useRouter();
|
||||
const params = useParams()
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
@@ -34,6 +37,13 @@ function EditPenangananDarurat() {
|
||||
imageId: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
imageUrl: '',
|
||||
});
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -53,6 +63,13 @@ function EditPenangananDarurat() {
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
imageUrl: data.image?.link || '',
|
||||
});
|
||||
|
||||
if (data.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
@@ -80,8 +97,20 @@ function EditPenangananDarurat() {
|
||||
setPreviewImage(URL.createObjectURL(selected));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
deskripsi: originalData.deskripsi,
|
||||
imageId: originalData.imageId,
|
||||
});
|
||||
setPreviewImage(originalData.imageUrl || null);
|
||||
setFile(null);
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
let imageId = formData.imageId;
|
||||
|
||||
if (file) {
|
||||
@@ -107,6 +136,8 @@ function EditPenangananDarurat() {
|
||||
} catch (err) {
|
||||
console.error("Error updating penanganan darurat:", err);
|
||||
toast.error("Gagal memperbarui data penanganan darurat");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -156,7 +187,7 @@ function EditPenangananDarurat() {
|
||||
onDrop={handleDrop}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
@@ -181,7 +212,7 @@ function EditPenangananDarurat() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos="relative">
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -194,11 +225,40 @@ function EditPenangananDarurat() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -209,7 +269,7 @@ function EditPenangananDarurat() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -30,6 +32,7 @@ function CreatePenangananDarurat() {
|
||||
const router = useRouter();
|
||||
const penangananDaruratState = useProxy(penangananDarurat);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
const resetForm = () => {
|
||||
@@ -43,26 +46,34 @@ function CreatePenangananDarurat() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!file) {
|
||||
return toast.warn('Pilih file gambar terlebih dahulu');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
return toast.warn('Pilih file gambar terlebih dahulu');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
penangananDaruratState.create.form.imageId = uploaded.id;
|
||||
|
||||
await penangananDaruratState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/penanganan-darurat');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Gagal menambahkan penanganan darurat');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
penangananDaruratState.create.form.imageId = uploaded.id;
|
||||
|
||||
await penangananDaruratState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/penanganan-darurat');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -96,7 +107,7 @@ function CreatePenangananDarurat() {
|
||||
<TextInput
|
||||
label={<Text fw="bold" fz="sm">Judul</Text>}
|
||||
placeholder="Masukkan judul"
|
||||
defaultValue={penangananDaruratState.create.form.name}
|
||||
value={penangananDaruratState.create.form.name}
|
||||
onChange={(val) => {
|
||||
penangananDaruratState.create.form.name = val.target.value;
|
||||
}}
|
||||
@@ -128,7 +139,7 @@ function CreatePenangananDarurat() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group
|
||||
justify="center"
|
||||
@@ -170,7 +181,7 @@ function CreatePenangananDarurat() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -183,6 +194,24 @@ function CreatePenangananDarurat() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@@ -190,6 +219,17 @@ function CreatePenangananDarurat() {
|
||||
|
||||
{/* Button Simpan */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -200,7 +240,7 @@ function CreatePenangananDarurat() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -6,10 +6,12 @@ import posyandustate from '@/app/admin/(dashboard)/_state/kesehatan/posyandu/pos
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -30,6 +32,7 @@ function EditPosyandu() {
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
nomor: '',
|
||||
@@ -37,6 +40,14 @@ function EditPosyandu() {
|
||||
imageId: '',
|
||||
jadwalPelayanan: '',
|
||||
});
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: "",
|
||||
nomor: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
jadwalPelayanan: "",
|
||||
imageUrl: ""
|
||||
});
|
||||
|
||||
// Load data posyandu
|
||||
useEffect(() => {
|
||||
@@ -54,6 +65,14 @@ function EditPosyandu() {
|
||||
imageId: data.imageId || '',
|
||||
jadwalPelayanan: data.jadwalPelayanan || '',
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
nomor: data.nomor || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
jadwalPelayanan: data.jadwalPelayanan || '',
|
||||
imageUrl: data.image?.link || '',
|
||||
});
|
||||
if (data?.image?.link) setPreviewImage(data.image.link);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -66,7 +85,7 @@ function EditPosyandu() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// Update global state hanya saat submit
|
||||
setIsSubmitting(true);
|
||||
const updatedForm = { ...statePosyandu.edit.form, ...formData };
|
||||
|
||||
// Upload file jika ada
|
||||
@@ -86,9 +105,24 @@ function EditPosyandu() {
|
||||
} catch (error) {
|
||||
console.error('Error updating posyandu:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui posyandu');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
nomor: originalData.nomor,
|
||||
deskripsi: originalData.deskripsi,
|
||||
imageId: originalData.imageId,
|
||||
jadwalPelayanan: originalData.jadwalPelayanan,
|
||||
});
|
||||
setPreviewImage(originalData.imageUrl || null);
|
||||
setFile(null);
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Tombol Back */}
|
||||
@@ -126,7 +160,7 @@ function EditPosyandu() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid, gunakan format gambar')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
radius="md"
|
||||
p="xl"
|
||||
>
|
||||
@@ -145,25 +179,45 @@ function EditPosyandu() {
|
||||
Seret gambar atau klik untuk memilih file
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
Maksimal 5MB, format gambar wajib
|
||||
Maksimal 5MB, format gambar .png, .jpg, .jpeg, webp
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Box mt="sm" pos={"relative"} style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
radius="md"
|
||||
style={{
|
||||
maxHeight: 220,
|
||||
maxHeight: 200,
|
||||
objectFit: 'contain',
|
||||
border: `1px solid ${colors['blue-button']}`,
|
||||
border: '1px solid #ddd',
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
{/* Tombol hapus (pojok kanan atas) */}
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@@ -209,6 +263,17 @@ function EditPosyandu() {
|
||||
|
||||
{/* Tombol Submit */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -219,7 +284,7 @@ function EditPosyandu() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -27,6 +29,7 @@ function CreatePosyandu() {
|
||||
const router = useRouter();
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
|
||||
const resetForm = () => {
|
||||
@@ -43,35 +46,31 @@ function CreatePosyandu() {
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!file) {
|
||||
return toast.warn('Silakan pilih file gambar terlebih dahulu');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
return toast.warn('Silakan 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');
|
||||
}
|
||||
statePosyandu.create.form.imageId = uploaded.id;
|
||||
await statePosyandu.create.create();
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/posyandu');
|
||||
} catch (error) {
|
||||
console.error('Error creating posyandu:', error);
|
||||
toast.error('Gagal menambahkan posyandu');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
|
||||
// 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');
|
||||
}
|
||||
|
||||
|
||||
statePosyandu.create.form.imageId = uploaded.id;
|
||||
|
||||
|
||||
await statePosyandu.create.create();
|
||||
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/posyandu');
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
@@ -109,7 +108,7 @@ function CreatePosyandu() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid, gunakan format gambar')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
radius="md"
|
||||
p="xl"
|
||||
>
|
||||
@@ -131,7 +130,7 @@ function CreatePosyandu() {
|
||||
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ textAlign: 'center' }}>
|
||||
<Box pos={"relative"} mt="sm" style={{ textAlign: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
@@ -143,6 +142,24 @@ function CreatePosyandu() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@@ -152,14 +169,14 @@ function CreatePosyandu() {
|
||||
<TextInput
|
||||
label="Nama Posyandu"
|
||||
placeholder="Masukkan nama posyandu"
|
||||
defaultValue={statePosyandu.create.form.name || ''}
|
||||
value={statePosyandu.create.form.name || ''}
|
||||
onChange={(e) => (statePosyandu.create.form.name = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Telepon Posyandu"
|
||||
placeholder="Masukkan telepon posyandu"
|
||||
defaultValue={statePosyandu.create.form.nomor || ''}
|
||||
value={statePosyandu.create.form.nomor || ''}
|
||||
onChange={(e) => (statePosyandu.create.form.nomor = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -189,6 +206,17 @@ function CreatePosyandu() {
|
||||
|
||||
{/* Button */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -199,7 +227,7 @@ function CreatePosyandu() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -5,10 +5,12 @@ import programKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/program-k
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -29,12 +31,20 @@ function EditProgramKesehatan() {
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
deskripsiSingkat: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
});
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
deskripsiSingkat: '',
|
||||
deskripsi: '',
|
||||
imageId: '',
|
||||
imageUrl: ''
|
||||
});
|
||||
|
||||
// Load data awal
|
||||
useEffect(() => {
|
||||
@@ -52,6 +62,13 @@ function EditProgramKesehatan() {
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
deskripsiSingkat: data.deskripsiSingkat || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
imageUrl: data.image?.link || '',
|
||||
});
|
||||
|
||||
if (data?.image?.link) setPreviewImage(data.image.link);
|
||||
} catch (err) {
|
||||
@@ -68,9 +85,22 @@ function EditProgramKesehatan() {
|
||||
setFormData((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
deskripsiSingkat: originalData.deskripsiSingkat,
|
||||
deskripsi: originalData.deskripsi,
|
||||
imageId: originalData.imageId,
|
||||
});
|
||||
setPreviewImage(originalData.imageUrl || null);
|
||||
setFile(null);
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
// Submit form
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const updatedForm = { ...programKesehatanState.edit.form, ...formData };
|
||||
|
||||
// Upload file kalau ada
|
||||
@@ -89,6 +119,8 @@ function EditProgramKesehatan() {
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui program kesehatan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -158,7 +190,7 @@ function EditProgramKesehatan() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
@@ -183,7 +215,7 @@ function EditProgramKesehatan() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -196,11 +228,40 @@ function EditProgramKesehatan() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -211,7 +272,7 @@ function EditProgramKesehatan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -26,6 +28,7 @@ function CreateProgramKesehatan() {
|
||||
const programKesehatanState = useProxy(programKesehatan);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
programKesehatanState.create.form = {
|
||||
@@ -45,25 +48,33 @@ function CreateProgramKesehatan() {
|
||||
if (!programKesehatanState.create.form.deskripsiSingkat) {
|
||||
return toast.warn("Deskripsi singkat wajib diisi");
|
||||
}
|
||||
if (!file) {
|
||||
return toast.warn("Pilih file gambar terlebih dahulu");
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
return toast.warn("Pilih file gambar terlebih dahulu");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
programKesehatanState.create.form.imageId = uploaded.id;
|
||||
await programKesehatanState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/program-kesehatan");
|
||||
} catch (error) {
|
||||
console.error("Error creating program kesehatan:", error);
|
||||
toast.error("Gagal menambahkan program kesehatan");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
programKesehatanState.create.form.imageId = uploaded.id;
|
||||
await programKesehatanState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/program-kesehatan");
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -89,7 +100,7 @@ function CreateProgramKesehatan() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={programKesehatanState.create.form.name}
|
||||
value={programKesehatanState.create.form.name}
|
||||
onChange={(val) => {
|
||||
programKesehatanState.create.form.name = val.target.value;
|
||||
}}
|
||||
@@ -136,7 +147,7 @@ function CreateProgramKesehatan() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
@@ -161,7 +172,7 @@ function CreateProgramKesehatan() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -174,11 +185,40 @@ function CreateProgramKesehatan() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -189,7 +229,7 @@ function CreateProgramKesehatan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
@@ -5,10 +6,12 @@ import puskesmasState from '@/app/admin/(dashboard)/_state/kesehatan/puskesmas/p
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -52,6 +55,7 @@ function EditPuskesmas() {
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<PuskesmasFormData>({
|
||||
name: '',
|
||||
alamat: '',
|
||||
@@ -59,6 +63,14 @@ function EditPuskesmas() {
|
||||
kontak: { kontakPuskesmas: '', email: '', facebook: '', kontakUGD: '' },
|
||||
imageId: '',
|
||||
});
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
alamat: '',
|
||||
jam: { workDays: '', weekDays: '', holiday: '' },
|
||||
kontak: { kontakPuskesmas: '', email: '', facebook: '', kontakUGD: '' },
|
||||
imageId: '',
|
||||
imageUrl: ''
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadPuskesmas = async () => {
|
||||
@@ -85,6 +97,24 @@ function EditPuskesmas() {
|
||||
},
|
||||
imageId: form.imageId,
|
||||
});
|
||||
setOriginalData({
|
||||
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,
|
||||
imageUrl: (form as any).image?.link
|
||||
|
||||
});
|
||||
|
||||
const formWithImage = form as PuskesmasFormData;
|
||||
if (formWithImage.image?.link) {
|
||||
@@ -99,8 +129,31 @@ function EditPuskesmas() {
|
||||
loadPuskesmas();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
alamat: originalData.alamat,
|
||||
jam: {
|
||||
workDays: originalData.jam.workDays,
|
||||
weekDays: originalData.jam.weekDays,
|
||||
holiday: originalData.jam.holiday,
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: originalData.kontak.kontakPuskesmas,
|
||||
email: originalData.kontak.email,
|
||||
facebook: originalData.kontak.facebook,
|
||||
kontakUGD: originalData.kontak.kontakUGD,
|
||||
},
|
||||
imageId: originalData.imageId,
|
||||
});
|
||||
setPreviewImage(originalData.imageUrl || null);
|
||||
setFile(null);
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
statePuskesmas.edit.form = {
|
||||
...statePuskesmas.edit.form,
|
||||
name: formData.name,
|
||||
@@ -130,6 +183,8 @@ function EditPuskesmas() {
|
||||
} catch (error) {
|
||||
console.error("Error updating puskesmas:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data puskesmas");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -252,7 +307,7 @@ function EditPuskesmas() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={200} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
@@ -276,7 +331,7 @@ function EditPuskesmas() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -289,11 +344,41 @@ function EditPuskesmas() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -303,9 +388,8 @@ function EditPuskesmas() {
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
loading={statePuskesmas.edit.loading}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -25,6 +27,7 @@ function CreatePuskesmas() {
|
||||
const router = useRouter();
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
statePuskesmas.create.form = {
|
||||
@@ -50,35 +53,43 @@ function CreatePuskesmas() {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!file) {
|
||||
return toast.warn('Pilih file gambar terlebih dahulu');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!file) {
|
||||
return toast.warn('Pilih file gambar terlebih dahulu');
|
||||
}
|
||||
|
||||
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;
|
||||
await statePuskesmas.create.submit();
|
||||
|
||||
toast.success('Data berhasil disimpan');
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/puskesmas');
|
||||
} catch (error) {
|
||||
console.error('Error creating posyandu:', error);
|
||||
toast.error('Terjadi kesalahan saat membuat posyandu');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
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;
|
||||
await statePuskesmas.create.submit();
|
||||
|
||||
toast.success('Data berhasil disimpan');
|
||||
resetForm();
|
||||
router.push('/admin/kesehatan/puskesmas');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md" component="form" onSubmit={handleSubmit}>
|
||||
{/* Header */}
|
||||
<Group mb="md">
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||
</Button>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Tambah Data Puskesmas
|
||||
</Title>
|
||||
@@ -97,40 +108,40 @@ function CreatePuskesmas() {
|
||||
<TextInput
|
||||
label="Nama Puskesmas"
|
||||
placeholder="Masukkan nama puskesmas"
|
||||
defaultValue={statePuskesmas.create.form.name}
|
||||
value={statePuskesmas.create.form.name}
|
||||
onChange={(e) => (statePuskesmas.create.form.name = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Masukkan alamat"
|
||||
defaultValue={statePuskesmas.create.form.alamat}
|
||||
value={statePuskesmas.create.form.alamat}
|
||||
onChange={(e) => (statePuskesmas.create.form.alamat = e.target.value)}
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
label="Jam Buka"
|
||||
placeholder="Masukkan jam buka"
|
||||
defaultValue={statePuskesmas.create.form.jam.workDays}
|
||||
value={statePuskesmas.create.form.jam.workDays}
|
||||
onChange={(e) => (statePuskesmas.create.form.jam.workDays = e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Jam Tutup"
|
||||
placeholder="Masukkan jam tutup"
|
||||
defaultValue={statePuskesmas.create.form.jam.weekDays}
|
||||
value={statePuskesmas.create.form.jam.weekDays}
|
||||
onChange={(e) => (statePuskesmas.create.form.jam.weekDays = e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Holiday"
|
||||
placeholder="Masukkan hari libur"
|
||||
defaultValue={statePuskesmas.create.form.jam.holiday}
|
||||
value={statePuskesmas.create.form.jam.holiday}
|
||||
onChange={(e) => (statePuskesmas.create.form.jam.holiday = e.target.value)}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Kontak Puskesmas"
|
||||
placeholder="Masukkan kontak puskesmas"
|
||||
defaultValue={statePuskesmas.create.form.kontak.kontakPuskesmas}
|
||||
value={statePuskesmas.create.form.kontak.kontakPuskesmas}
|
||||
onChange={(e) =>
|
||||
(statePuskesmas.create.form.kontak.kontakPuskesmas = e.target.value)
|
||||
}
|
||||
@@ -138,19 +149,19 @@ function CreatePuskesmas() {
|
||||
<TextInput
|
||||
label="Email"
|
||||
placeholder="Masukkan email"
|
||||
defaultValue={statePuskesmas.create.form.kontak.email}
|
||||
value={statePuskesmas.create.form.kontak.email}
|
||||
onChange={(e) => (statePuskesmas.create.form.kontak.email = e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Facebook"
|
||||
placeholder="Masukkan facebook"
|
||||
defaultValue={statePuskesmas.create.form.kontak.facebook}
|
||||
value={statePuskesmas.create.form.kontak.facebook}
|
||||
onChange={(e) => (statePuskesmas.create.form.kontak.facebook = e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kontak UGD"
|
||||
placeholder="Masukkan kontak UGD"
|
||||
defaultValue={statePuskesmas.create.form.kontak.kontakUGD}
|
||||
value={statePuskesmas.create.form.kontak.kontakUGD}
|
||||
onChange={(e) => (statePuskesmas.create.form.kontak.kontakUGD = e.target.value)}
|
||||
/>
|
||||
|
||||
@@ -168,7 +179,7 @@ function CreatePuskesmas() {
|
||||
}}
|
||||
onReject={() => toast.error('File tidak valid.')}
|
||||
maxSize={5 * 1024 ** 2}
|
||||
accept={{ 'image/*': [] }}
|
||||
accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={200} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
@@ -193,7 +204,7 @@ function CreatePuskesmas() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm">
|
||||
<Box mt="sm" pos={"relative"}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
@@ -206,14 +217,44 @@ function CreatePuskesmas() {
|
||||
}}
|
||||
loading="lazy"
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="red"
|
||||
radius="xl"
|
||||
size="sm"
|
||||
pos="absolute"
|
||||
top={5}
|
||||
right={5}
|
||||
onClick={() => {
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
}}
|
||||
style={{
|
||||
boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Action Button */}
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
type="submit"
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
@@ -222,7 +263,7 @@ function CreatePuskesmas() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user