- 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.
389 lines
22 KiB
TypeScript
389 lines
22 KiB
TypeScript
import { Form, Head, Link } from '@inertiajs/react';
|
|
import { AlertCircle, ArrowLeft } from 'lucide-react';
|
|
import { 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 { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
|
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, store } from '@/routes/admin/hr/employees';
|
|
|
|
type Role = {
|
|
id: number;
|
|
name: string;
|
|
};
|
|
|
|
type Props = {
|
|
roles: Role[];
|
|
canViewAll: boolean;
|
|
};
|
|
|
|
export default function EmployeeCreate({ roles, canViewAll }: Props) {
|
|
const [joinDate, setJoinDate] = useState<Date | undefined>(undefined);
|
|
const [resignDate, setResignDate] = useState<Date | undefined>(undefined);
|
|
const [selectedRole, setSelectedRole] = useState<Role | null>(
|
|
!canViewAll && roles.length === 1 ? roles[0] : null,
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<Head title="Tambah 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">
|
|
Tambah Pegawai
|
|
</h2>
|
|
</div>
|
|
<Button asChild variant="outline">
|
|
<Link href={employeeIndex.url()}>
|
|
<ArrowLeft className="h-4 w-4" />
|
|
Kembali
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
<Alert>
|
|
<AlertCircle className="h-4 w-4" />
|
|
<AlertTitle>Informasi Kata Sandi</AlertTitle>
|
|
<AlertDescription>
|
|
Kata sandi default untuk akun baru adalah Minimal8@
|
|
</AlertDescription>
|
|
</Alert>
|
|
|
|
<Form
|
|
action={store()}
|
|
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"
|
|
/>
|
|
<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"
|
|
/>
|
|
<InputError
|
|
message={errors.username}
|
|
/>
|
|
</div>
|
|
{canViewAll ? (
|
|
<div className="grid gap-2">
|
|
<Label>
|
|
Role{' '}
|
|
<span className="text-destructive">
|
|
*
|
|
</span>
|
|
</Label>
|
|
<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>
|
|
<InputError
|
|
message={errors.role}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<div className="grid gap-2">
|
|
<Label>Role</Label>
|
|
<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>
|
|
</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"
|
|
/>
|
|
<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"
|
|
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={undefined}
|
|
onChange={() => {}}
|
|
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}
|
|
/>
|
|
<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="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>
|
|
</>
|
|
);
|
|
}
|