From 0bdf27de2caa980aca9640864fcfdd999d56c2c9 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 29 Jan 2026 09:47:30 +0700 Subject: [PATCH 1/6] refactor(FE): Make delivery fields optional in movement types --- src/types/api/inventory/movement.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/types/api/inventory/movement.d.ts b/src/types/api/inventory/movement.d.ts index 10a62e5b..e84dead9 100644 --- a/src/types/api/inventory/movement.d.ts +++ b/src/types/api/inventory/movement.d.ts @@ -68,11 +68,11 @@ export type CreateMovementPayloadData = { product_qty: number; }[]; deliveries: { - delivery_cost: number; - delivery_cost_per_item: number; + delivery_cost?: number; + delivery_cost_per_item?: number; document_index?: number; - driver_name: string; - vehicle_plate: string; + driver_name?: string; + vehicle_plate?: string; supplier_id?: number | null; products: { product_id: number; From 737d8e943c5f4c74cbd0eba687f5ad0feca8dec2 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 29 Jan 2026 09:48:43 +0700 Subject: [PATCH 2/6] refactor(FE): Make delivery fields nullable and optional --- .../movement/form/MovementForm.schema.ts | 58 +++++------ .../inventory/movement/form/MovementForm.tsx | 98 +++++++++---------- 2 files changed, 72 insertions(+), 84 deletions(-) diff --git a/src/components/pages/inventory/movement/form/MovementForm.schema.ts b/src/components/pages/inventory/movement/form/MovementForm.schema.ts index 18161974..c448754a 100644 --- a/src/components/pages/inventory/movement/form/MovementForm.schema.ts +++ b/src/components/pages/inventory/movement/form/MovementForm.schema.ts @@ -27,12 +27,12 @@ type MovementFormSchemaType = { product_qty: number | string; }[]; deliveries: { - delivery_cost?: number | string; - delivery_cost_per_item?: number | string; + delivery_cost?: number | string | null; + delivery_cost_per_item?: number | string | null; document?: File | MovementDocument | null; document_path?: string | null; - driver_name: string; - vehicle_plate: string; + driver_name?: string | null; + vehicle_plate?: string | null; supplier?: { value: number; label: string; @@ -59,12 +59,12 @@ export type ProductSchema = { }; export type DeliverySchema = { - delivery_cost?: number | string; - delivery_cost_per_item?: number | string; + delivery_cost?: number | string | null; + delivery_cost_per_item?: number | string | null; document?: File | MovementDocument | null; document_path?: string | null; - driver_name: string; - vehicle_plate: string; + driver_name?: string | null; + vehicle_plate?: string | null; supplier?: { value: number; label: string; @@ -120,32 +120,26 @@ const DeliveryDocumentSchema = Yup.mixed() const DeliveryObjectSchema: Yup.ObjectSchema = Yup.object({ delivery_cost: Yup.number() - .transform((value) => (isNaN(value) || value === 0 ? undefined : value)) + .transform((value) => + isNaN(value) || value === '' || value === null ? undefined : value + ) + .optional() + .nullable() .min(1, 'Biaya minimal 1!') - .typeError('Biaya harus berupa angka!') - .test('one-of-cost-fields', 'Wajib diisi salah satu!', function (value) { - const { delivery_cost_per_item } = this.parent; - return ( - (value !== undefined && value > 0) || - (delivery_cost_per_item !== undefined && delivery_cost_per_item > 0) - ); - }), + .typeError('Biaya harus berupa angka!'), delivery_cost_per_item: Yup.number() - .transform((value) => (isNaN(value) || value === 0 ? undefined : value)) + .transform((value) => + isNaN(value) || value === '' || value === null ? undefined : value + ) + .optional() + .nullable() .min(1, 'Biaya per item minimal 1!') - .typeError('Biaya per item harus berupa angka!') - .test('one-of-cost-fields', 'Wajib diisi salah satu!', function (value) { - const { delivery_cost } = this.parent; - return ( - (value !== undefined && value > 0) || - (delivery_cost !== undefined && delivery_cost > 0) - ); - }), + .typeError('Biaya per item harus berupa angka!'), document_path: Yup.string().nullable().optional(), document_index: Yup.number().optional(), document: DeliveryDocumentSchema, - driver_name: Yup.string().required('Nama sopir wajib diisi!'), - vehicle_plate: Yup.string().required('Plat nomor wajib diisi!'), + driver_name: Yup.string().optional().nullable(), + vehicle_plate: Yup.string().optional().nullable(), supplier: Yup.object({ value: Yup.number().min(1).required(), label: Yup.string().required(), @@ -279,12 +273,12 @@ export const getMovementFormInitialValues = ( }) ?? [], })) ?? [ { - delivery_cost: undefined, - delivery_cost_per_item: undefined, + delivery_cost: null, + delivery_cost_per_item: null, document: null, document_path: null, - driver_name: '', - vehicle_plate: '', + driver_name: null, + vehicle_plate: null, supplier: null, supplier_id: 0, products: [ diff --git a/src/components/pages/inventory/movement/form/MovementForm.tsx b/src/components/pages/inventory/movement/form/MovementForm.tsx index ef4bc3bb..a5a6cbf9 100644 --- a/src/components/pages/inventory/movement/form/MovementForm.tsx +++ b/src/components/pages/inventory/movement/form/MovementForm.tsx @@ -228,19 +228,49 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { } } - return { - delivery_cost: parseInt((d.delivery_cost || '').toString()) || 0, - delivery_cost_per_item: - parseInt((d.delivery_cost_per_item || '').toString()) || 0, - document_index: documentIndex, - driver_name: d.driver_name, - vehicle_plate: d.vehicle_plate, - supplier_id: d.supplier_id, + const deliveryObj: { + products: Array<{ product_id: number; product_qty: number }>; + delivery_cost?: number; + delivery_cost_per_item?: number; + document_index?: number; + driver_name?: string; + vehicle_plate?: string; + supplier_id?: number; + } = { products: d.products.map((p) => ({ product_id: p.product_id, product_qty: parseInt(p.product_qty.toString()) || 0, })), }; + + const deliveryCost = parseInt((d.delivery_cost || '').toString()) || 0; + if (deliveryCost > 0) { + deliveryObj.delivery_cost = deliveryCost; + } + + const deliveryCostPerItem = + parseInt((d.delivery_cost_per_item || '').toString()) || 0; + if (deliveryCostPerItem > 0) { + deliveryObj.delivery_cost_per_item = deliveryCostPerItem; + } + + if (documentIndex >= 0) { + deliveryObj.document_index = documentIndex; + } + + if (d.driver_name) { + deliveryObj.driver_name = d.driver_name; + } + + if (d.vehicle_plate) { + deliveryObj.vehicle_plate = d.vehicle_plate; + } + + if (d.supplier_id) { + deliveryObj.supplier_id = d.supplier_id; + } + + return deliveryObj; }); const payload: CreateMovementPayload = { @@ -1647,43 +1677,11 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { Supplier - - Plat Nomor - - * - - + Plat Nomor Dokumen - - Biaya Pengiriman (Rp.) - - * - - - - Biaya Per Item (Rp.) - - * - - - - Nama Sopir - - * - - + Biaya Pengiriman (Rp.) + Biaya Per Item (Rp.) + Nama Sopir {type !== 'detail' && Aksi} @@ -1780,10 +1778,9 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { { { { Date: Thu, 29 Jan 2026 10:27:47 +0700 Subject: [PATCH 3/6] refactor(FE): Support inputPrefix/inputSuffix on SelectInput --- src/components/input/SelectInput.tsx | 363 +++++++++++++----- .../inventory/movement/form/MovementForm.tsx | 27 +- 2 files changed, 265 insertions(+), 125 deletions(-) diff --git a/src/components/input/SelectInput.tsx b/src/components/input/SelectInput.tsx index 419ed314..2a3ff98c 100644 --- a/src/components/input/SelectInput.tsx +++ b/src/components/input/SelectInput.tsx @@ -54,6 +54,9 @@ interface SelectInputBaseProps { wrapper?: string; label?: string; select?: string; + inputPrefix?: string; + inputSuffix?: string; + inputPrefixSuffixWrapper?: string; }; isError?: boolean; errorMessage?: string; @@ -62,6 +65,8 @@ interface SelectInputBaseProps { delay?: number; onInputChange?: (search: string) => void; startAdornment?: ReactNode; + inputPrefix?: ReactNode; + inputSuffix?: ReactNode; menuPortalTarget?: HTMLElement | null; closeMenuOnSelect?: boolean; hideSelectedOptions?: boolean; @@ -153,6 +158,8 @@ const SelectInput = (props: SelectInputProps) => { createables = false, onInputChange, startAdornment, + inputPrefix, + inputSuffix, menuPortalTarget, closeMenuOnSelect, hideSelectedOptions, @@ -227,111 +234,261 @@ const SelectInput = (props: SelectInputProps) => { )} - > - instanceId='select' - value={value ?? (isMulti ? [] : null)} - onChange={onChange ? handleChange : undefined} - options={options} - menuIsOpen={openMenu} - inputValue={internalInputValue} - onInputChange={internalInputChangeHandler} - onMenuClose={() => setInternalInputValue('')} - isMulti={isMulti} - isDisabled={isDisabled || readOnly} - isLoading={isLoading} - isClearable={isClearable} - isRtl={isRtl} - isSearchable={isSearchable} - placeholder={placeholder} - closeMenuOnSelect={closeMenuOnSelect} - hideSelectedOptions={hideSelectedOptions} - className={cn('w-full', className?.select)} - classNames={{ - ...(!startAdornment && { - control: ({ isFocused, isDisabled }) => - cn('w-full rounded-lg! border bg-white transition-shadow', { - 'cursor-pointer!': !readOnly && !isDisabled, - 'border-red-500! ring-2 ring-red-200': isError, - 'border-indigo-500 ring-2 ring-indigo-200': isFocused, - 'border-base-content/10!': !isError && !isFocused, - 'bg-gray-100 text-gray-400 cursor-not-allowed': - isDisabled && !readOnly, - 'bg-transparent! cursor-not-allowed!': readOnly, + {inputPrefix || inputSuffix ? ( +
+ {inputPrefix && ( +
+ {inputPrefix} +
+ )} + + > + instanceId='select' + value={value ?? (isMulti ? [] : null)} + onChange={onChange ? handleChange : undefined} + options={options} + menuIsOpen={openMenu} + inputValue={internalInputValue} + onInputChange={internalInputChangeHandler} + onMenuClose={() => setInternalInputValue('')} + isMulti={isMulti} + isDisabled={isDisabled || readOnly} + isLoading={isLoading} + isClearable={isClearable} + isRtl={isRtl} + isSearchable={isSearchable} + placeholder={placeholder} + closeMenuOnSelect={closeMenuOnSelect} + hideSelectedOptions={hideSelectedOptions} + className={cn('w-full flex-1', className?.select)} + classNames={{ + ...(!startAdornment && { + control: ({ isFocused, isDisabled }) => + cn('w-full rounded-lg! border bg-white transition-shadow', { + 'cursor-pointer!': !readOnly && !isDisabled, + 'border-red-500! ring-2 ring-red-200': isError, + 'border-indigo-500 ring-2 ring-indigo-200': isFocused, + 'border-base-content/10!': !isError && !isFocused, + 'bg-gray-100 text-gray-400 cursor-not-allowed': + isDisabled && !readOnly, + 'bg-transparent! cursor-not-allowed!': readOnly, + 'rounded-l-none!': inputPrefix, + 'rounded-r-none!': inputSuffix, + }), + valueContainer: () => cn('flex-1 px-3! pr-2! py-2.5! gap-1'), }), - valueContainer: () => cn('flex-1 px-3! pr-2! py-2.5! gap-1'), - }), - placeholder: () => - cn({ - 'text-gray-400 text-sm leading-tight': !isError, - 'text-red-300!': isError, + placeholder: () => + cn({ + 'text-gray-400 text-sm leading-tight': !isError, + 'text-red-300!': isError, + }), + singleValue: () => + cn({ + 'm-0! text-gray-900 text-sm leading-tight': !isError, + 'text-error!': isError, + 'text-gray-900!': readOnly, + }), + input: () => cn('text-gray-900 m-0! p-0! text-sm leading-tight'), + indicatorsContainer: () => + cn('flex items-center gap-1 pr-3 py-2'), + dropdownIndicator: ({ isFocused }) => + cn('p-0! rounded hover:bg-gray-100', { + 'text-gray-900': isFocused, + 'text-gray-500': !isFocused, + 'text-error!': isError, + }), + clearIndicator: () => cn('p-0! rounded hover:bg-gray-100'), + menu: () => + cn( + 'border border-base-content/5 rounded-xl! bg-base-100 shadow-lg! my-1.5!' + ), + menuList: () => cn('p-0! max-h-60 overflow-auto'), + option: ({ isFocused, isSelected }) => + cn('px-3 py-2 rounded-md cursor-pointer!', { + 'bg-indigo-600 text-white': isFocused, + 'bg-blue-500!': isSelected, + 'text-gray-700': !isFocused && !isSelected, + }), + multiValue: ({ getValue, index }) => { + const selectedValues = getValue() as T[]; + return cn( + 'bg-base-200! rounded-lg! py-[3px] px-2.5 m-0! flex items-center gap-1! w-fit gap-2!', + selectedValues[index]?.className + ); + }, + multiValueRemove: () => cn('p-0! w-3 h-3'), + multiValueLabel: ({ getValue, index }) => { + const selectedValues = getValue() as T[]; + return cn( + 'p-0! text-base-content! text-xs!', + selectedValues[index]?.labelClassName + ); + }, + }} + components={{ + ...components, + ...(optionComponent ? { Option: optionComponent } : {}), + MenuList: CustomMenuList, + }} + {...(startAdornment && { + shouldShowAdornment, + startAdornment, + })} + menuPortalTarget={ + typeof document !== 'undefined' + ? (menuPortalTarget ?? document.body) + : undefined + } + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + multiValue(base) { + return { + ...base, + borderRadius: '8px', + }; + }, + }} + onMenuScrollToBottom={onMenuScrollToBottom} + /> + + {inputSuffix && ( +
+ {inputSuffix} +
+ )} +
+ ) : ( + > + instanceId='select' + value={value ?? (isMulti ? [] : null)} + onChange={onChange ? handleChange : undefined} + options={options} + menuIsOpen={openMenu} + inputValue={internalInputValue} + onInputChange={internalInputChangeHandler} + onMenuClose={() => setInternalInputValue('')} + isMulti={isMulti} + isDisabled={isDisabled || readOnly} + isLoading={isLoading} + isClearable={isClearable} + isRtl={isRtl} + isSearchable={isSearchable} + placeholder={placeholder} + closeMenuOnSelect={closeMenuOnSelect} + hideSelectedOptions={hideSelectedOptions} + className={cn('w-full', className?.select)} + classNames={{ + ...(!startAdornment && { + control: ({ isFocused, isDisabled }) => + cn('w-full rounded-lg! border bg-white transition-shadow', { + 'cursor-pointer!': !readOnly && !isDisabled, + 'border-red-500! ring-2 ring-red-200': isError, + 'border-indigo-500 ring-2 ring-indigo-200': isFocused, + 'border-base-content/10!': !isError && !isFocused, + 'bg-gray-100 text-gray-400 cursor-not-allowed': + isDisabled && !readOnly, + 'bg-transparent! cursor-not-allowed!': readOnly, + }), + valueContainer: () => cn('flex-1 px-3! pr-2! py-2.5! gap-1'), }), - singleValue: () => - cn({ - 'm-0! text-gray-900 text-sm leading-tight': !isError, - 'text-error!': isError, - 'text-gray-900!': readOnly, - }), - input: () => cn('text-gray-900 m-0! p-0! text-sm leading-tight'), - indicatorsContainer: () => cn('flex items-center gap-1 pr-3 py-2'), - dropdownIndicator: ({ isFocused }) => - cn('p-0! rounded hover:bg-gray-100', { - 'text-gray-900': isFocused, - 'text-gray-500': !isFocused, - 'text-error!': isError, - }), - clearIndicator: () => cn('p-0! rounded hover:bg-gray-100'), - menu: () => - cn( - 'border border-base-content/5 rounded-xl! bg-base-100 shadow-lg! my-1.5!' - ), - menuList: () => cn('p-0! max-h-60 overflow-auto'), - option: ({ isFocused, isSelected }) => - cn('px-3 py-2 rounded-md cursor-pointer!', { - 'bg-indigo-600 text-white': isFocused, - 'bg-blue-500!': isSelected, - 'text-gray-700': !isFocused && !isSelected, - }), - multiValue: ({ getValue, index }) => { - const selectedValues = getValue() as T[]; - return cn( - 'bg-base-200! rounded-lg! py-[3px] px-2.5 m-0! flex items-center gap-1! w-fit gap-2!', - selectedValues[index]?.className - ); - }, - multiValueRemove: () => cn('p-0! w-3 h-3'), - multiValueLabel: ({ getValue, index }) => { - const selectedValues = getValue() as T[]; - return cn( - 'p-0! text-base-content! text-xs!', - selectedValues[index]?.labelClassName - ); - }, - }} - components={{ - ...components, - ...(optionComponent ? { Option: optionComponent } : {}), - MenuList: CustomMenuList, - }} - {...(startAdornment && { - shouldShowAdornment, - startAdornment, - })} - menuPortalTarget={ - typeof document !== 'undefined' - ? (menuPortalTarget ?? document.body) - : undefined - } - styles={{ - menuPortal: (base) => ({ ...base, zIndex: 9999 }), - multiValue(base) { - return { - ...base, - borderRadius: '8px', - }; - }, - }} - onMenuScrollToBottom={onMenuScrollToBottom} - /> + placeholder: () => + cn({ + 'text-gray-400 text-sm leading-tight': !isError, + 'text-red-300!': isError, + }), + singleValue: () => + cn({ + 'm-0! text-gray-900 text-sm leading-tight': !isError, + 'text-error!': isError, + 'text-gray-900!': readOnly, + }), + input: () => cn('text-gray-900 m-0! p-0! text-sm leading-tight'), + indicatorsContainer: () => cn('flex items-center gap-1 pr-3 py-2'), + dropdownIndicator: ({ isFocused }) => + cn('p-0! rounded hover:bg-gray-100', { + 'text-gray-900': isFocused, + 'text-gray-500': !isFocused, + 'text-error!': isError, + }), + clearIndicator: () => cn('p-0! rounded hover:bg-gray-100'), + menu: () => + cn( + 'border border-base-content/5 rounded-xl! bg-base-100 shadow-lg! my-1.5!' + ), + menuList: () => cn('p-0! max-h-60 overflow-auto'), + option: ({ isFocused, isSelected }) => + cn('px-3 py-2 rounded-md cursor-pointer!', { + 'bg-indigo-600 text-white': isFocused, + 'bg-blue-500!': isSelected, + 'text-gray-700': !isFocused && !isSelected, + }), + multiValue: ({ getValue, index }) => { + const selectedValues = getValue() as T[]; + return cn( + 'bg-base-200! rounded-lg! py-[3px] px-2.5 m-0! flex items-center gap-1! w-fit gap-2!', + selectedValues[index]?.className + ); + }, + multiValueRemove: () => cn('p-0! w-3 h-3'), + multiValueLabel: ({ getValue, index }) => { + const selectedValues = getValue() as T[]; + return cn( + 'p-0! text-base-content! text-xs!', + selectedValues[index]?.labelClassName + ); + }, + }} + components={{ + ...components, + ...(optionComponent ? { Option: optionComponent } : {}), + MenuList: CustomMenuList, + }} + {...(startAdornment && { + shouldShowAdornment, + startAdornment, + })} + menuPortalTarget={ + typeof document !== 'undefined' + ? (menuPortalTarget ?? document.body) + : undefined + } + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + multiValue(base) { + return { + ...base, + borderRadius: '8px', + }; + }, + }} + onMenuScrollToBottom={onMenuScrollToBottom} + /> + )} {isError &&

{errorMessage}

} {!isError && bottomLabel && ( diff --git a/src/components/pages/inventory/movement/form/MovementForm.tsx b/src/components/pages/inventory/movement/form/MovementForm.tsx index a5a6cbf9..05d5b052 100644 --- a/src/components/pages/inventory/movement/form/MovementForm.tsx +++ b/src/components/pages/inventory/movement/form/MovementForm.tsx @@ -874,32 +874,15 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { (warehouseId: number) => { const stockInfo = warehouseStockMap.get(warehouseId); if (!stockInfo) { - return ( - - Kosong - - ); + return Kosong; } const { productCount } = stockInfo; - let color: 'neutral' | 'success' | 'warning' = 'neutral'; - if (productCount === 0) color = 'warning'; - else if (productCount > 0) color = 'success'; return ( - + Tersedia {productCount} produk - + ); }, [warehouseStockMap] @@ -1360,7 +1343,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { errorMessage={formik.errors.source_warehouse_id as string} isDisabled={type === 'detail'} isClearable - startAdornment={ + inputPrefix={ formik.values.source_warehouse_id ? getWarehouseStockAdornment( formik.values.source_warehouse_id @@ -1418,7 +1401,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { errorMessage={formik.errors.destination_warehouse_id as string} isDisabled={type === 'detail'} isClearable - startAdornment={ + inputPrefix={ formik.values.destination_warehouse_id ? getWarehouseStockAdornment( formik.values.destination_warehouse_id From 711536975cb64a9b7b91dcdb802d578125480103 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 29 Jan 2026 10:58:49 +0700 Subject: [PATCH 4/6] refactor(FE): Apply innerProps and className to CustomControl --- src/components/input/SelectInput.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/input/SelectInput.tsx b/src/components/input/SelectInput.tsx index 2a3ff98c..fc7926cc 100644 --- a/src/components/input/SelectInput.tsx +++ b/src/components/input/SelectInput.tsx @@ -89,7 +89,7 @@ const CustomControl = < >( props: ControlProps ) => { - const { children } = props; + const { children, innerProps, className } = props; const customProps = props.selectProps as unknown as { shouldShowAdornment?: boolean; @@ -101,7 +101,10 @@ const CustomControl = < return ( -
+
{shouldShowAdornment && startAdornment} {children}
From 079d69dffb5bcf46d0befed50976c6a9779249ac Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 29 Jan 2026 11:05:06 +0700 Subject: [PATCH 5/6] refactor(FE): Simplify SelectInput control styling --- src/components/input/SelectInput.tsx | 54 ++++++++++++++-------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/components/input/SelectInput.tsx b/src/components/input/SelectInput.tsx index fc7926cc..a79054dd 100644 --- a/src/components/input/SelectInput.tsx +++ b/src/components/input/SelectInput.tsx @@ -89,7 +89,7 @@ const CustomControl = < >( props: ControlProps ) => { - const { children, innerProps, className } = props; + const { children, innerProps } = props; const customProps = props.selectProps as unknown as { shouldShowAdornment?: boolean; @@ -101,10 +101,7 @@ const CustomControl = < return ( -
+
{shouldShowAdornment && startAdornment} {children}
@@ -280,21 +277,20 @@ const SelectInput = (props: SelectInputProps) => { hideSelectedOptions={hideSelectedOptions} className={cn('w-full flex-1', className?.select)} classNames={{ - ...(!startAdornment && { - control: ({ isFocused, isDisabled }) => - cn('w-full rounded-lg! border bg-white transition-shadow', { - 'cursor-pointer!': !readOnly && !isDisabled, - 'border-red-500! ring-2 ring-red-200': isError, - 'border-indigo-500 ring-2 ring-indigo-200': isFocused, - 'border-base-content/10!': !isError && !isFocused, - 'bg-gray-100 text-gray-400 cursor-not-allowed': - isDisabled && !readOnly, - 'bg-transparent! cursor-not-allowed!': readOnly, - 'rounded-l-none!': inputPrefix, - 'rounded-r-none!': inputSuffix, - }), - valueContainer: () => cn('flex-1 px-3! pr-2! py-2.5! gap-1'), - }), + control: ({ isFocused, isDisabled }) => + cn('w-full border bg-white transition-shadow', 'rounded-lg!', { + 'cursor-pointer!': !readOnly && !isDisabled, + 'border-red-500! ring-2 ring-red-200': isError, + 'border-indigo-500 ring-2 ring-indigo-200': + isFocused && !startAdornment, + 'border-base-content/10!': !isError && !isFocused, + 'bg-gray-100 text-gray-400 cursor-not-allowed': + isDisabled && !readOnly, + 'bg-transparent! cursor-not-allowed!': readOnly, + 'rounded-l-none!': inputPrefix && !startAdornment, + 'rounded-r-none!': inputSuffix && !startAdornment, + }), + valueContainer: () => cn('flex-1 px-3! pr-2! py-2.5! gap-1'), placeholder: () => cn({ 'text-gray-400 text-sm leading-tight': !isError, @@ -406,19 +402,23 @@ const SelectInput = (props: SelectInputProps) => { hideSelectedOptions={hideSelectedOptions} className={cn('w-full', className?.select)} classNames={{ - ...(!startAdornment && { - control: ({ isFocused, isDisabled }) => - cn('w-full rounded-lg! border bg-white transition-shadow', { + control: ({ isFocused, isDisabled }) => + cn( + 'w-full border bg-white transition-shadow', + // Gunakan rounded-lg untuk semua kasus + 'rounded-lg!', + { 'cursor-pointer!': !readOnly && !isDisabled, 'border-red-500! ring-2 ring-red-200': isError, - 'border-indigo-500 ring-2 ring-indigo-200': isFocused, + 'border-indigo-500 ring-2 ring-indigo-200': + isFocused && !startAdornment, 'border-base-content/10!': !isError && !isFocused, 'bg-gray-100 text-gray-400 cursor-not-allowed': isDisabled && !readOnly, 'bg-transparent! cursor-not-allowed!': readOnly, - }), - valueContainer: () => cn('flex-1 px-3! pr-2! py-2.5! gap-1'), - }), + } + ), + valueContainer: () => cn('flex-1 px-3! pr-2! py-2.5! gap-1'), placeholder: () => cn({ 'text-gray-400 text-sm leading-tight': !isError, From 75dfd96934b2bf7b4ef9e21d515fcc5a722298ca Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 29 Jan 2026 11:09:30 +0700 Subject: [PATCH 6/6] refactor(FE): Replace Badge with simple span and rename adornment prop --- .../recording/form/RecordingForm.tsx | 41 +++---------------- 1 file changed, 6 insertions(+), 35 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 13ce9cc9..f3d6d2f9 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -1125,16 +1125,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } return ( - + Periode {projectFlockKandangLookup.project_flock?.period} - + ); }, [recordedProjectFlockKandangIds, projectFlockKandangLookup]); @@ -1150,33 +1143,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const hasOvkFlag = productWarehouse.product.flags?.includes('OVK'); if (hasPakanFlag) { - return ( - - PAKAN - - ); + return PAKAN; } if (hasOvkFlag) { - return ( - - OVK - - ); + return OVK; } return null; @@ -1826,7 +1797,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { Boolean(formik.errors.kandang_id) } errorMessage={formik.errors.kandang_id as string} - startAdornment={ + inputPrefix={ projectFlockKandangLookup || projectFlockKandangDetail ? getProjectFlockBadgeAdornment() : undefined @@ -2458,7 +2429,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { !formik.values.project_flock_kandang_id } isClearable={type !== 'detail'} - startAdornment={ + inputPrefix={ stock.product_warehouse_id ? getProductFlagBadgeAdornment( stock.product_warehouse_id