feat: add role selection to employee create and edit forms with conditional fields
This commit is contained in:
parent
318afca796
commit
81f8135273
@ -10,6 +10,7 @@
|
|||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use Inertia\Response;
|
use Inertia\Response;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
class EmployeeController extends Controller
|
class EmployeeController extends Controller
|
||||||
{
|
{
|
||||||
@ -30,13 +31,15 @@ public function index(PaginatedRequest $request): Response
|
|||||||
|
|
||||||
public function create(): Response
|
public function create(): Response
|
||||||
{
|
{
|
||||||
return Inertia::render('admin/hr/employee/create');
|
return Inertia::render('admin/hr/employee/create', [
|
||||||
|
'roles' => Role::where('name', '!=', 'Developer')->get(['id', 'name']),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(EmployeeRequest $request): RedirectResponse
|
public function store(EmployeeRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
return $this->handleAction(
|
return $this->handleAction(
|
||||||
fn () => $this->service->create($request->validated()),
|
fn() => $this->service->create($request->validated()),
|
||||||
'Pegawai berhasil ditambahkan.',
|
'Pegawai berhasil ditambahkan.',
|
||||||
'admin.hr.employees.index'
|
'admin.hr.employees.index'
|
||||||
);
|
);
|
||||||
@ -44,17 +47,18 @@ public function store(EmployeeRequest $request): RedirectResponse
|
|||||||
|
|
||||||
public function edit(User $user): Response
|
public function edit(User $user): Response
|
||||||
{
|
{
|
||||||
$user->load(['userProfile', 'employee']);
|
$user->load(['userProfile', 'employee', 'roles']);
|
||||||
|
|
||||||
return Inertia::render('admin/hr/employee/edit', [
|
return Inertia::render('admin/hr/employee/edit', [
|
||||||
'employee' => $user,
|
'employee' => $user,
|
||||||
|
'roles' => Role::where('name', '!=', 'Developer')->get(['id', 'name']),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(EmployeeRequest $request, User $user): RedirectResponse
|
public function update(EmployeeRequest $request, User $user): RedirectResponse
|
||||||
{
|
{
|
||||||
return $this->handleAction(
|
return $this->handleAction(
|
||||||
fn () => $this->service->update($user, $request->validated()),
|
fn() => $this->service->update($user, $request->validated()),
|
||||||
'Pegawai berhasil diperbarui.',
|
'Pegawai berhasil diperbarui.',
|
||||||
'admin.hr.employees.index'
|
'admin.hr.employees.index'
|
||||||
);
|
);
|
||||||
@ -63,7 +67,7 @@ public function update(EmployeeRequest $request, User $user): RedirectResponse
|
|||||||
public function destroy(User $user): RedirectResponse
|
public function destroy(User $user): RedirectResponse
|
||||||
{
|
{
|
||||||
return $this->handleAction(
|
return $this->handleAction(
|
||||||
fn () => $this->service->delete($user),
|
fn() => $this->service->delete($user),
|
||||||
'Pegawai berhasil dihapus.',
|
'Pegawai berhasil dihapus.',
|
||||||
'admin.hr.employees.index'
|
'admin.hr.employees.index'
|
||||||
);
|
);
|
||||||
@ -82,7 +86,7 @@ public function toggleActive(User $user): RedirectResponse
|
|||||||
public function resetPassword(User $user): RedirectResponse
|
public function resetPassword(User $user): RedirectResponse
|
||||||
{
|
{
|
||||||
return $this->handleAction(
|
return $this->handleAction(
|
||||||
fn () => $this->service->resetPassword($user),
|
fn() => $this->service->resetPassword($user),
|
||||||
'Kata sandi pegawai berhasil direset ke kata sandi default.',
|
'Kata sandi pegawai berhasil direset ke kata sandi default.',
|
||||||
'admin.hr.employees.index'
|
'admin.hr.employees.index'
|
||||||
);
|
);
|
||||||
|
|||||||
@ -15,6 +15,7 @@ public function authorize(): bool
|
|||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
$userId = $this->route('user')?->id;
|
$userId = $this->route('user')?->id;
|
||||||
|
$isOwner = $this->input('role') === 'owner';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'email' => [
|
'email' => [
|
||||||
@ -30,15 +31,30 @@ public function rules(): array
|
|||||||
'alpha_dash',
|
'alpha_dash',
|
||||||
Rule::unique('users', 'username')->ignore($userId, 'id'),
|
Rule::unique('users', 'username')->ignore($userId, 'id'),
|
||||||
],
|
],
|
||||||
|
'role' => ['required', 'string', Rule::exists('roles', 'name')],
|
||||||
'full_name' => ['required', 'string', 'max:200'],
|
'full_name' => ['required', 'string', 'max:200'],
|
||||||
'phone_number' => ['nullable', 'string', 'max:20'],
|
'phone_number' => ['nullable', 'string', 'max:20'],
|
||||||
'gender' => ['nullable', 'in:male,female'],
|
'gender' => ['nullable', 'in:male,female'],
|
||||||
'birth_date' => ['nullable', 'date'],
|
'birth_date' => ['nullable', 'date'],
|
||||||
'address' => ['nullable', 'string'],
|
'address' => ['nullable', 'string'],
|
||||||
'join_date' => ['required', 'date'],
|
'join_date' => [
|
||||||
'resign_date' => ['nullable', 'date', 'after_or_equal:join_date'],
|
'date',
|
||||||
'employment_status' => ['required', Rule::in(['full_time', 'part_time', 'contract', 'internship', 'resigned'])],
|
Rule::requiredIf(! $isOwner),
|
||||||
'base_salary' => ['required', 'integer', 'min:0'],
|
],
|
||||||
|
'resign_date' => [
|
||||||
|
'nullable',
|
||||||
|
'date',
|
||||||
|
'after_or_equal:join_date',
|
||||||
|
],
|
||||||
|
'employment_status' => [
|
||||||
|
Rule::requiredIf(! $isOwner),
|
||||||
|
Rule::in(['full_time', 'part_time', 'contract', 'internship', 'resigned']),
|
||||||
|
],
|
||||||
|
'base_salary' => [
|
||||||
|
Rule::requiredIf(! $isOwner),
|
||||||
|
'integer',
|
||||||
|
'min:0',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +63,7 @@ public function attributes(): array
|
|||||||
return [
|
return [
|
||||||
'email' => 'Email',
|
'email' => 'Email',
|
||||||
'username' => 'Username',
|
'username' => 'Username',
|
||||||
|
'role' => 'Role',
|
||||||
'full_name' => 'Nama Lengkap',
|
'full_name' => 'Nama Lengkap',
|
||||||
'phone_number' => 'No. Telepon',
|
'phone_number' => 'No. Telepon',
|
||||||
'gender' => 'Jenis Kelamin',
|
'gender' => 'Jenis Kelamin',
|
||||||
|
|||||||
@ -8,6 +8,14 @@ import { RupiahInput } from '@/components/rupiah-input';
|
|||||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
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 { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||||
@ -21,9 +29,19 @@ import {
|
|||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { index as employeeIndex, store } from '@/routes/admin/hr/employees';
|
import { index as employeeIndex, store } from '@/routes/admin/hr/employees';
|
||||||
|
|
||||||
export default function EmployeeCreate() {
|
type Role = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
roles: Role[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function EmployeeCreate({ roles }: Props) {
|
||||||
const [joinDate, setJoinDate] = useState<Date | undefined>(undefined);
|
const [joinDate, setJoinDate] = useState<Date | undefined>(undefined);
|
||||||
const [resignDate, setResignDate] = useState<Date | undefined>(undefined);
|
const [resignDate, setResignDate] = useState<Date | undefined>(undefined);
|
||||||
|
const [selectedRole, setSelectedRole] = useState<Role | null>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -55,12 +73,17 @@ export default function EmployeeCreate() {
|
|||||||
<Form action={store()}>
|
<Form action={store()}>
|
||||||
{({ errors, processing }) => (
|
{({ errors, processing }) => (
|
||||||
<>
|
<>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="role"
|
||||||
|
value={selectedRole?.name ?? ''}
|
||||||
|
/>
|
||||||
<div className="grid gap-6">
|
<div className="grid gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Akun</CardTitle>
|
<CardTitle>Akun</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="email">
|
<Label htmlFor="email">
|
||||||
Email{' '}
|
Email{' '}
|
||||||
@ -94,6 +117,46 @@ export default function EmployeeCreate() {
|
|||||||
message={errors.username}
|
message={errors.username}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<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>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@ -191,94 +254,100 @@ export default function EmployeeCreate() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
{selectedRole?.name !== 'owner' && (
|
||||||
<CardHeader>
|
<Card>
|
||||||
<CardTitle>Pegawai</CardTitle>
|
<CardHeader>
|
||||||
</CardHeader>
|
<CardTitle>Pegawai</CardTitle>
|
||||||
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
</CardHeader>
|
||||||
<div className="grid gap-2">
|
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
<Label>
|
<div className="grid gap-2">
|
||||||
Tanggal Masuk{' '}
|
<Label>
|
||||||
<span className="text-destructive">
|
Tanggal Masuk{' '}
|
||||||
*
|
<span className="text-destructive">
|
||||||
</span>
|
*
|
||||||
</Label>
|
</span>
|
||||||
<DatePicker
|
</Label>
|
||||||
name="join_date"
|
<DatePicker
|
||||||
value={joinDate}
|
name="join_date"
|
||||||
onChange={setJoinDate}
|
value={joinDate}
|
||||||
placeholder="Pilih tanggal masuk"
|
onChange={setJoinDate}
|
||||||
/>
|
placeholder="Pilih tanggal masuk"
|
||||||
<InputError
|
/>
|
||||||
message={errors.join_date}
|
<InputError
|
||||||
/>
|
message={errors.join_date}
|
||||||
</div>
|
/>
|
||||||
<div className="grid gap-2">
|
</div>
|
||||||
<Label>Tanggal Keluar</Label>
|
<div className="grid gap-2">
|
||||||
<DatePicker
|
<Label>Tanggal Keluar</Label>
|
||||||
name="resign_date"
|
<DatePicker
|
||||||
value={resignDate}
|
name="resign_date"
|
||||||
onChange={setResignDate}
|
value={resignDate}
|
||||||
placeholder="Pilih tanggal keluar"
|
onChange={setResignDate}
|
||||||
/>
|
placeholder="Pilih tanggal keluar"
|
||||||
<InputError
|
/>
|
||||||
message={errors.resign_date}
|
<InputError
|
||||||
/>
|
message={
|
||||||
</div>
|
errors.resign_date
|
||||||
<div className="grid gap-2">
|
}
|
||||||
<Label>
|
/>
|
||||||
Status Kepegawaian{' '}
|
</div>
|
||||||
<span className="text-destructive">
|
<div className="grid gap-2">
|
||||||
*
|
<Label>
|
||||||
</span>
|
Status Kepegawaian{' '}
|
||||||
</Label>
|
<span className="text-destructive">
|
||||||
<Select
|
*
|
||||||
name="employment_status"
|
</span>
|
||||||
defaultValue="full_time"
|
</Label>
|
||||||
>
|
<Select
|
||||||
<SelectTrigger className="w-full">
|
name="employment_status"
|
||||||
<SelectValue placeholder="Pilih status kepegawaian" />
|
defaultValue="full_time"
|
||||||
</SelectTrigger>
|
>
|
||||||
<SelectContent>
|
<SelectTrigger className="w-full">
|
||||||
<SelectItem value="full_time">
|
<SelectValue placeholder="Pilih status kepegawaian" />
|
||||||
Full Time
|
</SelectTrigger>
|
||||||
</SelectItem>
|
<SelectContent>
|
||||||
<SelectItem value="part_time">
|
<SelectItem value="full_time">
|
||||||
Part Time
|
Full Time
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="contract">
|
<SelectItem value="part_time">
|
||||||
Kontrak
|
Part Time
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="internship">
|
<SelectItem value="contract">
|
||||||
Magang
|
Kontrak
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="resigned">
|
<SelectItem value="internship">
|
||||||
Keluar
|
Magang
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectContent>
|
<SelectItem value="resigned">
|
||||||
</Select>
|
Keluar
|
||||||
<InputError
|
</SelectItem>
|
||||||
message={
|
</SelectContent>
|
||||||
errors.employment_status
|
</Select>
|
||||||
}
|
<InputError
|
||||||
/>
|
message={
|
||||||
</div>
|
errors.employment_status
|
||||||
<div className="grid gap-2">
|
}
|
||||||
<Label htmlFor="base_salary">
|
/>
|
||||||
Gaji Pokok{' '}
|
</div>
|
||||||
<span className="text-destructive">
|
<div className="grid gap-2">
|
||||||
*
|
<Label htmlFor="base_salary">
|
||||||
</span>
|
Gaji Pokok{' '}
|
||||||
</Label>
|
<span className="text-destructive">
|
||||||
<RupiahInput
|
*
|
||||||
name="base_salary"
|
</span>
|
||||||
/>
|
</Label>
|
||||||
<InputError
|
<RupiahInput
|
||||||
message={errors.base_salary}
|
name="base_salary"
|
||||||
/>
|
/>
|
||||||
</div>
|
<InputError
|
||||||
</CardContent>
|
message={
|
||||||
</Card>
|
errors.base_salary
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 flex items-center gap-4">
|
<div className="mt-6 flex items-center gap-4">
|
||||||
|
|||||||
@ -1,12 +1,20 @@
|
|||||||
import { Form, Head } from '@inertiajs/react';
|
import { Form, Head } from '@inertiajs/react';
|
||||||
import { ArrowLeft } from 'lucide-react';
|
import { ArrowLeft } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { DatePicker } from '@/components/date-picker';
|
import { DatePicker } from '@/components/date-picker';
|
||||||
import InputError from '@/components/input-error';
|
import InputError from '@/components/input-error';
|
||||||
import { PhoneNumberInput } from '@/components/phone-number-input';
|
import { PhoneNumberInput } from '@/components/phone-number-input';
|
||||||
import { RupiahInput } from '@/components/rupiah-input';
|
import { RupiahInput } from '@/components/rupiah-input';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
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 { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||||
@ -20,10 +28,16 @@ import {
|
|||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { index as employeeIndex, update } from '@/routes/admin/hr/employees';
|
import { index as employeeIndex, update } from '@/routes/admin/hr/employees';
|
||||||
|
|
||||||
|
type Role = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
type EmployeeData = {
|
type EmployeeData = {
|
||||||
id: number;
|
id: number;
|
||||||
email: string;
|
email: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
roles?: { name: string }[];
|
||||||
user_profile: {
|
user_profile: {
|
||||||
full_name: string;
|
full_name: string;
|
||||||
phone_number: string | null;
|
phone_number: string | null;
|
||||||
@ -41,9 +55,16 @@ type EmployeeData = {
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
employee: EmployeeData;
|
employee: EmployeeData;
|
||||||
|
roles: Role[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function EmployeeEdit({ employee }: Props) {
|
export default function EmployeeEdit({ employee, roles }: Props) {
|
||||||
|
const currentRole = useMemo(
|
||||||
|
() =>
|
||||||
|
roles.find((r) => r.name === employee.roles?.[0]?.name) ?? null,
|
||||||
|
[roles, employee.roles],
|
||||||
|
);
|
||||||
|
|
||||||
const [joinDate, setJoinDate] = useState<Date | undefined>(
|
const [joinDate, setJoinDate] = useState<Date | undefined>(
|
||||||
employee.employee?.join_date
|
employee.employee?.join_date
|
||||||
? new Date(employee.employee.join_date)
|
? new Date(employee.employee.join_date)
|
||||||
@ -59,6 +80,7 @@ export default function EmployeeEdit({ employee }: Props) {
|
|||||||
? new Date(employee.user_profile.birth_date)
|
? new Date(employee.user_profile.birth_date)
|
||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
|
const [selectedRole, setSelectedRole] = useState<Role | null>(currentRole);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -85,12 +107,17 @@ export default function EmployeeEdit({ employee }: Props) {
|
|||||||
>
|
>
|
||||||
{({ errors, processing }) => (
|
{({ errors, processing }) => (
|
||||||
<>
|
<>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="role"
|
||||||
|
value={selectedRole?.name ?? ''}
|
||||||
|
/>
|
||||||
<div className="grid gap-6">
|
<div className="grid gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Akun</CardTitle>
|
<CardTitle>Akun</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="email">
|
<Label htmlFor="email">
|
||||||
Email{' '}
|
Email{' '}
|
||||||
@ -126,6 +153,46 @@ export default function EmployeeEdit({ employee }: Props) {
|
|||||||
message={errors.username}
|
message={errors.username}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<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>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@ -235,98 +302,104 @@ export default function EmployeeEdit({ employee }: Props) {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
{selectedRole?.name !== 'owner' && (
|
||||||
<CardHeader>
|
<Card>
|
||||||
<CardTitle>Pegawai</CardTitle>
|
<CardHeader>
|
||||||
</CardHeader>
|
<CardTitle>Pegawai</CardTitle>
|
||||||
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
</CardHeader>
|
||||||
<div className="grid gap-2">
|
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
<Label>
|
<div className="grid gap-2">
|
||||||
Tanggal Masuk{' '}
|
<Label>
|
||||||
<span className="text-destructive">
|
Tanggal Masuk{' '}
|
||||||
*
|
<span className="text-destructive">
|
||||||
</span>
|
*
|
||||||
</Label>
|
</span>
|
||||||
<DatePicker
|
</Label>
|
||||||
name="join_date"
|
<DatePicker
|
||||||
value={joinDate}
|
name="join_date"
|
||||||
onChange={setJoinDate}
|
value={joinDate}
|
||||||
placeholder="Pilih tanggal masuk"
|
onChange={setJoinDate}
|
||||||
/>
|
placeholder="Pilih tanggal masuk"
|
||||||
<InputError
|
/>
|
||||||
message={errors.join_date}
|
<InputError
|
||||||
/>
|
message={errors.join_date}
|
||||||
</div>
|
/>
|
||||||
<div className="grid gap-2">
|
</div>
|
||||||
<Label>Tanggal Keluar</Label>
|
<div className="grid gap-2">
|
||||||
<DatePicker
|
<Label>Tanggal Keluar</Label>
|
||||||
name="resign_date"
|
<DatePicker
|
||||||
value={resignDate}
|
name="resign_date"
|
||||||
onChange={setResignDate}
|
value={resignDate}
|
||||||
placeholder="Pilih tanggal keluar"
|
onChange={setResignDate}
|
||||||
/>
|
placeholder="Pilih tanggal keluar"
|
||||||
<InputError
|
/>
|
||||||
message={errors.resign_date}
|
<InputError
|
||||||
/>
|
message={
|
||||||
</div>
|
errors.resign_date
|
||||||
<div className="grid gap-2">
|
}
|
||||||
<Label>
|
/>
|
||||||
Status Kepegawaian{' '}
|
</div>
|
||||||
<span className="text-destructive">
|
<div className="grid gap-2">
|
||||||
*
|
<Label>
|
||||||
</span>
|
Status Kepegawaian{' '}
|
||||||
</Label>
|
<span className="text-destructive">
|
||||||
<Select
|
*
|
||||||
name="employment_status"
|
</span>
|
||||||
defaultValue={
|
</Label>
|
||||||
employee.employee
|
<Select
|
||||||
?.employment_status ??
|
name="employment_status"
|
||||||
'full_time'
|
defaultValue={
|
||||||
}
|
employee.employee
|
||||||
>
|
?.employment_status ??
|
||||||
<SelectTrigger className="w-full">
|
'full_time'
|
||||||
<SelectValue placeholder="Pilih status kepegawaian" />
|
}
|
||||||
</SelectTrigger>
|
>
|
||||||
<SelectContent>
|
<SelectTrigger className="w-full">
|
||||||
<SelectItem value="full_time">
|
<SelectValue placeholder="Pilih status kepegawaian" />
|
||||||
Full Time
|
</SelectTrigger>
|
||||||
</SelectItem>
|
<SelectContent>
|
||||||
<SelectItem value="part_time">
|
<SelectItem value="full_time">
|
||||||
Part Time
|
Full Time
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="contract">
|
<SelectItem value="part_time">
|
||||||
Kontrak
|
Part Time
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="internship">
|
<SelectItem value="contract">
|
||||||
Magang
|
Kontrak
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="resigned">
|
<SelectItem value="internship">
|
||||||
Keluar
|
Magang
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectContent>
|
<SelectItem value="resigned">
|
||||||
</Select>
|
Keluar
|
||||||
<InputError
|
</SelectItem>
|
||||||
message={
|
</SelectContent>
|
||||||
errors.employment_status
|
</Select>
|
||||||
}
|
<InputError
|
||||||
/>
|
message={
|
||||||
</div>
|
errors.employment_status
|
||||||
<div className="grid gap-2">
|
}
|
||||||
<Label htmlFor="base_salary">
|
/>
|
||||||
Gaji Pokok{' '}
|
</div>
|
||||||
<span className="text-destructive">
|
<div className="grid gap-2">
|
||||||
*
|
<Label htmlFor="base_salary">
|
||||||
</span>
|
Gaji Pokok{' '}
|
||||||
</Label>
|
<span className="text-destructive">
|
||||||
<RupiahInput
|
*
|
||||||
name="base_salary"
|
</span>
|
||||||
/>
|
</Label>
|
||||||
<InputError
|
<RupiahInput
|
||||||
message={errors.base_salary}
|
name="base_salary"
|
||||||
/>
|
/>
|
||||||
</div>
|
<InputError
|
||||||
</CardContent>
|
message={
|
||||||
</Card>
|
errors.base_salary
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 flex items-center gap-4">
|
<div className="mt-6 flex items-center gap-4">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user