From a4378ebd04e3284383b805fc2fcd46600902b6dd Mon Sep 17 00:00:00 2001 From: rstubryan Date: Fri, 27 Feb 2026 14:57:45 +0700 Subject: [PATCH] refactor(FE): Refactor product flags to support single flag and sub-flags --- .../master-data/product/ProductTable.tsx | 14 ++-- .../product/form/ProductForm.schema.ts | 13 ++-- .../master-data/product/form/ProductForm.tsx | 70 +++++++++++++++---- src/types/api/master-data/product.d.ts | 8 ++- 4 files changed, 81 insertions(+), 24 deletions(-) diff --git a/src/components/pages/master-data/product/ProductTable.tsx b/src/components/pages/master-data/product/ProductTable.tsx index 74137a14..9d0dee22 100644 --- a/src/components/pages/master-data/product/ProductTable.tsx +++ b/src/components/pages/master-data/product/ProductTable.tsx @@ -180,11 +180,17 @@ const ProductsTable = () => { props.row.original.suppliers?.map((s) => s.name).join(', ') || '-', }, { - accessorKey: 'flags', - header: 'Flags', + accessorKey: 'flag', + header: 'Flag', cell: (props) => - props.row.original.flags?.length - ? props.row.original.flags.join(', ') + props.row.original.flag ? props.row.original.flag : '-', + }, + { + accessorKey: 'subs_flags', + header: 'Sub Flags', + cell: (props) => + props.row.original.sub_flags?.length + ? props.row.original.sub_flags.join(', ') : '-', }, { diff --git a/src/components/pages/master-data/product/form/ProductForm.schema.ts b/src/components/pages/master-data/product/form/ProductForm.schema.ts index 8a1d3de2..0f2a6956 100644 --- a/src/components/pages/master-data/product/form/ProductForm.schema.ts +++ b/src/components/pages/master-data/product/form/ProductForm.schema.ts @@ -25,7 +25,8 @@ type ProductFormSchemaType = { } | null; price: number; }[]; - flags: string[]; + flag: string; + sub_flags?: string[]; }; export const ProductFormSchema: Yup.ObjectSchema = @@ -94,10 +95,12 @@ export const ProductFormSchema: Yup.ObjectSchema = ) .required('Supplier wajib diisi!'), - flags: Yup.array() - .of(Yup.string().required()) - .min(1, 'Minimal harus ada 1 flag!') - .required('Flag wajib diisi!'), + flag: Yup.string() + .min(1, 'Flag wajib diisi!') + .required('Flag wajib diisi!') + .typeError('Flag wajib diisi!'), + + sub_flags: Yup.array().of(Yup.string().required()), }); export const UpdateProductFormSchema = ProductFormSchema; diff --git a/src/components/pages/master-data/product/form/ProductForm.tsx b/src/components/pages/master-data/product/form/ProductForm.tsx index 65329464..0a2887be 100644 --- a/src/components/pages/master-data/product/form/ProductForm.tsx +++ b/src/components/pages/master-data/product/form/ProductForm.tsx @@ -36,8 +36,10 @@ import { ProductApi, } from '@/services/api/master-data'; import { cn } from '@/lib/helper'; -import { PRODUCT_FLAG_OPTIONS } from '@/config/constant'; + import { useFormikErrorList } from '@/services/hooks/useFormikErrorList'; +import { PRODUCT_FLAG_MAPPING } from '@/config/constant'; + import { Supplier } from '@/types/api/master-data/supplier'; import Card from '@/components/Card'; import { removeArrayItemAndSync } from '@/lib/utils/formik'; @@ -110,7 +112,8 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => { price: supplier.price, })) : [], - flags: initialValues?.flags ?? [], + flag: initialValues?.flag ?? '', + sub_flags: initialValues?.sub_flags ?? [], }), [initialValues] ); @@ -139,7 +142,8 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => { supplier_id: s.supplier?.value as number, price: parseInt(s.price.toString()) || 0, })), - flags: values.flags.filter((f): f is string => typeof f === 'string'), + flag: values.flag, + sub_flags: values.sub_flags, }; switch (type) { case 'add': @@ -200,6 +204,28 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => { }); }, [supplierOptions, formik.values.suppliers]); + const selectedFlagMapping = useMemo(() => { + return PRODUCT_FLAG_MAPPING.options.find( + (opt) => opt.flag.value === formik.values.flag + ); + }, [formik.values.flag]); + + const subFlagOptions = useMemo(() => { + return (selectedFlagMapping?.sub_flags as unknown as OptionType[]) ?? []; + }, [selectedFlagMapping]); + + const selectedSubFlagValues = useMemo(() => { + return ( + (selectedFlagMapping?.sub_flags.filter((subFlag) => + formik.values.sub_flags?.includes(subFlag.value) + ) as unknown as OptionType[]) ?? [] + ); + }, [selectedFlagMapping, formik.values.sub_flags]); + + const isSubFlagRequired = useMemo(() => { + return selectedFlagMapping?.allow_without_sub_flag === false; + }, [selectedFlagMapping]); + const addSupplierHandler = () => { formik.setFieldValue('suppliers', [ ...formik.values.suppliers, @@ -213,7 +239,6 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => { const deleteSupplierItemHandler = (idx: number) => { const path = 'suppliers'; - // trims values, errors, and touched at idx removeArrayItemAndSync(formik, path, idx); }; @@ -428,26 +453,45 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => { readOnly={type === 'detail'} /> -
+
opt.value === formik.values.flag)} + onChange={(val) => { + const selectedFlag = (val as OptionType)?.value ?? ''; + formik.setFieldValue('flag', selectedFlag); + formik.setFieldValue('sub_flags', []); + }} + options={PRODUCT_FLAG_MAPPING.flags as unknown as OptionType[]} + isError={formik.touched.flag && Boolean(formik.errors.flag)} + errorMessage={formik.errors.flag as string} + isDisabled={type === 'detail'} + isClearable + /> + + - (formik.values.flags || []).includes(opt.value) - )} + required={isSubFlagRequired} + value={selectedSubFlagValues} onChange={(val) => { const arr = Array.isArray(val) ? val : val ? [val] : []; formik.setFieldValue( - 'flags', + 'sub_flags', arr.map((v) => (v as OptionType).value) ); }} - options={PRODUCT_FLAG_OPTIONS} - isError={formik.touched.flags && Boolean(formik.errors.flags)} - errorMessage={formik.errors.flags as string} - isDisabled={type === 'detail'} + options={subFlagOptions} + isError={ + formik.touched.sub_flags && Boolean(formik.errors.sub_flags) + } + errorMessage={formik.errors.sub_flags as string} + isDisabled={type === 'detail' || !formik.values.flag} isClearable />
diff --git a/src/types/api/master-data/product.d.ts b/src/types/api/master-data/product.d.ts index c1b9b4b6..99877762 100644 --- a/src/types/api/master-data/product.d.ts +++ b/src/types/api/master-data/product.d.ts @@ -15,7 +15,10 @@ export type BaseProduct = { uom: Uom; product_category: ProductCategory; suppliers: (BaseSupplier & { price: number })[]; - flags: string[]; + flag: string; + sub_flag?: string; + sub_flags?: string[]; + flags?: string[]; }; export type Product = BaseMetadata & BaseProduct; @@ -34,7 +37,8 @@ export type CreateProductPayload = { supplier_id: number; price: number; }[]; - flags: string[]; + flag: string; + sub_flags?: string[]; }; export type UpdateProductPayload = CreateProductPayload;