From c76f3a3715776cdcf437afecfb927a1b3660cd9a Mon Sep 17 00:00:00 2001 From: randy-ar Date: Tue, 2 Dec 2025 04:11:01 +0700 Subject: [PATCH] feat(FE): US#278 slicing UI from and client side validation --- src/app/production/project-flock/layout.tsx | 24 +- .../project-flock/ProjectFlockTable.tsx | 11 +- .../form/ProjectFlockForm.schema.ts | 156 +++++-- .../project-flock/form/ProjectFlockForm.tsx | 427 ++++++++++++++++-- .../project-flock.slice.ts} | 29 +- src/stores/ui/ui.store.ts | 2 - src/types/api/production/project-flock.d.ts | 11 + src/types/stores.d.ts | 11 +- 8 files changed, 556 insertions(+), 115 deletions(-) rename src/stores/ui/slices/{drawer.slice.ts => production/project-flock.slice.ts} (56%) diff --git a/src/app/production/project-flock/layout.tsx b/src/app/production/project-flock/layout.tsx index 5d0c7fd3..6bf27a41 100644 --- a/src/app/production/project-flock/layout.tsx +++ b/src/app/production/project-flock/layout.tsx @@ -4,7 +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'; +import { useProjectFlockUiStore } from '@/stores/ui/slices/production/project-flock.slice'; export default function ProjectFlockLayout({ children, @@ -13,7 +13,7 @@ export default function ProjectFlockLayout({ }) { const pathname = usePathname(); const router = useRouter(); - const toggleValidate = useUiStore((s) => s.toggleValidate); + const toggleValidate = useProjectFlockUiStore((s) => s.toggleValidate); const isAdd = pathname.endsWith('/add'); const isEdit = pathname.includes('/detail/edit'); @@ -22,15 +22,15 @@ export default function ProjectFlockLayout({ const isOpen = isAdd || isEdit || isDetail || isChickin; - // const childRef = useRef(null); - const handleBackdropClick = () => { - const unsub = useUiStore.getState().subscribeIsValid((isValid) => { - if (isValid) { - unsub(); // berhenti listen - router.push('/production/project-flock'); - } - }); + const unsub = useProjectFlockUiStore + .getState() + .subscribeIsValid((isValid) => { + if (isValid) { + unsub(); // berhenti listen + router.push('/production/project-flock'); + } + }); toggleValidate(); }; @@ -39,7 +39,9 @@ export default function ProjectFlockLayout({ <> {/* List page always rendered */}
- + !isOpen && router.push('/production/project-flock')} + />
{/* Render Drawer only on /add */} diff --git a/src/components/pages/production/project-flock/ProjectFlockTable.tsx b/src/components/pages/production/project-flock/ProjectFlockTable.tsx index 9f32ef0e..bdbfbe18 100644 --- a/src/components/pages/production/project-flock/ProjectFlockTable.tsx +++ b/src/components/pages/production/project-flock/ProjectFlockTable.tsx @@ -20,7 +20,7 @@ import { Kandang } from '@/types/api/master-data/kandang'; import { ProjectFlock } from '@/types/api/production/project-flock'; import { Icon } from '@iconify/react'; import { CellContext, SortingState } from '@tanstack/react-table'; -import { ChangeEventHandler, useRef, useState } from 'react'; +import { ChangeEventHandler, useEffect, useState } from 'react'; import toast from 'react-hot-toast'; import useSWR from 'swr'; @@ -94,7 +94,7 @@ const RowOptionsMenu = ({ ); }; -const ProjectFlockTable = () => { +const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => { const { state: tableFilterState, updateFilter, @@ -154,7 +154,8 @@ const ProjectFlockTable = () => { mutate: refreshProjectFlocks, } = useSWR( `${ProjectFlockApi.basePath}${getTableFilterQueryString()}`, - ProjectFlockApi.getAllFetcher + ProjectFlockApi.getAllFetcher, + { revalidateOnMount: true } ); const areaUrl = `${AreaApi.basePath}?${new URLSearchParams({ @@ -255,6 +256,10 @@ const ProjectFlockTable = () => { setIsApproveLoading(false); }; + useEffect(() => { + refreshProjectFlocks(); + }, [refresh]); + return ( <>
diff --git a/src/components/pages/production/project-flock/form/ProjectFlockForm.schema.ts b/src/components/pages/production/project-flock/form/ProjectFlockForm.schema.ts index ca27f64b..9ac07c0f 100644 --- a/src/components/pages/production/project-flock/form/ProjectFlockForm.schema.ts +++ b/src/components/pages/production/project-flock/form/ProjectFlockForm.schema.ts @@ -1,52 +1,124 @@ import * as Yup from 'yup'; -export const ProjectFlockFormSchema = Yup.object({ - // Flock - flock: Yup.object({ - value: Yup.number().required('ID Flock wajib diisi!'), - label: Yup.string().required('Nama Flock wajib diisi!'), - }).nullable(), - flock_name: Yup.string().required('Nama Flock wajib diisi!'), +type ProjectFlockFormSchemaType = { + flock: { + value: number | string; + label: string; + } | null; + flock_name: string; + area: { + value: number | string; + label: string; + } | null; + area_id: number; + category_option: { + value: string; + label: string; + } | null; + category: string; + fcr: { + value: number | string; + label: string; + } | null; + fcr_id: number; + location: { + value: number | string; + label: string; + } | null; + location_id: number; + kandang_ids: number[]; + project_budgets: ProjectFlockBudgetsSchemaType[]; +}; - // Area - area: Yup.object({ - value: Yup.number().required('ID Area wajib diisi!'), - label: Yup.string().required('Nama Area wajib diisi!'), - }).nullable(), - area_id: Yup.number() - .min(1, 'Area wajib diisi!') - .required('Area wajib diisi!'), +export type ProjectFlockBudgetsSchemaType = { + nonstock: { + value: number | string; + label: string; + } | null; + nonstock_id: number | string; + qty: number | string; + price: number | string; + total_price: number | string; +}; - // Kategori - category_option: Yup.object({ - value: Yup.string().required('Nilai Kategori wajib diisi!'), - label: Yup.string().required('Label Kategori wajib diisi!'), - }).nullable(), - category: Yup.string() - .oneOf(['GROWING', 'LAYING'], 'Kategori wajib diisi!') - .required('Kategori wajib diisi!'), +export const ProjectFlockBudgetsSchema: Yup.ObjectSchema = + Yup.object({ + nonstock: Yup.object({ + value: Yup.number().required('ID Nonstock wajib diisi!'), + label: Yup.string().required('Nama Nonstock wajib diisi!'), + }).required('Nonstock wajib diisi!'), + nonstock_id: Yup.number() + .min(1, 'Nonstock wajib diisi!') + .required('Nonstock wajib diisi!'), + qty: Yup.number() + .typeError('Jumlah harus berupa angka!') + .min(1, 'Jumlah minimal 1!') + .required('Jumlah wajib diisi!'), + price: Yup.number() + .typeError('Harga harus berupa angka!') + .min(1, 'Harga minimal 1!') + .required('Harga wajib diisi!'), + total_price: Yup.number() + .typeError('Harga harus berupa angka!') + .min(1, 'Harga minimal 1!') + .required('Harga wajib diisi!'), + }); - // FCR - fcr: Yup.object({ - value: Yup.number().required('ID FCR wajib diisi!'), - label: Yup.string().required('Nama FCR wajib diisi!'), - }).nullable(), - fcr_id: Yup.number().min(1, 'FCR wajib diisi!').required('FCR wajib diisi!'), +export const ProjectFlockFormSchema: Yup.ObjectSchema = + Yup.object({ + // Flock + flock: Yup.object({ + value: Yup.number().required('ID Flock wajib diisi!'), + label: Yup.string().required('Nama Flock wajib diisi!'), + }).nullable(), + flock_name: Yup.string().required('Nama Flock wajib diisi!'), - // Location - location: Yup.object({ - value: Yup.number().required('ID Lokasi wajib diisi!'), - label: Yup.string().required('Nama Lokasi wajib diisi!'), - }).nullable(), - location_id: Yup.number() - .min(1, 'Lokasi wajib diisi!') - .required('Lokasi wajib diisi!'), + // Area + area: Yup.object({ + value: Yup.number().required('ID Area wajib diisi!'), + label: Yup.string().required('Nama Area wajib diisi!'), + }).nullable(), + area_id: Yup.number() + .min(1, 'Area wajib diisi!') + .required('Area wajib diisi!'), - kandang_ids: Yup.array() - .of(Yup.number().typeError('Kandang tidak valid!')) - .min(1, 'Minimal harus ada 1 kandang!') - .required('Kandang wajib diisi!'), -}); + // Kategori + category_option: Yup.object({ + value: Yup.string().required('Nilai Kategori wajib diisi!'), + label: Yup.string().required('Label Kategori wajib diisi!'), + }).nullable(), + category: Yup.string() + .oneOf(['GROWING', 'LAYING'], 'Kategori wajib diisi!') + .required('Kategori wajib diisi!'), + + // FCR + fcr: Yup.object({ + value: Yup.number().required('ID FCR wajib diisi!'), + label: Yup.string().required('Nama FCR wajib diisi!'), + }).nullable(), + fcr_id: Yup.number() + .min(1, 'FCR wajib diisi!') + .required('FCR wajib diisi!'), + + // Location + location: Yup.object({ + value: Yup.number().required('ID Lokasi wajib diisi!'), + label: Yup.string().required('Nama Lokasi wajib diisi!'), + }).nullable(), + location_id: Yup.number() + .min(1, 'Lokasi wajib diisi!') + .required('Lokasi wajib diisi!'), + + kandang_ids: Yup.array() + .of(Yup.number().required('Kandang tidak valid!')) + .min(1, 'Minimal harus ada 1 kandang!') + .required('Kandang wajib diisi!'), + + project_budgets: Yup.array() + .of(ProjectFlockBudgetsSchema) + .min(1, 'Minimal harus ada 1 data budget!') + .required('Data budget wajib diisi!'), + }); export type ProjectFlockFormValues = Yup.InferType< typeof ProjectFlockFormSchema diff --git a/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx b/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx index ddf98941..d7a12951 100644 --- a/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx +++ b/src/components/pages/production/project-flock/form/ProjectFlockForm.tsx @@ -12,13 +12,15 @@ import { FlockApi, KandangApi, LocationApi, + NonstockApi, } from '@/services/api/master-data'; import { Icon } from '@iconify/react'; -import { useFormik } from 'formik'; +import { FormikErrors, useFormik } from 'formik'; import { useRouter } from 'next/navigation'; import { useEffect, useMemo, useState } from 'react'; import useSWR, { KeyedMutator } from 'swr'; import { + ProjectFlockBudgetsSchemaType, ProjectFlockFormSchema, ProjectFlockFormValues, UpdateProjectFlockFormSchema, @@ -26,6 +28,7 @@ import { import { CreateProjectFlockPayload, ProjectFlock, + ProjectFlockBudget, } from '@/types/api/production/project-flock'; import toast from 'react-hot-toast'; import { Kandang } from '@/types/api/master-data/kandang'; @@ -41,8 +44,9 @@ 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'; import ProjectFlockKandangTable from '@/components/pages/production/project-flock/form/ProjectFlockKandangTable'; +import { useProjectFlockUiStore } from '@/stores/ui/slices/production/project-flock.slice'; +import { Nonstock } from '@/types/api/master-data/nonstock'; interface ProjectFlockFormProps { formType?: 'add' | 'edit' | 'detail'; @@ -79,8 +83,8 @@ const ProjectFlockForm = ({ initialValues?.flock_name?.lastIndexOf(' ') ) ?? '' ); - const subscribeValidate = useUiStore((s) => s.subscribeValidate); - const setIsValid = useUiStore((s) => s.setIsValid); + const subscribeValidate = useProjectFlockUiStore((s) => s.subscribeValidate); + const setIsValid = useProjectFlockUiStore((s) => s.setIsValid); const deleteModal = useModal(); const confirmModal = useModal(); @@ -158,6 +162,12 @@ const ProjectFlockForm = ({ () => ProjectFlockApi.getNextPeriod(parseInt(selectedLocation as string)) ); + const { + options: optionsNonstock, + rawData: nonstocks, + isLoadingOptions: isLoadingNonstocks, + } = useSelect(NonstockApi.basePath, 'id', 'name'); + const { approvals, isLoading: approvalsLoading, @@ -359,10 +369,18 @@ const ProjectFlockForm = ({ >, fcr_id: initialValues?.fcr?.id ?? 0, location_id: initialValues?.location?.id ?? 0, - kandang_ids: initialValues?.kandangs?.map((k: Kandang) => k.id) as ( - | number - | undefined - )[], + kandang_ids: initialValues?.kandangs?.map( + (k: Kandang) => k.id + ) as number[], + project_budgets: [ + { + nonstock: null, + nonstock_id: '', + qty: '', + price: '', + total_price: '', + }, + ], }; }, [initialValues, optionsFlock]); @@ -441,6 +459,15 @@ const ProjectFlockForm = ({ | number | undefined )[], + project_budgets: [ + { + nonstock: null, + nonstock_id: '', + qty: '', + price: '', + total_price: '', + }, + ], } as ProjectFlockFormValues, enableReinitialize: true, validationSchema: @@ -457,6 +484,13 @@ const ProjectFlockForm = ({ fcr_id: values.fcr_id as number, location_id: values.location_id as number, kandang_ids: values.kandang_ids as number[], + project_budgets: values.project_budgets.flatMap((budget) => { + return { + nonstock_id: budget.nonstock_id, + qty: budget.qty, + price: budget.price, + } as ProjectFlockBudget; + }), }; switch (formType) { @@ -471,8 +505,8 @@ const ProjectFlockForm = ({ } }, }); - const { setValues: formikSetValues } = formik; + // Effect Initial useEffect(() => { if (formType == 'detail') { @@ -522,6 +556,33 @@ const ProjectFlockForm = ({ }); }, [rowSelection, formikSetValues]); + useEffect(() => { + const unsub = subscribeValidate(() => { + formik.validateForm().then((errors) => { + if (Object.keys(errors).length > 0) { + // Membentuk touched object yang strongly-typed + const touched: Record[]> = + {}; + Object.keys(formik.values).forEach((key) => { + if ( + key === 'project_budgets' && + Array.isArray(formik.values.project_budgets) + ) { + touched[key] = formik.values.project_budgets.map(() => ({})); // Mark each item as touched if it's an array + } else { + touched[key] = true; + } + }); + + formik.setTouched(touched, true); + } + setIsValid(Object.keys(errors).length === 0); + }); + }); + + return unsub; + }, []); + // Actions handler const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); @@ -539,6 +600,42 @@ const ProjectFlockForm = ({ setIsDeleteLoading(false); }; + const onAddBudgetRowHandler = () => { + const newProjectBudgets = [ + ...(formik.values.project_budgets ?? []), + { + nonstock: null, + nonstock_id: '', + qty: '', + price: '', + }, + ]; + formik.setFieldValue('project_budgets', newProjectBudgets); + }; + + const onDeleteBudgetRowHandler = (nonstock_id: number, index?: number) => { + console.log(`nonstock_id: ${nonstock_id}, index: ${index}`); + if (!nonstock_id) { + const updatedBudgets = formik.values.project_budgets + .map((budget, i) => { + if (i == index) { + console.log(`buget: ${null}, index: ${index}, i: ${i}`); + return null; + } else { + console.log(`buget: ${budget}, index: ${index}, i: ${i}`); + return budget; + } + }) + .filter((budget) => budget != null); + formik.setFieldValue('project_budgets', updatedBudgets); + } else { + const updatedBudgets = (formik.values.project_budgets ?? []).filter( + (budget) => budget.nonstock_id !== nonstock_id + ); + formik.setFieldValue('project_budgets', updatedBudgets); + } + }; + const confirmApprovalHandler = async ( notes: string, approvalAction: 'REJECTED' | 'APPROVED' @@ -562,6 +659,67 @@ const ProjectFlockForm = ({ setIsApproveLoading(false); }; + const handleBudgetChange = ( + index: number, + fieldName: 'qty' | 'price' | 'total_price', + value: string + ) => { + const updatedBudgets = [...formik.values.project_budgets]; + const currentBudget = updatedBudgets[index]; + + const isNewValueEmpty = value === ''; + + let numericValue: number; + + if (isNewValueEmpty) { + (currentBudget[fieldName] as string) = ''; + numericValue = 0; + + formik.setFieldValue('project_budgets', updatedBudgets); + return; + } else { + numericValue = Math.max(0, parseFloat(value) || 0); + + (currentBudget[fieldName] as number) = numericValue; + } + + const getSafeNumber = (val: string | number) => + Math.max(0, parseFloat(String(val)) || 0); + + const currentQty = getSafeNumber(currentBudget.qty); + const currentPrice = getSafeNumber(currentBudget.price); + const currentTotal = getSafeNumber(currentBudget.total_price); + + let newQty = currentQty; + let newPrice = currentPrice; + let newTotal = currentTotal; + + if (fieldName === 'price') { + // Jika Harga Satuan diubah, hitung Total Harga + newTotal = newQty * numericValue; + newPrice = numericValue; + } else if (fieldName === 'qty') { + // Jika Kuantitas diubah, hitung Total Harga + newTotal = numericValue * newPrice; + newQty = numericValue; + } else if (fieldName === 'total_price') { + // Jika Total Harga diubah, hitung Harga Satuan + newTotal = numericValue; + if (newQty > 0) { + newPrice = newTotal / newQty; + } else { + // Jika Qty 0, Harga Satuan tetap 0 + newPrice = 0; + } + } + + currentBudget.qty = newQty; + currentBudget.price = newPrice; + currentBudget.total_price = newTotal; + + formik.setFieldValue('project_budgets', updatedBudgets); + }; + const selectedPeriod = isResponseSuccess(periodFlocks) ? periodFlocks.data.find((kandang) => formik.values.kandang_ids?.includes(kandang.id) @@ -569,39 +727,11 @@ const ProjectFlockForm = ({ : undefined; const inputPeriod = (initialValues?.period ?? selectedPeriod == 0) ? 1 : selectedPeriod; - - // expose method validate() ke parent - // TODO: Buat Store untuk kirim props formik.isValid ke parent (layout.tsx) - // useImperativeHandle(ref, () => ({ - // validate() { - // formik.validateForm(); - // const isValid = formik.isValid; - // 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; - }, []); + const filteredNonStockOptions = optionsNonstock.filter((nonstock) => { + return !(formik.values.project_budgets ?? []).some( + (budget) => budget.nonstock_id === nonstock.value + ); + }); return ( <> @@ -699,6 +829,7 @@ const ProjectFlockForm = ({ onSubmit={formik.handleSubmit} onReset={formik.handleReset} > + {/* Card Informasi Umum */}
+ + {/* Card Pilih Kandang */}
@@ -833,6 +966,214 @@ const ProjectFlockForm = ({
+ {/* Card Estimasi Budget */} + + + + + + + + + + + + + {formik.values.project_budgets && + formik.values.project_budgets.length > 0 ? ( + formik.values.project_budgets.map((budget, index) => ( + + + + + + + + )) + ) : ( + + + + )} + +
ProdukKuantitasHarga SatuanHarga TotalAksi
+ { + const updatedBudgets = [ + ...formik.values.project_budgets, + ]; + updatedBudgets[index].nonstock = val as OptionType; + updatedBudgets[index].nonstock_id = + (val as OptionType) + ? (val as OptionType).value + : 0; + formik.setFieldValue( + 'project_budgets', + updatedBudgets + ); + formik.setFieldTouched( + `project_budgets[${index}].nonstock_id`, + true + ); + }} + errorMessage={ + ( + formik.errors.project_budgets?.[ + index + ] as FormikErrors + )?.nonstock_id as string + } + isError={ + formik.touched.project_budgets?.[index] + ?.nonstock_id && + Boolean( + ( + formik.errors.project_budgets?.[ + index + ] as FormikErrors + )?.nonstock_id as string + ) + } + /> + + + handleBudgetChange(index, 'qty', e.target.value) + } + onBlur={formik.handleBlur} + allowNegative={false} + endAdornment={ + isResponseSuccess(nonstocks) + ? (nonstocks.data.find( + (ns) => ns.id === budget.nonstock_id + )?.uom?.name ?? '') + : '' + } + errorMessage={ + ( + formik.errors.project_budgets?.[ + index + ] as FormikErrors + )?.qty as string + } + isError={ + formik.touched.project_budgets?.[index]?.qty && + Boolean( + ( + formik.errors.project_budgets?.[ + index + ] as FormikErrors + )?.qty as string + ) + } + /> + + + handleBudgetChange(index, 'price', e.target.value) + } + onBlur={formik.handleBlur} + placeholder='Masukkan harga satuan' + allowNegative={false} + startAdornment='Rp' + errorMessage={ + ( + formik.errors.project_budgets?.[ + index + ] as FormikErrors + )?.price as string + } + isError={ + formik.touched.project_budgets?.[index]?.price && + Boolean( + ( + formik.errors.project_budgets?.[ + index + ] as FormikErrors + )?.price as string + ) + } + /> + + + handleBudgetChange( + index, + 'total_price', + e.target.value + ) + } + onBlur={formik.handleBlur} + placeholder='Masukkan harga satuan' + allowNegative={false} + startAdornment='Rp' + errorMessage={ + ( + formik.errors.project_budgets?.[ + index + ] as FormikErrors + )?.total_price as string + } + isError={ + formik.touched.project_budgets?.[index] + ?.total_price && + Boolean( + ( + formik.errors.project_budgets?.[ + index + ] as FormikErrors + )?.total_price as string + ) + } + /> + + +
+ Tidak ada data estimasi anggaran. +
+ + +
+
{formType !== 'detail' && (
diff --git a/src/stores/ui/slices/drawer.slice.ts b/src/stores/ui/slices/production/project-flock.slice.ts similarity index 56% rename from src/stores/ui/slices/drawer.slice.ts rename to src/stores/ui/slices/production/project-flock.slice.ts index 1d3f54a2..4ecd1329 100644 --- a/src/stores/ui/slices/drawer.slice.ts +++ b/src/stores/ui/slices/production/project-flock.slice.ts @@ -1,11 +1,21 @@ import { StateCreator } from 'zustand'; -import { DrawerUiSlice } from '@/types/stores'; +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; -export const createFormDrawerUiSlice: StateCreator< - DrawerUiSlice, +export type ProjectFloockUISlice = { + triggerValidate: boolean; + toggleValidate: () => void; + subscribeValidate: (callback: () => void) => void; + isValid: boolean; + setIsValid: (v: boolean) => void; + subscribeIsValid: (callback: (isValid: boolean) => void) => () => void; +}; + +export const createProjectFlockUiSlice: StateCreator< + ProjectFloockUISlice, [], [], - DrawerUiSlice + ProjectFloockUISlice > = (set, get, api) => ({ // event flag untuk memicu formik validate triggerValidate: false, @@ -38,3 +48,14 @@ export const createFormDrawerUiSlice: StateCreator< }); }, }); + +export const useProjectFlockUiStore = create()( + devtools( + (...args) => ({ + ...createProjectFlockUiSlice(...args), + }), + { + name: 'ProjectFlockUiStore', + } + ) +); diff --git a/src/stores/ui/ui.store.ts b/src/stores/ui/ui.store.ts index 09d5d1a4..49554bc9 100644 --- a/src/stores/ui/ui.store.ts +++ b/src/stores/ui/ui.store.ts @@ -5,13 +5,11 @@ 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/api/production/project-flock.d.ts b/src/types/api/production/project-flock.d.ts index c5b0aaf8..845c5e77 100644 --- a/src/types/api/production/project-flock.d.ts +++ b/src/types/api/production/project-flock.d.ts @@ -4,6 +4,7 @@ import { Flock } from '@/types/api/master-data/flock'; import { Kandang } from '@/types/api/master-data/kandang'; import { Location } from '@/types/api/master-data/location'; import { BaseApproval, BaseMetadata } from '@/types/api/api-general'; +import { Nonstock } from '@/types/api/master-data/nonstock'; export type BaseProjectFlock = { id: number; @@ -30,6 +31,15 @@ export type PeriodFlock = { next_period: number; }; +export type ProjectFlockBudget = { + id?: number; + project_flock_id?: number; + nonstock_id: number; + nonstock?: Nonstock; + qty: number; + price: number; +}; + export type ProjectFlock = BaseMetadata & BaseProjectFlock; export type CreateProjectFlockPayload = { @@ -39,6 +49,7 @@ export type CreateProjectFlockPayload = { fcr_id: number; location_id: number; kandang_ids: number[]; + project_budgets?: ProjectFlockBudget[]; }; export type UpdateProjectFlockPayload = CreateProjectFlockPayload; diff --git a/src/types/stores.d.ts b/src/types/stores.d.ts index 23e2358c..1a3046ae 100644 --- a/src/types/stores.d.ts +++ b/src/types/stores.d.ts @@ -3,13 +3,4 @@ type MainUiSlice = { setMainDrawerOpen: (open: boolean) => void; }; -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; +export type UIStore = MainUiSlice;