feat: implement grouped table views for purchase items and cutting materials/results with updated type definitions
This commit is contained in:
parent
d8079fb3ff
commit
e5f2f526c3
@ -73,6 +73,63 @@ function statusVariant(status: string): 'default' | 'secondary' | 'destructive'
|
||||
|
||||
return 'outline';
|
||||
}
|
||||
|
||||
interface GroupedCuttingMaterials {
|
||||
rawMaterialId: number;
|
||||
rawMaterialName: string;
|
||||
unitLabel?: string;
|
||||
items: any[];
|
||||
}
|
||||
|
||||
interface GroupedCuttingResults {
|
||||
productId: number;
|
||||
productName: string;
|
||||
items: any[];
|
||||
}
|
||||
|
||||
function getGroupedMaterials(materials: any[]): GroupedCuttingMaterials[] {
|
||||
const groups: Record<number, GroupedCuttingMaterials> = {};
|
||||
|
||||
materials.forEach((item) => {
|
||||
const rawMaterial = item.raw_material_price?.raw_material;
|
||||
const rawMaterialId = rawMaterial?.id ?? 0;
|
||||
const rawMaterialName = rawMaterial?.name ?? 'Bahan Baku Tidak Diketahui';
|
||||
const unitLabel = rawMaterial?.unit_label;
|
||||
|
||||
if (!groups[rawMaterialId]) {
|
||||
groups[rawMaterialId] = {
|
||||
rawMaterialId,
|
||||
rawMaterialName,
|
||||
unitLabel,
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
groups[rawMaterialId].items.push(item);
|
||||
});
|
||||
|
||||
return Object.values(groups);
|
||||
}
|
||||
|
||||
function getGroupedResults(results: any[]): GroupedCuttingResults[] {
|
||||
const groups: Record<number, GroupedCuttingResults> = {};
|
||||
|
||||
results.forEach((item) => {
|
||||
const product = item.product_variant?.product;
|
||||
const productId = product?.id ?? 0;
|
||||
const productName = product?.name ?? 'Produk Tidak Diketahui';
|
||||
|
||||
if (!groups[productId]) {
|
||||
groups[productId] = {
|
||||
productId,
|
||||
productName,
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
groups[productId].items.push(item);
|
||||
});
|
||||
|
||||
return Object.values(groups);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -114,84 +171,93 @@ function statusVariant(status: string): 'default' | 'secondary' | 'destructive'
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-0 lg:grid-cols-2">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead colspan="4" class="bg-muted/20">
|
||||
Bahan Baku
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>Bahan</TableHead>
|
||||
<TableHead>Varian</TableHead>
|
||||
<TableHead>Pemakaian</TableHead>
|
||||
<TableHead>Sisa</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-if="!cutting.materials.length" :key="`${cutting.id}-material-empty`">
|
||||
<TableCell colspan="4" class="text-muted-foreground">
|
||||
Belum ada bahan baku
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow v-for="material in cutting.materials" :key="material.id">
|
||||
<TableCell class="font-medium">
|
||||
{{ material.raw_material_price?.raw_material?.name }}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{{ material.raw_material_price?.variant }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ material.material_usage_formatted }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ material.remaining_material_formatted }}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
<div class="space-y-6 p-4">
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-sm font-semibold tracking-tight text-foreground">Bahan Baku</h4>
|
||||
<div class="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Varian</TableHead>
|
||||
<TableHead>Pemakaian</TableHead>
|
||||
<TableHead>Sisa</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-if="!cutting.materials.length" :key="`${cutting.id}-material-empty`">
|
||||
<TableCell colspan="3" class="text-muted-foreground">
|
||||
Belum ada bahan baku
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<template v-else v-for="group in getGroupedMaterials(cutting.materials)" :key="group.rawMaterialId">
|
||||
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
||||
<TableCell colspan="3" class="font-semibold text-foreground">
|
||||
{{ group.rawMaterialName }}
|
||||
<Badge v-if="group.unitLabel" variant="secondary" class="ml-2 font-normal">
|
||||
{{ group.unitLabel }}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow v-for="material in group.items" :key="material.id">
|
||||
<TableCell class="pl-6 font-medium">
|
||||
{{ material.raw_material_price?.variant }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ material.material_usage_formatted }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ material.remaining_material_formatted }}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</template>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead colspan="5" class="bg-muted/20">
|
||||
Hasil Produk
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>Produk</TableHead>
|
||||
<TableHead>Varian</TableHead>
|
||||
<TableHead>Hasil</TableHead>
|
||||
<TableHead>Gudang</TableHead>
|
||||
<TableHead>Reject</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-if="!cutting.results.length" :key="`${cutting.id}-result-empty`">
|
||||
<TableCell colspan="5" class="text-muted-foreground">
|
||||
Belum ada hasil produk
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow v-for="result in cutting.results" :key="result.id">
|
||||
<TableCell class="font-medium">
|
||||
{{ result.product_variant?.product?.name }}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{{ result.product_variant?.name }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ result.cutting_result }} pcs
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ result.warehouse_stock }} pcs
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ result.cutting_reject }} pcs
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-sm font-semibold tracking-tight text-foreground">Hasil Produk</h4>
|
||||
<div class="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Varian</TableHead>
|
||||
<TableHead>Hasil</TableHead>
|
||||
<TableHead>Gudang</TableHead>
|
||||
<TableHead>Reject</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-if="!cutting.results.length" :key="`${cutting.id}-result-empty`">
|
||||
<TableCell colspan="4" class="text-muted-foreground">
|
||||
Belum ada hasil produk
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<template v-else v-for="group in getGroupedResults(cutting.results)" :key="group.productId">
|
||||
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
||||
<TableCell colspan="4" class="font-semibold text-foreground">
|
||||
{{ group.productName }}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow v-for="result in group.items" :key="result.id">
|
||||
<TableCell class="pl-6 font-medium">
|
||||
{{ result.product_variant?.name }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ result.cutting_result }} pcs
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ result.warehouse_stock }} pcs
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ result.cutting_reject }} pcs
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</template>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,6 +4,7 @@ import { computed } from 'vue';
|
||||
import DataTableActions from '@/components/admin/manage/purchases/data-table-actions.vue';
|
||||
import DataTableToolbar from '@/components/data-table/DataTableToolbar.vue';
|
||||
import MediaThumbnailCell from '@/components/media/MediaThumbnailCell.vue';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Empty,
|
||||
@ -57,6 +58,36 @@ const paginationSummary = computed(() => {
|
||||
function rowNumber(index: number): number {
|
||||
return (props.firstItem ?? 1) + index;
|
||||
}
|
||||
|
||||
interface GroupedPurchaseItems {
|
||||
rawMaterialId: number;
|
||||
rawMaterialName: string;
|
||||
unitLabel?: string;
|
||||
items: any[];
|
||||
}
|
||||
|
||||
function getGroupedItems(items: any[]): GroupedPurchaseItems[] {
|
||||
const groups: Record<number, GroupedPurchaseItems> = {};
|
||||
|
||||
items.forEach((item) => {
|
||||
const rawMaterial = item.raw_material_price?.raw_material;
|
||||
const rawMaterialId = rawMaterial?.id ?? 0;
|
||||
const rawMaterialName = rawMaterial?.name ?? 'Bahan Baku Tidak Diketahui';
|
||||
const unitLabel = rawMaterial?.unit_label;
|
||||
|
||||
if (!groups[rawMaterialId]) {
|
||||
groups[rawMaterialId] = {
|
||||
rawMaterialId,
|
||||
rawMaterialName,
|
||||
unitLabel,
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
groups[rawMaterialId].items.push(item);
|
||||
});
|
||||
|
||||
return Object.values(groups);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -102,7 +133,6 @@ function rowNumber(index: number): number {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Bahan Baku</TableHead>
|
||||
<TableHead>Varian</TableHead>
|
||||
<TableHead>Jumlah</TableHead>
|
||||
<TableHead>Harga Satuan</TableHead>
|
||||
@ -111,27 +141,34 @@ function rowNumber(index: number): number {
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-if="!purchase.items.length" :key="`${purchase.id}-empty`">
|
||||
<TableCell colspan="5" class="text-muted-foreground">
|
||||
<TableCell colspan="4" class="text-muted-foreground">
|
||||
Belum ada item
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow v-for="item in purchase.items" :key="item.id">
|
||||
<TableCell class="font-medium">
|
||||
{{ item.raw_material_price?.raw_material?.name }}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{{ item.raw_material_price?.variant }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ item.quantity_formatted }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ item.unit_price_formatted }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums font-medium">
|
||||
{{ item.subtotal_formatted }}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<template v-else v-for="group in getGroupedItems(purchase.items)" :key="group.rawMaterialId">
|
||||
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
||||
<TableCell colspan="4" class="font-semibold text-foreground">
|
||||
{{ group.rawMaterialName }}
|
||||
<Badge v-if="group.unitLabel" variant="secondary" class="ml-2 font-normal">
|
||||
{{ group.unitLabel }}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow v-for="item in group.items" :key="item.id">
|
||||
<TableCell class="pl-6 font-medium">
|
||||
{{ item.raw_material_price?.variant }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ item.quantity_formatted }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums">
|
||||
{{ item.unit_price_formatted }}
|
||||
</TableCell>
|
||||
<TableCell class="tabular-nums font-medium">
|
||||
{{ item.subtotal_formatted }}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</template>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
@ -17,15 +17,25 @@ export type PurchaseItemListItem = {
|
||||
|
||||
export type PurchaseListItem = {
|
||||
id: number;
|
||||
supplier_name: string;
|
||||
supplier_name?: string;
|
||||
supplier?: {
|
||||
id: number;
|
||||
name: string;
|
||||
} | null;
|
||||
subtotal_formatted: string;
|
||||
discount_formatted: string;
|
||||
total_formatted: string;
|
||||
notes: string | null;
|
||||
created_at_formatted: string;
|
||||
created_by_name: string;
|
||||
created_by_name?: string;
|
||||
created_by?: {
|
||||
username: string;
|
||||
profile?: {
|
||||
full_name: string;
|
||||
} | null;
|
||||
} | null;
|
||||
photos?: MediaItem | null;
|
||||
items: PurchaseItemListItem[];
|
||||
items: any[];
|
||||
};
|
||||
|
||||
export type PurchaseCartItem = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user