feat: replace raw image path input with interactive visual gallery and multi-image manager in admin and shop

This commit is contained in:
2026-11-01 16:24:12 +01:00
parent c06701673a
commit 4994d4a8ef
9 changed files with 724 additions and 29 deletions
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Category;
use App\Models\Product;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
@@ -84,7 +85,9 @@ class ProductController extends Controller
'base_price' => 'required|numeric|min:0.01',
'short_description' => 'nullable|string|max:500',
'description' => 'required|string',
'image_url' => 'nullable|string|max:500',
'image_url' => 'nullable|string|max:2048',
'gallery_images' => 'nullable|array',
'gallery_images.*' => 'nullable|string|max:2048',
'dimensions' => 'nullable|string|max:100',
'weight_grams' => 'nullable|integer|min:0',
'print_time_hours' => 'nullable|numeric|min:0',
@@ -95,6 +98,7 @@ class ProductController extends Controller
$validated['slug'] = Str::slug($validated['name']);
$validated['is_featured'] = $request->boolean('is_featured');
$validated['is_active'] = $request->boolean('is_active');
$validated['gallery_images'] = $this->cleanGalleryImages($request->input('gallery_images', []), $validated['image_url'] ?? null);
$validated['available_materials'] = ['pla', 'petg', 'silk', 'tpu'];
$validated['available_colors'] = [
'Zon Geel (Pikachu)', 'Vurig Rood (Charizard)', 'Hemelsblauw (Squirtle)',
@@ -129,7 +133,9 @@ class ProductController extends Controller
'base_price' => 'required|numeric|min:0.01',
'short_description' => 'nullable|string|max:500',
'description' => 'required|string',
'image_url' => 'nullable|string|max:500',
'image_url' => 'nullable|string|max:2048',
'gallery_images' => 'nullable|array',
'gallery_images.*' => 'nullable|string|max:2048',
'dimensions' => 'nullable|string|max:100',
'weight_grams' => 'nullable|integer|min:0',
'print_time_hours' => 'nullable|numeric|min:0',
@@ -139,6 +145,7 @@ class ProductController extends Controller
$validated['is_featured'] = $request->boolean('is_featured');
$validated['is_active'] = $request->boolean('is_active');
$validated['gallery_images'] = $this->cleanGalleryImages($request->input('gallery_images', []), $validated['image_url'] ?? null);
$product->update($validated);
@@ -146,6 +153,43 @@ class ProductController extends Controller
->with('success', "Product '{$product->name}' is succesvol bijgewerkt!");
}
/**
* Handle image upload for product gallery / cover.
*/
public function uploadImage(Request $request): JsonResponse
{
$request->validate([
'image' => 'required|image|mimes:jpeg,png,jpg,webp,gif|max:8192',
]);
$path = $request->file('image')->store('products', 'public');
$url = '/storage/'.$path;
return response()->json([
'success' => true,
'url' => $url,
]);
}
/**
* Sanitize and filter gallery images.
*
* @param array<int, mixed> $images
* @return array<int, string>
*/
protected function cleanGalleryImages(array $images, ?string $coverUrl = null): array
{
$clean = [];
foreach ($images as $img) {
$img = trim((string) $img);
if ($img !== '' && $img !== $coverUrl && ! in_array($img, $clean, true)) {
$clean[] = $img;
}
}
return $clean;
}
/**
* Remove the specified product from storage.
*/
+8 -3
View File
@@ -780,9 +780,14 @@ class MakerWorldService
if (! $existingProduct->makerworld_collection_id) {
$updates['makerworld_collection_id'] = $collection->id;
}
if (empty($existingProduct->gallery_images) && ! empty($design['designExtension']['design_pictures'])) {
$formatted = $this->formatDesignForImport($design);
$updates['gallery_images'] = $formatted['gallery_images'];
if (empty($existingProduct->gallery_images)) {
$full = (! empty($design['designExtension']['design_pictures']))
? $design
: ($this->getDesignDetails($designId) ?? $design);
$formatted = $this->formatDesignForImport($full);
if (! empty($formatted['gallery_images'])) {
$updates['gallery_images'] = $formatted['gallery_images'];
}
}
if (empty($existingProduct->print_info)) {
$formatted = $this->formatDesignForImport($design);