diff --git a/app/Media/PathGenerators/CustomPathGenerator.php b/app/Media/PathGenerators/CustomPathGenerator.php index e289c4d..787afc4 100644 --- a/app/Media/PathGenerators/CustomPathGenerator.php +++ b/app/Media/PathGenerators/CustomPathGenerator.php @@ -9,6 +9,16 @@ class CustomPathGenerator implements PathGenerator { public function getPath(Media $media): string { + $s3Key = $media->getCustomProperty('s3_key'); + + if ($s3Key) { + return dirname($s3Key).'/'; + } + + if (str_contains($media->file_name, '/')) { + return ''; + } + $model = $media->model; if ($model && method_exists($model, 'getMediaPath')) { @@ -20,20 +30,44 @@ public function getPath(Media $media): string public function getPathForConversions(Media $media): string { - return $this->getPath($media).'conversions/'; + $s3Key = $media->getCustomProperty('s3_key'); + $dir = $s3Key ? dirname($s3Key).'/' : $this->defaultPath($media); + + return $dir.'conversions/'; } public function getPathForResponsiveImages(Media $media): string { - return $this->getPath($media).'responsive/'; + $s3Key = $media->getCustomProperty('s3_key'); + $dir = $s3Key ? dirname($s3Key).'/' : $this->defaultPath($media); + + return $dir.'responsive/'; } private function defaultPath(Media $media): string { - $module = strtolower(class_basename($media->model_type)); - $date = $media->created_at->format('Y/m/d'); - $id = $media->id; + $moduleMap = [ + 'UserProfile' => 'user-profile', + 'Attendance' => 'attendance', + 'SystemConfiguration' => 'setting', + 'HomepageConfiguration' => 'homepage', + 'ProductVariant' => 'product', + 'Cutting' => 'cutting', + 'RawMaterialPrice' => 'raw-material', + 'CashTransaction' => 'cash', + 'Expense' => 'expense', + 'Order' => 'order', + 'Restock' => 'restock', + 'Purchase' => 'purchase', + 'OwnerVerificationRequest' => 'owner_verification_request', + ]; - return "{$module}/{$date}/{$id}/"; + $modelClass = class_basename($media->model_type); + $module = $moduleMap[$modelClass] ?? strtolower($modelClass); + $collection = $media->collection_name; + $date = $media->created_at->format('Y-m-d'); + $id = $media->model_id; + + return "{$module}/{$collection}/{$date}/{$id}/"; } } diff --git a/app/Models/RawMaterialPrice.php b/app/Models/RawMaterialPrice.php index 5c7e517..f0f8ac5 100644 --- a/app/Models/RawMaterialPrice.php +++ b/app/Models/RawMaterialPrice.php @@ -55,10 +55,10 @@ public function rawMaterial(): BelongsTo public function getPhotoUrlAttribute(): ?string { - $media = $this->getFirstMedia('photos'); + $media = $this->getFirstMedia('images'); return $media - ? app(S3PresignedService::class)->getTemporaryUrl($media->file_name) + ? app(S3PresignedService::class)->getTemporaryUrl($media->getPath()) : null; } } diff --git a/app/Services/Admin/Finance/Cash/CashTransactionService.php b/app/Services/Admin/Finance/Cash/CashTransactionService.php index de28db6..34425f9 100644 --- a/app/Services/Admin/Finance/Cash/CashTransactionService.php +++ b/app/Services/Admin/Finance/Cash/CashTransactionService.php @@ -56,7 +56,7 @@ public function deposit(array $data): CashTransaction )); if (array_key_exists('receipt_key', $data)) { - $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'receipts', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); + $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'photos', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); } NotificationService::notify( @@ -78,7 +78,7 @@ public function withdrawal(array $data): CashTransaction )); if (array_key_exists('receipt_key', $data)) { - $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'receipts', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); + $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'photos', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); } NotificationService::notify( @@ -123,7 +123,7 @@ public function update(CashTransaction $transaction, array $data): CashTransacti ]); if (array_key_exists('receipt_key', $data)) { - $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'receipts', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); + $this->syncReceipt($transaction, $data['receipt_key'] ?? null, 'photos', 'cash_transaction_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); } return $transaction; @@ -163,12 +163,12 @@ public function destroy(CashTransaction $transaction): bool $cashAccount->update(['balance' => $newBalance]); - $media = $transaction->getFirstMedia('receipts'); + $media = $transaction->getFirstMedia('photos'); if ($media) { Cache::forget("cash_transaction_receipt_{$media->id}"); } - $transaction->clearMediaCollection('receipts'); + $transaction->clearMediaCollection('photos'); $deleted = $transaction->delete(); @@ -187,7 +187,7 @@ public function destroy(CashTransaction $transaction): bool private function formatTransaction(CashTransaction $transaction): array { - $media = $transaction->getFirstMedia('receipts'); + $media = $transaction->getFirstMedia('photos'); if (! $media) { return $transaction->toArray() + [ @@ -196,11 +196,7 @@ private function formatTransaction(CashTransaction $transaction): array ]; } - $s3Key = $media->file_name; - - if (str_starts_with($s3Key, '/') || ! str_contains($s3Key, '/')) { - $s3Key = $media->getPath(); - } + $s3Key = $media->getCustomProperty('s3_key') ?? $media->getPath(); return $transaction->toArray() + [ 'receipt_key' => $s3Key, diff --git a/app/Services/Admin/Finance/ExpenseService.php b/app/Services/Admin/Finance/ExpenseService.php index ff03d0e..cf81d02 100644 --- a/app/Services/Admin/Finance/ExpenseService.php +++ b/app/Services/Admin/Finance/ExpenseService.php @@ -53,7 +53,7 @@ public function store(array $data): Expense ]); if (array_key_exists('receipt_key', $data)) { - $this->syncReceipt($expense, $data['receipt_key'] ?? null, 'receipts', 'expense_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); + $this->syncReceipt($expense, $data['receipt_key'] ?? null, 'photos', 'expense_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); } return $expense; @@ -100,7 +100,7 @@ public function update(Expense $expense, array $data): Expense ]); if (array_key_exists('receipt_key', $data)) { - $this->syncReceipt($expense, $data['receipt_key'] ?? null, 'receipts', 'expense_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); + $this->syncReceipt($expense, $data['receipt_key'] ?? null, 'photos', 'expense_receipt', $data['file_size'] ?? null, $data['file_mime_type'] ?? null); } return $expense; @@ -125,12 +125,12 @@ public function destroy(Expense $expense): bool type: CashTransactionType::DEPOSIT, ); - $media = $expense->getFirstMedia('receipts'); + $media = $expense->getFirstMedia('photos'); if ($media) { Cache::forget("expense_receipt_{$media->id}"); } - $expense->clearMediaCollection('receipts'); + $expense->clearMediaCollection('photos'); $deleted = $expense->delete(); @@ -149,7 +149,7 @@ public function destroy(Expense $expense): bool private function formatExpense(Expense $expense): array { - $media = $expense->getFirstMedia('receipts'); + $media = $expense->getFirstMedia('photos'); if (! $media) { return $expense->toArray() + [ @@ -158,11 +158,7 @@ private function formatExpense(Expense $expense): array ]; } - $s3Key = $media->file_name; - - if (str_starts_with($s3Key, '/') || ! str_contains($s3Key, '/')) { - $s3Key = $media->getPath(); - } + $s3Key = $media->getCustomProperty('s3_key') ?? $media->getPath(); $cacheKey = "expense_receipt_{$media->id}"; $receiptUrl = Cache::remember($cacheKey, now()->addMinutes(55), function () use ($s3Key) { diff --git a/app/Services/Admin/HR/AttendanceService.php b/app/Services/Admin/HR/AttendanceService.php index de1e722..5119a9c 100644 --- a/app/Services/Admin/HR/AttendanceService.php +++ b/app/Services/Admin/HR/AttendanceService.php @@ -168,7 +168,7 @@ public function checkIn(array $data): Attendance ]); if (! empty($data['photo'])) { - $this->registerMediaFromBase64($attendance, $data['photo'], 'check-in', 'attendances'); + $this->registerMediaFromBase64($attendance, $data['photo'], 'checkin', 'attendances'); } NotificationService::notify( @@ -190,7 +190,7 @@ public function checkOut(Attendance $attendance, array $data): Attendance ]); if (! empty($data['photo'])) { - $this->registerMediaFromBase64($attendance, $data['photo'], 'check-out', 'attendances'); + $this->registerMediaFromBase64($attendance, $data['photo'], 'checkout', 'attendances'); } NotificationService::notify( @@ -303,15 +303,15 @@ private function formatAttendance(Attendance $attendance): array $toArray['work_duration_minutes'] = $checkIn->diffInMinutes($end); } - $checkInMedia = $attendance->getFirstMedia('check-in'); - $checkOutMedia = $attendance->getFirstMedia('check-out'); + $checkInMedia = $attendance->getFirstMedia('checkin'); + $checkOutMedia = $attendance->getFirstMedia('checkout'); $toArray['check_in_photo'] = $checkInMedia - ? Cache::remember("attendance_check_in_{$checkInMedia->id}", now()->addMinutes(55), fn () => $this->s3Service->getTemporaryUrl($checkInMedia->file_name)) + ? Cache::remember("attendance_check_in_{$checkInMedia->id}", now()->addMinutes(55), fn () => $this->s3Service->getTemporaryUrl($checkInMedia->getPath())) : null; $toArray['check_out_photo'] = $checkOutMedia - ? Cache::remember("attendance_check_out_{$checkOutMedia->id}", now()->addMinutes(55), fn () => $this->s3Service->getTemporaryUrl($checkOutMedia->file_name)) + ? Cache::remember("attendance_check_out_{$checkOutMedia->id}", now()->addMinutes(55), fn () => $this->s3Service->getTemporaryUrl($checkOutMedia->getPath())) : null; return $toArray; diff --git a/app/Services/Admin/Manage/CuttingService.php b/app/Services/Admin/Manage/CuttingService.php index 57ce51d..41ba3ab 100644 --- a/app/Services/Admin/Manage/CuttingService.php +++ b/app/Services/Admin/Manage/CuttingService.php @@ -42,16 +42,16 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = ->paginate($perPage); $paginator->getCollection()->each(function (Cutting $cutting) { - $cuttingMedia = $cutting->getFirstMedia('photos'); + $cuttingMedia = $cutting->getFirstMedia('images'); $cutting->photo_url = $cuttingMedia - ? $this->s3Service->getTemporaryUrl($cuttingMedia->file_name) + ? $this->s3Service->getTemporaryUrl($cuttingMedia->getPath()) : null; $cutting->cuttingMaterials->each(function (CuttingMaterial $material) { - $media = $material->rawMaterialPrice?->getFirstMedia('photos'); + $media = $material->rawMaterialPrice?->getFirstMedia('images'); if ($material->rawMaterialPrice) { $material->rawMaterialPrice->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->file_name) + ? $this->s3Service->getTemporaryUrl($media->getPath()) : null; } }); @@ -71,7 +71,7 @@ public function getForEdit(Cutting $cutting): array $result = $cutting->cuttingResults->first(); $materials = $cutting->cuttingMaterials->map(function (CuttingMaterial $material) { - $media = $material->rawMaterialPrice?->getFirstMedia('photos'); + $media = $material->rawMaterialPrice?->getFirstMedia('images'); return [ 'id' => $material->id, @@ -81,7 +81,7 @@ public function getForEdit(Cutting $cutting): array 'combination_id' => $material->combination_id, 'variant' => $material->rawMaterialPrice?->variant, 'photo_url' => $media - ? $this->s3Service->getTemporaryUrl($media->file_name) + ? $this->s3Service->getTemporaryUrl($media->getPath()) : null, ]; }); @@ -99,10 +99,10 @@ public function getForEdit(Cutting $cutting): array ]; }); - $cuttingMedia = $cutting->getFirstMedia('photos'); - $photoKey = $cuttingMedia?->file_name; + $cuttingMedia = $cutting->getFirstMedia('images'); + $photoKey = $cuttingMedia?->getCustomProperty('s3_key') ?? $cuttingMedia?->file_name; $photoUrl = $cuttingMedia - ? $this->s3Service->getTemporaryUrl($cuttingMedia->file_name) + ? $this->s3Service->getTemporaryUrl($cuttingMedia->getPath()) : null; return [ @@ -213,7 +213,7 @@ public function store(array $data): Cutting $this->registerMedia( model: $cutting, s3Key: $data['photo_key'], - collectionName: 'photos', + collectionName: 'images', orderColumn: 1, ); } @@ -337,7 +337,7 @@ public function destroy(Cutting $cutting): bool } } - $cutting->clearMediaCollection('photos'); + $cutting->clearMediaCollection('images'); $cutting->cuttingResults()->delete(); $cutting->cuttingMaterials()->delete(); $cutting->cuttingMaterialCombinations()->delete(); diff --git a/app/Services/Admin/Manage/PurchaseService.php b/app/Services/Admin/Manage/PurchaseService.php index 342d9c7..0705100 100644 --- a/app/Services/Admin/Manage/PurchaseService.php +++ b/app/Services/Admin/Manage/PurchaseService.php @@ -47,7 +47,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = $paginator->getCollection()->each(function (Purchase $purchase) { $purchaseMedia = $purchase->getFirstMedia('photos'); $purchase->photo_url = $purchaseMedia - ? $this->s3Service->getTemporaryUrl($purchaseMedia->file_name) + ? $this->s3Service->getTemporaryUrl($purchaseMedia->getPath()) : null; $purchase->purchaseItems->each(function (PurchaseItem $item) { @@ -55,9 +55,9 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = return; } - $media = $item->rawMaterialPrice->getMedia('photos'); + $media = $item->rawMaterialPrice->getMedia('images'); $item->rawMaterialPrice->photo_url = $media->first() - ? $this->s3Service->getTemporaryUrl($media->first()->file_name) + ? $this->s3Service->getTemporaryUrl($media->first()->getPath()) : null; }); }); @@ -78,9 +78,9 @@ public function getForCreate(): array ->get() ->each(function (RawMaterial $rawMaterial) { $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { - $media = $price->getFirstMedia('photos'); + $media = $price->getFirstMedia('images'); $price->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->file_name) + ? $this->s3Service->getTemporaryUrl($media->getPath()) : null; }); }), @@ -103,15 +103,15 @@ public function getForEdit(Purchase $purchase): array return null; } - $media = $item->rawMaterialPrice->getFirstMedia('photos'); + $media = $item->rawMaterialPrice->getFirstMedia('images'); return [ 'id' => $item->rawMaterialPrice->id, 'variant' => $item->rawMaterialPrice->variant, 'price' => $item->unit_price, 'stock' => $item->quantity, - 'photo_key' => $media?->file_name, - 'photo_url' => $media ? $this->s3Service->getTemporaryUrl($media->file_name) : null, + 'photo_key' => $media?->getCustomProperty('s3_key') ?? $media?->file_name, + 'photo_url' => $media ? $this->s3Service->getTemporaryUrl($media->getPath()) : null, ]; })->filter()->values(); @@ -129,9 +129,9 @@ public function getForEdit(Purchase $purchase): array ->exists(); $purchaseMedia = $purchase->getFirstMedia('photos'); - $purchasePhotoKey = $purchaseMedia?->file_name; + $purchasePhotoKey = $purchaseMedia?->getCustomProperty('s3_key') ?? $purchaseMedia?->file_name; $purchasePhotoUrl = $purchaseMedia - ? $this->s3Service->getTemporaryUrl($purchaseMedia->file_name) + ? $this->s3Service->getTemporaryUrl($purchaseMedia->getPath()) : null; return [ @@ -267,7 +267,7 @@ private function storeNew(array $data): Purchase $this->registerMedia( model: $priceModel, s3Key: $variantData['photo_key'], - collectionName: 'photos', + collectionName: 'images', orderColumn: 1, ); } @@ -412,11 +412,12 @@ public function update(Purchase $purchase, array $data): Purchase ]); } - if (! empty($v['photo_key']) && $price->getFirstMedia('photos')?->file_name !== $v['photo_key']) { + if (! empty($v['photo_key']) && $price->getFirstMedia('images')?->getCustomProperty('s3_key') !== $v['photo_key']) { + $price->clearMediaCollection('images'); $this->registerMedia( model: $price, s3Key: $v['photo_key'], - collectionName: 'photos', + collectionName: 'images', orderColumn: 1, ); } diff --git a/app/Services/Admin/Manage/RestockService.php b/app/Services/Admin/Manage/RestockService.php index ebb50a9..8d090c5 100644 --- a/app/Services/Admin/Manage/RestockService.php +++ b/app/Services/Admin/Manage/RestockService.php @@ -49,9 +49,9 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = return; } - $media = $item->productVariant->getFirstMedia('photos'); + $media = $item->productVariant->getFirstMedia('images'); $item->productVariant->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->file_name) + ? $this->s3Service->getTemporaryUrl($media->getPath()) : null; }); }); diff --git a/app/Services/Admin/Manage/TransactionService.php b/app/Services/Admin/Manage/TransactionService.php index 074c5fa..e518dec 100644 --- a/app/Services/Admin/Manage/TransactionService.php +++ b/app/Services/Admin/Manage/TransactionService.php @@ -74,14 +74,19 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = $order->channel_label = $order->channel->label(); $order->price_type_label = $order->price_type->label(); + $orderMedia = $order->getFirstMedia('photos'); + $order->photo_url = $orderMedia + ? $this->s3Service->getTemporaryUrl($orderMedia->getPath()) + : null; + $order->orderItems->each(function (OrderItem $item) { if (! $item->productVariant) { return; } - $media = $item->productVariant->getFirstMedia('photos'); + $media = $item->productVariant->getFirstMedia('images'); $item->productVariant->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->file_name) + ? $this->s3Service->getTemporaryUrl($media->getPath()) : null; }); diff --git a/app/Services/Admin/Master/Product/ProductService.php b/app/Services/Admin/Master/Product/ProductService.php index 6ee2520..a1693f1 100644 --- a/app/Services/Admin/Master/Product/ProductService.php +++ b/app/Services/Admin/Master/Product/ProductService.php @@ -57,8 +57,8 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = $paginator->getCollection()->each(function ($product) { $product->productVariants->each(function ($variant) { - $media = $variant->getMedia('photos'); - $variant->photo_urls = $media->map(fn($m) => $this->s3Service->getTemporaryUrl($m->file_name))->toArray(); + $media = $variant->getMedia('images'); + $variant->photo_urls = $media->map(fn($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray(); }); }); @@ -160,9 +160,9 @@ public function getForEdit(Product $product): array ]); $variants = $product->productVariants->map(function (ProductVariant $variant) { - $media = $variant->getMedia('photos'); - $photoKeys = $media->pluck('file_name')->toArray(); - $photoUrls = $media->map(fn($m) => $this->s3Service->getTemporaryUrl($m->file_name))->toArray(); + $media = $variant->getMedia('images'); + $photoKeys = $media->map(fn($m) => $m->getCustomProperty('s3_key') ?? $m->file_name)->toArray(); + $photoUrls = $media->map(fn($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray(); return [ 'id' => $variant->id, @@ -222,7 +222,7 @@ public function update(Product $product, array $data): Product ->whereNotIn('id', $existingVariantIds) ->each(function (ProductVariant $variant) { $variant->productPrices()->delete(); - $variant->clearMediaCollection('photos'); + $variant->clearMediaCollection('images'); $variant->delete(); }); @@ -379,14 +379,7 @@ public function update(Product $product, array $data): Product continue; } - // Compare existing photo keys vs new ones - $existingKeys = $variant->getMedia('photos')->pluck('file_name')->sort()->values()->toArray(); - $newKeys = collect($variantData['photo_keys'])->sort()->values()->toArray(); - - if ($existingKeys !== $newKeys) { - $variant->clearMediaCollection('photos'); - $this->variantService->registerPhotos($variant, $variantData['photo_keys']); - } + $this->variantService->syncPhotos($variant, $variantData['photo_keys']); } return $product; @@ -409,7 +402,7 @@ public function destroy(Product $product): bool $result = DB::transaction(function () use ($product) { $product->productVariants->each(function (ProductVariant $variant) { $variant->productPrices()->delete(); - $variant->clearMediaCollection('photos'); + $variant->clearMediaCollection('images'); $variant->delete(); }); diff --git a/app/Services/Admin/Master/Product/ProductVariantService.php b/app/Services/Admin/Master/Product/ProductVariantService.php index a954911..8751011 100644 --- a/app/Services/Admin/Master/Product/ProductVariantService.php +++ b/app/Services/Admin/Master/Product/ProductVariantService.php @@ -37,9 +37,9 @@ public function getForRestock(): array ->get() ->each(function (Product $product) { $product->productVariants->each(function (ProductVariant $variant) { - $media = $variant->getFirstMedia('photos'); + $media = $variant->getFirstMedia('images'); $variant->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->file_name) + ? $this->s3Service->getTemporaryUrl($media->getPath()) : null; $capitalPrice = $variant->productPrices @@ -66,9 +66,9 @@ public function getForTransaction(): array ->get() ->each(function (Product $product) { $product->productVariants->each(function (ProductVariant $variant) { - $media = $variant->getFirstMedia('photos'); + $media = $variant->getFirstMedia('images'); $variant->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->file_name) + ? $this->s3Service->getTemporaryUrl($media->getPath()) : null; $prices = $variant->productPrices->mapWithKeys(fn ($p) => [$p->type->value => $p->price]); @@ -84,9 +84,9 @@ public function getForEdit(ProductVariant $variant): array 'media', ]); - $media = $variant->getMedia('photos'); - $photoKeys = $media->pluck('file_name')->toArray(); - $photoUrls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->file_name))->toArray(); + $media = $variant->getMedia('images'); + $photoKeys = $media->map(fn ($m) => $m->getCustomProperty('s3_key') ?? $m->file_name)->toArray(); + $photoUrls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray(); return [ 'id' => $variant->id, @@ -136,8 +136,7 @@ public function update(ProductVariant $variant, array $data): ProductVariant ); if (! empty($data['photo_keys']) && is_array($data['photo_keys'])) { - $variant->clearMediaCollection('photos'); - $this->registerPhotos($variant, $data['photo_keys']); + $this->syncPhotos($variant, $data['photo_keys']); } }); @@ -157,7 +156,7 @@ public function destroy(Product $product, ProductVariant $variant): bool $result = DB::transaction(function () use ($variant) { $variant->productPrices()->delete(); - $variant->clearMediaCollection('photos'); + $variant->clearMediaCollection('images'); return $variant->delete(); }); @@ -172,25 +171,6 @@ public function destroy(Product $product, ProductVariant $variant): bool return $result; } - public function registerPhotos(ProductVariant $variant, array $photoKeys): void - { - foreach ($photoKeys as $order => $s3Key) { - $this->registerMedia( - model: $variant, - s3Key: $s3Key, - collectionName: 'photos', - orderColumn: $order + 1, - ); - } - } - - public function getTemporaryUrls(ProductVariant $variant): array - { - $media = $variant->getMedia('photos'); - - return $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->file_name))->toArray(); - } - public function transferStock(ProductVariant $variant, array $data): ProductVariant { $quantity = (int) $data['quantity']; diff --git a/app/Services/Admin/Master/RawMaterial/RawMaterialService.php b/app/Services/Admin/Master/RawMaterial/RawMaterialService.php index 4726662..4dbcae0 100644 --- a/app/Services/Admin/Master/RawMaterial/RawMaterialService.php +++ b/app/Services/Admin/Master/RawMaterial/RawMaterialService.php @@ -39,9 +39,9 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = $paginator->getCollection()->each(function ($rawMaterial) { $rawMaterial->rawMaterialPrices->each(function ($price) { - $media = $price->getMedia('photos'); + $media = $price->getMedia('images'); $price->photo_url = $media->first() - ? $this->s3Service->getTemporaryUrl($media->first()->file_name) + ? $this->s3Service->getTemporaryUrl($media->first()->getPath()) : null; }); }); @@ -92,19 +92,17 @@ public function getForEdit(RawMaterial $rawMaterial): array ]); $variants = $rawMaterial->rawMaterialPrices->map(function (RawMaterialPrice $price) { - $media = $price->getMedia('photos'); - $photoKey = $media->first()?->file_name; - $photoUrl = $media->first() - ? $this->s3Service->getTemporaryUrl($media->first()->file_name) - : null; + $media = $price->getMedia('images'); + $photoKeys = $media->map(fn ($m) => $m->getCustomProperty('s3_key') ?? $m->file_name)->toArray(); + $photoUrls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray(); return [ 'id' => $price->id, 'variant' => $price->variant, 'price' => $price->price, 'stock' => $price->stock, - 'photo_key' => $photoKey, - 'photo_url' => $photoUrl, + 'photo_key' => $photoKeys[0] ?? null, + 'photo_url' => $photoUrls[0] ?? null, ]; }); @@ -134,7 +132,7 @@ public function update(RawMaterial $rawMaterial, array $data): RawMaterial $rawMaterial->rawMaterialPrices() ->whereNotIn('id', $existingVariantIds) ->each(function (RawMaterialPrice $price) { - $price->clearMediaCollection('photos'); + $price->clearMediaCollection('images'); $price->delete(); }); @@ -184,9 +182,9 @@ public function update(RawMaterial $rawMaterial, array $data): RawMaterial ]); if (isset($variantData['photo_key'])) { - $existingKey = $priceModel->getMedia('photos')->first()?->file_name; + $existingKey = $priceModel->getMedia('images')->first()?->getCustomProperty('s3_key'); if ($existingKey !== $variantData['photo_key']) { - $priceModel->clearMediaCollection('photos'); + $priceModel->clearMediaCollection('images'); if ($variantData['photo_key']) { $this->registerPhoto($priceModel, $variantData['photo_key']); } @@ -202,7 +200,7 @@ public function destroy(RawMaterial $rawMaterial): bool { return DB::transaction(function () use ($rawMaterial) { $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { - $price->clearMediaCollection('photos'); + $price->clearMediaCollection('images'); $price->delete(); }); @@ -216,14 +214,4 @@ public function toggleStatus(RawMaterial $rawMaterial): void 'is_active' => ! $rawMaterial->is_active, ]); } - - private function registerPhoto(RawMaterialPrice $price, string $s3Key): void - { - $this->registerMedia( - model: $price, - s3Key: $s3Key, - collectionName: 'photos', - orderColumn: 1, - ); - } } diff --git a/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php b/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php index e49d507..9281fea 100644 --- a/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php +++ b/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php @@ -30,9 +30,9 @@ public function getForCutting(): array ->get() ->each(function (RawMaterial $rawMaterial) { $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { - $media = $price->getFirstMedia('photos'); + $media = $price->getFirstMedia('images'); $price->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->file_name) + ? $this->s3Service->getTemporaryUrl($media->getPath()) : null; }); }) @@ -43,11 +43,9 @@ public function getForEdit(RawMaterialPrice $variant): array { $variant->load('media'); - $media = $variant->getMedia('photos'); - $photoKey = $media->first()?->file_name; - $photoUrl = $media->first() - ? $this->s3Service->getTemporaryUrl($media->first()->file_name) - : null; + $media = $variant->getMedia('images'); + $photoKeys = $media->map(fn ($m) => $m->getCustomProperty('s3_key') ?? $m->file_name)->toArray(); + $photoUrls = $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray(); return [ 'id' => $variant->id, @@ -55,8 +53,8 @@ public function getForEdit(RawMaterialPrice $variant): array 'variant' => $variant->variant, 'price' => $variant->price, 'stock' => $variant->stock, - 'photo_key' => $photoKey, - 'photo_url' => $photoUrl, + 'photo_key' => $photoKeys[0] ?? null, + 'photo_url' => $photoUrls[0] ?? null, ]; } @@ -70,11 +68,11 @@ public function update(RawMaterialPrice $variant, array $data): RawMaterialPrice ]); if (array_key_exists('photo_key', $data)) { - $existingKey = $variant->getMedia('photos')->first()?->file_name; + $existingKey = $variant->getMedia('images')->first()?->getCustomProperty('s3_key'); $newKey = $data['photo_key']; if ($existingKey !== $newKey) { - $variant->clearMediaCollection('photos'); + $variant->clearMediaCollection('images'); if ($newKey) { $this->registerPhoto($variant, $newKey); } @@ -95,7 +93,7 @@ public function update(RawMaterialPrice $variant, array $data): RawMaterialPrice public function destroy(RawMaterial $rawMaterial, RawMaterialPrice $variant): bool { $result = DB::transaction(function () use ($variant) { - $variant->clearMediaCollection('photos'); + $variant->clearMediaCollection('images'); return $variant->delete(); }); @@ -110,13 +108,4 @@ public function destroy(RawMaterial $rawMaterial, RawMaterialPrice $variant): bo return $result; } - private function registerPhoto(RawMaterialPrice $variant, string $s3Key): void - { - $this->registerMedia( - model: $variant, - s3Key: $s3Key, - collectionName: 'photos', - orderColumn: 1, - ); - } } diff --git a/app/Services/Concerns/RegistersMedia.php b/app/Services/Concerns/RegistersMedia.php index 67a0369..20fe42f 100644 --- a/app/Services/Concerns/RegistersMedia.php +++ b/app/Services/Concerns/RegistersMedia.php @@ -2,6 +2,7 @@ namespace App\Services\Concerns; +use App\Services\S3PresignedService; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Storage; @@ -66,21 +67,21 @@ private function registerMedia( ?int $orderColumn = null, ?string $name = null, ): void { - $defaultName = pathinfo($s3Key, PATHINFO_FILENAME); + $fileName = pathinfo($s3Key, PATHINFO_BASENAME); Media::create([ 'model_type' => $model->getMorphClass(), 'model_id' => $model->id, 'uuid' => Str::uuid(), 'collection_name' => $collectionName, - 'name' => $name ?? $defaultName, - 'file_name' => $s3Key, + 'name' => $name ?? pathinfo($s3Key, PATHINFO_FILENAME), + 'file_name' => $fileName, 'mime_type' => $mimeType ?? 'image/jpeg', 'disk' => 's3', 'conversions_disk' => 's3', 'size' => $fileSize ?? 0, 'manipulations' => [], - 'custom_properties' => [], + 'custom_properties' => ['s3_key' => $s3Key], 'generated_conversions' => $generatedConversions, 'responsive_images' => [], 'order_column' => $orderColumn ?? 1, @@ -93,7 +94,7 @@ private function syncPhoto(Model $model, array $data, string $collectionName = ' return; } - $currentKey = $model->getFirstMedia($collectionName)?->file_name; + $currentKey = $model->getFirstMedia($collectionName)?->getCustomProperty('s3_key'); if ($data['photo_key'] === $currentKey) { return; @@ -114,11 +115,7 @@ private function syncPhoto(Model $model, array $data, string $collectionName = ' private function syncReceipt(Model $model, ?string $newKey, string $collectionName = 'receipts', string $cachePrefix = 'receipt', ?int $fileSize = null, ?string $fileMimeType = null): void { $currentMedia = $model->getFirstMedia($collectionName); - $currentKey = $currentMedia?->file_name; - - if ($currentMedia && ! str_contains($currentKey, '/')) { - $currentKey = $currentMedia->getPath(); - } + $currentKey = $currentMedia?->getCustomProperty('s3_key'); if ($newKey === $currentKey) { return; @@ -141,4 +138,53 @@ private function syncReceipt(Model $model, ?string $newKey, string $collectionNa ); } } + + private function registerPhoto(Model $model, string $s3Key, string $collectionName = 'images'): void + { + $this->registerMedia( + model: $model, + s3Key: $s3Key, + collectionName: $collectionName, + orderColumn: 1, + ); + } + + public function registerPhotos(Model $model, array $photoKeys, string $collectionName = 'images'): void + { + foreach ($photoKeys as $order => $s3Key) { + $this->registerMedia( + model: $model, + s3Key: $s3Key, + collectionName: $collectionName, + orderColumn: $order + 1, + ); + } + } + + public function syncPhotos(Model $model, array $photoKeys, string $collectionName = 'images'): void + { + $existingMedia = $model->getMedia($collectionName); + $existingKeys = $existingMedia + ->map(fn (Media $m) => $m->getCustomProperty('s3_key') ?? $m->file_name) + ->toArray(); + + foreach ($existingMedia as $media) { + $key = $media->getCustomProperty('s3_key') ?? $media->file_name; + + if (! in_array($key, $photoKeys)) { + $media->delete(); + } + } + + $newKeys = array_values(array_diff($photoKeys, $existingKeys)); + + $this->registerPhotos($model, $newKeys, $collectionName); + } + + public function getTemporaryUrls(Model $model, string $collectionName = 'images'): array + { + $media = $model->getMedia($collectionName); + + return $media->map(fn ($m) => $this->s3Service->getTemporaryUrl($m->getPath()))->toArray(); + } } diff --git a/resources/js/pages/admin/manage/transaction/columns.tsx b/resources/js/pages/admin/manage/transaction/columns.tsx index fc4ad15..e237851 100644 --- a/resources/js/pages/admin/manage/transaction/columns.tsx +++ b/resources/js/pages/admin/manage/transaction/columns.tsx @@ -49,6 +49,7 @@ export type Transaction = { nego_price: number | null; total_amount: number; notes: string | null; + photo_url: string | null; created_at: string; created_by: { id: number; diff --git a/resources/js/pages/admin/manage/transaction/transaction-card.tsx b/resources/js/pages/admin/manage/transaction/transaction-card.tsx index 283b41d..70c0321 100644 --- a/resources/js/pages/admin/manage/transaction/transaction-card.tsx +++ b/resources/js/pages/admin/manage/transaction/transaction-card.tsx @@ -1,4 +1,5 @@ import { CheckCircle, ChevronDown, Pencil, Printer, Send, Trash2, XCircle } from 'lucide-react'; +import { ImagePreviewButton } from '@/components/dialogs'; import { RowActions } from '@/components/data-display'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -195,6 +196,16 @@ export function TransactionCardRow({ {formatCurrency(transaction.cogs)} + + {transaction.photo_url && ( +
+ +
+ )}