fix(kependudukan): remove jenisKelamin field and align MigrasiPenduduk with database schema

- Remove jenisKelamin field from API, state, and UI components
- Fix MigrasiPenduduk API to use null instead of undefined for optional fields
- Update create/edit forms to properly handle asal/tujuan fields based on jenis
- Fix DatePickerInput type handling with valueFormat prop
- Update list page to display asal or tujuan conditionally
- Add proper select statements in API responses
- Fix TypeScript type errors in migrasi-penduduk module

Closes: Schema mismatch causing errors when inputting migrasi penduduk data

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-04-13 15:53:58 +08:00
parent 80186bf493
commit 50801e5c8a
7 changed files with 46 additions and 75 deletions

View File

@@ -9,7 +9,6 @@ const templateMigrasiPenduduk = z.object({
tanggal: z.string().min(1, "Tanggal harus diisi"), tanggal: z.string().min(1, "Tanggal harus diisi"),
asalTujuan: z.string().min(1, "Asal/Tujuan harus diisi"), asalTujuan: z.string().min(1, "Asal/Tujuan harus diisi"),
alasan: z.string().optional(), alasan: z.string().optional(),
jenisKelamin: z.string().optional(),
}); });
const migrasiPenduduk = proxy({ const migrasiPenduduk = proxy({
@@ -20,7 +19,6 @@ const migrasiPenduduk = proxy({
tanggal: "", tanggal: "",
asalTujuan: "", asalTujuan: "",
alasan: "", alasan: "",
jenisKelamin: "",
}, },
loading: false, loading: false,
async create() { async create() {
@@ -38,7 +36,7 @@ const migrasiPenduduk = proxy({
const id = res.data?.data?.id; const id = res.data?.data?.id;
if (id) { if (id) {
toast.success("Sukses menambahkan data migrasi penduduk"); toast.success("Sukses menambahkan data migrasi penduduk");
migrasiPenduduk.create.form = { jenis: "", nama: "", tanggal: "", asalTujuan: "", alasan: "", jenisKelamin: "" }; migrasiPenduduk.create.form = { jenis: "", nama: "", tanggal: "", asalTujuan: "", alasan: "" };
migrasiPenduduk.findMany.load(); migrasiPenduduk.findMany.load();
return id; return id;
} }
@@ -116,7 +114,6 @@ const migrasiPenduduk = proxy({
tanggal: "", tanggal: "",
asalTujuan: "", asalTujuan: "",
alasan: "", alasan: "",
jenisKelamin: "",
}, },
loading: false, loading: false,
async submit() { async submit() {
@@ -132,7 +129,6 @@ const migrasiPenduduk = proxy({
tanggal: this.form.tanggal, tanggal: this.form.tanggal,
asalTujuan: this.form.asalTujuan, asalTujuan: this.form.asalTujuan,
alasan: this.form.alasan, alasan: this.form.alasan,
jenisKelamin: this.form.jenisKelamin,
}; };
const cek = templateMigrasiPenduduk.safeParse(formData); const cek = templateMigrasiPenduduk.safeParse(formData);

View File

@@ -28,7 +28,6 @@ interface MigrasiPendudukForm {
tanggal: string; tanggal: string;
asalTujuan: string; asalTujuan: string;
alasan: string; alasan: string;
jenisKelamin: string;
} }
export default function EditMigrasiPenduduk() { export default function EditMigrasiPenduduk() {
@@ -42,7 +41,6 @@ export default function EditMigrasiPenduduk() {
tanggal: '', tanggal: '',
asalTujuan: '', asalTujuan: '',
alasan: '', alasan: '',
jenisKelamin: '',
}); });
const [originalData, setOriginalData] = useState<MigrasiPendudukForm>({ const [originalData, setOriginalData] = useState<MigrasiPendudukForm>({
jenis: '', jenis: '',
@@ -50,7 +48,6 @@ export default function EditMigrasiPenduduk() {
tanggal: '', tanggal: '',
asalTujuan: '', asalTujuan: '',
alasan: '', alasan: '',
jenisKelamin: '',
}); });
const jenisOptions = [ const jenisOptions = [
@@ -58,11 +55,6 @@ export default function EditMigrasiPenduduk() {
{ value: 'KELUAR', label: 'Keluar' }, { value: 'KELUAR', label: 'Keluar' },
]; ];
const jenisKelaminOptions = [
{ value: 'L', label: 'Laki-laki' },
{ value: 'P', label: 'Perempuan' },
];
const isFormValid = () => { const isFormValid = () => {
return ( return (
formData.jenis?.trim() !== '' && formData.jenis?.trim() !== '' &&
@@ -81,23 +73,23 @@ export default function EditMigrasiPenduduk() {
stateMigrasiPenduduk.update.id = id; stateMigrasiPenduduk.update.id = id;
await stateMigrasiPenduduk.findUnique.load(id); await stateMigrasiPenduduk.findUnique.load(id);
const data = stateMigrasiPenduduk.findUnique.data as MigrasiPendudukForm | null; const data = stateMigrasiPenduduk.findUnique.data as any;
if (data) { if (data) {
const asalTujuan = data.jenis === 'MASUK' ? (data.asal || '') : (data.tujuan || '');
setFormData({ setFormData({
jenis: data.jenis ?? '', jenis: data.jenis ?? '',
nama: data.nama ?? '', nama: data.nama ?? '',
tanggal: data.tanggal ?? '', tanggal: data.tanggal ? new Date(data.tanggal).toISOString().split('T')[0] : '',
asalTujuan: data.asalTujuan ?? '', asalTujuan: asalTujuan,
alasan: data.alasan ?? '', alasan: data.alasan ?? '',
jenisKelamin: data.jenisKelamin ?? '',
}); });
setOriginalData({ setOriginalData({
jenis: data.jenis ?? '', jenis: data.jenis ?? '',
nama: data.nama ?? '', nama: data.nama ?? '',
tanggal: data.tanggal ?? '', tanggal: data.tanggal ? new Date(data.tanggal).toISOString().split('T')[0] : '',
asalTujuan: data.asalTujuan ?? '', asalTujuan: asalTujuan,
alasan: data.alasan ?? '', alasan: data.alasan ?? '',
jenisKelamin: data.jenisKelamin ?? '',
}); });
} }
} catch (error) { } catch (error) {
@@ -114,7 +106,7 @@ export default function EditMigrasiPenduduk() {
const handleChangeText = useCallback( const handleChangeText = useCallback(
(field: keyof MigrasiPendudukForm) => (field: keyof MigrasiPendudukForm) =>
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFormData((prev) => ({ ...prev, [field]: e.currentTarget.value as never })); setFormData((prev) => ({ ...prev, [field]: e.currentTarget.value }));
}, },
[] []
); );
@@ -122,7 +114,7 @@ export default function EditMigrasiPenduduk() {
const handleChangeSelect = useCallback( const handleChangeSelect = useCallback(
(field: keyof MigrasiPendudukForm) => (field: keyof MigrasiPendudukForm) =>
(value: string | null) => { (value: string | null) => {
setFormData((prev) => ({ ...prev, [field]: (value || '') as never })); setFormData((prev) => ({ ...prev, [field]: value || '' }));
}, },
[] []
); );
@@ -134,7 +126,6 @@ export default function EditMigrasiPenduduk() {
tanggal: originalData.tanggal, tanggal: originalData.tanggal,
asalTujuan: originalData.asalTujuan, asalTujuan: originalData.asalTujuan,
alasan: originalData.alasan, alasan: originalData.alasan,
jenisKelamin: originalData.jenisKelamin,
}); });
toast.info("Form dikembalikan ke data awal"); toast.info("Form dikembalikan ke data awal");
}; };
@@ -211,6 +202,7 @@ export default function EditMigrasiPenduduk() {
tanggal: val || '', tanggal: val || '',
})); }));
}} }}
valueFormat="YYYY-MM-DD"
required required
/> />
@@ -231,14 +223,6 @@ export default function EditMigrasiPenduduk() {
minRows={2} minRows={2}
/> />
<Select
label="Jenis Kelamin"
placeholder="Pilih jenis kelamin"
data={jenisKelaminOptions}
value={formData.jenisKelamin}
onChange={handleChangeSelect('jenisKelamin')}
/>
<Group justify="flex-end"> <Group justify="flex-end">
<Button <Button
variant="outline" variant="outline"

View File

@@ -31,11 +31,6 @@ function CreateMigrasiPenduduk() {
{ value: 'KELUAR', label: 'Keluar' }, { value: 'KELUAR', label: 'Keluar' },
]; ];
const jenisKelaminOptions = [
{ value: 'L', label: 'Laki-laki' },
{ value: 'P', label: 'Perempuan' },
];
const isFormValid = () => { const isFormValid = () => {
return ( return (
stateMigrasiPenduduk.create.form.jenis?.trim() !== '' && stateMigrasiPenduduk.create.form.jenis?.trim() !== '' &&
@@ -52,12 +47,12 @@ function CreateMigrasiPenduduk() {
tanggal: '', tanggal: '',
asalTujuan: '', asalTujuan: '',
alasan: '', alasan: '',
jenisKelamin: '',
}; };
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
try { try {
setIsSubmitting(true);
const id = await stateMigrasiPenduduk.create.create(); const id = await stateMigrasiPenduduk.create.create();
if (id) { if (id) {
resetForm(); resetForm();
@@ -126,6 +121,7 @@ function CreateMigrasiPenduduk() {
onChange={(val: string | null) => { onChange={(val: string | null) => {
stateMigrasiPenduduk.create.form.tanggal = val || ''; stateMigrasiPenduduk.create.form.tanggal = val || '';
}} }}
valueFormat="YYYY-MM-DD"
required required
/> />
@@ -150,16 +146,6 @@ function CreateMigrasiPenduduk() {
minRows={2} minRows={2}
/> />
<Select
label="Jenis Kelamin"
placeholder="Pilih jenis kelamin"
data={jenisKelaminOptions}
value={stateMigrasiPenduduk.create.form.jenisKelamin}
onChange={(val) => {
stateMigrasiPenduduk.create.form.jenisKelamin = val || '';
}}
/>
<Group justify="right"> <Group justify="right">
<Button <Button
variant="outline" variant="outline"

View File

@@ -57,9 +57,9 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
jenis: string; jenis: string;
nama: string; nama: string;
tanggal: string; tanggal: string;
asalTujuan: string; asal: string | null;
tujuan: string | null;
alasan: string | null; alasan: string | null;
jenisKelamin: string | null;
}; };
const router = useRouter(); const router = useRouter();
@@ -142,16 +142,17 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
<TableTr> <TableTr>
<TableTh style={{ width: '10%' }}>Jenis</TableTh> <TableTh style={{ width: '10%' }}>Jenis</TableTh>
<TableTh style={{ width: '20%' }}>Nama</TableTh> <TableTh style={{ width: '20%' }}>Nama</TableTh>
<TableTh style={{ width: '12%' }}>Tanggal</TableTh> <TableTh style={{ width: '15%' }}>Tanggal</TableTh>
<TableTh style={{ width: '20%' }}>Asal/Tujuan</TableTh> <TableTh style={{ width: '25%' }}>Asal/Tujuan</TableTh>
<TableTh style={{ width: '10%' }}>L/P</TableTh>
<TableTh style={{ width: '10%' }}>Edit</TableTh> <TableTh style={{ width: '10%' }}>Edit</TableTh>
<TableTh style={{ width: '10%' }}>Hapus</TableTh> <TableTh style={{ width: '10%' }}>Hapus</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
{filteredData.length > 0 ? ( {filteredData.length > 0 ? (
filteredData.map((item) => ( filteredData.map((item) => {
const asalTujuan = item.jenis === 'MASUK' ? (item.asal || '-') : (item.tujuan || '-');
return (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd> <TableTd>
<Text <Text
@@ -164,8 +165,7 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
</TableTd> </TableTd>
<TableTd>{item.nama}</TableTd> <TableTd>{item.nama}</TableTd>
<TableTd>{formatTanggal(item.tanggal)}</TableTd> <TableTd>{formatTanggal(item.tanggal)}</TableTd>
<TableTd>{item.asalTujuan}</TableTd> <TableTd>{asalTujuan}</TableTd>
<TableTd>{item.jenisKelamin || '-'}</TableTd>
<TableTd> <TableTd>
<Button <Button
variant="light" variant="light"
@@ -197,10 +197,11 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
</Button> </Button>
</TableTd> </TableTd>
</TableTr> </TableTr>
)) );
})
) : ( ) : (
<TableTr> <TableTr>
<TableTd colSpan={7}> <TableTd colSpan={6}>
<Center py={20}> <Center py={20}>
<Text c="dimmed" fz="sm" lh={1.4}> <Text c="dimmed" fz="sm" lh={1.4}>
Tidak ada data migrasi penduduk yang cocok Tidak ada data migrasi penduduk yang cocok
@@ -217,7 +218,9 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
<Box hiddenFrom="md"> <Box hiddenFrom="md">
<Stack gap="xs"> <Stack gap="xs">
{filteredData.length > 0 ? ( {filteredData.length > 0 ? (
filteredData.map((item) => ( filteredData.map((item) => {
const asalTujuan = item.jenis === 'MASUK' ? (item.asal || '-') : (item.tujuan || '-');
return (
<Paper key={item.id} withBorder p="sm" radius="sm"> <Paper key={item.id} withBorder p="sm" radius="sm">
<Stack gap={"xs"}> <Stack gap={"xs"}>
<Box> <Box>
@@ -250,10 +253,10 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
</Box> </Box>
<Box> <Box>
<Text fz="sm" fw={600} lh={1.4}> <Text fz="sm" fw={600} lh={1.4}>
Asal/Tujuan {item.jenis === 'MASUK' ? 'Asal' : 'Tujuan'}
</Text> </Text>
<Text fz="sm" fw={500} lh={1.4}> <Text fz="sm" fw={500} lh={1.4}>
{item.asalTujuan} {asalTujuan}
</Text> </Text>
</Box> </Box>
{item.alasan && ( {item.alasan && (
@@ -266,14 +269,6 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
</Text> </Text>
</Box> </Box>
)} )}
<Box>
<Text fz="sm" fw={600} lh={1.4}>
Jenis Kelamin
</Text>
<Text fz="sm" fw={500} lh={1.4}>
{item.jenisKelamin || '-'}
</Text>
</Box>
<Group justify="flex-end" gap="xs"> <Group justify="flex-end" gap="xs">
<Button <Button
variant="light" variant="light"
@@ -304,7 +299,8 @@ function ListMigrasiPenduduk({ search, year }: { search: string; year?: number }
</Group> </Group>
</Stack> </Stack>
</Paper> </Paper>
)) );
})
) : ( ) : (
<Center py={20}> <Center py={20}>
<Text c="dimmed" fz="sm" lh={1.4}> <Text c="dimmed" fz="sm" lh={1.4}>

View File

@@ -19,8 +19,8 @@ export default async function migrasiPendudukCreate(context: Context) {
jenis: body.jenis as 'MASUK' | 'KELUAR', jenis: body.jenis as 'MASUK' | 'KELUAR',
nama: body.nama, nama: body.nama,
tanggal: new Date(body.tanggal), tanggal: new Date(body.tanggal),
asal: isMasuk ? body.asalTujuan : undefined, asal: isMasuk ? body.asalTujuan : null,
tujuan: !isMasuk ? body.asalTujuan : undefined, tujuan: !isMasuk ? body.asalTujuan : null,
alasan: body.alasan, alasan: body.alasan,
}, },
select: { select: {
@@ -28,6 +28,8 @@ export default async function migrasiPendudukCreate(context: Context) {
jenis: true, jenis: true,
nama: true, nama: true,
tanggal: true, tanggal: true,
asal: true,
tujuan: true,
alasan: true, alasan: true,
} }
}); });

View File

@@ -21,7 +21,6 @@ const MigrasiPenduduk = new Elysia({
tanggal: t.String(), tanggal: t.String(),
asalTujuan: t.String(), asalTujuan: t.String(),
alasan: t.Optional(t.String()), alasan: t.Optional(t.String()),
jenisKelamin: t.Optional(t.String()),
}), }),
}) })
.put("/:id", migrasiPendudukUpdate, { .put("/:id", migrasiPendudukUpdate, {
@@ -34,7 +33,6 @@ const MigrasiPenduduk = new Elysia({
tanggal: t.String(), tanggal: t.String(),
asalTujuan: t.String(), asalTujuan: t.String(),
alasan: t.Optional(t.String()), alasan: t.Optional(t.String()),
jenisKelamin: t.Optional(t.String()),
}), }),
}) })
.delete("/del/:id", migrasiPendudukDelete, { .delete("/del/:id", migrasiPendudukDelete, {

View File

@@ -38,10 +38,19 @@ export default async function migrasiPendudukUpdate(context: Context) {
jenis: jenis as 'MASUK' | 'KELUAR', jenis: jenis as 'MASUK' | 'KELUAR',
nama, nama,
tanggal: new Date(tanggal), tanggal: new Date(tanggal),
asal: jenis === 'MASUK' ? asalTujuan : undefined, asal: jenis === 'MASUK' ? asalTujuan : null,
tujuan: jenis === 'KELUAR' ? asalTujuan : undefined, tujuan: jenis === 'KELUAR' ? asalTujuan : null,
alasan, alasan,
}, },
select: {
id: true,
jenis: true,
nama: true,
tanggal: true,
asal: true,
tujuan: true,
alasan: true,
},
}) })
return { return {