319 lines
16 KiB
TypeScript
319 lines
16 KiB
TypeScript
import { Button } from '@/components/ui/button';
|
|
import { Calendar } from '@/components/ui/calendar';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { useForm } from '@inertiajs/react';
|
|
import AppLayout from '@/layouts/app-layout';
|
|
import { Head } from '@inertiajs/react';
|
|
import { type BreadcrumbItem } from '@/types';
|
|
import InputError from '@/components/input-error';
|
|
import { format } from 'date-fns';
|
|
import { CalendarIcon } from 'lucide-react';
|
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface InsuranceFormProps {
|
|
mode: 'create' | 'edit';
|
|
insurance?: {
|
|
id?: string;
|
|
code: string;
|
|
name: string;
|
|
address?: string;
|
|
phone_number?: string;
|
|
email?: string;
|
|
contact_person?: string;
|
|
contact_person_phone?: string;
|
|
coverage_percentage: number;
|
|
coverage_description?: string;
|
|
agreement_details?: string;
|
|
agreement_start_date?: string;
|
|
agreement_end_date?: string;
|
|
agreement_file_path?: string;
|
|
is_active: boolean;
|
|
};
|
|
defaults?: {
|
|
coverage_percentage?: number;
|
|
is_active?: boolean;
|
|
};
|
|
}
|
|
|
|
const breadcrumbs: BreadcrumbItem[] = [
|
|
{ title: 'Dashboard', href: '/dashboard' },
|
|
{ title: 'Asuransi', href: '/insurances' },
|
|
{ title: 'Form', href: '#' },
|
|
];
|
|
|
|
const toLocalISOString = (date: Date) => {
|
|
const offset = date.getTimezoneOffset();
|
|
const localDate = new Date(date.getTime() - (offset * 60 * 1000));
|
|
return localDate.toISOString().split('T')[0];
|
|
};
|
|
|
|
export default function InsuranceForm({ mode, insurance, defaults }: InsuranceFormProps) {
|
|
const { data, setData, post, put, processing, errors, reset } = useForm({
|
|
code: insurance?.code || '',
|
|
name: insurance?.name || '',
|
|
address: insurance?.address || '',
|
|
phone_number: insurance?.phone_number || '',
|
|
email: insurance?.email || '',
|
|
contact_person: insurance?.contact_person || '',
|
|
contact_person_phone: insurance?.contact_person_phone || '',
|
|
coverage_percentage: insurance?.coverage_percentage || defaults?.coverage_percentage || 100,
|
|
coverage_description: insurance?.coverage_description || '',
|
|
agreement_details: insurance?.agreement_details || '',
|
|
agreement_start_date: insurance?.agreement_start_date || '',
|
|
agreement_end_date: insurance?.agreement_end_date || '',
|
|
agreement_file_path: insurance?.agreement_file_path || '',
|
|
is_active: insurance?.is_active ?? defaults?.is_active ?? true,
|
|
});
|
|
|
|
const onSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (mode === 'create') {
|
|
post(route('insurances.store'), {
|
|
onSuccess: () => reset(),
|
|
});
|
|
} else {
|
|
put(route('insurances.update', insurance?.id), {
|
|
preserveScroll: true,
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<AppLayout breadcrumbs={breadcrumbs}>
|
|
<Head title={`${mode === 'create' ? 'Tambah' : 'Edit'} Asuransi`} />
|
|
|
|
<div className="flex h-full flex-1 flex-col gap-4 rounded-xl p-4">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-2xl font-bold">
|
|
{mode === 'create' ? 'Tambah Asuransi Baru' : 'Edit Data Asuransi'}
|
|
</h1>
|
|
</div>
|
|
|
|
<div>
|
|
<form onSubmit={onSubmit} className="space-y-6">
|
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
{/* Informasi Dasar */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="code">Kode Asuransi*</Label>
|
|
<Input
|
|
id="code"
|
|
value={data.code}
|
|
onChange={(e) => setData('code', e.target.value)}
|
|
placeholder="BPJS, PRU, dll"
|
|
/>
|
|
<InputError message={errors.code} />
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="name">Nama Asuransi*</Label>
|
|
<Input
|
|
id="name"
|
|
value={data.name}
|
|
onChange={(e) => setData('name', e.target.value)}
|
|
placeholder="Nama lengkap asuransi"
|
|
/>
|
|
<InputError message={errors.name} />
|
|
</div>
|
|
|
|
{/* Kontak */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="phone_number">Nomor Telepon</Label>
|
|
<Input
|
|
id="phone_number"
|
|
value={data.phone_number}
|
|
onChange={(e) => setData('phone_number', e.target.value)}
|
|
placeholder="02112345678"
|
|
/>
|
|
<InputError message={errors.phone_number} />
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email">Email</Label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
value={data.email}
|
|
onChange={(e) => setData('email', e.target.value)}
|
|
placeholder="contact@asuransi.com"
|
|
/>
|
|
<InputError message={errors.email} />
|
|
</div>
|
|
|
|
<div className="space-y-2 col-span-full">
|
|
<Label htmlFor="address">Alamat</Label>
|
|
<Textarea
|
|
id="address"
|
|
value={data.address}
|
|
onChange={(e) => setData('address', e.target.value)}
|
|
placeholder="Alamat kantor pusat"
|
|
/>
|
|
<InputError message={errors.address} />
|
|
</div>
|
|
|
|
{/* Penanggung Jawab */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="contact_person">Nama Kontak</Label>
|
|
<Input
|
|
id="contact_person"
|
|
value={data.contact_person}
|
|
onChange={(e) => setData('contact_person', e.target.value)}
|
|
placeholder="Nama penanggung jawab"
|
|
/>
|
|
<InputError message={errors.contact_person} />
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="contact_person_phone">No. HP Kontak</Label>
|
|
<Input
|
|
id="contact_person_phone"
|
|
value={data.contact_person_phone}
|
|
onChange={(e) => setData('contact_person_phone', e.target.value)}
|
|
placeholder="08123456789"
|
|
/>
|
|
<InputError message={errors.contact_person_phone} />
|
|
</div>
|
|
|
|
{/* Cakupan */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="coverage_percentage">Persentase Cover (%)*</Label>
|
|
<Input
|
|
id="coverage_percentage"
|
|
type="number"
|
|
min="0"
|
|
max="100"
|
|
step="0.01"
|
|
value={data.coverage_percentage}
|
|
onChange={(e) => setData('coverage_percentage', parseFloat(e.target.value))}
|
|
/>
|
|
<InputError message={errors.coverage_percentage} />
|
|
</div>
|
|
|
|
<div className="space-y-2 col-span-full">
|
|
<Label htmlFor="coverage_description">Deskripsi Cakupan</Label>
|
|
<Textarea
|
|
id="coverage_description"
|
|
value={data.coverage_description}
|
|
onChange={(e) => setData('coverage_description', e.target.value)}
|
|
placeholder="Detail layanan yang dicover"
|
|
/>
|
|
<InputError message={errors.coverage_description} />
|
|
</div>
|
|
|
|
{/* Perjanjian */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="agreement_start_date">Tanggal Mulai Perjanjian</Label>
|
|
<Popover>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
variant={"outline"}
|
|
className={cn(
|
|
"w-full justify-start text-left font-normal",
|
|
!data.agreement_start_date && "text-muted-foreground"
|
|
)}
|
|
>
|
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
|
{data.agreement_start_date ? (
|
|
format(new Date(data.agreement_start_date), "dd/MM/yyyy")
|
|
) : (
|
|
<span>Pilih tanggal</span>
|
|
)}
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent className="w-auto p-0">
|
|
<Calendar
|
|
mode="single"
|
|
selected={data.agreement_start_date ? new Date(data.agreement_start_date) : undefined}
|
|
onSelect={(date) => date && setData('agreement_start_date', toLocalISOString(date))}
|
|
initialFocus
|
|
/>
|
|
</PopoverContent>
|
|
</Popover>
|
|
<InputError message={errors.agreement_start_date} />
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="agreement_end_date">Tanggal Berakhir Perjanjian</Label>
|
|
<Popover>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
variant={"outline"}
|
|
className={cn(
|
|
"w-full justify-start text-left font-normal",
|
|
!data.agreement_end_date && "text-muted-foreground"
|
|
)}
|
|
>
|
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
|
{data.agreement_end_date ? (
|
|
format(new Date(data.agreement_end_date), "dd/MM/yyyy")
|
|
) : (
|
|
<span>Pilih tanggal</span>
|
|
)}
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent className="w-auto p-0">
|
|
<Calendar
|
|
mode="single"
|
|
selected={data.agreement_end_date ? new Date(data.agreement_end_date) : undefined}
|
|
onSelect={(date) => date && setData('agreement_end_date', toLocalISOString(date))}
|
|
initialFocus
|
|
/>
|
|
</PopoverContent>
|
|
</Popover>
|
|
<InputError message={errors.agreement_end_date} />
|
|
</div>
|
|
|
|
<div className="space-y-2 col-span-full">
|
|
<Label htmlFor="agreement_details">Detail Perjanjian</Label>
|
|
<Textarea
|
|
id="agreement_details"
|
|
value={data.agreement_details}
|
|
onChange={(e) => setData('agreement_details', e.target.value)}
|
|
placeholder="Ringkasan perjanjian kerjasama"
|
|
/>
|
|
<InputError message={errors.agreement_details} />
|
|
</div>
|
|
|
|
{/* File & Status */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="agreement_file_path">File Perjanjian (Path)</Label>
|
|
<Input
|
|
id="agreement_file_path"
|
|
value={data.agreement_file_path}
|
|
onChange={(e) => setData('agreement_file_path', e.target.value)}
|
|
placeholder="/path/to/agreement.pdf"
|
|
/>
|
|
<InputError message={errors.agreement_file_path} />
|
|
</div>
|
|
|
|
{mode === 'edit' && (
|
|
<div className="flex items-center space-x-2">
|
|
<input
|
|
type="checkbox"
|
|
id="is_active"
|
|
checked={data.is_active}
|
|
onChange={(e) => setData('is_active', e.target.checked)}
|
|
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
|
|
/>
|
|
<Label htmlFor="is_active">Asuransi Aktif</Label>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex justify-end gap-2">
|
|
<Button variant="outline" type="button" onClick={() => window.history.back()}>
|
|
Batal
|
|
</Button>
|
|
<Button type="submit" disabled={processing}>
|
|
{mode === 'create' ? 'Simpan' : 'Perbarui'} Asuransi
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</AppLayout>
|
|
);
|
|
}
|