refactor(FE-Storyless): streamline RecordingForm component by native card and optimizing layout for better usability

This commit is contained in:
rstubryan
2025-11-03 10:45:36 +07:00
parent d53f7fc72f
commit 1d79e8de1d
@@ -32,6 +32,7 @@ import { MovementApi } from '@/services/api/inventory';
import FileInput from '@/components/input/FileInput';
import CheckboxInput from '@/components/input/CheckboxInput';
import Badge from '@/components/Badge';
import Card from '@/components/Card';
interface MovementFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -768,8 +769,13 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
className='w-full mt-8 flex flex-col gap-6'
>
{/* Top card - Movement details */}
<div className='card bg-base-100 shadow mb-4'>
<div className='card-body'>
<Card
title='Detail Movement'
className={{
wrapper: 'w-full mb-4 shadow',
body: 'flex flex-col gap-6',
}}
>
<div className='grid grid-cols-1 md:grid-cols-2 gap-4'>
<TextInput
required
@@ -802,14 +808,17 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
readOnly={type === 'detail'}
/>
</div>
</div>
</div>
</Card>
{/* Warehouse cards */}
<div className='grid grid-cols-1 md:grid-cols-2 gap-4 mb-4'>
<div className='card bg-base-100 shadow'>
<div className='card-body space-y-4'>
<h3 className='card-title text-lg'>Gudang Asal</h3>
<Card
title='Gudang Asal'
className={{
wrapper: 'w-full shadow',
body: 'space-y-4',
}}
>
<SelectInput
required
label='Gudang'
@@ -872,12 +881,15 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
}}
/>
</div>
</div>
</div>
</Card>
<div className='card bg-base-100 shadow'>
<div className='card-body space-y-4'>
<h3 className='card-title text-lg'>Gudang Tujuan</h3>
<Card
title='Gudang Tujuan'
className={{
wrapper: 'w-full shadow',
body: 'space-y-4',
}}
>
<SelectInput
required
label='Gudang'
@@ -899,9 +911,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
formik.touched.destination_warehouse_id &&
Boolean(formik.errors.destination_warehouse_id)
}
errorMessage={
formik.errors.destination_warehouse_id as string
}
errorMessage={formik.errors.destination_warehouse_id as string}
isDisabled={type === 'detail'}
isClearable
startAdornment={
@@ -919,10 +929,8 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
label='Area'
name='destination_warehouse_area'
value={
(
formik.values
.destination_warehouse as WarehouseOptionType
)?.area || '-'
(formik.values.destination_warehouse as WarehouseOptionType)
?.area || '-'
}
readOnly
disabled
@@ -934,10 +942,8 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
label='Lokasi'
name='destination_warehouse_location'
value={
(
formik.values
.destination_warehouse as WarehouseOptionType
)?.location || '-'
(formik.values.destination_warehouse as WarehouseOptionType)
?.location || '-'
}
readOnly
disabled
@@ -946,14 +952,17 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
}}
/>
</div>
</div>
</div>
</Card>
</div>
{/* Products table */}
<div className='card bg-base-100 shadow mb-4'>
<div className='card-body'>
<h2 className='card-title mb-4'>Produk</h2>
<Card
title='Produk'
className={{
wrapper: 'w-full mb-4 shadow',
title: 'mb-4',
}}
>
<div className='overflow-x-auto'>
<table className='table'>
<thead>
@@ -972,9 +981,8 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
) => {
if (e.target.checked) {
setSelectedProducts(
formik.values.products?.map(
(_, idx) => idx
) ?? []
formik.values.products?.map((_, idx) => idx) ??
[]
);
} else {
setSelectedProducts([]);
@@ -1020,10 +1028,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
e: React.ChangeEvent<HTMLInputElement>
) => {
if (e.target.checked) {
setSelectedProducts([
...selectedProducts,
idx,
]);
setSelectedProducts([...selectedProducts, idx]);
} else {
setSelectedProducts(
selectedProducts.filter((i) => i !== idx)
@@ -1078,8 +1083,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
idx
)}
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
wrapper: 'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/>
</td>
@@ -1097,25 +1101,18 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
decimalSeparator='.'
bottomLabel={getProductQtyBottomLabel(idx)}
isError={
isRepeaterInputError(
'products',
'product_qty',
idx
).isError || Boolean(getProductQtyError(idx))
isRepeaterInputError('products', 'product_qty', idx)
.isError || Boolean(getProductQtyError(idx))
}
errorMessage={
isRepeaterInputError(
'products',
'product_qty',
idx
).errorMessage ||
isRepeaterInputError('products', 'product_qty', idx)
.errorMessage ||
getProductQtyError(idx) ||
undefined
}
readOnly={type === 'detail'}
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
wrapper: 'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/>
</td>
@@ -1169,13 +1166,16 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
</Button>
</div>
)}
</div>
</div>
</Card>
{/* Deliveries table */}
<div className='card bg-base-100 shadow mb-4'>
<div className='card-body'>
<h2 className='card-title mb-8'>Pengiriman</h2>
<Card
title='Pengiriman'
className={{
wrapper: 'w-full mb-4 shadow',
title: 'mb-8',
}}
>
<div className='overflow-x-auto'>
<table className='table'>
<thead>
@@ -1340,8 +1340,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
.errorMessage
}
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
wrapper: 'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/>
</td>
@@ -1367,10 +1366,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
getDeliveryQtyError(idx, 0) ||
undefined
}
bottomLabel={getDeliveryProductQtyBottomLabel(
idx,
0
)}
bottomLabel={getDeliveryProductQtyBottomLabel(idx, 0)}
readOnly={type === 'detail'}
className={{
wrapper: 'w-full min-w-48',
@@ -1411,8 +1407,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
idx
)}
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
wrapper: 'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/>
</td>
@@ -1431,8 +1426,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
)}
readOnly={type === 'detail'}
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
wrapper: 'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/>
</td>
@@ -1470,9 +1464,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
const file = e.target.files?.[0];
if (file) {
if (file.size > 2 * 1024 * 1024) {
toast.error(
'Ukuran dokumen maksimal 2 MB!'
);
toast.error('Ukuran dokumen maksimal 2 MB!');
e.target.value = '';
return;
}
@@ -1514,8 +1506,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
)}
readOnly={type === 'detail'}
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
wrapper: 'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/>
</td>
@@ -1525,9 +1516,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
name={`deliveries.${idx}.delivery_cost_per_item`}
placeholder='Masukkan biaya per item...'
value={delivery.delivery_cost_per_item || ''}
onChange={handleDeliveryCostPerItemChangeWrapper(
idx
)}
onChange={handleDeliveryCostPerItemChangeWrapper(idx)}
onBlur={formik.handleBlur}
decimalScale={0}
allowNegative={false}
@@ -1541,8 +1530,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
)}
readOnly={type === 'detail'}
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
wrapper: 'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/>
</td>
@@ -1561,8 +1549,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
)}
readOnly={type === 'detail'}
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
wrapper: 'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/>
</td>
@@ -1616,8 +1603,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
</Button>
</div>
)}
</div>
</div>
</Card>
{/* Action buttons */}
<div className='flex flex-row justify-between gap-2 flex-wrap'>