mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
feat: integrate MasterEmployeeContent component to API
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
Download,
|
Download,
|
||||||
@@ -48,124 +48,132 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/figma-make/components/base/dropdown-menu';
|
} from '@/figma-make/components/base/dropdown-menu';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { supabase, isSupabaseConfigured } from '@/figma-make/lib/supabase';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { EmployeeApi } from '@/services/api/daily-checklist/employee';
|
import { EmployeeApi } from '@/services/api/daily-checklist/employee';
|
||||||
|
import Table from '@/components/Table';
|
||||||
interface Employee {
|
import { Employee } from '@/types/api/daily-checklist/employee';
|
||||||
id: string;
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
name: string;
|
import { cn } from '@/lib/helper';
|
||||||
kandang_id: string;
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
is_active: boolean;
|
import { ColumnDef } from '@tanstack/react-table';
|
||||||
kandang?: {
|
import { useSelect } from '@/components/input/SelectInput';
|
||||||
id: string;
|
import { KandangApi } from '@/services/api/master-data';
|
||||||
name: string;
|
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Kandang {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MasterEmployeeContent() {
|
export function MasterEmployeeContent() {
|
||||||
const { data: employeesTest, isLoading: isLoadingEmployees } = useSWR(
|
const {
|
||||||
EmployeeApi.basePath,
|
state: tableFilterState,
|
||||||
|
updateFilter,
|
||||||
|
setPage,
|
||||||
|
setPageSize,
|
||||||
|
toQueryString: getTableFilterQueryString,
|
||||||
|
} = useTableFilter({
|
||||||
|
initial: {
|
||||||
|
search: '',
|
||||||
|
kandang_id: '',
|
||||||
|
status: '',
|
||||||
|
},
|
||||||
|
paramMap: {
|
||||||
|
page: 'page',
|
||||||
|
pageSize: 'limit',
|
||||||
|
search: 'search',
|
||||||
|
kandang_id: 'kandang_id',
|
||||||
|
status: 'is_active',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: employees,
|
||||||
|
isLoading: isLoadingEmployees,
|
||||||
|
mutate: refreshEmployees,
|
||||||
|
} = useSWR(
|
||||||
|
`${EmployeeApi.basePath}${getTableFilterQueryString()}`,
|
||||||
EmployeeApi.getAllFetcher,
|
EmployeeApi.getAllFetcher,
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const { options: kandangOptions, isLoadingOptions: isLoadingKandangs } =
|
||||||
|
useSelect(KandangApi.basePath, 'id', 'name', 'search', {
|
||||||
|
page: '1',
|
||||||
|
limit: '100',
|
||||||
|
});
|
||||||
|
|
||||||
const [employees, setEmployees] = useState<Employee[]>([]);
|
|
||||||
const [kandangList, setKandangList] = useState<Kandang[]>([]);
|
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
const [employeeToDelete, setEmployeeToDelete] = useState<string | null>(null);
|
const [employeeToDelete, setEmployeeToDelete] = useState<number | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [initialLoading, setInitialLoading] = useState(true);
|
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
|
||||||
const [kandangFilter, setKandangFilter] = useState<string>('all');
|
|
||||||
const [statusFilter, setStatusFilter] = useState<string>('all');
|
|
||||||
const [modalMode, setModalMode] = useState<'create' | 'edit'>('create');
|
const [modalMode, setModalMode] = useState<'create' | 'edit'>('create');
|
||||||
const [employeeForm, setEmployeeForm] = useState({
|
const [employeeForm, setEmployeeForm] = useState({
|
||||||
id: '',
|
id: 0,
|
||||||
name: '',
|
name: '',
|
||||||
kandang_ids: [] as string[],
|
kandang_ids: [] as number[],
|
||||||
status: 'Active' as 'Active' | 'Non Active',
|
status: 'Active' as 'Active' | 'Non Active',
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchEmployees = async () => {
|
const employeeColumns: ColumnDef<Employee>[] = [
|
||||||
if (!isSupabaseConfigured()) {
|
{
|
||||||
console.warn(
|
id: 'name',
|
||||||
'Supabase not configured. Please add environment variables.'
|
header: 'Nama ABK',
|
||||||
);
|
accessorKey: 'name',
|
||||||
setInitialLoading(false);
|
enableSorting: false,
|
||||||
return;
|
},
|
||||||
}
|
{
|
||||||
|
id: 'kandang',
|
||||||
try {
|
header: 'Kandang',
|
||||||
const { data, error } = await supabase
|
accessorKey: 'kandangs',
|
||||||
.from('employees')
|
enableSorting: false,
|
||||||
.select(
|
cell: ({ row }) =>
|
||||||
`
|
row.original.kandangs.map((kandang) => kandang.name).join(', '),
|
||||||
id,
|
},
|
||||||
name,
|
{
|
||||||
kandang_id,
|
id: 'status',
|
||||||
is_active,
|
header: 'Status',
|
||||||
kandang:kandang_id (
|
accessorKey: 'is_active',
|
||||||
id,
|
enableSorting: false,
|
||||||
name
|
cell: ({ row }) => (
|
||||||
)
|
<Badge variant={row.original.is_active ? 'success' : 'secondary'}>
|
||||||
`
|
{row.original.is_active ? 'Active' : 'Non Active'}
|
||||||
)
|
</Badge>
|
||||||
.order('name', { ascending: true });
|
),
|
||||||
|
},
|
||||||
if (error) {
|
{
|
||||||
console.error('Error fetching employees:', error);
|
id: 'action',
|
||||||
toast.error('Gagal memuat data ABK');
|
header: 'Aksi',
|
||||||
return;
|
accessorKey: 'action',
|
||||||
}
|
enableSorting: false,
|
||||||
|
cell: ({ row }) => (
|
||||||
setEmployees((data as unknown as Employee[]) || []);
|
<DropdownMenu>
|
||||||
} catch (error) {
|
<DropdownMenuTrigger asChild>
|
||||||
console.error('Error fetching employees:', error);
|
<Button
|
||||||
toast.error('Gagal memuat data ABK');
|
variant='ghost'
|
||||||
} finally {
|
size='sm'
|
||||||
setInitialLoading(false);
|
className='h-8 w-8 p-0 hover:bg-gray-100'
|
||||||
}
|
>
|
||||||
};
|
<MoreVertical className='h-4 w-4 text-gray-600' />
|
||||||
|
</Button>
|
||||||
const fetchKandang = async () => {
|
</DropdownMenuTrigger>
|
||||||
if (!isSupabaseConfigured()) {
|
<DropdownMenuContent align='end'>
|
||||||
return;
|
<DropdownMenuItem onClick={() => handleEdit(row.original)}>
|
||||||
}
|
<Pencil className='mr-2 h-4 w-4' />
|
||||||
|
Edit
|
||||||
try {
|
</DropdownMenuItem>
|
||||||
const { data, error } = await supabase
|
<DropdownMenuItem
|
||||||
.from('kandang')
|
onClick={() => handleDeleteClick(row.original.id)}
|
||||||
.select('id, name')
|
className='text-red-600'
|
||||||
.order('name', { ascending: true });
|
>
|
||||||
|
<Trash2 className='mr-2 h-4 w-4' />
|
||||||
if (error) {
|
Hapus
|
||||||
console.error('Error fetching kandang:', error);
|
</DropdownMenuItem>
|
||||||
return;
|
</DropdownMenuContent>
|
||||||
}
|
</DropdownMenu>
|
||||||
|
),
|
||||||
setKandangList(data || []);
|
},
|
||||||
} catch (error) {
|
];
|
||||||
console.error('Error fetching kandang:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchEmployees();
|
|
||||||
fetchKandang();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
setModalMode('create');
|
setModalMode('create');
|
||||||
setEmployeeForm({ id: '', name: '', kandang_ids: [], status: 'Active' });
|
setEmployeeForm({ id: 0, name: '', kandang_ids: [], status: 'Active' });
|
||||||
setShowModal(true);
|
setShowModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,7 +182,7 @@ export function MasterEmployeeContent() {
|
|||||||
setEmployeeForm({
|
setEmployeeForm({
|
||||||
id: employee.id,
|
id: employee.id,
|
||||||
name: employee.name,
|
name: employee.name,
|
||||||
kandang_ids: employee.kandang_id ? [employee.kandang_id] : [],
|
kandang_ids: employee.kandangs ? employee.kandangs.map((k) => k.id) : [],
|
||||||
status: employee.is_active ? 'Active' : 'Non Active',
|
status: employee.is_active ? 'Active' : 'Non Active',
|
||||||
});
|
});
|
||||||
setShowModal(true);
|
setShowModal(true);
|
||||||
@@ -186,58 +194,52 @@ export function MasterEmployeeContent() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSupabaseConfigured()) {
|
|
||||||
toast.error(
|
|
||||||
'Supabase belum dikonfigurasi. Tambahkan environment variables.'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create payload - taking the first selected kandang for now as schema supports single FK
|
|
||||||
// TODO: Support multiple kandangs in backend
|
|
||||||
const kandangIdToSave = employeeForm.kandang_ids[0];
|
|
||||||
|
|
||||||
if (modalMode === 'create') {
|
if (modalMode === 'create') {
|
||||||
const { error } = await supabase.from('employees').insert([
|
const createEmployeeResponse = await EmployeeApi.create({
|
||||||
{
|
is_active: employeeForm.status === 'Active',
|
||||||
name: employeeForm.name.trim(),
|
kandang_ids: employeeForm.kandang_ids,
|
||||||
kandang_id: kandangIdToSave,
|
name: employeeForm.name.trim(),
|
||||||
is_active: employeeForm.status === 'Active',
|
});
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (error) {
|
if (isResponseError(createEmployeeResponse)) {
|
||||||
console.error('Error creating employee:', error);
|
console.error(
|
||||||
|
'Error creating employee:',
|
||||||
|
createEmployeeResponse.message
|
||||||
|
);
|
||||||
toast.error('Gagal menambahkan ABK');
|
toast.error('Gagal menambahkan ABK');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshEmployees();
|
||||||
toast.success('ABK berhasil ditambahkan');
|
toast.success('ABK berhasil ditambahkan');
|
||||||
} else {
|
} else {
|
||||||
const { error } = await supabase
|
const updateEmployeeResponse = await EmployeeApi.update(
|
||||||
.from('employees')
|
employeeForm.id,
|
||||||
.update({
|
{
|
||||||
name: employeeForm.name.trim(),
|
|
||||||
kandang_id: kandangIdToSave,
|
|
||||||
is_active: employeeForm.status === 'Active',
|
is_active: employeeForm.status === 'Active',
|
||||||
})
|
kandang_ids: employeeForm.kandang_ids,
|
||||||
.eq('id', employeeForm.id);
|
name: employeeForm.name.trim(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (error) {
|
if (isResponseError(updateEmployeeResponse)) {
|
||||||
console.error('Error updating employee:', error);
|
console.error(
|
||||||
toast.error('Gagal mengubah ABK');
|
'Error updating employee:',
|
||||||
|
updateEmployeeResponse.message
|
||||||
|
);
|
||||||
|
toast.error('Gagal menambahkan ABK');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshEmployees();
|
||||||
toast.success('ABK berhasil diubah');
|
toast.success('ABK berhasil diubah');
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowModal(false);
|
setShowModal(false);
|
||||||
setEmployeeForm({ id: '', name: '', kandang_ids: [], status: 'Active' });
|
setEmployeeForm({ id: 0, name: '', kandang_ids: [], status: 'Active' });
|
||||||
await fetchEmployees();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving employee:', error);
|
console.error('Error saving employee:', error);
|
||||||
toast.error('Terjadi kesalahan saat menyimpan ABK');
|
toast.error('Terjadi kesalahan saat menyimpan ABK');
|
||||||
@@ -246,7 +248,7 @@ export function MasterEmployeeContent() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteClick = (employeeId: string) => {
|
const handleDeleteClick = (employeeId: number) => {
|
||||||
setEmployeeToDelete(employeeId);
|
setEmployeeToDelete(employeeId);
|
||||||
setShowDeleteConfirm(true);
|
setShowDeleteConfirm(true);
|
||||||
};
|
};
|
||||||
@@ -257,21 +259,22 @@ export function MasterEmployeeContent() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { error } = await supabase
|
const deleteEmployeeResponse = await EmployeeApi.delete(employeeToDelete);
|
||||||
.from('employees')
|
|
||||||
.delete()
|
|
||||||
.eq('id', employeeToDelete);
|
|
||||||
|
|
||||||
if (error) {
|
if (isResponseError(deleteEmployeeResponse)) {
|
||||||
console.error('Error deleting employee:', error);
|
console.error(
|
||||||
|
'Error deleting employee:',
|
||||||
|
deleteEmployeeResponse.message
|
||||||
|
);
|
||||||
toast.error('Gagal menghapus ABK');
|
toast.error('Gagal menghapus ABK');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshEmployees();
|
||||||
toast.success('ABK berhasil dihapus');
|
toast.success('ABK berhasil dihapus');
|
||||||
setShowDeleteConfirm(false);
|
setShowDeleteConfirm(false);
|
||||||
setEmployeeToDelete(null);
|
setEmployeeToDelete(null);
|
||||||
await fetchEmployees();
|
// await fetchEmployees();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting employee:', error);
|
console.error('Error deleting employee:', error);
|
||||||
toast.error('Terjadi kesalahan saat menghapus ABK');
|
toast.error('Terjadi kesalahan saat menghapus ABK');
|
||||||
@@ -284,22 +287,7 @@ export function MasterEmployeeContent() {
|
|||||||
toast.success(`Data berhasil diekspor ke ${format}`);
|
toast.success(`Data berhasil diekspor ke ${format}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredEmployees = employees.filter((emp) => {
|
if (isLoadingEmployees && !employees) {
|
||||||
const kandangName = emp.kandang?.name || '';
|
|
||||||
const matchesSearch =
|
|
||||||
emp.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
||||||
kandangName.toLowerCase().includes(searchQuery.toLowerCase());
|
|
||||||
|
|
||||||
const matchesKandang =
|
|
||||||
kandangFilter === 'all' || emp.kandang_id === kandangFilter;
|
|
||||||
|
|
||||||
const matchesStatus =
|
|
||||||
statusFilter === 'all' || emp.is_active === (statusFilter === 'active');
|
|
||||||
|
|
||||||
return matchesSearch && matchesKandang && matchesStatus;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (initialLoading) {
|
|
||||||
return (
|
return (
|
||||||
<div className='min-h-screen'>
|
<div className='min-h-screen'>
|
||||||
<div className='p-6'>
|
<div className='p-6'>
|
||||||
@@ -339,48 +327,75 @@ export function MasterEmployeeContent() {
|
|||||||
<Card className='border-gray-200/60 shadow-sm rounded-xl bg-white'>
|
<Card className='border-gray-200/60 shadow-sm rounded-xl bg-white'>
|
||||||
<CardContent className='p-0'>
|
<CardContent className='p-0'>
|
||||||
{/* Single Toolbar Row */}
|
{/* Single Toolbar Row */}
|
||||||
<div className='flex items-center justify-between gap-4 p-6 border-b border-gray-200/60'>
|
<div className='flex flex-wrap items-center justify-between gap-4 p-6 border-b border-gray-200/60'>
|
||||||
{/* LEFT: Search + Filters */}
|
{/* LEFT: Search + Filters */}
|
||||||
<div className='flex items-center gap-3'>
|
<div className='flex items-center gap-3 flex-wrap'>
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
<Search className='absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4' />
|
<Search className='absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4' />
|
||||||
<Input
|
{/* <Input
|
||||||
type='text'
|
type='text'
|
||||||
placeholder='Cari nama ABK atau kandang...'
|
placeholder='Cari nama ABK atau kandang...'
|
||||||
value={searchQuery}
|
value={tableFilterState.search}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => updateFilter('search', e.target.value)}
|
||||||
className='pl-10 w-[280px] border-gray-200'
|
className='pl-10 w-[280px] border-gray-200'
|
||||||
|
/> */}
|
||||||
|
<DebouncedTextInput
|
||||||
|
name='search'
|
||||||
|
placeholder='Cari nama ABK atau kandang...'
|
||||||
|
value={tableFilterState.search}
|
||||||
|
onChange={(e) => updateFilter('search', e.target.value)}
|
||||||
|
className={{
|
||||||
|
wrapper: 'w-[280px] border-gray-200',
|
||||||
|
inputWrapper: 'px-3 py-2 h-fit rounded-md',
|
||||||
|
input: 'text-sm',
|
||||||
|
}}
|
||||||
|
startAdornment={
|
||||||
|
<Search className='text-gray-400 w-4 h-4' />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Select value={kandangFilter} onValueChange={setKandangFilter}>
|
<Select
|
||||||
|
value={tableFilterState.kandang_id}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
updateFilter('kandang_id', value === 'all' ? '' : value)
|
||||||
|
}
|
||||||
|
>
|
||||||
<SelectTrigger className='w-[180px] border-gray-200'>
|
<SelectTrigger className='w-[180px] border-gray-200'>
|
||||||
<SelectValue placeholder='Semua Kandang' />
|
<SelectValue placeholder='Semua Kandang' />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value='all'>Semua Kandang</SelectItem>
|
<SelectItem value='all'>Semua Kandang</SelectItem>
|
||||||
{kandangList.map((kandang) => (
|
{kandangOptions.map((kandang) => (
|
||||||
<SelectItem key={kandang.id} value={kandang.id}>
|
<SelectItem
|
||||||
{kandang.name}
|
key={kandang.value}
|
||||||
|
value={String(kandang.value)}
|
||||||
|
>
|
||||||
|
{kandang.label}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
<Select
|
||||||
|
value={tableFilterState.status}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
updateFilter('status', value === 'all' ? '' : value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<SelectTrigger className='w-[160px] border-gray-200'>
|
<SelectTrigger className='w-[160px] border-gray-200'>
|
||||||
<SelectValue placeholder='Semua Status' />
|
<SelectValue placeholder='Semua Status' />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value='all'>Semua Status</SelectItem>
|
<SelectItem value='all'>Semua Status</SelectItem>
|
||||||
<SelectItem value='active'>Active</SelectItem>
|
<SelectItem value='true'>Active</SelectItem>
|
||||||
<SelectItem value='non_active'>Non Active</SelectItem>
|
<SelectItem value='false'>Non Active</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* RIGHT: Export + Add */}
|
{/* RIGHT: Export + Add */}
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2 flex-wrap'>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@@ -413,93 +428,33 @@ export function MasterEmployeeContent() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
<div className='overflow-x-auto'>
|
<Table<Employee>
|
||||||
<table className='w-full'>
|
data={isResponseSuccess(employees) ? employees?.data : []}
|
||||||
<thead>
|
columns={employeeColumns}
|
||||||
<tr className='border-b border-gray-200/60 bg-gray-50/50'>
|
pageSize={tableFilterState.pageSize}
|
||||||
<th className='text-left py-3.5 px-6 text-sm font-semibold text-gray-700'>
|
onPageSizeChange={setPageSize}
|
||||||
Nama ABK
|
rowOptions={[10, 20, 50, 100]}
|
||||||
</th>
|
page={isResponseSuccess(employees) ? employees?.meta?.page : 0}
|
||||||
<th className='text-left py-3.5 px-6 text-sm font-semibold text-gray-700'>
|
totalItems={
|
||||||
Kandang
|
isResponseSuccess(employees)
|
||||||
</th>
|
? employees?.meta?.total_results
|
||||||
<th className='text-left py-3.5 px-6 text-sm font-semibold text-gray-700'>
|
: 0
|
||||||
Status
|
}
|
||||||
</th>
|
onPageChange={setPage}
|
||||||
<th className='text-center py-3.5 px-6 text-sm font-semibold text-gray-700 w-[80px]'>
|
isLoading={isLoadingEmployees}
|
||||||
Aksi
|
className={{
|
||||||
</th>
|
containerClassName: cn({
|
||||||
</tr>
|
'w-full mb-20':
|
||||||
</thead>
|
isResponseSuccess(employees) &&
|
||||||
<tbody className='divide-y divide-gray-200/60'>
|
employees?.data?.length === 0,
|
||||||
{filteredEmployees.length === 0 ? (
|
}),
|
||||||
<tr>
|
tableWrapperClassName: 'rounded-none',
|
||||||
<td
|
headerRowClassName: 'bg-gray-50/50',
|
||||||
colSpan={4}
|
headerColumnClassName:
|
||||||
className='text-center py-12 text-gray-500'
|
'text-left py-3.5 px-6 text-sm font-semibold text-gray-700',
|
||||||
>
|
paginationClassName: 'px-4',
|
||||||
{searchQuery ||
|
}}
|
||||||
kandangFilter !== 'all' ||
|
/>
|
||||||
statusFilter !== 'all'
|
|
||||||
? 'Tidak ada ABK yang ditemukan'
|
|
||||||
: 'Belum ada data ABK'}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
) : (
|
|
||||||
filteredEmployees.map((employee) => (
|
|
||||||
<tr
|
|
||||||
key={employee.id}
|
|
||||||
className='hover:bg-blue-50/30 transition-colors'
|
|
||||||
>
|
|
||||||
<td className='py-3.5 px-6 text-sm text-gray-900'>
|
|
||||||
{employee.name}
|
|
||||||
</td>
|
|
||||||
<td className='py-3.5 px-6 text-sm text-gray-700'>
|
|
||||||
{employee.kandang?.name || '-'}
|
|
||||||
</td>
|
|
||||||
<td className='py-3.5 px-6 text-sm'>
|
|
||||||
<Badge
|
|
||||||
variant={
|
|
||||||
employee.is_active ? 'success' : 'secondary'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{employee.is_active ? 'Active' : 'Non Active'}
|
|
||||||
</Badge>
|
|
||||||
</td>
|
|
||||||
<td className='py-3.5 px-6 text-center'>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant='ghost'
|
|
||||||
size='sm'
|
|
||||||
className='h-8 w-8 p-0 hover:bg-gray-100'
|
|
||||||
>
|
|
||||||
<MoreVertical className='h-4 w-4 text-gray-600' />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align='end'>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => handleEdit(employee)}
|
|
||||||
>
|
|
||||||
<Pencil className='mr-2 h-4 w-4' />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => handleDeleteClick(employee.id)}
|
|
||||||
className='text-red-600'
|
|
||||||
>
|
|
||||||
<Trash2 className='mr-2 h-4 w-4' />
|
|
||||||
Hapus
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
@@ -538,19 +493,17 @@ export function MasterEmployeeContent() {
|
|||||||
Kandang <span className='text-red-500'>*</span>
|
Kandang <span className='text-red-500'>*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
options={kandangList.map((k) => ({
|
options={kandangOptions.map((k) => ({
|
||||||
value: k.id,
|
value: String(k.value),
|
||||||
label: k.name,
|
label: k.label,
|
||||||
}))}
|
}))}
|
||||||
selected={employeeForm.kandang_ids}
|
selected={employeeForm.kandang_ids.map((id) => String(id))}
|
||||||
onChange={(selected) =>
|
onChange={(selected) =>
|
||||||
setEmployeeForm({ ...employeeForm, kandang_ids: selected })
|
setEmployeeForm({
|
||||||
|
...employeeForm,
|
||||||
|
kandang_ids: selected.map((id) => Number(id)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
// onSearchChange={(val) =>
|
|
||||||
// console.log({
|
|
||||||
// test: val,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
placeholder='Pilih kandang'
|
placeholder='Pilih kandang'
|
||||||
className='mt-1.5'
|
className='mt-1.5'
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user