Fix QC Kak Inno Admin, Fix QC Keano UI User, Fix QC Pak jun tabel apbdes
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
'use client';
|
||||
import PendapatanAsliDesa from '@/app/admin/(dashboard)/_state/ekonomi/PADesa';
|
||||
import colors from '@/con/colors';
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
MultiSelect,
|
||||
Paper,
|
||||
Skeleton,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
@@ -22,67 +24,120 @@ import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
// ==================== HELPERS ====================
|
||||
const safeStringArray = (arr: any[]): string[] => {
|
||||
if (!Array.isArray(arr)) return [];
|
||||
return arr
|
||||
.filter(item => item != null && item !== '')
|
||||
.map(item => String(item))
|
||||
.filter(item => item.trim() !== '');
|
||||
};
|
||||
|
||||
const createEmptyForm = () => ({
|
||||
tahun: '',
|
||||
pendapatanIds: [] as string[],
|
||||
belanjaIds: [] as string[],
|
||||
pembiayaanIds: [] as string[],
|
||||
});
|
||||
|
||||
// ==================== COMPONENT ====================
|
||||
function EditAPBDesa() {
|
||||
const apbState = useProxy(PendapatanAsliDesa.ApbDesa);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
tahun: '',
|
||||
pendapatanIds: [] as string[],
|
||||
belanjaIds: [] as string[],
|
||||
pembiayaanIds: [] as string[],
|
||||
});
|
||||
const [formData, setFormData] = useState(createEmptyForm());
|
||||
const [originalData, setOriginalData] = useState(createEmptyForm());
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
// Load APB desa by id → hanya update formData, bukan global state
|
||||
// ==================== LOAD DATA ====================
|
||||
useEffect(() => {
|
||||
const loadAPBdesa = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
if (!id) {
|
||||
toast.error('ID tidak valid');
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const data = await apbState.update.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
tahun: String(data.tahun || ''),
|
||||
pendapatanIds: data.pendapatan?.map((p: any) => p.id) || [],
|
||||
belanjaIds: data.belanja?.map((b: any) => b.id) || [],
|
||||
pembiayaanIds: data.pembiayaan?.map((p: any) => p.id) || [],
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
toast.error('Data APB Desa tidak ditemukan');
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading APBdesa:", error);
|
||||
toast.error("Gagal memuat data APBdesa");
|
||||
|
||||
const normalized = {
|
||||
tahun: String(data.tahun || ''),
|
||||
pendapatanIds: safeStringArray(data.pendapatan?.map((p: any) => p.id) || []),
|
||||
belanjaIds: safeStringArray(data.belanja?.map((b: any) => b.id) || []),
|
||||
pembiayaanIds: safeStringArray(data.pembiayaan?.map((p: any) => p.id) || []),
|
||||
};
|
||||
|
||||
setFormData(normalized);
|
||||
setOriginalData(normalized);
|
||||
} catch (err) {
|
||||
console.error('Error loading APBdesa:', err);
|
||||
toast.error('Gagal memuat data APB Desa');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadAPBdesa();
|
||||
}, [params?.id]);
|
||||
|
||||
// ==================== HANDLERS ====================
|
||||
const handleChange = (field: keyof typeof formData, value: any) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData(originalData);
|
||||
toast.info('Form dikembalikan ke data awal');
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// update global state cuma pas submit
|
||||
setIsSubmitting(true);
|
||||
|
||||
if (!formData.tahun.trim()) {
|
||||
toast.warning('Tahun harus diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
apbState.update.form = {
|
||||
...apbState.update.form,
|
||||
tahun: Number(formData.tahun),
|
||||
pendapatanIds: formData.pendapatanIds,
|
||||
belanjaIds: formData.belanjaIds,
|
||||
pembiayaanIds: formData.pembiayaanIds,
|
||||
pendapatanIds: safeStringArray(formData.pendapatanIds),
|
||||
belanjaIds: safeStringArray(formData.belanjaIds),
|
||||
pembiayaanIds: safeStringArray(formData.pembiayaanIds),
|
||||
};
|
||||
|
||||
await apbState.update.update();
|
||||
toast.success("APB Desa berhasil diperbarui!");
|
||||
router.push("/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa");
|
||||
toast.success('APB Desa berhasil diperbarui!');
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa');
|
||||
} catch (error) {
|
||||
console.error("Error updating APBdesa:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui APBdesa");
|
||||
console.error('Error updating APBdesa:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui APB Desa');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== UI ====================
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Stack align="center" py="xl">
|
||||
<Loader size="lg" type="dots" />
|
||||
<Text c="dimmed">Memuat data APB Desa...</Text>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
@@ -114,30 +169,46 @@ function EditAPBDesa() {
|
||||
<TextInput
|
||||
type="number"
|
||||
value={formData.tahun}
|
||||
onChange={(e) => handleChange("tahun", e.target.value)}
|
||||
onChange={(e) => handleChange('tahun', e.target.value)}
|
||||
label={<Text fz="sm" fw="bold">Tahun</Text>}
|
||||
placeholder="Masukkan tahun anggaran"
|
||||
required
|
||||
/>
|
||||
|
||||
{/* Selects */}
|
||||
<SelectPendapatan
|
||||
<SelectAPBItem
|
||||
label="Pendapatan"
|
||||
state={PendapatanAsliDesa.pendapatan}
|
||||
selectedIds={formData.pendapatanIds}
|
||||
onSelectionChange={(ids) => handleChange("pendapatanIds", ids)}
|
||||
onSelectionChange={(ids) => handleChange('pendapatanIds', ids)}
|
||||
/>
|
||||
|
||||
<SelectBelanja
|
||||
<SelectAPBItem
|
||||
label="Belanja"
|
||||
state={PendapatanAsliDesa.belanja}
|
||||
selectedIds={formData.belanjaIds}
|
||||
onSelectionChange={(ids) => handleChange("belanjaIds", ids)}
|
||||
onSelectionChange={(ids) => handleChange('belanjaIds', ids)}
|
||||
/>
|
||||
|
||||
<SelectPembiayaan
|
||||
<SelectAPBItem
|
||||
label="Pembiayaan"
|
||||
state={PendapatanAsliDesa.pembiayaan}
|
||||
selectedIds={formData.pembiayaanIds}
|
||||
onSelectionChange={(ids) => handleChange("pembiayaanIds", ids)}
|
||||
onSelectionChange={(ids) => handleChange('pembiayaanIds', ids)}
|
||||
/>
|
||||
|
||||
{/* Save Button */}
|
||||
<Group justify="right">
|
||||
<Group justify="right" mt="md">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -148,117 +219,75 @@ function EditAPBDesa() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
/* --- Sub Components --- */
|
||||
// ==================== SUB COMPONENT ====================
|
||||
function SelectAPBItem({
|
||||
label,
|
||||
state,
|
||||
selectedIds,
|
||||
onSelectionChange,
|
||||
}: {
|
||||
label: string;
|
||||
state: any;
|
||||
selectedIds: string[];
|
||||
onSelectionChange: (ids: string[]) => void;
|
||||
}) {
|
||||
const proxyState = useProxy(state);
|
||||
|
||||
function SelectPendapatan({
|
||||
selectedIds,
|
||||
onSelectionChange,
|
||||
}: {
|
||||
selectedIds: string[];
|
||||
onSelectionChange: (ids: string[]) => void;
|
||||
}) {
|
||||
const pendapatanState = useProxy(PendapatanAsliDesa.pendapatan);
|
||||
useShallowEffect(() => {
|
||||
proxyState.findMany.load();
|
||||
}, []);
|
||||
|
||||
useShallowEffect(() => {
|
||||
pendapatanState.findMany.load();
|
||||
}, []);
|
||||
const data = proxyState.findMany.data;
|
||||
const isLoading = !data;
|
||||
|
||||
if (!pendapatanState.findMany.data) {
|
||||
return <Skeleton height={38} />;
|
||||
}
|
||||
const options =
|
||||
data
|
||||
?.filter((item: any) => item?.id)
|
||||
.map((item: any) => ({
|
||||
value: String(item.id),
|
||||
label: String(item?.name || '(Tanpa Nama)'),
|
||||
})) || [];
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<MultiSelect
|
||||
label={<Text fz="sm" fw="bold">Pendapatan</Text>}
|
||||
data={pendapatanState.findMany.data.map((p: any) => ({
|
||||
value: p.id,
|
||||
label: p.name,
|
||||
}))}
|
||||
value={selectedIds}
|
||||
onChange={onSelectionChange}
|
||||
searchable
|
||||
clearable
|
||||
placeholder="Pilih pendapatan..."
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold" mb={4}>{label}</Text>
|
||||
<Skeleton height={38} radius="sm" />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function SelectBelanja({
|
||||
selectedIds,
|
||||
onSelectionChange,
|
||||
}: {
|
||||
selectedIds: string[];
|
||||
onSelectionChange: (ids: string[]) => void;
|
||||
}) {
|
||||
const belanjaState = useProxy(PendapatanAsliDesa.belanja);
|
||||
|
||||
useShallowEffect(() => {
|
||||
belanjaState.findMany.load();
|
||||
}, []);
|
||||
|
||||
if (!belanjaState.findMany.data) {
|
||||
return <Skeleton height={38} />;
|
||||
}
|
||||
|
||||
if (options.length === 0) {
|
||||
return (
|
||||
<MultiSelect
|
||||
label={<Text fz="sm" fw="bold">Belanja</Text>}
|
||||
data={belanjaState.findMany.data.map((b: any) => ({
|
||||
value: b.id,
|
||||
label: b.name,
|
||||
}))}
|
||||
value={selectedIds}
|
||||
onChange={onSelectionChange}
|
||||
searchable
|
||||
clearable
|
||||
placeholder="Pilih belanja..."
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
<Alert color="gray" variant="light">
|
||||
<Text size="sm">
|
||||
Tidak ada data {label.toLowerCase()} tersedia.
|
||||
</Text>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
function SelectPembiayaan({
|
||||
selectedIds,
|
||||
onSelectionChange,
|
||||
}: {
|
||||
selectedIds: string[];
|
||||
onSelectionChange: (ids: string[]) => void;
|
||||
}) {
|
||||
const pembiayaanState = useProxy(PendapatanAsliDesa.pembiayaan);
|
||||
|
||||
useShallowEffect(() => {
|
||||
pembiayaanState.findMany.load();
|
||||
}, []);
|
||||
|
||||
if (!pembiayaanState.findMany.data) {
|
||||
return <Skeleton height={38} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
label={<Text fz="sm" fw="bold">Pembiayaan</Text>}
|
||||
data={pembiayaanState.findMany.data.map((p: any) => ({
|
||||
value: p.id,
|
||||
label: p.name,
|
||||
}))}
|
||||
value={selectedIds}
|
||||
onChange={onSelectionChange}
|
||||
searchable
|
||||
clearable
|
||||
placeholder="Pilih pembiayaan..."
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<MultiSelect
|
||||
label={<Text fz="sm" fw="bold">{label}</Text>}
|
||||
data={options}
|
||||
value={selectedIds}
|
||||
onChange={(ids) => onSelectionChange(safeStringArray(ids))}
|
||||
searchable
|
||||
clearable
|
||||
placeholder={`Pilih ${label.toLowerCase()}...`}
|
||||
nothingFoundMessage="Tidak ditemukan"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditAPBDesa;
|
||||
|
||||
@@ -80,7 +80,7 @@ function DetailAPBDesa() {
|
||||
Detail APB Desa
|
||||
</Text>
|
||||
|
||||
<Paper bg={colors['BG-trans']} p="md" radius="md" shadow="xs">
|
||||
<Paper bg="#EEF3FBFF" p="md" radius="md" shadow="xs">
|
||||
<Stack gap="sm">
|
||||
<Box>
|
||||
<Text fz="lg" fw="bold">
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
MultiSelect,
|
||||
Paper,
|
||||
Skeleton,
|
||||
@@ -17,11 +18,14 @@ import {
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
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 CreateAPBDesa() {
|
||||
const apbDesaState = useProxy(PendapatanAsliDesa.ApbDesa);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
apbDesaState.create.form = {
|
||||
@@ -33,9 +37,17 @@ function CreateAPBDesa() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await apbDesaState.create.submit();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await apbDesaState.create.submit();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/apbdesa');
|
||||
} catch (error) {
|
||||
console.error('Error creating APB Desa:', error);
|
||||
toast.error('Gagal membuat APB Desa');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -62,7 +74,7 @@ function CreateAPBDesa() {
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
type="number"
|
||||
defaultValue={apbDesaState.create.form.tahun}
|
||||
value={apbDesaState.create.form.tahun}
|
||||
onChange={(val) => {
|
||||
apbDesaState.create.form.tahun = Number(val.target.value);
|
||||
}}
|
||||
@@ -94,6 +106,17 @@ function CreateAPBDesa() {
|
||||
|
||||
{/* Action */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -104,7 +127,7 @@ function CreateAPBDesa() {
|
||||
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,
|
||||
@@ -22,12 +23,18 @@ function EditBelanja() {
|
||||
const belanjaState = useProxy(PendapatanAsliDesa.belanja);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
value: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
value: '',
|
||||
});
|
||||
|
||||
// format angka ke rupiah
|
||||
const formatRupiah = (value: number | string) => {
|
||||
const number =
|
||||
@@ -58,6 +65,10 @@ function EditBelanja() {
|
||||
name: data.name || '',
|
||||
value: String(data.value || ''),
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
value: String(data.value || ''),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading belanja:", error);
|
||||
@@ -70,6 +81,7 @@ function EditBelanja() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
belanjaState.update.form = {
|
||||
...belanjaState.update.form,
|
||||
name: formData.name,
|
||||
@@ -82,9 +94,19 @@ function EditBelanja() {
|
||||
} catch (error) {
|
||||
console.error("Error updating jenis belanja:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui jenis belanja");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
value: originalData.value,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header */}
|
||||
@@ -135,6 +157,17 @@ function EditBelanja() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -145,7 +178,7 @@ function EditBelanja() {
|
||||
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 CreateBelanja() {
|
||||
const belanjaState = useProxy(PendapatanAsliDesa.belanja);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const formatRupiah = (value: number | string) => {
|
||||
const number =
|
||||
@@ -47,9 +50,17 @@ function CreateBelanja() {
|
||||
return toast.warn('Lengkapi semua field terlebih dahulu');
|
||||
}
|
||||
|
||||
await belanjaState.create.submit();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/belanja');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await belanjaState.create.submit();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/belanja');
|
||||
} catch (error) {
|
||||
console.error('Error creating belanja:', error);
|
||||
toast.error('Gagal menambahkan jenis belanja');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -82,7 +93,7 @@ function CreateBelanja() {
|
||||
<TextInput
|
||||
label={<Text fw="bold" fz="sm">Nama Jenis Belanja</Text>}
|
||||
placeholder="Masukkan nama jenis belanja"
|
||||
defaultValue={belanjaState.create.form.name}
|
||||
value={belanjaState.create.form.name}
|
||||
onChange={(e) => (belanjaState.create.form.name = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -91,7 +102,7 @@ function CreateBelanja() {
|
||||
type="text"
|
||||
label={<Text fw="bold" fz="sm">Nilai</Text>}
|
||||
placeholder="Masukkan nilai belanja"
|
||||
defaultValue={formatRupiah(belanjaState.create.form.value)}
|
||||
value={formatRupiah(belanjaState.create.form.value)}
|
||||
onChange={(e) => {
|
||||
const raw = e.currentTarget.value;
|
||||
belanjaState.create.form.value = unformatRupiah(raw);
|
||||
@@ -100,6 +111,17 @@ function CreateBelanja() {
|
||||
/>
|
||||
|
||||
<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 CreateBelanja() {
|
||||
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,
|
||||
@@ -21,12 +22,18 @@ function EditPembiayaan() {
|
||||
const pembiayaanState = useProxy(PendapatanAsliDesa.pembiayaan);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
value: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
value: '',
|
||||
});
|
||||
|
||||
const formatRupiah = (value: number | string) => {
|
||||
const number =
|
||||
typeof value === 'number'
|
||||
@@ -55,6 +62,10 @@ function EditPembiayaan() {
|
||||
name: data.name || '',
|
||||
value: String(data.value || ''),
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
value: String(data.value || ''),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading pembiayaan:', error);
|
||||
@@ -65,8 +76,17 @@ function EditPembiayaan() {
|
||||
loadPembiayaan();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
value: originalData.value,
|
||||
});
|
||||
toast.info('Form dikembalikan ke data awal');
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
pembiayaanState.update.form = {
|
||||
...pembiayaanState.update.form,
|
||||
name: formData.name,
|
||||
@@ -79,6 +99,8 @@ function EditPembiayaan() {
|
||||
} catch (error) {
|
||||
console.error('Error updating jenis pembiayaan:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui jenis pembiayaan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,6 +154,17 @@ function EditPembiayaan() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -142,7 +175,7 @@ function EditPembiayaan() {
|
||||
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,12 +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 CreatePembiayaan() {
|
||||
const pembiayaanState = useProxy(PendapatanAsliDesa.pembiayaan);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const formatRupiah = (value: number | string) => {
|
||||
const number =
|
||||
@@ -42,13 +45,21 @@ function CreatePembiayaan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!pembiayaanState.create.form.name || !pembiayaanState.create.form.value) {
|
||||
return toast.warn('Nama dan nilai wajib diisi');
|
||||
}
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!pembiayaanState.create.form.name || !pembiayaanState.create.form.value) {
|
||||
return toast.warn('Nama dan nilai wajib diisi');
|
||||
}
|
||||
|
||||
await pembiayaanState.create.submit();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan');
|
||||
await pembiayaanState.create.submit();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/pembiayaan');
|
||||
} catch (error) {
|
||||
console.error('Error creating pembiayaan:', error);
|
||||
toast.error('Gagal menambahkan jenis pembiayaan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -81,7 +92,7 @@ function CreatePembiayaan() {
|
||||
<TextInput
|
||||
label={<Text fw="bold" fz="sm">Nama Jenis Pembiayaan</Text>}
|
||||
placeholder="Masukkan nama jenis pembiayaan"
|
||||
defaultValue={pembiayaanState.create.form.name}
|
||||
value={pembiayaanState.create.form.name}
|
||||
onChange={(e) => {
|
||||
pembiayaanState.create.form.name = e.currentTarget.value;
|
||||
}}
|
||||
@@ -92,7 +103,7 @@ function CreatePembiayaan() {
|
||||
type="text"
|
||||
label={<Text fw="bold" fz="sm">Nilai</Text>}
|
||||
placeholder="Masukkan nilai"
|
||||
defaultValue={formatRupiah(pembiayaanState.create.form.value)}
|
||||
value={formatRupiah(pembiayaanState.create.form.value)}
|
||||
onChange={(e) => {
|
||||
const raw = e.currentTarget.value;
|
||||
pembiayaanState.create.form.value = unformatRupiah(raw);
|
||||
@@ -101,6 +112,17 @@ function CreatePembiayaan() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -111,7 +133,7 @@ function CreatePembiayaan() {
|
||||
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,
|
||||
@@ -21,6 +22,12 @@ function EditPendapatan() {
|
||||
const pendapatanState = useProxy(PendapatanAsliDesa.pendapatan);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: "",
|
||||
value: "",
|
||||
});
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
@@ -55,6 +62,10 @@ function EditPendapatan() {
|
||||
name: data.name ?? '',
|
||||
value: data.value?.toString() ?? '',
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name ?? '',
|
||||
value: data.value?.toString() ?? '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading pendapatan:', error);
|
||||
@@ -72,8 +83,18 @@ function EditPendapatan() {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
value: originalData.value,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
pendapatanState.update.form = {
|
||||
...pendapatanState.update.form,
|
||||
name: formData.name,
|
||||
@@ -86,7 +107,10 @@ function EditPendapatan() {
|
||||
} catch (error) {
|
||||
console.error('Error updating jenis pendapatan:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui jenis pendapatan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -137,6 +161,17 @@ function EditPendapatan() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -147,7 +182,7 @@ function EditPendapatan() {
|
||||
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,
|
||||
TextInput,
|
||||
@@ -12,11 +13,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 CreatePendapatan() {
|
||||
const pendapatanState = useProxy(PendapatanAsliDesa.pendapatan);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const formatRupiah = (value: number | string) => {
|
||||
const number = typeof value === 'number' ? value : Number(value.replace(/\D/g, ''));
|
||||
@@ -39,9 +43,17 @@ function CreatePendapatan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await pendapatanState.create.submit();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/pendapatan');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await pendapatanState.create.submit();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/PADesa-pendapatan-asli-desa/pendapatan');
|
||||
} catch (error) {
|
||||
console.error('Error creating pendapatan:', error);
|
||||
toast.error('Gagal menambahkan jenis pendapatan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -72,7 +84,7 @@ function CreatePendapatan() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={pendapatanState.create.form.name}
|
||||
value={pendapatanState.create.form.name}
|
||||
onChange={(val) => {
|
||||
pendapatanState.create.form.name = val.target.value;
|
||||
}}
|
||||
@@ -83,7 +95,7 @@ function CreatePendapatan() {
|
||||
|
||||
<TextInput
|
||||
type="text"
|
||||
defaultValue={formatRupiah(pendapatanState.create.form.value)}
|
||||
value={formatRupiah(pendapatanState.create.form.value)}
|
||||
onChange={(val) => {
|
||||
const raw = val.currentTarget.value;
|
||||
const cleanValue = unformatRupiah(raw);
|
||||
@@ -95,6 +107,17 @@ function CreatePendapatan() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -105,7 +128,7 @@ function CreatePendapatan() {
|
||||
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,
|
||||
@@ -28,12 +29,17 @@ export default function EditDemografiPekerjaan() {
|
||||
const router = useRouter();
|
||||
const { id } = useParams() as { id: string };
|
||||
const stateDemografi = useProxy(demografiPekerjaan);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
pekerjaan: '',
|
||||
lakiLaki: 0,
|
||||
perempuan: 0,
|
||||
});
|
||||
const [originalData, setOriginalData] = useState<FormData>({
|
||||
pekerjaan: '',
|
||||
lakiLaki: 0,
|
||||
perempuan: 0,
|
||||
});
|
||||
|
||||
// ✅ Load data hanya sekali di awal (tidak reset form)
|
||||
useEffect(() => {
|
||||
@@ -41,6 +47,7 @@ export default function EditDemografiPekerjaan() {
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
stateDemografi.update.id = id;
|
||||
await stateDemografi.findUnique.load(id);
|
||||
|
||||
@@ -51,10 +58,17 @@ export default function EditDemografiPekerjaan() {
|
||||
lakiLaki: Number(data.lakiLaki ?? 0),
|
||||
perempuan: Number(data.perempuan ?? 0),
|
||||
});
|
||||
setOriginalData({
|
||||
pekerjaan: data.pekerjaan ?? '',
|
||||
lakiLaki: Number(data.lakiLaki ?? 0),
|
||||
perempuan: Number(data.perempuan ?? 0),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error);
|
||||
toast.error('Gagal memuat data');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,9 +89,19 @@ export default function EditDemografiPekerjaan() {
|
||||
[]
|
||||
);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
pekerjaan: originalData.pekerjaan,
|
||||
lakiLaki: Number(originalData.lakiLaki),
|
||||
perempuan: Number(originalData.perempuan),
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
// ✅ Submit hanya update global state sekali
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
stateDemografi.update.id = id;
|
||||
stateDemografi.update.form = { ...formData };
|
||||
|
||||
@@ -88,6 +112,8 @@ export default function EditDemografiPekerjaan() {
|
||||
} catch (error) {
|
||||
console.error('Error updating data:', error);
|
||||
toast.error('Gagal memperbarui data');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,6 +171,17 @@ export default function EditDemografiPekerjaan() {
|
||||
/>
|
||||
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -155,7 +192,7 @@ export default function EditDemografiPekerjaan() {
|
||||
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,
|
||||
@@ -17,11 +18,13 @@ import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import demografiPekerjaan from '../../../_state/ekonomi/demografi-pekerjaan';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
function CreateDemografiPekerjaan() {
|
||||
const stateDemografi = useProxy(demografiPekerjaan);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stateDemografi.create.form = {
|
||||
@@ -32,16 +35,23 @@ function CreateDemografiPekerjaan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const id = await stateDemografi.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stateDemografi.findUnique.load(idStr);
|
||||
if (stateDemografi.findUnique.data) {
|
||||
setChartData([stateDemografi.findUnique.data]);
|
||||
try {
|
||||
const id = await stateDemografi.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stateDemografi.findUnique.load(idStr);
|
||||
if (stateDemografi.findUnique.data) {
|
||||
setChartData([stateDemografi.findUnique.data]);
|
||||
}
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/demografi-pekerjaan');
|
||||
} catch (error) {
|
||||
console.error('Error creating demografi pekerjaan:', error);
|
||||
toast.error('Terjadi kesalahan saat menambah demografi pekerjaan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/demografi-pekerjaan');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -74,7 +84,7 @@ function CreateDemografiPekerjaan() {
|
||||
<TextInput
|
||||
label="Pekerjaan"
|
||||
type="text"
|
||||
defaultValue={stateDemografi.create.form.pekerjaan}
|
||||
value={stateDemografi.create.form.pekerjaan}
|
||||
placeholder="Masukkan pekerjaan"
|
||||
onChange={(val) => {
|
||||
stateDemografi.create.form.pekerjaan = val.currentTarget.value;
|
||||
@@ -84,7 +94,7 @@ function CreateDemografiPekerjaan() {
|
||||
<TextInput
|
||||
label="Jumlah Pekerja Laki-Laki"
|
||||
type="number"
|
||||
defaultValue={stateDemografi.create.form.lakiLaki}
|
||||
value={stateDemografi.create.form.lakiLaki}
|
||||
placeholder="Masukkan jumlah pekerja laki-laki"
|
||||
onChange={(val) => {
|
||||
stateDemografi.create.form.lakiLaki = Number(val.currentTarget.value);
|
||||
@@ -94,7 +104,7 @@ function CreateDemografiPekerjaan() {
|
||||
<TextInput
|
||||
label="Jumlah Pekerja Perempuan"
|
||||
type="number"
|
||||
defaultValue={stateDemografi.create.form.perempuan}
|
||||
value={stateDemografi.create.form.perempuan}
|
||||
placeholder="Masukkan jumlah pekerja perempuan"
|
||||
onChange={(val) => {
|
||||
stateDemografi.create.form.perempuan = Number(val.currentTarget.value);
|
||||
@@ -103,6 +113,17 @@ function CreateDemografiPekerjaan() {
|
||||
/>
|
||||
|
||||
<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 +134,7 @@ function CreateDemografiPekerjaan() {
|
||||
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,
|
||||
@@ -22,7 +23,7 @@ function EditJumlahPendudukMiskin() {
|
||||
const router = useRouter();
|
||||
const params = useParams() as { id: string };
|
||||
const stateJPM = useProxy(jumlahPendudukMiskin);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const id = params.id;
|
||||
|
||||
// 🔹 State lokal untuk form
|
||||
@@ -31,6 +32,11 @@ function EditJumlahPendudukMiskin() {
|
||||
totalPoorPopulation: 0,
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
year: 0,
|
||||
totalPoorPopulation: 0,
|
||||
});
|
||||
|
||||
// 🔹 Load data awal dari backend
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
@@ -44,6 +50,10 @@ function EditJumlahPendudukMiskin() {
|
||||
year: data.year || 0,
|
||||
totalPoorPopulation: data.totalPoorPopulation || 0,
|
||||
});
|
||||
setOriginalData({
|
||||
year: data.year || 0,
|
||||
totalPoorPopulation: data.totalPoorPopulation || 0,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Gagal memuat data:', error);
|
||||
@@ -62,9 +72,18 @@ function EditJumlahPendudukMiskin() {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
year: originalData.year,
|
||||
totalPoorPopulation: originalData.totalPoorPopulation,
|
||||
});
|
||||
toast.info('Form dikembalikan ke data awal');
|
||||
};
|
||||
|
||||
// 🔹 Submit form
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
stateJPM.update.id = id;
|
||||
// update global state cuma saat submit
|
||||
stateJPM.update.form = { ...formData };
|
||||
@@ -75,6 +94,8 @@ function EditJumlahPendudukMiskin() {
|
||||
} catch (error) {
|
||||
console.error('Gagal menyimpan data:', error);
|
||||
toast.error('Terjadi kesalahan saat menyimpan data');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,6 +145,17 @@ function EditJumlahPendudukMiskin() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -134,7 +166,7 @@ function EditJumlahPendudukMiskin() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Group, Loader, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import jumlahPendudukMiskin from '../../../_state/ekonomi/jumlah-penduduk-miskin';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export default function CreateJumlahPendudukMiskin() {
|
||||
const stateJPM = useProxy(jumlahPendudukMiskin);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stateJPM.create.form = {
|
||||
@@ -22,16 +24,24 @@ export default function CreateJumlahPendudukMiskin() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const id = await stateJPM.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stateJPM.findUnique.load(idStr);
|
||||
if (stateJPM.findUnique.data) {
|
||||
setChartData([stateJPM.findUnique.data]);
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const id = await stateJPM.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stateJPM.findUnique.load(idStr);
|
||||
if (stateJPM.findUnique.data) {
|
||||
setChartData([stateJPM.findUnique.data]);
|
||||
}
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/jumlah-penduduk-miskin');
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error(error instanceof Error ? error.message : "Gagal menambahkan jumlah penduduk miskin")
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/jumlah-penduduk-miskin');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -59,7 +69,7 @@ export default function CreateJumlahPendudukMiskin() {
|
||||
<TextInput
|
||||
label="Tahun"
|
||||
type="number"
|
||||
defaultValue={stateJPM.create.form.year || ''}
|
||||
value={stateJPM.create.form.year || ''}
|
||||
placeholder="Masukkan tahun"
|
||||
onChange={(e) => {
|
||||
const value = e.currentTarget.value;
|
||||
@@ -71,7 +81,7 @@ export default function CreateJumlahPendudukMiskin() {
|
||||
<TextInput
|
||||
label="Jumlah Penduduk Miskin"
|
||||
type="number"
|
||||
defaultValue={stateJPM.create.form.totalPoorPopulation}
|
||||
value={stateJPM.create.form.totalPoorPopulation}
|
||||
placeholder="Masukkan jumlah penduduk miskin"
|
||||
onChange={(e) => {
|
||||
stateJPM.create.form.totalPoorPopulation = Number(e.currentTarget.value);
|
||||
@@ -80,6 +90,17 @@ export default function CreateJumlahPendudukMiskin() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -90,7 +111,7 @@ export default function CreateJumlahPendudukMiskin() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
'use client';
|
||||
import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Group, Loader, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditGrafikBerdasarkanPendidikan() {
|
||||
@@ -13,6 +14,7 @@ function EditGrafikBerdasarkanPendidikan() {
|
||||
const params = useParams() as { id: string };
|
||||
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanPendidikan);
|
||||
const id = params.id;
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// state lokal untuk form
|
||||
const [formData, setFormData] = useState({
|
||||
@@ -23,6 +25,14 @@ function EditGrafikBerdasarkanPendidikan() {
|
||||
S1: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
SD: '',
|
||||
SMP: '',
|
||||
SMA: '',
|
||||
D3: '',
|
||||
S1: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
stategrafik.findUnique.load(id).then(() => {
|
||||
@@ -35,26 +45,51 @@ function EditGrafikBerdasarkanPendidikan() {
|
||||
D3: data.D3 || '',
|
||||
S1: data.S1 || '',
|
||||
});
|
||||
setOriginalData({
|
||||
SD: data.SD || '',
|
||||
SMP: data.SMP || '',
|
||||
SMA: data.SMA || '',
|
||||
D3: data.D3 || '',
|
||||
S1: data.S1 || '',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const handleChange = (field: keyof typeof formData) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: e.currentTarget.value,
|
||||
}));
|
||||
};
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.currentTarget;
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
SD: originalData.SD,
|
||||
SMP: originalData.SMP,
|
||||
SMA: originalData.SMA,
|
||||
D3: originalData.D3,
|
||||
S1: originalData.S1,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
stategrafik.update.id = id;
|
||||
stategrafik.update.form = { ...formData }; // update global state pas submit aja
|
||||
await stategrafik.update.submit();
|
||||
router.push(
|
||||
'/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan'
|
||||
);
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
stategrafik.update.id = id;
|
||||
stategrafik.update.form = { ...formData }; // update global state pas submit aja
|
||||
await stategrafik.update.submit();
|
||||
router.push(
|
||||
'/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data grafik');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -83,42 +118,58 @@ function EditGrafikBerdasarkanPendidikan() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
name="SD"
|
||||
label="SD"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
value={formData.SD}
|
||||
onChange={handleChange('SD')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextInput
|
||||
name="SMP"
|
||||
label="SMP"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
value={formData.SMP}
|
||||
onChange={handleChange('SMP')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextInput
|
||||
name="SMA"
|
||||
label="SMA"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
value={formData.SMA}
|
||||
onChange={handleChange('SMA')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextInput
|
||||
name="D3"
|
||||
label="D3"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
value={formData.D3}
|
||||
onChange={handleChange('D3')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextInput
|
||||
name="S1"
|
||||
label="S1"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
value={formData.S1}
|
||||
onChange={handleChange('S1')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -129,7 +180,7 @@ function EditGrafikBerdasarkanPendidikan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Loader, Group, Paper, Stack, TextInput, Title } 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 CreateGrafikBerdasarkanPendidikan() {
|
||||
const router = useRouter();
|
||||
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanPendidikan);
|
||||
const [donutData, setDonutData] = useState<any[]>([]);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stategrafik.create.form = {
|
||||
@@ -26,18 +28,26 @@ function CreateGrafikBerdasarkanPendidikan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const id = await stategrafik.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stategrafik.findUnique.load(idStr);
|
||||
if (stategrafik.findUnique.data) {
|
||||
setDonutData([stategrafik.findUnique.data]);
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const id = await stategrafik.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stategrafik.findUnique.load(idStr);
|
||||
if (stategrafik.findUnique.data) {
|
||||
setDonutData([stategrafik.findUnique.data]);
|
||||
}
|
||||
}
|
||||
resetForm();
|
||||
router.push(
|
||||
'/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan'
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Terjadi kesalahan saat menambahkan data grafik');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
resetForm();
|
||||
router.push(
|
||||
'/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -64,7 +74,7 @@ function CreateGrafikBerdasarkanPendidikan() {
|
||||
label="SD"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.SD}
|
||||
value={stategrafik.create.form.SD}
|
||||
onChange={(val) => (stategrafik.create.form.SD = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
@@ -72,7 +82,7 @@ function CreateGrafikBerdasarkanPendidikan() {
|
||||
label="SMP"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.SMP}
|
||||
value={stategrafik.create.form.SMP}
|
||||
onChange={(val) => (stategrafik.create.form.SMP = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
@@ -80,7 +90,7 @@ function CreateGrafikBerdasarkanPendidikan() {
|
||||
label="SMA"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.SMA}
|
||||
value={stategrafik.create.form.SMA}
|
||||
onChange={(val) => (stategrafik.create.form.SMA = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
@@ -88,7 +98,7 @@ function CreateGrafikBerdasarkanPendidikan() {
|
||||
label="D3"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.D3}
|
||||
value={stategrafik.create.form.D3}
|
||||
onChange={(val) => (stategrafik.create.form.D3 = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
@@ -96,12 +106,23 @@ function CreateGrafikBerdasarkanPendidikan() {
|
||||
label="S1"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.S1}
|
||||
value={stategrafik.create.form.S1}
|
||||
onChange={(val) => (stategrafik.create.form.S1 = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<Group justify="right" mt="md">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -112,7 +133,7 @@ function CreateGrafikBerdasarkanPendidikan() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -217,20 +217,22 @@ function ListGrafikBerdasarkanPendidikan({ search }: { search: string }) {
|
||||
<Title order={3} pb={10}>
|
||||
Grafik Pengangguran Berdasarkan Pendidikan
|
||||
</Title>
|
||||
{donutData.length > 0 ? (
|
||||
<DonutChart
|
||||
data={donutData}
|
||||
withLabels
|
||||
withTooltip
|
||||
tooltipDataSource="segment"
|
||||
size={260}
|
||||
thickness={40}
|
||||
/>
|
||||
) : (
|
||||
<Text color="dimmed">
|
||||
Belum ada data untuk ditampilkan dalam grafik
|
||||
</Text>
|
||||
)}
|
||||
<Center>
|
||||
{donutData.length > 0 ? (
|
||||
<DonutChart
|
||||
data={donutData}
|
||||
withLabels
|
||||
withTooltip
|
||||
tooltipDataSource="segment"
|
||||
size={260}
|
||||
thickness={40}
|
||||
/>
|
||||
) : (
|
||||
<Text color="dimmed">
|
||||
Belum ada data untuk ditampilkan dalam grafik
|
||||
</Text>
|
||||
)}
|
||||
</Center>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
TextInput,
|
||||
@@ -25,6 +26,8 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
);
|
||||
const id = params.id;
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// ✅ state lokal, controlled
|
||||
const [formData, setFormData] = useState({
|
||||
usia18_25: '',
|
||||
@@ -33,6 +36,13 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
usia46_keatas: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
usia18_25: '',
|
||||
usia26_35: '',
|
||||
usia36_45: '',
|
||||
usia46_keatas: '',
|
||||
});
|
||||
|
||||
// load data dari global state -> masukin ke local state
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
@@ -45,6 +55,13 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
usia36_45: data.usia36_45 || '',
|
||||
usia46_keatas: data.usia46_keatas || '',
|
||||
});
|
||||
setOriginalData({
|
||||
usia18_25: data.usia18_25 || '',
|
||||
usia26_35: data.usia26_35 || '',
|
||||
usia36_45: data.usia36_45 || '',
|
||||
usia46_keatas: data.usia46_keatas || '',
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -57,8 +74,19 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
usia18_25: originalData.usia18_25,
|
||||
usia26_35: originalData.usia26_35,
|
||||
usia36_45: originalData.usia36_45,
|
||||
usia46_keatas: originalData.usia46_keatas,
|
||||
});
|
||||
toast.info('Form dikembalikan ke data awal');
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// ✅ baru update global state pas submit
|
||||
stategrafik.update.id = id;
|
||||
stategrafik.update.form = { ...formData };
|
||||
@@ -72,6 +100,8 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data grafik');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -134,6 +164,17 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -144,7 +185,7 @@ function EditGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -4,16 +4,18 @@
|
||||
|
||||
import grafikNganggur from '@/app/admin/(dashboard)/_state/ekonomi/usia-kerja-nganggur';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Group, Loader, Paper, Stack, TextInput, Title } 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 CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
const router = useRouter();
|
||||
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur);
|
||||
const [donutData, setDonutData] = useState<any[]>([]);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stategrafik.create.form = {
|
||||
@@ -26,16 +28,24 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const id = await stategrafik.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stategrafik.findUnique.load(idStr);
|
||||
if (stategrafik.findUnique.data) {
|
||||
setDonutData([stategrafik.findUnique.data]);
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const id = await stategrafik.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stategrafik.findUnique.load(idStr);
|
||||
if (stategrafik.findUnique.data) {
|
||||
setDonutData([stategrafik.findUnique.data]);
|
||||
}
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia');
|
||||
} catch (error) {
|
||||
console.error('Error creating:', error);
|
||||
toast.error('Terjadi kesalahan saat membuat data');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -64,7 +74,7 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
label="Usia 18 - 25"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.usia18_25}
|
||||
value={stategrafik.create.form.usia18_25}
|
||||
onChange={(val) => (stategrafik.create.form.usia18_25 = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
@@ -72,7 +82,7 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
label="Usia 26 - 35"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.usia26_35}
|
||||
value={stategrafik.create.form.usia26_35}
|
||||
onChange={(val) => (stategrafik.create.form.usia26_35 = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
@@ -80,7 +90,7 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
label="Usia 36 - 45"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.usia36_45}
|
||||
value={stategrafik.create.form.usia36_45}
|
||||
onChange={(val) => (stategrafik.create.form.usia36_45 = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
@@ -88,13 +98,24 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
label="Usia 46 +"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stategrafik.create.form.usia46_keatas}
|
||||
value={stategrafik.create.form.usia46_keatas}
|
||||
onChange={(val) => (stategrafik.create.form.usia46_keatas = val.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
{/* Submit Button */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -105,7 +126,7 @@ function CreateGrafikBerdasarkanUsiaKerjaYangMenganggur() {
|
||||
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,
|
||||
Text,
|
||||
@@ -31,6 +32,7 @@ function EditDetailDataPengangguran() {
|
||||
const stateDetail = useProxy(jumlahPengangguranState.jumlahPengangguran);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// --- state lokal form
|
||||
const [formData, setFormData] = useState({
|
||||
@@ -42,6 +44,15 @@ function EditDetailDataPengangguran() {
|
||||
percentageChange: 0,
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
month: '',
|
||||
year: new Date().getFullYear(),
|
||||
educatedUnemployment: 0,
|
||||
uneducatedUnemployment: 0,
|
||||
totalUnemployment: 0,
|
||||
percentageChange: 0,
|
||||
});
|
||||
|
||||
// --- hitung total + persentase perubahan
|
||||
const calculateTotalAndChange = useCallback(
|
||||
async (data: typeof formData) => {
|
||||
@@ -109,6 +120,15 @@ function EditDetailDataPengangguran() {
|
||||
totalUnemployment: data.totalUnemployment,
|
||||
percentageChange: data.percentageChange || 0,
|
||||
});
|
||||
|
||||
setOriginalData({
|
||||
month: data.month,
|
||||
year: yearValue,
|
||||
educatedUnemployment: data.educatedUnemployment,
|
||||
uneducatedUnemployment: data.uneducatedUnemployment,
|
||||
totalUnemployment: data.totalUnemployment,
|
||||
percentageChange: data.percentageChange || 0,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error loading detail:', err);
|
||||
toast.error('Gagal memuat data detail');
|
||||
@@ -118,9 +138,22 @@ function EditDetailDataPengangguran() {
|
||||
loadDetail();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
month: originalData.month,
|
||||
year: originalData.year,
|
||||
educatedUnemployment: originalData.educatedUnemployment,
|
||||
uneducatedUnemployment: originalData.uneducatedUnemployment,
|
||||
totalUnemployment: originalData.totalUnemployment,
|
||||
percentageChange: originalData.percentageChange,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
// --- submit form
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const { total, percentageChange } = await calculateTotalAndChange(formData);
|
||||
|
||||
stateDetail.update.form = {
|
||||
@@ -137,6 +170,8 @@ function EditDetailDataPengangguran() {
|
||||
} catch (err) {
|
||||
console.error('Error updating:', err);
|
||||
toast.error('Terjadi kesalahan saat memperbarui data');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -205,6 +240,17 @@ function EditDetailDataPengangguran() {
|
||||
</Text>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -215,7 +261,7 @@ function EditDetailDataPengangguran() {
|
||||
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,
|
||||
NumberInput,
|
||||
Paper,
|
||||
Select,
|
||||
@@ -17,12 +18,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 CreateJumlahPengangguran() {
|
||||
const stateDetail = useProxy(jumlahPengangguranState.jumlahPengangguran);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const monthOptions = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun',
|
||||
@@ -72,15 +75,23 @@ function CreateJumlahPengangguran() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await calculateTotalAndChange();
|
||||
const id = await stateDetail.create.create();
|
||||
if (id) {
|
||||
await stateDetail.findUnique.load(String(id));
|
||||
if (stateDetail.findUnique.data) {
|
||||
setChartData([stateDetail.findUnique.data]);
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await calculateTotalAndChange();
|
||||
const id = await stateDetail.create.create();
|
||||
if (id) {
|
||||
await stateDetail.findUnique.load(String(id));
|
||||
if (stateDetail.findUnique.data) {
|
||||
setChartData([stateDetail.findUnique.data]);
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/jumlah-pengangguran');
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/jumlah-pengangguran');
|
||||
} catch (error) {
|
||||
console.error("Error creating jumlah pengangguran:", error);
|
||||
toast.error("Gagal menambahkan data pengangguran");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -176,7 +187,19 @@ function CreateJumlahPengangguran() {
|
||||
</Box>
|
||||
|
||||
{/* Action Button */}
|
||||
<Group justify="right" mt="md">
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -186,9 +209,8 @@ function CreateJumlahPengangguran() {
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
disabled={!stateDetail.create.form.month || !stateDetail.create.form.year}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -23,6 +24,7 @@ function EditLowonganKerja() {
|
||||
const lowonganState = useProxy(lowonganKerjaState);
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
posisi: '',
|
||||
@@ -35,6 +37,17 @@ function EditLowonganKerja() {
|
||||
notelp: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
posisi: '',
|
||||
namaPerusahaan: '',
|
||||
lokasi: '',
|
||||
tipePekerjaan: '',
|
||||
gaji: '',
|
||||
deskripsi: '',
|
||||
kualifikasi: '',
|
||||
notelp: '',
|
||||
})
|
||||
|
||||
// load data sekali aja ketika mount / id berubah
|
||||
useEffect(() => {
|
||||
const loadLowongan = async () => {
|
||||
@@ -54,6 +67,16 @@ function EditLowonganKerja() {
|
||||
kualifikasi: data.kualifikasi || '',
|
||||
notelp: data.notelp || '',
|
||||
});
|
||||
setOriginalData({
|
||||
posisi: data.posisi || '',
|
||||
namaPerusahaan: data.namaPerusahaan || '',
|
||||
lokasi: data.lokasi || '',
|
||||
tipePekerjaan: data.tipePekerjaan || '',
|
||||
gaji: data.gaji || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
kualifikasi: data.kualifikasi || '',
|
||||
notelp: data.notelp || '',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading lowongan kerja:", error);
|
||||
@@ -70,9 +93,23 @@ function EditLowonganKerja() {
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
posisi: originalData.posisi,
|
||||
namaPerusahaan: originalData.namaPerusahaan,
|
||||
lokasi: originalData.lokasi,
|
||||
tipePekerjaan: originalData.tipePekerjaan,
|
||||
gaji: originalData.gaji,
|
||||
deskripsi: originalData.deskripsi,
|
||||
kualifikasi: originalData.kualifikasi,
|
||||
notelp: originalData.notelp,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
lowonganState.update.id = params?.id as string;
|
||||
lowonganState.update.form = { ...formData };
|
||||
|
||||
@@ -82,6 +119,8 @@ function EditLowonganKerja() {
|
||||
} catch (error) {
|
||||
console.error("Error updating lowongan kerja:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui lowongan kerja");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -175,6 +214,17 @@ function EditLowonganKerja() {
|
||||
</Box>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -185,7 +235,7 @@ function EditLowonganKerja() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -15,10 +16,13 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
import lowonganKerjaState from '../../../_state/ekonomi/lowongan-kerja';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
function CreateLowonganKerja() {
|
||||
const lowonganState = useProxy(lowonganKerjaState);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
lowonganState.create.form = {
|
||||
@@ -34,9 +38,19 @@ function CreateLowonganKerja() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await lowonganState.create.create();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/lowongan-kerja-lokal');
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await lowonganState.create.create();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/lowongan-kerja-lokal');
|
||||
} catch (error) {
|
||||
console.error('Error creating lowongan kerja:', error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : 'Gagal membuat lowongan kerja'
|
||||
);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -66,7 +80,7 @@ function CreateLowonganKerja() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
defaultValue={lowonganState.create.form.posisi}
|
||||
value={lowonganState.create.form.posisi}
|
||||
onChange={(val) =>
|
||||
(lowonganState.create.form.posisi = val.target.value)
|
||||
}
|
||||
@@ -75,7 +89,7 @@ function CreateLowonganKerja() {
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={lowonganState.create.form.namaPerusahaan}
|
||||
value={lowonganState.create.form.namaPerusahaan}
|
||||
onChange={(val) =>
|
||||
(lowonganState.create.form.namaPerusahaan = val.target.value)
|
||||
}
|
||||
@@ -84,7 +98,7 @@ function CreateLowonganKerja() {
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={lowonganState.create.form.notelp}
|
||||
value={lowonganState.create.form.notelp}
|
||||
onChange={(val) =>
|
||||
(lowonganState.create.form.notelp = val.target.value)
|
||||
}
|
||||
@@ -93,7 +107,7 @@ function CreateLowonganKerja() {
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={lowonganState.create.form.lokasi}
|
||||
value={lowonganState.create.form.lokasi}
|
||||
onChange={(val) =>
|
||||
(lowonganState.create.form.lokasi = val.target.value)
|
||||
}
|
||||
@@ -102,7 +116,7 @@ function CreateLowonganKerja() {
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={lowonganState.create.form.tipePekerjaan}
|
||||
value={lowonganState.create.form.tipePekerjaan}
|
||||
onChange={(val) =>
|
||||
(lowonganState.create.form.tipePekerjaan = val.target.value)
|
||||
}
|
||||
@@ -111,7 +125,7 @@ function CreateLowonganKerja() {
|
||||
required
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={lowonganState.create.form.gaji}
|
||||
value={lowonganState.create.form.gaji}
|
||||
onChange={(val) =>
|
||||
(lowonganState.create.form.gaji = val.target.value)
|
||||
}
|
||||
@@ -146,6 +160,17 @@ function CreateLowonganKerja() {
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -156,7 +181,7 @@ function CreateLowonganKerja() {
|
||||
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,
|
||||
@@ -23,9 +24,9 @@ function EditKategoriProduk() {
|
||||
const params = useParams();
|
||||
const id = params?.id as string;
|
||||
const statePasar = useProxy(pasarDesaState.kategoriProduk);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState({ nama: '' });
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [originalData, setOriginalData] = useState({ nama: '' });
|
||||
|
||||
useEffect(() => {
|
||||
const loadKategoriProduk = async () => {
|
||||
@@ -40,12 +41,11 @@ function EditKategoriProduk() {
|
||||
|
||||
// simpan data ke state lokal
|
||||
setFormData({ nama: data.nama || '' });
|
||||
setOriginalData({ nama: data.nama || '' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading kategori produk:', error);
|
||||
toast.error('Gagal memuat data kategori produk');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,8 +59,16 @@ function EditKategoriProduk() {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
nama: originalData.nama,
|
||||
});
|
||||
toast.info('Form dikembalikan ke data awal');
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!formData.nama.trim()) {
|
||||
toast.error('Nama kategori produk tidak boleh kosong');
|
||||
return;
|
||||
@@ -81,13 +89,11 @@ function EditKategoriProduk() {
|
||||
} catch (error) {
|
||||
console.error('Error updating kategori produk:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui kategori produk');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <Text>Loading...</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header dengan tombol back */}
|
||||
@@ -125,6 +131,17 @@ function EditKategoriProduk() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -135,7 +152,7 @@ function EditKategoriProduk() {
|
||||
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,
|
||||
TextInput,
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import pasarDesaState from '../../../../_state/ekonomi/pasar-desa/pasar-desa';
|
||||
@@ -20,6 +21,7 @@ import pasarDesaState from '../../../../_state/ekonomi/pasar-desa/pasar-desa';
|
||||
function CreateKategoriProduk() {
|
||||
const router = useRouter();
|
||||
const statePasar = useProxy(pasarDesaState.kategoriProduk);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
statePasar.findMany.load();
|
||||
@@ -32,27 +34,34 @@ function CreateKategoriProduk() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!statePasar.create.form.nama) {
|
||||
return toast.warn('Nama kategori produk wajib diisi');
|
||||
try {
|
||||
if (!statePasar.create.form.nama) {
|
||||
return toast.warn('Nama kategori produk wajib diisi');
|
||||
}
|
||||
setIsSubmitting(true);
|
||||
await statePasar.create.create();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/pasar-desa/kategori-produk');
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error('Gagal menambahkan kategori produk');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
await statePasar.create.create();
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/pasar-desa/kategori-produk');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
{/* Header dengan tombol kembali */}
|
||||
<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 Kategori Produk
|
||||
</Title>
|
||||
@@ -71,12 +80,23 @@ function CreateKategoriProduk() {
|
||||
<TextInput
|
||||
label="Nama Kategori Produk"
|
||||
placeholder="Masukkan nama kategori produk"
|
||||
defaultValue={statePasar.create.form.nama || ''}
|
||||
value={statePasar.create.form.nama || ''}
|
||||
onChange={(e) => (statePasar.create.form.nama = 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"
|
||||
@@ -87,7 +107,7 @@ function CreateKategoriProduk() {
|
||||
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 pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pa
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
MultiSelect,
|
||||
Paper,
|
||||
Stack,
|
||||
@@ -40,6 +42,7 @@ function EditPasarDesa() {
|
||||
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
nama: '',
|
||||
harga: 0,
|
||||
@@ -50,6 +53,17 @@ function EditPasarDesa() {
|
||||
kontak: '',
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
nama: '',
|
||||
harga: 0,
|
||||
alamatUsaha: '',
|
||||
imageId: '',
|
||||
imageUrl: "",
|
||||
rating: 0,
|
||||
kategoriId: [],
|
||||
kontak: '',
|
||||
});
|
||||
|
||||
// load data awal
|
||||
useEffect(() => {
|
||||
pasarState.kategoriProduk.findManyAll.load();
|
||||
@@ -70,6 +84,16 @@ function EditPasarDesa() {
|
||||
kategoriId: data.KategoriToPasar?.map((k: any) => k.kategoriId) || [],
|
||||
kontak: data.kontak || '',
|
||||
});
|
||||
setOriginalData({
|
||||
nama: data.nama || '',
|
||||
harga: data.harga || 0,
|
||||
alamatUsaha: data.alamatUsaha || '',
|
||||
imageId: data.imageId || '',
|
||||
imageUrl: data.image?.link || "",
|
||||
rating: data.rating || 0,
|
||||
kategoriId: data.KategoriToPasar?.map((k: any) => k.kategoriId) || [],
|
||||
kontak: data.kontak || '',
|
||||
});
|
||||
if (data.image?.link) setPreviewImage(data.image.link);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -87,8 +111,25 @@ function EditPasarDesa() {
|
||||
setFormData((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
nama: originalData.nama,
|
||||
harga: originalData.harga,
|
||||
alamatUsaha: originalData.alamatUsaha,
|
||||
imageId: originalData.imageId,
|
||||
rating: originalData.rating,
|
||||
kategoriId: (originalData as any)?.KategoriToPasar?.map((k: any) => k.kategoriId) || [],
|
||||
kontak: originalData.kontak,
|
||||
});
|
||||
setPreviewImage(originalData.imageUrl || null);
|
||||
setFile(null);
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// upload image kalau ada file baru
|
||||
let imageId = formData.imageId;
|
||||
if (file) {
|
||||
@@ -110,6 +151,8 @@ function EditPasarDesa() {
|
||||
} catch (error) {
|
||||
console.error('Error updating pasar desa:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui pasar desa');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -148,7 +191,7 @@ function EditPasarDesa() {
|
||||
}}
|
||||
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"
|
||||
>
|
||||
@@ -167,25 +210,45 @@ function EditPasarDesa() {
|
||||
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 pos={"relative"} mt="sm" 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>
|
||||
@@ -254,6 +317,17 @@ function EditPasarDesa() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -264,7 +338,7 @@ function EditPasarDesa() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
MultiSelect,
|
||||
Paper,
|
||||
Stack,
|
||||
@@ -27,6 +29,7 @@ export default function CreatePasarDesa() {
|
||||
const statePasar = useProxy(pasarDesaState);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
statePasar.kategoriProduk.findManyAll.load();
|
||||
@@ -47,25 +50,33 @@ export default function CreatePasarDesa() {
|
||||
};
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
statePasar.pasarDesa.create.form.imageId = uploaded.id;
|
||||
await statePasar.pasarDesa.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/pasar-desa/produk-pasar-desa');
|
||||
} catch (error) {
|
||||
console.error('Error creating kategori produk:', error);
|
||||
toast.error('Gagal membuat kategori produk');
|
||||
} 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');
|
||||
}
|
||||
|
||||
statePasar.pasarDesa.create.form.imageId = uploaded.id;
|
||||
await statePasar.pasarDesa.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/pasar-desa/produk-pasar-desa');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -105,7 +116,7 @@ export default function CreatePasarDesa() {
|
||||
}}
|
||||
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"
|
||||
>
|
||||
@@ -126,7 +137,7 @@ export default function CreatePasarDesa() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ textAlign: 'center' }}>
|
||||
<Box pos={"relative"} mt="sm" style={{ textAlign: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
@@ -134,6 +145,24 @@ export default function CreatePasarDesa() {
|
||||
style={{ maxHeight: 200, objectFit: 'contain', border: '1px solid #ddd' }}
|
||||
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>
|
||||
@@ -142,7 +171,7 @@ export default function CreatePasarDesa() {
|
||||
<TextInput
|
||||
label="Nama Produk"
|
||||
placeholder="Masukkan nama produk"
|
||||
defaultValue={statePasar.pasarDesa.create.form.nama}
|
||||
value={statePasar.pasarDesa.create.form.nama}
|
||||
onChange={(e) => (statePasar.pasarDesa.create.form.nama = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -152,7 +181,7 @@ export default function CreatePasarDesa() {
|
||||
type="number"
|
||||
label="Harga Produk"
|
||||
placeholder="Masukkan harga produk"
|
||||
defaultValue={statePasar.pasarDesa.create.form.harga}
|
||||
value={statePasar.pasarDesa.create.form.harga}
|
||||
onChange={(e) => (statePasar.pasarDesa.create.form.harga = Number(e.target.value))}
|
||||
required
|
||||
/>
|
||||
@@ -165,7 +194,7 @@ export default function CreatePasarDesa() {
|
||||
step={0.1}
|
||||
label="Rating Produk (0–5)"
|
||||
placeholder="Masukkan rating produk"
|
||||
defaultValue={statePasar.pasarDesa.create.form.rating}
|
||||
value={statePasar.pasarDesa.create.form.rating}
|
||||
onChange={(e) => {
|
||||
const value = Number(e.target.value);
|
||||
if (value >= 0 && value <= 5) {
|
||||
@@ -178,7 +207,7 @@ export default function CreatePasarDesa() {
|
||||
<TextInput
|
||||
label="Alamat Usaha"
|
||||
placeholder="Masukkan alamat usaha"
|
||||
defaultValue={statePasar.pasarDesa.create.form.alamatUsaha}
|
||||
value={statePasar.pasarDesa.create.form.alamatUsaha}
|
||||
onChange={(e) => (statePasar.pasarDesa.create.form.alamatUsaha = e.target.value)}
|
||||
/>
|
||||
|
||||
@@ -187,7 +216,7 @@ export default function CreatePasarDesa() {
|
||||
label="Kontak"
|
||||
type="number"
|
||||
placeholder="Masukkan kontak"
|
||||
defaultValue={statePasar.pasarDesa.create.form.kontak}
|
||||
value={statePasar.pasarDesa.create.form.kontak}
|
||||
onChange={(e) => (statePasar.pasarDesa.create.form.kontak = e.target.value)}
|
||||
/>
|
||||
|
||||
@@ -207,6 +236,17 @@ export default function CreatePasarDesa() {
|
||||
|
||||
{/* Tombol Submit */}
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -217,7 +257,7 @@ export default function CreatePasarDesa() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Loader,
|
||||
Paper,
|
||||
Stack,
|
||||
Text,
|
||||
@@ -49,6 +50,8 @@ function EditProgramKemiskinan() {
|
||||
const stateProgram = useProxy(programKemiskinanState);
|
||||
|
||||
const [formData, setFormData] = useState<FormData>(initialForm);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [originalData, setOriginalData] = useState<FormData>(initialForm);
|
||||
|
||||
// Load data 1x dari global state → isi local state
|
||||
useEffect(() => {
|
||||
@@ -68,6 +71,15 @@ function EditProgramKemiskinan() {
|
||||
jumlah: data.statistik?.jumlah?.toString() ?? '',
|
||||
},
|
||||
});
|
||||
setOriginalData({
|
||||
nama: data.nama ?? '',
|
||||
deskripsi: data.deskripsi ?? '',
|
||||
icon: data.icon ?? '',
|
||||
statistik: {
|
||||
tahun: data.statistik?.tahun?.toString() ?? '',
|
||||
jumlah: data.statistik?.jumlah?.toString() ?? '',
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error load data:', err);
|
||||
@@ -99,8 +111,22 @@ function EditProgramKemiskinan() {
|
||||
[]
|
||||
);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
nama: originalData.nama,
|
||||
deskripsi: originalData.deskripsi,
|
||||
icon: originalData.icon,
|
||||
statistik: {
|
||||
tahun: originalData.statistik.tahun,
|
||||
jumlah: originalData.statistik.jumlah,
|
||||
},
|
||||
});
|
||||
toast.info('Form dikembalikan ke data awal');
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
stateProgram.update.id = id;
|
||||
stateProgram.update.form = formData;
|
||||
await stateProgram.update.update();
|
||||
@@ -110,6 +136,8 @@ function EditProgramKemiskinan() {
|
||||
} catch (error) {
|
||||
console.error('Error update program:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui program');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -192,6 +220,17 @@ function EditProgramKemiskinan() {
|
||||
</Box>
|
||||
|
||||
<Group justify="right" mt="md">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -202,7 +241,7 @@ function EditProgramKemiskinan() {
|
||||
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,
|
||||
Text,
|
||||
@@ -27,6 +28,7 @@ function CreateProgramKemiskinan() {
|
||||
const router = useRouter();
|
||||
const [lineChart, setLineChart] = useState<any[]>([]);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const resetForm = () => {
|
||||
programState.create.form = {
|
||||
nama: '',
|
||||
@@ -40,24 +42,32 @@ function CreateProgramKemiskinan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!programState.create.form.nama || !programState.create.form.deskripsi) {
|
||||
return toast.warn('Judul dan deskripsi wajib diisi');
|
||||
}
|
||||
|
||||
const id = await programState.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await programState.findUnique.load(idStr);
|
||||
if (programState.findUnique.data) {
|
||||
setLineChart([programState.findUnique.data]);
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!programState.create.form.nama || !programState.create.form.deskripsi) {
|
||||
return toast.warn('Judul dan deskripsi wajib diisi');
|
||||
}
|
||||
toast.success('Program berhasil ditambahkan');
|
||||
} else {
|
||||
toast.error('Gagal menambahkan program, coba lagi');
|
||||
}
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/program-kemiskinan');
|
||||
const id = await programState.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await programState.findUnique.load(idStr);
|
||||
if (programState.findUnique.data) {
|
||||
setLineChart([programState.findUnique.data]);
|
||||
}
|
||||
toast.success('Program berhasil ditambahkan');
|
||||
} else {
|
||||
toast.error('Gagal menambahkan program, coba lagi');
|
||||
}
|
||||
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/program-kemiskinan');
|
||||
} catch (error) {
|
||||
console.error('Gagal menyimpan data:', error);
|
||||
toast.error('Terjadi kesalahan saat menyimpan data');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -90,7 +100,7 @@ function CreateProgramKemiskinan() {
|
||||
<TextInput
|
||||
label="Judul Program"
|
||||
placeholder="Masukkan judul program"
|
||||
defaultValue={programState.create.form.nama}
|
||||
value={programState.create.form.nama}
|
||||
onChange={(val) => (programState.create.form.nama = val.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -125,7 +135,7 @@ function CreateProgramKemiskinan() {
|
||||
<Group grow>
|
||||
<TextInput
|
||||
type="number"
|
||||
defaultValue={programState.create.form.statistik.jumlah}
|
||||
value={programState.create.form.statistik.jumlah}
|
||||
onChange={(val) =>
|
||||
(programState.create.form.statistik.jumlah = val.target.value)
|
||||
}
|
||||
@@ -135,7 +145,7 @@ function CreateProgramKemiskinan() {
|
||||
/>
|
||||
<TextInput
|
||||
type="number"
|
||||
defaultValue={programState.create.form.statistik.tahun}
|
||||
value={programState.create.form.statistik.tahun}
|
||||
onChange={(val) =>
|
||||
(programState.create.form.statistik.tahun = val.target.value)
|
||||
}
|
||||
@@ -147,6 +157,17 @@ function CreateProgramKemiskinan() {
|
||||
|
||||
{/* Tombol Submit */}
|
||||
<Group justify="right" mt="sm">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -157,7 +178,7 @@ function CreateProgramKemiskinan() {
|
||||
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,
|
||||
@@ -24,7 +25,7 @@ function EditSektorUnggulanDesa() {
|
||||
const router = useRouter();
|
||||
const params = useParams() as { id: string };
|
||||
const stateGrafik = useProxy(grafikSektorUnggulan);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const id = params.id;
|
||||
|
||||
// state lokal buat form
|
||||
@@ -34,6 +35,12 @@ function EditSektorUnggulanDesa() {
|
||||
value: 0,
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
name: '',
|
||||
description: '',
|
||||
value: 0,
|
||||
});
|
||||
|
||||
// Load data saat komponen mount
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
@@ -47,6 +54,11 @@ function EditSektorUnggulanDesa() {
|
||||
description: data.description || '',
|
||||
value: data.value || 0,
|
||||
});
|
||||
setOriginalData({
|
||||
name: data.name || '',
|
||||
description: data.description || '',
|
||||
value: data.value || 0,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -65,6 +77,7 @@ function EditSektorUnggulanDesa() {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
stateGrafik.update.id = id;
|
||||
stateGrafik.update.form = { ...formData }; // update global pas submit
|
||||
await stateGrafik.update.submit();
|
||||
@@ -73,9 +86,20 @@ function EditSektorUnggulanDesa() {
|
||||
} catch (error) {
|
||||
console.error('Error update sektor unggulan:', error);
|
||||
toast.error('Terjadi kesalahan saat memperbarui sektor unggulan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
name: originalData.name,
|
||||
description: originalData.description,
|
||||
value: originalData.value,
|
||||
});
|
||||
toast.info('Form dikembalikan ke data awal');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
<Group mb="md">
|
||||
@@ -129,6 +153,17 @@ function EditSektorUnggulanDesa() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -139,7 +174,7 @@ function EditSektorUnggulanDesa() {
|
||||
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,
|
||||
@@ -18,11 +19,13 @@ import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
import grafikSektorUnggulan from '../../../_state/ekonomi/sektor-unggulan-desa';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
function CreateSektorUnggulanDesa() {
|
||||
const stateGrafik = useProxy(grafikSektorUnggulan);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stateGrafik.create.form = {
|
||||
@@ -33,16 +36,24 @@ function CreateSektorUnggulanDesa() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const id = await stateGrafik.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stateGrafik.findUnique.load(idStr);
|
||||
if (stateGrafik.findUnique.data) {
|
||||
setChartData([stateGrafik.findUnique.data]);
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const id = await stateGrafik.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stateGrafik.findUnique.load(idStr);
|
||||
if (stateGrafik.findUnique.data) {
|
||||
setChartData([stateGrafik.findUnique.data]);
|
||||
}
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/sektor-unggulan-desa');
|
||||
} catch (error) {
|
||||
console.error('Error creating sektor unggulan:', error);
|
||||
toast.error('Terjadi kesalahan saat menambahkan sektor unggulan');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
resetForm();
|
||||
router.push('/admin/ekonomi/sektor-unggulan-desa');
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -75,7 +86,7 @@ function CreateSektorUnggulanDesa() {
|
||||
<TextInput
|
||||
label="Nama Sektor Unggulan"
|
||||
placeholder="Masukkan nama sektor unggulan"
|
||||
defaultValue={stateGrafik.create.form.name}
|
||||
value={stateGrafik.create.form.name}
|
||||
onChange={(e) => {
|
||||
stateGrafik.create.form.name = e.currentTarget.value;
|
||||
}}
|
||||
@@ -98,7 +109,7 @@ function CreateSektorUnggulanDesa() {
|
||||
label="Jumlah"
|
||||
type="number"
|
||||
placeholder="Masukkan jumlah"
|
||||
defaultValue={stateGrafik.create.form.value}
|
||||
value={stateGrafik.create.form.value}
|
||||
onChange={(e) => {
|
||||
stateGrafik.create.form.value = Number(e.currentTarget.value);
|
||||
}}
|
||||
@@ -106,6 +117,17 @@ function CreateSektorUnggulanDesa() {
|
||||
/>
|
||||
|
||||
<Group justify="right">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -116,7 +138,7 @@ function CreateSektorUnggulanDesa() {
|
||||
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 stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
Paper,
|
||||
Select,
|
||||
Stack,
|
||||
@@ -27,7 +29,7 @@ export default function EditPegawaiBumDes() {
|
||||
const router = useRouter();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai);
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
namaLengkap: '',
|
||||
gelarAkademik: '',
|
||||
@@ -39,6 +41,18 @@ export default function EditPegawaiBumDes() {
|
||||
posisiId: '',
|
||||
isActive: true,
|
||||
});
|
||||
const [originalData, setOriginalData] = useState({
|
||||
namaLengkap: '',
|
||||
gelarAkademik: '',
|
||||
imageId: '',
|
||||
tanggalMasuk: '',
|
||||
email: '',
|
||||
telepon: '',
|
||||
alamat: '',
|
||||
posisiId: '',
|
||||
isActive: true,
|
||||
imageUrl: ''
|
||||
});
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
@@ -67,6 +81,18 @@ export default function EditPegawaiBumDes() {
|
||||
posisiId: data.posisiId || '',
|
||||
isActive: data.isActive ?? true,
|
||||
});
|
||||
setOriginalData({
|
||||
namaLengkap: data.namaLengkap || '',
|
||||
gelarAkademik: data.gelarAkademik || '',
|
||||
imageId: data.imageId || '',
|
||||
tanggalMasuk: data.tanggalMasuk || '',
|
||||
email: data.email || '',
|
||||
telepon: data.telepon || '',
|
||||
alamat: data.alamat || '',
|
||||
posisiId: data.posisiId || '',
|
||||
isActive: data.isActive ?? true,
|
||||
imageUrl: data.image?.link || '',
|
||||
});
|
||||
|
||||
setPreviewImage(data.image?.link || null);
|
||||
}
|
||||
@@ -79,8 +105,26 @@ export default function EditPegawaiBumDes() {
|
||||
loadPegawai();
|
||||
}, [id]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
namaLengkap: originalData.namaLengkap,
|
||||
gelarAkademik: originalData.gelarAkademik,
|
||||
imageId: originalData.imageId,
|
||||
tanggalMasuk: originalData.tanggalMasuk,
|
||||
email: originalData.email,
|
||||
telepon: originalData.telepon,
|
||||
alamat: originalData.alamat,
|
||||
posisiId: originalData.posisiId,
|
||||
isActive: originalData.isActive,
|
||||
});
|
||||
setPreviewImage(originalData.imageUrl || null);
|
||||
setFile(null);
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
if (!formData.namaLengkap.trim()) {
|
||||
return toast.error('Nama lengkap tidak boleh kosong');
|
||||
}
|
||||
@@ -103,6 +147,8 @@ export default function EditPegawaiBumDes() {
|
||||
} catch (error) {
|
||||
console.error('Error updating pegawai:', error);
|
||||
toast.error(error instanceof Error ? error.message : 'Gagal memperbarui data pegawai');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -164,13 +210,13 @@ export default function EditPegawaiBumDes() {
|
||||
<Dropzone.Idle><IconPhoto size={48} color="#868e96" stroke={1.5} /></Dropzone.Idle>
|
||||
<Stack gap="xs" align="center">
|
||||
<Text size="md" fw={500}>Seret gambar atau klik untuk memilih file</Text>
|
||||
<Text size="sm" c="dimmed">Maksimal 5MB, format gambar wajib</Text>
|
||||
<Text size="sm" c="dimmed">Maksimal 5MB, format gambar .png, .jpg, .jpeg, webp</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="sm" style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Box pos={"relative"} mt="sm" style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Image
|
||||
src={previewImage}
|
||||
alt="Preview Gambar"
|
||||
@@ -178,6 +224,24 @@ export default function EditPegawaiBumDes() {
|
||||
style={{ maxHeight: 220, objectFit: 'contain', border: `1px solid ${colors['blue-button']}` }}
|
||||
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>
|
||||
@@ -244,10 +308,21 @@ export default function EditPegawaiBumDes() {
|
||||
</Box>
|
||||
|
||||
{/* Submit Button */}
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
loading={stateOrganisasi.edit.loading}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
@@ -256,7 +331,7 @@ export default function EditPegawaiBumDes() {
|
||||
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 stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { ActionIcon, Box, Button, Group, Image, Loader, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -20,6 +20,8 @@ function CreatePegawaiBumDes() {
|
||||
stateStrukturBumDes.posisiOrganisasi.findManyAll.load();
|
||||
resetForm();
|
||||
}, []);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
stateOrganisasi.create.form = {
|
||||
@@ -33,6 +35,8 @@ function CreatePegawaiBumDes() {
|
||||
posisiId: "",
|
||||
isActive: true,
|
||||
};
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
@@ -41,15 +45,18 @@ function CreatePegawaiBumDes() {
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Upload gambar dulu
|
||||
if (!file) {
|
||||
return toast.warn('Silakan pilih file gambar terlebih dahulu');
|
||||
}
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file: previewImage.file,
|
||||
name: previewImage.file.name,
|
||||
file,
|
||||
name: file.name,
|
||||
});
|
||||
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) {
|
||||
return toast.error("Gagal upload gambar");
|
||||
return toast.error('Gagal mengunggah gambar, silakan coba lagi');
|
||||
}
|
||||
|
||||
// Set status aktif secara otomatis
|
||||
@@ -69,6 +76,8 @@ function CreatePegawaiBumDes() {
|
||||
} catch (error) {
|
||||
console.error("Error creating pegawai:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan pegawai");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,7 +105,7 @@ function CreatePegawaiBumDes() {
|
||||
<TextInput
|
||||
label="Nama Lengkap"
|
||||
placeholder="Masukkan nama lengkap"
|
||||
defaultValue={stateOrganisasi.create.form.namaLengkap}
|
||||
value={stateOrganisasi.create.form.namaLengkap}
|
||||
onChange={(e) => (stateOrganisasi.create.form.namaLengkap = e.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
@@ -105,7 +114,7 @@ function CreatePegawaiBumDes() {
|
||||
<TextInput
|
||||
label="Gelar Akademik"
|
||||
placeholder="Contoh: S.Kom"
|
||||
defaultValue={stateOrganisasi.create.form.gelarAkademik}
|
||||
value={stateOrganisasi.create.form.gelarAkademik}
|
||||
onChange={(e) => (stateOrganisasi.create.form.gelarAkademik = e.currentTarget.value)}
|
||||
/>
|
||||
</Box>
|
||||
@@ -163,7 +172,7 @@ function CreatePegawaiBumDes() {
|
||||
</Dropzone>
|
||||
|
||||
{previewImage && (
|
||||
<Box mt="md">
|
||||
<Box mt="md" pos={"relative"}>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Preview Gambar
|
||||
</Text>
|
||||
@@ -180,6 +189,24 @@ function CreatePegawaiBumDes() {
|
||||
}}
|
||||
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>
|
||||
@@ -188,7 +215,7 @@ function CreatePegawaiBumDes() {
|
||||
label="Tanggal Masuk"
|
||||
type="date"
|
||||
placeholder="Contoh: 2022-01-01"
|
||||
defaultValue={stateOrganisasi.create.form.tanggalMasuk}
|
||||
value={stateOrganisasi.create.form.tanggalMasuk}
|
||||
onChange={(e) => (stateOrganisasi.create.form.tanggalMasuk = e.currentTarget.value)}
|
||||
/>
|
||||
</Box>
|
||||
@@ -198,7 +225,7 @@ function CreatePegawaiBumDes() {
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="Contoh: email@example.com"
|
||||
defaultValue={stateOrganisasi.create.form.email}
|
||||
value={stateOrganisasi.create.form.email}
|
||||
onChange={(e) => (stateOrganisasi.create.form.email = e.currentTarget.value)}
|
||||
/>
|
||||
</Box>
|
||||
@@ -207,7 +234,7 @@ function CreatePegawaiBumDes() {
|
||||
<TextInput
|
||||
label="Nomor Telepon"
|
||||
placeholder="Contoh: 08123456789"
|
||||
defaultValue={stateOrganisasi.create.form.telepon}
|
||||
value={stateOrganisasi.create.form.telepon}
|
||||
onChange={(e) => (stateOrganisasi.create.form.telepon = e.currentTarget.value)}
|
||||
/>
|
||||
</Box>
|
||||
@@ -216,7 +243,7 @@ function CreatePegawaiBumDes() {
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Contoh: Jl. Contoh No. 1"
|
||||
defaultValue={stateOrganisasi.create.form.alamat}
|
||||
value={stateOrganisasi.create.form.alamat}
|
||||
onChange={(e) => (stateOrganisasi.create.form.alamat = e.currentTarget.value)}
|
||||
/>
|
||||
</Box>
|
||||
@@ -242,6 +269,17 @@ function CreatePegawaiBumDes() {
|
||||
|
||||
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
@@ -252,7 +290,7 @@ function CreatePegawaiBumDes() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
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';
|
||||
@@ -17,6 +17,7 @@ function EditPosisiOrganisasiBumDes() {
|
||||
const params = useParams();
|
||||
const id = params?.id as string;
|
||||
const stateOrganisasi = useProxy(stateStrukturBumDes.posisiOrganisasi);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: '',
|
||||
@@ -24,6 +25,12 @@ function EditPosisiOrganisasiBumDes() {
|
||||
hierarki: 0,
|
||||
});
|
||||
|
||||
const [originalData, setOriginalData] = useState({
|
||||
nama: '',
|
||||
deskripsi: '',
|
||||
hierarki: 0,
|
||||
});
|
||||
|
||||
// Fungsi generik untuk update formData
|
||||
const handleChange = (field: keyof typeof formData, value: any) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
@@ -42,6 +49,11 @@ function EditPosisiOrganisasiBumDes() {
|
||||
deskripsi: data.deskripsi || '',
|
||||
hierarki: data.hierarki || 0,
|
||||
});
|
||||
setOriginalData({
|
||||
nama: data.nama || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
hierarki: data.hierarki || 0,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error loading posisi organisasi:', err);
|
||||
@@ -52,6 +64,15 @@ function EditPosisiOrganisasiBumDes() {
|
||||
loadPosisiOrganisasi();
|
||||
}, [id]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setFormData({
|
||||
nama: originalData.nama,
|
||||
deskripsi: originalData.deskripsi,
|
||||
hierarki: originalData.hierarki,
|
||||
});
|
||||
toast.info("Form dikembalikan ke data awal");
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.nama.trim()) {
|
||||
toast.error('Nama posisi organisasi tidak boleh kosong');
|
||||
@@ -59,6 +80,7 @@ function EditPosisiOrganisasiBumDes() {
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
// Update global state hanya saat submit
|
||||
stateOrganisasi.edit.form = {
|
||||
nama: formData.nama.trim(),
|
||||
@@ -78,6 +100,8 @@ function EditPosisiOrganisasiBumDes() {
|
||||
} catch (err) {
|
||||
console.error('Error updating posisi organisasi:', err);
|
||||
// toast error biasanya sudah ada di update
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,10 +156,21 @@ function EditPosisiOrganisasiBumDes() {
|
||||
required
|
||||
/>
|
||||
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={handleResetForm}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
loading={stateOrganisasi.edit.loading}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
@@ -144,7 +179,7 @@ function EditPosisiOrganisasiBumDes() {
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
@@ -3,37 +3,32 @@
|
||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||
import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
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 { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function CreatePosisiOrganisasiBumDes() {
|
||||
const router = useRouter();
|
||||
const stateOrganisasi = useProxy(stateStrukturBumDes.posisiOrganisasi);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
stateOrganisasi.findMany.load();
|
||||
// Initialize form with default values
|
||||
}, []);
|
||||
|
||||
const resetForm = () => {
|
||||
stateOrganisasi.create.form = {
|
||||
nama: "",
|
||||
deskripsi: "",
|
||||
hierarki: 0,
|
||||
};
|
||||
|
||||
return () => {
|
||||
// Clean up form on unmount
|
||||
stateOrganisasi.create.form = {
|
||||
nama: "",
|
||||
deskripsi: "",
|
||||
hierarki: 0,
|
||||
};
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
if (!stateOrganisasi.create.form.nama.trim()) {
|
||||
return toast.error('Nama posisi tidak boleh kosong');
|
||||
@@ -45,6 +40,8 @@ function CreatePosisiOrganisasiBumDes() {
|
||||
} catch (error) {
|
||||
toast.error('Gagal menambahkan posisi organisasi');
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,7 +68,7 @@ function CreatePosisiOrganisasiBumDes() {
|
||||
<TextInput
|
||||
label="Nama Posisi"
|
||||
placeholder="Contoh: Kepala Desa"
|
||||
defaultValue={stateOrganisasi.create.form.nama}
|
||||
value={stateOrganisasi.create.form.nama}
|
||||
onChange={(e) => (stateOrganisasi.create.form.nama = e.target.value)}
|
||||
required
|
||||
/>
|
||||
@@ -93,7 +90,7 @@ function CreatePosisiOrganisasiBumDes() {
|
||||
type="number"
|
||||
min={0}
|
||||
placeholder="Contoh: 1 (Angka semakin kecil, posisi semakin tinggi)"
|
||||
defaultValue={stateOrganisasi.create.form.hierarki || ''}
|
||||
value={stateOrganisasi.create.form.hierarki || ''}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.target.value, 10);
|
||||
stateOrganisasi.create.form.hierarki = isNaN(value) ? 0 : value;
|
||||
@@ -101,10 +98,21 @@ function CreatePosisiOrganisasiBumDes() {
|
||||
required
|
||||
/>
|
||||
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Group justify="right">
|
||||
{/* Tombol Batal */}
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
radius="md"
|
||||
size="md"
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
||||
{/* Tombol Simpan */}
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
loading={stateOrganisasi.create.loading}
|
||||
radius="md"
|
||||
size="md"
|
||||
style={{
|
||||
@@ -113,7 +121,7 @@ function CreatePosisiOrganisasiBumDes() {
|
||||
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