feat: include and display media images for raw material prices and product variants in cutting management
This commit is contained in:
parent
e76fd8107b
commit
9f0676c923
@ -35,8 +35,9 @@ public function paginateForIndex(array $tableQuery, User $user): LengthAwarePagi
|
|||||||
'createdBy.profile',
|
'createdBy.profile',
|
||||||
'rejection.rejectedBy.profile',
|
'rejection.rejectedBy.profile',
|
||||||
'materials.rawMaterialPrice.rawMaterial:id,name,unit',
|
'materials.rawMaterialPrice.rawMaterial:id,name,unit',
|
||||||
|
'materials.rawMaterialPrice.media',
|
||||||
'results.productVariant.product:id,name',
|
'results.productVariant.product:id,name',
|
||||||
'results.productVariant:id,product_id,name',
|
'results.productVariant.media',
|
||||||
])
|
])
|
||||||
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
||||||
$search = $tableQuery['search'];
|
$search = $tableQuery['search'];
|
||||||
@ -62,6 +63,7 @@ public function paginateForIndex(array $tableQuery, User $user): LengthAwarePagi
|
|||||||
$cutting->setAttribute('available_actions', $this->filterActionsForUser($cutting, $user));
|
$cutting->setAttribute('available_actions', $this->filterActionsForUser($cutting, $user));
|
||||||
$cutting->setAttribute('is_editable', $cutting->status->isEditable());
|
$cutting->setAttribute('is_editable', $cutting->status->isEditable());
|
||||||
$this->appendCostPreview($cutting);
|
$this->appendCostPreview($cutting);
|
||||||
|
$this->appendPhotos($cutting);
|
||||||
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
||||||
|
|
||||||
return $cutting;
|
return $cutting;
|
||||||
@ -75,8 +77,9 @@ public function getInProgressCuttings(User $user): Collection
|
|||||||
'createdBy.profile',
|
'createdBy.profile',
|
||||||
'rejection.rejectedBy.profile',
|
'rejection.rejectedBy.profile',
|
||||||
'materials.rawMaterialPrice.rawMaterial:id,name,unit',
|
'materials.rawMaterialPrice.rawMaterial:id,name,unit',
|
||||||
|
'materials.rawMaterialPrice.media',
|
||||||
'results.productVariant.product:id,name',
|
'results.productVariant.product:id,name',
|
||||||
'results.productVariant:id,product_id,name',
|
'results.productVariant.media',
|
||||||
])
|
])
|
||||||
->inProgress()
|
->inProgress()
|
||||||
->latest()
|
->latest()
|
||||||
@ -85,6 +88,7 @@ public function getInProgressCuttings(User $user): Collection
|
|||||||
$cutting->setAttribute('available_actions', $this->filterActionsForUser($cutting, $user));
|
$cutting->setAttribute('available_actions', $this->filterActionsForUser($cutting, $user));
|
||||||
$cutting->setAttribute('is_editable', $cutting->status->isEditable());
|
$cutting->setAttribute('is_editable', $cutting->status->isEditable());
|
||||||
$this->appendCostPreview($cutting);
|
$this->appendCostPreview($cutting);
|
||||||
|
$this->appendPhotos($cutting);
|
||||||
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -96,8 +100,9 @@ public function getCompletedCuttings(User $user): Collection
|
|||||||
'createdBy.profile',
|
'createdBy.profile',
|
||||||
'rejection.rejectedBy.profile',
|
'rejection.rejectedBy.profile',
|
||||||
'materials.rawMaterialPrice.rawMaterial:id,name,unit',
|
'materials.rawMaterialPrice.rawMaterial:id,name,unit',
|
||||||
|
'materials.rawMaterialPrice.media',
|
||||||
'results.productVariant.product:id,name',
|
'results.productVariant.product:id,name',
|
||||||
'results.productVariant:id,product_id,name',
|
'results.productVariant.media',
|
||||||
])
|
])
|
||||||
->completed()
|
->completed()
|
||||||
->latest()
|
->latest()
|
||||||
@ -106,6 +111,7 @@ public function getCompletedCuttings(User $user): Collection
|
|||||||
$cutting->setAttribute('available_actions', $this->filterActionsForUser($cutting, $user));
|
$cutting->setAttribute('available_actions', $this->filterActionsForUser($cutting, $user));
|
||||||
$cutting->setAttribute('is_editable', $cutting->status->isEditable());
|
$cutting->setAttribute('is_editable', $cutting->status->isEditable());
|
||||||
$this->appendCostPreview($cutting);
|
$this->appendCostPreview($cutting);
|
||||||
|
$this->appendPhotos($cutting);
|
||||||
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -187,14 +193,37 @@ public function findForEdit(Cutting $cutting): Cutting
|
|||||||
'rejection.rejectedBy.profile',
|
'rejection.rejectedBy.profile',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->appendPhotos($cutting);
|
||||||
|
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
||||||
|
|
||||||
|
return $cutting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findForShare(Cutting $cutting): Cutting
|
||||||
|
{
|
||||||
|
$cutting->load([
|
||||||
|
'createdBy.profile',
|
||||||
|
'materials.rawMaterialPrice.rawMaterial:id,name,unit',
|
||||||
|
'materials.rawMaterialPrice.media',
|
||||||
|
'results.productVariant.product:id,name',
|
||||||
|
'results.productVariant.media',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->appendCostPreview($cutting);
|
||||||
|
$this->appendPhotos($cutting);
|
||||||
|
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
||||||
|
|
||||||
|
return $cutting;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendPhotos(Cutting $cutting): void
|
||||||
|
{
|
||||||
$cutting->materials->each(function (CuttingMaterial $material): void {
|
$cutting->materials->each(function (CuttingMaterial $material): void {
|
||||||
$price = $material->rawMaterialPrice;
|
$price = $material->rawMaterialPrice;
|
||||||
|
|
||||||
if ($price) {
|
if ($price) {
|
||||||
$price->setAttribute('images', MediaPresenter::collection($price, 'images'));
|
$price->setAttribute('images', MediaPresenter::collection($price, 'images'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->breakMaterialCircularReference($material);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$cutting->results->each(function (CuttingResult $result): void {
|
$cutting->results->each(function (CuttingResult $result): void {
|
||||||
@ -207,23 +236,6 @@ public function findForEdit(Cutting $cutting): Cutting
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return $cutting;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function findForShare(Cutting $cutting): Cutting
|
|
||||||
{
|
|
||||||
$cutting->load([
|
|
||||||
'createdBy.profile',
|
|
||||||
'materials.rawMaterialPrice.rawMaterial:id,name,unit',
|
|
||||||
'results.productVariant.product:id,name',
|
|
||||||
'results.productVariant:id,product_id,name',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->appendCostPreview($cutting);
|
|
||||||
$cutting->materials->each(fn (CuttingMaterial $m) => $this->breakMaterialCircularReference($m));
|
|
||||||
|
|
||||||
return $cutting;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function draftMaterialsForUser(User $user): array
|
public function draftMaterialsForUser(User $user): array
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from '@/components/ui/table';
|
} from '@/components/ui/table';
|
||||||
import { cuttingStatusBadgeVariant } from '@/constants/cutting-status';
|
import { cuttingStatusBadgeVariant } from '@/constants/cutting-status';
|
||||||
|
import MediaThumbnailCell from '@/components/media/MediaThumbnailCell.vue';
|
||||||
import type { CuttingListItem } from '@/types/cutting';
|
import type { CuttingListItem } from '@/types/cutting';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@ -120,13 +121,14 @@ const groupedResults = computed<GroupedResults[]>(() => {
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Varian</TableHead>
|
<TableHead>Varian</TableHead>
|
||||||
|
<TableHead>Foto</TableHead>
|
||||||
<TableHead class="text-right">Pemakaian</TableHead>
|
<TableHead class="text-right">Pemakaian</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<template v-for="group in groupedMaterials" :key="group.key">
|
<template v-for="group in groupedMaterials" :key="group.key">
|
||||||
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
||||||
<TableCell colspan="2" class="font-semibold">
|
<TableCell colspan="3" class="font-semibold">
|
||||||
{{ group.name }}
|
{{ group.name }}
|
||||||
<Badge v-if="group.unitLabel" variant="secondary" class="ml-2 font-normal">
|
<Badge v-if="group.unitLabel" variant="secondary" class="ml-2 font-normal">
|
||||||
{{ group.unitLabel }}
|
{{ group.unitLabel }}
|
||||||
@ -137,6 +139,9 @@ const groupedResults = computed<GroupedResults[]>(() => {
|
|||||||
<TableCell class="pl-6">
|
<TableCell class="pl-6">
|
||||||
{{ material.variant }}
|
{{ material.variant }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<MediaThumbnailCell :items="material.images ?? []" />
|
||||||
|
</TableCell>
|
||||||
<TableCell class="text-right tabular-nums">
|
<TableCell class="text-right tabular-nums">
|
||||||
{{ material.material_usage_formatted }}
|
{{ material.material_usage_formatted }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@ -155,6 +160,7 @@ const groupedResults = computed<GroupedResults[]>(() => {
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Varian</TableHead>
|
<TableHead>Varian</TableHead>
|
||||||
|
<TableHead>Foto</TableHead>
|
||||||
<TableHead class="text-right">Hasil</TableHead>
|
<TableHead class="text-right">Hasil</TableHead>
|
||||||
<TableHead class="text-right">Sample</TableHead>
|
<TableHead class="text-right">Sample</TableHead>
|
||||||
<TableHead class="text-right">Diluar Sample</TableHead>
|
<TableHead class="text-right">Diluar Sample</TableHead>
|
||||||
@ -163,7 +169,7 @@ const groupedResults = computed<GroupedResults[]>(() => {
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
<template v-for="group in groupedResults" :key="group.key">
|
<template v-for="group in groupedResults" :key="group.key">
|
||||||
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
||||||
<TableCell colspan="4" class="font-semibold">
|
<TableCell colspan="5" class="font-semibold">
|
||||||
{{ group.name }}
|
{{ group.name }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@ -171,6 +177,9 @@ const groupedResults = computed<GroupedResults[]>(() => {
|
|||||||
<TableCell class="pl-6">
|
<TableCell class="pl-6">
|
||||||
{{ result.product_variant?.name }}
|
{{ result.product_variant?.name }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<MediaThumbnailCell :items="result.product_variant?.images ?? []" />
|
||||||
|
</TableCell>
|
||||||
<TableCell class="text-right tabular-nums">
|
<TableCell class="text-right tabular-nums">
|
||||||
{{ result.cutting_result }} pcs
|
{{ result.cutting_result }} pcs
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
} from '@/components/ui/table';
|
} from '@/components/ui/table';
|
||||||
import { usePaginationSummary } from '@/composables/usePaginationSummary';
|
import { usePaginationSummary } from '@/composables/usePaginationSummary';
|
||||||
import { cuttingStatusBadgeVariant } from '@/constants/cutting-status';
|
import { cuttingStatusBadgeVariant } from '@/constants/cutting-status';
|
||||||
|
import MediaThumbnailCell from '@/components/media/MediaThumbnailCell.vue';
|
||||||
import { groupedTableRowNumber } from '@/lib/grouped-table';
|
import { groupedTableRowNumber } from '@/lib/grouped-table';
|
||||||
import type { CuttingListItem } from '@/types/cutting';
|
import type { CuttingListItem } from '@/types/cutting';
|
||||||
import type {
|
import type {
|
||||||
@ -155,19 +156,20 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Varian</TableHead>
|
<TableHead>Varian</TableHead>
|
||||||
|
<TableHead>Foto</TableHead>
|
||||||
<TableHead>Pemakaian</TableHead>
|
<TableHead>Pemakaian</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<TableRow v-if="!cutting.materials.length" :key="`${cutting.id}-material-empty`">
|
<TableRow v-if="!cutting.materials.length" :key="`${cutting.id}-material-empty`">
|
||||||
<TableCell colspan="2" class="text-muted-foreground">
|
<TableCell colspan="3" class="text-muted-foreground">
|
||||||
Belum ada bahan baku
|
Belum ada bahan baku
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<template v-else v-for="group in getGroupedMaterials(cutting.materials)"
|
<template v-else v-for="group in getGroupedMaterials(cutting.materials)"
|
||||||
:key="group.rawMaterialId">
|
:key="group.rawMaterialId">
|
||||||
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
||||||
<TableCell colspan="2" class="font-semibold text-foreground">
|
<TableCell colspan="3" class="font-semibold text-foreground">
|
||||||
{{ group.rawMaterialName }}
|
{{ group.rawMaterialName }}
|
||||||
<Badge v-if="group.unitLabel" variant="secondary"
|
<Badge v-if="group.unitLabel" variant="secondary"
|
||||||
class="ml-2 font-normal">
|
class="ml-2 font-normal">
|
||||||
@ -179,6 +181,9 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
|
|||||||
<TableCell class="pl-6 font-medium">
|
<TableCell class="pl-6 font-medium">
|
||||||
{{ material.variant }}
|
{{ material.variant }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<MediaThumbnailCell :items="material.images ?? []" />
|
||||||
|
</TableCell>
|
||||||
<TableCell class="tabular-nums">
|
<TableCell class="tabular-nums">
|
||||||
{{ material.material_usage_formatted }}
|
{{ material.material_usage_formatted }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@ -196,6 +201,7 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Varian</TableHead>
|
<TableHead>Varian</TableHead>
|
||||||
|
<TableHead>Foto</TableHead>
|
||||||
<TableHead>Hasil</TableHead>
|
<TableHead>Hasil</TableHead>
|
||||||
<TableHead>Sample</TableHead>
|
<TableHead>Sample</TableHead>
|
||||||
<TableHead>Diluar Sample</TableHead>
|
<TableHead>Diluar Sample</TableHead>
|
||||||
@ -203,14 +209,14 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
|
|||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<TableRow v-if="!cutting.results.length" :key="`${cutting.id}-result-empty`">
|
<TableRow v-if="!cutting.results.length" :key="`${cutting.id}-result-empty`">
|
||||||
<TableCell colspan="4" class="text-muted-foreground">
|
<TableCell colspan="5" class="text-muted-foreground">
|
||||||
Belum ada hasil produk
|
Belum ada hasil produk
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<template v-else v-for="group in getGroupedResults(cutting.results)"
|
<template v-else v-for="group in getGroupedResults(cutting.results)"
|
||||||
:key="group.productId">
|
:key="group.productId">
|
||||||
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
<TableRow class="bg-muted/20 hover:bg-muted/20">
|
||||||
<TableCell colspan="4" class="font-semibold text-foreground">
|
<TableCell colspan="5" class="font-semibold text-foreground">
|
||||||
{{ group.productName }}
|
{{ group.productName }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@ -218,6 +224,9 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
|
|||||||
<TableCell class="pl-6 font-medium">
|
<TableCell class="pl-6 font-medium">
|
||||||
{{ result.product_variant?.name }}
|
{{ result.product_variant?.name }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<MediaThumbnailCell :items="result.product_variant?.images ?? []" />
|
||||||
|
</TableCell>
|
||||||
<TableCell class="tabular-nums">
|
<TableCell class="tabular-nums">
|
||||||
{{ result.cutting_result }} pcs
|
{{ result.cutting_result }} pcs
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@ -18,9 +18,11 @@ export type CuttingMaterialListItem = {
|
|||||||
raw_material_id?: number;
|
raw_material_id?: number;
|
||||||
raw_material_name?: string;
|
raw_material_name?: string;
|
||||||
raw_material_unit_label?: string;
|
raw_material_unit_label?: string;
|
||||||
|
images?: MediaItem[];
|
||||||
raw_material_price?: {
|
raw_material_price?: {
|
||||||
id: number;
|
id: number;
|
||||||
variant: string;
|
variant: string;
|
||||||
|
images?: MediaItem[];
|
||||||
raw_material?: {
|
raw_material?: {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@ -50,6 +52,7 @@ export type CuttingResultListItem = {
|
|||||||
product_variant?: {
|
product_variant?: {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
images?: MediaItem[];
|
||||||
product?: {
|
product?: {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user