diff --git a/src/app/purchase/detail/page.tsx b/src/app/purchase/detail/page.tsx
index 5a497f14..29675d89 100644
--- a/src/app/purchase/detail/page.tsx
+++ b/src/app/purchase/detail/page.tsx
@@ -2,7 +2,7 @@
import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from 'swr';
-import PurchaseRequestForm from '@/components/pages/purchase/form/request/PurchaseRequestForm';
+import PurchaseOrderDetail from '@/components/pages/purchase/order/PurchaseOrderDetail';
import { PurchaseRequestApi } from '@/services/api/purchase';
import { isResponseSuccess, isResponseError } from '@/lib/api-helper';
@@ -33,12 +33,14 @@ const PurchaseDetail = () => {
}
return (
-
+
{isLoadingPurchase && (
-
+
+
+
)}
{!isLoadingPurchase && isResponseSuccess(purchase) && (
-
+
)}
);
diff --git a/src/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm.tsx b/src/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm.tsx
index a1475c18..256ffed3 100644
--- a/src/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm.tsx
+++ b/src/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm.tsx
@@ -206,15 +206,13 @@ const PurchaseOrderAcceptApprovalForm = ({
if (initialValues?.items) {
return initialValues.items.map((item) => ({
value: item.id,
- label: `${item.product.name} (${item.quantity} ${item.product.uom.name})`,
+ label: `${item.product.name} (${item.quantity} ${item.product.uom?.name || 'unit'})`,
id: item.id,
quantity: item.quantity,
product: {
name: item.product.name,
product_category: item.product.product_category,
- uom: {
- name: item.product.uom.name,
- },
+ uom: item.product.uom,
},
warehouse: {
name: item.warehouse?.name || '',
diff --git a/src/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm.tsx b/src/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm.tsx
index 137b9ff7..2517b337 100644
--- a/src/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm.tsx
+++ b/src/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm.tsx
@@ -44,7 +44,7 @@ const PurchaseOrderStaffApprovalForm = ({
product: {
name: string;
type?: string;
- uom: {
+ uom?: {
name: string;
};
};
@@ -170,15 +170,13 @@ const PurchaseOrderStaffApprovalForm = ({
if (initialValues?.items) {
return initialValues.items.map((item) => ({
value: item.id,
- label: `${item.product.name} (${item.quantity} ${item.product.uom.name})`,
+ label: `${item.product.name} (${item.quantity} ${item.product.uom?.name || 'unit'})`,
id: item.id,
quantity: item.quantity,
product: {
name: item.product.name,
product_category: item.product.product_category,
- uom: {
- name: item.product.uom.name,
- },
+ uom: item.product.uom,
},
warehouse: {
name: item.warehouse?.name || '',
diff --git a/src/components/pages/purchase/order/PurchaseOrderDetail.tsx b/src/components/pages/purchase/order/PurchaseOrderDetail.tsx
index ec18ed15..98940a13 100644
--- a/src/components/pages/purchase/order/PurchaseOrderDetail.tsx
+++ b/src/components/pages/purchase/order/PurchaseOrderDetail.tsx
@@ -3,9 +3,7 @@
import { useCallback, useMemo, useState } from 'react';
import { ColumnDef, SortingState } from '@tanstack/react-table';
-import ApprovalSteps, {
- formatGroupedApprovalsToApprovalSteps,
-} from '@/components/pages/ApprovalSteps';
+import ApprovalSteps from '@/components/pages/ApprovalSteps';
import Table from '@/components/Table';
import Button from '@/components/Button';
import { Icon } from '@iconify/react';
@@ -20,21 +18,13 @@ import PurchaseOrderStaffApprovalForm from '@/components/pages/purchase/form/ord
import PurchaseOrderAcceptApprovalForm from '@/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm';
import PurchaseOrderInvoice from '@/components/pages/purchase/order/PurchaseOrderInvoice';
-import { BaseGroupedApproval } from '@/types/api/api-general';
-import { PURCHASE_ORDER_APPROVAL_LINE } from '@/config/approval-line';
+import { ApprovalStepStatus } from '@/components/pages/ApprovalSteps';
import Card from '@/components/Card';
import {
CreateManagerApprovalRequestPayload,
Purchase,
PurchaseItem,
} from '@/types/api/purchase/purchase';
-import {
- createdUser,
- dummyAreas,
- dummyLocations,
- dummyProductWarehouses,
- dummyWarehouses,
-} from '@/dummy/marketing.dummy';
import {
ManagerApprovalApi,
PurchaseDeleteItemsApi,
@@ -81,321 +71,8 @@ interface PurchaseOrderDetailProps {
data?: Purchase;
}
-const dummyPurchaseData: Purchase = {
- id: 1,
- pr_number: 'PR-MBU-01837',
- po_number: 'PO-MBU-01837',
- po_document_path: '/documents/po-mbu-01837.pdf',
- po_date: '2025-01-10T00:00:00Z',
- supplier: {
- id: 1,
- name: 'PT. CHAROEN POKPHAND JAYA FARM',
- alias: 'CP JAYA FARM',
- pic: 'Budi Santoso',
- type: 'Supplier',
- category: 'Feed',
- hatchery: 'Jawa Barat',
- phone: '+62-22-7563850',
- email: 'info@cp.co.id',
- address:
- 'Jl. Raya Bandung - Sumedang Km. 28, Desa Cisantana, Kec. Cigendel, Kabupaten Sumedang, Jawa Barat 45363',
- npwp: '01.938.451.6-433.000',
- account_number: '123-456-7890',
- due_date: 30,
- balance: 0,
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- created_user: createdUser,
- },
- credit_term: 30,
- due_date: '2025-11-13T00:00:00Z',
- grand_total: 65000000,
- notes: null,
- area: dummyAreas[0],
- location: dummyLocations[0],
- items: [
- {
- id: 1,
- purchase_id: 1,
- product: {
- id: 1,
- name: 'CP Vaksin',
- brand: '',
- sku: '',
- product_price: 0,
- selling_price: 0,
- tax: 0,
- expiry_period: 0,
- uom: {
- id: 1,
- name: 'Ekor',
- created_user: {
- id: 1,
- id_user: 1,
- email: 'hello@gmail.com',
- name: 'Admin',
- },
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- product_category: {
- id: 1,
- code: 'DOC',
- name: 'DOC',
- created_user: {
- id: 1,
- id_user: 1,
- email: 'hello@gmail.com',
- name: 'Admin',
- },
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- suppliers: [],
- flags: [],
- created_user: {
- id: 1,
- id_user: 1,
- email: 'hello@gmail.com',
- name: 'Admin',
- },
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- product_warehouse: dummyProductWarehouses[0],
- quantity: 10000,
- sub_qty: 10000,
- total_qty: 10000,
- total_used: 0,
- price: 6500,
- total_price: 65000000,
- received_date: null,
- travel_number: null,
- travel_number_docs: null,
- vehicle_number: null,
- warehouse: dummyWarehouses[0],
- },
- {
- id: 2,
- purchase_id: 2,
- product: {
- id: 1,
- name: 'CP Vaksin',
- brand: '',
- sku: '',
- product_price: 0,
- selling_price: 0,
- tax: 0,
- expiry_period: 0,
- uom: {
- id: 1,
- name: 'Ekor',
- created_user: {
- id: 1,
- id_user: 1,
- email: 'hello@gmail.com',
- name: 'Admin',
- },
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- product_category: {
- id: 1,
- code: 'DOC',
- name: 'DOC',
- created_user: {
- id: 1,
- id_user: 1,
- email: 'hello@gmail.com',
- name: 'Admin',
- },
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- suppliers: [],
- flags: [],
- created_user: {
- id: 1,
- id_user: 1,
- email: 'hello@gmail.com',
- name: 'Admin',
- },
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- product_warehouse: dummyProductWarehouses[0],
- quantity: 10000,
- sub_qty: 10000,
- total_qty: 10000,
- total_used: 0,
- price: 6500,
- total_price: 65000000,
- received_date: null,
- travel_number: null,
- travel_number_docs: null,
- vehicle_number: null,
- warehouse: dummyWarehouses[0],
- },
- ],
- created_at: '2025-01-10T00:00:00Z',
- updated_at: '2025-01-10T00:00:00Z',
- created_by: 1,
- deleted_at: null,
- created_user: createdUser,
- warehouse: dummyWarehouses[0],
-};
-const dummyGoodsReceiptItems: PurchaseItem[] = [
- {
- id: 1,
- purchase_id: 1,
- product: {
- id: 1,
- product_category: {
- id: 1,
- code: 'DOC',
- name: 'DOC',
- created_user: createdUser,
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- name: 'CP Vaksin',
- brand: '',
- sku: '',
- product_price: 0,
- selling_price: 0,
- tax: 0,
- expiry_period: 0,
- uom: {
- id: 1,
- name: 'Ekor',
- created_user: createdUser,
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- suppliers: [],
- flags: [],
- created_user: {
- id: 1,
- id_user: 1,
- email: 'hello@gmail.com',
- name: 'Admin',
- },
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- },
- product_warehouse: dummyProductWarehouses[0],
- quantity: 10000,
- sub_qty: 10000,
- total_qty: 10000,
- total_used: 0,
- price: 6500,
- total_price: 65000000,
- received_date: '2025-01-15T00:00:00Z',
- travel_number: 'NSJ-1',
- travel_number_docs: '/documents/nsj-1.pdf',
- vehicle_number: 'NAP-1',
- warehouse: dummyWarehouses[0],
- },
-];
-const dummyGroupedApprovals: BaseGroupedApproval[] = [
- {
- step_number: 1,
- step_name: 'Pengajuan',
- approvals: [
- {
- step_number: 1,
- step_name: 'Pengajuan',
- action: 'submit',
- notes: 'Pengajuan purchase order dibuat',
- action_by: {
- id: 1,
- id_user: 1,
- email: 'user@company.com',
- name: 'User Pengajuan',
- },
- action_at: '2025-01-10T08:00:00Z',
- },
- ],
- },
- {
- step_number: 2,
- step_name: 'Approval Purchasing',
- approvals: [
- {
- step_number: 2,
- step_name: 'Approval Purchasing',
- action: 'approve',
- notes: 'Purchase order disetujui oleh purchasing',
- action_by: {
- id: 2,
- id_user: 2,
- email: 'purchasing@company.com',
- name: 'Staff Purchasing',
- },
- action_at: '2025-01-10T10:30:00Z',
- },
- ],
- },
- {
- step_number: 3,
- step_name: 'Approval Manager Purchasing',
- approvals: [
- {
- step_number: 3,
- step_name: 'Approval Manager Purchasing',
- action: 'approve',
- notes: 'Purchase order disetujui oleh manager purchasing',
- action_by: {
- id: 3,
- id_user: 3,
- email: 'manager.purchasing@company.com',
- name: 'Manager Purchasing',
- },
- action_at: '2025-01-10T14:15:00Z',
- },
- ],
- },
- {
- step_number: 4,
- step_name: 'Produk Diterima',
- approvals: [
- {
- step_number: 4,
- step_name: 'Produk Diterima',
- action: 'receive',
- notes: 'Produk telah diterima sesuai pesanan',
- action_by: {
- id: 4,
- id_user: 4,
- email: 'user@company.com',
- name: 'User Pengajuan',
- },
- action_at: '2025-01-12T09:00:00Z',
- },
- ],
- },
- {
- step_number: 5,
- step_name: 'Selesai',
- approvals: [
- {
- step_number: 5,
- step_name: 'Selesai',
- action: 'complete',
- notes: 'Purchase order telah selesai diproses',
- action_by: {
- id: 5,
- id_user: 5,
- email: 'user@company.com',
- name: 'User Pengajuan',
- },
- action_at: '2025-01-12T16:00:00Z',
- },
- ],
- },
-];
const PurchaseOrderDetail = ({
type = 'detail',
@@ -420,23 +97,49 @@ const PurchaseOrderDetail = ({
parseInt(item)
);
- // ===== STATIC DATA =====
- const purchaseData = data || dummyPurchaseData;
+ // ===== COMPUTED VALUES =====
const purchaseOrderItems = useMemo(
- () => purchaseData.items || [],
- [purchaseData.items]
+ () => data?.items || [],
+ [data?.items]
);
- const goodsReceiptItems = dummyGoodsReceiptItems;
- const groupedApprovals = dummyGroupedApprovals;
- const latestApproval =
- groupedApprovals[groupedApprovals.length - 1]?.approvals[0];
+
+ // Create goods receipt items from received items
+ const goodsReceiptItems = useMemo(() => {
+ return purchaseOrderItems.filter(item => item.received_date);
+ }, [purchaseOrderItems]);
+
+ // Create simple approval steps from single approval data
+ const approvalSteps = useMemo(() => {
+ if (!data?.approval) return [];
+
+ // Create a simple approval step based on the single approval data
+ const status = data.approval.action === 'APPROVED' ? 'APPROVED' :
+ data.approval.action === 'REJECTED' ? 'REJECTED' : 'WAITING';
+
+ return [{
+ name: data.approval.step_name,
+ status: status as ApprovalStepStatus,
+ logs: [{
+ action_by: data.approval.action_by?.name,
+ date: data.approval.action_at,
+ notes: data.approval.notes,
+ }],
+ }];
+ }, [data?.approval]);
+
+ const totalBeforeTax = useMemo(() => {
+ return purchaseOrderItems.reduce(
+ (sum, item) => sum + (item.total_price || 0),
+ 0
+ );
+ }, [purchaseOrderItems]);
// ===== SUBMISSION HANDLER =====
const createManagerApprovalHandler = useCallback(
async (payload: CreateManagerApprovalRequestPayload) => {
const purchaseRequestId = searchParams.get('purchaseId')
? parseInt(searchParams.get('purchaseId')!)
- : purchaseData?.id || 1;
+ : data?.id || 1;
if (!purchaseRequestId) {
toast.error('Purchase Request ID is required');
@@ -454,14 +157,14 @@ const PurchaseOrderDetail = ({
}
toast.success(res?.message as string);
},
- [purchaseData?.id, searchParams]
+ [data?.id, searchParams]
);
// ===== DELETE HANDLER =====
const deleteItemsHandler = useCallback(async () => {
const purchaseRequestId = searchParams.get('purchaseId')
? parseInt(searchParams.get('purchaseId')!)
- : purchaseData?.id || 1;
+ : data?.id || 1;
if (!purchaseRequestId) {
toast.error('Purchase Request ID is required');
@@ -501,30 +204,14 @@ const PurchaseOrderDetail = ({
} finally {
setIsDeleteLoading(false);
}
- }, [purchaseData?.id, searchParams, selectedItem, selectedRowIds]);
+ }, [data?.id, searchParams, selectedItem, selectedRowIds]);
- // ===== COMPUTED VALUES =====
- const approvalSteps = useMemo(() => {
- if (!groupedApprovals.length || !latestApproval) return [];
+ // Return null if no data provided
+ if (!data) {
+ return null;
+ }
- try {
- return formatGroupedApprovalsToApprovalSteps(
- PURCHASE_ORDER_APPROVAL_LINE,
- groupedApprovals,
- latestApproval
- );
- } catch (error) {
- console.error('Error formatting approval steps:', error);
- return [];
- }
- }, [groupedApprovals, latestApproval]);
-
- const totalBeforeTax = useMemo(() => {
- return purchaseOrderItems.reduce(
- (sum, item) => sum + (item.total_price || 0),
- 0
- );
- }, [purchaseOrderItems]);
+ const purchaseData = data;
const purchaseOrderColumns: ColumnDef
[] = [
{
@@ -563,10 +250,15 @@ const PurchaseOrderDetail = ({
cell: (props) => props.row.original.product?.name || '-',
},
{
- accessorKey: 'product.product_category.name',
+ accessorKey: 'product.product_category',
header: 'Jenis Produk',
- cell: (props) =>
- props.row.original.product?.product_category?.name || '-',
+ cell: (props) => {
+ const category = props.row.original.product?.product_category;
+ if (typeof category === 'string') {
+ return category;
+ }
+ return category?.name || '-';
+ },
},
{
accessorKey: 'quantity',
@@ -576,7 +268,13 @@ const PurchaseOrderDetail = ({
{
accessorKey: 'product.uom.name',
header: 'Satuan',
- cell: (props) => props.row.original.product?.uom?.name || '-',
+ cell: (props) => {
+ const uom = props.row.original.product?.uom;
+ if (uom && typeof uom === 'object' && uom.name) {
+ return uom.name;
+ }
+ return uom || '-';
+ },
},
{
accessorKey: 'price',
@@ -628,10 +326,12 @@ const PurchaseOrderDetail = ({
: '-',
},
{
- accessorKey: 'product_warehouse.warehouse.name',
+ accessorKey: 'warehouse.name',
header: 'Gudang Tujuan',
- cell: (props) =>
- props.row.original.product_warehouse?.warehouse?.name || '-',
+ cell: (props) => {
+ const warehouse = props.row.original.warehouse;
+ return warehouse?.name || '-';
+ },
},
{
accessorKey: 'travel_number',
@@ -639,10 +339,10 @@ const PurchaseOrderDetail = ({
cell: (props) => props.row.original.travel_number || '-',
},
{
- accessorKey: 'travel_number_docs',
+ accessorKey: 'travel_document_path',
header: 'Dokumen Surat Jalan',
cell: (props) => {
- const documentPath = props.row.original.travel_number_docs;
+ const documentPath = props.row.original.travel_document_path;
return documentPath ? (
@@ -817,7 +517,7 @@ const PurchaseOrderDetail = ({
Lokasi