163 lines
7.8 KiB
TypeScript
163 lines
7.8 KiB
TypeScript
import { ImagePreviewButton } from '@/components/dialogs';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
import { formatNumber } from '@/lib/format';
|
|
import { formatCurrency } from '@/lib/utils';
|
|
import { Fragment } from 'react';
|
|
import type { Purchase, PurchaseItemDetail } from './columns';
|
|
|
|
export function PurchaseItemSubRow({
|
|
purchase,
|
|
items: loadedItems,
|
|
isLoading,
|
|
}: {
|
|
purchase: Purchase;
|
|
items: PurchaseItemDetail[];
|
|
isLoading: boolean;
|
|
}) {
|
|
const items = loadedItems ?? [];
|
|
|
|
const groupedByMaterial = items.reduce(
|
|
(acc, item) => {
|
|
const materialName = item.raw_material_price?.raw_material?.name ?? '-';
|
|
const unit = item.raw_material_price?.raw_material?.unit ?? '';
|
|
|
|
if (!acc[materialName]) {
|
|
acc[materialName] = { unit, items: [] };
|
|
}
|
|
|
|
acc[materialName].items.push(item);
|
|
|
|
return acc;
|
|
},
|
|
{} as Record<string, { unit: string; items: PurchaseItemDetail[] }>,
|
|
);
|
|
|
|
const materialEntries = Object.entries(groupedByMaterial);
|
|
const isMultiMaterial = materialEntries.length > 1;
|
|
|
|
let counter = 0;
|
|
|
|
return (
|
|
<div className="overflow-x-auto rounded-md border">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead className="w-[50px] text-center">
|
|
No
|
|
</TableHead>
|
|
<TableHead className="w-[60px]">Foto</TableHead>
|
|
<TableHead>Varian</TableHead>
|
|
<TableHead className="text-right">Harga</TableHead>
|
|
<TableHead className="text-center">Qty</TableHead>
|
|
<TableHead className="text-right">Subtotal</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{isLoading ? (
|
|
<TableRow>
|
|
<TableCell
|
|
colSpan={6}
|
|
className="text-center text-muted-foreground"
|
|
>
|
|
Memuat item...
|
|
</TableCell>
|
|
</TableRow>
|
|
) : items.length === 0 ? (
|
|
<TableRow>
|
|
<TableCell
|
|
colSpan={6}
|
|
className="text-center text-muted-foreground"
|
|
>
|
|
Tidak ada item.
|
|
</TableCell>
|
|
</TableRow>
|
|
) : (
|
|
<>
|
|
{materialEntries.map(([materialName, group]) => {
|
|
const totalQty = group.items.reduce(
|
|
(sum, item) => sum + item.quantity,
|
|
0,
|
|
);
|
|
const totalSubtotal = group.items.reduce(
|
|
(sum, item) => sum + item.subtotal,
|
|
0,
|
|
);
|
|
|
|
return (
|
|
<Fragment key={materialName}>
|
|
{isMultiMaterial && (
|
|
<TableRow>
|
|
<TableCell
|
|
colSpan={4}
|
|
className="text-center font-medium text-muted-foreground bg-muted/20"
|
|
>
|
|
{materialName}
|
|
</TableCell>
|
|
<TableCell className="text-center font-medium text-muted-foreground bg-muted/20">
|
|
{formatNumber(totalQty)}{' '}
|
|
{group.unit}
|
|
</TableCell>
|
|
<TableCell className="text-right font-medium text-muted-foreground bg-muted/20">
|
|
{formatCurrency(totalSubtotal)}
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{group.items.map((item) => {
|
|
counter++;
|
|
|
|
return (
|
|
<TableRow key={item.id}>
|
|
<TableCell className="text-center">
|
|
{counter}
|
|
</TableCell>
|
|
<TableCell>
|
|
{item.raw_material_price?.photo_url ? (
|
|
<ImagePreviewButton
|
|
srcs={[
|
|
item.raw_material_price.photo_conversion_url ?? item.raw_material_price.photo_url,
|
|
]}
|
|
modalSrc={item.raw_material_price.photo_url}
|
|
title={
|
|
item.raw_material_price.variant
|
|
}
|
|
/>
|
|
) : (
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-muted text-xs text-muted-foreground">
|
|
N/A
|
|
</div>
|
|
)}
|
|
</TableCell>
|
|
<TableCell>
|
|
{item.raw_material_price?.variant ?? '-'}
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
{formatCurrency(item.unit_price)}
|
|
</TableCell>
|
|
<TableCell className="text-center">
|
|
{formatNumber(item.quantity)}{' '}
|
|
{group.unit}
|
|
</TableCell>
|
|
<TableCell className="text-right font-medium">
|
|
{formatCurrency(item.subtotal)}
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</Fragment>
|
|
);
|
|
})}
|
|
</>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
);
|
|
}
|