mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-23 06:45:46 +00:00
fix(FE): refactor sales order form create
This commit is contained in:
@@ -677,7 +677,7 @@ const MarketingForm = ({
|
||||
}}
|
||||
variant='bordered'
|
||||
>
|
||||
<MemoizedSalesOrderProductTable
|
||||
{/* <MemoizedSalesOrderProductTable
|
||||
formType={formType}
|
||||
data={memoSalesOrder}
|
||||
rowSelection={rowSOSelection}
|
||||
@@ -687,7 +687,7 @@ const MarketingForm = ({
|
||||
onEdit={handleEditSO}
|
||||
onBulkDelete={handleBulkDeleteSO}
|
||||
onAddProductClick={handleAddSOClick}
|
||||
/>
|
||||
/> */}
|
||||
</Card>
|
||||
|
||||
{/* Input Table Repeater Delivery Order */}
|
||||
|
||||
+158
-163
@@ -282,7 +282,7 @@ const SalesOrderProductForm = ({
|
||||
return (
|
||||
<>
|
||||
<form
|
||||
className='size-full'
|
||||
className='size-full flex flex-col p-4 relative overflow-x-hidden'
|
||||
onSubmit={handleFormSubmit}
|
||||
onReset={handleResetForm}
|
||||
>
|
||||
@@ -293,177 +293,172 @@ const SalesOrderProductForm = ({
|
||||
</Alert>
|
||||
</div>
|
||||
)}
|
||||
<div className='grid sm:grid-cols-3 gap-4 z-200'>
|
||||
<PatternInput
|
||||
name='vehicle_number'
|
||||
label='No. Polisi'
|
||||
format='AA #### AAA'
|
||||
mask='_'
|
||||
inputVehicleNumber
|
||||
required
|
||||
type='text'
|
||||
placeholder='B 1234 CDE'
|
||||
value={formatVechicleNumber(formik.values.vehicle_number ?? '')}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={
|
||||
formik.touched.vehicle_number &&
|
||||
Boolean(formik.errors.vehicle_number)
|
||||
}
|
||||
errorMessage={formik.errors.vehicle_number}
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
label='Kandang'
|
||||
options={kandangSourceOptions}
|
||||
isLoading={isLoadingKandangSourceOptions}
|
||||
value={formik.values.kandang}
|
||||
onChange={kandangChangeHandler}
|
||||
isClearable
|
||||
onInputChange={setKandangInputValue}
|
||||
onMenuScrollToBottom={loadMoreKandang}
|
||||
isError={
|
||||
formik.touched.kandang_id && Boolean(formik.errors.kandang_id)
|
||||
}
|
||||
errorMessage={formik.errors.kandang_id}
|
||||
placeholder='Pilih Kandang'
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
label='Produk'
|
||||
options={productOptionsFiltered}
|
||||
isLoading={isLoadingWarehouseSourceOptions}
|
||||
value={formik.values.product_warehouse}
|
||||
onChange={warehouseChangeHandler}
|
||||
onInputChange={setWarehouseInputValue}
|
||||
onMenuScrollToBottom={loadMoreWarehouse}
|
||||
isClearable
|
||||
placeholder={
|
||||
formik.values.kandang_id
|
||||
? productOptionsFiltered.length == 0
|
||||
? 'Tidak ada produk yang tersedia'
|
||||
: 'Pilih produk'
|
||||
: 'Pilih Kandang Terlebih Dahulu'
|
||||
}
|
||||
isDisabled={!formik.values.kandang_id}
|
||||
isError={
|
||||
formik.touched.product_warehouse_id &&
|
||||
Boolean(formik.errors.product_warehouse_id)
|
||||
}
|
||||
errorMessage={formik.errors.product_warehouse_id}
|
||||
/>
|
||||
</div>
|
||||
<div className='divider my-6'></div>
|
||||
<div className='grid sm:grid-cols-3 gap-4 z-200'>
|
||||
<NumberInput
|
||||
required
|
||||
label='Kuantitas'
|
||||
name='qty'
|
||||
value={formik.values.qty}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('qty')}
|
||||
isError={formik.touched.qty && Boolean(formik.errors.qty)}
|
||||
errorMessage={formik.errors.qty}
|
||||
placeholder='Masukan Kuantitas'
|
||||
endAdornment={
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='text-sm text-gray-500'>
|
||||
{selectedProductWarehouse?.product?.uom?.name}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
bottomLabel={
|
||||
isResponseSuccess(warehouseSourceRawData) &&
|
||||
formik.values.product_warehouse_id
|
||||
? `Stok tersedia: ${formatNumber(
|
||||
warehouseSourceRawData?.data?.find(
|
||||
(item) => item.id === formik.values.product_warehouse_id
|
||||
)?.quantity ?? 0
|
||||
)} ${selectedProductWarehouse?.product?.uom?.name}`
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label={`Harga / ${selectedProductWarehouse?.product?.uom?.name ?? 'Produk'} (Rp)`}
|
||||
name='unit_price'
|
||||
value={formik.values.unit_price}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('unit_price')}
|
||||
isError={
|
||||
formik.touched.unit_price && Boolean(formik.errors.unit_price)
|
||||
}
|
||||
errorMessage={formik.errors.unit_price}
|
||||
placeholder='Masukan Harga Satuan'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Avg. Bobot (Kg)'
|
||||
name='avg_weight'
|
||||
value={formik.values.avg_weight}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('avg_weight')}
|
||||
isError={
|
||||
formik.touched.avg_weight && Boolean(formik.errors.avg_weight)
|
||||
}
|
||||
errorMessage={formik.errors.avg_weight}
|
||||
placeholder='Masukan Bobot Rata-rata'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Total Bobot (Kg)'
|
||||
name='total_weight'
|
||||
value={formik.values.total_weight}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('total_weight')}
|
||||
isError={
|
||||
formik.touched.total_weight && Boolean(formik.errors.total_weight)
|
||||
}
|
||||
errorMessage={formik.errors.total_weight}
|
||||
placeholder='Masukan Total Bobot'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Total Penjualan (Rp)'
|
||||
name='total_price'
|
||||
value={formik.values.total_price}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('total_price')}
|
||||
isError={
|
||||
formik.touched.total_price && Boolean(formik.errors.total_price)
|
||||
}
|
||||
errorMessage={formik.errors.total_price}
|
||||
placeholder='Masukan Total Penjualan'
|
||||
/>
|
||||
</div>
|
||||
<PatternInput
|
||||
name='vehicle_number'
|
||||
label='No. Polisi'
|
||||
format='AA #### AAA'
|
||||
mask='_'
|
||||
inputVehicleNumber
|
||||
required
|
||||
type='text'
|
||||
placeholder='B 1234 CDE'
|
||||
value={formatVechicleNumber(formik.values.vehicle_number ?? '')}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={
|
||||
formik.touched.vehicle_number &&
|
||||
Boolean(formik.errors.vehicle_number)
|
||||
}
|
||||
errorMessage={formik.errors.vehicle_number}
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
label='Kandang'
|
||||
options={kandangSourceOptions}
|
||||
isLoading={isLoadingKandangSourceOptions}
|
||||
value={formik.values.kandang}
|
||||
onChange={kandangChangeHandler}
|
||||
isClearable
|
||||
onInputChange={setKandangInputValue}
|
||||
onMenuScrollToBottom={loadMoreKandang}
|
||||
isError={
|
||||
formik.touched.kandang_id && Boolean(formik.errors.kandang_id)
|
||||
}
|
||||
errorMessage={formik.errors.kandang_id}
|
||||
placeholder='Pilih Kandang'
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
label='Produk'
|
||||
options={productOptionsFiltered}
|
||||
isLoading={isLoadingWarehouseSourceOptions}
|
||||
value={formik.values.product_warehouse}
|
||||
onChange={warehouseChangeHandler}
|
||||
onInputChange={setWarehouseInputValue}
|
||||
onMenuScrollToBottom={loadMoreWarehouse}
|
||||
isClearable
|
||||
placeholder={
|
||||
formik.values.kandang_id
|
||||
? productOptionsFiltered.length == 0
|
||||
? 'Tidak ada produk yang tersedia'
|
||||
: 'Pilih produk'
|
||||
: 'Pilih Kandang Terlebih Dahulu'
|
||||
}
|
||||
isDisabled={!formik.values.kandang_id}
|
||||
isError={
|
||||
formik.touched.product_warehouse_id &&
|
||||
Boolean(formik.errors.product_warehouse_id)
|
||||
}
|
||||
errorMessage={formik.errors.product_warehouse_id}
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Kuantitas'
|
||||
name='qty'
|
||||
value={formik.values.qty}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('qty')}
|
||||
isError={formik.touched.qty && Boolean(formik.errors.qty)}
|
||||
errorMessage={formik.errors.qty}
|
||||
placeholder='Masukan Kuantitas'
|
||||
endAdornment={
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='text-sm text-gray-500'>
|
||||
{selectedProductWarehouse?.product?.uom?.name}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
bottomLabel={
|
||||
isResponseSuccess(warehouseSourceRawData) &&
|
||||
formik.values.product_warehouse_id
|
||||
? `Stok tersedia: ${formatNumber(
|
||||
warehouseSourceRawData?.data?.find(
|
||||
(item) => item.id === formik.values.product_warehouse_id
|
||||
)?.quantity ?? 0
|
||||
)} ${selectedProductWarehouse?.product?.uom?.name}`
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label={`Harga / ${selectedProductWarehouse?.product?.uom?.name ?? 'Produk'} (Rp)`}
|
||||
name='unit_price'
|
||||
value={formik.values.unit_price}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('unit_price')}
|
||||
isError={
|
||||
formik.touched.unit_price && Boolean(formik.errors.unit_price)
|
||||
}
|
||||
errorMessage={formik.errors.unit_price}
|
||||
placeholder='Masukan Harga Satuan'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Avg. Bobot (Kg)'
|
||||
name='avg_weight'
|
||||
value={formik.values.avg_weight}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('avg_weight')}
|
||||
isError={
|
||||
formik.touched.avg_weight && Boolean(formik.errors.avg_weight)
|
||||
}
|
||||
errorMessage={formik.errors.avg_weight}
|
||||
placeholder='Masukan Bobot Rata-rata'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Total Bobot (Kg)'
|
||||
name='total_weight'
|
||||
value={formik.values.total_weight}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('total_weight')}
|
||||
isError={
|
||||
formik.touched.total_weight && Boolean(formik.errors.total_weight)
|
||||
}
|
||||
errorMessage={formik.errors.total_weight}
|
||||
placeholder='Masukan Total Bobot'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Total Penjualan (Rp)'
|
||||
name='total_price'
|
||||
value={formik.values.total_price}
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
setCurrentInput(e.target.name);
|
||||
}}
|
||||
onBlur={() => handleBlurField('total_price')}
|
||||
isError={
|
||||
formik.touched.total_price && Boolean(formik.errors.total_price)
|
||||
}
|
||||
errorMessage={formik.errors.total_price}
|
||||
placeholder='Masukan Total Penjualan'
|
||||
/>
|
||||
|
||||
<div className='mt-4'>
|
||||
<AlertErrorList formErrorList={formErrorList} onClose={close} />
|
||||
</div>
|
||||
|
||||
<div className='flex flex-row justify-end gap-3 mt-4'>
|
||||
<Button type='reset' color='warning' onClick={handleResetForm}>
|
||||
Reset
|
||||
</Button>
|
||||
<div className='h-18' />
|
||||
|
||||
<div className='absolute p-4 sm:w-[446px] bottom-0 right-0'>
|
||||
<Button
|
||||
type='submit'
|
||||
isLoading={formik.isSubmitting}
|
||||
disabled={formik.isSubmitting}
|
||||
className='w-full rounded-lg'
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
|
||||
@@ -16,27 +16,17 @@ import CheckboxInput from '@/components/input/CheckboxInput';
|
||||
|
||||
type SalesOrderProductTableProps = {
|
||||
data: SalesOrderProductFormValues[];
|
||||
formType: 'add' | 'edit' | 'add_deliver' | 'edit_deliver';
|
||||
rowSelection: Record<string, boolean>;
|
||||
setRowSelection: React.Dispatch<
|
||||
React.SetStateAction<Record<string, boolean>>
|
||||
>;
|
||||
selectedRowIds: number[];
|
||||
formType: 'add' | 'edit' | 'add_deliver' | 'edit_deliver' | 'success';
|
||||
onDelete: (id: number) => void;
|
||||
onEdit: (id: number) => void;
|
||||
onBulkDelete: () => void;
|
||||
onAddProductClick: () => void;
|
||||
};
|
||||
|
||||
const SalesOrderProductTable = ({
|
||||
data,
|
||||
formType,
|
||||
rowSelection,
|
||||
setRowSelection,
|
||||
selectedRowIds,
|
||||
onDelete,
|
||||
onEdit,
|
||||
onBulkDelete,
|
||||
onAddProductClick,
|
||||
}: SalesOrderProductTableProps) => {
|
||||
const onDeleteRef = useRef(onDelete);
|
||||
@@ -156,67 +146,100 @@ const SalesOrderProductTable = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table<SalesOrderProductFormValues>
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
data={data}
|
||||
columns={
|
||||
formType == 'add_deliver' || formType == 'edit_deliver'
|
||||
? columns.filter(
|
||||
(col) => col.header != 'Aksi' && col.id != 'select'
|
||||
)
|
||||
: columns
|
||||
}
|
||||
className={{
|
||||
tableWrapperClassName: 'overflow-x-auto min-h-full!',
|
||||
tableClassName: 'font-inter w-full table-auto min-h-full!',
|
||||
headerRowClassName: 'border-b border-b-gray-200',
|
||||
headerColumnClassName:
|
||||
'px-2 py-2 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end first:flex first:flex-row first:justify-start',
|
||||
bodyRowClassName: 'border-b border-b-gray-200',
|
||||
bodyColumnClassName:
|
||||
'px-2 py-2 last:flex last:flex-row last:justify-end first:flex first:flex-row first:justify-start',
|
||||
paginationClassName: 'hidden',
|
||||
}}
|
||||
emptyContent={
|
||||
<div className='size-full flex flex-col relative overflow-x-hidden gap-3'>
|
||||
{data.map((item) => (
|
||||
<div
|
||||
className={cn(
|
||||
'w-full h-16 flex flex-col justify-center items-center gap-2'
|
||||
)}
|
||||
className='rounded-lg border border-tools-table-outline border-base-content/5'
|
||||
key={`table-${item.id}`}
|
||||
>
|
||||
<span className='text-gray-500'>Belum ada data penjualan</span>
|
||||
<table
|
||||
style={{
|
||||
borderRadius: '0.5rem',
|
||||
}}
|
||||
className='border-none'
|
||||
>
|
||||
<tbody>
|
||||
<tr className='border-b border-tools-table-outline border-base-content/5'>
|
||||
<th className='text-start not-first:font-medium text-base-content/50 text-sm px-4 py-3'>
|
||||
Label
|
||||
</th>
|
||||
<th className='text-start font-medium text-base-content/50 text-sm px-4 py-3'>
|
||||
Value
|
||||
</th>
|
||||
</tr>
|
||||
<>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>No. Polisi</td>
|
||||
<td className='text-sm px-4 py-3'>{item.vehicle_number}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Gudang</td>
|
||||
<td className='text-sm px-4 py-3'>
|
||||
{item.product_warehouse?.label}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Kategori</td>
|
||||
<td className='text-sm px-4 py-3'>Telur</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Produk</td>
|
||||
<td className='text-sm px-4 py-3'>
|
||||
{item.product_warehouse?.label}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Tipe Konversi</td>
|
||||
<td className='text-sm px-4 py-3'>Peti 15Kg</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Total Peti</td>
|
||||
<td className='text-sm px-4 py-3'>
|
||||
{formatNumber(parseFloat(item.qty as string))}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Total Bobot</td>
|
||||
<td className='text-sm px-4 py-3'>
|
||||
{formatNumber(parseFloat(item.total_weight as string))}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Total Butir Telur</td>
|
||||
<td className='text-sm px-4 py-3'>
|
||||
{formatNumber(parseFloat(item.qty as string))}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Total Harga Satuan</td>
|
||||
<td className='text-sm px-4 py-3'>
|
||||
{formatNumber(parseFloat(item.unit_price as string))}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='text-sm px-4 py-3'>Total Penjualan</td>
|
||||
<td className='text-sm px-4 py-3'>
|
||||
{formatNumber(parseFloat(item.total_price as string))}
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
{formType != 'add_deliver' && formType != 'edit_deliver' && (
|
||||
<div className='flex flex-row gap-3 mt-3'>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
className='justify-start w-fit py-1 text-sm'
|
||||
onClick={onAddProductClick}
|
||||
>
|
||||
<Icon icon='mdi:plus' width={16} height={16} />
|
||||
Tambah Produk
|
||||
</Button>
|
||||
{selectedRowIds.length > 0 && (
|
||||
))}
|
||||
{formType != 'add_deliver' &&
|
||||
formType != 'edit_deliver' &&
|
||||
formType != 'success' && (
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
color='error'
|
||||
className='justify-start w-fit py-1 text-sm'
|
||||
onClick={onBulkDelete}
|
||||
className='justify-center w-full rounded-lg text-center text-sm mt-5'
|
||||
onClick={onAddProductClick}
|
||||
>
|
||||
<Icon icon='mdi:trash' width={16} height={16} />
|
||||
Hapus
|
||||
{selectedRowIds.length > 0
|
||||
? ` (${selectedRowIds.length})`
|
||||
: ''}{' '}
|
||||
Produk
|
||||
Tambah Produk
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user