From 819b709f7efdf9eabc83c122260b7fba9db72bb1 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Sat, 27 Dec 2025 21:25:06 +0700 Subject: [PATCH] feat(FE-316): Add Uniformity result drawer and flow --- .../pages/uniformity/form/UniformityForm.tsx | 19 +- .../uniformity/form/UniformityPreviewForm.tsx | 15 +- .../uniformity/form/UniformityResultForm.tsx | 178 ++++++++++++++++++ src/stores/ui/slices/drawer.slice.ts | 6 + src/types/stores.d.ts | 15 +- 5 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 src/components/pages/uniformity/form/UniformityResultForm.tsx diff --git a/src/components/pages/uniformity/form/UniformityForm.tsx b/src/components/pages/uniformity/form/UniformityForm.tsx index d8c1bda4..f9b22a65 100644 --- a/src/components/pages/uniformity/form/UniformityForm.tsx +++ b/src/components/pages/uniformity/form/UniformityForm.tsx @@ -37,6 +37,7 @@ import { type BaseApiResponse } from '@/types/api/api-general'; import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock'; import { Kandang } from '@/types/api/master-data/kandang'; import UniformityPreviewForm from '@/components/pages/uniformity/form/UniformityPreviewForm'; +import UniformityResultForm from '@/components/pages/uniformity/form/UniformityResultForm'; import useSWR from 'swr'; import { cn } from '@/lib/helper'; @@ -63,6 +64,9 @@ const UniformityForm = ({ const setVerifyUniformityResult = useUiStore( (s) => s.setVerifyUniformityResult ); + const setUniformityFormData = useUiStore((s) => s.setUniformityFormData); + const uniformityStep = useUiStore((s) => s.uniformityStep); + const setUniformityStep = useUiStore((s) => s.setUniformityStep); const [uniformityFormErrorMessage, setUniformityFormErrorMessage] = useState(''); @@ -230,6 +234,12 @@ const UniformityForm = ({ return; } + setUniformityFormData({ + date: values.date, + project_flock_kandang_id: projectFlockKandangId, + files: values.files as File, + }); + const payload: VerifyUniformityPayload = { project_flock_kandang_id: projectFlockKandangId, files: values.files as File, @@ -251,6 +261,7 @@ const UniformityForm = ({ if (formType === 'add') { setIsNextStep(true); setExpandedDrawerOpen(true); + setUniformityStep('preview'); } else { router.push('/uniformity'); } @@ -361,11 +372,15 @@ const UniformityForm = ({ useEffect(() => { if (expandedDrawerOpen) { - setExpandedDrawerContent(); + if (uniformityStep === 'preview') { + setExpandedDrawerContent(); + } else if (uniformityStep === 'result') { + setExpandedDrawerContent(); + } } else { setExpandedDrawerContent(null); } - }, [expandedDrawerOpen, setExpandedDrawerContent]); + }, [expandedDrawerOpen, uniformityStep, setExpandedDrawerContent]); return ( <> diff --git a/src/components/pages/uniformity/form/UniformityPreviewForm.tsx b/src/components/pages/uniformity/form/UniformityPreviewForm.tsx index 2e239fbf..ff526bc0 100644 --- a/src/components/pages/uniformity/form/UniformityPreviewForm.tsx +++ b/src/components/pages/uniformity/form/UniformityPreviewForm.tsx @@ -19,11 +19,17 @@ type BodyWeightData = { const UniformityPreviewForm = () => { const setExpandedDrawerOpen = useUiStore((s) => s.setExpandedDrawerOpen); const setIsNextStep = useUiStore((s) => s.setIsNextStep); + const setUniformityStep = useUiStore((s) => s.setUniformityStep); const verifyUniformityResult = useUiStore((s) => s.verifyUniformityResult); const handleClose = () => { setExpandedDrawerOpen(false); setIsNextStep(false); + setUniformityStep('preview'); + }; + + const handleNext = () => { + setUniformityStep('result'); }; const tableData = useMemo(() => { @@ -87,9 +93,14 @@ const UniformityPreviewForm = () => { data={tableData} columns={columns} - pageSize={20} - className={{ containerClassName: 'mb-10' }} + pageSize={15} + className={{ containerClassName: 'mb-5' }} /> + + + ) : (
diff --git a/src/components/pages/uniformity/form/UniformityResultForm.tsx b/src/components/pages/uniformity/form/UniformityResultForm.tsx new file mode 100644 index 00000000..bda0ca09 --- /dev/null +++ b/src/components/pages/uniformity/form/UniformityResultForm.tsx @@ -0,0 +1,178 @@ +'use client'; + +import React, { useMemo } from 'react'; +import { Icon } from '@iconify/react'; +import { ColumnDef } from '@tanstack/react-table'; +import Button from '@/components/Button'; +import Tooltip from '@/components/Tooltip'; +import DrawerHeader from '@/components/helper/drawer/DrawerHeader'; +import { useUiStore } from '@/stores/ui/ui.store'; +import RequirePermission from '@/components/helper/RequirePermission'; +import Table from '@/components/Table'; +import { useRouter } from 'next/navigation'; +import toast from 'react-hot-toast'; +import { UniformityApi } from '@/services/api/uniformity'; +import { isResponseError } from '@/lib/api-helper'; + +type BodyWeightData = { + id: string; + number: number; + weight: number; +}; + +const UniformityResultForm = () => { + const router = useRouter(); + const setExpandedDrawerOpen = useUiStore((s) => s.setExpandedDrawerOpen); + const setIsNextStep = useUiStore((s) => s.setIsNextStep); + const setUniformityStep = useUiStore((s) => s.setUniformityStep); + const verifyUniformityResult = useUiStore((s) => s.verifyUniformityResult); + const setVerifyUniformityResult = useUiStore( + (s) => s.setVerifyUniformityResult + ); + const uniformityFormData = useUiStore((s) => s.uniformityFormData); + + const [isSubmitting, setIsSubmitting] = React.useState(false); + + const handleClose = () => { + setExpandedDrawerOpen(false); + setIsNextStep(false); + setUniformityStep('preview'); + setVerifyUniformityResult(null); + }; + + const handleBack = () => { + setUniformityStep('preview'); + }; + + const handleSubmit = async () => { + if (!uniformityFormData || !uniformityFormData.files) { + toast.error('Form data is missing. Please try again.'); + return; + } + + setIsSubmitting(true); + + try { + const payload = { + date: uniformityFormData.date, + project_flock_kandang_id: uniformityFormData.project_flock_kandang_id, + files: uniformityFormData.files, + }; + + const res = await UniformityApi.createUniformity(payload); + + if (isResponseError(res)) { + toast.error(res.message); + return; + } + + toast.success('Uniformity created successfully!'); + + setExpandedDrawerOpen(false); + setIsNextStep(false); + setUniformityStep('preview'); + setVerifyUniformityResult(null); + router.push('/uniformity'); + } finally { + setIsSubmitting(false); + } + }; + + const tableData = useMemo(() => { + if (!verifyUniformityResult) return []; + + return verifyUniformityResult.body_weights.map((weight, index) => ({ + id: `weight-${index}`, + number: index + 1, + weight: weight, + })); + }, [verifyUniformityResult]); + + const columns: ColumnDef[] = useMemo( + () => [ + { + accessorKey: 'number', + header: 'No', + cell: (props) => props.row.original.number, + }, + { + accessorKey: 'weight', + header: 'Weight (g)', + cell: (props) => ( + {props.row.original.weight} + ), + }, + ], + [] + ); + + return ( +
+ {/* Header */} + + + + + + + {/* Form Section */} +
+
+ {verifyUniformityResult ? ( +
+
+ + data={tableData} + columns={columns} + pageSize={15} + className={{ containerClassName: 'mb-5' }} + /> +
+ + {/* Action Buttons */} + + + +
+ ) : ( +
+ +

No data available

+

Upload a file to verify uniformity

+
+ )} +
+
+ ); +}; + +export default UniformityResultForm; diff --git a/src/stores/ui/slices/drawer.slice.ts b/src/stores/ui/slices/drawer.slice.ts index c8eb3c8b..4e93dea9 100644 --- a/src/stores/ui/slices/drawer.slice.ts +++ b/src/stores/ui/slices/drawer.slice.ts @@ -52,4 +52,10 @@ export const createDrawerUISlice: StateCreator< verifyUniformityResult: null, setVerifyUniformityResult: (result) => set({ verifyUniformityResult: result }), + + uniformityStep: 'preview', + setUniformityStep: (step) => set({ uniformityStep: step }), + + uniformityFormData: null, + setUniformityFormData: (data) => set({ uniformityFormData: data }), }); diff --git a/src/types/stores.d.ts b/src/types/stores.d.ts index ff18d06a..de103cfd 100644 --- a/src/types/stores.d.ts +++ b/src/types/stores.d.ts @@ -1,11 +1,20 @@ import type { ReactNode } from 'react'; -import type { VerifyUniformityResponse } from '@/types/api/uniformity/uniformity'; +import type { + VerifyUniformityResponse, + CreateUniformityPayload, +} from '@/types/api/uniformity/uniformity'; type MainUiSlice = { mainDrawerOpen: boolean; setMainDrawerOpen: (open: boolean) => void; }; +type UniformityFormData = { + date: string; + project_flock_kandang_id: number; + files: File | null; +}; + type DrawerUISlice = { triggerValidate: boolean; toggleValidate: () => void; @@ -21,6 +30,10 @@ type DrawerUISlice = { setIsNextStep: (v: boolean) => void; verifyUniformityResult: VerifyUniformityResponse | null; setVerifyUniformityResult: (result: VerifyUniformityResponse | null) => void; + uniformityStep: 'preview' | 'result'; + setUniformityStep: (step: 'preview' | 'result') => void; + uniformityFormData: UniformityFormData | null; + setUniformityFormData: (data: UniformityFormData | null) => void; }; export type UIStore = MainUiSlice & DrawerUISlice;