refactor(FE-114): enhance type safety and improve checkbox input handling

This commit is contained in:
rstubryan
2025-10-23 19:52:38 +07:00
parent 6e5875a7b7
commit cebe738beb
2 changed files with 56 additions and 124 deletions
@@ -16,95 +16,31 @@ import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import { type CellContext } from '@tanstack/react-table';
import { type Recording } from '@/types/api/production/recording';
import { type ProjectFlock } from '@/types/api/production/project-flock';
const dummyRecordings: Recording[] = [
// Extended type that includes related data
type RecordingWithRelations = Recording & {
project_flock?: ProjectFlock;
};
const dummyRecordings: RecordingWithRelations[] = [
{
id: 1,
flock: {
id: 1,
name: 'Flock Recording 1',
created_at: '2024-01-01',
updated_at: '2024-01-01',
created_user: {
id: 1,
id_user: 1,
email: 'admin@example.com',
name: 'Admin',
},
},
recording_date: '2024-01-01',
location: {
id: 1,
name: 'Location 1',
address: 'Jl. Contoh No. 1',
area: {
id: 1,
name: 'Area 1',
},
created_at: '2024-01-01',
updated_at: '2024-01-01',
created_user: {
id: 1,
id_user: 1,
email: 'admin@example.com',
name: 'Admin',
},
},
coop: {
id: 1,
name: 'Coop 1',
status: 'ACTIVE',
location: {
id: 1,
name: 'Location 1',
address: 'Jl. Contoh No. 1',
area: {
id: 1,
name: 'Area 1',
},
},
pic: {
id: 1,
id_user: 1,
email: 'pic@example.com',
name: 'PIC User',
},
created_at: '2024-01-01',
updated_at: '2024-01-01',
created_user: {
id: 1,
id_user: 1,
email: 'admin@example.com',
name: 'Admin',
},
},
feed_data: [
{
feed_name: 'Feed 1',
feed_qty: 100,
feed_stock: 500,
},
],
body_weight: [
{
chicken_weight: 2.5,
chicken_count: 1000,
average_chicken_weight: 2.5,
},
],
vaccination: [
{
vaccine_name: 'Vaccine 1',
total_stock: 200,
used_stock: 150,
},
],
mortality: [
{
condition: 'NORMAL',
count: 5,
},
],
project_flock_kandang_id: 1,
record_date: '2024-01-01',
ontime: true,
day: 10,
status: 1,
total_depletion: 10,
cum_depletion_rate: 1.0,
daily_gain: 50,
avg_daily_gain: 5.0,
cum_intake: 200,
fcr_value: 1.5,
total_chick: 1000,
daily_depletion_rate: 0.5,
cum_depletion: 20,
record_datetime: '2024-01-01T08:00:00Z',
created_at: '2024-01-01',
updated_at: '2024-01-01',
created_user: {
@@ -112,6 +48,10 @@ const dummyRecordings: Recording[] = [
id_user: 1,
email: 'admin@example.com',
name: 'Admin',
image: null,
npk: '0001',
created_at: '2024-01-01',
updated_at: '2024-01-01',
},
},
];
@@ -122,7 +62,7 @@ const RowOptionsMenu = ({
deleteClickHandler,
}: {
type: 'dropdown' | 'collapse';
props: CellContext<Recording, unknown>;
props: CellContext<RecordingWithRelations, unknown>;
deleteClickHandler: () => void;
}) => {
return (
@@ -178,7 +118,7 @@ const RecordingTable = () => {
const [pageSize, setPageSize] = useState(10);
const [sorting, setSorting] = useState<SortingState>([]);
const [selectedRecordings, setSelectedRecordings] = useState<number[]>([]);
const [, setSelectedRecording] = useState<Recording | undefined>(undefined);
const [, setSelectedRecording] = useState<RecordingWithRelations | undefined>(undefined);
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [isBulkApproveLoading, setIsBulkApproveLoading] = useState(false);
const [isBulkRejectLoading, setIsBulkRejectLoading] = useState(false);
@@ -206,10 +146,15 @@ const RecordingTable = () => {
const paginatedData = useMemo(() => {
const filteredData = dummyRecordings.filter(
(recording) =>
recording.flock.name.toLowerCase().includes(search.toLowerCase()) ||
recording.location.name.toLowerCase().includes(search.toLowerCase()) ||
recording.coop.name.toLowerCase().includes(search.toLowerCase())
(recording: RecordingWithRelations) => {
const projectName = recording.project_flock?.name || '';
const locationName = recording.project_flock?.location?.name || '';
const coopName = recording.project_flock?.kandangs?.[0]?.name || '';
return projectName.toLowerCase().includes(search.toLowerCase()) ||
locationName.toLowerCase().includes(search.toLowerCase()) ||
coopName.toLowerCase().includes(search.toLowerCase());
}
);
const start = (page - 1) * pageSize;
return filteredData.slice(start, start + pageSize);
@@ -383,35 +328,34 @@ const RecordingTable = () => {
cell: (props) => pageSize * (page - 1) + props.row.index + 1,
},
{
accessorKey: 'flock.name',
header: 'Flock',
cell: (props) => props.row.original.project_flock?.name || '-',
},
{
accessorKey: 'recording_date',
accessorKey: 'record_date',
header: 'Tanggal Recording',
cell: (props) =>
new Date(props.row.original.recording_date).toLocaleDateString(),
new Date(props.row.original.record_date).toLocaleDateString(),
},
{
accessorKey: 'location.name',
header: 'Lokasi',
cell: (props) => props.row.original.project_flock?.location?.name || '-',
},
{
accessorKey: 'coop.name',
header: 'Kandang',
cell: (props) => {
const coopName = props.row.original.project_flock?.kandangs?.[0]?.name;
return coopName || '-';
},
},
{
accessorKey: 'mortality',
header: 'Total Mortality',
cell: (props) =>
props.row.original.mortality.reduce(
(acc, curr) => acc + curr.count,
0
),
accessorKey: 'total_depletion',
header: 'Total Depletion',
cell: (props) => props.row.original.total_depletion,
},
{
header: 'Aksi',
cell: (props: CellContext<Recording, unknown>) => {
cell: (props: CellContext<RecordingWithRelations, unknown>) => {
const currentPageSize =
props.table.getPaginationRowModel().rows.length;
const currentPageRows =
@@ -334,7 +334,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
label='On Time'
name='ontime'
checked={formik.values.ontime || false}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
formik.setFieldValue('ontime', e.target.checked);
}}
disabled={type === 'detail'}
@@ -363,7 +363,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
selectedBodyWeights.length &&
formik.values.body_weights?.length > 0
}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
setSelectedBodyWeights(
formik.values.body_weights?.map(
@@ -374,8 +374,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
setSelectedBodyWeights([]);
}
}}
naked={true}
size='sm'
/>
</div>
</th>
@@ -411,7 +409,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
<CheckboxInput
name={`body-weight-${idx}`}
checked={selectedBodyWeights.includes(idx)}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
setSelectedBodyWeights([
...selectedBodyWeights,
@@ -423,8 +421,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
);
}
}}
naked={true}
size='sm'
/>
</div>
</td>
@@ -558,7 +554,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
selectedStocks.length &&
formik.values.stocks?.length > 0
}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
setSelectedStocks(
formik.values.stocks?.map((_, idx) => idx) ??
@@ -568,8 +564,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
setSelectedStocks([]);
}
}}
naked={true}
size='sm'
/>
</div>
</th>
@@ -599,7 +593,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
<CheckboxInput
name={`stock-${idx}`}
checked={selectedStocks.includes(idx)}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
setSelectedStocks([...selectedStocks, idx]);
} else {
@@ -608,8 +602,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
);
}
}}
naked={true}
size='sm'
/>
</div>
</td>
@@ -796,7 +788,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
selectedDepletions.length &&
formik.values.depletions?.length > 0
}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
setSelectedDepletions(
formik.values.depletions?.map(
@@ -807,8 +799,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
setSelectedDepletions([]);
}
}}
naked={true}
size='sm'
/>
</div>
</th>
@@ -853,7 +843,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
<CheckboxInput
name={`depletion-${idx}`}
checked={selectedDepletions.includes(idx)}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
setSelectedDepletions([
...selectedDepletions,
@@ -865,8 +855,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
);
}
}}
naked={true}
size='sm'
/>
</div>
</td>