dstpabuaran.com/resources/js/pages/admin/hr/employee/edit.tsx
Yoga Pangestu 50b2be68ac Add error handling with toast notifications for form submissions
- Integrated toast notifications to display error messages when form submissions fail across various components, enhancing user feedback and experience.
- Updated components including DeleteUser, FormDialog, ManageTwoFactor, TwoFactorRecoveryCodes, TwoFactorSetupModal, PayrollPeriodShow, EmployeeCreate, EmployeeEdit, CuttingCreate, CuttingEdit, PurchaseCreate, PurchaseEdit, RestockCreate, RestockEdit, TransactionCreate, TransactionEdit, Category management, Product management, Raw Material management, Role management, Settings, and Authentication pages.
2026-08-07 08:50:11 +07:00

428 lines
23 KiB
TypeScript

import { Form, Head, Link } from '@inertiajs/react';
import { ArrowLeft } from 'lucide-react';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { DatePicker } from '@/components/date-picker';
import InputError from '@/components/input-error';
import { PhoneNumberInput } from '@/components/phone-number-input';
import { RupiahInput } from '@/components/rupiah-input';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from '@/components/ui/combobox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
import { index as employeeIndex, update } from '@/routes/admin/hr/employees';
type Role = {
id: number;
name: string;
};
type EmployeeData = {
id: number;
email: string;
username: string;
roles?: { name: string }[];
user_profile: {
full_name: string;
phone_number: string | null;
gender: string | null;
birth_date: string | null;
address: string | null;
} | null;
employee: {
join_date: string;
resign_date: string | null;
employment_status: string;
base_salary: number;
} | null;
};
type Props = {
employee: EmployeeData;
roles: Role[];
canViewAll: boolean;
};
export default function EmployeeEdit({ employee, roles, canViewAll }: Props) {
const currentRole = useMemo(
() =>
roles.find((r) => r.name === employee.roles?.[0]?.name) ?? null,
[roles, employee.roles],
);
const [joinDate, setJoinDate] = useState<Date | undefined>(
employee.employee?.join_date
? new Date(employee.employee.join_date)
: undefined,
);
const [resignDate, setResignDate] = useState<Date | undefined>(
employee.employee?.resign_date
? new Date(employee.employee.resign_date)
: undefined,
);
const [birthDate, setBirthDate] = useState<Date | undefined>(
employee.user_profile?.birth_date
? new Date(employee.user_profile.birth_date)
: undefined,
);
const [selectedRole, setSelectedRole] = useState<Role | null>(currentRole);
return (
<>
<Head title="Edit Pegawai" />
<div className="flex h-full flex-1 flex-col gap-6 overflow-x-auto p-4 md:p-6">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-semibold tracking-tight">
Edit Pegawai
</h2>
</div>
<Button asChild variant="outline">
<Link href={employeeIndex.url()}>
<ArrowLeft className="h-4 w-4" />
Kembali
</Link>
</Button>
</div>
<Form
action={update(employee.id)}
resetOnSuccess={['password', 'password_confirmation']}
onError={() => {
toast.error('Terjadi kesalahan saat menyimpan data. Silakan periksa kembali input Anda.');
}}
>
{({ errors, processing }) => (
<>
<input
type="hidden"
name="role"
value={selectedRole?.name ?? ''}
/>
<div className="grid gap-6">
<Card>
<CardHeader>
<CardTitle>Akun</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="grid gap-2">
<Label htmlFor="email">
Email{' '}
<span className="text-destructive">
*
</span>
</Label>
<Input
id="email"
name="email"
type="email"
placeholder="Masukkan email"
defaultValue={employee.email}
/>
<InputError
message={errors.email}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="username">
Username{' '}
<span className="text-destructive">
*
</span>
</Label>
<Input
id="username"
name="username"
placeholder="Masukkan username"
defaultValue={employee.username}
/>
<InputError
message={errors.username}
/>
</div>
<div className="grid gap-2">
<Label>
Role{' '}
<span className="text-destructive">
*
</span>
</Label>
{canViewAll ? (
<Combobox
items={roles}
itemToStringLabel={(r) => r.name}
value={selectedRole}
onValueChange={(value) =>
setSelectedRole(value)
}
>
<ComboboxInput
placeholder="Cari role..."
className="w-full"
/>
<ComboboxContent>
<ComboboxEmpty>
Tidak ada role
ditemukan.
</ComboboxEmpty>
<ComboboxList>
{(role) => (
<ComboboxItem
key={role.id}
value={role}
>
{role.name}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
) : (
<div className="flex h-10 w-full items-center rounded-md border border-input bg-muted px-3 py-2 text-sm">
{selectedRole?.name ?? '-'}
</div>
)}
<InputError
message={errors.role}
/>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Profil</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="grid gap-2">
<Label htmlFor="full_name">
Nama Lengkap{' '}
<span className="text-destructive">
*
</span>
</Label>
<Input
id="full_name"
name="full_name"
placeholder="Masukkan nama lengkap"
defaultValue={
employee.user_profile
?.full_name ?? ''
}
/>
<InputError
message={errors.full_name}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="phone_number">
No. Telepon
</Label>
<PhoneNumberInput name="phone_number" />
<InputError
message={errors.phone_number}
/>
</div>
<div className="grid gap-2">
<Label>Jenis Kelamin</Label>
<RadioGroup
name="gender"
defaultValue={
employee.user_profile
?.gender ?? ''
}
className="flex gap-4"
>
<div className="flex items-center space-x-2">
<RadioGroupItem
value="male"
id="gender-male"
/>
<Label
htmlFor="gender-male"
className="font-normal"
>
Laki-laki
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem
value="female"
id="gender-female"
/>
<Label
htmlFor="gender-female"
className="font-normal"
>
Perempuan
</Label>
</div>
</RadioGroup>
<InputError
message={errors.gender}
/>
</div>
<div className="grid gap-2">
<Label>Tanggal Lahir</Label>
<DatePicker
name="birth_date"
value={birthDate}
onChange={setBirthDate}
placeholder="Pilih tanggal lahir"
/>
<InputError
message={errors.birth_date}
/>
</div>
<div className="grid gap-2 md:col-span-2">
<Label htmlFor="address">
Alamat
</Label>
<Textarea
id="address"
name="address"
placeholder="Masukkan alamat"
rows={3}
defaultValue={
employee.user_profile
?.address ?? ''
}
/>
<InputError
message={errors.address}
/>
</div>
</CardContent>
</Card>
{selectedRole?.name !== 'owner' && (
<Card>
<CardHeader>
<CardTitle>Pegawai</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="grid gap-2">
<Label>
Tanggal Masuk{' '}
<span className="text-destructive">
*
</span>
</Label>
<DatePicker
name="join_date"
value={joinDate}
onChange={setJoinDate}
placeholder="Pilih tanggal masuk"
/>
<InputError
message={errors.join_date}
/>
</div>
<div className="grid gap-2">
<Label>Tanggal Keluar</Label>
<DatePicker
name="resign_date"
value={resignDate}
onChange={setResignDate}
placeholder="Pilih tanggal keluar"
/>
<InputError
message={
errors.resign_date
}
/>
</div>
<div className="grid gap-2">
<Label>
Status Kepegawaian{' '}
<span className="text-destructive">
*
</span>
</Label>
<Select
name="employment_status"
defaultValue={
employee.employee
?.employment_status ??
'full_time'
}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Pilih status kepegawaian" />
</SelectTrigger>
<SelectContent>
<SelectItem value="full_time">
Full Time
</SelectItem>
<SelectItem value="part_time">
Part Time
</SelectItem>
<SelectItem value="contract">
Kontrak
</SelectItem>
<SelectItem value="internship">
Magang
</SelectItem>
<SelectItem value="resigned">
Keluar
</SelectItem>
</SelectContent>
</Select>
<InputError
message={
errors.employment_status
}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="base_salary">
Gaji Pokok{' '}
<span className="text-destructive">
*
</span>
</Label>
<RupiahInput
name="base_salary"
/>
<InputError
message={
errors.base_salary
}
/>
</div>
</CardContent>
</Card>
)}
</div>
<div className="mt-6 flex items-center gap-4">
<Button type="submit" disabled={processing}>
{processing ? 'Menyimpan...' : 'Simpan'}
</Button>
</div>
</>
)}
</Form>
</div>
</>
);
}