refactor: reorganize imports and improve code formatting in PayrollFormModal component

This commit is contained in:
Yoga Pangestu 2026-04-30 14:07:07 +07:00
parent fb1ad29741
commit 29ca4f8ec4

View File

@ -1,9 +1,3 @@
import { useForm } from '@inertiajs/react';
import { Loader, Plus, Trash2 } from 'lucide-react';
import { X, Save } from 'lucide-react';
import { useEffect } from 'react';
import { NumericFormat } from 'react-number-format';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
@ -11,14 +5,24 @@ import {
DialogFooter, DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@/components/ui/dialog" } from '@/components/ui/dialog';
import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty'; import {
import { FieldGroup } from "@/components/ui/field" Empty,
import { Input } from "@/components/ui/input" EmptyDescription,
import { Label } from "@/components/ui/label" EmptyHeader,
EmptyTitle,
} from '@/components/ui/empty';
import { FieldGroup } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import payrollRoutes from '@/routes/payroll'; import payrollRoutes from '@/routes/payroll';
import type { Payroll } from '@/types'; import type { Payroll } from '@/types';
import { useForm } from '@inertiajs/react';
import { Loader, Plus, Save, Trash2, X } from 'lucide-react';
import { useEffect } from 'react';
import { NumericFormat } from 'react-number-format';
import { toast } from 'sonner';
interface PayrollFormModalProps { interface PayrollFormModalProps {
isOpen: boolean; isOpen: boolean;
@ -26,19 +30,29 @@ interface PayrollFormModalProps {
payroll: Payroll | null; payroll: Payroll | null;
} }
export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalProps) { export function PayrollFormModal({
const { data, setData, patch, processing, errors, reset, clearErrors } = useForm({ isOpen,
adjustments: [] as { type: 'bonus' | 'deduction', amount: number, description: string }[], onClose,
}); payroll,
}: PayrollFormModalProps) {
const { data, setData, patch, processing, errors, reset, clearErrors } =
useForm({
adjustments: [] as {
type: 'bonus' | 'deduction';
amount: number;
description: string;
}[],
});
useEffect(() => { useEffect(() => {
if (payroll) { if (payroll) {
setData({ setData({
adjustments: payroll.adjustments?.map(adj => ({ adjustments:
type: adj.type, payroll.adjustments?.map((adj) => ({
amount: adj.amount, type: adj.type,
description: adj.description amount: adj.amount,
})) || [], description: adj.description,
})) || [],
}); });
} else { } else {
reset(); reset();
@ -56,7 +70,7 @@ export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalP
const addAdjustment = () => { const addAdjustment = () => {
setData('adjustments', [ setData('adjustments', [
...data.adjustments, ...data.adjustments,
{ type: 'bonus', amount: 0, description: '' } { type: 'bonus', amount: 0, description: '' },
]); ]);
}; };
@ -89,14 +103,24 @@ export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalP
<Dialog open={isOpen} onOpenChange={(open) => !open && handleClose()}> <Dialog open={isOpen} onOpenChange={(open) => !open && handleClose()}>
<DialogContent className="max-w-2xl"> <DialogContent className="max-w-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle>Penyesuaian Penggajian - {payroll?.user?.name}</DialogTitle> <DialogTitle>
Penyesuaian Penggajian - {payroll?.user?.name}
</DialogTitle>
</DialogHeader> </DialogHeader>
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
<div className="max-h-[60vh] overflow-y-auto px-1 py-2"> <div className="max-h-[60vh] overflow-y-auto px-1 py-2">
<FieldGroup> <FieldGroup>
<div className="flex items-center justify-between border-b pb-2 mb-4"> <div className="mb-4 flex items-center justify-between border-b pb-2">
<h3 className="text-sm font-medium">Bonus & Potongan</h3> <h3 className="text-sm font-medium">
<Button type="button" variant="outline" size="sm" onClick={addAdjustment} className="gap-2"> Bonus & Potongan
</h3>
<Button
type="button"
variant="outline"
size="sm"
onClick={addAdjustment}
className="gap-2"
>
<Plus className="size-4" /> <Plus className="size-4" />
Tambah Tambah
</Button> </Button>
@ -114,30 +138,61 @@ export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalP
)} )}
{data.adjustments.map((adj, index) => ( {data.adjustments.map((adj, index) => (
<div key={index} className="flex gap-3 items-start border-l-2 border-primary/20 pl-4 py-2 mb-4 group relative"> <div
<div className="flex flex-col gap-3 flex-1"> key={index}
className="group relative mb-4 flex items-start gap-3 border-l-2 border-primary/20 py-2 pl-4"
>
<div className="flex flex-1 flex-col gap-3">
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div> <div>
<Label className="text-[10px] uppercase mb-1">Jenis</Label> <Label className="mb-1 text-[10px] uppercase">
Jenis
</Label>
<RadioGroup <RadioGroup
value={adj.type} value={adj.type}
onValueChange={(val) => updateAdjustment(index, 'type', val)} onValueChange={(val) =>
className="flex items-center gap-6 w-fit" updateAdjustment(
index,
'type',
val,
)
}
className="flex w-fit items-center gap-6"
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<RadioGroupItem value="bonus" id={`bonus-${index}`} /> <RadioGroupItem
<Label htmlFor={`bonus-${index}`}>Bonus</Label> value="bonus"
id={`bonus-${index}`}
/>
<Label
htmlFor={`bonus-${index}`}
>
Bonus
</Label>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<RadioGroupItem value="deduction" id={`deduction-${index}`} /> <RadioGroupItem
<Label htmlFor={`deduction-${index}`}>Potongan</Label> value="deduction"
id={`deduction-${index}`}
/>
<Label
htmlFor={`deduction-${index}`}
>
Potongan
</Label>
</div> </div>
</RadioGroup> </RadioGroup>
</div> </div>
<div> <div>
<Label className="text-[10px] uppercase mb-1" htmlFor={`amount-${index}`} required>Jumlah</Label> <Label
className="mb-1 text-[10px] uppercase"
htmlFor={`amount-${index}`}
required
>
Jumlah
</Label>
<NumericFormat <NumericFormat
id={`amount-${index}`} id={`amount-${index}`}
customInput={Input} customInput={Input}
@ -146,23 +201,38 @@ export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalP
prefix="Rp " prefix="Rp "
value={adj.amount} value={adj.amount}
onValueChange={(values) => { onValueChange={(values) => {
updateAdjustment(index, 'amount', values.value) updateAdjustment(
index,
'amount',
values.value,
);
}} }}
placeholder="Rp 0" placeholder="Rp 0"
autoComplete='off' autoComplete="off"
/> />
</div> </div>
</div> </div>
<div> <div>
<Label className="text-[10px] uppercase mb-1" htmlFor={`description-${index}`} required>Keterangan</Label> <Label
className="mb-1 text-[10px] uppercase"
htmlFor={`description-${index}`}
required
>
Keterangan
</Label>
<Input <Input
id={`description-${index}`} id={`description-${index}`}
value={adj.description} value={adj.description}
onChange={e => onChange={(e) =>
updateAdjustment(index, 'description', e.target.value) updateAdjustment(
index,
'description',
e.target.value,
)
} }
placeholder="Contoh: Lembur" placeholder="Contoh: Lembur"
autoComplete="off"
/> />
</div> </div>
</div> </div>
@ -170,7 +240,7 @@ export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalP
type="button" type="button"
variant="ghost" variant="ghost"
size="icon" size="icon"
className="text-red-500 mt-6" className="mt-6 text-red-500"
onClick={() => removeAdjustment(index)} onClick={() => removeAdjustment(index)}
> >
<Trash2 className="size-4" /> <Trash2 className="size-4" />
@ -181,12 +251,25 @@ export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalP
</div> </div>
<DialogFooter className="mt-6"> <DialogFooter className="mt-6">
<Button type="button" variant="outline" className="gap-2" onClick={handleClose}> <Button
type="button"
variant="outline"
className="gap-2"
onClick={handleClose}
>
<X className="size-4" /> <X className="size-4" />
Batal Batal
</Button> </Button>
<Button type="submit" className="gap-2" disabled={processing}> <Button
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />} type="submit"
className="gap-2"
disabled={processing}
>
{processing ? (
<Loader className="size-4 animate-spin" />
) : (
<Save className="size-4" />
)}
{processing ? 'Menyimpan...' : 'Simpan'} {processing ? 'Menyimpan...' : 'Simpan'}
</Button> </Button>
</DialogFooter> </DialogFooter>