From bf834cf79bbc1710994d20187db98184619bf965 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Mon, 12 Jan 2026 14:12:12 +0700 Subject: [PATCH 1/2] refactor(FE): Allow null for select fields and track id fields --- .../expense/form/ExpenseRequestForm.schema.ts | 96 ++++++++++++------- .../pages/expense/form/ExpenseRequestForm.tsx | 21 +++- .../ExpenseRequestKandangDetailExpense.tsx | 11 ++- 3 files changed, 90 insertions(+), 38 deletions(-) diff --git a/src/components/pages/expense/form/ExpenseRequestForm.schema.ts b/src/components/pages/expense/form/ExpenseRequestForm.schema.ts index 9eff1d26..cd34e5a3 100644 --- a/src/components/pages/expense/form/ExpenseRequestForm.schema.ts +++ b/src/components/pages/expense/form/ExpenseRequestForm.schema.ts @@ -7,18 +7,19 @@ type ExpenseFormSchemaType = { category?: { value: 'BOP' | 'NON-BOP'; label: 'BOP' | 'NON-BOP'; - }; + } | null; location?: { value: number; label: string; - }; + } | null; location_id: number; transaction_date?: string; kandangs?: { id?: number; name?: string }[]; supplier?: { value: number; label: string; - }; + } | null; + supplier_id: number; existing_documents?: { id: number; name: string; url: string }[]; deleted_documents?: number[]; documents?: File[]; @@ -28,7 +29,8 @@ type ExpenseFormSchemaType = { nonstock?: { value: number; label: string; - }; + } | null; + nonstock_id?: number; quantity?: number; price?: number; notes?: string; @@ -41,16 +43,24 @@ export const ExpenseRequestFormSchema: Yup.ObjectSchema = category: Yup.object({ value: Yup.string().oneOf(['BOP', 'NON-BOP']).required(), label: Yup.string().oneOf(['BOP', 'NON-BOP']).required(), - }).required('Kategori wajib diisi!'), + }) + .nullable() + .optional(), location: Yup.object({ value: Yup.number().min(1).required(), label: Yup.string().required(), - }).required('Lokasi wajib diisi!'), + }) + .nullable() + .optional(), - location_id: Yup.number().min(1).required('Lokasi wajib diisi!'), + location_id: Yup.number() + .required('Lokasi wajib diisi!') + .min(1, 'Lokasi wajib diisi!') + .typeError('Lokasi wajib diisi!'), transaction_date: Yup.string().required('Tanggal transaksi wajib diisi!'), + kandangs: Yup.array() .of( Yup.object({ @@ -63,28 +73,28 @@ export const ExpenseRequestFormSchema: Yup.ObjectSchema = supplier: Yup.object({ value: Yup.number().min(1).required(), label: Yup.string().required(), - }).required('Vendor wajib diisi!'), + }) + .nullable() + .optional(), - existing_documents: Yup.array().of( - Yup.object({ - id: Yup.number().required(), - name: Yup.string().required(), - url: Yup.string().required(), - }) - ), + supplier_id: Yup.number() + .required('Vendor wajib diisi!') + .min(1, 'Vendor wajib diisi!') + .typeError('Vendor wajib diisi!'), + + existing_documents: Yup.array() + .of( + Yup.object({ + id: Yup.number().required(), + name: Yup.string().required(), + url: Yup.string().required(), + }) + ) + .optional(), deleted_documents: Yup.array().of(Yup.number().required()).optional(), - documents: Yup.array() - .of( - Yup.mixed() - .required() - .test('fileSize', 'Ukuran dokumen maksimal 5 MB', (value) => { - if (!value || !(value instanceof File)) return true; - return value.size <= 5 * 1024 * 1024; - }) - ) - .optional(), + documents: Yup.array().of(Yup.mixed().required()).optional(), expense_nonstocks: Yup.array() .of( @@ -96,9 +106,17 @@ export const ExpenseRequestFormSchema: Yup.ObjectSchema = nonstock: Yup.object({ value: Yup.number().min(1).required(), label: Yup.string().required(), - }).required('Nonstock wajib diisi!'), - quantity: Yup.number().required('Total kuantitas wajib diisi!'), - price: Yup.number().required('Harga satuan wajib diisi!'), + }).nullable(), + nonstock_id: Yup.number() + .required('Nonstock wajib diisi!') + .min(1, 'Nonstock wajib diisi!') + .typeError('Nonstock wajib diisi!'), + quantity: Yup.number() + .required('Total kuantitas wajib diisi!') + .typeError('Total kuantitas wajib diisi!'), + price: Yup.number() + .required('Harga satuan wajib diisi!') + .typeError('Harga satuan wajib diisi!'), notes: Yup.string(), }) ) @@ -142,13 +160,13 @@ export const getExpenseFormInitialValues = ( value: initialValues.category, label: initialValues.category, } - : undefined, + : null, location: initialValues?.location ? { value: initialValues.location.id, label: initialValues.location.name, } - : undefined, + : null, location_id: Number(initialValues?.location.id || 0), transaction_date: initialValues?.transaction_date ? formatDate(initialValues.transaction_date, 'YYYY-MM-DD') @@ -162,7 +180,8 @@ export const getExpenseFormInitialValues = ( value: initialValues.supplier.id, label: initialValues.supplier.name, } - : undefined, + : null, + supplier_id: initialValues?.supplier?.id ?? 0, existing_documents: initialValues?.documents?.map((doc) => { const path = doc.path.startsWith('/') ? doc.path.slice(1) : doc.path; return { @@ -182,12 +201,25 @@ export const getExpenseFormInitialValues = ( value: expenseItem.nonstock.id, label: expenseItem.nonstock.name, }, + nonstock_id: expenseItem.nonstock.id, quantity: expenseItem.qty, price: expenseItem.price, notes: expenseItem.note, })) : [], })) - : [], + : [ + { + cost_items: [ + { + nonstock: null, + nonstock_id: 0, + quantity: undefined, + price: undefined, + notes: '', + }, + ], + }, + ], }; }; diff --git a/src/components/pages/expense/form/ExpenseRequestForm.tsx b/src/components/pages/expense/form/ExpenseRequestForm.tsx index ebd1b066..a41290b8 100644 --- a/src/components/pages/expense/form/ExpenseRequestForm.tsx +++ b/src/components/pages/expense/form/ExpenseRequestForm.tsx @@ -204,7 +204,8 @@ const ExpenseRequestForm = ({ { cost_items: [ { - nonstock: undefined, + nonstock: null, + nonstock_id: 0, quantity: undefined, price: undefined, notes: '', @@ -226,7 +227,8 @@ const ExpenseRequestForm = ({ { cost_items: [ { - nonstock: undefined, + nonstock: null, + nonstock_id: 0, quantity: undefined, price: undefined, notes: '', @@ -251,7 +253,8 @@ const ExpenseRequestForm = ({ kandang_id: kandangItem.id, cost_items: existingExpenseNonstock?.cost_items || [ { - nonstock: undefined, + nonstock: null, + nonstock_id: 0, quantity: undefined, price: undefined, notes: '', @@ -266,10 +269,20 @@ const ExpenseRequestForm = ({ const supplierChangeHandler = (val: OptionType | OptionType[] | null) => { formik.setFieldTouched('supplier', true); formik.setFieldValue('supplier', val); + + const supplierId = Array.isArray(val) ? val[0]?.value : val?.value; + formik.setFieldValue('supplier_id', supplierId ?? 0); }; const requestDocumentsChangeHandler = (val: File[]) => { formik.setFieldTouched('documents', true); + + const invalidFiles = val.filter((file) => file.size > 5 * 1024 * 1024); + if (invalidFiles.length > 0) { + toast.error('Ukuran dokumen maksimal 5 MB!'); + return; + } + formik.setFieldValue('documents', val); }; @@ -585,7 +598,7 @@ const ExpenseRequestForm = ({ type='submit' color='primary' isLoading={formik.isSubmitting} - disabled={!formik.isValid || formik.isSubmitting} + disabled={formik.isSubmitting} className='px-4' > Submit diff --git a/src/components/pages/expense/form/ExpenseRequestKandangDetailExpense.tsx b/src/components/pages/expense/form/ExpenseRequestKandangDetailExpense.tsx index e219870e..41eb40f8 100644 --- a/src/components/pages/expense/form/ExpenseRequestKandangDetailExpense.tsx +++ b/src/components/pages/expense/form/ExpenseRequestKandangDetailExpense.tsx @@ -25,7 +25,7 @@ interface ExpenseRequestKandangDetailExpenseProps { location?: { value: number; label: string; - }; + } | null; className?: { wrapper?: string; }; @@ -59,13 +59,20 @@ const ExpenseRequestKandangDetailExpense: React.FC< `expense_nonstocks[${kandangExpenseIdx}].cost_items[${expenseIdx}].nonstock`, val ); + + const nonstockId = Array.isArray(val) ? val[0]?.value : val?.value; + formik.setFieldValue( + `expense_nonstocks[${kandangExpenseIdx}].cost_items[${expenseIdx}].nonstock_id`, + nonstockId ?? 0 + ); }; const addExpenseItemHandler = (kandangExpenseIdx: number) => { const newExpensesValue = [ ...formik.values.expense_nonstocks[kandangExpenseIdx].cost_items, { - nonstock: undefined, + nonstock: null, + nonstock_id: 0, price: undefined, quantity: undefined, notes: '', From 90eef08f9b66ff71bf21c19395f1f36ba698e7dd Mon Sep 17 00:00:00 2001 From: rstubryan Date: Mon, 12 Jan 2026 14:30:34 +0700 Subject: [PATCH 2/2] refactor(FE): Enable sales tab and fetch sales data --- src/app/closing/detail/page.tsx | 10 ++-- .../pages/closing/ClosingDetail.tsx | 10 ++-- .../pages/closing/sale/SalesReportTable.tsx | 48 +++++++++---------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/app/closing/detail/page.tsx b/src/app/closing/detail/page.tsx index f3a78d9d..e630a279 100644 --- a/src/app/closing/detail/page.tsx +++ b/src/app/closing/detail/page.tsx @@ -19,10 +19,10 @@ const ClosingDetailPage = () => { (id: number) => ClosingApi.getGeneralInfo(id) ); - // const { data: salesData, isLoading: isLoadingSales } = useSWR( - // closingId ? `sales-${closingId}` : null, - // () => ClosingApi.getPenjualan(Number(closingId)) - // ); + const { data: salesData, isLoading: isLoadingSales } = useSWR( + closingId ? `sales-${closingId}` : null, + () => ClosingApi.getPenjualan(Number(closingId)) + ); const { data: hppEkspedisiData, isLoading: isLoadingHppEkspedisi } = useSWR( closingId ? `hpp-ekspedisi-${closingId}` : null, @@ -55,7 +55,7 @@ const ClosingDetailPage = () => { = ({ /> ), }, - // { - // id: 'penjualan', - // label: 'Penjualan', - // content: , - // }, + { + id: 'penjualan', + label: 'Penjualan', + content: , + }, { id: 'overhead', label: 'Overhead', diff --git a/src/components/pages/closing/sale/SalesReportTable.tsx b/src/components/pages/closing/sale/SalesReportTable.tsx index 89cb6615..fe8d46a5 100644 --- a/src/components/pages/closing/sale/SalesReportTable.tsx +++ b/src/components/pages/closing/sale/SalesReportTable.tsx @@ -215,31 +215,31 @@ const SalesReportTable = ({ return kandang?.name || '-'; }, }, - { - id: 'payment_status', - accessorKey: 'payment_status', - header: 'Status Pembayaran', - cell: (props) => { - const status = props.getValue() as string; - const getStatusColor = (status: string) => { - if (!status) return 'neutral'; - switch (status.toLowerCase()) { - case 'paid': - return 'success'; - case 'tempo': - return 'warning'; - default: - return 'neutral'; - } - }; + // { + // id: 'payment_status', + // accessorKey: 'payment_status', + // header: 'Status Pembayaran', + // cell: (props) => { + // const status = props.getValue() as string; + // const getStatusColor = (status: string) => { + // if (!status) return 'neutral'; + // switch (status.toLowerCase()) { + // case 'paid': + // return 'success'; + // case 'tempo': + // return 'warning'; + // default: + // return 'neutral'; + // } + // }; - return ( - - {status || '-'} - - ); - }, - }, + // return ( + // + // {status || '-'} + // + // ); + // }, + // }, ], [] );