diff --git a/src/app/production/project-flock/layout.tsx b/src/app/production/project-flock/layout.tsx index e7c45600..5d0c7fd3 100644 --- a/src/app/production/project-flock/layout.tsx +++ b/src/app/production/project-flock/layout.tsx @@ -4,6 +4,7 @@ import { usePathname, useRouter } from 'next/navigation'; import Drawer from '@/components/Drawer'; import React, { ReactNode } from 'react'; import ProjectFlockTable from '@/components/pages/production/project-flock/ProjectFlockTable'; +import { useUiStore } from '@/stores/ui/ui.store'; export default function ProjectFlockLayout({ children, @@ -12,6 +13,7 @@ export default function ProjectFlockLayout({ }) { const pathname = usePathname(); const router = useRouter(); + const toggleValidate = useUiStore((s) => s.toggleValidate); const isAdd = pathname.endsWith('/add'); const isEdit = pathname.includes('/detail/edit'); @@ -23,13 +25,14 @@ export default function ProjectFlockLayout({ // const childRef = useRef(null); const handleBackdropClick = () => { - // const isValid = childRef.current?.validate(); // 🔥 trigger validation child + const unsub = useUiStore.getState().subscribeIsValid((isValid) => { + if (isValid) { + unsub(); // berhenti listen + router.push('/production/project-flock'); + } + }); - // if (!isValid) { - // toast.error('Form belum valid, Drawer tidak bisa close'); - // return; - // } - router.push('/production/project-flock'); + toggleValidate(); }; return ( diff --git a/src/components/input/DateInput.tsx b/src/components/input/DateInput.tsx index 77267090..2d55fe6d 100644 --- a/src/components/input/DateInput.tsx +++ b/src/components/input/DateInput.tsx @@ -7,11 +7,11 @@ import { useState, } from 'react'; import { cn, formatDate } from '@/lib/helper'; -import Modal, { useModal } from '../Modal'; import { DateRange, DayPicker, Matcher } from 'react-day-picker'; import 'react-day-picker/dist/style.css'; -import Button from '../Button'; import { Icon } from '@iconify/react'; +import Modal, { useModal } from '@/components/Modal'; +import Button from '@/components/Button'; export interface DateInputProps { label?: string; diff --git a/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx b/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx index ebc9a4e2..8162b8f8 100644 --- a/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx +++ b/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx @@ -43,6 +43,7 @@ import { PROJECT_FLOCK_APPROVAL_LINE } from '@/config/approval-line'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import NumberInput from '@/components/input/NumberInput'; import Card from '@/components/Card'; +import { useUiStore } from '@/stores/ui/ui.store'; interface ProjectFlockFormProps { formType?: 'add' | 'edit' | 'detail'; @@ -79,6 +80,8 @@ const ProjectFlockForm = ({ initialValues?.flock_name?.lastIndexOf(' ') ) ?? '' ); + const subscribeValidate = useUiStore((s) => s.subscribeValidate); + const setIsValid = useUiStore((s) => s.setIsValid); const deleteModal = useModal(); const confirmModal = useModal(); @@ -577,6 +580,29 @@ const ProjectFlockForm = ({ // return isValid; // }, // })); + useEffect(() => { + const unsub = subscribeValidate(() => { + formik.validateForm().then((errors) => { + if (Object.keys(errors).length > 0) { + // Membentuk touched object yang strongly-typed + const touched = Object.keys(formik.values).reduce< + Record + >( + (acc, key) => { + acc[key as keyof typeof formik.values] = true; + return acc; + }, + {} as Record + ); + + formik.setTouched(touched, true); + } + setIsValid(Object.keys(errors).length === 0); + }); + }); + + return unsub; + }, []); return ( <> diff --git a/src/stores/ui/slices/drawer.slice.ts b/src/stores/ui/slices/drawer.slice.ts new file mode 100644 index 00000000..1d3f54a2 --- /dev/null +++ b/src/stores/ui/slices/drawer.slice.ts @@ -0,0 +1,40 @@ +import { StateCreator } from 'zustand'; +import { DrawerUiSlice } from '@/types/stores'; + +export const createFormDrawerUiSlice: StateCreator< + DrawerUiSlice, + [], + [], + DrawerUiSlice +> = (set, get, api) => ({ + // event flag untuk memicu formik validate + triggerValidate: false, + + // dibalik untuk memicu event + toggleValidate: () => { + const current = get().triggerValidate; + set({ triggerValidate: !current }); + }, + + // sistem subscriber sederhana agar form bisa listen perubahan flag + subscribeValidate: (callback: () => void) => { + let prev = get().triggerValidate; + + const unsub = api.subscribe((state) => { + if (state.triggerValidate !== prev) { + prev = state.triggerValidate; + callback(); + } + }); + + return unsub; + }, + + isValid: false, + setIsValid: (isValid: boolean) => set({ isValid }), + subscribeIsValid: (callback: (isValid: boolean) => void) => { + return api.subscribe((state) => { + callback(Boolean(state.isValid)); + }); + }, +}); diff --git a/src/stores/ui/ui.store.ts b/src/stores/ui/ui.store.ts index 49554bc9..09d5d1a4 100644 --- a/src/stores/ui/ui.store.ts +++ b/src/stores/ui/ui.store.ts @@ -5,11 +5,13 @@ import { devtools } from 'zustand/middleware'; import { UIStore } from '@/types/stores'; import { createMainUiSlice } from '@/stores/ui/slices/main.slice'; +import { createFormDrawerUiSlice } from '@/stores/ui/slices/drawer.slice'; export const useUiStore = create()( devtools( (...args) => ({ ...createMainUiSlice(...args), + ...createFormDrawerUiSlice(...args), }), { name: 'UIStore', diff --git a/src/types/stores.d.ts b/src/types/stores.d.ts index 1a3046ae..23e2358c 100644 --- a/src/types/stores.d.ts +++ b/src/types/stores.d.ts @@ -3,4 +3,13 @@ type MainUiSlice = { setMainDrawerOpen: (open: boolean) => void; }; -export type UIStore = MainUiSlice; +type DrawerUiSlice = { + triggerValidate: boolean; + toggleValidate: () => void; + subscribeValidate: (callback: () => void) => void; + isValid: boolean; + setIsValid: (v: boolean) => void; + subscribeIsValid: (callback: (isValid: boolean) => void) => () => void; +}; + +export type UIStore = MainUiSlice & DrawerUiSlice;