feat(FE-86-88): Adding reject button and integrate with approval api

This commit is contained in:
randy-ar
2025-10-23 20:23:25 +07:00
parent 8a467c2d65
commit 51bce1a2c7
11 changed files with 298 additions and 219 deletions
@@ -16,13 +16,12 @@ import { ProjectFlockApi } from '@/services/api/production';
import { useTableFilter } from '@/services/hooks/useTableFilter';
import { BaseApiResponse } from '@/types/api/api-general';
import { Kandang } from '@/types/api/master-data/kandang';
import { ProjectFlock } from '@/types/api/production/project-flock';
import { Icon } from '@iconify/react';
import {
CellContext,
ColumnDef,
SortingState,
} from '@tanstack/react-table';
ProjectFlockApprovalPayload,
ProjectFlock,
} from '@/types/api/production/project-flock';
import { Icon } from '@iconify/react';
import { CellContext, SortingState } from '@tanstack/react-table';
import { ChangeEventHandler, useState } from 'react';
import toast from 'react-hot-toast';
import useSWR from 'swr';
@@ -56,24 +55,28 @@ const RowOptionsMenu = ({
<Icon icon='mdi:eye-outline' width={16} height={16} />
Detail
</Button>
<Button
href={`/production/chickin/add?projectFlockId=${props.row.original.id}`}
variant='ghost'
color='success'
className='justify-start text-sm'
>
<Icon icon='mdi:home-import-outline' width={16} height={16} />
Chickin
</Button>
{/* <Button
href={`/production/project-flock/detail/edit?projectFlockId=${props.row.original.id}`}
variant='ghost'
color='warning'
className='justify-start text-sm'
>
<Icon icon='mdi:pencil-outline' width={16} height={16} />
Edit
</Button> */}
{props.row.original.approval.step_name === 'Aktif' && (
<Button
href={`/production/chickin/add?projectFlockId=${props.row.original.id}`}
variant='ghost'
color='success'
className='justify-start text-sm'
>
<Icon icon='mdi:home-import-outline' width={16} height={16} />
Chickin
</Button>
)}
{props.row.original.approval.step_name === 'Pengajuan' && (
<Button
href={`/production/project-flock/detail/edit?projectFlockId=${props.row.original.id}`}
variant='ghost'
color='warning'
className='justify-start text-sm'
>
<Icon icon='mdi:pencil-outline' width={16} height={16} />
Edit
</Button>
)}
<Button
onClick={deleteClickHandler}
variant='ghost'
@@ -144,20 +147,20 @@ const ProjectFlockTable = () => {
search: areaSelectInputValue,
limit: '100',
}).toString()}`;
const {
data: areas,
isLoading: isLoadingAreas,
} = useSWR(areaUrl, AreaApi.getAllFetcher);
const { data: areas, isLoading: isLoadingAreas } = useSWR(
areaUrl,
AreaApi.getAllFetcher
);
const locationUrl = `${LocationApi.basePath}?${new URLSearchParams({
search: locationSelectInputValue,
area_id: selectedArea != null ? selectedArea.value.toString() : '',
limit: '100',
}).toString()}`;
const {
data: locations,
isLoading: isLoadingLocations,
} = useSWR(locationUrl, LocationApi.getAllFetcher);
const { data: locations, isLoading: isLoadingLocations } = useSWR(
locationUrl,
LocationApi.getAllFetcher
);
const kandangUrl = `${KandangApi.basePath}?${new URLSearchParams({
search: kandangSelectInputValue,
@@ -165,10 +168,10 @@ const ProjectFlockTable = () => {
selectedLocation != null ? selectedLocation.value.toString() : '',
limit: '100',
}).toString()}`;
const {
data: kandangs,
isLoading: isLoadingKandang,
} = useSWR(kandangUrl, KandangApi.getAllFetcher);
const { data: kandangs, isLoading: isLoadingKandang } = useSWR(
kandangUrl,
KandangApi.getAllFetcher
);
// Data to Options Mapping
const optionsArea = isResponseSuccess(areas)
@@ -200,129 +203,6 @@ const ProjectFlockTable = () => {
const [selectedIds, setSelectedIds] = useState<number[]>([]);
const [selectedFlocks, setSelectedFlocks] = useState<ProjectFlock[]>([]);
const [isApproveLoading, setIsApproveLoading] = useState(false);
// Columns
const projectFlocksColumns: ColumnDef<ProjectFlock>[] = [
{
id: 'select',
header: () => {
const allSelected =
isResponseSuccess(projectFlocks) &&
projectFlocks.data.length > 0 &&
selectedIds.length === projectFlocks.data.length;
return (
<input
type='checkbox'
className='checkbox checkbox-sm'
checked={allSelected}
onChange={(e) => handleSelectAll(e.target.checked)}
/>
);
},
cell: (props) => {
const id = props.row.original.id;
const isChecked = selectedIds.includes(id);
return (
<input
type='checkbox'
className='checkbox checkbox-sm'
checked={isChecked}
onChange={(e) => handleSelectRow(id, e.target.checked)}
/>
);
},
},
{
accessorKey: 'flock.name',
header: 'Flock',
},
{
accessorKey: 'area.name',
header: 'Area',
},
{
accessorKey: 'location.name',
header: 'Lokasi',
},
{
accessorKey: 'fcr.name',
header: 'FCR',
},
{
accessorKey: 'category',
header: 'Kategori',
},
{
header: 'Kandang',
cell: (props) => {
const kandang = props.row.original.kandangs;
if (kandang) {
const kandangNames = kandang.map((k: Kandang) => k.name);
return (
<div>
{kandangNames.length > 0 ? kandangNames.join(', ') : 'Tidak ada'}
</div>
);
} else {
return '-';
}
},
},
{
accessorKey: 'period',
header: 'Periode',
},
{
accessorKey: 'created_at',
header: 'Dibuat pada',
cell: (props) =>
new Date(props.row.original.created_at).toLocaleDateString(),
},
{
header: 'Aksi',
cell: (props) => {
const currentPageSize = props.table.getPaginationRowModel().rows.length;
const currentPageRows = props.table.getPaginationRowModel().flatRows;
const currentRowRelativeIndex =
currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2;
const deleteClickHandler = () => {
setSelectedProjectFlock(props.row.original);
deleteModal.openModal();
};
return (
<>
{currentPageSize > 2 && (
<RowDropdownOptions isLast2Rows={isLast2Rows}>
<RowOptionsMenu
type='dropdown'
props={props}
deleteClickHandler={deleteClickHandler}
/>
</RowDropdownOptions>
)}
{currentPageSize <= 2 && (
<RowCollapseOptions>
<RowOptionsMenu
type='dropdown'
props={props}
deleteClickHandler={deleteClickHandler}
/>
</RowCollapseOptions>
)}
</>
);
},
},
];
// Handler
const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => {
const newVal = val as OptionType;
@@ -343,9 +223,15 @@ const ProjectFlockTable = () => {
};
const handleSelectAll = (checked: boolean) => {
if (checked && isResponseSuccess(projectFlocks)) {
const allIds = projectFlocks.data.map((item) => item.id);
const allIds = projectFlocks.data
.filter((item) => item.approval.step_name === 'Pengajuan')
.map((item) => item.id);
setSelectedIds(allIds);
setSelectedFlocks(projectFlocks.data);
setSelectedFlocks(
projectFlocks.data.filter(
(item) => item.approval.step_name === 'Pengajuan'
)
);
} else {
setSelectedIds([]);
setSelectedFlocks([]);
@@ -374,12 +260,12 @@ const ProjectFlockTable = () => {
setIsApproveLoading(true);
const approveProjectFlockRes = await ProjectFlockApi.customRequest<
BaseApiResponse<ProjectFlock>,
'POST'
>(`/approve`, {
ProjectFlockApprovalPayload
>(`/approvals`, {
method: 'POST',
payload: 'POST',
params: {
ids: selectedFlocks.map((flock) => flock.id).join(','),
payload: {
action: 'APPROVED',
approvable_ids: selectedFlocks.map((flock) => flock.id),
},
});
@@ -391,6 +277,9 @@ const ProjectFlockTable = () => {
toast.error(approveProjectFlockRes?.message as string);
confirmModal.closeModal();
}
setSelectedIds([]);
setSelectedFlocks([]);
refreshProjectFlocks();
setIsApproveLoading(false);
};
@@ -508,7 +397,137 @@ const ProjectFlockTable = () => {
<Table<ProjectFlock>
data={isResponseSuccess(projectFlocks) ? projectFlocks?.data : []}
columns={projectFlocksColumns}
columns={[
{
id: 'select',
header: () => {
const allSelected =
isResponseSuccess(projectFlocks) &&
projectFlocks.data.length > 0 &&
selectedIds.length === projectFlocks.data.length;
return (
<input
type='checkbox'
className='checkbox checkbox-sm'
checked={allSelected}
onChange={(e) => handleSelectAll(e.target.checked)}
/>
);
},
cell: (props) => {
const id = props.row.original.id;
const isChecked = selectedIds.includes(id);
return (
<input
disabled={
props.row.original.approval.step_name != 'Pengajuan'
}
type='checkbox'
className='checkbox checkbox-sm'
checked={isChecked}
onChange={(e) => handleSelectRow(id, e.target.checked)}
/>
);
},
},
{
accessorKey: 'flock.name',
header: 'Flock',
},
{
accessorKey: 'area.name',
header: 'Area',
},
{
accessorKey: 'location.name',
header: 'Lokasi',
},
{
accessorKey: 'fcr.name',
header: 'FCR',
},
{
accessorKey: 'category',
header: 'Kategori',
},
{
accessorKey: 'approval.step_name',
header: 'Status',
},
{
header: 'Kandang',
cell: (props) => {
const kandang = props.row.original.kandangs;
if (kandang) {
const kandangNames = kandang.map((k: Kandang) => k.name);
return (
<div>
{kandangNames.length > 0
? kandangNames.join(', ')
: 'Tidak ada'}
</div>
);
} else {
return '-';
}
},
},
{
accessorKey: 'period',
header: 'Periode',
},
{
accessorKey: 'created_at',
header: 'Dibuat pada',
cell: (props) =>
new Date(props.row.original.created_at).toLocaleDateString(),
},
{
header: 'Aksi',
cell: (props) => {
const currentPageSize =
props.table.getPaginationRowModel().rows.length;
const currentPageRows =
props.table.getPaginationRowModel().flatRows;
const currentRowRelativeIndex =
currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
const isLast2Rows =
currentRowRelativeIndex > currentPageSize - 2;
const deleteClickHandler = () => {
setSelectedProjectFlock(props.row.original);
deleteModal.openModal();
};
return (
<>
{currentPageSize > 2 && (
<RowDropdownOptions isLast2Rows={isLast2Rows}>
<RowOptionsMenu
type='dropdown'
props={props}
deleteClickHandler={deleteClickHandler}
/>
</RowDropdownOptions>
)}
{currentPageSize <= 2 && (
<RowCollapseOptions>
<RowOptionsMenu
type='dropdown'
props={props}
deleteClickHandler={deleteClickHandler}
/>
</RowCollapseOptions>
)}
</>
);
},
},
]}
pageSize={tableFilterState.pageSize}
page={
isResponseSuccess(projectFlocks) ? projectFlocks?.meta?.page : 0