From 9f2fcbf1542502489b548a85f1e556b637949dcf Mon Sep 17 00:00:00 2001 From: rstubryan Date: Mon, 29 Dec 2025 12:08:30 +0700 Subject: [PATCH] refactor(FE-438): Add uniformity details preview drawer --- .../uniformity/detail/UniformityDetail.tsx | 49 ++- .../detail/UniformityDetailsPreview.tsx | 295 ++++++++++++++++++ 2 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 src/components/pages/uniformity/detail/UniformityDetailsPreview.tsx diff --git a/src/components/pages/uniformity/detail/UniformityDetail.tsx b/src/components/pages/uniformity/detail/UniformityDetail.tsx index 1ba06b0f..cae0a1ec 100644 --- a/src/components/pages/uniformity/detail/UniformityDetail.tsx +++ b/src/components/pages/uniformity/detail/UniformityDetail.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useMemo } from 'react'; +import { useMemo, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { Icon } from '@iconify/react'; import { ColumnDef } from '@tanstack/react-table'; @@ -8,10 +8,13 @@ import Button from '@/components/Button'; import DrawerHeader from '@/components/helper/drawer/DrawerHeader'; import Table from '@/components/Table'; import Badge from '@/components/Badge'; +import Tooltip from '@/components/Tooltip'; import { type OptionType } from '@/components/input/SelectInput'; import RequirePermission from '@/components/helper/RequirePermission'; import { UniformityDetail as UniformityDetailType } from '@/types/api/uniformity/uniformity'; import { formatDate } from '@/lib/helper'; +import { useUiStore } from '@/stores/ui/ui.store'; +import UniformityDetailsPreview from './UniformityDetailsPreview'; const statusColorMap: Record = { APPROVED: 'bg-[#00D39033]', @@ -43,7 +46,7 @@ const getStatusText = (status: string): string => { return statusTextMap[status] || status; }; -type DetailOptionType = OptionType & { +export type DetailOptionType = OptionType & { id: string; }; @@ -55,6 +58,10 @@ const UniformityDetail: React.FC = ({ initialValues, }) => { const router = useRouter(); + const setExpandedDrawerOpen = useUiStore((s) => s.setExpandedDrawerOpen); + const setExpandedDrawerContent = useUiStore( + (s) => s.setExpandedDrawerContent + ); const handleApprove = () => { router.push(`/uniformity?action=approve&id=${initialValues.id}`); @@ -64,6 +71,28 @@ const UniformityDetail: React.FC = ({ router.push(`/uniformity?action=reject&id=${initialValues.id}`); }; + const handleViewUniformityDetails = () => { + setExpandedDrawerContent( + + ); + + setTimeout(() => { + setExpandedDrawerOpen(true); + }, 0); + }; + + useEffect(() => { + return () => { + setExpandedDrawerOpen(false); + setExpandedDrawerContent(null); + }; + }, []); + const infoUmumTableData: DetailOptionType[] = useMemo(() => { if (!initialValues) return []; @@ -147,6 +176,22 @@ const UniformityDetail: React.FC = ({ return -; } + if (id === 'file-name') { + return ( +
+ {valueMap[id]} + + + +
+ ); + } + return {valueMap[id] || '-'}; }, }, diff --git a/src/components/pages/uniformity/detail/UniformityDetailsPreview.tsx b/src/components/pages/uniformity/detail/UniformityDetailsPreview.tsx new file mode 100644 index 00000000..4949e368 --- /dev/null +++ b/src/components/pages/uniformity/detail/UniformityDetailsPreview.tsx @@ -0,0 +1,295 @@ +'use client'; + +import React, { useMemo } from 'react'; +import { Icon } from '@iconify/react'; +import { ColumnDef } from '@tanstack/react-table'; +import DrawerHeader from '@/components/helper/drawer/DrawerHeader'; +import { useUiStore } from '@/stores/ui/ui.store'; +import { + UniformityDetailItem, + UniformitySampling, + UniformityResult, + UniformityInfoUmum, +} from '@/types/api/uniformity/uniformity'; +import Table from '@/components/Table'; +import Badge from '@/components/Badge'; +import { formatNumber } from '@/lib/helper'; +import { DetailOptionType } from '@/components/pages/uniformity/detail/UniformityDetail'; + +const weightStatusColorMap: Record = { + ideal: 'bg-[#00D39033]', + outside: 'bg-error/10', +}; + +const weightStatusIndicatorColorMap: Record = { + ideal: 'bg-[#008000]', + outside: 'bg-error', +}; + +const weightStatusTextMap: Record = { + ideal: 'Ideal', + outside: 'Outside', +}; + +const getWeightStatusColor = (status: string): string => { + return weightStatusColorMap[status] || 'bg-info'; +}; + +const getWeightStatusIndicatorColor = (status: string): string => { + return weightStatusIndicatorColorMap[status] || 'bg-info'; +}; + +const getWeightStatusText = (status: string): string => { + return weightStatusTextMap[status] || status; +}; + +type BodyWeightData = { + id: string; + number: number; + weight: number; + status?: 'ideal' | 'outside'; +}; + +interface UniformityDetailsPreviewProps { + info_umum: UniformityInfoUmum; + uniformityDetails: UniformityDetailItem[]; + sampling: UniformitySampling; + result: UniformityResult; +} + +const UniformityDetailsPreview = ({ + info_umum, + uniformityDetails, + sampling, + result, +}: UniformityDetailsPreviewProps) => { + const setExpandedDrawerOpen = useUiStore((s) => s.setExpandedDrawerOpen); + + const handleClose = () => { + setExpandedDrawerOpen(false); + }; + + const samplingTableData: DetailOptionType[] = useMemo(() => { + if (!sampling) return []; + + return [ + { + id: 'sampling-size', + label: 'Sampling size', + value: `${formatNumber(sampling.chick_qty_of_weight)} of Birds`, + }, + { + id: 'mean-weight', + label: 'Mean Weight', + value: `${sampling.mean_weight} g`, + }, + { + id: 'min-limit', + label: 'Min Limit (-10%)', + value: `${sampling.mean_down} g`, + }, + { + id: 'max-limit', + label: 'Max Limit (+10%)', + value: `${sampling.mean_up} g`, + }, + ]; + }, [sampling]); + + const columnsSampling: ColumnDef[] = useMemo( + () => [ + { + accessorKey: 'label', + header: 'Label', + cell: (props) => props.row.original.label, + }, + { + accessorKey: 'value', + header: 'Value', + cell: (props) => {props.row.original.value}, + }, + ], + [] + ); + + const resultTableData: DetailOptionType[] = useMemo(() => { + if (!result) return []; + + return [ + { + id: 'ideal-birds', + label: 'Ideal Birds', + value: `${formatNumber(result.uniform_qty)} of Birds`, + }, + { + id: 'outside-range', + label: 'Outside Range', + value: `${formatNumber(result.outside_qty)} of Birds`, + }, + { + id: 'uniformity', + label: 'Uniformity', + value: `${result.uniformity} %`, + }, + { + id: 'cv', + label: 'CV', + value: `${result.cv} %`, + }, + ]; + }, [result]); + + const resultColumns: ColumnDef[] = useMemo( + () => [ + { + accessorKey: 'label', + header: 'Label', + cell: (props) => props.row.original.label, + }, + { + accessorKey: 'value', + header: 'Value', + cell: (props) => {props.row.original.value}, + }, + ], + [] + ); + + const tableData = useMemo(() => { + if (!uniformityDetails) return []; + + return uniformityDetails.map((detail, index) => ({ + id: `body-weight-${index + 1}`, + number: index + 1, + weight: detail.weight, + status: detail.range.toLowerCase() as 'ideal' | 'outside', + })); + }, [uniformityDetails]); + + const columnsUniformity: ColumnDef[] = useMemo( + () => [ + { + accessorKey: 'number', + header: 'No', + cell: (props) => props.row.original.number, + }, + { + accessorKey: 'weight', + header: 'Weight (g)', + cell: (props) => {props.row.original.weight}, + }, + { + accessorKey: 'status', + header: 'Status', + cell: (props) => { + const status = props.row.original.status; + return status ? ( +
+ + {getWeightStatusText(status)} + +
+ ) : ( + + Unknown + + ); + }, + }, + ], + [] + ); + + return ( +
+ {/* Header */} + + + + + {/* Form Section */} +
+
+ {uniformityDetails && uniformityDetails.length > 0 ? ( +
+ {/* Sampling and Range */} +
+

Sampling and Range

+ + data={samplingTableData} + columns={columnsSampling} + pageSize={4} + className={{ + containerClassName: 'mb-0', + paginationClassName: 'hidden', + }} + /> +
+ + {/* Result */} +
+

Result

+ + data={resultTableData} + columns={resultColumns} + pageSize={4} + className={{ + containerClassName: 'mb-0', + paginationClassName: 'hidden', + }} + /> +
+ + {/* Body Weight Details */} +
+ + data={tableData} + columns={columnsUniformity} + pageSize={15} + className={{ containerClassName: 'mb-5' }} + /> +
+
+ ) : ( +
+ +

No data available

+

Uniformity details not found

+
+ )} +
+
+ ); +}; + +export default UniformityDetailsPreview;