TIFNJK_E41222887/resources/js/pages/admin/employees/index.tsx

201 lines
9.6 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any */
import { Head, Link, usePage, router } from '@inertiajs/react';
import React, { useState, useEffect } from 'react';
import { useDebounce } from 'use-debounce';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Separator } from '@/components/ui/separator';
import AppLayout from '@/layouts/app-layout';
interface Employee {
id: number;
nip: string;
status: string;
join_date: string;
position: {
id: number;
name: string;
};
department: {
id: number;
name: string;
};
user: {
name: string;
email: string;
} | null;
}
interface PageProps {
employees: Employee[];
departments: { id: number; name: string }[];
filters: {
search?: string;
department_id?: string;
};
[key: string]: unknown;
}
import * as XLSX from 'xlsx';
export default function Index({ employees, departments, filters }: PageProps) {
const [search, setSearch] = useState(filters.search || '');
const [departmentId, setDepartmentId] = useState(filters.department_id || 'all');
const [debouncedSearch] = useDebounce(search, 500);
useEffect(() => {
if (debouncedSearch !== filters.search || (departmentId !== 'all' && departmentId !== filters.department_id)) {
router.get(
'/admin/employees',
{
search: debouncedSearch,
department_id: departmentId === 'all' ? '' : departmentId
},
{ preserveState: true, replace: true }
);
}
}, [debouncedSearch, departmentId]);
const exportExcel = () => {
const rows = employees.map((p, i) => ({
No: i + 1,
Nama: p.user?.name || '-',
Email: p.user?.email || '-',
NIP: p.nip,
Departemen: p.department?.name || '-',
Jabatan: p.position?.name || '-',
Status: p.status,
Bergabung: new Date(p.join_date).toLocaleDateString('id-ID'),
}));
const ws = XLSX.utils.json_to_sheet(rows);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Data Karyawan');
XLSX.writeFile(wb, `Data_Karyawan_${new Date().toISOString().slice(0, 10)}.xlsx`);
};
return (
<AppLayout>
<Head title="Manajemen Karyawan" />
<div className="p-4 md:p-8 w-full space-y-4">
{/* Page Header */}
<div className="space-y-1">
<h2 className="text-2xl font-bold tracking-tight">Manajemen Karyawan</h2>
<p className="text-muted-foreground">Kelola data seluruh karyawan perusahaan.</p>
</div>
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-4">
<CardTitle className="text-xl font-bold">Daftar Karyawan</CardTitle>
<div className="flex gap-2">
<Button variant="outline" onClick={exportExcel}>Export Excel</Button>
<Button asChild>
<Link href="/admin/employees/create">+ Tambah Karyawan</Link>
</Button>
</div>
</CardHeader>
<Separator />
<div className="p-4 flex flex-col md:flex-row gap-4">
<div className="w-full md:w-1/3">
<Input
placeholder="Cari Nama / NIP..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<div className="w-full md:w-1/4">
<Select
value={departmentId}
onValueChange={(val) => setDepartmentId(val)}
>
<SelectTrigger>
<SelectValue placeholder="Filter Departemen" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Semua Departemen</SelectItem>
{departments.map((dept) => (
<SelectItem key={dept.id} value={String(dept.id)}>
{dept.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<CardContent className="pt-0">
<div className="relative w-full overflow-auto">
<table className="w-full text-sm text-left">
<thead className="bg-zinc-50/50">
<tr className="border-b text-muted-foreground">
<th className="h-10 px-4 font-medium">Karyawan</th>
<th className="h-10 px-4 font-medium">Posisi & Dept</th>
<th className="h-10 px-4 font-medium">Status</th>
<th className="h-10 px-4 font-medium">Bergabung</th>
<th className="h-10 px-4 text-right font-medium">Aksi</th>
</tr>
</thead>
<tbody>
{employees.length > 0 ? (
employees.map((employee) => (
<tr key={employee.id} className="border-b hover:bg-zinc-50">
<td className="p-4">
<div className="font-bold">{employee.user?.name}</div>
<div className="text-xs text-muted-foreground">{employee.user?.email}</div>
<div className="text-xs text-zinc-400 mt-1">NIP: {employee.nip}</div>
</td>
<td className="p-4">
<div className="font-medium">{employee.position?.name || '-'}</div>
<Badge variant="outline" className="mt-1 font-normal text-xs">
{employee.department?.name || '-'}
</Badge>
</td>
<td className="p-4">
<Badge className={employee.status === 'PKWTT' ? 'bg-green-600' : 'bg-orange-500'}>
{employee.status}
</Badge>
</td>
<td className="p-4 text-muted-foreground">
{new Date(employee.join_date).toLocaleDateString('id-ID')}
</td>
<td className="p-4 text-right space-x-2">
<Button variant="ghost" size="sm" asChild>
<Link href={`/admin/employees/${employee.id}/edit`}>Edit</Link>
</Button>
<Link
href={`/admin/employees/${employee.id}`}
method="delete"
as="button"
className="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 text-red-600 hover:bg-red-50"
>
Hapus
</Link>
</td>
</tr>
))
) : (
<tr>
<td colSpan={5} className="p-8 text-center text-muted-foreground">
Tidak ada data ditemukan.
</td>
</tr>
)}
</tbody>
</table>
</div>
</CardContent>
</Card>
</div>
</AppLayout>
);
}