428 lines
23 KiB
TypeScript
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/inputs';
|
|
import { InputError } from '@/components/ui';
|
|
import { PhoneNumberInput } from '@/components/inputs';
|
|
import { RupiahInput } from '@/components/inputs';
|
|
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('Ada data yang belum sesuai, 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>
|
|
</>
|
|
);
|
|
}
|