feat(FE-316): Add Uniformity result drawer and flow

This commit is contained in:
rstubryan
2025-12-27 21:25:06 +07:00
parent 549a710a8d
commit 819b709f7e
5 changed files with 228 additions and 5 deletions
@@ -37,6 +37,7 @@ import { type BaseApiResponse } from '@/types/api/api-general';
import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock'; import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock';
import { Kandang } from '@/types/api/master-data/kandang'; import { Kandang } from '@/types/api/master-data/kandang';
import UniformityPreviewForm from '@/components/pages/uniformity/form/UniformityPreviewForm'; import UniformityPreviewForm from '@/components/pages/uniformity/form/UniformityPreviewForm';
import UniformityResultForm from '@/components/pages/uniformity/form/UniformityResultForm';
import useSWR from 'swr'; import useSWR from 'swr';
import { cn } from '@/lib/helper'; import { cn } from '@/lib/helper';
@@ -63,6 +64,9 @@ const UniformityForm = ({
const setVerifyUniformityResult = useUiStore( const setVerifyUniformityResult = useUiStore(
(s) => s.setVerifyUniformityResult (s) => s.setVerifyUniformityResult
); );
const setUniformityFormData = useUiStore((s) => s.setUniformityFormData);
const uniformityStep = useUiStore((s) => s.uniformityStep);
const setUniformityStep = useUiStore((s) => s.setUniformityStep);
const [uniformityFormErrorMessage, setUniformityFormErrorMessage] = const [uniformityFormErrorMessage, setUniformityFormErrorMessage] =
useState(''); useState('');
@@ -230,6 +234,12 @@ const UniformityForm = ({
return; return;
} }
setUniformityFormData({
date: values.date,
project_flock_kandang_id: projectFlockKandangId,
files: values.files as File,
});
const payload: VerifyUniformityPayload = { const payload: VerifyUniformityPayload = {
project_flock_kandang_id: projectFlockKandangId, project_flock_kandang_id: projectFlockKandangId,
files: values.files as File, files: values.files as File,
@@ -251,6 +261,7 @@ const UniformityForm = ({
if (formType === 'add') { if (formType === 'add') {
setIsNextStep(true); setIsNextStep(true);
setExpandedDrawerOpen(true); setExpandedDrawerOpen(true);
setUniformityStep('preview');
} else { } else {
router.push('/uniformity'); router.push('/uniformity');
} }
@@ -361,11 +372,15 @@ const UniformityForm = ({
useEffect(() => { useEffect(() => {
if (expandedDrawerOpen) { if (expandedDrawerOpen) {
setExpandedDrawerContent(<UniformityPreviewForm />); if (uniformityStep === 'preview') {
setExpandedDrawerContent(<UniformityPreviewForm />);
} else if (uniformityStep === 'result') {
setExpandedDrawerContent(<UniformityResultForm />);
}
} else { } else {
setExpandedDrawerContent(null); setExpandedDrawerContent(null);
} }
}, [expandedDrawerOpen, setExpandedDrawerContent]); }, [expandedDrawerOpen, uniformityStep, setExpandedDrawerContent]);
return ( return (
<> <>
@@ -19,11 +19,17 @@ type BodyWeightData = {
const UniformityPreviewForm = () => { const UniformityPreviewForm = () => {
const setExpandedDrawerOpen = useUiStore((s) => s.setExpandedDrawerOpen); const setExpandedDrawerOpen = useUiStore((s) => s.setExpandedDrawerOpen);
const setIsNextStep = useUiStore((s) => s.setIsNextStep); const setIsNextStep = useUiStore((s) => s.setIsNextStep);
const setUniformityStep = useUiStore((s) => s.setUniformityStep);
const verifyUniformityResult = useUiStore((s) => s.verifyUniformityResult); const verifyUniformityResult = useUiStore((s) => s.verifyUniformityResult);
const handleClose = () => { const handleClose = () => {
setExpandedDrawerOpen(false); setExpandedDrawerOpen(false);
setIsNextStep(false); setIsNextStep(false);
setUniformityStep('preview');
};
const handleNext = () => {
setUniformityStep('result');
}; };
const tableData = useMemo(() => { const tableData = useMemo(() => {
@@ -87,9 +93,14 @@ const UniformityPreviewForm = () => {
<Table<BodyWeightData> <Table<BodyWeightData>
data={tableData} data={tableData}
columns={columns} columns={columns}
pageSize={20} pageSize={15}
className={{ containerClassName: 'mb-10' }} className={{ containerClassName: 'mb-5' }}
/> />
<RequirePermission permissions='lti.production.uniformity.create'>
<Button color='primary' onClick={handleNext} className='mb-10'>
Next
</Button>
</RequirePermission>
</div> </div>
) : ( ) : (
<div className='flex flex-col items-center justify-center py-10 text-gray-400'> <div className='flex flex-col items-center justify-center py-10 text-gray-400'>
@@ -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<BodyWeightData>[] = useMemo(
() => [
{
accessorKey: 'number',
header: 'No',
cell: (props) => props.row.original.number,
},
{
accessorKey: 'weight',
header: 'Weight (g)',
cell: (props) => (
<span className='font-medium'>{props.row.original.weight}</span>
),
},
],
[]
);
return (
<section className='w-full h-full bg-white border-l border-gray-200'>
{/* Header */}
<DrawerHeader
leftIcon='mdi:arrow-left'
leftIconSize={24}
leftIconOnClick={handleBack}
leftIconClassName='hover:text-gray-400 cursor-pointer'
subtitle='Uniformity Result'
subtitleClassName='text-sm text-neutral'
showDivider
>
<RequirePermission permissions='lti.production.uniformity.delete'>
<Button
variant='link'
className='p-0 text-error'
onClick={handleClose}
>
<Tooltip content='Tutup' position='bottom'>
<Icon icon='mdi:close' width={20} height={20} />
</Tooltip>
</Button>
</RequirePermission>
</DrawerHeader>
{/* Form Section */}
<div className='divider mt-3'></div>
<section className='w-full px-6'>
{verifyUniformityResult ? (
<div className='flex flex-col gap-4'>
<div className='mt-4'>
<Table<BodyWeightData>
data={tableData}
columns={columns}
pageSize={15}
className={{ containerClassName: 'mb-5' }}
/>
</div>
{/* Action Buttons */}
<RequirePermission permissions='lti.production.uniformity.create'>
<Button
color='primary'
onClick={handleSubmit}
isLoading={isSubmitting}
disabled={!uniformityFormData}
className='mb-10'
>
Submit
</Button>
</RequirePermission>
</div>
) : (
<div className='flex flex-col items-center justify-center py-10 text-gray-400'>
<Icon
icon='mdi:file-document-outline'
width={64}
height={64}
className='mb-4'
/>
<p className='text-lg'>No data available</p>
<p className='text-sm'>Upload a file to verify uniformity</p>
</div>
)}
</section>
</section>
);
};
export default UniformityResultForm;
+6
View File
@@ -52,4 +52,10 @@ export const createDrawerUISlice: StateCreator<
verifyUniformityResult: null, verifyUniformityResult: null,
setVerifyUniformityResult: (result) => setVerifyUniformityResult: (result) =>
set({ verifyUniformityResult: result }), set({ verifyUniformityResult: result }),
uniformityStep: 'preview',
setUniformityStep: (step) => set({ uniformityStep: step }),
uniformityFormData: null,
setUniformityFormData: (data) => set({ uniformityFormData: data }),
}); });
+14 -1
View File
@@ -1,11 +1,20 @@
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import type { VerifyUniformityResponse } from '@/types/api/uniformity/uniformity'; import type {
VerifyUniformityResponse,
CreateUniformityPayload,
} from '@/types/api/uniformity/uniformity';
type MainUiSlice = { type MainUiSlice = {
mainDrawerOpen: boolean; mainDrawerOpen: boolean;
setMainDrawerOpen: (open: boolean) => void; setMainDrawerOpen: (open: boolean) => void;
}; };
type UniformityFormData = {
date: string;
project_flock_kandang_id: number;
files: File | null;
};
type DrawerUISlice = { type DrawerUISlice = {
triggerValidate: boolean; triggerValidate: boolean;
toggleValidate: () => void; toggleValidate: () => void;
@@ -21,6 +30,10 @@ type DrawerUISlice = {
setIsNextStep: (v: boolean) => void; setIsNextStep: (v: boolean) => void;
verifyUniformityResult: VerifyUniformityResponse | null; verifyUniformityResult: VerifyUniformityResponse | null;
setVerifyUniformityResult: (result: VerifyUniformityResponse | null) => void; 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; export type UIStore = MainUiSlice & DrawerUISlice;