473 lines
24 KiB
TypeScript
473 lines
24 KiB
TypeScript
import { Button } from '@/components/ui/button';
|
|
import { Calendar } from '@/components/ui/calendar';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Field, FieldError } from '@/components/ui/field';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from '@/components/ui/popover';
|
|
|
|
import {
|
|
Combobox,
|
|
ComboboxChip,
|
|
ComboboxChips,
|
|
ComboboxChipsInput,
|
|
ComboboxContent,
|
|
ComboboxEmpty,
|
|
ComboboxItem,
|
|
ComboboxList,
|
|
ComboboxValue,
|
|
useComboboxAnchor,
|
|
} from '@/components/ui/combobox';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import userRoutes from '@/routes/user';
|
|
import { Head, Link, useForm } from '@inertiajs/react';
|
|
import { ArrowLeft, Loader, Save } from 'lucide-react';
|
|
import React from 'react';
|
|
import { NumericFormat } from 'react-number-format';
|
|
import { toast } from 'sonner';
|
|
|
|
export default function UserCreate({ roles }: { roles: any[] }) {
|
|
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
|
|
|
|
const { data, setData, post, processing, errors } = useForm({
|
|
username: '',
|
|
email: '',
|
|
nik: '',
|
|
full_name: '',
|
|
phone_number: '',
|
|
address: '',
|
|
birth_place: '',
|
|
birth_date: '',
|
|
base_salary: 0,
|
|
roles: [] as string[],
|
|
});
|
|
|
|
const onSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
post(userRoutes.store().url, {
|
|
onSuccess: (response: any) => {
|
|
const flash = response.props.flash;
|
|
if (flash?.error) {
|
|
toast.error(flash.error);
|
|
} else if (flash?.success) {
|
|
toast.success(flash.success);
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
const anchor = useComboboxAnchor();
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6 p-6">
|
|
<Head title="Tambah Pegawai" />
|
|
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold tracking-tight">
|
|
Tambah Pegawai
|
|
</h1>
|
|
</div>
|
|
<Link href={userRoutes.index().url}>
|
|
<Button variant="outline" className="gap-2">
|
|
<ArrowLeft className="size-4" />
|
|
Kembali
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
|
|
<form onSubmit={onSubmit} className="space-y-6">
|
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
<div className="space-y-6 lg:col-span-2">
|
|
<Card className="overflow-hidden border-none bg-card/50 shadow-lg backdrop-blur-sm">
|
|
<CardHeader className="border-b">
|
|
<CardTitle>Informasi Profil</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
<Field>
|
|
<Label htmlFor="full_name" required>
|
|
Nama Lengkap
|
|
</Label>
|
|
<Input
|
|
id="full_name"
|
|
name="full_name"
|
|
value={data.full_name}
|
|
onChange={(e) =>
|
|
setData(
|
|
'full_name',
|
|
e.target.value,
|
|
)
|
|
}
|
|
autoComplete="off"
|
|
placeholder="Contoh: John Doe"
|
|
/>
|
|
<FieldError
|
|
error={errors.full_name}
|
|
label="Nama Lengkap"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="nik" required>
|
|
NIK
|
|
</Label>
|
|
<Input
|
|
id="nik"
|
|
name="nik"
|
|
value={data.nik}
|
|
onChange={(e) =>
|
|
setData('nik', e.target.value)
|
|
}
|
|
autoComplete="off"
|
|
placeholder="Contoh: 3213051307900001"
|
|
maxLength={16}
|
|
/>
|
|
<FieldError
|
|
error={errors.nik}
|
|
label="NIK"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="phone_number" required>
|
|
Nomor Telepon
|
|
</Label>
|
|
<Input
|
|
id="phone_number"
|
|
name="phone_number"
|
|
value={data.phone_number}
|
|
onChange={(e) =>
|
|
setData(
|
|
'phone_number',
|
|
e.target.value,
|
|
)
|
|
}
|
|
autoComplete="off"
|
|
placeholder="Contoh: 08123456789"
|
|
/>
|
|
<FieldError
|
|
error={errors.phone_number}
|
|
label="Nomor Telepon"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="base_salary" required>
|
|
Gaji Pokok
|
|
</Label>
|
|
<NumericFormat
|
|
id="base_salary"
|
|
customInput={Input}
|
|
thousandSeparator="."
|
|
decimalSeparator=","
|
|
prefix="Rp "
|
|
value={data.base_salary}
|
|
onValueChange={(values) => {
|
|
setData(
|
|
'base_salary',
|
|
values.floatValue || 0,
|
|
);
|
|
}}
|
|
placeholder="Rp 0"
|
|
autoComplete="off"
|
|
/>
|
|
<FieldError
|
|
error={errors.base_salary}
|
|
label="Gaji Pokok"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="birth_place" required>
|
|
Tempat Lahir
|
|
</Label>
|
|
<Input
|
|
id="birth_place"
|
|
name="birth_place"
|
|
value={data.birth_place}
|
|
onChange={(e) =>
|
|
setData(
|
|
'birth_place',
|
|
e.target.value,
|
|
)
|
|
}
|
|
autoComplete="off"
|
|
placeholder="Contoh: Jakarta"
|
|
/>
|
|
<FieldError
|
|
error={errors.birth_place}
|
|
label="Tempat Lahir"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="birth_date" required>
|
|
Tanggal Lahir
|
|
</Label>
|
|
<Popover
|
|
open={isCalendarOpen}
|
|
onOpenChange={setIsCalendarOpen}
|
|
>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
variant="outline"
|
|
id="birth_date"
|
|
className="w-full justify-start font-normal"
|
|
>
|
|
{data.birth_date ? (
|
|
new Intl.DateTimeFormat(
|
|
'id-ID',
|
|
{
|
|
day: 'numeric',
|
|
month: 'long',
|
|
year: 'numeric',
|
|
},
|
|
).format(
|
|
new Date(
|
|
data.birth_date,
|
|
),
|
|
)
|
|
) : (
|
|
<span className="text-muted-foreground">
|
|
Pilih Tanggal
|
|
</span>
|
|
)}
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent
|
|
className="w-auto overflow-hidden p-0"
|
|
align="start"
|
|
>
|
|
<Calendar
|
|
mode="single"
|
|
selected={
|
|
data.birth_date
|
|
? new Date(
|
|
data.birth_date,
|
|
)
|
|
: undefined
|
|
}
|
|
defaultMonth={
|
|
data.birth_date
|
|
? new Date(
|
|
data.birth_date,
|
|
)
|
|
: new Date(
|
|
2000,
|
|
0,
|
|
1,
|
|
)
|
|
}
|
|
captionLayout="dropdown"
|
|
onSelect={(
|
|
selectedDate:
|
|
| Date
|
|
| undefined,
|
|
) => {
|
|
if (selectedDate) {
|
|
setData(
|
|
'birth_date',
|
|
selectedDate.getFullYear() +
|
|
'-' +
|
|
String(
|
|
selectedDate.getMonth() +
|
|
1,
|
|
).padStart(
|
|
2,
|
|
'0',
|
|
) +
|
|
'-' +
|
|
String(
|
|
selectedDate.getDate(),
|
|
).padStart(
|
|
2,
|
|
'0',
|
|
),
|
|
);
|
|
} else {
|
|
setData(
|
|
'birth_date',
|
|
'',
|
|
);
|
|
}
|
|
|
|
setIsCalendarOpen(
|
|
false,
|
|
);
|
|
}}
|
|
/>
|
|
</PopoverContent>
|
|
</Popover>
|
|
<FieldError
|
|
error={errors.birth_date}
|
|
label="Tanggal Lahir"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
</div>
|
|
|
|
<Field>
|
|
<Label htmlFor="address" required>
|
|
Alamat
|
|
</Label>
|
|
<Textarea
|
|
id="address"
|
|
name="address"
|
|
value={data.address}
|
|
onChange={(e) =>
|
|
setData('address', e.target.value)
|
|
}
|
|
placeholder="Contoh: Kp. Bakan Sampeu"
|
|
/>
|
|
<FieldError
|
|
error={errors.address}
|
|
label="Alamat"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="space-y-6">
|
|
<Card className="overflow-hidden border-none bg-card/50 shadow-lg backdrop-blur-sm">
|
|
<CardHeader className="border-b">
|
|
<CardTitle>Kredensial Akun</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<Field>
|
|
<Label htmlFor="email" required>
|
|
Alamat Surel
|
|
</Label>
|
|
<Input
|
|
id="email"
|
|
name="email"
|
|
type="email"
|
|
value={data.email}
|
|
onChange={(e) =>
|
|
setData('email', e.target.value)
|
|
}
|
|
autoComplete="off"
|
|
placeholder="Contoh: john@example.com"
|
|
/>
|
|
<FieldError
|
|
error={errors.email}
|
|
label="Alamat Surel"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="username" required>
|
|
Nama Pengguna
|
|
</Label>
|
|
<Input
|
|
id="username"
|
|
name="username"
|
|
value={data.username}
|
|
onChange={(e) =>
|
|
setData('username', e.target.value)
|
|
}
|
|
autoComplete="off"
|
|
placeholder="Contoh: johndoe"
|
|
/>
|
|
<FieldError
|
|
error={errors.username}
|
|
label="Nama Pengguna"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="roles" required>
|
|
Peran
|
|
</Label>
|
|
<Combobox
|
|
multiple
|
|
autoHighlight
|
|
items={roles.map((r) => r.name)}
|
|
value={data.roles}
|
|
onValueChange={(val) =>
|
|
setData('roles', val)
|
|
}
|
|
>
|
|
<ComboboxChips
|
|
ref={anchor}
|
|
className="w-full"
|
|
>
|
|
<ComboboxValue>
|
|
{(values) => (
|
|
<React.Fragment>
|
|
{values.map(
|
|
(value: string) => (
|
|
<ComboboxChip
|
|
key={value}
|
|
value={value}
|
|
>
|
|
{value}
|
|
</ComboboxChip>
|
|
),
|
|
)}
|
|
<ComboboxChipsInput placeholder="Pilih Peran" />
|
|
</React.Fragment>
|
|
)}
|
|
</ComboboxValue>
|
|
</ComboboxChips>
|
|
<ComboboxContent anchor={anchor}>
|
|
<ComboboxEmpty>
|
|
Peran tidak ditemukan
|
|
</ComboboxEmpty>
|
|
<ComboboxList>
|
|
{(item: string) => (
|
|
<ComboboxItem
|
|
key={item}
|
|
value={item}
|
|
>
|
|
{item}
|
|
</ComboboxItem>
|
|
)}
|
|
</ComboboxList>
|
|
</ComboboxContent>
|
|
</Combobox>
|
|
<FieldError
|
|
error={errors.roles}
|
|
label="Peran"
|
|
className="text-xs"
|
|
/>
|
|
</Field>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
|
|
<Button type="submit" disabled={processing}>
|
|
{processing ? (
|
|
<Loader className="size-4 animate-spin" />
|
|
) : (
|
|
<Save className="size-4" />
|
|
)}
|
|
{processing ? 'Menyimpan...' : 'Simpan'}
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
UserCreate.layout = {
|
|
breadcrumbs: [
|
|
{
|
|
title: 'Master',
|
|
},
|
|
],
|
|
};
|