mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-316): Refactor Uniformity table and status helpers
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
import UniformityBarChart from './chart/UniformityBarChart';
|
import UniformityBarChart from '@/components/pages/uniformity/chart/UniformityBarChart';
|
||||||
import UniformityGaugeChart from './chart/UniformityGaugeChart';
|
import UniformityGaugeChart from '@/components/pages/uniformity/chart/UniformityGaugeChart';
|
||||||
|
|
||||||
interface BarChartData {
|
interface BarChartData {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useCallback, useState, useEffect } from 'react';
|
import React, { useCallback, useState, useEffect, useMemo } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
import { CellContext, ColumnDef, SortingState } from '@tanstack/react-table';
|
||||||
@@ -22,16 +22,60 @@ import { useModal } from '@/components/Modal';
|
|||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
|
import { Color } from '@/types/theme';
|
||||||
|
|
||||||
|
const statusColorMap: Record<string, Color> = {
|
||||||
|
APPROVED: 'success',
|
||||||
|
REJECTED: 'error',
|
||||||
|
CREATED: 'none',
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusIndicatorColorMap: Record<string, string> = {
|
||||||
|
APPROVED: 'bg-success',
|
||||||
|
REJECTED: 'bg-error',
|
||||||
|
CREATED: 'bg-[#D9D9D9]',
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusTextMap: Record<string, string> = {
|
||||||
|
APPROVED: 'Disetujui',
|
||||||
|
REJECTED: 'Ditolak',
|
||||||
|
CREATED: 'Pengajuan',
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusColor = (status: string): Color => {
|
||||||
|
return statusColorMap[status] || 'info';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusIndicatorColor = (status: string): string => {
|
||||||
|
return statusIndicatorColorMap[status] || 'bg-info';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusText = (status: string): string => {
|
||||||
|
return statusTextMap[status] || status;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isUniformityLocked = (uniformity: Uniformity): boolean => {
|
||||||
|
return uniformity.status === 'APPROVED' || uniformity.status === 'REJECTED';
|
||||||
|
};
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
type = 'dropdown',
|
type = 'dropdown',
|
||||||
props,
|
props,
|
||||||
deleteClickHandler,
|
deleteClickHandler,
|
||||||
|
setSelectedUniformity,
|
||||||
|
openModal,
|
||||||
}: {
|
}: {
|
||||||
type: 'dropdown' | 'collapse';
|
type: 'dropdown' | 'collapse';
|
||||||
props: CellContext<Uniformity, unknown>;
|
props: CellContext<Uniformity, unknown>;
|
||||||
deleteClickHandler: () => void;
|
deleteClickHandler: () => void;
|
||||||
|
setSelectedUniformity: (uniformity: Uniformity) => void;
|
||||||
|
openModal: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
const handleDeleteClick = useCallback(() => {
|
||||||
|
setSelectedUniformity(props.row.original);
|
||||||
|
openModal();
|
||||||
|
}, [props.row.original, setSelectedUniformity, openModal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RowOptionsMenuWrapper type={type}>
|
<RowOptionsMenuWrapper type={type}>
|
||||||
<Button
|
<Button
|
||||||
@@ -53,7 +97,7 @@ const RowOptionsMenu = ({
|
|||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={deleteClickHandler}
|
onClick={handleDeleteClick}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='error'
|
color='error'
|
||||||
className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
|
className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
|
||||||
@@ -104,11 +148,7 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
UniformityApi.getAllFetcher
|
UniformityApi.getAllFetcher
|
||||||
);
|
);
|
||||||
|
|
||||||
const isUniformityLocked = useCallback((uniformity: Uniformity): boolean => {
|
const singleDeleteHandler = useCallback(async () => {
|
||||||
return uniformity.status === 'APPROVED' || uniformity.status === 'REJECTED';
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const singleDeleteHandler = async () => {
|
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await UniformityApi.delete(selectedUniformity?.id as number);
|
await UniformityApi.delete(selectedUniformity?.id as number);
|
||||||
@@ -117,7 +157,7 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
singleDeleteModal.closeModal();
|
singleDeleteModal.closeModal();
|
||||||
toast.success('Successfully delete Uniformity!');
|
toast.success('Successfully delete Uniformity!');
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
};
|
}, [selectedUniformity?.id, refreshUniformities, singleDeleteModal]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isResponseSuccess(uniformities) && uniformities.data) {
|
if (isResponseSuccess(uniformities) && uniformities.data) {
|
||||||
@@ -140,176 +180,169 @@ const UniformityTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
setRowSelection(newSelection);
|
setRowSelection(newSelection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [uniformities, rowSelection, isUniformityLocked, setRowSelection]);
|
}, [uniformities, rowSelection]);
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
|
||||||
switch (status) {
|
|
||||||
case 'APPROVED':
|
|
||||||
return 'success';
|
|
||||||
case 'REJECTED':
|
|
||||||
return 'error';
|
|
||||||
case 'CREATED':
|
|
||||||
return 'info';
|
|
||||||
default:
|
|
||||||
return 'info';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStatusText = (status: string) => {
|
|
||||||
switch (status) {
|
|
||||||
case 'APPROVED':
|
|
||||||
return 'Disetujui';
|
|
||||||
case 'REJECTED':
|
|
||||||
return 'Ditolak';
|
|
||||||
case 'CREATED':
|
|
||||||
return 'Pengajuan';
|
|
||||||
default:
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===== TABLE COLUMNS DEFINITION =====
|
// ===== TABLE COLUMNS DEFINITION =====
|
||||||
const uniformityColumns: ColumnDef<Uniformity>[] = [
|
const uniformityColumns: ColumnDef<Uniformity>[] = useMemo(
|
||||||
{
|
() => [
|
||||||
id: 'select',
|
{
|
||||||
header: ({ table }) => {
|
id: 'select',
|
||||||
const allRows = table.getRowModel().rows;
|
header: ({ table }) => {
|
||||||
const selectableRows = allRows.filter((row) => {
|
const allRows = table.getRowModel().rows;
|
||||||
const uniformity = row.original;
|
const selectableRows = allRows.filter((row) => {
|
||||||
return !isUniformityLocked(uniformity);
|
const uniformity = row.original;
|
||||||
});
|
return !isUniformityLocked(uniformity);
|
||||||
|
});
|
||||||
|
|
||||||
const hasNoSelectableRows = selectableRows.length === 0;
|
const hasNoSelectableRows = selectableRows.length === 0;
|
||||||
|
|
||||||
const handleSelectAll = () => {
|
const handleSelectAll = () => {
|
||||||
const isAllSelected = selectableRows.every((row) =>
|
const isAllSelected = selectableRows.every((row) =>
|
||||||
|
row.getIsSelected()
|
||||||
|
);
|
||||||
|
|
||||||
|
selectableRows.forEach((row) => {
|
||||||
|
row.toggleSelected(!isAllSelected);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAllSelected =
|
||||||
|
selectableRows.length > 0 &&
|
||||||
|
selectableRows.every((row) => row.getIsSelected());
|
||||||
|
|
||||||
|
const isSomeSelected = selectableRows.some((row) =>
|
||||||
row.getIsSelected()
|
row.getIsSelected()
|
||||||
);
|
);
|
||||||
|
|
||||||
selectableRows.forEach((row) => {
|
return (
|
||||||
row.toggleSelected(!isAllSelected);
|
<div className='w-full flex flex-row justify-center'>
|
||||||
});
|
<CheckboxInput
|
||||||
};
|
name='allRow'
|
||||||
|
checked={isAllSelected}
|
||||||
|
indeterminate={isSomeSelected && !isAllSelected}
|
||||||
|
onChange={handleSelectAll}
|
||||||
|
disabled={hasNoSelectableRows}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const uniformity = row.original;
|
||||||
|
const isDisabled = isUniformityLocked(uniformity);
|
||||||
|
|
||||||
const isAllSelected =
|
return (
|
||||||
selectableRows.length > 0 &&
|
<div className={cn({ 'opacity-50': isDisabled })}>
|
||||||
selectableRows.every((row) => row.getIsSelected());
|
<CheckboxInput
|
||||||
|
name='row'
|
||||||
const isSomeSelected = selectableRows.some((row) =>
|
checked={row.getIsSelected()}
|
||||||
row.getIsSelected()
|
indeterminate={row.getIsSomeSelected()}
|
||||||
);
|
onChange={row.getToggleSelectedHandler()}
|
||||||
|
disabled={isDisabled}
|
||||||
return (
|
/>
|
||||||
<div className='w-full flex flex-row justify-center'>
|
</div>
|
||||||
<CheckboxInput
|
);
|
||||||
name='allRow'
|
},
|
||||||
checked={isAllSelected}
|
|
||||||
indeterminate={isSomeSelected && !isAllSelected}
|
|
||||||
onChange={handleSelectAll}
|
|
||||||
disabled={hasNoSelectableRows}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
cell: ({ row }) => {
|
{
|
||||||
const uniformity = row.original;
|
accessorKey: 'location.name',
|
||||||
const isDisabled = isUniformityLocked(uniformity);
|
header: 'Lokasi',
|
||||||
|
cell: (props) => props.row.original.location.name || '-',
|
||||||
return (
|
|
||||||
<div className={cn({ 'opacity-50': isDisabled })}>
|
|
||||||
<CheckboxInput
|
|
||||||
name='row'
|
|
||||||
checked={row.getIsSelected()}
|
|
||||||
indeterminate={row.getIsSomeSelected()}
|
|
||||||
onChange={row.getToggleSelectedHandler()}
|
|
||||||
disabled={isDisabled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
accessorKey: 'project_flock_kandang_id',
|
||||||
accessorKey: 'location.name',
|
header: 'Flock',
|
||||||
header: 'Lokasi',
|
cell: (props) => `Flock ${props.row.original.project_flock_kandang_id}`,
|
||||||
cell: (props) => props.row.original.location.name || '-',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'project_flock_kandang_id',
|
|
||||||
header: 'Flock',
|
|
||||||
cell: (props) => `Flock ${props.row.original.project_flock_kandang_id}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'kandang.name',
|
|
||||||
header: 'Kandang',
|
|
||||||
cell: (props) => props.row.original.kandang.name || '-',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'week',
|
|
||||||
header: 'Tanggal (Week)',
|
|
||||||
cell: (props) => `Week ${props.row.original.week}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'status',
|
|
||||||
header: 'Status',
|
|
||||||
cell: (props) => {
|
|
||||||
const status = props.row.original.status;
|
|
||||||
return (
|
|
||||||
<Badge variant='soft' color={getStatusColor(status)}>
|
|
||||||
{getStatusText(status)}
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
accessorKey: 'kandang.name',
|
||||||
accessorKey: 'uniformity',
|
header: 'Kandang',
|
||||||
header: 'Uniformity',
|
cell: (props) => props.row.original.kandang.name || '-',
|
||||||
cell: (props) => {
|
|
||||||
const uniformity = props.row.original.uniformity;
|
|
||||||
return <span>{uniformity}%</span>;
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
accessorKey: 'week',
|
||||||
id: 'actions',
|
header: 'Tanggal (Week)',
|
||||||
header: 'Aksi',
|
cell: (props) => `Week ${props.row.original.week}`,
|
||||||
cell: (props: CellContext<Uniformity, unknown>) => {
|
|
||||||
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 = () => {
|
|
||||||
setSelectedUniformity(props.row.original);
|
|
||||||
singleDeleteModal.openModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{currentPageSize > 2 && (
|
|
||||||
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
|
||||||
<RowOptionsMenu
|
|
||||||
type='dropdown'
|
|
||||||
props={props}
|
|
||||||
deleteClickHandler={deleteClickHandler}
|
|
||||||
/>
|
|
||||||
</RowDropdownOptions>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{currentPageSize <= 2 && (
|
|
||||||
<RowCollapseOptions>
|
|
||||||
<RowOptionsMenu
|
|
||||||
type='collapse'
|
|
||||||
props={props}
|
|
||||||
deleteClickHandler={deleteClickHandler}
|
|
||||||
/>
|
|
||||||
</RowCollapseOptions>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
];
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: (props) => {
|
||||||
|
const status = props.row.original.status;
|
||||||
|
return (
|
||||||
|
<div className='w-full'>
|
||||||
|
<Badge
|
||||||
|
statusIndicator={true}
|
||||||
|
variant='soft'
|
||||||
|
color={getStatusColor(status)}
|
||||||
|
className={{
|
||||||
|
badge: `rounded-xl w-full justify-start border border-gray-200`,
|
||||||
|
status: getStatusIndicatorColor(status),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getStatusText(status)}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'uniformity',
|
||||||
|
header: 'Uniformity',
|
||||||
|
cell: (props) => {
|
||||||
|
const uniformity = props.row.original.uniformity;
|
||||||
|
return <span>{uniformity}%</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
header: 'Aksi',
|
||||||
|
cell: (props: CellContext<Uniformity, unknown>) => {
|
||||||
|
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;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{currentPageSize > 2 && (
|
||||||
|
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
||||||
|
<RowOptionsMenu
|
||||||
|
type='dropdown'
|
||||||
|
props={props}
|
||||||
|
deleteClickHandler={() => {
|
||||||
|
setSelectedUniformity(props.row.original);
|
||||||
|
singleDeleteModal.openModal();
|
||||||
|
}}
|
||||||
|
setSelectedUniformity={setSelectedUniformity}
|
||||||
|
openModal={singleDeleteModal.openModal}
|
||||||
|
/>
|
||||||
|
</RowDropdownOptions>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{currentPageSize <= 2 && (
|
||||||
|
<RowCollapseOptions>
|
||||||
|
<RowOptionsMenu
|
||||||
|
type='collapse'
|
||||||
|
props={props}
|
||||||
|
deleteClickHandler={() => {
|
||||||
|
setSelectedUniformity(props.row.original);
|
||||||
|
singleDeleteModal.openModal();
|
||||||
|
}}
|
||||||
|
setSelectedUniformity={setSelectedUniformity}
|
||||||
|
openModal={singleDeleteModal.openModal}
|
||||||
|
/>
|
||||||
|
</RowCollapseOptions>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import { Location } from '@/types/api/location/location';
|
import { Location } from '@/types/api/location/location';
|
||||||
import { Kandang } from '@/types/api/kandang/kandang';
|
import { Kandang } from '@/types/api/kandang/kandang';
|
||||||
import { BaseMetadata } from '../api-general';
|
import { BaseMetadata } from '@/types/common/base-metadata';
|
||||||
|
|
||||||
export type Uniformity = BaseMetadata & {
|
export type Uniformity = BaseMetadata & {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user