From 6fa9224983a98cd8b77102cd2547d87c9eaaf824 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sat, 15 Aug 2026 20:30:04 +0700 Subject: [PATCH] feat: fix raw material photo display in Cutting and Purchase pages by adding conversion URLs --- .../RawMaterial/RawMaterialVariantService.php | 12 +++++ docs/2026-08-15-raw-material-photo-fix.md | 49 +++++++++++++++++++ .../js/pages/admin/manage/cutting/columns.tsx | 1 + .../js/pages/admin/manage/cutting/create.tsx | 23 +++++---- .../js/pages/admin/manage/cutting/edit.tsx | 22 +++++---- .../pages/admin/manage/purchase/columns.tsx | 1 + .../js/pages/admin/manage/purchase/create.tsx | 6 +-- .../js/pages/admin/manage/purchase/edit.tsx | 12 ++--- 8 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 docs/2026-08-15-raw-material-photo-fix.md diff --git a/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php b/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php index 83fb008..a653857 100644 --- a/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php +++ b/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php @@ -24,10 +24,22 @@ public function getForCutting(): array ->select(['id', 'name', 'unit', 'is_active']) ->with([ 'rawMaterialPrices:id,raw_material_id,variant,price,stock', + 'rawMaterialPrices.media', ]) ->active() ->orderBy('name') ->get() + ->each(function (RawMaterial $rawMaterial) { + $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { + $media = $price->getFirstMedia('images'); + $price->photo_url = $media + ? $this->s3Service->getTemporaryUrl($media->getPath()) + : null; + $price->photo_conversion_url = $media + ? $this->s3Service->getTemporaryUrl($media->getPath('thumb')) + : null; + }); + }) ->toArray(); } diff --git a/docs/2026-08-15-raw-material-photo-fix.md b/docs/2026-08-15-raw-material-photo-fix.md new file mode 100644 index 0000000..b810507 --- /dev/null +++ b/docs/2026-08-15-raw-material-photo-fix.md @@ -0,0 +1,49 @@ +# Fix: Foto Bahan Baku Tidak Muncul di Cutting & Belanja + +## Tujuan +Memperbaiki foto bahan baku (raw material) yang tidak muncul di halaman create/edit Cutting dan Belanja (Purchasing). Menampilkan foto conversion (thumbnail) bukan original. + +## File yang Dibaca +- `app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php` → `getForCutting()` method (root cause backend) +- `resources/js/pages/admin/manage/cutting/columns.tsx` → `CuttingCreateData` type (root cause frontend) +- `resources/js/pages/admin/manage/purchase/columns.tsx` → `PurchaseCreateData` type (root cause frontend) +- `resources/js/pages/admin/master/raw-material/columns.tsx` → reference type pattern + +## Root Cause + +### Backend +`getForCutting()` tidak load media atau set `photo_url`/`photo_conversion_url` pada `RawMaterialPrice`. + +### Frontend +Type `CuttingCreateData` dan `PurchaseCreateData` tidak include `photo_conversion_url` di `raw_material_prices` → TypeScript strip property → frontend tidak bisa akses. + +## Perubahan + +### 1. Backend — `RawMaterialVariantService::getForCutting()` +- Tambah eager load `rawMaterialPrices.media` +- Iterasi setiap `RawMaterialPrice` untuk set `photo_url` + `photo_conversion_url` + +### 2. Frontend Types — `columns.tsx` +- `CuttingCreateData.raw_material_prices` → tambah `photo_conversion_url: string | null` +- `PurchaseCreateData.raw_material_prices` → tambah `photo_conversion_url: string | null` + +### 3. Frontend Components — 4 files +- Tambah `photo_conversion_url` ke `MaterialState` type (cutting create/edit) +- Map `photo_conversion_url` dari backend data +- ` ({ material_result: c.material_result, @@ -192,6 +195,7 @@ export default function CuttingCreate({ rawMaterials }: Props) { material_name: rawMaterial.name, unit: rawMaterial.unit, photo_url: price.photo_url, + photo_conversion_url: price.photo_conversion_url, }, ]; }); @@ -242,6 +246,7 @@ export default function CuttingCreate({ rawMaterials }: Props) { material_name: foundMaterial?.name ?? '', unit: foundMaterial?.unit ?? '', photo_url: foundPrice?.photo_url ?? null, + photo_conversion_url: foundPrice?.photo_conversion_url ?? null, }; }); @@ -393,8 +398,8 @@ export default function CuttingCreate({ rawMaterials }: Props) { return (
- {price.photo_url ? ( - {price.variant} + {price.photo_conversion_url ?? price.photo_url ? ( + {price.variant} ) : (
N/A
)} @@ -439,8 +444,8 @@ export default function CuttingCreate({ rawMaterials }: Props) { return (
- {price.photo_url ? ( - {price.variant} + {price.photo_conversion_url ?? price.photo_url ? ( + {price.variant} ) : (
N/A
)} @@ -623,9 +628,9 @@ export default function CuttingCreate({ rawMaterials }: Props) {
- {m.photo_url ? ( + {m.photo_conversion_url ?? m.photo_url ? ( ) : (
N/A
@@ -744,7 +749,7 @@ export default function CuttingCreate({ rawMaterials }: Props) { if (p) { variantName = p.variant; materialName = rm.name; - photoUrl = p.photo_url; + photoUrl = p.photo_conversion_url ?? p.photo_url; break; } } @@ -782,8 +787,8 @@ export default function CuttingCreate({ rawMaterials }: Props) { return (
- {price.photo_url ? ( - {price.variant} + {price.photo_conversion_url ?? price.photo_url ? ( + {price.variant} ) : (
N/A
)} diff --git a/resources/js/pages/admin/manage/cutting/edit.tsx b/resources/js/pages/admin/manage/cutting/edit.tsx index 1f8315d..2c181d2 100644 --- a/resources/js/pages/admin/manage/cutting/edit.tsx +++ b/resources/js/pages/admin/manage/cutting/edit.tsx @@ -33,6 +33,7 @@ type MaterialState = { material_name: string; unit: string; photo_url: string | null; + photo_conversion_url: string | null; }; type CombinationState = { @@ -73,6 +74,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) { material_name: rawMaterial?.name ?? '', unit: rawMaterial?.unit ?? '', photo_url: price?.photo_url ?? m.photo_url ?? null, + photo_conversion_url: price?.photo_conversion_url ?? m.photo_conversion_url ?? null, }; }); }); @@ -175,6 +177,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) { material_name: rawMaterial.name, unit: rawMaterial.unit, photo_url: price.photo_url, + photo_conversion_url: price.photo_conversion_url, }, ]; }); @@ -225,6 +228,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) { material_name: foundMaterial?.name ?? '', unit: foundMaterial?.unit ?? '', photo_url: foundPrice?.photo_url ?? null, + photo_conversion_url: foundPrice?.photo_conversion_url ?? null, }; }); @@ -371,8 +375,8 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) { return (
- {price.photo_url ? ( - {price.variant} + {price.photo_conversion_url ?? price.photo_url ? ( + {price.variant} ) : (
N/A
)} @@ -417,8 +421,8 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) { return (
- {price.photo_url ? ( - {price.variant} + {price.photo_conversion_url ?? price.photo_url ? ( + {price.variant} ) : (
N/A
)} @@ -601,9 +605,9 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) {
- {m.photo_url ? ( + {m.photo_conversion_url ?? m.photo_url ? ( ) : (
N/A
@@ -722,7 +726,7 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) { if (p) { variantName = p.variant; materialName = rm.name; - photoUrl = p.photo_url; + photoUrl = p.photo_conversion_url ?? p.photo_url; break; } } @@ -760,8 +764,8 @@ export default function CuttingEdit({ cutting, rawMaterials }: Props) { return (
- {price.photo_url ? ( - {price.variant} + {price.photo_conversion_url ?? price.photo_url ? ( + {price.variant} ) : (
N/A
)} diff --git a/resources/js/pages/admin/manage/purchase/columns.tsx b/resources/js/pages/admin/manage/purchase/columns.tsx index 16abf9d..b8d3c15 100644 --- a/resources/js/pages/admin/manage/purchase/columns.tsx +++ b/resources/js/pages/admin/manage/purchase/columns.tsx @@ -98,6 +98,7 @@ export type PurchaseCreateData = { price: number; stock: number; photo_url: string | null; + photo_conversion_url: string | null; }[]; }[]; }; diff --git a/resources/js/pages/admin/manage/purchase/create.tsx b/resources/js/pages/admin/manage/purchase/create.tsx index a27ee91..43bf4e6 100644 --- a/resources/js/pages/admin/manage/purchase/create.tsx +++ b/resources/js/pages/admin/manage/purchase/create.tsx @@ -354,7 +354,7 @@ export default function PurchaseCreate({ suppliers, rawMaterials }: Props) { if (price && material) { lines.push({ key: `existing-${id}`, - photoUrl: price.photo_url, + photoUrl: price.photo_conversion_url ?? price.photo_url, title: `${material.name} — ${price.variant}`, subtitle: `${formatCurrency(price.price)} / ${material.unit}`, price: price.price, @@ -876,10 +876,10 @@ export default function PurchaseCreate({ suppliers, rawMaterials }: Props) { } >
- {price.photo_url ? ( + {price.photo_conversion_url ?? price.photo_url ? ( {
- {price.photo_url ? ( -