From 797f88fe15736091ffd1628c1bfdff228a44c8ff Mon Sep 17 00:00:00 2001 From: rstubryan Date: Fri, 20 Feb 2026 10:23:47 +0700 Subject: [PATCH] feat(FE): Add project flock closing modal and zustand store --- .../project-flock/ProjectFlockTable.tsx | 58 ++++++++++++++ .../closing/ProjectFlockClosingForm.tsx | 78 ++++++++----------- .../project-flock-closing.store.ts | 19 +++++ .../slices/project-flock-closing.slice.ts | 70 +++++++++++++++++ 4 files changed, 178 insertions(+), 47 deletions(-) create mode 100644 src/stores/production/project-flock-closing/project-flock-closing.store.ts create mode 100644 src/stores/production/project-flock-closing/slices/project-flock-closing.slice.ts diff --git a/src/components/pages/production/project-flock/ProjectFlockTable.tsx b/src/components/pages/production/project-flock/ProjectFlockTable.tsx index 040948ff..040771a8 100644 --- a/src/components/pages/production/project-flock/ProjectFlockTable.tsx +++ b/src/components/pages/production/project-flock/ProjectFlockTable.tsx @@ -37,6 +37,7 @@ import ProjectFlockConfirmationModal from './ProjectFlockConfirmationModal'; import { useProjectFlockStore } from '@/stores/production/project-flock/project-flock.store'; import { ProjectFlockFormValues } from './form/ProjectFlockForm.schema'; import { useChickinStore } from '@/stores/production/chickin/chickin.store'; +import { useProjectFlockClosingStore } from '@/stores/production/project-flock-closing/project-flock-closing.store'; const RowOptionsMenu = ({ props, @@ -195,6 +196,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { const confirmModal = useModal(); const successModal = useModal(); const chickinApproveModal = useModal(); + const closingModal = useModal(); const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>( 'APPROVED' ); @@ -210,6 +212,15 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { setChickinApproveLoading, } = useChickinStore(); + const { + isClosingModalOpen, + isKandangClosed, + isClosingLoading, + closingCallback, + closeClosingModal, + setClosingLoading, + } = useProjectFlockClosingStore(); + // ===== Fetch Data ===== const { data: projectFlocks, @@ -309,6 +320,14 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { } }, [isChickinApproveModalOpen, chickinApproveModal]); + useEffect(() => { + if (isClosingModalOpen) { + closingModal.openModal(); + } else { + closingModal.closeModal(); + } + }, [isClosingModalOpen, closingModal]); + useEffect(() => { if (isSuccess) { successModal.openModal(); @@ -1025,6 +1044,45 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { isLoading: isChickinApproveLoading, }} /> + + {/* Project Flock Closing Modal */} + { + closeClosingModal(); + closingModal.closeModal(); + }, + }} + primaryButton={{ + text: 'Ya', + color: 'error', + isLoading: isClosingLoading, + onClick: async () => { + if (closingCallback) { + setClosingLoading(true); + try { + await closingCallback(!isKandangClosed ? 'close' : 'unclose'); + } finally { + setClosingLoading(false); + closeClosingModal(); + closingModal.closeModal(); + refreshProjectFlocks(); + } + } + }, + }} + /> ); }; diff --git a/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx b/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx index f963a793..e73a157a 100644 --- a/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx +++ b/src/components/pages/production/project-flock/closing/ProjectFlockClosingForm.tsx @@ -17,9 +17,8 @@ import { Icon } from '@iconify/react'; import useSWR from 'swr'; import { ProjectFlockKandangApi } from '@/services/api/production/project-flock-kandang'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; -import { useModal } from '@/components/Modal'; -import ConfirmationModal from '@/components/modal/ConfirmationModal'; -import { useMemo, useState } from 'react'; +import { useProjectFlockClosingStore } from '@/stores/production/project-flock-closing/project-flock-closing.store'; +import { useMemo } from 'react'; import toast from 'react-hot-toast'; import { useRouter } from 'next/navigation'; import { ApprovalApi } from '@/services/api/approval'; @@ -53,9 +52,8 @@ const ProjectFlockClosingForm = ({ projectFlockKandang: ProjectFlockKandang; }) => { const router = useRouter(); - const closeModal = useModal(); - const [isClosingLoading, setIsClosingLoading] = useState(false); + const { openClosingModal } = useProjectFlockClosingStore(); const { data: closingData, isLoading } = useSWR( `${ProjectFlockKandangApi.basePath}/${projectFlockKandang.id}/closing`, @@ -80,29 +78,34 @@ const ProjectFlockClosingForm = ({ : true; }, [projectFlockKandangApprovals]); - const confirmationModalCloseClickHandler = async () => { - setIsClosingLoading(true); - const deleteProjectFlockRes = await ProjectFlockKandangApi.closing( - projectFlockKandang?.id as number, - { - closed_date: !isKandangClosed - ? formatDate(new Date(), 'YYYY-MM-DD') - : '', - action: !isKandangClosed ? 'close' : 'unclose', - } - ); - - if (isResponseSuccess(deleteProjectFlockRes)) { - toast.success(deleteProjectFlockRes?.message as string); - router.push( - `/production/project-flock/detail?projectFlockId=${projectFlock.id}` + const handleCloseClick = () => { + const closingCallback = async (action: 'close' | 'unclose') => { + const deleteProjectFlockRes = await ProjectFlockKandangApi.closing( + projectFlockKandang?.id as number, + { + closed_date: + action === 'close' ? formatDate(new Date(), 'YYYY-MM-DD') : '', + action, + } ); - } - if (isResponseError(deleteProjectFlockRes)) { - toast.error(deleteProjectFlockRes?.message as string); - } - setIsClosingLoading(false); - closeModal.closeModal(); + + if (isResponseSuccess(deleteProjectFlockRes)) { + toast.success(deleteProjectFlockRes?.message as string); + router.push( + `/production/project-flock/detail?projectFlockId=${projectFlock.id}` + ); + } + if (isResponseError(deleteProjectFlockRes)) { + toast.error(deleteProjectFlockRes?.message as string); + } + }; + + openClosingModal( + projectFlockKandang, + projectFlock.id, + isKandangClosed, + closingCallback + ); }; // const errorStock = useMemo(() => { @@ -334,7 +337,7 @@ const ProjectFlockClosingForm = ({ color='error' isLoading={isLoading} disabled={!isCanCloseValid} - onClick={() => closeModal.openModal()} + onClick={handleCloseClick} > - - ); diff --git a/src/stores/production/project-flock-closing/project-flock-closing.store.ts b/src/stores/production/project-flock-closing/project-flock-closing.store.ts new file mode 100644 index 00000000..b6543b97 --- /dev/null +++ b/src/stores/production/project-flock-closing/project-flock-closing.store.ts @@ -0,0 +1,19 @@ +'use client'; + +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { createProjectFlockClosingSlice } from '@/stores/production/project-flock-closing/slices/project-flock-closing.slice'; +import { ProjectFlockClosingSlice } from '@/stores/production/project-flock-closing/slices/project-flock-closing.slice'; + +export type ProjectFlockClosingStore = ProjectFlockClosingSlice; + +export const useProjectFlockClosingStore = create()( + devtools( + (...args) => ({ + ...createProjectFlockClosingSlice(...args), + }), + { + name: 'ProjectFlockClosingStore', + } + ) +); diff --git a/src/stores/production/project-flock-closing/slices/project-flock-closing.slice.ts b/src/stores/production/project-flock-closing/slices/project-flock-closing.slice.ts new file mode 100644 index 00000000..faff8816 --- /dev/null +++ b/src/stores/production/project-flock-closing/slices/project-flock-closing.slice.ts @@ -0,0 +1,70 @@ +import { StateCreator } from 'zustand'; +import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; + +export type ProjectFlockClosingSlice = { + // State + isClosingModalOpen: boolean; + selectedProjectFlockKandang: ProjectFlockKandang | null; + projectFlockId: number | null; + isKandangClosed: boolean; + isClosingLoading: boolean; + closingCallback: ((action: 'close' | 'unclose') => Promise) | null; + + // Actions + openClosingModal: ( + data: ProjectFlockKandang, + projectFlockId: number, + isClosed: boolean, + callback: (action: 'close' | 'unclose') => Promise + ) => void; + closeClosingModal: () => void; + setClosingLoading: (loading: boolean) => void; + resetClosing: () => void; +}; + +export const createProjectFlockClosingSlice: StateCreator< + ProjectFlockClosingSlice, + [], + [], + ProjectFlockClosingSlice +> = (set) => ({ + // Initial state + isClosingModalOpen: false, + selectedProjectFlockKandang: null, + projectFlockId: null, + isKandangClosed: false, + isClosingLoading: false, + closingCallback: null, + + // Actions + openClosingModal: (data, projectFlockId, isClosed, callback) => + set({ + isClosingModalOpen: true, + selectedProjectFlockKandang: data, + projectFlockId, + isKandangClosed: isClosed, + closingCallback: callback, + }), + + closeClosingModal: () => + set({ + isClosingModalOpen: false, + selectedProjectFlockKandang: null, + projectFlockId: null, + isKandangClosed: false, + closingCallback: null, + }), + + setClosingLoading: (loading) => + set({ isClosingLoading: loading }), + + resetClosing: () => + set({ + isClosingModalOpen: false, + selectedProjectFlockKandang: null, + projectFlockId: null, + isKandangClosed: false, + isClosingLoading: false, + closingCallback: null, + }), +});