mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE): Add invoice download and PO table columns
This commit is contained in:
@@ -17,6 +17,7 @@ import RowCollapseOptions from '@/components/table/RowCollapseOptions';
|
|||||||
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
import StatusBadge from '@/components/helper/StatusBadge';
|
import StatusBadge from '@/components/helper/StatusBadge';
|
||||||
|
import PurchaseOrderInvoice from '@/components/pages/purchase/order/PurchaseOrderInvoice';
|
||||||
|
|
||||||
import { cn, formatDate } from '@/lib/helper';
|
import { cn, formatDate } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
@@ -158,6 +159,27 @@ const PurchaseTable = () => {
|
|||||||
PurchaseApi.getAllFetcher
|
PurchaseApi.getAllFetcher
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [isDownloadingInvoice, setIsDownloadingInvoice] = useState(false);
|
||||||
|
const [invoicePurchaseData, setInvoicePurchaseData] =
|
||||||
|
useState<Purchase | null>(null);
|
||||||
|
|
||||||
|
const handleDownloadInvoice = async (purchaseId: number) => {
|
||||||
|
setIsDownloadingInvoice(true);
|
||||||
|
try {
|
||||||
|
const response = await PurchaseApi.getSingle(purchaseId);
|
||||||
|
if (isResponseSuccess(response) && response.data) {
|
||||||
|
setInvoicePurchaseData(response.data);
|
||||||
|
setTimeout(() => {
|
||||||
|
setInvoicePurchaseData(null);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
toast.error('Gagal mengambil data purchase order.');
|
||||||
|
} finally {
|
||||||
|
setIsDownloadingInvoice(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ===== TABLE COLUMNS DEFINITION =====
|
// ===== TABLE COLUMNS DEFINITION =====
|
||||||
const purchaseColumns: ColumnDef<Purchase>[] = [
|
const purchaseColumns: ColumnDef<Purchase>[] = [
|
||||||
{
|
{
|
||||||
@@ -168,10 +190,66 @@ const PurchaseTable = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'supplier',
|
accessorKey: 'po_expedition',
|
||||||
|
header: 'PO Ekspedisi',
|
||||||
|
cell: (props) => {
|
||||||
|
const purchase = props.row.original;
|
||||||
|
|
||||||
|
if (!purchase.po_number || purchase.po_number === 'Belum dibuat') {
|
||||||
|
return <span>-</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
color='primary'
|
||||||
|
className='w-fit min-w-32 flex items-center justify-start gap-1 px-2 py-1 text-sm font-mono'
|
||||||
|
onClick={() => handleDownloadInvoice(purchase.id)}
|
||||||
|
disabled={isDownloadingInvoice}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={
|
||||||
|
isDownloadingInvoice
|
||||||
|
? 'eos-icons:loading'
|
||||||
|
: 'material-symbols:file-open-outline'
|
||||||
|
}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
{purchase.po_number}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'supplier.name',
|
||||||
header: 'Vendor',
|
header: 'Vendor',
|
||||||
cell: (props) => props.row.original.supplier.name,
|
cell: (props) => props.row.original.supplier.name,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'requester_name',
|
||||||
|
header: 'Nama Pengaju',
|
||||||
|
cell: (props) => props.row.original.requester_name || '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'products.name',
|
||||||
|
header: 'Produk',
|
||||||
|
cell: (props) => {
|
||||||
|
const products = props.row.original.products;
|
||||||
|
if (!products || products.length === 0) return '-';
|
||||||
|
return (
|
||||||
|
<ul className='list-disc pl-4'>
|
||||||
|
{products.map((product, index) => (
|
||||||
|
<li key={index}>{product.name}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'location.name',
|
||||||
|
header: 'Lokasi',
|
||||||
|
cell: (props) => props.row.original.location?.name || '-',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'po_date',
|
accessorKey: 'po_date',
|
||||||
header: 'Tgl. PO',
|
header: 'Tgl. PO',
|
||||||
@@ -180,6 +258,14 @@ const PurchaseTable = () => {
|
|||||||
? formatDate(props.row.original.po_date, 'DD MMM YYYY')
|
? formatDate(props.row.original.po_date, 'DD MMM YYYY')
|
||||||
: '-',
|
: '-',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'due_date',
|
||||||
|
header: 'Jatuh Tempo',
|
||||||
|
cell: (props) =>
|
||||||
|
props.row.original.due_date
|
||||||
|
? formatDate(props.row.original.due_date, 'DD MMM YYYY')
|
||||||
|
: '-',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
header: 'Aging',
|
header: 'Aging',
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
@@ -231,7 +317,7 @@ const PurchaseTable = () => {
|
|||||||
color={statusColor}
|
color={statusColor}
|
||||||
text={statusText}
|
text={statusText}
|
||||||
className={{
|
className={{
|
||||||
badge: 'whitespace-nowrap',
|
badge: 'whitespace-nowrap max-w-max w-fit',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -409,6 +495,15 @@ const PurchaseTable = () => {
|
|||||||
onClick: confirmationModalDeleteClickHandler,
|
onClick: confirmationModalDeleteClickHandler,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{invoicePurchaseData && (
|
||||||
|
<div className='hidden'>
|
||||||
|
<PurchaseOrderInvoice
|
||||||
|
data={invoicePurchaseData}
|
||||||
|
triggerDownloadOnMount={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
Page,
|
Page,
|
||||||
Text,
|
Text,
|
||||||
@@ -235,11 +235,16 @@ const pdfStyles = StyleSheet.create({
|
|||||||
interface PurchaseOrderInvoiceProps {
|
interface PurchaseOrderInvoiceProps {
|
||||||
data?: Purchase;
|
data?: Purchase;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
triggerDownloadOnMount?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => {
|
const PurchaseOrderInvoice = ({
|
||||||
|
data,
|
||||||
|
triggerDownloadOnMount,
|
||||||
|
}: PurchaseOrderInvoiceProps) => {
|
||||||
const [, setIsGeneratingPDF] = useState(false);
|
const [, setIsGeneratingPDF] = useState(false);
|
||||||
const purchaseData = data;
|
const purchaseData = data;
|
||||||
|
const hasDownloadedRef = useRef(false);
|
||||||
|
|
||||||
const grandTotal = useMemo(() => {
|
const grandTotal = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
@@ -250,7 +255,7 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => {
|
|||||||
);
|
);
|
||||||
}, [purchaseData?.items]);
|
}, [purchaseData?.items]);
|
||||||
|
|
||||||
const handleDownloadPDF = async () => {
|
const handleDownloadPDF = useCallback(async () => {
|
||||||
if (!purchaseData) {
|
if (!purchaseData) {
|
||||||
toast.error('No purchase order data available');
|
toast.error('No purchase order data available');
|
||||||
return;
|
return;
|
||||||
@@ -510,7 +515,20 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => {
|
|||||||
} finally {
|
} finally {
|
||||||
setIsGeneratingPDF(false);
|
setIsGeneratingPDF(false);
|
||||||
}
|
}
|
||||||
};
|
}, [purchaseData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (triggerDownloadOnMount && purchaseData && !hasDownloadedRef.current) {
|
||||||
|
hasDownloadedRef.current = true;
|
||||||
|
handleDownloadPDF();
|
||||||
|
}
|
||||||
|
}, [triggerDownloadOnMount, purchaseData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!triggerDownloadOnMount) {
|
||||||
|
hasDownloadedRef.current = false;
|
||||||
|
}
|
||||||
|
}, [triggerDownloadOnMount]);
|
||||||
|
|
||||||
if (!purchaseData) {
|
if (!purchaseData) {
|
||||||
return (
|
return (
|
||||||
@@ -520,6 +538,10 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (triggerDownloadOnMount) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return purchaseData?.po_number &&
|
return purchaseData?.po_number &&
|
||||||
purchaseData.po_number !== 'Belum dibuat' ? (
|
purchaseData.po_number !== 'Belum dibuat' ? (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
Vendored
+14
-4
@@ -1,10 +1,15 @@
|
|||||||
import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
|
import {
|
||||||
|
BaseApproval,
|
||||||
|
BaseMetadata,
|
||||||
|
CreatedUser,
|
||||||
|
} from '@/types/api/api-general';
|
||||||
import { Supplier } from '@/types/api/master-data/supplier';
|
import { Supplier } from '@/types/api/master-data/supplier';
|
||||||
import { Warehouse } from '@/types/api/master-data/warehouse';
|
import { Warehouse } from '@/types/api/master-data/warehouse';
|
||||||
import { Product } from '@/types/api/master-data/product';
|
import { Product } from '@/types/api/master-data/product';
|
||||||
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
|
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
|
||||||
import { Area } from '@/types/api/master-data/area';
|
import { Area } from '@/types/api/master-data/area';
|
||||||
import { Location } from '@/types/api/master-data/location';
|
import { Location } from '@/types/api/master-data/location';
|
||||||
|
import { Uom } from '@/types/api/master-data/uom';
|
||||||
|
|
||||||
export type PurchaseItemProduct = {
|
export type PurchaseItemProduct = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -12,14 +17,15 @@ export type PurchaseItemProduct = {
|
|||||||
flags?: string[];
|
flags?: string[];
|
||||||
ProductPrice?: number;
|
ProductPrice?: number;
|
||||||
SellingPrice?: number;
|
SellingPrice?: number;
|
||||||
uom?: {
|
uom: Uom;
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
product_category?:
|
product_category?:
|
||||||
| {
|
| {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
code: string;
|
||||||
}
|
}
|
||||||
| string;
|
| string;
|
||||||
|
suppliers?: Supplier[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PurchaseItem = {
|
export type PurchaseItem = {
|
||||||
@@ -69,6 +75,10 @@ export type BasePurchase = {
|
|||||||
warehouse?: Warehouse;
|
warehouse?: Warehouse;
|
||||||
items?: PurchaseItem[];
|
items?: PurchaseItem[];
|
||||||
latest_approval?: BaseApproval;
|
latest_approval?: BaseApproval;
|
||||||
|
requester_name?: string;
|
||||||
|
po_expedition?: string[];
|
||||||
|
created_user?: CreatedUser;
|
||||||
|
products?: PurchaseItemProduct[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Purchase = BaseMetadata & BasePurchase;
|
export type Purchase = BaseMetadata & BasePurchase;
|
||||||
|
|||||||
Reference in New Issue
Block a user