diff --git a/src/app/marketing/sales-orders/add/page.tsx b/src/app/marketing/sales-orders/add/page.tsx
index e60085ef..8d64e66d 100644
--- a/src/app/marketing/sales-orders/add/page.tsx
+++ b/src/app/marketing/sales-orders/add/page.tsx
@@ -1,4 +1,4 @@
-import SalesForm from '@/components/pages/marketing/sales-orders/form/SalesForm';
+import SalesForm from '@/components/pages/marketing/form/MarketingForm';
const AddSalesOrder = () => {
return (
diff --git a/src/app/marketing/sales-orders/detail/edit/page.tsx b/src/app/marketing/sales-orders/detail/edit/page.tsx
index 660468f3..2b41f144 100644
--- a/src/app/marketing/sales-orders/detail/edit/page.tsx
+++ b/src/app/marketing/sales-orders/detail/edit/page.tsx
@@ -1,6 +1,6 @@
'use client';
-import SalesForm from '@/components/pages/marketing/sales-orders/form/SalesForm';
+import SalesForm from '@/components/pages/marketing/form/MarketingForm';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { MarketingApi } from '@/services/api/marketing/marketing';
import { useRouter, useSearchParams } from 'next/navigation';
diff --git a/src/app/marketing/sales-orders/detail/page.tsx b/src/app/marketing/sales-orders/detail/page.tsx
index 0ac71f56..ca08f41b 100644
--- a/src/app/marketing/sales-orders/detail/page.tsx
+++ b/src/app/marketing/sales-orders/detail/page.tsx
@@ -1,6 +1,6 @@
'use client';
-import SalesOrderDetail from '@/components/pages/marketing/sales-orders/detail/SalesOrderDetail';
+import SalesOrderDetail from '@/components/pages/marketing/detail/MarketingDetail';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { MarketingApi } from '@/services/api/marketing/marketing';
import { useRouter, useSearchParams } from 'next/navigation';
diff --git a/src/app/marketing/sales-orders/page.tsx b/src/app/marketing/sales-orders/page.tsx
index 3494b6a1..427db57d 100644
--- a/src/app/marketing/sales-orders/page.tsx
+++ b/src/app/marketing/sales-orders/page.tsx
@@ -1,4 +1,4 @@
-import SalesOrderTable from '@/components/pages/marketing/sales-orders/SalesOrderTable';
+import SalesOrderTable from '@/components/pages/marketing/MarketingTable';
const SalesOrder = () => {
return (
diff --git a/src/components/pages/marketing/sales-orders/SalesOrderTable.tsx b/src/components/pages/marketing/MarketingTable.tsx
similarity index 94%
rename from src/components/pages/marketing/sales-orders/SalesOrderTable.tsx
rename to src/components/pages/marketing/MarketingTable.tsx
index cb7a9649..2acffe7c 100644
--- a/src/components/pages/marketing/sales-orders/SalesOrderTable.tsx
+++ b/src/components/pages/marketing/MarketingTable.tsx
@@ -12,16 +12,10 @@ import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector';
import { TableToolbar } from '@/components/table/TableToolbar';
import { ROWS_OPTIONS } from '@/config/constant';
import { isResponseSuccess } from '@/lib/api-helper';
-import {
- cn,
- formatCurrency,
- formatDate,
- formatVechicleNumber,
-} from '@/lib/helper';
+import { cn, formatCurrency, formatDate } from '@/lib/helper';
import { MarketingApi } from '@/services/api/marketing/marketing';
import { useTableFilter } from '@/services/hooks/useTableFilter';
-import { Marketing, MarketingProduct } from '@/types/api/marketing/marketing';
-import { Customer } from '@/types/api/master-data/customer';
+import { BaseSalesOrder, Marketing } from '@/types/api/marketing/marketing';
import { Icon } from '@iconify/react';
import { CellContext } from '@tanstack/react-table';
import { useCallback, useState } from 'react';
@@ -241,13 +235,13 @@ const SalesOrderTable = () => {
},
{
accessorFn: (row) =>
- row.marketing_products
+ row.sales_order
?.map((product) => product.total_price)
.reduce((a, b) => a + b, 0) ?? 0,
header: 'Grand Total',
cell: (props) => {
return formatCurrency(
- props.row.original?.marketing_products
+ props.row.original?.sales_order
?.map((product) => product.total_price)
.reduce((a, b) => a + b, 0) ?? 0
);
@@ -257,8 +251,8 @@ const SalesOrderTable = () => {
accessorKey: 'marketing_products.length',
header: 'Product Details',
cell: (props) => {
- if (props?.row?.original?.marketing_products?.length) {
- if (props?.row?.original?.marketing_products?.length > 1) {
+ if (props?.row?.original?.sales_order?.length) {
+ if (props?.row?.original?.sales_order?.length > 1) {
return (
);
} else {
- const product = props?.row?.original?.marketing_products[0];
+ const product = props?.row?.original?.sales_order[0];
return <>{product?.product_warehouse?.product?.name}>;
}
}
@@ -379,10 +372,10 @@ const SalesOrderTable = () => {
-
+
data={
isResponseSuccess(marketing) && selectedItem
- ? (selectedItem?.marketing_products ?? [])
+ ? (selectedItem?.sales_order ?? [])
: []
}
columns={[
diff --git a/src/components/pages/marketing/sales-orders/detail/SalesOrderDetail.tsx b/src/components/pages/marketing/detail/MarketingDetail.tsx
similarity index 89%
rename from src/components/pages/marketing/sales-orders/detail/SalesOrderDetail.tsx
rename to src/components/pages/marketing/detail/MarketingDetail.tsx
index e0262d9d..feca11f9 100644
--- a/src/components/pages/marketing/sales-orders/detail/SalesOrderDetail.tsx
+++ b/src/components/pages/marketing/detail/MarketingDetail.tsx
@@ -10,15 +10,9 @@ import ApprovalSteps, {
} from '@/components/pages/ApprovalSteps';
import Table from '@/components/Table';
import { MARKETING_APPROVAL_LINE } from '@/config/approval-line';
-import {
- cn,
- formatCurrency,
- formatDate,
- formatNumber,
- formatVechicleNumber,
-} from '@/lib/helper';
+import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
import { MarketingApi } from '@/services/api/marketing/marketing';
-import { Marketing, MarketingProduct } from '@/types/api/marketing/marketing';
+import { BaseSalesOrder, Marketing } from '@/types/api/marketing/marketing';
import { Icon } from '@iconify/react';
import { useState } from 'react';
import toast from 'react-hot-toast';
@@ -34,7 +28,7 @@ const SalesOrderDetail = ({
'APPROVED'
);
const [grandTotal, setGrandTotal] = useState(
- initialValues?.marketing_products
+ initialValues?.sales_order
?.map((item) => item.total_price)
.reduce((a, b) => a + b, 0)
);
@@ -48,7 +42,7 @@ const SalesOrderDetail = ({
isLoading: isLoadingApproval,
refresh: refreshApproval,
} = useApprovalSteps({
- latestApproval: initialValues?.approval,
+ latestApproval: initialValues?.latest_approval,
approvalLines: MARKETING_APPROVAL_LINE,
moduleName: 'MARKETINGS',
moduleId: initialValues?.id as number as unknown as string,
@@ -114,12 +108,12 @@ const SalesOrderDetail = ({
)}
- {initialValues?.approval?.step_number != 3 && (
+ {initialValues?.latest_approval?.step_number != 3 && (
<>
- {initialValues?.marketing_products && (
+ {initialValues?.sales_order && (
-
- data={initialValues?.marketing_products}
+
+ data={initialValues?.sales_order}
columns={[
- {
- header: 'No. Polisi',
- accessorFn(row) {
- return formatVechicleNumber(
- row.delivery_product?.vehicle_number as string
- );
- },
- },
{
header: 'Kandang',
accessorFn(row) {
@@ -251,8 +237,8 @@ const SalesOrderDetail = ({
className={{
containerClassName: cn({
'mb-20':
- initialValues?.marketing_products &&
- initialValues?.marketing_products?.length === 0,
+ initialValues?.sales_order &&
+ initialValues?.sales_order?.length === 0,
}),
tableWrapperClassName: 'overflow-x-auto min-h-full!',
tableClassName: 'font-inter w-full table-auto min-h-full!',
diff --git a/src/components/pages/marketing/sales-orders/form/SalesForm.schema.ts b/src/components/pages/marketing/form/MarketingForm.schema.ts
similarity index 84%
rename from src/components/pages/marketing/sales-orders/form/SalesForm.schema.ts
rename to src/components/pages/marketing/form/MarketingForm.schema.ts
index 6cd4a3be..4d03faae 100644
--- a/src/components/pages/marketing/sales-orders/form/SalesForm.schema.ts
+++ b/src/components/pages/marketing/form/MarketingForm.schema.ts
@@ -1,9 +1,8 @@
import * as Yup from 'yup';
-import { MarketingProduct } from '@/types/api/marketing/marketing';
import {
SalesOrderProductFormValues,
SalesOrderProductSchema,
-} from './repeater/MarketingProduct.schema';
+} from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema';
type MarketingSchemaType = {
customer_id: number | undefined;
@@ -20,7 +19,7 @@ type MarketingSchemaType = {
};
type SalesOrderSchemaType = MarketingSchemaType & {
- marketing_products: SalesOrderProductFormValues[];
+ sales_order: SalesOrderProductFormValues[];
};
export const SalesOrderSchema: Yup.ObjectSchema =
@@ -33,7 +32,7 @@ export const SalesOrderSchema: Yup.ObjectSchema =
}).nullable(),
so_date: Yup.string().required('Tanggal wajib diisi!'),
notes: Yup.string().required('Catatan wajib diisi!'),
- marketing_products: Yup.array()
+ sales_order: Yup.array()
.of(SalesOrderProductSchema)
.min(1, 'Produk wajib diisi!')
.required('Produk wajib diisi!'),
diff --git a/src/components/pages/marketing/sales-orders/form/SalesForm.tsx b/src/components/pages/marketing/form/MarketingForm.tsx
similarity index 57%
rename from src/components/pages/marketing/sales-orders/form/SalesForm.tsx
rename to src/components/pages/marketing/form/MarketingForm.tsx
index 9286fe93..66f7b815 100644
--- a/src/components/pages/marketing/sales-orders/form/SalesForm.tsx
+++ b/src/components/pages/marketing/form/MarketingForm.tsx
@@ -10,36 +10,56 @@ import SelectInput, {
} from '@/components/input/SelectInput';
import TextArea from '@/components/input/TextArea';
import Modal, { useModal } from '@/components/Modal';
-import * as TanStack from '@tanstack/react-table';
-import Table from '@/components/Table'; // Keep this import
-import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
+import { formatCurrency, formatDate } from '@/lib/helper';
import {
+ BaseSalesOrder,
CreateSalesOrderPayload,
CreateSalesOrderProductPayload,
Marketing,
- MarketingProduct,
} from '@/types/api/marketing/marketing';
import { Icon } from '@iconify/react';
import { useCallback, useEffect, useMemo, useState } from 'react';
-import MarketingProductForm from './repeater/MarketingProductForm';
-import CheckboxInput from '@/components/input/CheckboxInput';
import { Customer } from '@/types/api/master-data/customer';
import { CustomerApi } from '@/services/api/master-data';
import { useFormik } from 'formik';
-import { SalesOrderFormValues, SalesOrderSchema } from './SalesForm.schema';
+import { SalesOrderFormValues, SalesOrderSchema } from './MarketingForm.schema';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { MarketingApi } from '@/services/api/marketing/marketing';
-import { SalesOrderProductFormValues } from './repeater/MarketingProduct.schema';
+import { SalesOrderProductFormValues } from './repeater/sales-order/SalesOrderProduct.schema';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import toast from 'react-hot-toast';
import { useRouter } from 'next/navigation';
+import SalesOrderProductTable from './table-view/SalesOrderProductTable';
+import SalesOrderProductForm from './repeater/sales-order/SalesOrderProductForm';
+
+const MarketingProductToFieldValues = (
+ product: BaseSalesOrder
+): SalesOrderProductFormValues => {
+ return {
+ kandang_id: product.product_warehouse.warehouse.id,
+ kandang: {
+ value: product.product_warehouse.warehouse.id,
+ label: product.product_warehouse.warehouse.name,
+ },
+ product_warehouse: {
+ 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 SalesForm = ({
formType = 'add',
initialValues,
afterSubmit,
}: {
- formType?: 'add' | 'edit';
+ formType?: 'add' | 'edit' | 'deliver';
initialValues?: Marketing;
afterSubmit?: () => void;
}) => {
@@ -49,10 +69,14 @@ const SalesForm = ({
const [isLoading, setIsLoading] = useState(false);
const [selectedMarketingProduct, setSelectedMarketingProduct] =
- useState(null);
+ useState(null);
const [rawMarketingProducts, setRawMarketingProducts] = useState<
- MarketingProduct[]
- >(initialValues?.marketing_products || []);
+ SalesOrderProductFormValues[]
+ >(
+ initialValues?.sales_order.map((item) =>
+ MarketingProductToFieldValues(item)
+ ) || []
+ );
const [selectedCustomer, setSelectedCustomer] = useState(
initialValues?.customer
? { value: initialValues.customer.id, label: initialValues.customer.name }
@@ -63,63 +87,54 @@ const SalesForm = ({
parseInt(item)
);
const [grandTotal, setGrandTotal] = useState(
- initialValues?.marketing_products
+ initialValues?.sales_order
?.map((item) => item.total_price)
.reduce((a, b) => a + b, 0) ?? 0
);
const {
options: customerOptions,
- rawData: customerRawData,
isLoadingOptions: isLoadingCustomerOptions,
} = useSelect(CustomerApi.basePath, 'id', 'name');
- const handleAddProduct = useCallback(() => {
- addProductModal.openModal();
- }, [addProductModal]);
- const handleDeleteProduct = useCallback((id: number) => {
- setRawMarketingProducts((prev) => prev.filter((p) => p.id !== id));
- }, []);
+ const handleDeleteProduct = useCallback(
+ (product_warehouse_id: number, kandang_id: number) => {
+ setRawMarketingProducts((prev) =>
+ prev.filter(
+ (p) =>
+ p.product_warehouse_id !== product_warehouse_id &&
+ p.kandang_id !== kandang_id
+ )
+ );
+ },
+ []
+ );
const handleBulkDeleteProduct = () => {
setRawMarketingProducts((prev) =>
- prev.filter((product) => !selectedRowIds.includes(product.id))
+ prev.filter(
+ (product) =>
+ !selectedRowIds.includes(
+ parseInt(`${product.product_warehouse_id}${product.kandang_id}`)
+ )
+ )
);
};
const handleDelete = () => {
deleteModal.openModal();
};
+ const handleAddProductClick = useCallback(() => {
+ setSelectedMarketingProduct(null); // Pastikan form tambah
+ addProductModal.openModal();
+ }, [addProductModal]);
const handleAddSubmitProduct = useCallback(
- async (
- tableValue: CreateSalesOrderProductPayload,
- fieldValues: SalesOrderProductFormValues
- ) => {
- const newMarketingProduct: MarketingProduct = {
- id: rawMarketingProducts.length + 1,
- product_warehouse: tableValue.product_warehouse!,
- unit_price: tableValue.unit_price as number,
- total_weight: tableValue.total_weight as number,
- qty: tableValue.qty as number,
- avg_weight: tableValue.avg_weight as number,
- total_price: tableValue.total_price as number,
- delivery_product: {
- id: rawMarketingProducts.length + 1,
- vehicle_number: tableValue.vehicle_number as string,
- delivery_date: '' as string,
- unit_price: tableValue.unit_price as number,
- total_weight: tableValue.total_weight as number,
- qty: tableValue.qty as number,
- avg_weight: tableValue.avg_weight as number,
- total_price: tableValue.total_price as number,
- },
- };
-
- setRawMarketingProducts((prev) => [...prev, newMarketingProduct]);
+ async (values: SalesOrderProductFormValues) => {
+ setRawMarketingProducts((prev) => [...prev, values]);
formik.setValues({
...formik.values,
- marketing_products: [...formik.values.marketing_products, fieldValues],
+ sales_order: [...formik.values.sales_order, values],
});
- setGrandTotal((prev) => prev + (tableValue.total_price as number));
+ setGrandTotal((prev) => prev + (values.total_price as number));
addProductModal.closeModal();
},
[rawMarketingProducts.length, addProductModal]
@@ -176,41 +191,18 @@ const SalesForm = ({
router.push('/marketing/sales-orders');
};
- const MarketingProductToFieldValues = (
- product: MarketingProduct
- ): SalesOrderProductFormValues => {
- return {
- vehicle_number: product.delivery_product?.vehicle_number,
- kandang_id: product.product_warehouse.warehouse.id,
- kandang: {
- value: product.product_warehouse.warehouse.id,
- label: product.product_warehouse.warehouse.name,
- },
- product_warehouse: {
- value: product.product_warehouse.product.id,
- label: product.product_warehouse.product.name,
- },
- product_warehouse_id: product.product_warehouse.product.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 formikInitialValues = useMemo(() => {
return {
so_date: initialValues?.so_date || undefined,
notes: initialValues?.notes || undefined,
customer_id: initialValues?.customer?.id || undefined,
- sales_person_id: initialValues?.sales_person_id || 1,
+ sales_person_id: initialValues?.sales_person?.id || 1,
customer: {
value: initialValues?.customer?.id as number,
label: initialValues?.customer?.name as string,
},
- marketing_products:
- initialValues?.marketing_products?.map((product) =>
+ sales_order:
+ initialValues?.sales_order?.map((product) =>
MarketingProductToFieldValues(product)
) ?? [],
};
@@ -227,9 +219,9 @@ const SalesForm = ({
sales_person_id: values.sales_person_id as number,
date: formatDate(values.so_date as string, 'yyyy-MM-DD'),
notes: values.notes as string,
- marketing_products: values.marketing_products.map((product) => {
+ marketing_products: values.sales_order.map((product) => {
return {
- vehicle_number: product.vehicle_number as string,
+ vehicle_number: 'D 1234 XXXX',
kandang_id: product.kandang_id as number,
product_warehouse_id: product.product_warehouse_id as number,
unit_price: parseFloat(product.unit_price as string),
@@ -261,23 +253,14 @@ const SalesForm = ({
}, [formikSetValues, formik.initialValues]);
useEffect(() => {
- // Konversi array MarketingProduct ke format SalesOrderProductFormValues
- const newMarketingProductValues = rawMarketingProducts.map((product) =>
- MarketingProductToFieldValues(product)
- );
-
// Hitung Grand Total baru
const newGrandTotal = rawMarketingProducts.reduce(
- (total, product) => total + product.total_price,
+ (total, product) => total + parseFloat(product.total_price as string),
0
);
// Perbarui nilai formik.values.marketing_products
- formik.setFieldValue(
- 'marketing_products',
- newMarketingProductValues,
- false
- );
+ formik.setFieldValue('marketing_products', rawMarketingProducts, false);
// Parameter ketiga (false) untuk menghindari validasi secara langsung
// Perbarui state grandTotal
@@ -287,85 +270,6 @@ const SalesForm = ({
setRowSelection({});
}, [rawMarketingProducts]);
- const columns = useMemo(
- () => [
- {
- id: 'select',
- header: ({ table }: { table: TanStack.Table }) => (
-
-
-
- ),
- cell: ({ row }: { row: TanStack.Row }) => (
-
-
-
- ),
- },
- {
- accessorFn: (row: MarketingProduct) =>
- row.delivery_product?.vehicle_number,
- header: 'No. Polisi',
- },
- {
- accessorFn: (row: MarketingProduct) =>
- row.product_warehouse.warehouse.name,
- header: 'Kandang',
- },
- {
- accessorFn: (row: MarketingProduct) =>
- row.product_warehouse.product.name,
- header: 'Produk',
- },
- {
- accessorFn: (row: MarketingProduct) => formatCurrency(row.unit_price),
- header: 'Harga Satuan (Rp)',
- },
- {
- accessorFn: (row: MarketingProduct) => formatNumber(row.total_weight),
- header: 'Total Bobot (Kg)',
- },
- {
- accessorFn: (row: MarketingProduct) => formatNumber(row.qty),
- header: 'Kuantitas',
- },
- {
- accessorFn: (row: MarketingProduct) => formatNumber(row.avg_weight),
- header: 'Avg. Bobot (Kg)',
- },
- {
- accessorFn: (row: MarketingProduct) => formatCurrency(row.total_price),
- header: 'Total Penjualan (Rp)',
- },
- {
- header: 'Aksi',
- cell: (props: TanStack.CellContext) => (
-
- handleDeleteProduct(props.row.original.id)}
- >
-
-
-
- ),
- },
- ],
- [handleDeleteProduct, initialValues, rawMarketingProducts] // dependensi tunggal
- );
-
return (
<>