mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'feat/FE/US-160/marketing-delivery-order' into 'development'
[FE/FE][US#160] Marketing - Delivery Order See merge request mbugroup/lti-web-client!62
This commit is contained in:
@@ -213,7 +213,7 @@ const DateInput = ({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'input h-12 px-4 py-2 text-base font-normal leading-6 w-full rounded transition-all duration-200 flex items-center border',
|
'input h-12 bg-inherit px-4 py-2 text-base font-normal leading-6 w-full rounded transition-all duration-200 flex items-center border',
|
||||||
{
|
{
|
||||||
'border-error': finalIsError,
|
'border-error': finalIsError,
|
||||||
'border-success': externalValid && !finalIsError,
|
'border-success': externalValid && !finalIsError,
|
||||||
|
|||||||
@@ -71,9 +71,8 @@ const InventoryAdjustmentForm = ({
|
|||||||
Partial<InventoryAdjustmentFormValues>
|
Partial<InventoryAdjustmentFormValues>
|
||||||
>(() => {
|
>(() => {
|
||||||
return {
|
return {
|
||||||
product_category_id: initialValues?.product_category?.id ?? 0,
|
product_id: initialValues?.product_warehouse?.product_id ?? 0,
|
||||||
product_id: initialValues?.product?.id ?? 0,
|
warehouse_id: initialValues?.product_warehouse?.warehouse_id ?? 0,
|
||||||
warehouse_id: initialValues?.warehouse?.id ?? 0,
|
|
||||||
product_category: undefined,
|
product_category: undefined,
|
||||||
product: undefined,
|
product: undefined,
|
||||||
warehouse: undefined,
|
warehouse: undefined,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||||
import { OptionType } from '@/components/input/SelectInput';
|
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||||
import Modal, { useModal } from '@/components/Modal';
|
import Modal, { useModal } from '@/components/Modal';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
|
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
|
||||||
@@ -183,14 +183,18 @@ const MarketingTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const hasApprovable = selectedRowsData.some(
|
const hasApprovable = selectedRowsData.some(
|
||||||
(row) => row.latest_approval.step_number === 1
|
(row) =>
|
||||||
|
row.latest_approval.step_number === 1 &&
|
||||||
|
row.latest_approval.action !== 'REJECTED'
|
||||||
);
|
);
|
||||||
const hasRejectable = selectedRowsData.some(
|
const hasRejectable = selectedRowsData.some(
|
||||||
(row) => row.latest_approval.step_number === 2
|
(row) =>
|
||||||
|
row.latest_approval.step_number === 1 &&
|
||||||
|
row.latest_approval.action !== 'REJECTED'
|
||||||
);
|
);
|
||||||
|
|
||||||
const disableApprove = !hasApprovable || hasRejectable;
|
const disableApprove = !hasApprovable;
|
||||||
// const disableReject = !hasRejectable || hasApprovable;
|
const disableReject = !hasRejectable;
|
||||||
|
|
||||||
const idsToProcess =
|
const idsToProcess =
|
||||||
approveAction === 'APPROVED'
|
approveAction === 'APPROVED'
|
||||||
@@ -204,15 +208,9 @@ const MarketingTable = () => {
|
|||||||
const approveMarketingHandler = async (notes: string) => {
|
const approveMarketingHandler = async (notes: string) => {
|
||||||
let idsToProcess: number[] = [];
|
let idsToProcess: number[] = [];
|
||||||
|
|
||||||
if (approveAction === 'APPROVED') {
|
idsToProcess = selectedRowsData
|
||||||
idsToProcess = selectedRowsData
|
.filter((row) => row.latest_approval.step_number === 1)
|
||||||
.filter((row) => row.latest_approval.step_number === 1)
|
.map((row) => row.id);
|
||||||
.map((row) => row.id);
|
|
||||||
} else if (approveAction === 'REJECTED') {
|
|
||||||
idsToProcess = selectedRowsData
|
|
||||||
.filter((row) => row.latest_approval.step_number === 2)
|
|
||||||
.map((row) => row.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idsToProcess.length === 0) {
|
if (idsToProcess.length === 0) {
|
||||||
toast.error(`Tidak ada data yang valid untuk di ${approveAction}.`);
|
toast.error(`Tidak ada data yang valid untuk di ${approveAction}.`);
|
||||||
@@ -263,8 +261,8 @@ const MarketingTable = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getRowCanSelect = (row: Row<Marketing>): boolean => {
|
const getRowCanSelect = (row: Row<Marketing>): boolean => {
|
||||||
const step = row.original.latest_approval?.step_number;
|
const approval = row.original.latest_approval;
|
||||||
return step === 1;
|
return approval?.step_number === 1 && approval?.action !== 'REJECTED';
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -282,11 +280,6 @@ const MarketingTable = () => {
|
|||||||
placeholder: 'Cari Sales Order',
|
placeholder: 'Cari Sales Order',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TableRowSizeSelector
|
|
||||||
value={pageSize}
|
|
||||||
onChange={pageSizeChangeHandler}
|
|
||||||
options={ROWS_OPTIONS}
|
|
||||||
/>
|
|
||||||
<div className='flex flex-row gap-2'>
|
<div className='flex flex-row gap-2'>
|
||||||
<Button
|
<Button
|
||||||
color='success'
|
color='success'
|
||||||
@@ -298,7 +291,7 @@ const MarketingTable = () => {
|
|||||||
Approve
|
Approve
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* <Button
|
<Button
|
||||||
color='error'
|
color='error'
|
||||||
onClick={rejectClickHandler}
|
onClick={rejectClickHandler}
|
||||||
className='justify-start text-sm'
|
className='justify-start text-sm'
|
||||||
@@ -306,8 +299,36 @@ const MarketingTable = () => {
|
|||||||
>
|
>
|
||||||
<Icon icon='material-symbols:close' width={24} height={24} />
|
<Icon icon='material-symbols:close' width={24} height={24} />
|
||||||
Reject
|
Reject
|
||||||
</Button> */}
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<TableRowSizeSelector
|
||||||
|
value={pageSize}
|
||||||
|
onChange={pageSizeChangeHandler}
|
||||||
|
options={ROWS_OPTIONS}
|
||||||
|
>
|
||||||
|
{/* select multiple product */}
|
||||||
|
<SelectInput
|
||||||
|
label='Product'
|
||||||
|
isClearable
|
||||||
|
placeholder='Pilih product'
|
||||||
|
options={[]}
|
||||||
|
isMulti
|
||||||
|
/>
|
||||||
|
{/* select status */}
|
||||||
|
<SelectInput
|
||||||
|
label='Status'
|
||||||
|
isClearable
|
||||||
|
placeholder='Pilih status'
|
||||||
|
options={[]}
|
||||||
|
/>
|
||||||
|
{/* select customer */}
|
||||||
|
<SelectInput
|
||||||
|
label='Customer'
|
||||||
|
isClearable
|
||||||
|
placeholder='Pilih customer'
|
||||||
|
options={[]}
|
||||||
|
/>
|
||||||
|
</TableRowSizeSelector>
|
||||||
</div>
|
</div>
|
||||||
<Table
|
<Table
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import { useRouter } from 'next/navigation';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import SalesOrderExport from '@/components/pages/marketing/pdf/SalesOrderExport';
|
import SalesOrderExport from '@/components/pages/marketing/pdf/SalesOrderExport';
|
||||||
import DeliveryOrderExport from '../pdf/DeliveryOrderExport';
|
import DeliveryOrderExport from '@/components/pages/marketing/pdf/DeliveryOrderExport';
|
||||||
|
|
||||||
const MarketingDetail = ({
|
const MarketingDetail = ({
|
||||||
initialValues,
|
initialValues,
|
||||||
@@ -71,10 +71,10 @@ const MarketingDetail = ({
|
|||||||
confirmationModal.openModal();
|
confirmationModal.openModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
// const rejectClickHandler = () => {
|
const rejectClickHandler = () => {
|
||||||
// setApprovalAction('REJECTED');
|
setApprovalAction('REJECTED');
|
||||||
// confirmationModal.openModal();
|
confirmationModal.openModal();
|
||||||
// };
|
};
|
||||||
|
|
||||||
const deliveryClickHandler = () => {
|
const deliveryClickHandler = () => {
|
||||||
deliveryModal.openModal();
|
deliveryModal.openModal();
|
||||||
@@ -87,10 +87,11 @@ const MarketingDetail = ({
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const res = await MarketingApi.delete(initialValues?.id as number);
|
const res = await MarketingApi.delete(initialValues?.id as number);
|
||||||
setIsLoading(false);
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
router.push('/marketing');
|
||||||
toast.success(res?.message as string);
|
toast.success(res?.message as string);
|
||||||
refresh?.();
|
refresh?.();
|
||||||
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmationModalApproveClickHandler = async (notes: string) => {
|
const confirmationModalApproveClickHandler = async (notes: string) => {
|
||||||
@@ -131,32 +132,45 @@ const MarketingDetail = ({
|
|||||||
<ApprovalSteps approvals={approvals} />
|
<ApprovalSteps approvals={approvals} />
|
||||||
)}
|
)}
|
||||||
<div className='flex-row flex gap-3'>
|
<div className='flex-row flex gap-3'>
|
||||||
{initialValues?.latest_approval?.step_number != 3 && (
|
{initialValues?.latest_approval?.step_number == 1 && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
color='success'
|
color='success'
|
||||||
onClick={approveClickHandler}
|
onClick={approveClickHandler}
|
||||||
disabled={initialValues?.latest_approval?.step_number != 1}
|
disabled={
|
||||||
|
initialValues?.latest_approval?.step_number == 1 &&
|
||||||
|
initialValues?.latest_approval?.action == 'REJECTED'
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:check' width={24} height={24} />
|
<Icon icon='mdi:check' width={24} height={24} />
|
||||||
Approve
|
Approve
|
||||||
</Button>
|
</Button>
|
||||||
{/* <Button
|
<Button
|
||||||
color='error'
|
color='error'
|
||||||
onClick={rejectClickHandler}
|
onClick={rejectClickHandler}
|
||||||
disabled={initialValues?.latest_approval?.step_number != 2}
|
disabled={
|
||||||
|
initialValues?.latest_approval?.step_number == 1 &&
|
||||||
|
initialValues?.latest_approval?.action == 'REJECTED'
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:close' width={24} height={24} />
|
<Icon icon='mdi:close' width={24} height={24} />
|
||||||
Reject
|
Reject
|
||||||
</Button> */}
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{initialValues?.latest_approval?.step_number == 2 && (
|
{initialValues?.latest_approval?.step_number != 1 && (
|
||||||
<Button
|
<Button
|
||||||
color='success'
|
color='success'
|
||||||
href={`/marketing/add/delivery-orders?marketingId=${initialValues?.id}`}
|
href={
|
||||||
|
initialValues?.latest_approval?.step_number == 3
|
||||||
|
? `/marketing/detail/delivery-orders/edit?marketingId=${initialValues?.id}`
|
||||||
|
: `/marketing/add/delivery-orders?marketingId=${initialValues?.id}`
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:truck' width={24} height={24} />
|
<Icon icon='mdi:truck' width={24} height={24} />
|
||||||
|
{initialValues?.latest_approval?.step_number == 3
|
||||||
|
? 'Edit '
|
||||||
|
: 'Tambah '}
|
||||||
Delivery Order
|
Delivery Order
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -398,14 +412,16 @@ const MarketingDetail = ({
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
<div className='flex flex-row gap-3'>
|
<div className='flex flex-row gap-3'>
|
||||||
<Button
|
{initialValues?.latest_approval?.step_number != 3 && (
|
||||||
color='warning'
|
<Button
|
||||||
type='button'
|
color='warning'
|
||||||
href={`/marketing/detail/${initialValues?.latest_approval.step_number == 3 ? 'delivery-orders' : 'sales-orders'}/edit?marketingId=${initialValues?.id}`}
|
type='button'
|
||||||
>
|
href={`/marketing/detail/${initialValues?.latest_approval.step_number == 3 ? 'delivery-orders' : 'sales-orders'}/edit?marketingId=${initialValues?.id}`}
|
||||||
<Icon icon='mdi:pencil' width={24} height={24} />
|
>
|
||||||
Edit
|
<Icon icon='mdi:pencil' width={24} height={24} />
|
||||||
</Button>
|
Edit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button color='error' onClick={deleteClickHandler}>
|
<Button color='error' onClick={deleteClickHandler}>
|
||||||
<Icon icon='mdi:delete' width={24} height={24} />
|
<Icon icon='mdi:delete' width={24} height={24} />
|
||||||
Hapus
|
Hapus
|
||||||
@@ -429,7 +445,7 @@ const MarketingDetail = ({
|
|||||||
<ConfirmationModalWithNotes
|
<ConfirmationModalWithNotes
|
||||||
ref={confirmationModal.ref}
|
ref={confirmationModal.ref}
|
||||||
type={approvalAction === 'APPROVED' ? 'success' : 'error'}
|
type={approvalAction === 'APPROVED' ? 'success' : 'error'}
|
||||||
text={`Apakah anda yakin ingin ${approvalAction} data penjualan ini?`}
|
text={`Apakah anda yakin ingin ${approvalAction == 'APPROVED' ? 'approve' : 'reject'} data penjualan ini?`}
|
||||||
secondaryButton={{
|
secondaryButton={{
|
||||||
text: 'Tidak',
|
text: 'Tidak',
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -49,22 +49,23 @@ export const SalesOrderSchema: Yup.ObjectSchema<SalesOrderSchemaType> =
|
|||||||
export const DeliveryOrderSchema: Yup.ObjectSchema<DeliveryOrderSchemaType> =
|
export const DeliveryOrderSchema: Yup.ObjectSchema<DeliveryOrderSchemaType> =
|
||||||
Yup.object({
|
Yup.object({
|
||||||
delivery_order: Yup.array()
|
delivery_order: Yup.array()
|
||||||
.of(DeliveryOrderProductSchema)
|
|
||||||
.min(1, 'Pengiriman wajib diisi!')
|
.min(1, 'Pengiriman wajib diisi!')
|
||||||
.required()
|
.required('Pengiriman wajib diisi!')
|
||||||
.test(
|
.test(
|
||||||
'at-least-one-delivery-date',
|
'at-least-one-valid-row',
|
||||||
'Minimal ada satu tanggal pengiriman yang harus diisi!',
|
'Minimal ada satu baris pengiriman yang valid!',
|
||||||
(value) => {
|
function (items) {
|
||||||
if (!value || value.length == 0) {
|
if (!items || items.length === 0) return false;
|
||||||
return false;
|
|
||||||
}
|
// VALIDASI: minimal 1 item valid full
|
||||||
return value.some(
|
const itemSchema = DeliveryOrderProductSchema;
|
||||||
(item) =>
|
|
||||||
item.delivery_date !== null &&
|
const hasValidItem = items.some((item) => {
|
||||||
item.delivery_date !== undefined &&
|
if (!item) return false;
|
||||||
item.delivery_date !== ''
|
return itemSchema.isValidSync(item, { abortEarly: true });
|
||||||
);
|
});
|
||||||
|
|
||||||
|
return hasValidItem;
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,13 +47,23 @@ import SalesOrderProductForm from './repeater/sales-order/SalesOrderProductForm'
|
|||||||
import DeliveryOrderProductTable from './table-view/DeliveryOrderProductTable';
|
import DeliveryOrderProductTable from './table-view/DeliveryOrderProductTable';
|
||||||
import DeliveryOrderProductForm from './repeater/delivery-order/DeliverOrderProduct';
|
import DeliveryOrderProductForm from './repeater/delivery-order/DeliverOrderProduct';
|
||||||
import { DeliveryOrderProductFormValues } from './repeater/delivery-order/DeliverOrderProduct.schema';
|
import { DeliveryOrderProductFormValues } from './repeater/delivery-order/DeliverOrderProduct.schema';
|
||||||
|
import DebouncedTextArea from '@/components/input/DebouncedTextArea';
|
||||||
|
|
||||||
const MemoizedSalesOrderProductTable = memo(SalesOrderProductTable);
|
const MemoizedSalesOrderProductTable = memo(SalesOrderProductTable);
|
||||||
const MemoizedSalesOrderProductForm = memo(SalesOrderProductForm);
|
const MemoizedSalesOrderProductForm = memo(SalesOrderProductForm);
|
||||||
const MemoizedDeliveryOrderProductTable = memo(DeliveryOrderProductTable);
|
const MemoizedDeliveryOrderProductTable = memo(DeliveryOrderProductTable);
|
||||||
const MemoizedDeliveryOrderProductForm = memo(DeliveryOrderProductForm);
|
const MemoizedDeliveryOrderProductForm = memo(DeliveryOrderProductForm);
|
||||||
|
|
||||||
const MarketingProductToFieldValues = (
|
// ================== EXTERNAL HELPER FUNCTION ==================
|
||||||
|
export interface ProductCalculationFields {
|
||||||
|
qty: string | number | undefined;
|
||||||
|
unit_price: string | number | undefined;
|
||||||
|
total_price: string | number | undefined;
|
||||||
|
avg_weight: string | number | undefined;
|
||||||
|
total_weight: string | number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SalesProductToFieldValues = (
|
||||||
product: BaseSalesOrder
|
product: BaseSalesOrder
|
||||||
): SalesOrderProductFormValues => {
|
): SalesOrderProductFormValues => {
|
||||||
return {
|
return {
|
||||||
@@ -76,8 +86,7 @@ const MarketingProductToFieldValues = (
|
|||||||
total_price: product.total_price,
|
total_price: product.total_price,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
export const DeliveryProductToFieldValues = (
|
||||||
const DeliveryProductToFieldValues = (
|
|
||||||
salesOrders: BaseSalesOrder[],
|
salesOrders: BaseSalesOrder[],
|
||||||
delivery: BaseDeliveryOrder
|
delivery: BaseDeliveryOrder
|
||||||
): DeliveryOrderProductFormValues[] => {
|
): DeliveryOrderProductFormValues[] => {
|
||||||
@@ -119,8 +128,7 @@ const DeliveryProductToFieldValues = (
|
|||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
export const mergeSOwithDO = (
|
||||||
const mergeSOwithDO = (
|
|
||||||
salesOrders: SalesOrderProductFormValues[],
|
salesOrders: SalesOrderProductFormValues[],
|
||||||
deliveryOrders: DeliveryOrderProductFormValues[]
|
deliveryOrders: DeliveryOrderProductFormValues[]
|
||||||
): DeliveryOrderProductFormValues[] => {
|
): DeliveryOrderProductFormValues[] => {
|
||||||
@@ -135,15 +143,63 @@ const mergeSOwithDO = (
|
|||||||
delivery_date: delivery?.delivery_date || undefined,
|
delivery_date: delivery?.delivery_date || undefined,
|
||||||
do_number: delivery?.do_number || undefined,
|
do_number: delivery?.do_number || undefined,
|
||||||
vehicle_number: delivery?.vehicle_number || so.vehicle_number,
|
vehicle_number: delivery?.vehicle_number || so.vehicle_number,
|
||||||
unit_price: delivery?.unit_price ?? so.unit_price,
|
unit_price: delivery?.unit_price,
|
||||||
total_weight: delivery?.total_weight ?? so.total_weight,
|
total_weight: delivery?.total_weight,
|
||||||
qty: delivery?.qty ?? so.qty,
|
qty: delivery?.qty,
|
||||||
avg_weight: delivery?.avg_weight ?? so.avg_weight,
|
avg_weight: delivery?.avg_weight,
|
||||||
total_price: delivery?.total_price ?? so.total_price,
|
total_price: delivery?.total_price,
|
||||||
marketing_product: so, // jika ada, override
|
marketing_product: so, // jika ada, override
|
||||||
} as DeliveryOrderProductFormValues;
|
} as DeliveryOrderProductFormValues;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
export const recalculate = (
|
||||||
|
field: string,
|
||||||
|
values: ProductCalculationFields
|
||||||
|
) => {
|
||||||
|
console.log('Values');
|
||||||
|
console.log(values);
|
||||||
|
const { qty, unit_price, total_price, avg_weight, total_weight } = values;
|
||||||
|
const result: Partial<ProductCalculationFields> = {};
|
||||||
|
if (field == 'unit_price' || field == 'total_price' || field == 'qty') {
|
||||||
|
if (qty && unit_price && (field == 'unit_price' || field == 'qty')) {
|
||||||
|
result.total_price = Number(qty) * Number(unit_price);
|
||||||
|
} else if (qty && total_price && field == 'total_price') {
|
||||||
|
result.unit_price = Number(total_price) / Number(qty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (field == 'avg_weight' || field == 'total_weight' || field == 'qty') {
|
||||||
|
if (qty && avg_weight && (field == 'avg_weight' || field == 'qty')) {
|
||||||
|
result.total_weight = Number(qty) * Number(avg_weight);
|
||||||
|
} else if (qty && total_weight && field == 'total_weight') {
|
||||||
|
result.avg_weight = Number(total_weight) / Number(qty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('Result');
|
||||||
|
console.log(result);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
export const getSubmitField = (values: ProductCalculationFields) => {
|
||||||
|
const { qty, unit_price, total_price, avg_weight, total_weight } = values;
|
||||||
|
|
||||||
|
// Harga logic
|
||||||
|
if (qty && unit_price && !total_price) {
|
||||||
|
return 'unit_price';
|
||||||
|
}
|
||||||
|
if (qty && total_price && !unit_price) {
|
||||||
|
return 'total_price';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bobot logic
|
||||||
|
if (qty && avg_weight && !total_weight) {
|
||||||
|
return 'avg_weight';
|
||||||
|
}
|
||||||
|
if (qty && total_weight && !avg_weight) {
|
||||||
|
return 'total_weight';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tidak ada yang perlu dihitung
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
const MarketingForm = ({
|
const MarketingForm = ({
|
||||||
formType = 'add',
|
formType = 'add',
|
||||||
@@ -162,19 +218,21 @@ const MarketingForm = ({
|
|||||||
useState<SalesOrderProductFormValues | null>(null);
|
useState<SalesOrderProductFormValues | null>(null);
|
||||||
const [selectedDeliveryProduct, setSelectedDeliveryProduct] =
|
const [selectedDeliveryProduct, setSelectedDeliveryProduct] =
|
||||||
useState<DeliveryOrderProductFormValues | null>(null);
|
useState<DeliveryOrderProductFormValues | null>(null);
|
||||||
|
const [deliveryFormState, setDeliveryFormState] = useState<'add' | 'edit'>(
|
||||||
|
'add'
|
||||||
|
);
|
||||||
const [deliveryOrderValues, setDeliveryOrderValues] = useState<
|
const [deliveryOrderValues, setDeliveryOrderValues] = useState<
|
||||||
DeliveryOrderProductFormValues[]
|
DeliveryOrderProductFormValues[]
|
||||||
>(
|
>(
|
||||||
mergeSOwithDO(
|
mergeSOwithDO(
|
||||||
initialValues?.sales_order?.map(MarketingProductToFieldValues) ?? [],
|
initialValues?.sales_order?.map(SalesProductToFieldValues) ?? [],
|
||||||
initialValues?.delivery_order?.flatMap((delivery) =>
|
initialValues?.delivery_order?.flatMap((delivery) =>
|
||||||
DeliveryProductToFieldValues(initialValues.sales_order, delivery)
|
DeliveryProductToFieldValues(initialValues.sales_order, delivery)
|
||||||
) ?? []
|
) ?? []
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Repeater Props
|
// ================== REPEATER ==================
|
||||||
const addSOModal = useModal();
|
const addSOModal = useModal();
|
||||||
const addDOModal = useModal();
|
const addDOModal = useModal();
|
||||||
const [rowSOSelection, setRowSOSelection] = useState<Record<string, boolean>>(
|
const [rowSOSelection, setRowSOSelection] = useState<Record<string, boolean>>(
|
||||||
@@ -183,19 +241,14 @@ const MarketingForm = ({
|
|||||||
const selectedRowSOIds = Object.keys(rowSOSelection).map((item) =>
|
const selectedRowSOIds = Object.keys(rowSOSelection).map((item) =>
|
||||||
parseInt(item)
|
parseInt(item)
|
||||||
);
|
);
|
||||||
const [rowDOSelection, setRowDOSelection] = useState<Record<string, boolean>>(
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const selectedRowDOIds = Object.keys(rowDOSelection).map((item) =>
|
|
||||||
parseInt(item)
|
|
||||||
);
|
|
||||||
|
|
||||||
// End Repeater Props
|
// ================== FETCH OPTIONS ==================
|
||||||
const {
|
const {
|
||||||
options: customerOptions,
|
options: customerOptions,
|
||||||
isLoadingOptions: isLoadingCustomerOptions,
|
isLoadingOptions: isLoadingCustomerOptions,
|
||||||
} = useSelect<Customer>(CustomerApi.basePath, 'id', 'name');
|
} = useSelect<Customer>(CustomerApi.basePath, 'id', 'name');
|
||||||
|
|
||||||
|
// ================== SETUP FORMIK ==================
|
||||||
const formikInitialValues = useMemo<
|
const formikInitialValues = useMemo<
|
||||||
SalesOrderFormValues & DeliveryOrderFormValues
|
SalesOrderFormValues & DeliveryOrderFormValues
|
||||||
>(() => {
|
>(() => {
|
||||||
@@ -212,17 +265,16 @@ const MarketingForm = ({
|
|||||||
: null,
|
: null,
|
||||||
sales_order:
|
sales_order:
|
||||||
initialValues?.sales_order?.map((product) =>
|
initialValues?.sales_order?.map((product) =>
|
||||||
MarketingProductToFieldValues(product)
|
SalesProductToFieldValues(product)
|
||||||
) ?? [],
|
) ?? [],
|
||||||
delivery_order: mergeSOwithDO(
|
delivery_order: mergeSOwithDO(
|
||||||
initialValues?.sales_order?.map(MarketingProductToFieldValues) ?? [],
|
initialValues?.sales_order?.map(SalesProductToFieldValues) ?? [],
|
||||||
initialValues?.delivery_order?.flatMap((delivery) =>
|
initialValues?.delivery_order?.flatMap((delivery) =>
|
||||||
DeliveryProductToFieldValues(initialValues.sales_order, delivery)
|
DeliveryProductToFieldValues(initialValues.sales_order, delivery)
|
||||||
) ?? []
|
) ?? []
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}, [initialValues]);
|
}, [initialValues]);
|
||||||
|
|
||||||
const formik = useFormik<SalesOrderFormValues & DeliveryOrderFormValues>({
|
const formik = useFormik<SalesOrderFormValues & DeliveryOrderFormValues>({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
initialValues: formikInitialValues,
|
initialValues: formikInitialValues,
|
||||||
@@ -297,14 +349,7 @@ const MarketingForm = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const grandTotal = useMemo(() => {
|
// ================== FORM REPEATER HANDLER ==================
|
||||||
return formik.values.sales_order.reduce(
|
|
||||||
(total, product) =>
|
|
||||||
total + parseFloat((product.total_price as string) || '0'),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}, [formik.values.sales_order]);
|
|
||||||
|
|
||||||
const createMarketingHandler = async (values: CreateSalesOrderPayload) => {
|
const createMarketingHandler = async (values: CreateSalesOrderPayload) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
console.log(values);
|
console.log(values);
|
||||||
@@ -334,7 +379,6 @@ const MarketingForm = ({
|
|||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createDeliveryHandler = async (values: CreateDeliveryOrderPayload) => {
|
const createDeliveryHandler = async (values: CreateDeliveryOrderPayload) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
console.log(initialValues?.id);
|
console.log(initialValues?.id);
|
||||||
@@ -350,9 +394,7 @@ const MarketingForm = ({
|
|||||||
)
|
)
|
||||||
) ?? []
|
) ?? []
|
||||||
);
|
);
|
||||||
router.push(
|
router.push(`/marketing/detail?marketingId=${initialValues?.id}`);
|
||||||
`/marketing/detail/delivery-orders/edit?marketingId=${initialValues?.id}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (isResponseError(createDeliveryRes)) {
|
if (isResponseError(createDeliveryRes)) {
|
||||||
console.log(createDeliveryRes);
|
console.log(createDeliveryRes);
|
||||||
@@ -360,7 +402,6 @@ const MarketingForm = ({
|
|||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDeliveryHandler = async (values: UpdateDeliveryOrderPayload) => {
|
const updateDeliveryHandler = async (values: UpdateDeliveryOrderPayload) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
console.log(initialValues?.id);
|
console.log(initialValues?.id);
|
||||||
@@ -371,15 +412,18 @@ const MarketingForm = ({
|
|||||||
if (isResponseSuccess(updateDeliveryRes)) {
|
if (isResponseSuccess(updateDeliveryRes)) {
|
||||||
console.log(updateDeliveryRes);
|
console.log(updateDeliveryRes);
|
||||||
toast.success(updateDeliveryRes?.message as string);
|
toast.success(updateDeliveryRes?.message as string);
|
||||||
// router.push(`/marketing/detail?marketingId=${initialValues?.id}`);
|
|
||||||
setDeliveryOrderValues(
|
setDeliveryOrderValues(
|
||||||
updateDeliveryRes.data?.delivery_order?.flatMap((delivery) =>
|
mergeSOwithDO(
|
||||||
DeliveryProductToFieldValues(
|
formik.values.sales_order,
|
||||||
updateDeliveryRes.data?.sales_order,
|
updateDeliveryRes.data?.delivery_order?.flatMap((delivery) =>
|
||||||
delivery
|
DeliveryProductToFieldValues(
|
||||||
)
|
updateDeliveryRes.data?.sales_order,
|
||||||
) ?? []
|
delivery
|
||||||
|
)
|
||||||
|
) ?? []
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
router.push(`/marketing/detail?marketingId=${initialValues?.id}`);
|
||||||
}
|
}
|
||||||
if (isResponseError(updateDeliveryRes)) {
|
if (isResponseError(updateDeliveryRes)) {
|
||||||
console.log(updateDeliveryRes);
|
console.log(updateDeliveryRes);
|
||||||
@@ -388,6 +432,7 @@ const MarketingForm = ({
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ================== MARKETING HANDLER ==================
|
||||||
const deleteMarketingHandler = async () => {
|
const deleteMarketingHandler = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
console.log(initialValues?.id);
|
console.log(initialValues?.id);
|
||||||
@@ -404,28 +449,27 @@ const MarketingForm = ({
|
|||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
router.push('/marketing/sales-orders');
|
router.push('/marketing');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeCustomer = useCallback(
|
const handleChangeCustomer = useCallback(
|
||||||
(val: OptionType | OptionType[] | null) => {
|
(val: OptionType | OptionType[] | null) => {
|
||||||
formik.setFieldValue('customer_id', (val as OptionType)?.value);
|
formik.setFieldValue('customer_id', (val as OptionType)?.value);
|
||||||
formik.setFieldValue('customer', val as OptionType);
|
formik.setFieldValue('customer', val as OptionType);
|
||||||
},
|
},
|
||||||
[formik]
|
[]
|
||||||
);
|
);
|
||||||
|
const handleDelete = useCallback(() => {
|
||||||
|
deleteModal.openModal();
|
||||||
|
}, [deleteModal]);
|
||||||
|
|
||||||
// Repeater Handle
|
// ================== SALES ORDER HANDLER ==================
|
||||||
const handleDeleteSO = useCallback(
|
const handleDeleteSO = useCallback((id: number) => {
|
||||||
(id: number) => {
|
const currentProducts = formik.values.sales_order;
|
||||||
const currentProducts = formik.values.sales_order;
|
formik.setFieldValue(
|
||||||
formik.setFieldValue(
|
'sales_order',
|
||||||
'sales_order',
|
currentProducts.filter((p) => p.id != id)
|
||||||
currentProducts.filter((p) => p.id != id)
|
);
|
||||||
);
|
}, []);
|
||||||
},
|
|
||||||
[formik]
|
|
||||||
);
|
|
||||||
const handleBulkDeleteSO = useCallback(() => {
|
const handleBulkDeleteSO = useCallback(() => {
|
||||||
const currentProducts = formik.values.sales_order;
|
const currentProducts = formik.values.sales_order;
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
@@ -435,10 +479,7 @@ const MarketingForm = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
setRowSOSelection({});
|
setRowSOSelection({});
|
||||||
}, [formik, selectedRowSOIds]);
|
}, [selectedRowSOIds]);
|
||||||
const handleDelete = useCallback(() => {
|
|
||||||
deleteModal.openModal();
|
|
||||||
}, [deleteModal]);
|
|
||||||
const handleAddSOClick = useCallback(() => {
|
const handleAddSOClick = useCallback(() => {
|
||||||
setSelectedMarketingProduct(null);
|
setSelectedMarketingProduct(null);
|
||||||
addSOModal.openModal();
|
addSOModal.openModal();
|
||||||
@@ -446,48 +487,54 @@ const MarketingForm = ({
|
|||||||
const handleAddSubmitSO = useCallback(
|
const handleAddSubmitSO = useCallback(
|
||||||
async (values: SalesOrderProductFormValues) => {
|
async (values: SalesOrderProductFormValues) => {
|
||||||
const currentProducts = formik.values.sales_order;
|
const currentProducts = formik.values.sales_order;
|
||||||
|
|
||||||
const newValues = {
|
const newValues = {
|
||||||
...values,
|
...values,
|
||||||
id: values.id ?? Date.now(),
|
id: values.id ?? Date.now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
formik.setFieldValue('sales_order', [...currentProducts, newValues]);
|
const existingIndex = currentProducts.findIndex(
|
||||||
|
(item) =>
|
||||||
|
item.kandang_id === newValues.kandang_id &&
|
||||||
|
item.product_warehouse_id === newValues.product_warehouse_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let updatedProducts = [];
|
||||||
|
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
// Overwrite
|
||||||
|
updatedProducts = currentProducts.map((item, index) =>
|
||||||
|
index === existingIndex ? newValues : item
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Add new item
|
||||||
|
updatedProducts = [...currentProducts, newValues];
|
||||||
|
}
|
||||||
|
|
||||||
|
formik.setFieldValue('sales_order', updatedProducts);
|
||||||
|
|
||||||
addSOModal.closeModal();
|
addSOModal.closeModal();
|
||||||
},
|
},
|
||||||
[formik, addSOModal]
|
[addSOModal]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDeleteDO = useCallback(
|
// ================== DELIVERY ORDER HANDLER ==================
|
||||||
(id: number) => {
|
|
||||||
const currentProducts = formik.values.delivery_order;
|
|
||||||
setDeliveryOrderValues((prev) => prev.filter((p) => p.id !== id));
|
|
||||||
},
|
|
||||||
[formik]
|
|
||||||
);
|
|
||||||
const handleEditDO = useCallback(
|
const handleEditDO = useCallback(
|
||||||
(id: number) => {
|
(id: number, values?: DeliveryOrderProductFormValues) => {
|
||||||
|
setDeliveryFormState('edit');
|
||||||
const currentProducts = formik.values.delivery_order.find(
|
const currentProducts = formik.values.delivery_order.find(
|
||||||
(product) => product.id == id
|
(product) => product.id == id
|
||||||
);
|
);
|
||||||
setSelectedDeliveryProduct(currentProducts ?? null);
|
setSelectedDeliveryProduct(values ?? currentProducts ?? null);
|
||||||
addDOModal.openModal();
|
addDOModal.openModal();
|
||||||
},
|
},
|
||||||
[formik]
|
[addDOModal]
|
||||||
);
|
);
|
||||||
const handleBulkDeleteDO = useCallback(() => {
|
|
||||||
setDeliveryOrderValues((prev) =>
|
|
||||||
prev.filter((product) => !selectedRowDOIds.includes(product.id ?? -1))
|
|
||||||
);
|
|
||||||
|
|
||||||
setRowDOSelection({});
|
|
||||||
}, [formik, selectedRowDOIds]);
|
|
||||||
|
|
||||||
const handleAddDOClick = useCallback(() => {
|
const handleAddDOClick = useCallback(() => {
|
||||||
|
setDeliveryFormState('add');
|
||||||
setSelectedDeliveryProduct(null);
|
setSelectedDeliveryProduct(null);
|
||||||
addDOModal.openModal();
|
addDOModal.openModal();
|
||||||
}, [addDOModal]);
|
}, [addDOModal]);
|
||||||
|
|
||||||
const handleAddSubmitDO = useCallback(
|
const handleAddSubmitDO = useCallback(
|
||||||
async (values: DeliveryOrderProductFormValues) => {
|
async (values: DeliveryOrderProductFormValues) => {
|
||||||
const newValues = {
|
const newValues = {
|
||||||
@@ -496,23 +543,10 @@ const MarketingForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
setDeliveryOrderValues((prev) => [...prev, newValues]);
|
setDeliveryOrderValues((prev) => [...prev, newValues]);
|
||||||
|
|
||||||
addDOModal.closeModal();
|
addDOModal.closeModal();
|
||||||
|
setSelectedDeliveryProduct(null);
|
||||||
},
|
},
|
||||||
[formik, addDOModal]
|
[addDOModal]
|
||||||
);
|
|
||||||
const handleInputDate = useCallback(
|
|
||||||
(newData: DeliveryOrderProductFormValues) => {
|
|
||||||
setDeliveryOrderValues((prev) => {
|
|
||||||
return prev.map((item) => {
|
|
||||||
if (item.marketing_product_id == newData.marketing_product_id) {
|
|
||||||
return newData;
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
);
|
||||||
const handleUpdateDO = useCallback(
|
const handleUpdateDO = useCallback(
|
||||||
async (id: number, values: DeliveryOrderProductFormValues) => {
|
async (id: number, values: DeliveryOrderProductFormValues) => {
|
||||||
@@ -521,12 +555,11 @@ const MarketingForm = ({
|
|||||||
product.id === id ? { ...product, ...values } : product
|
product.id === id ? { ...product, ...values } : product
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
setSelectedDeliveryProduct(null);
|
|
||||||
addDOModal.closeModal();
|
addDOModal.closeModal();
|
||||||
|
setSelectedDeliveryProduct(null);
|
||||||
},
|
},
|
||||||
[formik, addDOModal]
|
[addDOModal]
|
||||||
);
|
);
|
||||||
// End Repeater Handle
|
|
||||||
|
|
||||||
const memoSalesOrder = formik.values.sales_order;
|
const memoSalesOrder = formik.values.sales_order;
|
||||||
|
|
||||||
@@ -534,6 +567,14 @@ const MarketingForm = ({
|
|||||||
formik.setFieldValue('delivery_order', deliveryOrderValues);
|
formik.setFieldValue('delivery_order', deliveryOrderValues);
|
||||||
}, [deliveryOrderValues, initialValues]);
|
}, [deliveryOrderValues, initialValues]);
|
||||||
|
|
||||||
|
const grandTotal = useMemo(() => {
|
||||||
|
return memoSalesOrder.reduce(
|
||||||
|
(total, product) =>
|
||||||
|
total + parseFloat((product.total_price as string) || '0'),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}, [memoSalesOrder]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<form
|
<form
|
||||||
@@ -545,6 +586,7 @@ const MarketingForm = ({
|
|||||||
title={`${formType == 'add' || formType == 'add_deliver' ? 'Tambah' : 'Edit'} ${formType === 'add_deliver' || formType === 'edit_deliver' ? 'Delivery' : 'Sales'} Order`}
|
title={`${formType == 'add' || formType == 'add_deliver' ? 'Tambah' : 'Edit'} ${formType === 'add_deliver' || formType === 'edit_deliver' ? 'Delivery' : 'Sales'} Order`}
|
||||||
backUrl='/marketing'
|
backUrl='/marketing'
|
||||||
/>
|
/>
|
||||||
|
{/* Input Cutomer And Date */}
|
||||||
<Card
|
<Card
|
||||||
title='Informasi Order'
|
title='Informasi Order'
|
||||||
className={{
|
className={{
|
||||||
@@ -580,28 +622,27 @@ const MarketingForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
{(formType == 'add' || formType == 'edit') && (
|
|
||||||
<Card
|
{/* Input Table Repeater Sales Order */}
|
||||||
title='Informasi Produk'
|
<Card
|
||||||
className={{
|
title='Informasi Produk'
|
||||||
wrapper: 'bg-white w-full',
|
className={{
|
||||||
}}
|
wrapper: 'bg-white w-full',
|
||||||
>
|
}}
|
||||||
{/* <div className='text-blue-500'>{JSON.stringify(initialValues)}</div>
|
>
|
||||||
<div className='text-green-500'>{JSON.stringify(formik.values)}</div>
|
<MemoizedSalesOrderProductTable
|
||||||
<div className='text-red-500'>{JSON.stringify(formik.errors)}</div> */}
|
formType={formType}
|
||||||
<MemoizedSalesOrderProductTable
|
data={memoSalesOrder}
|
||||||
formType={formType}
|
rowSelection={rowSOSelection}
|
||||||
data={memoSalesOrder}
|
setRowSelection={setRowSOSelection}
|
||||||
rowSelection={rowSOSelection}
|
selectedRowIds={selectedRowSOIds}
|
||||||
setRowSelection={setRowSOSelection}
|
onDelete={handleDeleteSO}
|
||||||
selectedRowIds={selectedRowSOIds}
|
onBulkDelete={handleBulkDeleteSO}
|
||||||
onDelete={handleDeleteSO}
|
onAddProductClick={handleAddSOClick}
|
||||||
onBulkDelete={handleBulkDeleteSO}
|
/>
|
||||||
onAddProductClick={handleAddSOClick}
|
</Card>
|
||||||
/>
|
|
||||||
</Card>
|
{/* Input Table Repeater Delivery Order */}
|
||||||
)}
|
|
||||||
{(formType == 'add_deliver' || formType == 'edit_deliver') &&
|
{(formType == 'add_deliver' || formType == 'edit_deliver') &&
|
||||||
initialValues?.sales_order &&
|
initialValues?.sales_order &&
|
||||||
initialValues?.sales_order.length > 0 && (
|
initialValues?.sales_order.length > 0 && (
|
||||||
@@ -611,29 +652,24 @@ const MarketingForm = ({
|
|||||||
wrapper: 'bg-white w-full',
|
wrapper: 'bg-white w-full',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* {JSON.stringify(memoSalesOrder)} */}
|
{/* <div className='text-blue-500'>
|
||||||
{/* <small>{JSON.stringify(memoDeliveryOrder)}</small> */}
|
{JSON.stringify(formik.values)}
|
||||||
{/* <small className='block text-error'>
|
</div>
|
||||||
|
<div className='text-red-500'>
|
||||||
{JSON.stringify(formik.errors)}
|
{JSON.stringify(formik.errors)}
|
||||||
</small> */}
|
</div> */}
|
||||||
<MemoizedDeliveryOrderProductTable
|
<MemoizedDeliveryOrderProductTable
|
||||||
formType={formType}
|
formType={formType}
|
||||||
data={deliveryOrderValues}
|
data={deliveryOrderValues}
|
||||||
salesOrder={memoSalesOrder}
|
|
||||||
rowSelection={rowDOSelection}
|
|
||||||
setRowSelection={setRowDOSelection}
|
|
||||||
selectedRowIds={selectedRowDOIds}
|
|
||||||
onDelete={handleDeleteDO}
|
|
||||||
onEdit={handleEditDO}
|
onEdit={handleEditDO}
|
||||||
onBulkDelete={handleBulkDeleteDO}
|
|
||||||
onAddProductClick={handleAddDOClick}
|
onAddProductClick={handleAddDOClick}
|
||||||
onInputDate={handleInputDate}
|
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Input Notes */}
|
||||||
<div className='grid grid-cols-2 gap-3'>
|
<div className='grid grid-cols-2 gap-3'>
|
||||||
<TextArea
|
<DebouncedTextArea
|
||||||
required
|
required
|
||||||
name='notes'
|
name='notes'
|
||||||
label='Catatan'
|
label='Catatan'
|
||||||
@@ -652,6 +688,8 @@ const MarketingForm = ({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Form Actions */}
|
||||||
<div className='flex flex-row items-start justify-center gap-2 mt-4'>
|
<div className='flex flex-row items-start justify-center gap-2 mt-4'>
|
||||||
<Button type='reset' color='warning' disabled={formik.isSubmitting}>
|
<Button type='reset' color='warning' disabled={formik.isSubmitting}>
|
||||||
Reset
|
Reset
|
||||||
@@ -665,6 +703,8 @@ const MarketingForm = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
{/* Actions button */}
|
||||||
{formType == 'edit' && (
|
{formType == 'edit' && (
|
||||||
<div className='flex flex-row justify-start'>
|
<div className='flex flex-row justify-start'>
|
||||||
<Button
|
<Button
|
||||||
@@ -678,6 +718,8 @@ const MarketingForm = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Modals */}
|
||||||
<Modal
|
<Modal
|
||||||
ref={addSOModal.ref}
|
ref={addSOModal.ref}
|
||||||
closeOnBackdrop
|
closeOnBackdrop
|
||||||
@@ -701,6 +743,7 @@ const MarketingForm = ({
|
|||||||
<MemoizedSalesOrderProductForm
|
<MemoizedSalesOrderProductForm
|
||||||
onSubmitForm={handleAddSubmitSO}
|
onSubmitForm={handleAddSubmitSO}
|
||||||
initialValues={selectedMarketingProduct ?? undefined}
|
initialValues={selectedMarketingProduct ?? undefined}
|
||||||
|
exisitingValues={memoSalesOrder}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -728,7 +771,9 @@ const MarketingForm = ({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<MemoizedDeliveryOrderProductForm
|
<MemoizedDeliveryOrderProductForm
|
||||||
|
formState={deliveryFormState}
|
||||||
salesOrders={initialValues?.sales_order ?? []}
|
salesOrders={initialValues?.sales_order ?? []}
|
||||||
|
exisitingValues={deliveryOrderValues}
|
||||||
onSubmitForm={handleAddSubmitDO}
|
onSubmitForm={handleAddSubmitDO}
|
||||||
initialValues={selectedDeliveryProduct ?? undefined}
|
initialValues={selectedDeliveryProduct ?? undefined}
|
||||||
onUpdateForm={handleUpdateDO}
|
onUpdateForm={handleUpdateDO}
|
||||||
|
|||||||
+2
-18
@@ -1,9 +1,5 @@
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import {
|
import { SalesOrderProductFormValues } from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema';
|
||||||
SalesOrderProductFormValues,
|
|
||||||
SalesOrderProductSchema,
|
|
||||||
} from '../sales-order/SalesOrderProduct.schema';
|
|
||||||
import { de } from 'react-day-picker/locale';
|
|
||||||
|
|
||||||
type DeliveryOrderProductSchemaType = {
|
type DeliveryOrderProductSchemaType = {
|
||||||
id?: number | undefined;
|
id?: number | undefined;
|
||||||
@@ -42,22 +38,10 @@ export const DeliveryOrderProductSchema: Yup.ObjectSchema<DeliveryOrderProductSc
|
|||||||
.min(1, 'Total Penjualan wajib diisi!')
|
.min(1, 'Total Penjualan wajib diisi!')
|
||||||
.required('Total Penjualan wajib diisi!'),
|
.required('Total Penjualan wajib diisi!'),
|
||||||
vehicle_number: Yup.string().required('Nomor Kendaraan wajib diisi!'),
|
vehicle_number: Yup.string().required('Nomor Kendaraan wajib diisi!'),
|
||||||
delivery_date: Yup.string()
|
delivery_date: Yup.string().required('Tanggal Pengiriman wajib diisi!'),
|
||||||
.required('Tanggal Pengiriman wajib diisi!')
|
|
||||||
.nullable()
|
|
||||||
.optional(),
|
|
||||||
do_number: Yup.string().nullable().optional(),
|
do_number: Yup.string().nullable().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type DeliveryOrderProductFormValues = Yup.InferType<
|
export type DeliveryOrderProductFormValues = Yup.InferType<
|
||||||
typeof DeliveryOrderProductSchema
|
typeof DeliveryOrderProductSchema
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// "marketing_product_id": 3,
|
|
||||||
// "qty": 20,
|
|
||||||
// "unit_price": 1000,
|
|
||||||
// "avg_weight": 1.1,
|
|
||||||
// "total_weight": 220,
|
|
||||||
// "total_price": 20000,
|
|
||||||
// "delivery_date": "2025-11-09",
|
|
||||||
// "vehicle_number": "D 4321 XXX"
|
|
||||||
|
|||||||
+99
-59
@@ -10,20 +10,24 @@ import NumberInput from '@/components/input/NumberInput';
|
|||||||
import PatternInput from '@/components/input/PatternInput';
|
import PatternInput from '@/components/input/PatternInput';
|
||||||
import { formatVechicleNumber } from '@/lib/helper';
|
import { formatVechicleNumber } from '@/lib/helper';
|
||||||
import DateInput from '@/components/input/DateInput';
|
import DateInput from '@/components/input/DateInput';
|
||||||
import TextInput from '@/components/input/TextInput';
|
|
||||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||||
import { SalesOrderProductFormValues } from '../sales-order/SalesOrderProduct.schema';
|
|
||||||
import { BaseSalesOrder } from '@/types/api/marketing/marketing';
|
import { BaseSalesOrder } from '@/types/api/marketing/marketing';
|
||||||
import Badge from '@/components/Badge';
|
import Badge from '@/components/Badge';
|
||||||
|
import { SalesProductToFieldValues } from '@/components/pages/marketing/form/MarketingForm';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
const DeliveryOrderProductForm = ({
|
const DeliveryOrderProductForm = ({
|
||||||
|
formState,
|
||||||
salesOrders,
|
salesOrders,
|
||||||
initialValues,
|
initialValues,
|
||||||
|
exisitingValues,
|
||||||
onSubmitForm,
|
onSubmitForm,
|
||||||
onUpdateForm,
|
onUpdateForm,
|
||||||
}: {
|
}: {
|
||||||
|
formState: 'add' | 'edit';
|
||||||
salesOrders: BaseSalesOrder[];
|
salesOrders: BaseSalesOrder[];
|
||||||
initialValues?: DeliveryOrderProductFormValues;
|
initialValues?: DeliveryOrderProductFormValues;
|
||||||
|
exisitingValues?: DeliveryOrderProductFormValues[];
|
||||||
onSubmitForm?: (value: DeliveryOrderProductFormValues) => Promise<void>;
|
onSubmitForm?: (value: DeliveryOrderProductFormValues) => Promise<void>;
|
||||||
onUpdateForm?: (
|
onUpdateForm?: (
|
||||||
id: number,
|
id: number,
|
||||||
@@ -34,13 +38,18 @@ const DeliveryOrderProductForm = ({
|
|||||||
const [selectedProduct, setSelectedProduct] = useState<OptionType | null>(
|
const [selectedProduct, setSelectedProduct] = useState<OptionType | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
const [currentInput, setCurrentInput] = useState<string>('');
|
||||||
|
const salesOrder = salesOrders.find(
|
||||||
|
(item) => item.id === initialValues?.marketing_product_id
|
||||||
|
);
|
||||||
|
|
||||||
const formik = useFormik<DeliveryOrderProductFormValues>({
|
const formik = useFormik<DeliveryOrderProductFormValues>({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
delivery_date: initialValues?.delivery_date || undefined,
|
delivery_date: initialValues?.delivery_date || undefined,
|
||||||
vehicle_number: initialValues?.vehicle_number || undefined,
|
vehicle_number: initialValues?.vehicle_number || undefined,
|
||||||
marketing_product_id: initialValues?.marketing_product_id || undefined,
|
marketing_product_id:
|
||||||
|
salesOrder?.id || initialValues?.marketing_product_id || undefined,
|
||||||
unit_price: initialValues?.unit_price || undefined,
|
unit_price: initialValues?.unit_price || undefined,
|
||||||
total_weight: initialValues?.total_weight || undefined,
|
total_weight: initialValues?.total_weight || undefined,
|
||||||
qty: initialValues?.qty || undefined,
|
qty: initialValues?.qty || undefined,
|
||||||
@@ -48,15 +57,33 @@ const DeliveryOrderProductForm = ({
|
|||||||
total_price: initialValues?.total_price || undefined,
|
total_price: initialValues?.total_price || undefined,
|
||||||
marketing_product: initialValues?.marketing_product || undefined,
|
marketing_product: initialValues?.marketing_product || undefined,
|
||||||
},
|
},
|
||||||
validationSchema: DeliveryOrderProductSchema,
|
isInitialValid: false,
|
||||||
|
validationSchema: Yup.object().shape({
|
||||||
|
...DeliveryOrderProductSchema.fields,
|
||||||
|
|
||||||
|
qty: Yup.lazy((_, context) => {
|
||||||
|
// values diambil aman dari context
|
||||||
|
const { parent } = context;
|
||||||
|
|
||||||
|
const mpId = parent?.marketing_product_id;
|
||||||
|
const selectedSO = salesOrders.find((item) => item.id === mpId);
|
||||||
|
|
||||||
|
const maxQty = selectedSO?.qty ?? Infinity;
|
||||||
|
|
||||||
|
return Yup.number()
|
||||||
|
.min(1, 'Kuantitas wajib diisi!')
|
||||||
|
.max(maxQty, `Maksimal kuantitas adalah ${maxQty}`)
|
||||||
|
.required('Kuantitas wajib diisi!');
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
validateOnChange: true,
|
||||||
validateOnBlur: true,
|
validateOnBlur: true,
|
||||||
validateOnChange: false,
|
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
setFormErrorMessage('');
|
setFormErrorMessage('');
|
||||||
if (initialValues?.id) {
|
if (initialValues?.id) {
|
||||||
await onUpdateForm?.(initialValues.id, values);
|
await onUpdateForm?.(initialValues.id, values);
|
||||||
} else {
|
} else {
|
||||||
await onSubmitForm?.(values);
|
await onUpdateForm?.(values.marketing_product_id as number, values);
|
||||||
}
|
}
|
||||||
handleResetForm();
|
handleResetForm();
|
||||||
},
|
},
|
||||||
@@ -81,6 +108,7 @@ const DeliveryOrderProductForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleBlurField = (field: string) => {
|
const handleBlurField = (field: string) => {
|
||||||
|
setCurrentInput(field);
|
||||||
const { qty, unit_price, total_price, avg_weight, total_weight } =
|
const { qty, unit_price, total_price, avg_weight, total_weight } =
|
||||||
formik.values;
|
formik.values;
|
||||||
|
|
||||||
@@ -101,47 +129,37 @@ const DeliveryOrderProductForm = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const MarketingProductToFieldValues = (
|
const options = exisitingValues
|
||||||
product: BaseSalesOrder
|
?.map((item) => {
|
||||||
): SalesOrderProductFormValues => {
|
if (!Boolean(item.qty)) {
|
||||||
return {
|
return {
|
||||||
id: product.id,
|
value: item.id,
|
||||||
vehicle_number: product.vehicle_number,
|
label: `${item.marketing_product?.product_warehouse?.label} - ${item.marketing_product?.kandang?.label}`,
|
||||||
kandang_id: product.product_warehouse.warehouse.id,
|
} as OptionType;
|
||||||
kandang: {
|
} else {
|
||||||
value: product.product_warehouse.warehouse.id,
|
return null;
|
||||||
label: product.product_warehouse.warehouse.name,
|
}
|
||||||
},
|
})
|
||||||
product_warehouse: {
|
?.filter((item) => item != null) as OptionType[];
|
||||||
value: product.product_warehouse.id,
|
|
||||||
label: product.product_warehouse.product.name,
|
|
||||||
},
|
|
||||||
product_warehouse_id: product.product_warehouse.id,
|
|
||||||
unit_price: product.unit_price,
|
|
||||||
total_weight: product.total_weight,
|
|
||||||
qty: product.qty,
|
|
||||||
avg_weight: product.avg_weight,
|
|
||||||
total_price: product.total_price,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const options = salesOrders.map((item) => ({
|
|
||||||
value: item.id,
|
|
||||||
label: `${item.product_warehouse.product.name} - ${item.product_warehouse.warehouse.name}`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const { setValues: setFormikValues } = formik;
|
const { setValues: setFormikValues } = formik;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialValues) {
|
if (initialValues) {
|
||||||
setFormikValues(initialValues);
|
if (!Boolean(initialValues.qty)) {
|
||||||
const value = salesOrders.find(
|
handleResetForm();
|
||||||
(item) => item.id === initialValues.marketing_product_id
|
} else {
|
||||||
);
|
setFormikValues(initialValues);
|
||||||
setSelectedProduct({
|
// const value = exisitingValues?.find(
|
||||||
value: value?.id,
|
// (item) => item.id === initialValues?.id
|
||||||
label: `${value?.product_warehouse.product.name} - ${value?.product_warehouse.warehouse.name}`,
|
// );
|
||||||
} as OptionType);
|
if (initialValues?.marketing_product_id) {
|
||||||
|
setSelectedProduct({
|
||||||
|
value: initialValues?.id,
|
||||||
|
label: `${initialValues?.marketing_product?.product_warehouse?.label} - ${initialValues?.marketing_product?.kandang?.label}`,
|
||||||
|
} as OptionType);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [initialValues]);
|
}, [initialValues]);
|
||||||
|
|
||||||
@@ -149,17 +167,21 @@ const DeliveryOrderProductForm = ({
|
|||||||
<>
|
<>
|
||||||
<form
|
<form
|
||||||
className='size-full'
|
className='size-full'
|
||||||
onSubmit={formik.handleSubmit}
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleBlurField(currentInput);
|
||||||
|
formik.handleSubmit(e);
|
||||||
|
}}
|
||||||
onReset={handleResetForm}
|
onReset={handleResetForm}
|
||||||
>
|
>
|
||||||
{/* <small className='block text-blue-500'>
|
{/* <small className='block text-blue-500'>
|
||||||
{JSON.stringify(initialValues)}
|
{JSON.stringify(exisitingValues)}
|
||||||
</small>
|
|
||||||
<small className='block text-red-500'>
|
|
||||||
{JSON.stringify(formik.errors)}
|
|
||||||
</small>
|
</small>
|
||||||
<small className='block text-emerald-500'>
|
<small className='block text-emerald-500'>
|
||||||
{JSON.stringify(formik.values)}
|
{JSON.stringify(formik.values)}
|
||||||
|
</small> */}
|
||||||
|
{/* <small className='block text-red-500'>
|
||||||
|
{JSON.stringify(formik.errors)}
|
||||||
</small>
|
</small>
|
||||||
<div className='hidden'>
|
<div className='hidden'>
|
||||||
{JSON.stringify(formik.values.marketing_product)}
|
{JSON.stringify(formik.values.marketing_product)}
|
||||||
@@ -176,14 +198,14 @@ const DeliveryOrderProductForm = ({
|
|||||||
options={options}
|
options={options}
|
||||||
label='Produk'
|
label='Produk'
|
||||||
placeholder='Pilih Produk'
|
placeholder='Pilih Produk'
|
||||||
isDisabled
|
isDisabled={formState == 'edit'}
|
||||||
value={
|
value={
|
||||||
selectedProduct
|
selectedProduct
|
||||||
? ({
|
? ({
|
||||||
value: selectedProduct?.value,
|
value: selectedProduct?.value,
|
||||||
label: salesOrders.find(
|
label: exisitingValues?.find(
|
||||||
(item) => item.id === selectedProduct?.value
|
(item) => item.id === selectedProduct?.value
|
||||||
)?.product_warehouse.product.name,
|
)?.marketing_product?.product_warehouse?.label,
|
||||||
} as OptionType)
|
} as OptionType)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
@@ -191,7 +213,7 @@ const DeliveryOrderProductForm = ({
|
|||||||
const selected = value as OptionType;
|
const selected = value as OptionType;
|
||||||
setSelectedProduct(selected);
|
setSelectedProduct(selected);
|
||||||
|
|
||||||
const so = salesOrders.find(
|
const so = salesOrders?.find(
|
||||||
(item) => item.id === selected?.value
|
(item) => item.id === selected?.value
|
||||||
);
|
);
|
||||||
if (!so) {
|
if (!so) {
|
||||||
@@ -212,7 +234,7 @@ const DeliveryOrderProductForm = ({
|
|||||||
formik.setValues({
|
formik.setValues({
|
||||||
...formik.values,
|
...formik.values,
|
||||||
marketing_product_id: selected.value as number,
|
marketing_product_id: selected.value as number,
|
||||||
marketing_product: MarketingProductToFieldValues(so),
|
marketing_product: SalesProductToFieldValues(so),
|
||||||
qty: formik.values.qty || so.qty,
|
qty: formik.values.qty || so.qty,
|
||||||
unit_price: so.unit_price,
|
unit_price: so.unit_price,
|
||||||
total_price: so.total_price,
|
total_price: so.total_price,
|
||||||
@@ -230,9 +252,9 @@ const DeliveryOrderProductForm = ({
|
|||||||
className={{ badge: 'whitespace-nowrap font-semibold' }}
|
className={{ badge: 'whitespace-nowrap font-semibold' }}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
salesOrders.find(
|
exisitingValues?.find(
|
||||||
(item) => item.id === selectedProduct?.value
|
(item) => item.id === selectedProduct?.value
|
||||||
)?.product_warehouse?.warehouse?.name
|
)?.marketing_product?.kandang?.label
|
||||||
}
|
}
|
||||||
</Badge>
|
</Badge>
|
||||||
)
|
)
|
||||||
@@ -254,6 +276,9 @@ const DeliveryOrderProductForm = ({
|
|||||||
}
|
}
|
||||||
errorMessage={formik.errors.delivery_date}
|
errorMessage={formik.errors.delivery_date}
|
||||||
placeholder='Pilih Tanggal'
|
placeholder='Pilih Tanggal'
|
||||||
|
className={{
|
||||||
|
inputWrapper: 'bg-white',
|
||||||
|
}}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -278,7 +303,10 @@ const DeliveryOrderProductForm = ({
|
|||||||
label='Kuantitas'
|
label='Kuantitas'
|
||||||
name='qty'
|
name='qty'
|
||||||
value={formik.values.qty}
|
value={formik.values.qty}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('qty')}
|
onBlur={() => handleBlurField('qty')}
|
||||||
isError={Boolean(formik.errors.qty)}
|
isError={Boolean(formik.errors.qty)}
|
||||||
errorMessage={formik.errors.qty}
|
errorMessage={formik.errors.qty}
|
||||||
@@ -290,7 +318,10 @@ const DeliveryOrderProductForm = ({
|
|||||||
label='Avg. Bobot (Kg)'
|
label='Avg. Bobot (Kg)'
|
||||||
name='avg_weight'
|
name='avg_weight'
|
||||||
value={formik.values.avg_weight}
|
value={formik.values.avg_weight}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('avg_weight')}
|
onBlur={() => handleBlurField('avg_weight')}
|
||||||
isError={Boolean(formik.errors.avg_weight)}
|
isError={Boolean(formik.errors.avg_weight)}
|
||||||
errorMessage={formik.errors.avg_weight}
|
errorMessage={formik.errors.avg_weight}
|
||||||
@@ -302,7 +333,10 @@ const DeliveryOrderProductForm = ({
|
|||||||
label='Harga Satuan (Rp)'
|
label='Harga Satuan (Rp)'
|
||||||
name='unit_price'
|
name='unit_price'
|
||||||
value={formik.values.unit_price}
|
value={formik.values.unit_price}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('unit_price')}
|
onBlur={() => handleBlurField('unit_price')}
|
||||||
isError={Boolean(formik.errors.unit_price)}
|
isError={Boolean(formik.errors.unit_price)}
|
||||||
errorMessage={formik.errors.unit_price}
|
errorMessage={formik.errors.unit_price}
|
||||||
@@ -314,7 +348,10 @@ const DeliveryOrderProductForm = ({
|
|||||||
label='Total Bobot (Kg)'
|
label='Total Bobot (Kg)'
|
||||||
name='total_weight'
|
name='total_weight'
|
||||||
value={formik.values.total_weight}
|
value={formik.values.total_weight}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('total_weight')}
|
onBlur={() => handleBlurField('total_weight')}
|
||||||
isError={Boolean(formik.errors.total_weight)}
|
isError={Boolean(formik.errors.total_weight)}
|
||||||
errorMessage={formik.errors.total_weight}
|
errorMessage={formik.errors.total_weight}
|
||||||
@@ -326,7 +363,10 @@ const DeliveryOrderProductForm = ({
|
|||||||
label='Total Penjualan (Rp)'
|
label='Total Penjualan (Rp)'
|
||||||
name='total_price'
|
name='total_price'
|
||||||
value={formik.values.total_price}
|
value={formik.values.total_price}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('total_price')}
|
onBlur={() => handleBlurField('total_price')}
|
||||||
isError={Boolean(formik.errors.total_price)}
|
isError={Boolean(formik.errors.total_price)}
|
||||||
errorMessage={formik.errors.total_price}
|
errorMessage={formik.errors.total_price}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
SalesOrderProductFormValues,
|
SalesOrderProductFormValues,
|
||||||
SalesOrderProductSchema,
|
SalesOrderProductSchema,
|
||||||
} from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema';
|
} from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema';
|
||||||
import { RefObject, useState } from 'react';
|
import { RefObject, useMemo, useState } from 'react';
|
||||||
import SelectInput, {
|
import SelectInput, {
|
||||||
OptionType,
|
OptionType,
|
||||||
useSelect,
|
useSelect,
|
||||||
@@ -23,13 +23,16 @@ import Alert from '@/components/Alert';
|
|||||||
|
|
||||||
const SalesOrderProductForm = ({
|
const SalesOrderProductForm = ({
|
||||||
initialValues,
|
initialValues,
|
||||||
|
exisitingValues,
|
||||||
onSubmitForm,
|
onSubmitForm,
|
||||||
}: {
|
}: {
|
||||||
initialValues?: SalesOrderProductFormValues;
|
initialValues?: SalesOrderProductFormValues;
|
||||||
|
exisitingValues?: SalesOrderProductFormValues[];
|
||||||
modalRef?: RefObject<HTMLDialogElement | null>;
|
modalRef?: RefObject<HTMLDialogElement | null>;
|
||||||
onSubmitForm?: (value: SalesOrderProductFormValues) => Promise<void>;
|
onSubmitForm?: (value: SalesOrderProductFormValues) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
const [formErrorMessage, setFormErrorMessage] = useState('');
|
const [formErrorMessage, setFormErrorMessage] = useState('');
|
||||||
|
const [currentInput, setCurrentInput] = useState<string>('');
|
||||||
|
|
||||||
const formik = useFormik<SalesOrderProductFormValues>({
|
const formik = useFormik<SalesOrderProductFormValues>({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
@@ -51,6 +54,8 @@ const SalesOrderProductForm = ({
|
|||||||
onSubmitForm?.(values);
|
onSubmitForm?.(values);
|
||||||
handleResetForm();
|
handleResetForm();
|
||||||
},
|
},
|
||||||
|
validateOnBlur: true,
|
||||||
|
isInitialValid: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -72,6 +77,15 @@ const SalesOrderProductForm = ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const productOptionsFiltered = useMemo(() => {
|
||||||
|
return warehouseSourceOptions.filter(
|
||||||
|
(product) =>
|
||||||
|
!exisitingValues
|
||||||
|
?.map((item) => item.product_warehouse_id)
|
||||||
|
.includes(product.value)
|
||||||
|
);
|
||||||
|
}, [warehouseSourceOptions, exisitingValues]);
|
||||||
|
|
||||||
const kandangChangeHandler = (val: OptionType | OptionType[] | null) => {
|
const kandangChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
formik.setFieldValue('kandang', val as OptionType);
|
formik.setFieldValue('kandang', val as OptionType);
|
||||||
formik.setFieldValue('kandang_id', (val as OptionType)?.value);
|
formik.setFieldValue('kandang_id', (val as OptionType)?.value);
|
||||||
@@ -115,6 +129,7 @@ const SalesOrderProductForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleBlurField = (field: string) => {
|
const handleBlurField = (field: string) => {
|
||||||
|
setCurrentInput(field);
|
||||||
const { qty, unit_price, total_price, avg_weight, total_weight } =
|
const { qty, unit_price, total_price, avg_weight, total_weight } =
|
||||||
formik.values;
|
formik.values;
|
||||||
|
|
||||||
@@ -151,7 +166,11 @@ const SalesOrderProductForm = ({
|
|||||||
<>
|
<>
|
||||||
<form
|
<form
|
||||||
className='size-full'
|
className='size-full'
|
||||||
onSubmit={formik.handleSubmit}
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleBlurField(currentInput);
|
||||||
|
formik.handleSubmit(e);
|
||||||
|
}}
|
||||||
onReset={handleResetForm}
|
onReset={handleResetForm}
|
||||||
>
|
>
|
||||||
{formErrorMessage && (
|
{formErrorMessage && (
|
||||||
@@ -200,12 +219,18 @@ const SalesOrderProductForm = ({
|
|||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
label='Produk'
|
label='Produk'
|
||||||
options={warehouseSourceOptions}
|
options={productOptionsFiltered}
|
||||||
isLoading={isLoadingWarehouseSourceOptions}
|
isLoading={isLoadingWarehouseSourceOptions}
|
||||||
value={formik.values.product_warehouse}
|
value={formik.values.product_warehouse}
|
||||||
onChange={warehouseChangeHandler}
|
onChange={warehouseChangeHandler}
|
||||||
isClearable
|
isClearable
|
||||||
placeholder='Pilih Kandang Terlebih Dahulu'
|
placeholder={
|
||||||
|
formik.values.kandang_id
|
||||||
|
? productOptionsFiltered.length == 0
|
||||||
|
? 'Tidak ada produk yang tersedia'
|
||||||
|
: 'Pilih produk'
|
||||||
|
: 'Pilih Kandang Terlebih Dahulu'
|
||||||
|
}
|
||||||
isDisabled={!formik.values.kandang_id}
|
isDisabled={!formik.values.kandang_id}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.product_warehouse_id &&
|
formik.touched.product_warehouse_id &&
|
||||||
@@ -218,7 +243,10 @@ const SalesOrderProductForm = ({
|
|||||||
label='Kuantitas'
|
label='Kuantitas'
|
||||||
name='qty'
|
name='qty'
|
||||||
value={formik.values.qty}
|
value={formik.values.qty}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('qty')}
|
onBlur={() => handleBlurField('qty')}
|
||||||
isError={formik.touched.qty && Boolean(formik.errors.qty)}
|
isError={formik.touched.qty && Boolean(formik.errors.qty)}
|
||||||
errorMessage={formik.errors.qty}
|
errorMessage={formik.errors.qty}
|
||||||
@@ -229,7 +257,10 @@ const SalesOrderProductForm = ({
|
|||||||
label='Avg. Bobot (Kg)'
|
label='Avg. Bobot (Kg)'
|
||||||
name='avg_weight'
|
name='avg_weight'
|
||||||
value={formik.values.avg_weight}
|
value={formik.values.avg_weight}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('avg_weight')}
|
onBlur={() => handleBlurField('avg_weight')}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.avg_weight && Boolean(formik.errors.avg_weight)
|
formik.touched.avg_weight && Boolean(formik.errors.avg_weight)
|
||||||
@@ -242,7 +273,10 @@ const SalesOrderProductForm = ({
|
|||||||
label='Harga Satuan (Rp)'
|
label='Harga Satuan (Rp)'
|
||||||
name='unit_price'
|
name='unit_price'
|
||||||
value={formik.values.unit_price}
|
value={formik.values.unit_price}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('unit_price')}
|
onBlur={() => handleBlurField('unit_price')}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.unit_price && Boolean(formik.errors.unit_price)
|
formik.touched.unit_price && Boolean(formik.errors.unit_price)
|
||||||
@@ -255,7 +289,10 @@ const SalesOrderProductForm = ({
|
|||||||
label='Total Bobot (Kg)'
|
label='Total Bobot (Kg)'
|
||||||
name='total_weight'
|
name='total_weight'
|
||||||
value={formik.values.total_weight}
|
value={formik.values.total_weight}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('total_weight')}
|
onBlur={() => handleBlurField('total_weight')}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.total_weight && Boolean(formik.errors.total_weight)
|
formik.touched.total_weight && Boolean(formik.errors.total_weight)
|
||||||
@@ -268,7 +305,10 @@ const SalesOrderProductForm = ({
|
|||||||
label='Total Penjualan (Rp)'
|
label='Total Penjualan (Rp)'
|
||||||
name='total_price'
|
name='total_price'
|
||||||
value={formik.values.total_price}
|
value={formik.values.total_price}
|
||||||
onChange={formik.handleChange}
|
onChange={(e) => {
|
||||||
|
formik.handleChange(e);
|
||||||
|
setCurrentInput(e.target.name);
|
||||||
|
}}
|
||||||
onBlur={() => handleBlurField('total_price')}
|
onBlur={() => handleBlurField('total_price')}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.total_price && Boolean(formik.errors.total_price)
|
formik.touched.total_price && Boolean(formik.errors.total_price)
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import { DeliveryOrderProductFormValues } from '../repeater/delivery-order/DeliverOrderProduct.schema';
|
import { DeliveryOrderProductFormValues } from '@/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.schema';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import * as TanStack from '@tanstack/react-table';
|
import * as TanStack from '@tanstack/react-table';
|
||||||
import { useMemo, useRef } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
|
||||||
import {
|
import {
|
||||||
cn,
|
cn,
|
||||||
formatCurrency,
|
formatCurrency,
|
||||||
@@ -12,49 +11,24 @@ import {
|
|||||||
formatNumber,
|
formatNumber,
|
||||||
formatVechicleNumber,
|
formatVechicleNumber,
|
||||||
} from '@/lib/helper';
|
} from '@/lib/helper';
|
||||||
import { SalesOrderProductFormValues } from '../repeater/sales-order/SalesOrderProduct.schema';
|
|
||||||
import DateInput from '@/components/input/DateInput';
|
|
||||||
|
|
||||||
type DeliveryOrderProductTableProps = {
|
type DeliveryOrderProductTableProps = {
|
||||||
data: DeliveryOrderProductFormValues[];
|
data: DeliveryOrderProductFormValues[];
|
||||||
salesOrder: SalesOrderProductFormValues[];
|
|
||||||
formType?: 'add' | 'edit' | 'add_deliver' | 'edit_deliver';
|
formType?: 'add' | 'edit' | 'add_deliver' | 'edit_deliver';
|
||||||
rowSelection: Record<string, boolean>;
|
|
||||||
setRowSelection: React.Dispatch<
|
|
||||||
React.SetStateAction<Record<string, boolean>>
|
|
||||||
>;
|
|
||||||
selectedRowIds: number[];
|
|
||||||
onDelete: (id: number) => void;
|
|
||||||
onEdit: (id: number) => void;
|
onEdit: (id: number) => void;
|
||||||
onBulkDelete: () => void;
|
|
||||||
onAddProductClick: () => void;
|
onAddProductClick: () => void;
|
||||||
onInputDate: (data: DeliveryOrderProductFormValues) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeliveryOrderProductTable = ({
|
const DeliveryOrderProductTable = ({
|
||||||
data,
|
data,
|
||||||
salesOrder,
|
|
||||||
formType,
|
formType,
|
||||||
rowSelection,
|
|
||||||
setRowSelection,
|
|
||||||
selectedRowIds,
|
|
||||||
onDelete,
|
|
||||||
onEdit,
|
onEdit,
|
||||||
onBulkDelete,
|
|
||||||
onAddProductClick,
|
onAddProductClick,
|
||||||
onInputDate,
|
|
||||||
}: DeliveryOrderProductTableProps) => {
|
}: DeliveryOrderProductTableProps) => {
|
||||||
const onDeleteRef = useRef(onDelete);
|
const onEditRef = useRef(onEdit);
|
||||||
const onEditRef = useRef(onDelete);
|
|
||||||
onDeleteRef.current = onDelete;
|
|
||||||
onEditRef.current = onEdit;
|
onEditRef.current = onEdit;
|
||||||
|
|
||||||
const canAddData = salesOrder.reduce((acc, curr) => {
|
const canAddData = data.filter((item) => !Boolean(item.qty));
|
||||||
const deliveredQty = data.filter(
|
|
||||||
(deliveryItem) => deliveryItem.marketing_product_id == curr.id
|
|
||||||
);
|
|
||||||
return acc && deliveredQty.length != salesOrder.length;
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
const cols = [
|
const cols = [
|
||||||
@@ -93,54 +67,23 @@ const DeliveryOrderProductTable = ({
|
|||||||
{
|
{
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) => row.do_number,
|
accessorFn: (row: DeliveryOrderProductFormValues) => row.do_number,
|
||||||
header: 'No. Pengiriman',
|
header: 'No. Pengiriman',
|
||||||
cell: (
|
|
||||||
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
|
||||||
) => <div>{props.row.original.do_number}</div>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
|
||||||
row.delivery_date
|
|
||||||
? formatDate(row.delivery_date as string, 'DD MMM YYYY')
|
|
||||||
: '-',
|
|
||||||
header: 'Tanggal Delivery',
|
|
||||||
cell: (
|
cell: (
|
||||||
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
||||||
) => (
|
) => (
|
||||||
<>
|
<>
|
||||||
{formType == 'add_deliver' && (
|
{props.row.original.do_number ? props.row.original.do_number : '-'}
|
||||||
<DateInput
|
|
||||||
name={`delivery_date_${props.row.original.marketing_product_id}`}
|
|
||||||
className={{
|
|
||||||
input: 'p-0',
|
|
||||||
inputWrapper: 'py-1 px-3 h-fit w-fit bg-white',
|
|
||||||
wrapper: 'p-0',
|
|
||||||
}}
|
|
||||||
value={
|
|
||||||
props.row.original.delivery_date
|
|
||||||
? formatDate(props.row.original.delivery_date, 'yyyy-MM-DD')
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onChange={(val) => {
|
|
||||||
onInputDate({
|
|
||||||
...props.row.original,
|
|
||||||
delivery_date: val.target.value,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{formType == 'edit_deliver' &&
|
|
||||||
formatDate(
|
|
||||||
props.row.original.delivery_date as string,
|
|
||||||
'DD MMM YYYY'
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
accessorFn: (row: DeliveryOrderProductFormValues) => row.vehicle_number,
|
||||||
formatVechicleNumber(row.vehicle_number as string),
|
|
||||||
header: 'No. Polisi',
|
header: 'No. Polisi',
|
||||||
|
cell: (
|
||||||
|
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
||||||
|
) =>
|
||||||
|
props.row.original.vehicle_number
|
||||||
|
? formatVechicleNumber(props.row.original.vehicle_number as string)
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
||||||
@@ -154,29 +97,77 @@ const DeliveryOrderProductTable = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
||||||
formatCurrency(parseFloat(row.unit_price as string)),
|
row.delivery_date
|
||||||
|
? formatDate(row.delivery_date as string, 'DD MMM YYYY')
|
||||||
|
: '-',
|
||||||
|
header: 'Tanggal Delivery',
|
||||||
|
cell: (
|
||||||
|
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
||||||
|
) =>
|
||||||
|
props.row.original.delivery_date
|
||||||
|
? formatDate(
|
||||||
|
props.row.original.delivery_date as string,
|
||||||
|
'DD MMM YYYY'
|
||||||
|
)
|
||||||
|
: '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorFn: (row: DeliveryOrderProductFormValues) => row.unit_price,
|
||||||
header: 'Harga Satuan (Rp)',
|
header: 'Harga Satuan (Rp)',
|
||||||
|
cell: (
|
||||||
|
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
||||||
|
) =>
|
||||||
|
props.row.original.unit_price
|
||||||
|
? formatCurrency(
|
||||||
|
parseFloat(props.row.original.unit_price as string)
|
||||||
|
)
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
accessorFn: (row: DeliveryOrderProductFormValues) => row.total_weight,
|
||||||
formatNumber(parseFloat(row.total_weight as string)),
|
|
||||||
header: 'Total Bobot (Kg)',
|
header: 'Total Bobot (Kg)',
|
||||||
|
cell: (
|
||||||
|
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
||||||
|
) =>
|
||||||
|
props.row.original.total_weight
|
||||||
|
? formatNumber(
|
||||||
|
parseFloat(props.row.original.total_weight as string)
|
||||||
|
)
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
accessorFn: (row: DeliveryOrderProductFormValues) => row.qty,
|
||||||
formatNumber(parseFloat(row.qty as string)),
|
|
||||||
header: 'Kuantitas',
|
header: 'Kuantitas',
|
||||||
|
cell: (
|
||||||
|
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
||||||
|
) =>
|
||||||
|
props.row.original.qty
|
||||||
|
? formatNumber(parseFloat(props.row.original.qty as string))
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
accessorFn: (row: DeliveryOrderProductFormValues) => row.avg_weight,
|
||||||
formatNumber(parseFloat(row.avg_weight as string)),
|
|
||||||
header: 'Avg. Bobot (Kg)',
|
header: 'Avg. Bobot (Kg)',
|
||||||
|
cell: (
|
||||||
|
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
||||||
|
) =>
|
||||||
|
props.row.original.avg_weight
|
||||||
|
? formatNumber(parseFloat(props.row.original.avg_weight as string))
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row: DeliveryOrderProductFormValues) =>
|
accessorFn: (row: DeliveryOrderProductFormValues) => row.total_price,
|
||||||
formatCurrency(parseFloat(row.total_price as string)),
|
|
||||||
header: 'Total Penjualan (Rp)',
|
header: 'Total Penjualan (Rp)',
|
||||||
|
cell: (
|
||||||
|
props: TanStack.CellContext<DeliveryOrderProductFormValues, unknown>
|
||||||
|
) =>
|
||||||
|
props.row.original.total_price
|
||||||
|
? formatCurrency(
|
||||||
|
parseFloat(props.row.original.total_price as string)
|
||||||
|
)
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
header: 'Aksi',
|
header: 'Aksi',
|
||||||
cell: (
|
cell: (
|
||||||
@@ -184,44 +175,45 @@ const DeliveryOrderProductTable = ({
|
|||||||
) => (
|
) => (
|
||||||
<div className='flex flex-row gap-1 items-center justify-end h-full mt-2'>
|
<div className='flex flex-row gap-1 items-center justify-end h-full mt-2'>
|
||||||
<>
|
<>
|
||||||
<Button
|
{props.row.original.qty && (
|
||||||
color='warning'
|
<Button
|
||||||
className='px-2 py-1 text-sm'
|
color='warning'
|
||||||
onClick={() =>
|
className='px-2 py-1 text-sm'
|
||||||
onEditRef.current(props.row.original.id as number)
|
onClick={() =>
|
||||||
}
|
onEditRef.current(props.row.original.id as number)
|
||||||
type='button'
|
}
|
||||||
>
|
type='button'
|
||||||
<Icon icon='mdi:edit' width={16} height={16} /> Edit
|
>
|
||||||
</Button>
|
<Icon icon='mdi:edit' width={16} height={16} /> Edit
|
||||||
{/* <Button
|
</Button>
|
||||||
color='error'
|
)}
|
||||||
className='p-1'
|
{!props.row.original.qty && '-'}
|
||||||
onClick={() =>
|
{/* {formType == 'add_deliver' && (
|
||||||
onDeleteRef.current(props.row.original.id as number)
|
<Button
|
||||||
}
|
color='error'
|
||||||
type='button'
|
className='p-1'
|
||||||
>
|
onClick={() =>
|
||||||
<Icon icon='mdi:trash' width={16} height={16} />
|
onDeleteRef.current(props.row.original.id as number)
|
||||||
</Button> */}
|
}
|
||||||
|
type='button'
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:trash' width={16} height={16} />
|
||||||
|
</Button>
|
||||||
|
)} */}
|
||||||
</>
|
</>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if (formType == 'add_deliver') {
|
if (formType == 'add_deliver') {
|
||||||
return cols.filter(
|
return cols.filter((col) => col.header != 'No. Pengiriman');
|
||||||
(col) => col.header != 'Aksi' && col.header != 'No. Pengiriman'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return cols;
|
return cols;
|
||||||
}, [formType, onInputDate, onEditRef]);
|
}, [formType, onEditRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Table<DeliveryOrderProductFormValues>
|
<Table<DeliveryOrderProductFormValues>
|
||||||
rowSelection={rowSelection}
|
|
||||||
setRowSelection={setRowSelection}
|
|
||||||
data={data}
|
data={data}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
className={{
|
className={{
|
||||||
@@ -246,17 +238,17 @@ const DeliveryOrderProductTable = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div className='flex flex-row gap-3 mt-3'>
|
<div className='flex flex-row gap-3 mt-3'>
|
||||||
{/* <Button
|
<Button
|
||||||
type='button'
|
type='button'
|
||||||
variant='outline'
|
variant='outline'
|
||||||
className='justify-start w-fit py-1 text-sm'
|
className='justify-start w-fit py-1 text-sm'
|
||||||
onClick={onAddProductClick}
|
onClick={onAddProductClick}
|
||||||
// disabled={!canAddData}
|
disabled={!canAddData}
|
||||||
>
|
>
|
||||||
<Icon icon='mdi:plus' width={16} height={16} />
|
<Icon icon='mdi:plus' width={16} height={16} />
|
||||||
Tambah Pengiriman
|
Tambah Pengiriman
|
||||||
</Button> */}
|
</Button>
|
||||||
{selectedRowIds.length > 0 && (
|
{/* {selectedRowIds.length > 0 && (
|
||||||
<Button
|
<Button
|
||||||
type='button'
|
type='button'
|
||||||
variant='outline'
|
variant='outline'
|
||||||
@@ -271,7 +263,7 @@ const DeliveryOrderProductTable = ({
|
|||||||
: ''}{' '}
|
: ''}{' '}
|
||||||
Pengiriman
|
Pengiriman
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import CheckboxInput from '@/components/input/CheckboxInput';
|
|||||||
|
|
||||||
type SalesOrderProductTableProps = {
|
type SalesOrderProductTableProps = {
|
||||||
data: SalesOrderProductFormValues[];
|
data: SalesOrderProductFormValues[];
|
||||||
formType?: 'add' | 'edit' | 'deliver';
|
formType: 'add' | 'edit' | 'add_deliver' | 'edit_deliver';
|
||||||
rowSelection: Record<string, boolean>;
|
rowSelection: Record<string, boolean>;
|
||||||
setRowSelection: React.Dispatch<
|
setRowSelection: React.Dispatch<
|
||||||
React.SetStateAction<Record<string, boolean>>
|
React.SetStateAction<Record<string, boolean>>
|
||||||
@@ -140,7 +140,7 @@ const SalesOrderProductTable = ({
|
|||||||
setRowSelection={setRowSelection}
|
setRowSelection={setRowSelection}
|
||||||
data={data}
|
data={data}
|
||||||
columns={
|
columns={
|
||||||
formType == 'deliver'
|
formType == 'add_deliver' || formType == 'edit_deliver'
|
||||||
? columns.filter(
|
? columns.filter(
|
||||||
(col) => col.header != 'Aksi' && col.id != 'select'
|
(col) => col.header != 'Aksi' && col.id != 'select'
|
||||||
)
|
)
|
||||||
@@ -167,7 +167,7 @@ const SalesOrderProductTable = ({
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{formType != 'deliver' && (
|
{formType != 'add_deliver' && formType != 'edit_deliver' && (
|
||||||
<div className='flex flex-row gap-3 mt-3'>
|
<div className='flex flex-row gap-3 mt-3'>
|
||||||
<Button
|
<Button
|
||||||
type='button'
|
type='button'
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
ChickinFormValues,
|
ChickinFormValues,
|
||||||
ChickinRequestFormValues,
|
ChickinRequestFormValues,
|
||||||
ChickinSchema,
|
ChickinSchema,
|
||||||
} from '../ChickinForm.schema';
|
} from '@/components/pages/production/chickin/form/ChickinForm.schema';
|
||||||
import DateInput from '@/components/input/DateInput';
|
import DateInput from '@/components/input/DateInput';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import Button from '../Button';
|
import Button from '@/components/Button';
|
||||||
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
||||||
|
|
||||||
interface TableRowOptionsProps {
|
interface TableRowOptionsProps {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import SelectInput from '../input/SelectInput';
|
import SelectInput from '@/components/input/SelectInput';
|
||||||
|
|
||||||
export interface OptionType {
|
export interface OptionType {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -9,15 +9,18 @@ interface TableRowSizeSelectorProps {
|
|||||||
value: number;
|
value: number;
|
||||||
onChange: (val: OptionType | OptionType[] | null) => void;
|
onChange: (val: OptionType | OptionType[] | null) => void;
|
||||||
options: OptionType[];
|
options: OptionType[];
|
||||||
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TableRowSizeSelector = ({
|
export const TableRowSizeSelector = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
options,
|
options,
|
||||||
|
children,
|
||||||
}: TableRowSizeSelectorProps) => {
|
}: TableRowSizeSelectorProps) => {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-row justify-end'>
|
<div className='flex flex-row gap-3 items-end justify-end'>
|
||||||
|
{children}
|
||||||
<SelectInput
|
<SelectInput
|
||||||
label='Baris'
|
label='Baris'
|
||||||
options={options}
|
options={options}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import Button from '../Button';
|
import Button from '@/components/Button';
|
||||||
import DebouncedTextInput from '../input/DebouncedTextInput';
|
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||||
|
|
||||||
interface TableToolbarProps {
|
interface TableToolbarProps {
|
||||||
addButton?: {
|
addButton?: {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
CreateChickinPayload,
|
CreateChickinPayload,
|
||||||
UpdateChickinPayload,
|
UpdateChickinPayload,
|
||||||
} from '@/types/api/production/chickin';
|
} from '@/types/api/production/chickin';
|
||||||
import { BaseApiService } from '../base';
|
import { BaseApiService } from '@/services/api/base';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
import { httpClient } from '@/services/http/client';
|
import { httpClient } from '@/services/http/client';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
ProjectFlock,
|
ProjectFlock,
|
||||||
UpdateProjectFlockPayload,
|
UpdateProjectFlockPayload,
|
||||||
} from '@/types/api/production/project-flock';
|
} from '@/types/api/production/project-flock';
|
||||||
import { BaseApiService } from '../base';
|
import { BaseApiService } from '@/services/api/base';
|
||||||
import {
|
import {
|
||||||
BaseApiResponse,
|
BaseApiResponse,
|
||||||
BaseGroupedApproval,
|
BaseGroupedApproval,
|
||||||
@@ -120,7 +120,7 @@ export class ProjectFlockService extends BaseApiService<
|
|||||||
| undefined
|
| undefined
|
||||||
> {
|
> {
|
||||||
try {
|
try {
|
||||||
const path = `${this.basePath}/location/${locationId.toString()}/periods`;
|
const path = `${this.basePath}/locations/${locationId.toString()}/periods`;
|
||||||
return await httpClient<
|
return await httpClient<
|
||||||
SuccessApiResponse<
|
SuccessApiResponse<
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import { Product } from '@/types/api/master-data/product';
|
import { Product } from '@/types/api/master-data/product';
|
||||||
import { BaseMetadata } from '../base-metadata';
|
|
||||||
import { Warehouse } from '@/types/api/master-data/warehouse';
|
import { Warehouse } from '@/types/api/master-data/warehouse';
|
||||||
|
import { BaseMetadata } from '@/types/api/api-general';
|
||||||
|
|
||||||
export type BaseInventoryAdjustment = {
|
export type BaseInventoryAdjustment = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import {
|
|||||||
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
|
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
|
||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
import { Kandang } from '@/types/api/master-data/kandang';
|
||||||
import { id } from 'react-day-picker/locale';
|
import { id } from 'react-day-picker/locale';
|
||||||
import { Warehouse } from '../master-data/warehouse';
|
import { Warehouse } from '@/types/api/master-data/warehouse';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Data Response
|
* Base Data Response
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
import { Kandang } from '@/type/master-data/kandang';
|
import { Kandang } from '@/type/master-data/kandang';
|
||||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||||
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
|
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
|
||||||
import { Supplier } from '../master-data/supplier';
|
import { Supplier } from '@/types/api/master-data/supplier';
|
||||||
import { BaseApproval } from '../api-general';
|
import { BaseApproval } from '@/types/api/api-general';
|
||||||
|
|
||||||
export type BaseProjectFlockKandang = {
|
export type BaseProjectFlockKandang = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user