feat(FE): Add project flock closing modal and zustand store

This commit is contained in:
rstubryan
2026-02-20 10:23:47 +07:00
parent 4c3e7c615f
commit 797f88fe15
4 changed files with 178 additions and 47 deletions
@@ -37,6 +37,7 @@ import ProjectFlockConfirmationModal from './ProjectFlockConfirmationModal';
import { useProjectFlockStore } from '@/stores/production/project-flock/project-flock.store'; import { useProjectFlockStore } from '@/stores/production/project-flock/project-flock.store';
import { ProjectFlockFormValues } from './form/ProjectFlockForm.schema'; import { ProjectFlockFormValues } from './form/ProjectFlockForm.schema';
import { useChickinStore } from '@/stores/production/chickin/chickin.store'; import { useChickinStore } from '@/stores/production/chickin/chickin.store';
import { useProjectFlockClosingStore } from '@/stores/production/project-flock-closing/project-flock-closing.store';
const RowOptionsMenu = ({ const RowOptionsMenu = ({
props, props,
@@ -195,6 +196,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
const confirmModal = useModal(); const confirmModal = useModal();
const successModal = useModal(); const successModal = useModal();
const chickinApproveModal = useModal(); const chickinApproveModal = useModal();
const closingModal = useModal();
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>( const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
'APPROVED' 'APPROVED'
); );
@@ -210,6 +212,15 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
setChickinApproveLoading, setChickinApproveLoading,
} = useChickinStore(); } = useChickinStore();
const {
isClosingModalOpen,
isKandangClosed,
isClosingLoading,
closingCallback,
closeClosingModal,
setClosingLoading,
} = useProjectFlockClosingStore();
// ===== Fetch Data ===== // ===== Fetch Data =====
const { const {
data: projectFlocks, data: projectFlocks,
@@ -309,6 +320,14 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
} }
}, [isChickinApproveModalOpen, chickinApproveModal]); }, [isChickinApproveModalOpen, chickinApproveModal]);
useEffect(() => {
if (isClosingModalOpen) {
closingModal.openModal();
} else {
closingModal.closeModal();
}
}, [isClosingModalOpen, closingModal]);
useEffect(() => { useEffect(() => {
if (isSuccess) { if (isSuccess) {
successModal.openModal(); successModal.openModal();
@@ -1025,6 +1044,45 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
isLoading: isChickinApproveLoading, isLoading: isChickinApproveLoading,
}} }}
/> />
{/* Project Flock Closing Modal */}
<ConfirmationModal
ref={closingModal.ref}
type='error'
text={
!isKandangClosed
? 'Apakah kamu yakin ingin mengakhiri project ini ? *Pastikan persediaan produk di gudang terkait sudah kosong, dan BOP sudah selesai'
: 'Apakah kamu yakin ingin membuka kembali project ini ? *Project ini akan kembali ke status aktif'
}
className={{
modal: 'z-9999',
}}
secondaryButton={{
text: 'Tidak',
onClick: () => {
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();
}
}
},
}}
/>
</> </>
); );
}; };
@@ -17,9 +17,8 @@ import { Icon } from '@iconify/react';
import useSWR from 'swr'; import useSWR from 'swr';
import { ProjectFlockKandangApi } from '@/services/api/production/project-flock-kandang'; import { ProjectFlockKandangApi } from '@/services/api/production/project-flock-kandang';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { useModal } from '@/components/Modal'; import { useProjectFlockClosingStore } from '@/stores/production/project-flock-closing/project-flock-closing.store';
import ConfirmationModal from '@/components/modal/ConfirmationModal'; import { useMemo } from 'react';
import { useMemo, useState } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { ApprovalApi } from '@/services/api/approval'; import { ApprovalApi } from '@/services/api/approval';
@@ -53,9 +52,8 @@ const ProjectFlockClosingForm = ({
projectFlockKandang: ProjectFlockKandang; projectFlockKandang: ProjectFlockKandang;
}) => { }) => {
const router = useRouter(); const router = useRouter();
const closeModal = useModal();
const [isClosingLoading, setIsClosingLoading] = useState(false); const { openClosingModal } = useProjectFlockClosingStore();
const { data: closingData, isLoading } = useSWR( const { data: closingData, isLoading } = useSWR(
`${ProjectFlockKandangApi.basePath}/${projectFlockKandang.id}/closing`, `${ProjectFlockKandangApi.basePath}/${projectFlockKandang.id}/closing`,
@@ -80,15 +78,14 @@ const ProjectFlockClosingForm = ({
: true; : true;
}, [projectFlockKandangApprovals]); }, [projectFlockKandangApprovals]);
const confirmationModalCloseClickHandler = async () => { const handleCloseClick = () => {
setIsClosingLoading(true); const closingCallback = async (action: 'close' | 'unclose') => {
const deleteProjectFlockRes = await ProjectFlockKandangApi.closing( const deleteProjectFlockRes = await ProjectFlockKandangApi.closing(
projectFlockKandang?.id as number, projectFlockKandang?.id as number,
{ {
closed_date: !isKandangClosed closed_date:
? formatDate(new Date(), 'YYYY-MM-DD') action === 'close' ? formatDate(new Date(), 'YYYY-MM-DD') : '',
: '', action,
action: !isKandangClosed ? 'close' : 'unclose',
} }
); );
@@ -101,8 +98,14 @@ const ProjectFlockClosingForm = ({
if (isResponseError(deleteProjectFlockRes)) { if (isResponseError(deleteProjectFlockRes)) {
toast.error(deleteProjectFlockRes?.message as string); toast.error(deleteProjectFlockRes?.message as string);
} }
setIsClosingLoading(false); };
closeModal.closeModal();
openClosingModal(
projectFlockKandang,
projectFlock.id,
isKandangClosed,
closingCallback
);
}; };
// const errorStock = useMemo(() => { // const errorStock = useMemo(() => {
@@ -334,7 +337,7 @@ const ProjectFlockClosingForm = ({
color='error' color='error'
isLoading={isLoading} isLoading={isLoading}
disabled={!isCanCloseValid} disabled={!isCanCloseValid}
onClick={() => closeModal.openModal()} onClick={handleCloseClick}
> >
<Icon <Icon
icon={ icon={
@@ -347,25 +350,6 @@ const ProjectFlockClosingForm = ({
</Button> </Button>
</RequirePermission> </RequirePermission>
</div> </div>
<ConfirmationModal
ref={closeModal.ref}
type='error'
text={
!isKandangClosed
? 'Apakah kamu yakin ingin mengakhiri project ini ? *Pastikan persediaan produk di gudang terkait sudah kosong, dan BOP sudah selesai'
: 'Apakah kamu yakin ingin membuka kembali project ini ? *Project ini akan kembali ke status aktif'
}
secondaryButton={{
text: 'Tidak',
}}
primaryButton={{
text: 'Ya',
color: 'error',
isLoading: isClosingLoading,
onClick: confirmationModalCloseClickHandler,
}}
/>
</div> </div>
</> </>
); );
@@ -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<ProjectFlockClosingStore>()(
devtools(
(...args) => ({
...createProjectFlockClosingSlice(...args),
}),
{
name: 'ProjectFlockClosingStore',
}
)
);
@@ -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<void>) | null;
// Actions
openClosingModal: (
data: ProjectFlockKandang,
projectFlockId: number,
isClosed: boolean,
callback: (action: 'close' | 'unclose') => Promise<void>
) => 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,
}),
});