Redesign logo & storefront, light subtle admin theme, and 3D print cost calculator

This commit is contained in:
2026-11-01 21:23:53 +01:00
parent 0c95c7d120
commit e0592212e1
18 changed files with 1887 additions and 1153 deletions
@@ -89,6 +89,15 @@ class ContentController extends Controller
// Offline / Maintenance Mode // Offline / Maintenance Mode
'shop_offline_title', 'shop_offline_title',
'shop_offline_message', 'shop_offline_message',
// 3D Print Kostprijscalculator Tarieven
'calc_filament_price_kg',
'calc_electricity_kwh',
'calc_printer_wattage',
'calc_machine_hourly_rate',
'calc_handling_flat',
'calc_default_markup',
'calc_failure_rate',
]; ];
// Handle checkbox toggles that aren't present if unchecked // Handle checkbox toggles that aren't present if unchecked
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Category; use App\Models\Category;
use App\Models\Product; use App\Models\Product;
use App\Services\PriceCalculatorService;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -171,6 +172,25 @@ class ProductController extends Controller
]); ]);
} }
/**
* Calculate 3D printing cost price and suggested retail price.
*/
public function calculatePrice(Request $request, PriceCalculatorService $calculator): JsonResponse
{
$weight = (float) $request->input('weight_grams', 80);
$hours = (float) $request->input('print_time_hours', 4.0);
$colors = (int) $request->input('color_count', 1);
$material = (string) $request->input('material', 'pla');
$markup = $request->filled('markup_percent') ? (float) $request->input('markup_percent') : null;
$result = $calculator->calculate($weight, $hours, $colors, $material, $markup);
return response()->json([
'success' => true,
'data' => $result,
]);
}
/** /**
* Sanitize and filter gallery images. * Sanitize and filter gallery images.
* *
+7 -12
View File
@@ -653,19 +653,14 @@ class MakerWorldService
$dimensions = is_array($design['dimensions']) ? implode(' x ', $design['dimensions']) : (string) $design['dimensions']; $dimensions = is_array($design['dimensions']) ? implode(' x ', $design['dimensions']) : (string) $design['dimensions'];
} }
// Calculate a smart suggested retail price // Calculate a smart suggested retail price using the 3D Printing Cost Calculator
// Base starting price of 9.95, + ~0.08 per gram + 1.25 per print hour $colorCount = 1;
$suggestedPrice = 14.95; if (! empty($profile['filaments']) && is_array($profile['filaments'])) {
if ($weightGrams || $printTimeHours) { $colorCount = max(1, min(4, count($profile['filaments'])));
$cost = 5.0;
if ($weightGrams) {
$cost += ($weightGrams * 0.07);
}
if ($printTimeHours) {
$cost += ($printTimeHours * 1.50);
}
$suggestedPrice = round(max(9.95, $cost * 1.6), 2);
} }
$calculator = app(PriceCalculatorService::class);
$calcResult = $calculator->calculate($weightGrams ?: 85, $printTimeHours ?: 4.0, $colorCount);
$suggestedPrice = $calcResult['suggested_price'];
// Creator and MakerWorld URL // Creator and MakerWorld URL
$authorName = $design['designCreator']['name'] ?? $design['designCreator']['handle'] ?? 'MakerWorld Creator'; $authorName = $design['designCreator']['name'] ?? $design['designCreator']['handle'] ?? 'MakerWorld Creator';
+160
View File
@@ -0,0 +1,160 @@
<?php
namespace App\Services;
use App\Models\Setting;
class PriceCalculatorService
{
/**
* Purge/waste multipliers depending on the number of AMS/filament colors.
*/
public const COLOR_WASTE_MULTIPLIERS = [
1 => 1.00, // 0% extra waste for single-color
2 => 1.15, // +15% purge tower & poop waste
3 => 1.25, // +25% purge tower & poop waste
4 => 1.35, // +35% purge tower & poop waste
];
/**
* Material base multipliers relative to standard PLA.
*/
public const MATERIAL_MULTIPLIERS = [
'pla' => 1.00,
'petg' => 1.20,
'silk' => 1.30,
'tpu' => 1.40,
];
/**
* Calculate comprehensive 3D printing costs and suggested retail price.
*
* @param float|int|null $weightGrams Model weight in grams
* @param float|int|null $printTimeHours Printing duration in hours
* @param int $colorCount Number of filament colors (1 to 4)
* @param string $material Material code ('pla', 'petg', 'silk', 'tpu')
* @param float|null $customMarkupPercent Optional override for markup percentage (e.g. 60%)
* @return array<string, mixed> Detailed calculation breakdown
*/
public function calculate(
float|int|null $weightGrams = 80,
float|int|null $printTimeHours = 4.0,
int $colorCount = 1,
string $material = 'pla',
?float $customMarkupPercent = null
): array {
$weight = max(1.0, (float) ($weightGrams ?: 80));
$hours = max(0.2, (float) ($printTimeHours ?: 4.0));
$colors = max(1, min(4, $colorCount));
$matCode = strtolower(trim($material)) ?: 'pla';
// 1. Settings / Rates configuration
$filamentPriceKg = (float) Setting::get('calc_filament_price_kg', 20.00); // € 20 / kg
$electricityPriceKwh = (float) Setting::get('calc_electricity_kwh', 0.35); // € 0.35 / kWh
$printerWattage = (int) Setting::get('calc_printer_wattage', 150); // 150 W average Bambu Lab
$machineHourlyRate = (float) Setting::get('calc_machine_hourly_rate', 0.50); // € 0.50 / uur afschrijving & nozzle/bed slijtage
$handlingFlat = (float) Setting::get('calc_handling_flat', 1.50); // € 1.50 voorbereiding, supports & verpakking
$failureRate = (float) Setting::get('calc_failure_rate', 5.0); // 5% faalkans opslag
$markupPercent = $customMarkupPercent !== null ? $customMarkupPercent : (float) Setting::get('calc_default_markup', 65.0); // 65% winstmarge
$vatPercent = 21.0; // 21% btw
// 2. Multipliers
$colorWasteMult = self::COLOR_WASTE_MULTIPLIERS[$colors] ?? 1.00;
$matMult = self::MATERIAL_MULTIPLIERS[$matCode] ?? 1.00;
// 3. Material cost (includes multi-color purge tower waste)
$effectiveWeight = round($weight * $colorWasteMult, 1);
$materialCost = ($effectiveWeight / 1000.0) * ($filamentPriceKg * $matMult);
// 4. Electricity cost
$kw = $printerWattage / 1000.0;
$kwhUsed = $hours * $kw;
$electricityCost = $kwhUsed * $electricityPriceKwh;
// 5. Machine wear & depreciation
$machineCost = $hours * $machineHourlyRate;
// 6. Multi-color handling fee (extra setup for loading AMS spools)
$colorHandlingCost = max(0, $colors - 1) * 0.50;
// 7. Handling / labor
$handlingCost = $handlingFlat + $colorHandlingCost;
// 8. Direct production cost
$subtotalCost = $materialCost + $electricityCost + $machineCost + $handlingCost;
// 9. Failure rate risk buffer
$failureCost = $subtotalCost * ($failureRate / 100.0);
// 10. Net cost price (direct production cost + risk)
$totalCostPrice = $subtotalCost + $failureCost;
// 11. Margin & Retail price excl. VAT
$profitAmount = $totalCostPrice * ($markupPercent / 100.0);
$priceExclVat = $totalCostPrice + $profitAmount;
// 12. VAT (21%) & Consumer price
$vatAmount = $priceExclVat * ($vatPercent / 100.0);
$consumerPriceExact = $priceExclVat + $vatAmount;
// 13. Smart commercial rounding (.95 / .50)
$suggestedRetailPrice = $this->smartRound($consumerPriceExact);
return [
'inputs' => [
'weight_grams' => $weight,
'print_time_hours' => $hours,
'color_count' => $colors,
'material' => $matCode,
'markup_percent' => $markupPercent,
],
'rates' => [
'filament_price_kg' => $filamentPriceKg,
'electricity_price_kwh' => $electricityPriceKwh,
'machine_hourly_rate' => $machineHourlyRate,
'handling_flat' => $handlingFlat,
'failure_rate_percent' => $failureRate,
],
'breakdown' => [
'effective_weight_grams' => $effectiveWeight,
'purge_waste_grams' => round($effectiveWeight - $weight, 1),
'material_cost' => round($materialCost, 2),
'electricity_cost' => round($electricityCost, 2),
'machine_cost' => round($machineCost, 2),
'handling_cost' => round($handlingCost, 2),
'failure_cost' => round($failureCost, 2),
'total_cost_price' => round($totalCostPrice, 2),
'profit_amount' => round($profitAmount, 2),
'price_excl_vat' => round($priceExclVat, 2),
'vat_amount' => round($vatAmount, 2),
'price_incl_vat_exact' => round($consumerPriceExact, 2),
],
'suggested_price' => $suggestedRetailPrice,
'suggested_price_formatted' => '€ '.number_format($suggestedRetailPrice, 2, ',', '.'),
];
}
/**
* Smart commercial rounding to appealing .95 or .50 price points.
*/
protected function smartRound(float $rawPrice): float
{
// Enforce minimum price of 6.95
if ($rawPrice <= 7.00) {
return 6.95;
}
$floor = floor($rawPrice);
$fraction = $rawPrice - $floor;
if ($fraction <= 0.25) {
$rounded = $floor - 0.05; // e.g. 14.10 -> 13.95 or 14.00 -> 13.95, but keep at least floor
return max(6.95, $floor < $rawPrice ? $floor + 0.50 : $floor - 0.05);
} elseif ($fraction <= 0.70) {
return $floor + 0.50; // e.g. 14.40 -> 14.50
} else {
return $floor + 0.95; // e.g. 14.80 -> 14.95
}
}
}
+27 -56
View File
@@ -1,70 +1,41 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
<defs> <defs>
<!-- Top Slice Gradients --> <!-- Soft Warm Coral Gradient -->
<linearGradient id="icoTopFace" x1="0%" y1="0%" x2="100%" y2="100%"> <linearGradient id="iconCoral" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#38BDF8" />
<stop offset="100%" stop-color="#0284C7" />
</linearGradient>
<linearGradient id="icoTopRight" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0284C7" />
<stop offset="100%" stop-color="#0369A1" />
</linearGradient>
<linearGradient id="icoTopLeft" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0EA5E9" />
<stop offset="100%" stop-color="#0284C7" />
</linearGradient>
<!-- Mid Slice Gradients -->
<linearGradient id="icoMidFace" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#F43F5E" />
<stop offset="100%" stop-color="#E11D48" />
</linearGradient>
<linearGradient id="icoMidRight" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#E11D48" />
<stop offset="100%" stop-color="#BE123C" />
</linearGradient>
<linearGradient id="icoMidLeft" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#FB7185" /> <stop offset="0%" stop-color="#FB7185" />
<stop offset="100%" stop-color="#E11D48" /> <stop offset="100%" stop-color="#F43F5E" />
</linearGradient> </linearGradient>
<!-- Bot Slice Gradients --> <!-- Warm Amber Gradient -->
<linearGradient id="icoBotFace" x1="0%" y1="0%" x2="100%" y2="100%"> <linearGradient id="iconAmber" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#FBBF24" /> <stop offset="0%" stop-color="#FBBF24" />
<stop offset="100%" stop-color="#F59E0B" /> <stop offset="100%" stop-color="#F59E0B" />
</linearGradient> </linearGradient>
<linearGradient id="icoBotRight" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#F59E0B" /> <!-- Gentle Sky Gradient -->
<stop offset="100%" stop-color="#D97706" /> <linearGradient id="iconSky" x1="0%" y1="0%" x2="100%" y2="100%">
</linearGradient> <stop offset="0%" stop-color="#38BDF8" />
<linearGradient id="icoBotLeft" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="100%" stop-color="#0284C7" />
<stop offset="0%" stop-color="#FCD34D" />
<stop offset="100%" stop-color="#F59E0B" />
</linearGradient> </linearGradient>
</defs> </defs>
<g transform="translate(4, 2)"> <!-- Friendly Rounded Layered 3D Emblem -->
<!-- Bottom Layer (Amber) --> <g transform="translate(4, 4)">
<g transform="translate(0, 22)"> <!-- Base Layer (Amber) -->
<polygon points="22,6 40,16 22,26 4,16" fill="url(#icoBotFace)" opacity="0.9" /> <rect x="2" y="26" width="36" height="12" rx="6" fill="url(#iconAmber)" />
<polygon points="4,16 22,26 22,34 4,24" fill="url(#icoBotLeft)" />
<polygon points="22,26 40,16 40,24 22,34" fill="url(#icoBotRight)" />
</g>
<!-- Middle Layer (Rose Pink) --> <!-- Middle Layer (Coral) -->
<g transform="translate(0, 11)"> <rect x="2" y="14" width="36" height="12" rx="6" fill="url(#iconCoral)" />
<polygon points="22,6 40,16 22,26 4,16" fill="url(#icoMidFace)" opacity="0.95" />
<polygon points="4,16 22,26 22,34 4,24" fill="url(#icoMidLeft)" />
<polygon points="22,26 40,16 40,24 22,34" fill="url(#icoMidRight)" />
</g>
<!-- Top Layer (Cyan / Sky) --> <!-- Top Layer (Sky) -->
<g transform="translate(0, 0)"> <rect x="2" y="2" width="36" height="12" rx="6" fill="url(#iconSky)" />
<polygon points="22,6 40,16 22,26 4,16" fill="url(#icoTopFace)" />
<polygon points="4,16 22,26 22,34 4,24" fill="url(#icoTopLeft)" /> <!-- Cute extrusion highlights -->
<polygon points="22,26 40,16 40,24 22,34" fill="url(#icoTopRight)" /> <circle cx="10" cy="8" r="2.5" fill="#FFFFFF" opacity="0.85" />
<circle cx="22" cy="16" r="3" fill="#FFFFFF" opacity="0.95" /> <circle cx="10" cy="20" r="2.5" fill="#FFFFFF" opacity="0.85" />
</g> <circle cx="10" cy="32" r="2.5" fill="#FFFFFF" opacity="0.85" />
<!-- Cute Sparkle -->
<path d="M33 4L34 7L37 8L34 9L33 12L32 9L29 8L32 7Z" fill="#FFFFFF" opacity="0.95" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

+46 -83
View File
@@ -1,105 +1,68 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 260 56" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 52" fill="none">
<defs> <defs>
<!-- Top Slice Gradients --> <!-- Soft Warm Coral Gradient -->
<linearGradient id="topFace" x1="0%" y1="0%" x2="100%" y2="100%"> <linearGradient id="softCoral" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#38BDF8" />
<stop offset="100%" stop-color="#0284C7" />
</linearGradient>
<linearGradient id="topRight" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0284C7" />
<stop offset="100%" stop-color="#0369A1" />
</linearGradient>
<linearGradient id="topLeft" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0EA5E9" />
<stop offset="100%" stop-color="#0284C7" />
</linearGradient>
<!-- Mid Slice Gradients -->
<linearGradient id="midFace" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#F43F5E" />
<stop offset="100%" stop-color="#E11D48" />
</linearGradient>
<linearGradient id="midRight" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#E11D48" />
<stop offset="100%" stop-color="#BE123C" />
</linearGradient>
<linearGradient id="midLeft" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#FB7185" /> <stop offset="0%" stop-color="#FB7185" />
<stop offset="100%" stop-color="#E11D48" /> <stop offset="100%" stop-color="#F43F5E" />
</linearGradient> </linearGradient>
<!-- Bot Slice Gradients --> <!-- Warm Amber / Mango Gradient -->
<linearGradient id="botFace" x1="0%" y1="0%" x2="100%" y2="100%"> <linearGradient id="softAmber" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#FBBF24" /> <stop offset="0%" stop-color="#FBBF24" />
<stop offset="100%" stop-color="#F59E0B" /> <stop offset="100%" stop-color="#F59E0B" />
</linearGradient> </linearGradient>
<linearGradient id="botRight" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#F59E0B" /> <!-- Gentle Sky / Mint Gradient -->
<stop offset="100%" stop-color="#D97706" /> <linearGradient id="softSky" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#38BDF8" />
<stop offset="100%" stop-color="#0284C7" />
</linearGradient> </linearGradient>
<linearGradient id="botLeft" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#FCD34D" /> <!-- Playful Text Gradient -->
<linearGradient id="textGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#F43F5E" />
<stop offset="60%" stop-color="#EA580C" />
<stop offset="100%" stop-color="#F59E0B" /> <stop offset="100%" stop-color="#F59E0B" />
</linearGradient> </linearGradient>
<!-- Wordmark Gradient --> <!-- Soft Glow Filter -->
<linearGradient id="wordmarkGrad" x1="0%" y1="0%" x2="100%" y2="0%"> <filter id="softGlow" x="-20%" y="-20%" width="140%" height="140%">
<stop offset="0%" stop-color="#F43F5E" /> <feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#F43F5E" flood-opacity="0.18" />
<stop offset="50%" stop-color="#8B5CF6" /> </filter>
<stop offset="100%" stop-color="#06B6D4" />
</linearGradient>
<linearGradient id="badgeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#6366F1" />
<stop offset="100%" stop-color="#4F46E5" />
</linearGradient>
</defs> </defs>
<!-- 3D Layered Isometric Cube Mark --> <!-- Friendly Rounded 3D Printing Emblem (Cubic Spool / Layered Soft Prism) -->
<g transform="translate(6, 4)"> <g transform="translate(6, 6)" filter="url(#softGlow)">
<!-- Bottom Layer (Amber) --> <!-- Base Layer (Soft Amber Pill) -->
<g transform="translate(0, 22)"> <rect x="2" y="24" width="36" height="12" rx="6" fill="url(#softAmber)" />
<!-- Top surface of bottom layer -->
<polygon points="22,6 40,16 22,26 4,16" fill="url(#botFace)" opacity="0.9" />
<!-- Left side of bottom layer -->
<polygon points="4,16 22,26 22,34 4,24" fill="url(#botLeft)" />
<!-- Right side of bottom layer -->
<polygon points="22,26 40,16 40,24 22,34" fill="url(#botRight)" />
</g>
<!-- Middle Layer (Rose Pink) --> <!-- Middle Layer (Soft Coral Pill) -->
<g transform="translate(0, 11)"> <rect x="2" y="13" width="36" height="12" rx="6" fill="url(#softCoral)" />
<!-- Top surface of middle layer -->
<polygon points="22,6 40,16 22,26 4,16" fill="url(#midFace)" opacity="0.95" />
<!-- Left side of middle layer -->
<polygon points="4,16 22,26 22,34 4,24" fill="url(#midLeft)" />
<!-- Right side of middle layer -->
<polygon points="22,26 40,16 40,24 22,34" fill="url(#midRight)" />
</g>
<!-- Top Layer (Cyan / Sky) --> <!-- Top Layer (Soft Sky Pill) -->
<g transform="translate(0, 0)"> <rect x="2" y="2" width="36" height="12" rx="6" fill="url(#softSky)" />
<!-- Top surface of top layer -->
<polygon points="22,6 40,16 22,26 4,16" fill="url(#topFace)" /> <!-- Cute 3D Extrusion Dot / Sparkle Highlight -->
<!-- Left side of top layer --> <circle cx="10" cy="8" r="2.5" fill="#FFFFFF" opacity="0.85" />
<polygon points="4,16 22,26 22,34 4,24" fill="url(#topLeft)" /> <circle cx="10" cy="19" r="2.5" fill="#FFFFFF" opacity="0.85" />
<!-- Right side of top layer --> <circle cx="10" cy="30" r="2.5" fill="#FFFFFF" opacity="0.85" />
<polygon points="22,26 40,16 40,24 22,34" fill="url(#topRight)" />
<!-- Accent Sparkle/Nozzle Dot on Top Face --> <!-- Delicate cute star / nozzle sparkle -->
<circle cx="22" cy="16" r="3" fill="#FFFFFF" opacity="0.9" /> <path d="M33 4L34 7L37 8L34 9L33 12L32 9L29 8L32 7Z" fill="#FFFFFF" opacity="0.9" />
</g>
</g> </g>
<!-- Wordmark Text --> <!-- Wordmark Text -->
<g transform="translate(62, 38)"> <g transform="translate(56, 36)">
<!-- "Jouw" in Bold Slate --> <!-- "jouw" in Friendly Rounded Slate -->
<text x="0" y="0" font-family="'Fredoka', system-ui, sans-serif" font-weight="700" font-size="28" fill="#0F172A" letter-spacing="-0.5">Jouw</text> <text x="0" y="0" font-family="'Fredoka', 'Nunito', system-ui, sans-serif" font-weight="600" font-size="27" fill="#1E293B" letter-spacing="-0.3">jouw</text>
<!-- "driedee" in Vibrant Gradient --> <!-- "3D" / "driedee" in Vibrant Soft Gradient -->
<text x="74" y="0" font-family="'Fredoka', system-ui, sans-serif" font-weight="700" font-size="28" fill="url(#wordmarkGrad)" letter-spacing="-0.5">driedee</text> <text x="64" y="0" font-family="'Fredoka', 'Nunito', system-ui, sans-serif" font-weight="700" font-size="27" fill="url(#textGradient)" letter-spacing="-0.3">driedee</text>
<!-- ".nl" Badge --> <!-- Subtle Friendly ".nl" Badge -->
<rect x="168" y="-21" width="28" height="16" rx="5" fill="url(#badgeGrad)" /> <g transform="translate(148, -20)">
<text x="173" y="-9" font-family="'Nunito', system-ui, sans-serif" font-weight="800" font-size="10" fill="#FFFFFF" letter-spacing="0.5">.NL</text> <rect x="0" y="0" width="26" height="17" rx="8.5" fill="#F1F5F9" />
<text x="13" y="12" font-family="'Fredoka', 'Nunito', system-ui, sans-serif" font-weight="700" font-size="10.5" fill="#64748B" text-anchor="middle">.nl</text>
</g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because it is too large Load Diff
@@ -3,105 +3,87 @@
@section('title', 'Artikel Beheer (Geïmporteerde Modellen) | Jouwdriedee.nl Beheer') @section('title', 'Artikel Beheer (Geïmporteerde Modellen) | Jouwdriedee.nl Beheer')
@section('content') @section('content')
<div class="py-8 bg-slate-950 text-slate-100 min-h-screen" x-data="artikelBeheerApp()"> <div class="space-y-6" x-data="artikelBeheerApp()">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-6">
<!-- Top Header & Breadcrumb --> <!-- Top Header & Breadcrumb -->
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 pb-6 border-b border-slate-800"> <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 pb-6 border-b border-slate-200/80">
<div> <div>
<div class="flex items-center space-x-2 text-xs text-amber-400 font-bold uppercase tracking-wider mb-1"> <div class="flex items-center space-x-2 text-xs text-amber-700 font-bold uppercase tracking-wider mb-1">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-amber-400 animate-pulse"></span> <span class="inline-block w-2 h-2 rounded-full bg-amber-500 animate-pulse"></span>
<span>Staging & Curation</span> <span>Staging & Curation</span>
<span>&bull;</span> <span>&bull;</span>
<span>Geïmporteerde Modellen</span> <span>Geïmporteerde Modellen</span>
</div> </div>
<h1 class="text-2xl sm:text-3xl font-black text-white flex items-center space-x-3"> <h1 class="text-2xl sm:text-3xl font-black text-slate-900 flex items-center space-x-3">
<span class="p-2 bg-gradient-to-tr from-amber-400 to-orange-500 rounded-2xl text-slate-950 shadow-lg shadow-amber-400/20"> <span class="p-2 bg-gradient-to-tr from-amber-400 to-amber-500 rounded-2xl text-slate-950 shadow-sm">
<i data-lucide="layers" class="w-6 h-6"></i> <i data-lucide="layers" class="w-6 h-6"></i>
</span> </span>
<span>Artikel Beheer</span> <span>Artikel Beheer</span>
</h1> </h1>
<p class="mt-1 text-sm text-slate-400"> <p class="mt-1 text-xs sm:text-sm text-slate-500">
Beoordeel automatisch binnengehaalde MakerWorld modellen. Stel de gewenste categorie en verkoopprijs in en publiceer ze met 1 klik naar de webshop! Beoordeel binnengehaalde MakerWorld modellen. Prijzen zijn automatisch berekend op basis van de 3D-printcalculator (gewicht, printtijd & kleuren). Pas naar wens aan en publiceer met 1 klik!
</p> </p>
</div> </div>
<!-- Header Quick Actions --> <!-- Header Quick Actions -->
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-2.5 self-start md:self-auto">
<a href="{{ route('admin.makerworld.index', ['tab' => 'collections']) }}" class="px-4 py-2.5 rounded-xl bg-slate-900 hover:bg-slate-800 text-slate-200 text-xs font-bold transition flex items-center space-x-2 border border-slate-800"> <a href="{{ route('admin.makerworld.index', ['tab' => 'collections']) }}" class="px-3.5 py-2 rounded-xl bg-white hover:bg-slate-50 text-slate-700 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200 shadow-sm">
<i data-lucide="refresh-cw" class="w-4 h-4 text-amber-400"></i> <i data-lucide="refresh-cw" class="w-4 h-4 text-amber-600"></i>
<span>Collecties & Auto-Sync</span> <span>Collecties & Sync</span>
</a> </a>
<a href="{{ route('admin.products.index') }}" class="px-4 py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-white text-xs font-bold transition flex items-center space-x-2 border border-slate-700"> <a href="{{ route('admin.products.index') }}" class="px-3.5 py-2 rounded-xl bg-slate-900 hover:bg-slate-800 text-white text-xs font-bold transition flex items-center space-x-1.5 shadow-sm">
<i data-lucide="box" class="w-4 h-4"></i> <i data-lucide="box" class="w-4 h-4"></i>
<span>Actieve Producten</span> <span>Actieve Producten</span>
</a> </a>
</div> </div>
</div> </div>
<!-- Notification Alerts -->
@if(session('success'))
<div class="p-4 rounded-2xl bg-emerald-950/60 border border-emerald-500/40 text-emerald-200 text-sm flex items-center space-x-3">
<i data-lucide="check-circle-2" class="w-5 h-5 text-emerald-400 shrink-0"></i>
<div class="font-bold">{{ session('success') }}</div>
</div>
@endif
@if(session('error'))
<div class="p-4 rounded-2xl bg-rose-950/60 border border-rose-500/40 text-rose-200 text-sm flex items-center space-x-3">
<i data-lucide="x-circle" class="w-5 h-5 text-rose-400 shrink-0"></i>
<div class="font-bold">{{ session('error') }}</div>
</div>
@endif
<!-- Status Filter Tabs --> <!-- Status Filter Tabs -->
<div class="flex items-center space-x-2 border-b border-slate-800 pb-2 overflow-x-auto text-xs font-bold"> <div class="flex items-center space-x-2 border-b border-slate-200/80 pb-2 overflow-x-auto text-xs font-bold">
<a href="{{ route('admin.makerworld.staging', array_merge(request()->except('tab', 'page'), ['tab' => 'draft'])) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'draft') === 'draft' ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}"> <a href="{{ route('admin.makerworld.staging', array_merge(request()->except('tab', 'page'), ['tab' => 'draft'])) }}" class="px-3.5 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'draft') === 'draft' ? 'bg-amber-400 text-slate-950 font-black shadow-sm' : 'text-slate-600 hover:text-slate-900 hover:bg-white' }}">
<i data-lucide="layers" class="w-3.5 h-3.5"></i> <i data-lucide="layers" class="w-3.5 h-3.5"></i>
<span>Wacht op Beoordeling (Concepten)</span> <span>Wacht op Beoordeling</span>
<span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'draft') === 'draft' ? 'bg-slate-950 text-amber-400 font-black' : 'bg-slate-800 text-slate-400' }}"> <span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'draft') === 'draft' ? 'bg-slate-950 text-amber-400 font-black' : 'bg-slate-100 text-slate-600' }}">
{{ $countDraft ?? 0 }} {{ $countDraft ?? 0 }}
</span> </span>
</a> </a>
<a href="{{ route('admin.makerworld.staging', array_merge(request()->except('tab', 'page'), ['tab' => 'published'])) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'draft') === 'published' ? 'bg-emerald-500 text-white font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}"> <a href="{{ route('admin.makerworld.staging', array_merge(request()->except('tab', 'page'), ['tab' => 'published'])) }}" class="px-3.5 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'draft') === 'published' ? 'bg-emerald-600 text-white font-black shadow-sm' : 'text-slate-600 hover:text-slate-900 hover:bg-white' }}">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400"></span> <span class="w-1.5 h-1.5 rounded-full {{ ($tab ?? 'draft') === 'published' ? 'bg-emerald-200' : 'bg-emerald-500' }}"></span>
<span>Reeds Gepubliceerd in Webshop</span> <span>Reeds Gepubliceerd in Webshop</span>
<span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'draft') === 'published' ? 'bg-slate-950 text-emerald-400 font-black' : 'bg-slate-800 text-slate-400' }}"> <span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'draft') === 'published' ? 'bg-emerald-800 text-emerald-200 font-black' : 'bg-slate-100 text-slate-600' }}">
{{ $countPublished ?? 0 }} {{ $countPublished ?? 0 }}
</span> </span>
</a> </a>
<a href="{{ route('admin.makerworld.staging', array_merge(request()->except('tab', 'page'), ['tab' => 'all'])) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'draft') === 'all' ? 'bg-indigo-600 text-white font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}"> <a href="{{ route('admin.makerworld.staging', array_merge(request()->except('tab', 'page'), ['tab' => 'all'])) }}" class="px-3.5 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'draft') === 'all' ? 'bg-indigo-600 text-white font-black shadow-sm' : 'text-slate-600 hover:text-slate-900 hover:bg-white' }}">
<i data-lucide="download-cloud" class="w-3.5 h-3.5"></i> <i data-lucide="download-cloud" class="w-3.5 h-3.5"></i>
<span>Alle Binnengehaalde Modellen</span> <span>Alle Binnengehaalde Modellen</span>
<span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'draft') === 'all' ? 'bg-slate-950 text-indigo-300 font-black' : 'bg-slate-800 text-slate-400' }}"> <span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'draft') === 'all' ? 'bg-indigo-800 text-indigo-200 font-black' : 'bg-slate-100 text-slate-600' }}">
{{ $countAll ?? 0 }} {{ $countAll ?? 0 }}
</span> </span>
</a> </a>
</div> </div>
<!-- Filter & Search Toolbar --> <!-- Filter & Search Toolbar -->
<div class="bg-slate-900 rounded-3xl p-4 sm:p-5 border border-slate-800 flex flex-col md:flex-row md:items-center justify-between gap-4"> <div class="bg-white rounded-2xl p-3.5 border border-slate-200/80 shadow-sm flex flex-col md:flex-row md:items-center justify-between gap-3">
<form method="GET" action="{{ route('admin.makerworld.staging') }}" class="flex flex-wrap items-center gap-3 flex-1"> <form method="GET" action="{{ route('admin.makerworld.staging') }}" class="flex flex-wrap items-center gap-2.5 flex-1">
<!-- Search input --> <!-- Search input -->
<div class="relative min-w-[240px] flex-1"> <div class="relative min-w-[220px] flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-slate-500">
<i data-lucide="search" class="w-4 h-4"></i>
</div>
<input <input
type="text" type="text"
name="q" name="q"
value="{{ request('q') }}" value="{{ request('q') }}"
placeholder="Zoek op titel, maker of model-ID..." placeholder="Zoek op titel, maker of model-ID..."
class="w-full pl-9 pr-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white text-xs placeholder-slate-500 focus:ring-amber-400" class="w-full pl-9 pr-3 py-2 bg-slate-50 border border-slate-200 rounded-xl text-slate-800 text-xs placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-amber-400 focus:bg-white transition"
> >
<i data-lucide="search" class="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2"></i>
</div> </div>
<!-- Collection dropdown filter --> <!-- Collection dropdown filter -->
<div class="w-48"> <div class="w-52">
<select name="collection_id" onchange="this.form.submit()" class="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white text-xs font-bold focus:ring-amber-400"> <select name="collection_id" onchange="this.form.submit()" class="w-full px-3 py-2 bg-slate-50 border border-slate-200 rounded-xl text-slate-700 text-xs font-semibold focus:outline-none focus:ring-2 focus:ring-amber-400">
<option value="">Alle collecties</option> <option value="">Alle collecties</option>
@foreach($collections as $col) @foreach($collections as $col)
<option value="{{ $col->id }}" {{ request('collection_id') == $col->id ? 'selected' : '' }}> <option value="{{ $col->id }}" {{ request('collection_id') == $col->id ? 'selected' : '' }}>
@@ -111,26 +93,26 @@
</select> </select>
</div> </div>
<button type="submit" class="px-3.5 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-bold transition"> <button type="submit" class="px-3.5 py-2 rounded-xl bg-slate-900 hover:bg-slate-800 text-white text-xs font-bold transition">
Filteren Filteren
</button> </button>
@if(request()->hasAny(['q', 'collection_id'])) @if(request()->hasAny(['q', 'collection_id']))
<a href="{{ route('admin.makerworld.staging') }}" class="text-xs text-rose-400 hover:underline font-bold"> <a href="{{ route('admin.makerworld.staging') }}" class="text-xs text-rose-600 hover:underline font-bold">
Filters wissen Wissen
</a> </a>
@endif @endif
</form> </form>
<div class="flex items-center space-x-3 text-xs"> <div class="flex items-center space-x-2 text-xs">
<span class="px-3 py-1.5 rounded-xl bg-amber-400/10 text-amber-300 border border-amber-400/30 font-black"> <span class="px-3 py-1 rounded-full bg-amber-50 text-amber-800 border border-amber-200/80 font-bold">
{{ $totalStaged }} concepten wachten op beoordeling {{ $totalStaged }} concepten wachten op beoordeling
</span> </span>
</div> </div>
</div> </div>
<!-- Bulk Selection Action Bar --> <!-- Bulk Selection Action Bar -->
<div x-show="selectedIds.length > 0" x-cloak class="p-4 rounded-2xl bg-amber-400 text-slate-950 flex flex-col md:flex-row items-center justify-between gap-4 shadow-xl"> <div x-show="selectedIds.length > 0" x-cloak class="p-4 rounded-2xl bg-amber-400 text-slate-950 flex flex-col md:flex-row items-center justify-between gap-4 shadow-md">
<div class="flex items-center space-x-2 font-black text-xs uppercase tracking-wider"> <div class="flex items-center space-x-2 font-black text-xs uppercase tracking-wider">
<i data-lucide="check-square" class="w-5 h-5"></i> <i data-lucide="check-square" class="w-5 h-5"></i>
<span x-text="selectedIds.length + ' artikelen geselecteerd'"></span> <span x-text="selectedIds.length + ' artikelen geselecteerd'"></span>
@@ -143,8 +125,8 @@
</template> </template>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<label class="text-xs font-black uppercase">Categorie:</label> <label class="text-xs font-bold uppercase">Categorie:</label>
<select name="category_id" required class="px-3 py-1.5 bg-slate-950 text-white rounded-xl text-xs font-bold"> <select name="category_id" required class="px-3 py-1.5 bg-white text-slate-900 rounded-xl text-xs font-bold border border-amber-300">
@foreach($categories as $cat) @foreach($categories as $cat)
<option value="{{ $cat->id }}">{{ $cat->name }}</option> <option value="{{ $cat->id }}">{{ $cat->name }}</option>
@endforeach @endforeach
@@ -152,11 +134,11 @@
</div> </div>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<label class="text-xs font-black uppercase">Prijs-marge %:</label> <label class="text-xs font-bold uppercase">Winstmarge %:</label>
<input type="number" name="custom_markup" placeholder="0" class="w-16 px-2 py-1.5 bg-slate-950 text-white rounded-xl text-xs font-bold"> <input type="number" name="custom_markup" placeholder="40" class="w-16 px-2 py-1.5 bg-white text-slate-900 rounded-xl text-xs font-bold border border-amber-300">
</div> </div>
<button type="submit" class="px-4 py-2 bg-slate-950 text-amber-400 hover:bg-slate-900 font-black text-xs rounded-xl shadow-md transition flex items-center space-x-1.5"> <button type="submit" class="px-4 py-2 bg-slate-950 hover:bg-slate-900 text-amber-400 font-black text-xs rounded-xl shadow transition flex items-center space-x-1.5">
<i data-lucide="check" class="w-4 h-4"></i> <i data-lucide="check" class="w-4 h-4"></i>
<span>Publiceer Selectie naar Webshop</span> <span>Publiceer Selectie naar Webshop</span>
</button> </button>
@@ -176,8 +158,8 @@
<!-- Select All Trigger --> <!-- Select All Trigger -->
@if($stagedProducts->isNotEmpty()) @if($stagedProducts->isNotEmpty())
<div class="flex items-center justify-between text-xs text-slate-400 px-2"> <div class="flex items-center justify-between text-xs text-slate-500 px-1">
<button type="button" @click="toggleSelectAll()" class="hover:text-amber-400 font-bold transition flex items-center space-x-1.5"> <button type="button" @click="toggleSelectAll()" class="hover:text-amber-700 font-bold transition flex items-center space-x-1.5">
<i data-lucide="check" class="w-4 h-4"></i> <i data-lucide="check" class="w-4 h-4"></i>
<span x-text="selectedIds.length === {{ $stagedProducts->count() }} ? 'Deselecteer alles' : 'Selecteer alle {{ $stagedProducts->count() }} modellen op deze pagina'"></span> <span x-text="selectedIds.length === {{ $stagedProducts->count() }} ? 'Deselecteer alles' : 'Selecteer alle {{ $stagedProducts->count() }} modellen op deze pagina'"></span>
</button> </button>
@@ -187,145 +169,145 @@
<!-- Staged Products Grid --> <!-- Staged Products Grid -->
@if($stagedProducts->isEmpty()) @if($stagedProducts->isEmpty())
<div class="text-center py-20 bg-slate-900/60 rounded-3xl border border-slate-800 p-8"> <div class="text-center py-16 bg-white rounded-3xl border border-slate-200/80 p-8 shadow-sm">
<div class="w-20 h-20 rounded-3xl bg-slate-800 flex items-center justify-center text-slate-400 mx-auto mb-4"> <div class="w-16 h-16 rounded-2xl bg-amber-50 flex items-center justify-center text-amber-600 mx-auto mb-4 border border-amber-200/60">
<i data-lucide="sparkles" class="w-10 h-10 text-amber-400"></i> <i data-lucide="sparkles" class="w-8 h-8"></i>
</div> </div>
<h3 class="text-xl font-black text-white">Geen concepten in de wachtrij!</h3> <h3 class="text-lg font-black text-slate-900">Geen concepten in de wachtrij!</h3>
<p class="text-xs text-slate-400 max-w-md mx-auto mt-2"> <p class="text-xs text-slate-500 max-w-md mx-auto mt-1.5">
Alle binnengehaalde modellen zijn al beoordeeld en gepubliceerd, of er zijn nog geen nieuwe modellen toegevoegd aan je gevolgde collecties. Alle binnengehaalde modellen zijn al beoordeeld en gepubliceerd, of er zijn nog geen nieuwe modellen toegevoegd aan je gevolgde collecties.
</p> </p>
<div class="mt-6 flex items-center justify-center space-x-3"> <div class="mt-5 flex items-center justify-center space-x-3">
<a href="{{ route('admin.makerworld.index', ['tab' => 'collections']) }}" class="px-5 py-2.5 rounded-xl bg-amber-400 text-slate-950 font-black text-xs uppercase tracking-wider shadow-lg shadow-amber-400/20 transition"> <a href="{{ route('admin.makerworld.index', ['tab' => 'collections']) }}" class="px-4 py-2.5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-bold text-xs transition shadow-sm">
Bekijk Collecties & Auto-Sync Bekijk Collecties & Auto-Sync
</a> </a>
</div> </div>
</div> </div>
@else @else
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
@foreach($stagedProducts as $product) @foreach($stagedProducts as $product)
@php @php
$printInfo = $product->print_info ?? []; $printInfo = $product->print_info ?? [];
@endphp @endphp
<div class="bg-slate-900 rounded-3xl border border-slate-800 overflow-hidden flex flex-col justify-between shadow-lg hover:border-slate-700 transition relative"> <div class="bg-white rounded-2xl border border-slate-200/80 overflow-hidden flex flex-col justify-between shadow-xs hover:border-slate-300 hover:shadow-md transition relative">
<!-- Top Image Preview & Gallery --> <!-- Top Image Preview & Gallery -->
<div> <div>
<div class="relative aspect-4/3 bg-slate-950 overflow-hidden group"> <div class="relative aspect-4/3 bg-slate-100 overflow-hidden group">
<img src="{{ $product->image_url }}" alt="{{ $product->name }}" class="w-full h-full object-cover"> <img src="{{ $product->image_url }}" alt="{{ $product->name }}" class="w-full h-full object-cover">
<!-- Selection Checkbox --> <!-- Selection Checkbox -->
<div class="absolute top-3 left-3 z-10"> <div class="absolute top-2.5 left-2.5 z-10">
<label class="flex items-center justify-center w-7 h-7 rounded-xl bg-slate-900/90 backdrop-blur border border-white/20 cursor-pointer shadow-md hover:scale-105 transition"> <label class="flex items-center justify-center w-7 h-7 rounded-xl bg-white/90 backdrop-blur border border-slate-300 cursor-pointer shadow-sm hover:scale-105 transition">
<input type="checkbox" value="{{ $product->id }}" x-model="selectedIds" class="w-4 h-4 text-amber-500 rounded focus:ring-0"> <input type="checkbox" value="{{ $product->id }}" x-model="selectedIds" class="w-4 h-4 text-amber-500 rounded focus:ring-0">
</label> </label>
</div> </div>
<!-- Draft Badge --> <!-- Draft Badge -->
<div class="absolute top-3 right-3 flex flex-col items-end gap-1.5"> <div class="absolute top-2.5 right-2.5 flex flex-col items-end gap-1">
<span class="px-2.5 py-1 rounded-full text-[10px] font-black uppercase tracking-wider bg-amber-400 text-slate-950 shadow-md"> <span class="px-2.5 py-0.5 rounded-full text-[10px] font-black uppercase tracking-wider bg-amber-400 text-slate-950 shadow-xs">
Concept Concept
</span> </span>
@if($product->makerworldCollection) @if($product->makerworldCollection)
<span class="px-2 py-0.5 rounded-md text-[9px] font-bold bg-slate-950/80 text-amber-300 border border-amber-400/20 backdrop-blur truncate max-w-[150px]"> <span class="px-2 py-0.5 rounded-md text-[9px] font-bold bg-white/95 text-slate-700 border border-slate-200 shadow-xs truncate max-w-[140px]">
{{ $product->makerworldCollection->name }} {{ $product->makerworldCollection->name }}
</span> </span>
@endif @endif
</div> </div>
<!-- MakerWorld Link --> <!-- MakerWorld Link -->
<a href="https://makerworld.com/nl/models/{{ $product->makerworld_id }}" target="_blank" class="absolute bottom-3 right-3 p-2 rounded-xl bg-slate-950/80 hover:bg-slate-900 text-slate-300 hover:text-white border border-white/10 backdrop-blur transition" title="Bekijk origineel op MakerWorld"> <a href="https://makerworld.com/nl/models/{{ $product->makerworld_id }}" target="_blank" class="absolute bottom-2.5 right-2.5 p-1.5 rounded-xl bg-white/90 hover:bg-white text-slate-700 border border-slate-200 shadow-xs backdrop-blur transition" title="Bekijk origineel op MakerWorld">
<i data-lucide="external-link" class="w-3.5 h-3.5"></i> <i data-lucide="external-link" class="w-3.5 h-3.5"></i>
</a> </a>
</div> </div>
<!-- Photo Thumbnails Strip (All Pulled Pictures) --> <!-- Photo Thumbnails Strip (All Pulled Pictures) -->
@if(!empty($product->gallery_images) && count($product->gallery_images) > 0) @if(!empty($product->gallery_images) && count($product->gallery_images) > 0)
<div class="px-4 py-2 bg-slate-950 border-b border-slate-800/80 flex items-center space-x-2 overflow-x-auto"> <div class="px-3.5 py-2 bg-slate-50 border-b border-slate-100 flex items-center space-x-1.5 overflow-x-auto">
<span class="text-[10px] font-bold text-slate-500 uppercase tracking-wider shrink-0">Foto's:</span> <span class="text-[10px] font-bold text-slate-400 uppercase tracking-wider shrink-0">Foto's:</span>
<img src="{{ $product->image_url }}" alt="" class="w-9 h-9 rounded-lg object-cover border-2 border-amber-400 shrink-0"> <img src="{{ $product->image_url }}" alt="" class="w-8 h-8 rounded-lg object-cover border-2 border-amber-400 shrink-0">
@foreach(array_slice($product->gallery_images, 0, 6) as $galleryPic) @foreach(array_slice($product->gallery_images, 0, 5) as $galleryPic)
<img src="{{ $galleryPic }}" alt="" class="w-9 h-9 rounded-lg object-cover border border-slate-800 shrink-0 hover:border-slate-600 transition" loading="lazy"> <img src="{{ $galleryPic }}" alt="" class="w-8 h-8 rounded-lg object-cover border border-slate-200 shrink-0 hover:border-slate-400 transition" loading="lazy">
@endforeach @endforeach
@if(count($product->gallery_images) > 6) @if(count($product->gallery_images) > 5)
<span class="text-[10px] text-slate-400 font-bold shrink-0">+{{ count($product->gallery_images) - 6 }} meer</span> <span class="text-[10px] text-slate-500 font-bold shrink-0">+{{ count($product->gallery_images) - 5 }} meer</span>
@endif @endif
</div> </div>
@endif @endif
<!-- Print Specifications Strip --> <!-- Print Specifications Strip -->
<div class="grid grid-cols-3 gap-1 px-4 py-2.5 bg-slate-950/40 border-b border-slate-800/60 text-center text-xs"> <div class="grid grid-cols-3 gap-1 px-3.5 py-2 bg-slate-50/50 border-b border-slate-100 text-center text-xs">
<div> <div>
<span class="text-[10px] font-bold text-slate-500 block uppercase">Printtijd</span> <span class="text-[10px] font-bold text-slate-400 block uppercase">Printtijd</span>
<span class="font-black text-slate-200"> <span class="font-bold text-slate-700">
{{ $product->print_time_hours ? $product->print_time_hours . ' uur' : '-' }} {{ $product->print_time_hours ? $product->print_time_hours . ' u' : '-' }}
</span> </span>
</div> </div>
<div> <div>
<span class="text-[10px] font-bold text-slate-500 block uppercase">Gewicht</span> <span class="text-[10px] font-bold text-slate-400 block uppercase">Gewicht</span>
<span class="font-black text-slate-200"> <span class="font-bold text-slate-700">
{{ $product->weight_grams ? $product->weight_grams . ' gram' : '-' }} {{ $product->weight_grams ? $product->weight_grams . ' g' : '-' }}
</span> </span>
</div> </div>
<div> <div>
<span class="text-[10px] font-bold text-amber-400 block uppercase">Adviesprijs</span> <span class="text-[10px] font-bold text-amber-700 block uppercase">Adviesprijs</span>
<span class="font-black text-amber-400"> <span class="font-black text-slate-900 font-heading">
{{ $product->formatted_price }} {{ $product->formatted_price }}
</span> </span>
</div> </div>
</div> </div>
<!-- Meta Details --> <!-- Meta Details -->
<div class="p-4 space-y-2"> <div class="p-3.5 space-y-1.5">
<div class="text-[11px] text-slate-400 flex items-center justify-between"> <div class="text-[11px] text-slate-500 flex items-center justify-between">
<span class="truncate">Maker: <strong class="text-slate-300">{{ $product->makerworld_author ?? 'MakerWorld Maker' }}</strong></span> <span class="truncate">Maker: <strong class="text-slate-700">{{ $product->makerworld_author ?? 'MakerWorld' }}</strong></span>
<span class="font-mono text-slate-500 text-[10px]">#{{ $product->makerworld_id }}</span> <span class="font-mono text-slate-400 text-[10px]">#{{ $product->makerworld_id }}</span>
</div> </div>
</div> </div>
</div> </div>
@if($product->status === 'published') @if($product->status === 'published')
<!-- Already Published Info Card --> <!-- Already Published Info Card -->
<div class="p-4 bg-slate-950 border-t border-slate-800 space-y-3"> <div class="p-3.5 bg-slate-50/80 border-t border-slate-100 space-y-2.5">
<div class="p-3 rounded-2xl bg-emerald-500/10 border border-emerald-500/30 text-emerald-300 text-xs flex items-center justify-between"> <div class="p-2.5 rounded-xl bg-emerald-50 border border-emerald-200 text-emerald-800 text-xs flex items-center justify-between">
<div class="flex items-center space-x-1.5 font-bold"> <div class="flex items-center space-x-1.5 font-bold">
<i data-lucide="check-circle" class="w-4 h-4 text-emerald-400"></i> <i data-lucide="check-circle" class="w-4 h-4 text-emerald-600"></i>
<span>Gepubliceerd in Webshop</span> <span>In Webshop</span>
</div> </div>
<span class="font-black text-white">{{ $product->formatted_price }}</span> <span class="font-black text-slate-900">{{ $product->formatted_price }}</span>
</div> </div>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<a href="{{ route('admin.products.edit', $product) }}" class="flex-1 py-2 px-3 bg-slate-800 hover:bg-slate-700 text-slate-200 font-bold rounded-xl text-xs text-center transition flex items-center justify-center space-x-1 border border-slate-700"> <a href="{{ route('admin.products.edit', $product) }}" class="flex-1 py-1.5 px-3 bg-white hover:bg-slate-100 text-slate-700 font-bold rounded-xl text-xs text-center transition flex items-center justify-center space-x-1 border border-slate-200 shadow-xs">
<i data-lucide="pencil" class="w-3.5 h-3.5"></i> <i data-lucide="pencil" class="w-3.5 h-3.5"></i>
<span>Bewerken</span> <span>Bewerken & Prijscalculator</span>
</a> </a>
<a href="{{ route('product.show', $product->slug) }}" target="_blank" class="p-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white transition border border-slate-700" title="Bekijk in Webshop"> <a href="{{ route('product.show', $product->slug) }}" target="_blank" class="p-1.5 rounded-xl bg-white hover:bg-slate-100 text-slate-600 border border-slate-200 transition shadow-xs" title="Bekijk in Webshop">
<i data-lucide="external-link" class="w-4 h-4"></i> <i data-lucide="external-link" class="w-4 h-4"></i>
</a> </a>
</div> </div>
</div> </div>
@else @else
<!-- Curation / Publish Form (Inline) --> <!-- Curation / Publish Form (Inline) -->
<form action="{{ route('admin.makerworld.staging.publish', $product) }}" method="POST" class="p-4 bg-slate-950 border-t border-slate-800 space-y-3"> <form action="{{ route('admin.makerworld.staging.publish', $product) }}" method="POST" class="p-3.5 bg-slate-50/50 border-t border-slate-100 space-y-2.5">
@csrf @csrf
<!-- Editable Product Name --> <!-- Editable Product Name -->
<div> <div>
<label class="block text-[10px] font-black uppercase tracking-wider text-slate-400 mb-1">Producttitel</label> <label class="block text-[10px] font-bold uppercase tracking-wider text-slate-500 mb-1">Producttitel</label>
<input <input
type="text" type="text"
name="name" name="name"
value="{{ old('name', $product->name) }}" value="{{ old('name', $product->name) }}"
required required
class="w-full px-3 py-1.5 bg-slate-900 border border-slate-700 rounded-xl text-white text-xs font-bold focus:ring-amber-400" class="w-full px-2.5 py-1.5 bg-white border border-slate-200 rounded-xl text-slate-800 text-xs font-semibold focus:outline-none focus:ring-2 focus:ring-amber-400"
> >
</div> </div>
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
<!-- Category selector --> <!-- Category selector -->
<div> <div>
<label class="block text-[10px] font-black uppercase tracking-wider text-slate-400 mb-1">Categorie</label> <label class="block text-[10px] font-bold uppercase tracking-wider text-slate-500 mb-1">Categorie</label>
<select name="category_id" required class="w-full px-2.5 py-1.5 bg-slate-900 border border-slate-700 rounded-xl text-white text-xs font-bold focus:ring-amber-400"> <select name="category_id" required class="w-full px-2 py-1.5 bg-white border border-slate-200 rounded-xl text-slate-800 text-xs font-semibold focus:outline-none focus:ring-2 focus:ring-amber-400">
@foreach($categories as $cat) @foreach($categories as $cat)
<option value="{{ $cat->id }}" {{ $product->category_id == $cat->id ? 'selected' : '' }}> <option value="{{ $cat->id }}" {{ $product->category_id == $cat->id ? 'selected' : '' }}>
{{ $cat->name }} {{ $cat->name }}
@@ -336,33 +318,33 @@
<!-- Retail Price --> <!-- Retail Price -->
<div> <div>
<label class="block text-[10px] font-black uppercase tracking-wider text-slate-400 mb-1">Verkoopprijs (€)</label> <label class="block text-[10px] font-bold uppercase tracking-wider text-slate-500 mb-1">Verkoopprijs (€)</label>
<input <input
type="number" type="number"
step="0.01" step="0.01"
name="base_price" name="base_price"
value="{{ old('base_price', $product->base_price) }}" value="{{ old('base_price', $product->base_price) }}"
required required
class="w-full px-2.5 py-1.5 bg-slate-900 border border-slate-700 rounded-xl text-amber-300 text-xs font-black focus:ring-amber-400" class="w-full px-2 py-1.5 bg-white border border-slate-200 rounded-xl text-slate-900 text-xs font-black focus:outline-none focus:ring-2 focus:ring-amber-400"
> >
</div> </div>
</div> </div>
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="pt-2 flex items-center justify-between gap-2"> <div class="pt-1.5 flex items-center justify-between gap-2">
<button type="submit" class="flex-1 py-2 px-3 bg-emerald-500 hover:bg-emerald-400 text-slate-950 font-black rounded-xl text-xs uppercase tracking-wider shadow-md shadow-emerald-500/20 transition flex items-center justify-center space-x-1"> <button type="submit" class="flex-1 py-2 px-3 bg-emerald-600 hover:bg-emerald-500 text-white font-bold rounded-xl text-xs uppercase tracking-wider shadow-xs transition flex items-center justify-center space-x-1">
<i data-lucide="check" class="w-4 h-4"></i> <i data-lucide="check" class="w-4 h-4"></i>
<span>Publiceer naar Webshop</span> <span>Publiceer naar Webshop</span>
</button> </button>
<a href="{{ route('admin.products.edit', $product) }}" class="p-2 rounded-xl text-slate-400 hover:text-amber-400 hover:bg-slate-800 border border-slate-700 transition" title="Foto's en productdetails bewerken"> <a href="{{ route('admin.products.edit', $product) }}" class="p-2 rounded-xl text-slate-500 hover:text-amber-700 hover:bg-white border border-slate-200 transition" title="Foto's en calculator openen">
<i data-lucide="image" class="w-4 h-4"></i> <i data-lucide="sliders" class="w-4 h-4"></i>
</a> </a>
<button <button
type="submit" type="submit"
form="reject-form-{{ $product->id }}" form="reject-form-{{ $product->id }}"
class="p-2 rounded-xl text-slate-500 hover:text-rose-400 hover:bg-rose-500/10 border border-slate-800 transition" class="p-2 rounded-xl text-slate-400 hover:text-rose-600 hover:bg-rose-50 border border-slate-200 transition"
title="Concept afwijzen & verwijderen" title="Concept afwijzen & verwijderen"
> >
<i data-lucide="trash-2" class="w-4 h-4"></i> <i data-lucide="trash-2" class="w-4 h-4"></i>
@@ -381,12 +363,11 @@
</div> </div>
<!-- Pagination --> <!-- Pagination -->
<div class="mt-8"> <div class="mt-6">
{{ $stagedProducts->links() }} {{ $stagedProducts->links() }}
</div> </div>
@endif @endif
</div>
</div> </div>
<script> <script>
@@ -406,4 +387,3 @@ function artikelBeheerApp() {
} }
</script> </script>
@endsection @endsection
+262 -69
View File
@@ -3,16 +3,86 @@
@section('title', 'Nieuw Product Toevoegen | Jouwdriedee Beheer') @section('title', 'Nieuw Product Toevoegen | Jouwdriedee Beheer')
@section('content') @section('content')
<div class="space-y-6 max-w-4xl mx-auto"> <div class="space-y-6 max-w-5xl mx-auto"
x-data="{
basePrice: '{{ old('base_price', '14.95') }}',
weightGrams: {{ old('weight_grams', 85) }},
printTimeHours: {{ old('print_time_hours', 4.0) }},
colorCount: 1,
material: 'pla',
markupPercent: {{ setting('calc_default_markup', 65.0) }},
showCalculator: true,
rates: {
filamentPriceKg: {{ setting('calc_filament_price_kg', 20.00) }},
electricityKwh: {{ setting('calc_electricity_kwh', 0.35) }},
machineHourly: {{ setting('calc_machine_hourly_rate', 0.50) }},
handlingFlat: {{ setting('calc_handling_flat', 1.50) }},
failureRate: {{ setting('calc_failure_rate', 5.0) }}
},
colorWasteMults: { 1: 1.0, 2: 1.15, 3: 1.25, 4: 1.35 },
materialMults: { 'pla': 1.0, 'petg': 1.20, 'silk': 1.30, 'tpu': 1.40 },
get effectiveWeight() {
let mult = this.colorWasteMults[this.colorCount] || 1.0;
return (this.weightGrams * mult).toFixed(1);
},
get purgeWasteGrams() {
return Math.max(0, this.effectiveWeight - this.weightGrams).toFixed(1);
},
get materialCost() {
let matMult = this.materialMults[this.material] || 1.0;
return ((this.effectiveWeight / 1000) * (this.rates.filamentPriceKg * matMult)).toFixed(2);
},
get electricityCost() {
return (this.printTimeHours * 0.15 * this.rates.electricityKwh).toFixed(2);
},
get machineCost() {
return (this.printTimeHours * this.rates.machineHourly).toFixed(2);
},
get handlingCost() {
let extraColorFee = Math.max(0, this.colorCount - 1) * 0.50;
return (this.rates.handlingFlat + extraColorFee).toFixed(2);
},
get subtotalCost() {
return (parseFloat(this.materialCost) + parseFloat(this.electricityCost) + parseFloat(this.machineCost) + parseFloat(this.handlingCost)).toFixed(2);
},
get failureCost() {
return (parseFloat(this.subtotalCost) * (this.rates.failureRate / 100)).toFixed(2);
},
get totalProductionCost() {
return (parseFloat(this.subtotalCost) + parseFloat(this.failureCost)).toFixed(2);
},
get priceExclVat() {
return (parseFloat(this.totalProductionCost) * (1 + this.markupPercent / 100)).toFixed(2);
},
get suggestedPrice() {
let rawWithVat = parseFloat(this.priceExclVat) * 1.21;
if (rawWithVat <= 7.0) return '6.95';
let floor = Math.floor(rawWithVat);
let fraction = rawWithVat - floor;
if (fraction <= 0.25) {
return (floor < rawWithVat ? floor + 0.50 : floor - 0.05).toFixed(2);
} else if (fraction <= 0.70) {
return (floor + 0.50).toFixed(2);
} else {
return (floor + 0.95).toFixed(2);
}
},
applySuggestedPrice() {
this.basePrice = this.suggestedPrice;
}
}">
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between pb-6 border-b border-slate-800"> <div class="flex items-center justify-between pb-5 border-b border-slate-200/80">
<div> <div>
<a href="{{ route('admin.products.index') }}" class="text-xs text-amber-400 font-bold hover:underline inline-flex items-center space-x-1 mb-1"> <a href="{{ route('admin.products.index') }}" class="text-xs text-amber-600 font-bold hover:underline inline-flex items-center space-x-1 mb-1">
<i data-lucide="arrow-left" class="w-3.5 h-3.5"></i> <i data-lucide="arrow-left" class="w-3.5 h-3.5"></i>
<span>Terug naar Producten</span> <span>Terug naar Producten</span>
</a> </a>
<h1 class="font-heading text-2xl font-black text-white">Nieuw Product Toevoegen</h1> <h1 class="font-heading text-2xl font-black text-slate-900">Nieuw Product Toevoegen</h1>
</div> </div>
</div> </div>
@@ -20,17 +90,18 @@
<form action="{{ route('admin.products.store') }}" method="POST" class="space-y-6"> <form action="{{ route('admin.products.store') }}" method="POST" class="space-y-6">
@csrf @csrf
<div class="p-6 rounded-3xl bg-slate-900 border border-slate-800 space-y-5"> <div class="p-6 sm:p-8 rounded-3xl bg-white border border-slate-200/80 shadow-2xs space-y-6">
<!-- Basis Informatie -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="md:col-span-2 space-y-1.5"> <div class="md:col-span-2 space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Productnaam *</label> <label class="block text-xs font-bold text-slate-700">Productnaam *</label>
<input type="text" name="name" value="{{ old('name') }}" required placeholder="bijv. Articulated Mecha Robot" class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white font-bold focus:outline-none focus:ring-2 focus:ring-amber-400"> <input type="text" name="name" value="{{ old('name') }}" required placeholder="bijv. Articulated Mecha Robot" class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs text-slate-900 font-bold focus:outline-none focus:ring-2 focus:ring-amber-400/20 focus:border-amber-400 transition">
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Categorie *</label> <label class="block text-xs font-bold text-slate-700">Categorie *</label>
<select name="category_id" required class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3 py-2.5 text-xs text-white font-bold"> <select name="category_id" required class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3 py-2.5 text-xs text-slate-900 font-bold">
@foreach($categories as $cat) @foreach($categories as $cat)
<option value="{{ $cat->id }}" {{ old('category_id') == $cat->id ? 'selected' : '' }}> <option value="{{ $cat->id }}" {{ old('category_id') == $cat->id ? 'selected' : '' }}>
{{ $cat->name }} {{ $cat->name }}
@@ -40,20 +111,144 @@
</div> </div>
</div> </div>
<!-- ==================================================== -->
<!-- 3D PRINT KOSTPRIJSCALCULATOR WIDGET -->
<!-- ==================================================== -->
<div class="p-5 rounded-2xl bg-amber-50/40 border border-amber-200/70 space-y-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-2">
<span class="w-6 h-6 rounded-lg bg-amber-400 text-slate-950 flex items-center justify-center text-xs">🖨️</span>
<h3 class="font-heading text-xs font-black text-slate-900 uppercase tracking-wider">3D Print Kostprijscalculator</h3>
<span class="px-2 py-0.5 rounded-full bg-amber-100 text-amber-900 font-mono text-[10px] font-bold">Automatisch Prijsvoorstel</span>
</div>
<button type="button" @click="showCalculator = !showCalculator" class="text-xs text-slate-500 hover:text-slate-800 font-semibold flex items-center space-x-1">
<span x-text="showCalculator ? 'Inklappen' : 'Calculator Openen'"></span>
<i data-lucide="chevron-down" class="w-3.5 h-3.5 transition-transform" :class="showCalculator ? 'rotate-180' : ''"></i>
</button>
</div>
<div x-show="showCalculator" x-transition class="space-y-4 pt-2">
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 text-xs">
<!-- Gewicht -->
<div class="space-y-1">
<label class="block text-[11px] font-bold text-slate-600">Gewicht (gram)</label>
<div class="relative">
<input type="number" x-model.number="weightGrams" min="1" max="5000" class="w-full bg-white border border-slate-200 rounded-xl px-3 py-1.5 text-xs font-black text-slate-900">
<span class="absolute right-2.5 top-1/2 -translate-y-1/2 text-[10px] text-slate-400 font-bold">gram</span>
</div>
</div>
<!-- Printtijd -->
<div class="space-y-1">
<label class="block text-[11px] font-bold text-slate-600">Printtijd (uren)</label>
<div class="relative">
<input type="number" step="0.1" x-model.number="printTimeHours" min="0.1" max="100" class="w-full bg-white border border-slate-200 rounded-xl px-3 py-1.5 text-xs font-black text-slate-900">
<span class="absolute right-2.5 top-1/2 -translate-y-1/2 text-[10px] text-slate-400 font-bold">uur</span>
</div>
</div>
<!-- Aantal Kleuren (AMS Purge) -->
<div class="space-y-1">
<label class="block text-[11px] font-bold text-slate-600">Aantal Kleuren</label>
<div class="grid grid-cols-4 gap-1">
<template x-for="c in [1, 2, 3, 4]" :key="c">
<button
type="button"
@click="colorCount = c"
class="py-1.5 rounded-lg text-center font-bold text-xs transition border"
:class="colorCount === c ? 'bg-amber-400 border-amber-500 text-slate-950 font-black shadow-2xs' : 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'"
x-text="c"
></button>
</template>
</div>
</div>
<!-- Materiaalsoort -->
<div class="space-y-1">
<label class="block text-[11px] font-bold text-slate-600">Materiaalsoort</label>
<select x-model="material" class="w-full bg-white border border-slate-200 rounded-xl px-2.5 py-1.5 text-xs font-bold text-slate-900">
<option value="pla">Bio-PLA (1.0x)</option>
<option value="petg">PETG Tough (1.2x)</option>
<option value="silk">Silk Zijdeglans (1.3x)</option>
<option value="tpu">TPU Flexibel (1.4x)</option>
</select>
</div>
</div>
<!-- Winstmarge selectie -->
<div class="flex flex-wrap items-center justify-between gap-2 pt-2 border-t border-amber-200/50 text-xs">
<div class="flex items-center space-x-1.5">
<span class="text-slate-600 font-bold text-[11px]">Winstmarge:</span>
<div class="inline-flex rounded-lg border border-slate-200 bg-white p-0.5 text-[11px] font-bold">
<button type="button" @click="markupPercent = 35" :class="markupPercent === 35 ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-600 hover:text-slate-900'" class="px-2 py-0.5 rounded-md transition">35%</button>
<button type="button" @click="markupPercent = 50" :class="markupPercent === 50 ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-600 hover:text-slate-900'" class="px-2 py-0.5 rounded-md transition">50%</button>
<button type="button" @click="markupPercent = 65" :class="markupPercent === 65 ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-600 hover:text-slate-900'" class="px-2 py-0.5 rounded-md transition">65%</button>
<button type="button" @click="markupPercent = 100" :class="markupPercent === 100 ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-600 hover:text-slate-900'" class="px-2 py-0.5 rounded-md transition">100%</button>
</div>
</div>
<!-- Live Cost Breakdown Strip -->
<div class="flex items-center space-x-3 text-[11px] text-slate-500 font-semibold">
<span>🧵 Materiaal: €<span x-text="materialCost"></span></span>
<span>⚡ Stroom: €<span x-text="electricityCost"></span></span>
<span>⚙️ Slijtage: €<span x-text="machineCost"></span></span>
<span class="font-bold text-slate-700">Kostprijs: €<span x-text="totalProductionCost"></span></span>
</div>
</div>
<!-- Result Banner met 1-klik overneem knop -->
<div class="p-3.5 rounded-xl bg-white border border-amber-300 shadow-2xs flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 rounded-xl bg-amber-100 text-amber-900 flex items-center justify-center font-heading font-black text-sm">
€
</div>
<div>
<span class="text-[10px] text-slate-400 block font-bold uppercase tracking-wider">Berekend Prijsvoorstel (incl. 21% btw)</span>
<div class="flex items-baseline space-x-1.5">
<span class="font-heading text-xl font-black text-slate-900">€ <span x-text="suggestedPrice"></span></span>
<span class="text-[11px] text-slate-500">op basis van <span x-text="weightGrams"></span>g &bull; <span x-text="printTimeHours"></span>u &bull; <span x-text="colorCount"></span> kleur(en)</span>
</div>
</div>
</div>
<button
type="button"
@click="applySuggestedPrice"
class="px-4 py-2 bg-gradient-to-r from-amber-400 to-rose-400 hover:from-amber-300 hover:to-rose-300 text-slate-950 font-black text-xs rounded-xl shadow-xs transition flex items-center justify-center space-x-1.5 cursor-pointer"
>
<i data-lucide="check" class="w-4 h-4"></i>
<span>Neem Adviesprijs Over (€ <span x-text="suggestedPrice"></span>)</span>
</button>
</div>
</div>
</div>
<!-- Prijs & Korte Omschrijving -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Basisprijs (€) *</label> <label class="block text-xs font-bold text-slate-700">Basisprijs (€) *</label>
<input type="number" step="0.01" name="base_price" value="{{ old('base_price', '14.95') }}" required class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white font-bold"> <div class="relative">
<span class="absolute left-3.5 top-1/2 -translate-y-1/2 font-bold text-slate-400 text-xs">€</span>
<input
type="number"
step="0.01"
name="base_price"
x-model="basePrice"
required
class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl pl-7 pr-3.5 py-2.5 text-xs text-slate-900 font-bold focus:outline-none focus:ring-2 focus:ring-amber-400/20 focus:border-amber-400"
>
</div>
</div> </div>
<div class="md:col-span-2 space-y-1.5"> <div class="md:col-span-2 space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Korte Omschrijving (voor overzichtskaarten)</label> <label class="block text-xs font-bold text-slate-700">Korte Omschrijving (voor catalogus cards)</label>
<input type="text" name="short_description" value="{{ old('short_description') }}" placeholder="Korte samenvatting in 1 zin..." class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white"> <input type="text" name="short_description" value="{{ old('short_description') }}" placeholder="Korte samenvatting in 1 zin..." class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs text-slate-900">
</div> </div>
</div> </div>
<!-- Visueel Afbeeldingen & Fotogalerij Beheer --> <!-- Visueel Afbeeldingen & Fotogalerij Beheer -->
<div class="pt-5 border-t border-slate-800 space-y-4" <div class="pt-5 border-t border-slate-200/80 space-y-4"
x-data="{ x-data="{
imageUrl: '{{ old('image_url', '/images/products/crystal-dragon.jpg') }}', imageUrl: '{{ old('image_url', '/images/products/crystal-dragon.jpg') }}',
galleryImages: {{ json_encode(old('gallery_images', []), JSON_UNESCAPED_SLASHES) }}, galleryImages: {{ json_encode(old('gallery_images', []), JSON_UNESCAPED_SLASHES) }},
@@ -139,7 +334,7 @@
} }
}"> }">
<!-- Verborgen form fields die verstuurd worden naar de server --> <!-- Verborgen form fields -->
<input type="hidden" name="image_url" :value="imageUrl"> <input type="hidden" name="image_url" :value="imageUrl">
<template x-for="(img, idx) in galleryImages" :key="idx"> <template x-for="(img, idx) in galleryImages" :key="idx">
<input type="hidden" name="gallery_images[]" :value="img"> <input type="hidden" name="gallery_images[]" :value="img">
@@ -148,64 +343,64 @@
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-2"> <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
<div> <div>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<i data-lucide="image" class="w-4 h-4 text-amber-400"></i> <i data-lucide="image" class="w-4 h-4 text-amber-500"></i>
<h3 class="font-heading text-sm font-black text-white uppercase tracking-wider">Productafbeeldingen & Fotogalerij</h3> <h3 class="font-heading text-sm font-black text-slate-900 uppercase tracking-wider">Productafbeeldingen & Fotogalerij</h3>
<span class="px-2 py-0.5 rounded-full bg-slate-800 text-amber-400 font-mono text-[11px] font-bold" x-text="(imageUrl ? 1 : 0) + galleryImages.length + ' foto(\'s)'"></span> <span class="px-2 py-0.5 rounded-full bg-slate-100 text-slate-700 font-mono text-[11px] font-bold" x-text="(imageUrl ? 1 : 0) + galleryImages.length + ' foto(\'s)'"></span>
</div> </div>
<p class="text-xs text-slate-400 mt-0.5"> <p class="text-xs text-slate-500 mt-0.5">
Upload foto's vanaf je computer/telefoon of plak een web-URL. De eerste foto is automatisch de hoofdfoto. Upload foto's vanaf je computer of telefoon, of plak een web-URL.
</p> </p>
</div> </div>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<!-- Bestand uploaden button --> <!-- Bestand uploaden button -->
<label class="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-700 cursor-pointer"> <label class="px-3 py-1.5 rounded-xl bg-white hover:bg-slate-50 text-slate-700 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200 shadow-2xs cursor-pointer">
<i data-lucide="upload" class="w-3.5 h-3.5 text-amber-400"></i> <i data-lucide="upload" class="w-3.5 h-3.5 text-amber-500"></i>
<span x-text="isUploading ? 'Uploaden...' : 'Foto Uploaden'"></span> <span x-text="isUploading ? 'Uploaden...' : 'Foto Uploaden'"></span>
<input type="file" accept="image/*" class="hidden" @change="uploadFile" :disabled="isUploading"> <input type="file" accept="image/*" class="hidden" @change="uploadFile" :disabled="isUploading">
</label> </label>
<!-- URL toevoegen toggle --> <!-- URL toevoegen toggle -->
<button type="button" @click="showUrlInput = !showUrlInput" class="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-700"> <button type="button" @click="showUrlInput = !showUrlInput" class="px-3 py-1.5 rounded-xl bg-white hover:bg-slate-50 text-slate-700 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200 shadow-2xs">
<i data-lucide="link" class="w-3.5 h-3.5 text-amber-400"></i> <i data-lucide="link" class="w-3.5 h-3.5 text-amber-500"></i>
<span>URL Plakken</span> <span>URL Plakken</span>
</button> </button>
</div> </div>
</div> </div>
<!-- URL Toevoegen Bar (uitklapbaar) --> <!-- URL Toevoegen Bar -->
<div x-show="showUrlInput" x-transition class="p-3 bg-slate-950 rounded-2xl border border-slate-800 space-y-2"> <div x-show="showUrlInput" x-transition class="p-3 bg-slate-50 rounded-2xl border border-slate-200 space-y-2">
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<input <input
type="text" type="text"
x-model="newImageUrl" x-model="newImageUrl"
@keydown.enter.prevent="addImageUrl" @keydown.enter.prevent="addImageUrl"
placeholder="Plak hier een foto-URL (bijv. https://... of /images/products/...)" placeholder="Plak hier een foto-URL (bijv. https://... of /images/products/...)"
class="flex-1 bg-slate-900 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-400" class="flex-1 bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-amber-400"
> >
<button type="button" @click="addImageUrl" class="px-4 py-2 bg-amber-400 hover:bg-amber-300 text-slate-950 rounded-xl text-xs font-black transition flex items-center space-x-1"> <button type="button" @click="addImageUrl" class="px-4 py-2 bg-amber-400 hover:bg-amber-300 text-slate-950 rounded-xl text-xs font-black transition flex items-center space-x-1">
<i data-lucide="plus" class="w-3.5 h-3.5"></i> <i data-lucide="plus" class="w-3.5 h-3.5"></i>
<span>Toevoegen</span> <span>Toevoegen</span>
</button> </button>
</div> </div>
<p x-show="addError" x-text="addError" class="text-xs text-rose-400 font-bold"></p> <p x-show="addError" x-text="addError" class="text-xs text-rose-600 font-bold"></p>
</div> </div>
<div x-show="uploadError" x-text="uploadError" class="p-2.5 bg-rose-500/10 border border-rose-500/30 text-xs text-rose-300 rounded-xl font-bold"></div> <div x-show="uploadError" x-text="uploadError" class="p-2.5 bg-rose-50 border border-rose-200 text-xs text-rose-700 rounded-xl font-bold"></div>
<!-- Visuele Weergave: Hoofdafbeelding + Gallerij Grid --> <!-- Visuele Weergave: Hoofdafbeelding + Gallerij Grid -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-4 items-start"> <div class="grid grid-cols-1 md:grid-cols-12 gap-4 items-start">
<!-- Hoofdafbeelding Box (Links) --> <!-- Hoofdafbeelding Box (Links) -->
<div class="md:col-span-5 bg-slate-950 rounded-2xl border-2 border-amber-400/50 p-3 space-y-2 relative overflow-hidden"> <div class="md:col-span-5 bg-slate-50/80 rounded-2xl border-2 border-amber-300 p-3.5 space-y-2 relative overflow-hidden">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="px-2.5 py-1 rounded-full bg-amber-400 text-slate-950 font-black text-[10px] uppercase tracking-wider flex items-center space-x-1 shadow-sm"> <span class="px-2.5 py-1 rounded-full bg-amber-400 text-slate-950 font-black text-[10px] uppercase tracking-wider flex items-center space-x-1 shadow-2xs">
<i data-lucide="star" class="w-3 h-3 fill-current"></i> <i data-lucide="star" class="w-3 h-3 fill-current"></i>
<span>Hoofdafbeelding (Cover)</span> <span>Hoofdafbeelding (Cover)</span>
</span> </span>
<template x-if="imageUrl"> <template x-if="imageUrl">
<button type="button" @click="removeCover" class="text-slate-400 hover:text-rose-400 text-xs font-bold transition flex items-center space-x-1" title="Verwijder hoofdafbeelding"> <button type="button" @click="removeCover" class="text-slate-400 hover:text-rose-600 text-xs font-bold transition flex items-center space-x-1" title="Verwijder hoofdafbeelding">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i> <i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
<span>Verwijderen</span> <span>Verwijderen</span>
</button> </button>
@@ -213,52 +408,51 @@
</div> </div>
<!-- Grote Image Preview --> <!-- Grote Image Preview -->
<div class="aspect-4/3 rounded-xl bg-slate-900 overflow-hidden flex items-center justify-center relative border border-slate-800"> <div class="aspect-4/3 rounded-xl bg-white overflow-hidden flex items-center justify-center relative border border-slate-200 shadow-2xs">
<template x-if="imageUrl"> <template x-if="imageUrl">
<img :src="imageUrl" alt="Hoofdafbeelding" class="w-full h-full object-cover"> <img :src="imageUrl" alt="Hoofdafbeelding" class="w-full h-full object-cover">
</template> </template>
<template x-if="!imageUrl"> <template x-if="!imageUrl">
<div class="text-center p-4 text-slate-500 space-y-1"> <div class="text-center p-4 text-slate-400 space-y-1">
<i data-lucide="image" class="w-8 h-8 mx-auto text-slate-600"></i> <i data-lucide="image" class="w-8 h-8 mx-auto text-slate-300"></i>
<p class="text-xs font-bold">Geen hoofdafbeelding</p> <p class="text-xs font-bold">Geen hoofdafbeelding</p>
<p class="text-[10px]">Kies een foto uit de galerij of voeg er een toe.</p> <p class="text-[10px]">Kies een foto of voeg er een toe.</p>
</div> </div>
</template> </template>
</div> </div>
<!-- Huidige Cover URL weergave --> <!-- Huidige Cover URL weergave -->
<div class="pt-1"> <div class="pt-1">
<span class="text-[10px] font-mono text-slate-500 truncate block" x-text="imageUrl || 'Geen URL ingesteld'"></span> <span class="text-[10px] font-mono text-slate-400 truncate block" x-text="imageUrl || 'Geen URL ingesteld'"></span>
</div> </div>
</div> </div>
<!-- Gallerij Foto's (Rechts) --> <!-- Gallerij Foto's (Rechts) -->
<div class="md:col-span-7 bg-slate-950/60 rounded-2xl border border-slate-800 p-3 space-y-3"> <div class="md:col-span-7 bg-slate-50/60 rounded-2xl border border-slate-200 p-3.5 space-y-3">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-xs font-black text-slate-300 uppercase tracking-wider">Extra Gallerij Foto's</span> <span class="text-xs font-black text-slate-700 uppercase tracking-wider">Extra Gallerij Foto's</span>
<span class="text-[10px] text-slate-500 font-bold" x-text="galleryImages.length + ' extra foto(\'s)'"></span> <span class="text-[10px] text-slate-400 font-bold" x-text="galleryImages.length + ' extra foto(\'s)'"></span>
</div> </div>
<!-- Grid van Extra Foto's --> <!-- Grid van Extra Foto's -->
<div class="grid grid-cols-2 sm:grid-cols-3 gap-2.5 max-h-96 overflow-y-auto pr-1"> <div class="grid grid-cols-2 sm:grid-cols-3 gap-2.5 max-h-96 overflow-y-auto pr-1">
<template x-for="(img, index) in galleryImages" :key="index"> <template x-for="(img, index) in galleryImages" :key="index">
<div class="group relative rounded-xl bg-slate-900 border border-slate-800 hover:border-amber-400 transition overflow-hidden"> <div class="group relative rounded-xl bg-white border border-slate-200 hover:border-amber-400 transition overflow-hidden shadow-2xs">
<div class="aspect-square w-full overflow-hidden bg-slate-950"> <div class="aspect-square w-full overflow-hidden bg-slate-100">
<img :src="img" alt="Gallerij foto" class="w-full h-full object-cover group-hover:scale-105 transition duration-200" loading="lazy"> <img :src="img" alt="Gallerij foto" class="w-full h-full object-cover group-hover:scale-105 transition duration-200" loading="lazy">
</div> </div>
<!-- Index Badge --> <!-- Index Badge -->
<div class="absolute top-1.5 left-1.5"> <div class="absolute top-1.5 left-1.5">
<span class="px-1.5 py-0.5 rounded bg-slate-950/80 text-white font-mono text-[9px] font-bold backdrop-blur" x-text="'#' + (index + 1)"></span> <span class="px-1.5 py-0.5 rounded bg-slate-900/80 text-white font-mono text-[9px] font-bold backdrop-blur" x-text="'#' + (index + 1)"></span>
</div> </div>
<!-- Overlay Knoppen (Maak Hoofdfoto & Verwijderen) --> <!-- Overlay Knoppen -->
<div class="absolute inset-0 bg-slate-950/70 backdrop-blur-xs opacity-0 group-hover:opacity-100 transition flex flex-col items-center justify-center gap-1.5 p-2"> <div class="absolute inset-0 bg-slate-900/60 opacity-0 group-hover:opacity-100 transition flex flex-col items-center justify-center gap-1.5 p-2">
<button <button
type="button" type="button"
@click="setAsCover(index)" @click="setAsCover(index)"
class="w-full py-1.5 px-2 bg-amber-400 hover:bg-amber-300 text-slate-950 rounded-lg text-[10px] font-black uppercase tracking-wider transition flex items-center justify-center space-x-1 shadow" class="w-full py-1.5 px-2 bg-amber-400 hover:bg-amber-300 text-slate-950 rounded-lg text-[10px] font-black uppercase tracking-wider transition flex items-center justify-center space-x-1 shadow"
title="Maak deze foto de hoofdafbeelding"
> >
<i data-lucide="star" class="w-3 h-3 fill-current"></i> <i data-lucide="star" class="w-3 h-3 fill-current"></i>
<span>Als Cover</span> <span>Als Cover</span>
@@ -268,7 +462,6 @@
type="button" type="button"
@click="removeGalleryImage(index)" @click="removeGalleryImage(index)"
class="w-full py-1 px-2 bg-rose-600 hover:bg-rose-500 text-white rounded-lg text-[10px] font-bold transition flex items-center justify-center space-x-1" class="w-full py-1 px-2 bg-rose-600 hover:bg-rose-500 text-white rounded-lg text-[10px] font-bold transition flex items-center justify-center space-x-1"
title="Verwijder deze foto uit galerij"
> >
<i data-lucide="trash-2" class="w-3 h-3"></i> <i data-lucide="trash-2" class="w-3 h-3"></i>
<span>Verwijder</span> <span>Verwijder</span>
@@ -278,11 +471,11 @@
</template> </template>
</div> </div>
<!-- Empty state als er geen extra gallerij foto's zijn --> <!-- Empty state -->
<div x-show="galleryImages.length === 0" class="text-center py-8 px-4 rounded-xl border border-dashed border-slate-800 text-slate-500 space-y-1"> <div x-show="galleryImages.length === 0" class="text-center py-8 px-4 rounded-xl border border-dashed border-slate-200 text-slate-400 space-y-1">
<i data-lucide="images" class="w-6 h-6 mx-auto text-slate-600"></i> <i data-lucide="images" class="w-6 h-6 mx-auto text-slate-300"></i>
<p class="text-xs font-bold text-slate-400">Nog geen extra galerijfoto's</p> <p class="text-xs font-bold text-slate-600">Nog geen extra galerijfoto's</p>
<p class="text-[11px] text-slate-500 max-w-xs mx-auto"> <p class="text-[11px] text-slate-400 max-w-xs mx-auto">
Voeg extra detailfoto's toe via "URL Plakken" of "Foto Uploaden". Voeg extra detailfoto's toe via "URL Plakken" of "Foto Uploaden".
</p> </p>
</div> </div>
@@ -292,38 +485,39 @@
</div> </div>
<!-- Beschrijving -->
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Volledige Productbeschrijving *</label> <label class="block text-xs font-bold text-slate-700">Volledige Productbeschrijving *</label>
<textarea name="description" rows="5" required class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white leading-relaxed">{{ old('description') }}</textarea> <textarea name="description" rows="5" required class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs text-slate-900 leading-relaxed">{{ old('description') }}</textarea>
</div> </div>
<!-- Print Specs --> <!-- Print Specs -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 pt-3 border-t border-slate-800"> <div class="grid grid-cols-1 sm:grid-cols-3 gap-4 pt-4 border-t border-slate-200/80">
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Afmetingen (LxBxH)</label> <label class="block text-xs font-bold text-slate-700">Afmetingen (LxBxH)</label>
<input type="text" name="dimensions" value="{{ old('dimensions', '15 x 10 x 8 cm') }}" class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white"> <input type="text" name="dimensions" value="{{ old('dimensions', '15 x 10 x 8 cm') }}" class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-900">
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Gewicht (gram)</label> <label class="block text-xs font-bold text-slate-700">Gewicht (gram)</label>
<input type="number" name="weight_grams" value="{{ old('weight_grams', '85') }}" class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white"> <input type="number" name="weight_grams" x-model.number="weightGrams" class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-900">
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Printtijd (uren)</label> <label class="block text-xs font-bold text-slate-700">Printtijd (uren)</label>
<input type="number" step="0.1" name="print_time_hours" value="{{ old('print_time_hours', '4.5') }}" class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white"> <input type="number" step="0.1" name="print_time_hours" x-model.number="printTimeHours" class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-900">
</div> </div>
</div> </div>
<!-- Toggles --> <!-- Toggles -->
<div class="pt-3 border-t border-slate-800 flex items-center space-x-6"> <div class="pt-4 border-t border-slate-200/80 flex items-center space-x-6">
<label class="flex items-center space-x-2 cursor-pointer text-xs font-bold text-slate-200"> <label class="flex items-center space-x-2 cursor-pointer text-xs font-bold text-slate-700">
<input type="checkbox" name="is_active" value="1" checked class="w-4 h-4 rounded text-amber-400 bg-slate-950 border-slate-700"> <input type="checkbox" name="is_active" value="1" checked class="w-4 h-4 rounded text-amber-500 bg-white border-slate-300">
<span>Direct zichtbaar in webshop (Actief)</span> <span>Direct zichtbaar in webshop (Actief)</span>
</label> </label>
<label class="flex items-center space-x-2 cursor-pointer text-xs font-bold text-slate-200"> <label class="flex items-center space-x-2 cursor-pointer text-xs font-bold text-slate-700">
<input type="checkbox" name="is_featured" value="1" class="w-4 h-4 rounded text-amber-400 bg-slate-950 border-slate-700"> <input type="checkbox" name="is_featured" value="1" class="w-4 h-4 rounded text-amber-500 bg-white border-slate-300">
<span>Uitlichten op homepage</span> <span>Uitlichten op homepage</span>
</label> </label>
</div> </div>
@@ -331,10 +525,10 @@
</div> </div>
<div class="flex items-center justify-end space-x-3"> <div class="flex items-center justify-end space-x-3">
<a href="{{ route('admin.products.index') }}" class="px-4 py-2.5 rounded-xl bg-slate-800 text-slate-300 hover:text-white text-xs font-bold transition"> <a href="{{ route('admin.products.index') }}" class="px-4 py-2.5 rounded-xl bg-white hover:bg-slate-50 text-slate-600 text-xs font-bold transition border border-slate-200">
Annuleren Annuleren
</a> </a>
<button type="submit" class="px-6 py-2.5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs transition flex items-center space-x-1.5"> <button type="submit" class="px-6 py-2.5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs transition flex items-center space-x-1.5 shadow-xs cursor-pointer">
<i data-lucide="plus" class="w-4 h-4"></i> <i data-lucide="plus" class="w-4 h-4"></i>
<span>Product Publiceren</span> <span>Product Publiceren</span>
</button> </button>
@@ -343,4 +537,3 @@
</form> </form>
</div> </div>
@endsection @endsection
+264 -69
View File
@@ -3,40 +3,113 @@
@section('title', 'Product Bewerken: ' . $product->name . ' | Jouwdriedee Beheer') @section('title', 'Product Bewerken: ' . $product->name . ' | Jouwdriedee Beheer')
@section('content') @section('content')
<div class="space-y-6 max-w-4xl mx-auto"> <div class="space-y-6 max-w-5xl mx-auto"
x-data="{
basePrice: '{{ old('base_price', $product->base_price) }}',
weightGrams: {{ old('weight_grams', $product->weight_grams ?: 85) }},
printTimeHours: {{ old('print_time_hours', $product->print_time_hours ?: 4.0) }},
colorCount: 1,
material: 'pla',
markupPercent: {{ setting('calc_default_markup', 65.0) }},
showCalculator: true,
rates: {
filamentPriceKg: {{ setting('calc_filament_price_kg', 20.00) }},
electricityKwh: {{ setting('calc_electricity_kwh', 0.35) }},
machineHourly: {{ setting('calc_machine_hourly_rate', 0.50) }},
handlingFlat: {{ setting('calc_handling_flat', 1.50) }},
failureRate: {{ setting('calc_failure_rate', 5.0) }}
},
colorWasteMults: { 1: 1.0, 2: 1.15, 3: 1.25, 4: 1.35 },
materialMults: { 'pla': 1.0, 'petg': 1.20, 'silk': 1.30, 'tpu': 1.40 },
get effectiveWeight() {
let mult = this.colorWasteMults[this.colorCount] || 1.0;
return (this.weightGrams * mult).toFixed(1);
},
get purgeWasteGrams() {
return Math.max(0, this.effectiveWeight - this.weightGrams).toFixed(1);
},
get materialCost() {
let matMult = this.materialMults[this.material] || 1.0;
return ((this.effectiveWeight / 1000) * (this.rates.filamentPriceKg * matMult)).toFixed(2);
},
get electricityCost() {
return (this.printTimeHours * 0.15 * this.rates.electricityKwh).toFixed(2);
},
get machineCost() {
return (this.printTimeHours * this.rates.machineHourly).toFixed(2);
},
get handlingCost() {
let extraColorFee = Math.max(0, this.colorCount - 1) * 0.50;
return (this.rates.handlingFlat + extraColorFee).toFixed(2);
},
get subtotalCost() {
return (parseFloat(this.materialCost) + parseFloat(this.electricityCost) + parseFloat(this.machineCost) + parseFloat(this.handlingCost)).toFixed(2);
},
get failureCost() {
return (parseFloat(this.subtotalCost) * (this.rates.failureRate / 100)).toFixed(2);
},
get totalProductionCost() {
return (parseFloat(this.subtotalCost) + parseFloat(this.failureCost)).toFixed(2);
},
get priceExclVat() {
return (parseFloat(this.totalProductionCost) * (1 + this.markupPercent / 100)).toFixed(2);
},
get suggestedPrice() {
let rawWithVat = parseFloat(this.priceExclVat) * 1.21;
if (rawWithVat <= 7.0) return '6.95';
let floor = Math.floor(rawWithVat);
let fraction = rawWithVat - floor;
if (fraction <= 0.25) {
return (floor < rawWithVat ? floor + 0.50 : floor - 0.05).toFixed(2);
} else if (fraction <= 0.70) {
return (floor + 0.50).toFixed(2);
} else {
return (floor + 0.95).toFixed(2);
}
},
applySuggestedPrice() {
this.basePrice = this.suggestedPrice;
}
}">
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between pb-6 border-b border-slate-800"> <div class="flex items-center justify-between pb-5 border-b border-slate-200/80">
<div> <div>
<a href="{{ route('admin.products.index') }}" class="text-xs text-amber-400 font-bold hover:underline inline-flex items-center space-x-1 mb-1"> <a href="{{ route('admin.products.index') }}" class="text-xs text-amber-600 font-bold hover:underline inline-flex items-center space-x-1 mb-1">
<i data-lucide="arrow-left" class="w-3.5 h-3.5"></i> <i data-lucide="arrow-left" class="w-3.5 h-3.5"></i>
<span>Terug naar Producten</span> <span>Terug naar Producten</span>
</a> </a>
<h1 class="font-heading text-2xl font-black text-white">Product Bewerken: {{ $product->name }}</h1> <h1 class="font-heading text-2xl font-black text-slate-900">Product Bewerken: {{ $product->name }}</h1>
</div> </div>
<a href="{{ route('product.show', $product->slug) }}" target="_blank" class="px-3.5 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white text-xs font-bold transition flex items-center space-x-1.5 border border-slate-700"> <div class="flex items-center space-x-2">
<a href="{{ route('product.show', $product->slug) }}" target="_blank" class="px-3.5 py-2 rounded-xl bg-white hover:bg-slate-50 text-slate-700 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200 shadow-2xs">
<span>Bekijk Live</span> <span>Bekijk Live</span>
<i data-lucide="external-link" class="w-3.5 h-3.5"></i> <i data-lucide="external-link" class="w-3.5 h-3.5 text-slate-400"></i>
</a> </a>
</div> </div>
</div>
<!-- Edit Form --> <!-- Edit Form -->
<form action="{{ route('admin.products.update', $product) }}" method="POST" class="space-y-6"> <form action="{{ route('admin.products.update', $product) }}" method="POST" class="space-y-6">
@csrf @csrf
@method('PUT') @method('PUT')
<div class="p-6 rounded-3xl bg-slate-900 border border-slate-800 space-y-5"> <div class="p-6 sm:p-8 rounded-3xl bg-white border border-slate-200/80 shadow-2xs space-y-6">
<!-- Basis Informatie -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="md:col-span-2 space-y-1.5"> <div class="md:col-span-2 space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Productnaam *</label> <label class="block text-xs font-bold text-slate-700">Productnaam *</label>
<input type="text" name="name" value="{{ old('name', $product->name) }}" required class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white font-bold focus:outline-none focus:ring-2 focus:ring-amber-400"> <input type="text" name="name" value="{{ old('name', $product->name) }}" required class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs text-slate-900 font-bold focus:outline-none focus:ring-2 focus:ring-amber-400/20 focus:border-amber-400 transition">
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Categorie *</label> <label class="block text-xs font-bold text-slate-700">Categorie *</label>
<select name="category_id" required class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3 py-2.5 text-xs text-white font-bold"> <select name="category_id" required class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3 py-2.5 text-xs text-slate-900 font-bold">
@foreach($categories as $cat) @foreach($categories as $cat)
<option value="{{ $cat->id }}" {{ old('category_id', $product->category_id) == $cat->id ? 'selected' : '' }}> <option value="{{ $cat->id }}" {{ old('category_id', $product->category_id) == $cat->id ? 'selected' : '' }}>
{{ $cat->name }} {{ $cat->name }}
@@ -46,20 +119,144 @@
</div> </div>
</div> </div>
<!-- ==================================================== -->
<!-- 3D PRINT KOSTPRIJSCALCULATOR WIDGET (3dprintingcostcalculator) -->
<!-- ==================================================== -->
<div class="p-5 rounded-2xl bg-amber-50/40 border border-amber-200/70 space-y-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-2">
<span class="w-6 h-6 rounded-lg bg-amber-400 text-slate-950 flex items-center justify-center text-xs">🖨️</span>
<h3 class="font-heading text-xs font-black text-slate-900 uppercase tracking-wider">3D Print Kostprijscalculator</h3>
<span class="px-2 py-0.5 rounded-full bg-amber-100 text-amber-900 font-mono text-[10px] font-bold">Live Calculator</span>
</div>
<button type="button" @click="showCalculator = !showCalculator" class="text-xs text-slate-500 hover:text-slate-800 font-semibold flex items-center space-x-1">
<span x-text="showCalculator ? 'Inklappen' : 'Calculator Openen'"></span>
<i data-lucide="chevron-down" class="w-3.5 h-3.5 transition-transform" :class="showCalculator ? 'rotate-180' : ''"></i>
</button>
</div>
<div x-show="showCalculator" x-transition class="space-y-4 pt-2">
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 text-xs">
<!-- Gewicht -->
<div class="space-y-1">
<label class="block text-[11px] font-bold text-slate-600">Gewicht (gram)</label>
<div class="relative">
<input type="number" x-model.number="weightGrams" min="1" max="5000" class="w-full bg-white border border-slate-200 rounded-xl px-3 py-1.5 text-xs font-black text-slate-900">
<span class="absolute right-2.5 top-1/2 -translate-y-1/2 text-[10px] text-slate-400 font-bold">gram</span>
</div>
</div>
<!-- Printtijd -->
<div class="space-y-1">
<label class="block text-[11px] font-bold text-slate-600">Printtijd (uren)</label>
<div class="relative">
<input type="number" step="0.1" x-model.number="printTimeHours" min="0.1" max="100" class="w-full bg-white border border-slate-200 rounded-xl px-3 py-1.5 text-xs font-black text-slate-900">
<span class="absolute right-2.5 top-1/2 -translate-y-1/2 text-[10px] text-slate-400 font-bold">uur</span>
</div>
</div>
<!-- Aantal Kleuren (AMS Purge) -->
<div class="space-y-1">
<label class="block text-[11px] font-bold text-slate-600">Aantal Kleuren</label>
<div class="grid grid-cols-4 gap-1">
<template x-for="c in [1, 2, 3, 4]" :key="c">
<button
type="button"
@click="colorCount = c"
class="py-1.5 rounded-lg text-center font-bold text-xs transition border"
:class="colorCount === c ? 'bg-amber-400 border-amber-500 text-slate-950 font-black shadow-2xs' : 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'"
x-text="c"
></button>
</template>
</div>
</div>
<!-- Materiaalsoort -->
<div class="space-y-1">
<label class="block text-[11px] font-bold text-slate-600">Materiaalsoort</label>
<select x-model="material" class="w-full bg-white border border-slate-200 rounded-xl px-2.5 py-1.5 text-xs font-bold text-slate-900">
<option value="pla">Bio-PLA (1.0x)</option>
<option value="petg">PETG Tough (1.2x)</option>
<option value="silk">Silk Zijdeglans (1.3x)</option>
<option value="tpu">TPU Flexibel (1.4x)</option>
</select>
</div>
</div>
<!-- Winstmarge selectie -->
<div class="flex flex-wrap items-center justify-between gap-2 pt-2 border-t border-amber-200/50 text-xs">
<div class="flex items-center space-x-1.5">
<span class="text-slate-600 font-bold text-[11px]">Winstmarge:</span>
<div class="inline-flex rounded-lg border border-slate-200 bg-white p-0.5 text-[11px] font-bold">
<button type="button" @click="markupPercent = 35" :class="markupPercent === 35 ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-600 hover:text-slate-900'" class="px-2 py-0.5 rounded-md transition">35%</button>
<button type="button" @click="markupPercent = 50" :class="markupPercent === 50 ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-600 hover:text-slate-900'" class="px-2 py-0.5 rounded-md transition">50%</button>
<button type="button" @click="markupPercent = 65" :class="markupPercent === 65 ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-600 hover:text-slate-900'" class="px-2 py-0.5 rounded-md transition">65%</button>
<button type="button" @click="markupPercent = 100" :class="markupPercent === 100 ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-600 hover:text-slate-900'" class="px-2 py-0.5 rounded-md transition">100%</button>
</div>
</div>
<!-- Live Cost Breakdown Strip -->
<div class="flex items-center space-x-3 text-[11px] text-slate-500 font-semibold">
<span>🧵 Materiaal: €<span x-text="materialCost"></span></span>
<span>⚡ Stroom: €<span x-text="electricityCost"></span></span>
<span>⚙️ Slijtage: €<span x-text="machineCost"></span></span>
<span class="font-bold text-slate-700">Kostprijs: €<span x-text="totalProductionCost"></span></span>
</div>
</div>
<!-- Result Banner met 1-klik overneem knop -->
<div class="p-3.5 rounded-xl bg-white border border-amber-300 shadow-2xs flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 rounded-xl bg-amber-100 text-amber-900 flex items-center justify-center font-heading font-black text-sm">
€
</div>
<div>
<span class="text-[10px] text-slate-400 block font-bold uppercase tracking-wider">Berekend Prijsvoorstel (incl. 21% btw)</span>
<div class="flex items-baseline space-x-1.5">
<span class="font-heading text-xl font-black text-slate-900">€ <span x-text="suggestedPrice"></span></span>
<span class="text-[11px] text-slate-500">op basis van <span x-text="weightGrams"></span>g &bull; <span x-text="printTimeHours"></span>u &bull; <span x-text="colorCount"></span> kleur(en)</span>
</div>
</div>
</div>
<button
type="button"
@click="applySuggestedPrice"
class="px-4 py-2 bg-gradient-to-r from-amber-400 to-rose-400 hover:from-amber-300 hover:to-rose-300 text-slate-950 font-black text-xs rounded-xl shadow-xs transition flex items-center justify-center space-x-1.5 cursor-pointer"
>
<i data-lucide="check" class="w-4 h-4"></i>
<span>Neem Adviesprijs Over (€ <span x-text="suggestedPrice"></span>)</span>
</button>
</div>
</div>
</div>
<!-- Prijs & Korte Omschrijving -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Basisprijs (€) *</label> <label class="block text-xs font-bold text-slate-700">Basisprijs (€) *</label>
<input type="number" step="0.01" name="base_price" value="{{ old('base_price', $product->base_price) }}" required class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white font-bold"> <div class="relative">
<span class="absolute left-3.5 top-1/2 -translate-y-1/2 font-bold text-slate-400 text-xs">€</span>
<input
type="number"
step="0.01"
name="base_price"
x-model="basePrice"
required
class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl pl-7 pr-3.5 py-2.5 text-xs text-slate-900 font-bold focus:outline-none focus:ring-2 focus:ring-amber-400/20 focus:border-amber-400"
>
</div>
</div> </div>
<div class="md:col-span-2 space-y-1.5"> <div class="md:col-span-2 space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Korte Omschrijving (voor catalogus cards)</label> <label class="block text-xs font-bold text-slate-700">Korte Omschrijving (voor catalogus cards)</label>
<input type="text" name="short_description" value="{{ old('short_description', $product->short_description) }}" placeholder="Korte samenvatting in 1 zin..." class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white"> <input type="text" name="short_description" value="{{ old('short_description', $product->short_description) }}" placeholder="Korte samenvatting in 1 zin..." class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs text-slate-900">
</div> </div>
</div> </div>
<!-- Visueel Afbeeldingen & Fotogalerij Beheer --> <!-- Visueel Afbeeldingen & Fotogalerij Beheer -->
<div class="pt-5 border-t border-slate-800 space-y-4" <div class="pt-5 border-t border-slate-200/80 space-y-4"
x-data="{ x-data="{
imageUrl: '{{ old('image_url', $product->image_url ?? '') }}', imageUrl: '{{ old('image_url', $product->image_url ?? '') }}',
galleryImages: {{ json_encode(old('gallery_images', $product->gallery_images ?? []), JSON_UNESCAPED_SLASHES) }}, galleryImages: {{ json_encode(old('gallery_images', $product->gallery_images ?? []), JSON_UNESCAPED_SLASHES) }},
@@ -145,7 +342,7 @@
} }
}"> }">
<!-- Verborgen form fields die verstuurd worden naar de server --> <!-- Verborgen form fields -->
<input type="hidden" name="image_url" :value="imageUrl"> <input type="hidden" name="image_url" :value="imageUrl">
<template x-for="(img, idx) in galleryImages" :key="idx"> <template x-for="(img, idx) in galleryImages" :key="idx">
<input type="hidden" name="gallery_images[]" :value="img"> <input type="hidden" name="gallery_images[]" :value="img">
@@ -154,64 +351,64 @@
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-2"> <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
<div> <div>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<i data-lucide="image" class="w-4 h-4 text-amber-400"></i> <i data-lucide="image" class="w-4 h-4 text-amber-500"></i>
<h3 class="font-heading text-sm font-black text-white uppercase tracking-wider">Productafbeeldingen & Fotogalerij</h3> <h3 class="font-heading text-sm font-black text-slate-900 uppercase tracking-wider">Productafbeeldingen & Fotogalerij</h3>
<span class="px-2 py-0.5 rounded-full bg-slate-800 text-amber-400 font-mono text-[11px] font-bold" x-text="(imageUrl ? 1 : 0) + galleryImages.length + ' foto(\'s)'"></span> <span class="px-2 py-0.5 rounded-full bg-slate-100 text-slate-700 font-mono text-[11px] font-bold" x-text="(imageUrl ? 1 : 0) + galleryImages.length + ' foto(\'s)'"></span>
</div> </div>
<p class="text-xs text-slate-400 mt-0.5"> <p class="text-xs text-slate-500 mt-0.5">
Alle foto's die vanuit MakerWorld zijn binnengehaald worden hier visueel getoond. Je kunt foto's toevoegen, wisselen van hoofdfoto of verwijderen. Alle foto's die vanuit MakerWorld zijn binnengehaald worden hier visueel getoond. Je kunt foto's toevoegen, wisselen van hoofdfoto of verwijderen.
</p> </p>
</div> </div>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<!-- Bestand uploaden button --> <!-- Bestand uploaden button -->
<label class="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-700 cursor-pointer"> <label class="px-3 py-1.5 rounded-xl bg-white hover:bg-slate-50 text-slate-700 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200 shadow-2xs cursor-pointer">
<i data-lucide="upload" class="w-3.5 h-3.5 text-amber-400"></i> <i data-lucide="upload" class="w-3.5 h-3.5 text-amber-500"></i>
<span x-text="isUploading ? 'Uploaden...' : 'Foto Uploaden'"></span> <span x-text="isUploading ? 'Uploaden...' : 'Foto Uploaden'"></span>
<input type="file" accept="image/*" class="hidden" @change="uploadFile" :disabled="isUploading"> <input type="file" accept="image/*" class="hidden" @change="uploadFile" :disabled="isUploading">
</label> </label>
<!-- URL toevoegen toggle --> <!-- URL toevoegen toggle -->
<button type="button" @click="showUrlInput = !showUrlInput" class="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-700"> <button type="button" @click="showUrlInput = !showUrlInput" class="px-3 py-1.5 rounded-xl bg-white hover:bg-slate-50 text-slate-700 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200 shadow-2xs">
<i data-lucide="link" class="w-3.5 h-3.5 text-amber-400"></i> <i data-lucide="link" class="w-3.5 h-3.5 text-amber-500"></i>
<span>URL Plakken</span> <span>URL Plakken</span>
</button> </button>
</div> </div>
</div> </div>
<!-- URL Toevoegen Bar (uitklapbaar) --> <!-- URL Toevoegen Bar -->
<div x-show="showUrlInput" x-transition class="p-3 bg-slate-950 rounded-2xl border border-slate-800 space-y-2"> <div x-show="showUrlInput" x-transition class="p-3 bg-slate-50 rounded-2xl border border-slate-200 space-y-2">
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<input <input
type="text" type="text"
x-model="newImageUrl" x-model="newImageUrl"
@keydown.enter.prevent="addImageUrl" @keydown.enter.prevent="addImageUrl"
placeholder="Plak hier een foto-URL (bijv. https://makerworld... of /images/products/...)" placeholder="Plak hier een foto-URL (bijv. https://makerworld... of /images/products/...)"
class="flex-1 bg-slate-900 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-400" class="flex-1 bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-amber-400"
> >
<button type="button" @click="addImageUrl" class="px-4 py-2 bg-amber-400 hover:bg-amber-300 text-slate-950 rounded-xl text-xs font-black transition flex items-center space-x-1"> <button type="button" @click="addImageUrl" class="px-4 py-2 bg-amber-400 hover:bg-amber-300 text-slate-950 rounded-xl text-xs font-black transition flex items-center space-x-1">
<i data-lucide="plus" class="w-3.5 h-3.5"></i> <i data-lucide="plus" class="w-3.5 h-3.5"></i>
<span>Toevoegen</span> <span>Toevoegen</span>
</button> </button>
</div> </div>
<p x-show="addError" x-text="addError" class="text-xs text-rose-400 font-bold"></p> <p x-show="addError" x-text="addError" class="text-xs text-rose-600 font-bold"></p>
</div> </div>
<div x-show="uploadError" x-text="uploadError" class="p-2.5 bg-rose-500/10 border border-rose-500/30 text-xs text-rose-300 rounded-xl font-bold"></div> <div x-show="uploadError" x-text="uploadError" class="p-2.5 bg-rose-50 border border-rose-200 text-xs text-rose-700 rounded-xl font-bold"></div>
<!-- Visuele Weergave: Hoofdafbeelding + Gallerij Grid --> <!-- Visuele Weergave: Hoofdafbeelding + Gallerij Grid -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-4 items-start"> <div class="grid grid-cols-1 md:grid-cols-12 gap-4 items-start">
<!-- Hoofdafbeelding Box (Links) --> <!-- Hoofdafbeelding Box (Links) -->
<div class="md:col-span-5 bg-slate-950 rounded-2xl border-2 border-amber-400/50 p-3 space-y-2 relative overflow-hidden"> <div class="md:col-span-5 bg-slate-50/80 rounded-2xl border-2 border-amber-300 p-3.5 space-y-2 relative overflow-hidden">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="px-2.5 py-1 rounded-full bg-amber-400 text-slate-950 font-black text-[10px] uppercase tracking-wider flex items-center space-x-1 shadow-sm"> <span class="px-2.5 py-1 rounded-full bg-amber-400 text-slate-950 font-black text-[10px] uppercase tracking-wider flex items-center space-x-1 shadow-2xs">
<i data-lucide="star" class="w-3 h-3 fill-current"></i> <i data-lucide="star" class="w-3 h-3 fill-current"></i>
<span>Hoofdafbeelding (Cover)</span> <span>Hoofdafbeelding (Cover)</span>
</span> </span>
<template x-if="imageUrl"> <template x-if="imageUrl">
<button type="button" @click="removeCover" class="text-slate-400 hover:text-rose-400 text-xs font-bold transition flex items-center space-x-1" title="Verwijder hoofdafbeelding"> <button type="button" @click="removeCover" class="text-slate-400 hover:text-rose-600 text-xs font-bold transition flex items-center space-x-1" title="Verwijder hoofdafbeelding">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i> <i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
<span>Verwijderen</span> <span>Verwijderen</span>
</button> </button>
@@ -219,13 +416,13 @@
</div> </div>
<!-- Grote Image Preview --> <!-- Grote Image Preview -->
<div class="aspect-4/3 rounded-xl bg-slate-900 overflow-hidden flex items-center justify-center relative border border-slate-800"> <div class="aspect-4/3 rounded-xl bg-white overflow-hidden flex items-center justify-center relative border border-slate-200 shadow-2xs">
<template x-if="imageUrl"> <template x-if="imageUrl">
<img :src="imageUrl" alt="Hoofdafbeelding" class="w-full h-full object-cover"> <img :src="imageUrl" alt="Hoofdafbeelding" class="w-full h-full object-cover">
</template> </template>
<template x-if="!imageUrl"> <template x-if="!imageUrl">
<div class="text-center p-4 text-slate-500 space-y-1"> <div class="text-center p-4 text-slate-400 space-y-1">
<i data-lucide="image" class="w-8 h-8 mx-auto text-slate-600"></i> <i data-lucide="image" class="w-8 h-8 mx-auto text-slate-300"></i>
<p class="text-xs font-bold">Geen hoofdafbeelding</p> <p class="text-xs font-bold">Geen hoofdafbeelding</p>
<p class="text-[10px]">Kies een foto uit de galerij of voeg er een toe.</p> <p class="text-[10px]">Kies een foto uit de galerij of voeg er een toe.</p>
</div> </div>
@@ -234,37 +431,36 @@
<!-- Huidige Cover URL weergave --> <!-- Huidige Cover URL weergave -->
<div class="pt-1"> <div class="pt-1">
<span class="text-[10px] font-mono text-slate-500 truncate block" x-text="imageUrl || 'Geen URL ingesteld'"></span> <span class="text-[10px] font-mono text-slate-400 truncate block" x-text="imageUrl || 'Geen URL ingesteld'"></span>
</div> </div>
</div> </div>
<!-- Gallerij Foto's (Rechts) --> <!-- Gallerij Foto's (Rechts) -->
<div class="md:col-span-7 bg-slate-950/60 rounded-2xl border border-slate-800 p-3 space-y-3"> <div class="md:col-span-7 bg-slate-50/60 rounded-2xl border border-slate-200 p-3.5 space-y-3">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-xs font-black text-slate-300 uppercase tracking-wider">Extra Gallerij Foto's</span> <span class="text-xs font-black text-slate-700 uppercase tracking-wider">Extra Gallerij Foto's</span>
<span class="text-[10px] text-slate-500 font-bold" x-text="galleryImages.length + ' extra foto(\'s)'"></span> <span class="text-[10px] text-slate-400 font-bold" x-text="galleryImages.length + ' extra foto(\'s)'"></span>
</div> </div>
<!-- Grid van Extra Foto's --> <!-- Grid van Extra Foto's -->
<div class="grid grid-cols-2 sm:grid-cols-3 gap-2.5 max-h-96 overflow-y-auto pr-1"> <div class="grid grid-cols-2 sm:grid-cols-3 gap-2.5 max-h-96 overflow-y-auto pr-1">
<template x-for="(img, index) in galleryImages" :key="index"> <template x-for="(img, index) in galleryImages" :key="index">
<div class="group relative rounded-xl bg-slate-900 border border-slate-800 hover:border-amber-400 transition overflow-hidden"> <div class="group relative rounded-xl bg-white border border-slate-200 hover:border-amber-400 transition overflow-hidden shadow-2xs">
<div class="aspect-square w-full overflow-hidden bg-slate-950"> <div class="aspect-square w-full overflow-hidden bg-slate-100">
<img :src="img" alt="Gallerij foto" class="w-full h-full object-cover group-hover:scale-105 transition duration-200" loading="lazy"> <img :src="img" alt="Gallerij foto" class="w-full h-full object-cover group-hover:scale-105 transition duration-200" loading="lazy">
</div> </div>
<!-- Index Badge --> <!-- Index Badge -->
<div class="absolute top-1.5 left-1.5"> <div class="absolute top-1.5 left-1.5">
<span class="px-1.5 py-0.5 rounded bg-slate-950/80 text-white font-mono text-[9px] font-bold backdrop-blur" x-text="'#' + (index + 1)"></span> <span class="px-1.5 py-0.5 rounded bg-slate-900/80 text-white font-mono text-[9px] font-bold backdrop-blur" x-text="'#' + (index + 1)"></span>
</div> </div>
<!-- Overlay Knoppen (Maak Hoofdfoto & Verwijderen) --> <!-- Overlay Knoppen -->
<div class="absolute inset-0 bg-slate-950/70 backdrop-blur-xs opacity-0 group-hover:opacity-100 transition flex flex-col items-center justify-center gap-1.5 p-2"> <div class="absolute inset-0 bg-slate-900/60 opacity-0 group-hover:opacity-100 transition flex flex-col items-center justify-center gap-1.5 p-2">
<button <button
type="button" type="button"
@click="setAsCover(index)" @click="setAsCover(index)"
class="w-full py-1.5 px-2 bg-amber-400 hover:bg-amber-300 text-slate-950 rounded-lg text-[10px] font-black uppercase tracking-wider transition flex items-center justify-center space-x-1 shadow" class="w-full py-1.5 px-2 bg-amber-400 hover:bg-amber-300 text-slate-950 rounded-lg text-[10px] font-black uppercase tracking-wider transition flex items-center justify-center space-x-1 shadow"
title="Maak deze foto de hoofdafbeelding"
> >
<i data-lucide="star" class="w-3 h-3 fill-current"></i> <i data-lucide="star" class="w-3 h-3 fill-current"></i>
<span>Als Cover</span> <span>Als Cover</span>
@@ -274,7 +470,6 @@
type="button" type="button"
@click="removeGalleryImage(index)" @click="removeGalleryImage(index)"
class="w-full py-1 px-2 bg-rose-600 hover:bg-rose-500 text-white rounded-lg text-[10px] font-bold transition flex items-center justify-center space-x-1" class="w-full py-1 px-2 bg-rose-600 hover:bg-rose-500 text-white rounded-lg text-[10px] font-bold transition flex items-center justify-center space-x-1"
title="Verwijder deze foto uit galerij"
> >
<i data-lucide="trash-2" class="w-3 h-3"></i> <i data-lucide="trash-2" class="w-3 h-3"></i>
<span>Verwijder</span> <span>Verwijder</span>
@@ -284,11 +479,11 @@
</template> </template>
</div> </div>
<!-- Empty state als er geen extra gallerij foto's zijn --> <!-- Empty state -->
<div x-show="galleryImages.length === 0" class="text-center py-8 px-4 rounded-xl border border-dashed border-slate-800 text-slate-500 space-y-1"> <div x-show="galleryImages.length === 0" class="text-center py-8 px-4 rounded-xl border border-dashed border-slate-200 text-slate-400 space-y-1">
<i data-lucide="images" class="w-6 h-6 mx-auto text-slate-600"></i> <i data-lucide="images" class="w-6 h-6 mx-auto text-slate-300"></i>
<p class="text-xs font-bold text-slate-400">Nog geen extra galerijfoto's</p> <p class="text-xs font-bold text-slate-600">Nog geen extra galerijfoto's</p>
<p class="text-[11px] text-slate-500 max-w-xs mx-auto"> <p class="text-[11px] text-slate-400 max-w-xs mx-auto">
Voeg extra detailfoto's of printbedfoto's toe via "URL Plakken" of "Foto Uploaden". Voeg extra detailfoto's of printbedfoto's toe via "URL Plakken" of "Foto Uploaden".
</p> </p>
</div> </div>
@@ -298,38 +493,39 @@
</div> </div>
<!-- Beschrijving -->
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Volledige Productbeschrijving *</label> <label class="block text-xs font-bold text-slate-700">Volledige Productbeschrijving *</label>
<textarea name="description" rows="6" required class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3.5 py-2.5 text-xs text-white leading-relaxed">{{ old('description', $product->description) }}</textarea> <textarea name="description" rows="6" required class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs text-slate-900 leading-relaxed">{{ old('description', $product->description) }}</textarea>
</div> </div>
<!-- Print Specs --> <!-- Print Specs -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 pt-3 border-t border-slate-800"> <div class="grid grid-cols-1 sm:grid-cols-3 gap-4 pt-4 border-t border-slate-200/80">
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Afmetingen (LxBxH)</label> <label class="block text-xs font-bold text-slate-700">Afmetingen (LxBxH)</label>
<input type="text" name="dimensions" value="{{ old('dimensions', $product->dimensions) }}" placeholder="bijv. 12 x 8 x 10 cm" class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white"> <input type="text" name="dimensions" value="{{ old('dimensions', $product->dimensions) }}" placeholder="bijv. 12 x 8 x 10 cm" class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-900">
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Gewicht (gram)</label> <label class="block text-xs font-bold text-slate-700">Gewicht (gram)</label>
<input type="number" name="weight_grams" value="{{ old('weight_grams', $product->weight_grams) }}" class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white"> <input type="number" name="weight_grams" x-model.number="weightGrams" class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-900">
</div> </div>
<div class="space-y-1.5"> <div class="space-y-1.5">
<label class="block text-xs font-bold text-slate-300">Printtijd (uren)</label> <label class="block text-xs font-bold text-slate-700">Printtijd (uren)</label>
<input type="number" step="0.1" name="print_time_hours" value="{{ old('print_time_hours', $product->print_time_hours) }}" class="w-full bg-slate-950 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white"> <input type="number" step="0.1" name="print_time_hours" x-model.number="printTimeHours" class="w-full bg-slate-50/60 focus:bg-white border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-900">
</div> </div>
</div> </div>
<!-- Toggles --> <!-- Toggles -->
<div class="pt-3 border-t border-slate-800 flex items-center space-x-6"> <div class="pt-4 border-t border-slate-200/80 flex items-center space-x-6">
<label class="flex items-center space-x-2 cursor-pointer text-xs font-bold text-slate-200"> <label class="flex items-center space-x-2 cursor-pointer text-xs font-bold text-slate-700">
<input type="checkbox" name="is_active" value="1" {{ old('is_active', $product->is_active) ? 'checked' : '' }} class="w-4 h-4 rounded text-amber-400 bg-slate-950 border-slate-700"> <input type="checkbox" name="is_active" value="1" {{ old('is_active', $product->is_active) ? 'checked' : '' }} class="w-4 h-4 rounded text-amber-500 bg-white border-slate-300">
<span>Zichtbaar in webshop (Actief)</span> <span>Zichtbaar in webshop (Actief)</span>
</label> </label>
<label class="flex items-center space-x-2 cursor-pointer text-xs font-bold text-slate-200"> <label class="flex items-center space-x-2 cursor-pointer text-xs font-bold text-slate-700">
<input type="checkbox" name="is_featured" value="1" {{ old('is_featured', $product->is_featured) ? 'checked' : '' }} class="w-4 h-4 rounded text-amber-400 bg-slate-950 border-slate-700"> <input type="checkbox" name="is_featured" value="1" {{ old('is_featured', $product->is_featured) ? 'checked' : '' }} class="w-4 h-4 rounded text-amber-500 bg-white border-slate-300">
<span>Uitlichten op homepage</span> <span>Uitlichten op homepage</span>
</label> </label>
</div> </div>
@@ -337,10 +533,10 @@
</div> </div>
<div class="flex items-center justify-end space-x-3"> <div class="flex items-center justify-end space-x-3">
<a href="{{ route('admin.products.index') }}" class="px-4 py-2.5 rounded-xl bg-slate-800 text-slate-300 hover:text-white text-xs font-bold transition"> <a href="{{ route('admin.products.index') }}" class="px-4 py-2.5 rounded-xl bg-white hover:bg-slate-50 text-slate-600 text-xs font-bold transition border border-slate-200">
Annuleren Annuleren
</a> </a>
<button type="submit" class="px-6 py-2.5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs transition flex items-center space-x-1.5"> <button type="submit" class="px-6 py-2.5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs transition flex items-center space-x-1.5 shadow-xs cursor-pointer">
<i data-lucide="save" class="w-4 h-4"></i> <i data-lucide="save" class="w-4 h-4"></i>
<span>Wijzigingen Opslaan</span> <span>Wijzigingen Opslaan</span>
</button> </button>
@@ -349,4 +545,3 @@
</form> </form>
</div> </div>
@endsection @endsection
+62 -63
View File
@@ -6,19 +6,19 @@
<div class="space-y-6"> <div class="space-y-6">
<!-- Header --> <!-- Header -->
<div class="flex flex-col sm:flex-row sm:items-center justify-between pb-6 border-b border-slate-800 gap-4"> <div class="flex flex-col sm:flex-row sm:items-center justify-between pb-6 border-b border-slate-200/80 gap-4">
<div> <div>
<div class="inline-flex items-center space-x-2 text-xs text-amber-400 font-bold uppercase tracking-wider mb-1"> <div class="inline-flex items-center space-x-1.5 text-xs text-amber-700 font-bold uppercase tracking-wider mb-1">
<i data-lucide="box" class="w-4 h-4"></i> <i data-lucide="box" class="w-4 h-4"></i>
<span>Assortiment & Voorraad</span> <span>Assortiment & Voorraad</span>
</div> </div>
<h1 class="font-heading text-2xl sm:text-3xl font-black text-white">Producten Overzicht ({{ $products->total() }})</h1> <h1 class="font-heading text-2xl sm:text-3xl font-black text-slate-900">Producten Overzicht ({{ $products->total() }})</h1>
<p class="text-xs sm:text-sm text-slate-400 mt-1">Beheer prijzen, teksten, specificaties en afbeeldingen van alle 3D prints.</p> <p class="text-xs sm:text-sm text-slate-500 mt-1">Beheer prijzen, teksten, kostprijscalculaties en afbeeldingen van alle 3D prints.</p>
</div> </div>
<div class="flex items-center space-x-3 self-start sm:self-auto"> <div class="flex items-center space-x-2.5 self-start sm:self-auto">
<a href="{{ route('admin.makerworld.index') }}" class="px-3.5 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white text-xs font-bold transition flex items-center space-x-1.5 border border-slate-700"> <a href="{{ route('admin.makerworld.index') }}" class="px-3.5 py-2 rounded-xl bg-white hover:bg-slate-50 text-slate-700 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200 shadow-sm">
<i data-lucide="download-cloud" class="w-4 h-4"></i> <i data-lucide="download-cloud" class="w-4 h-4 text-slate-500"></i>
<span>MakerWorld Importer</span> <span>MakerWorld Importer</span>
</a> </a>
<a href="{{ route('admin.products.create') }}" class="px-4 py-2 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs transition flex items-center space-x-1.5 shadow-sm"> <a href="{{ route('admin.products.create') }}" class="px-4 py-2 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs transition flex items-center space-x-1.5 shadow-sm">
@@ -29,36 +29,36 @@
</div> </div>
<!-- Status Filter Tabs --> <!-- Status Filter Tabs -->
<div class="flex items-center space-x-2 border-b border-slate-800 pb-2 overflow-x-auto text-xs font-bold"> <div class="flex items-center space-x-2 border-b border-slate-200/80 pb-2 overflow-x-auto text-xs font-bold">
<a href="{{ route('admin.products.index', array_merge(request()->except('tab', 'page'), ['tab' => 'all'])) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'all') === 'all' ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}"> <a href="{{ route('admin.products.index', array_merge(request()->except('tab', 'page'), ['tab' => 'all'])) }}" class="px-3.5 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'all') === 'all' ? 'bg-amber-400 text-slate-950 font-black shadow-sm' : 'text-slate-600 hover:text-slate-900 hover:bg-white' }}">
<span>Alle Producten</span> <span>Alle Producten</span>
<span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'all') === 'all' ? 'bg-slate-950 text-amber-400 font-black' : 'bg-slate-800 text-slate-400' }}"> <span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'all') === 'all' ? 'bg-slate-950 text-amber-400 font-black' : 'bg-slate-100 text-slate-600' }}">
{{ $totalAll ?? $products->total() }} {{ $totalAll ?? $products->total() }}
</span> </span>
</a> </a>
<a href="{{ route('admin.products.index', array_merge(request()->except('tab', 'page'), ['tab' => 'published'])) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'all') === 'published' ? 'bg-emerald-500 text-white font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}"> <a href="{{ route('admin.products.index', array_merge(request()->except('tab', 'page'), ['tab' => 'published'])) }}" class="px-3.5 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'all') === 'published' ? 'bg-emerald-600 text-white font-black shadow-sm' : 'text-slate-600 hover:text-slate-900 hover:bg-white' }}">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400"></span> <span class="w-1.5 h-1.5 rounded-full {{ ($tab ?? 'all') === 'published' ? 'bg-emerald-200' : 'bg-emerald-500' }}"></span>
<span>Live in Webshop</span> <span>Live in Webshop</span>
<span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'all') === 'published' ? 'bg-slate-950 text-emerald-400 font-black' : 'bg-slate-800 text-slate-400' }}"> <span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'all') === 'published' ? 'bg-emerald-800 text-emerald-200 font-black' : 'bg-slate-100 text-slate-600' }}">
{{ $totalPublished ?? 0 }} {{ $totalPublished ?? 0 }}
</span> </span>
</a> </a>
<a href="{{ route('admin.products.index', array_merge(request()->except('tab', 'page'), ['tab' => 'draft'])) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'all') === 'draft' ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}"> <a href="{{ route('admin.products.index', array_merge(request()->except('tab', 'page'), ['tab' => 'draft'])) }}" class="px-3.5 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'all') === 'draft' ? 'bg-amber-400 text-slate-950 font-black shadow-sm' : 'text-slate-600 hover:text-slate-900 hover:bg-white' }}">
<i data-lucide="layers" class="w-3.5 h-3.5"></i> <i data-lucide="layers" class="w-3.5 h-3.5"></i>
<span>Concepten / MakerWorld Staging</span> <span>Concepten / Staging</span>
@if(($totalDraft ?? 0) > 0) @if(($totalDraft ?? 0) > 0)
<span class="px-1.5 py-0.5 rounded text-[10px] bg-amber-400 text-slate-950 font-black"> <span class="px-1.5 py-0.5 rounded text-[10px] bg-amber-500 text-slate-950 font-black">
{{ $totalDraft }} {{ $totalDraft }}
</span> </span>
@endif @endif
</a> </a>
<a href="{{ route('admin.products.index', array_merge(request()->except('tab', 'page'), ['tab' => 'makerworld'])) }}" class="px-4 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'all') === 'makerworld' ? 'bg-indigo-600 text-white font-black' : 'text-slate-400 hover:text-white hover:bg-slate-900' }}"> <a href="{{ route('admin.products.index', array_merge(request()->except('tab', 'page'), ['tab' => 'makerworld'])) }}" class="px-3.5 py-2 rounded-xl transition flex items-center space-x-1.5 {{ ($tab ?? 'all') === 'makerworld' ? 'bg-indigo-600 text-white font-black shadow-sm' : 'text-slate-600 hover:text-slate-900 hover:bg-white' }}">
<i data-lucide="download-cloud" class="w-3.5 h-3.5"></i> <i data-lucide="download-cloud" class="w-3.5 h-3.5"></i>
<span>MakerWorld Modellen</span> <span>MakerWorld Modellen</span>
<span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'all') === 'makerworld' ? 'bg-slate-950 text-indigo-300 font-black' : 'bg-slate-800 text-slate-400' }}"> <span class="px-1.5 py-0.5 rounded text-[10px] {{ ($tab ?? 'all') === 'makerworld' ? 'bg-indigo-800 text-indigo-200 font-black' : 'bg-slate-100 text-slate-600' }}">
{{ $totalMakerWorld ?? 0 }} {{ $totalMakerWorld ?? 0 }}
</span> </span>
</a> </a>
@@ -74,7 +74,7 @@
</div> </div>
<!-- Search & Filters --> <!-- Search & Filters -->
<div class="p-4 rounded-2xl bg-slate-900 border border-slate-800 flex flex-col sm:flex-row items-center justify-between gap-3"> <div class="p-3.5 rounded-2xl bg-white border border-slate-200/80 shadow-sm flex flex-col sm:flex-row items-center justify-between gap-3">
<form action="{{ route('admin.products.index') }}" method="GET" class="w-full sm:w-auto flex-1 flex items-center space-x-2"> <form action="{{ route('admin.products.index') }}" method="GET" class="w-full sm:w-auto flex-1 flex items-center space-x-2">
<div class="relative flex-1 max-w-md"> <div class="relative flex-1 max-w-md">
<input <input
@@ -82,130 +82,130 @@
name="q" name="q"
value="{{ request('q') }}" value="{{ request('q') }}"
placeholder="Zoek op naam of omschrijving..." placeholder="Zoek op naam of omschrijving..."
class="w-full bg-slate-950 border border-slate-700 rounded-xl pl-9 pr-3.5 py-2 text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-400" class="w-full bg-slate-50 border border-slate-200 rounded-xl pl-9 pr-3.5 py-2 text-xs text-slate-800 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-amber-400 focus:bg-white transition"
> >
<i data-lucide="search" class="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2"></i> <i data-lucide="search" class="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2"></i>
</div> </div>
<select name="category_id" onchange="this.form.submit()" class="bg-slate-950 border border-slate-700 rounded-xl px-3 py-2 text-xs text-white"> <select name="category_id" onchange="this.form.submit()" class="bg-slate-50 border border-slate-200 rounded-xl px-3 py-2 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-amber-400">
<option value="">Alle Categorieën</option> <option value="">Alle Categorieën</option>
@foreach($categories as $cat) @foreach($categories as $cat)
<option value="{{ $cat->id }}" {{ request('category_id') == $cat->id ? 'selected' : '' }}>{{ $cat->name }}</option> <option value="{{ $cat->id }}" {{ request('category_id') == $cat->id ? 'selected' : '' }}>{{ $cat->name }}</option>
@endforeach @endforeach
</select> </select>
<button type="submit" class="px-3 py-2 bg-slate-800 hover:bg-slate-700 text-white rounded-xl text-xs font-bold transition"> <button type="submit" class="px-3.5 py-2 bg-slate-900 hover:bg-slate-800 text-white rounded-xl text-xs font-bold transition">
Filter Filter
</button> </button>
@if(request()->hasAny(['q', 'category_id'])) @if(request()->hasAny(['q', 'category_id']))
<a href="{{ route('admin.products.index') }}" class="text-xs text-rose-400 hover:underline">Wissen</a> <a href="{{ route('admin.products.index') }}" class="text-xs text-rose-600 hover:underline">Wissen</a>
@endif @endif
</form> </form>
</div> </div>
<!-- Products Table --> <!-- Products Table -->
<div class="rounded-3xl bg-slate-900 border border-slate-800 overflow-hidden shadow-xl"> <div class="rounded-2xl bg-white border border-slate-200/80 overflow-hidden shadow-sm">
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="w-full text-left text-xs text-slate-300"> <table class="w-full text-left text-xs text-slate-600">
<thead class="bg-slate-950/80 text-[11px] uppercase tracking-wider text-slate-400 font-bold border-b border-slate-800"> <thead class="bg-slate-50/80 text-[11px] uppercase tracking-wider text-slate-500 font-bold border-b border-slate-200">
<tr> <tr>
<th class="py-3.5 px-4">Product</th> <th class="py-3 px-4">Product</th>
<th class="py-3.5 px-4">Categorie</th> <th class="py-3 px-4">Categorie</th>
<th class="py-3.5 px-4">Basisprijs</th> <th class="py-3 px-4">Prijs</th>
<th class="py-3.5 px-4">Specificaties</th> <th class="py-3 px-4">Specificaties</th>
<th class="py-3.5 px-4">Status</th> <th class="py-3 px-4">Status</th>
<th class="py-3.5 px-4 text-right">Acties</th> <th class="py-3 px-4 text-right">Acties</th>
</tr> </tr>
</thead> </thead>
<tbody class="divide-y divide-slate-800/80 font-medium"> <tbody class="divide-y divide-slate-100 font-medium">
@forelse($products as $product) @forelse($products as $product)
<tr class="hover:bg-slate-800/50 transition"> <tr class="hover:bg-slate-50/70 transition">
<td class="py-3.5 px-4"> <td class="py-3 px-4">
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<div class="w-12 h-12 rounded-xl bg-slate-950 overflow-hidden border border-slate-800 flex-shrink-0 relative group"> <div class="w-12 h-12 rounded-xl bg-slate-100 overflow-hidden border border-slate-200 flex-shrink-0 relative group shadow-xs">
<img src="{{ $product->image_url }}" alt="{{ $product->name }}" class="w-full h-full object-cover"> <img src="{{ $product->image_url }}" alt="{{ $product->name }}" class="w-full h-full object-cover">
@if(!empty($product->gallery_images) && count($product->gallery_images) > 0) @if(!empty($product->gallery_images) && count($product->gallery_images) > 0)
<span class="absolute bottom-0 right-0 px-1 py-0.5 rounded-tl-md bg-slate-950/90 text-amber-400 font-mono text-[9px] font-black border-t border-l border-slate-800" title="{{ count($product->gallery_images) + 1 }} foto's in galerij"> <span class="absolute bottom-0 right-0 px-1 py-0.5 rounded-tl-md bg-white/95 text-slate-700 font-mono text-[9px] font-bold border-t border-l border-slate-200 shadow-xs" title="{{ count($product->gallery_images) + 1 }} foto's in galerij">
+{{ count($product->gallery_images) }} +{{ count($product->gallery_images) }}
</span> </span>
@endif @endif
</div> </div>
<div> <div>
<a href="{{ route('admin.products.edit', $product) }}" class="font-bold text-white hover:text-amber-400 transition block text-sm"> <a href="{{ route('admin.products.edit', $product) }}" class="font-bold text-slate-900 hover:text-amber-600 transition block text-sm">
{{ $product->name }} {{ $product->name }}
</a> </a>
<span class="text-[10px] text-slate-500 font-mono">/product/{{ $product->slug }}</span> <span class="text-[10px] text-slate-400 font-mono">/product/{{ $product->slug }}</span>
</div> </div>
</div> </div>
</td> </td>
<td class="py-3.5 px-4"> <td class="py-3 px-4">
@if($product->category) @if($product->category)
<span class="px-2 py-0.5 rounded-md bg-slate-800 text-slate-300 text-[11px] font-bold border border-slate-700"> <span class="px-2 py-0.5 rounded-md bg-slate-100 text-slate-700 text-[11px] font-bold border border-slate-200/80">
{{ $product->category->name }} {{ $product->category->name }}
</span> </span>
@else @else
<span class="text-slate-500">-</span> <span class="text-slate-400">-</span>
@endif @endif
</td> </td>
<td class="py-3.5 px-4"> <td class="py-3 px-4">
<span class="font-black text-amber-400 font-heading text-sm">{{ $product->formatted_price }}</span> <span class="font-black text-slate-900 font-heading text-sm">{{ $product->formatted_price }}</span>
</td> </td>
<td class="py-3.5 px-4 text-[11px] text-slate-400 space-y-0.5"> <td class="py-3 px-4 text-[11px] text-slate-500 space-y-0.5">
<div>{{ $product->dimensions ?? 'Standaard' }}</div> <div>{{ $product->dimensions ?? 'Standaard' }}</div>
<div>~{{ $product->weight_grams ?? 0 }}g &bull; {{ $product->print_time_hours ?? 0 }}u</div> <div>~{{ $product->weight_grams ?? 0 }}g &bull; {{ $product->print_time_hours ?? 0 }}u</div>
</td> </td>
<td class="py-3.5 px-4"> <td class="py-3 px-4">
<div class="flex flex-col gap-1 items-start"> <div class="flex flex-col gap-1 items-start">
@if($product->status === 'draft') @if($product->status === 'draft')
<span class="inline-flex items-center space-x-1 px-2 py-0.5 rounded-md bg-amber-400/20 text-amber-300 border border-amber-400/40 text-[10px] font-black uppercase tracking-wider"> <span class="inline-flex items-center space-x-1 px-2 py-0.5 rounded-md bg-amber-50 text-amber-800 border border-amber-200 text-[10px] font-bold uppercase tracking-wider">
<i data-lucide="layers" class="w-3 h-3"></i> <i data-lucide="layers" class="w-3 h-3 text-amber-600"></i>
<span>Concept Staging</span> <span>Concept Staging</span>
</span> </span>
<a href="{{ route('admin.makerworld.staging') }}" class="text-[10px] text-amber-400 hover:underline font-bold"> <a href="{{ route('admin.makerworld.staging') }}" class="text-[10px] text-amber-700 hover:underline font-bold">
Beoordeel in Artikel Beheer &rarr; Beoordeel in Artikel Beheer &rarr;
</a> </a>
@elseif($product->is_active) @elseif($product->is_active)
<span class="inline-flex items-center space-x-1 text-emerald-400 text-[11px] font-bold"> <span class="inline-flex items-center space-x-1 text-emerald-700 text-[11px] font-bold bg-emerald-50 px-2 py-0.5 rounded-md border border-emerald-200/60">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400"></span> <span class="w-1.5 h-1.5 rounded-full bg-emerald-500"></span>
<span>Live in Webshop</span> <span>Live in Webshop</span>
</span> </span>
@else @else
<span class="inline-flex items-center space-x-1 text-slate-500 text-[11px] font-bold"> <span class="inline-flex items-center space-x-1 text-slate-500 text-[11px] font-bold bg-slate-100 px-2 py-0.5 rounded-md border border-slate-200">
<span class="w-1.5 h-1.5 rounded-full bg-slate-500"></span> <span class="w-1.5 h-1.5 rounded-full bg-slate-400"></span>
<span>Verborgen</span> <span>Verborgen</span>
</span> </span>
@endif @endif
@if($product->makerworld_id) @if($product->makerworld_id)
<span class="text-[10px] text-indigo-400 font-mono"> <span class="text-[10px] text-indigo-600 font-mono font-medium">
MakerWorld #{{ $product->makerworld_id }} MakerWorld #{{ $product->makerworld_id }}
</span> </span>
@endif @endif
@if($product->is_featured) @if($product->is_featured)
<span class="inline-flex items-center text-amber-400 text-[10px] font-bold"> <span class="inline-flex items-center text-amber-600 text-[10px] font-bold">
★ Uitgelicht ★ Uitgelicht
</span> </span>
@endif @endif
</div> </div>
</td> </td>
<td class="py-3.5 px-4 text-right"> <td class="py-3 px-4 text-right">
<div class="flex items-center justify-end space-x-2"> <div class="flex items-center justify-end space-x-1.5">
<a href="{{ route('product.show', $product->slug) }}" target="_blank" class="p-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white transition" title="Bekijk in Shop"> <a href="{{ route('product.show', $product->slug) }}" target="_blank" class="p-1.5 rounded-lg bg-slate-100 hover:bg-slate-200 text-slate-600 hover:text-slate-900 transition" title="Bekijk in Shop">
<i data-lucide="external-link" class="w-3.5 h-3.5"></i> <i data-lucide="external-link" class="w-3.5 h-3.5"></i>
</a> </a>
<a href="{{ route('admin.products.edit', $product) }}" class="p-1.5 rounded-lg bg-slate-800 hover:bg-amber-400 text-slate-300 hover:text-slate-950 transition" title="Bewerken"> <a href="{{ route('admin.products.edit', $product) }}" class="p-1.5 rounded-lg bg-slate-100 hover:bg-amber-100 text-slate-600 hover:text-amber-800 transition" title="Bewerken & Prijscalculator">
<i data-lucide="pencil" class="w-3.5 h-3.5"></i> <i data-lucide="pencil" class="w-3.5 h-3.5"></i>
</a> </a>
<form action="{{ route('admin.products.destroy', $product) }}" method="POST" onsubmit="return confirm('Weet je zeker dat je dit product wilt verwijderen?')"> <form action="{{ route('admin.products.destroy', $product) }}" method="POST" onsubmit="return confirm('Weet je zeker dat je dit product wilt verwijderen?')">
@csrf @csrf
@method('DELETE') @method('DELETE')
<button type="submit" class="p-1.5 rounded-lg bg-slate-800 hover:bg-rose-500 text-slate-300 hover:text-white transition cursor-pointer" title="Verwijderen"> <button type="submit" class="p-1.5 rounded-lg bg-slate-100 hover:bg-rose-100 text-slate-600 hover:text-rose-700 transition cursor-pointer" title="Verwijderen">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i> <i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
</button> </button>
</form> </form>
@@ -214,7 +214,7 @@
</tr> </tr>
@empty @empty
<tr> <tr>
<td colspan="6" class="p-8 text-center text-slate-500"> <td colspan="6" class="p-8 text-center text-slate-400">
Geen producten gevonden. Geen producten gevonden.
</td> </td>
</tr> </tr>
@@ -224,7 +224,7 @@
</div> </div>
@if($products->hasPages()) @if($products->hasPages())
<div class="p-4 border-t border-slate-800 bg-slate-950/60"> <div class="p-4 border-t border-slate-200/80 bg-slate-50/50">
{{ $products->links() }} {{ $products->links() }}
</div> </div>
@endif @endif
@@ -232,4 +232,3 @@
</div> </div>
@endsection @endsection
+41 -41
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="nl" class="h-full bg-slate-900 text-slate-100 antialiased"> <html lang="nl" class="h-full bg-slate-50 text-slate-800 antialiased">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -18,28 +18,28 @@
h1, h2, h3, h4, .font-heading { font-family: 'Fredoka', ui-rounded, 'Nunito', sans-serif; } h1, h2, h3, h4, .font-heading { font-family: 'Fredoka', ui-rounded, 'Nunito', sans-serif; }
</style> </style>
</head> </head>
<body class="min-h-full flex flex-col bg-slate-950 text-slate-100" x-data="{ mobileNav: false }"> <body class="min-h-full flex flex-col bg-slate-50 text-slate-800 selection:bg-amber-200 selection:text-slate-900" x-data="{ mobileNav: false }">
<!-- Admin Header --> <!-- Admin Header (Clean, Light & Modern) -->
<header class="bg-slate-900/90 backdrop-blur-md border-b border-slate-800 sticky top-0 z-30"> <header class="bg-white/95 backdrop-blur-md border-b border-slate-200/80 sticky top-0 z-30 shadow-2xs">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16"> <div class="flex items-center justify-between h-16">
<!-- Brand / Logo --> <!-- Brand / Logo -->
<div class="flex items-center space-x-6"> <div class="flex items-center space-x-6">
<a href="{{ route('admin.content.index') }}" class="flex items-center space-x-3 group"> <a href="{{ route('admin.content.index') }}" class="flex items-center space-x-2.5 group">
<img src="/images/logo.svg" alt="Jouwdriedee.nl" class="h-8 w-auto brightness-200 invert"> <img src="/images/logo.svg" alt="Jouwdriedee.nl" class="h-8 w-auto">
<span class="px-2 py-0.5 rounded-md bg-amber-400 text-slate-950 font-black text-[10px] uppercase tracking-wider">Beheer</span> <span class="px-2 py-0.5 rounded-full bg-amber-50 text-amber-800 border border-amber-200/60 font-bold text-[10px] uppercase tracking-wider">Beheer</span>
</a> </a>
<!-- Desktop Nav Links --> <!-- Desktop Nav Links -->
<nav class="hidden md:flex items-center space-x-1 text-xs font-bold"> <nav class="hidden md:flex items-center space-x-1 text-xs font-bold">
<a href="{{ route('admin.content.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.content.*') ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-300 hover:text-white hover:bg-slate-800' }}"> <a href="{{ route('admin.content.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.content.*') ? 'bg-amber-100 text-amber-950 font-black shadow-2xs' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
<i data-lucide="palette" class="w-4 h-4"></i> <i data-lucide="palette" class="w-4 h-4"></i>
<span>Layout & Teksten</span> <span>Layout & Teksten</span>
</a> </a>
<a href="{{ route('admin.products.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.products.*') ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-300 hover:text-white hover:bg-slate-800' }}"> <a href="{{ route('admin.products.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.products.*') ? 'bg-amber-100 text-amber-950 font-black shadow-2xs' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
<i data-lucide="box" class="w-4 h-4"></i> <i data-lucide="box" class="w-4 h-4"></i>
<span>Producten</span> <span>Producten</span>
</a> </a>
@@ -47,27 +47,27 @@
@php @php
$draftBadgeCount = \App\Models\Product::where('status', 'draft')->count(); $draftBadgeCount = \App\Models\Product::where('status', 'draft')->count();
@endphp @endphp
<a href="{{ route('admin.makerworld.staging') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.makerworld.staging*') ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-300 hover:text-white hover:bg-slate-800' }}"> <a href="{{ route('admin.makerworld.staging') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.makerworld.staging*') ? 'bg-amber-100 text-amber-950 font-black shadow-2xs' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
<i data-lucide="layers" class="w-4 h-4"></i> <i data-lucide="layers" class="w-4 h-4"></i>
<span>Artikel Beheer</span> <span>Artikel Beheer</span>
@if($draftBadgeCount > 0) @if($draftBadgeCount > 0)
<span class="ml-1 px-1.5 py-0.2 rounded-full text-[10px] font-black {{ request()->routeIs('admin.makerworld.staging*') ? 'bg-slate-950 text-amber-400' : 'bg-amber-400 text-slate-950' }}"> <span class="ml-1 px-1.5 py-0.2 rounded-full text-[10px] font-black {{ request()->routeIs('admin.makerworld.staging*') ? 'bg-white text-amber-900' : 'bg-amber-200 text-amber-950' }}">
{{ $draftBadgeCount }} {{ $draftBadgeCount }}
</span> </span>
@endif @endif
</a> </a>
<a href="{{ route('admin.categories.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.categories.*') ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-300 hover:text-white hover:bg-slate-800' }}"> <a href="{{ route('admin.categories.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.categories.*') ? 'bg-amber-100 text-amber-950 font-black shadow-2xs' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
<i data-lucide="folder-tree" class="w-4 h-4"></i> <i data-lucide="folder-tree" class="w-4 h-4"></i>
<span>Categorieën</span> <span>Categorieën</span>
</a> </a>
<a href="{{ route('admin.orders.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.orders.*') ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-300 hover:text-white hover:bg-slate-800' }}"> <a href="{{ route('admin.orders.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.orders.*') ? 'bg-amber-100 text-amber-950 font-black shadow-2xs' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
<i data-lucide="shopping-cart" class="w-4 h-4"></i> <i data-lucide="shopping-cart" class="w-4 h-4"></i>
<span>Bestellingen</span> <span>Bestellingen</span>
</a> </a>
<a href="{{ route('admin.makerworld.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.makerworld.index*') ? 'bg-amber-400 text-slate-950 font-black' : 'text-slate-300 hover:text-white hover:bg-slate-800' }}"> <a href="{{ route('admin.makerworld.index') }}" class="px-3 py-2 rounded-xl transition flex items-center space-x-1.5 {{ request()->routeIs('admin.makerworld.index*') ? 'bg-amber-100 text-amber-950 font-black shadow-2xs' : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100' }}">
<i data-lucide="download-cloud" class="w-4 h-4"></i> <i data-lucide="download-cloud" class="w-4 h-4"></i>
<span>MakerWorld Sync</span> <span>MakerWorld Sync</span>
</a> </a>
@@ -75,7 +75,7 @@
</div> </div>
<!-- Right Actions --> <!-- Right Actions -->
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-2.5">
@php @php
$isShopOffline = \App\Models\Setting::get('shop_offline', '0') === '1'; $isShopOffline = \App\Models\Setting::get('shop_offline', '0') === '1';
@endphp @endphp
@@ -84,26 +84,26 @@
<form action="{{ route('admin.toggle-offline') }}" method="POST" class="inline"> <form action="{{ route('admin.toggle-offline') }}" method="POST" class="inline">
@csrf @csrf
@if($isShopOffline) @if($isShopOffline)
<button type="submit" class="px-3 py-1.5 rounded-xl bg-rose-600 hover:bg-rose-500 text-white text-xs font-black transition flex items-center space-x-1.5 shadow-lg shadow-rose-600/30 animate-pulse cursor-pointer" title="Klik om de webshop weer direct online te zetten"> <button type="submit" class="px-3 py-1.5 rounded-xl bg-rose-500 hover:bg-rose-600 text-white text-xs font-black transition flex items-center space-x-1.5 shadow-sm shadow-rose-500/20 animate-pulse cursor-pointer" title="Klik om de webshop weer direct online te zetten">
<span class="w-2 h-2 rounded-full bg-white"></span> <span class="w-2 h-2 rounded-full bg-white"></span>
<span>Webshop: OFFLINE</span> <span>Webshop: OFFLINE</span>
</button> </button>
@else @else
<button type="submit" class="px-2.5 sm:px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-rose-950/40 text-slate-300 hover:text-rose-400 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-700 cursor-pointer" title="Klik om de webshop direct offline te zetten voor bezoekers"> <button type="submit" class="px-2.5 sm:px-3 py-1.5 rounded-xl bg-white hover:bg-slate-50 text-slate-700 hover:text-rose-600 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200/90 shadow-2xs cursor-pointer" title="Klik om de webshop direct offline te zetten voor bezoekers">
<span class="w-2 h-2 rounded-full bg-emerald-400"></span> <span class="w-2 h-2 rounded-full bg-emerald-500"></span>
<span class="hidden sm:inline">Webshop: Online</span> <span class="hidden sm:inline">Webshop: Online</span>
<span class="text-[10px] text-slate-500 hover:text-rose-300">(Zet Offline)</span> <span class="text-[10px] text-slate-400 hover:text-rose-500">(Zet Offline)</span>
</button> </button>
@endif @endif
</form> </form>
<a href="{{ route('home') }}" target="_blank" class="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-700"> <a href="{{ route('home') }}" target="_blank" class="px-3 py-1.5 rounded-xl bg-white hover:bg-slate-50 text-slate-700 hover:text-slate-900 text-xs font-bold transition flex items-center space-x-1.5 border border-slate-200/90 shadow-2xs">
<span>Naar Webshop</span> <span>Naar Webshop</span>
<i data-lucide="external-link" class="w-3.5 h-3.5"></i> <i data-lucide="external-link" class="w-3.5 h-3.5 text-slate-400"></i>
</a> </a>
<!-- Mobile trigger --> <!-- Mobile trigger -->
<button @click="mobileNav = !mobileNav" type="button" class="md:hidden p-2 rounded-xl bg-slate-800 text-slate-200 hover:bg-slate-700"> <button @click="mobileNav = !mobileNav" type="button" class="md:hidden p-2 rounded-xl bg-white text-slate-700 hover:bg-slate-50 border border-slate-200 shadow-2xs">
<i data-lucide="menu" class="w-5 h-5"></i> <i data-lucide="menu" class="w-5 h-5"></i>
</button> </button>
</div> </div>
@@ -112,28 +112,28 @@
</div> </div>
<!-- Mobile Drawer --> <!-- Mobile Drawer -->
<div x-show="mobileNav" x-cloak class="md:hidden border-t border-slate-800 bg-slate-900 px-4 py-3 space-y-1 text-sm font-bold"> <div x-show="mobileNav" x-cloak class="md:hidden border-t border-slate-200 bg-white px-4 py-3 space-y-1 text-sm font-bold shadow-lg">
<a href="{{ route('admin.content.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.content.*') ? 'bg-amber-400 text-slate-950' : 'text-slate-300' }}"> <a href="{{ route('admin.content.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.content.*') ? 'bg-amber-100 text-amber-950 font-black' : 'text-slate-700 hover:bg-slate-50' }}">
🎨 Layout & Teksten 🎨 Layout & Teksten
</a> </a>
<a href="{{ route('admin.products.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.products.*') ? 'bg-amber-400 text-slate-950' : 'text-slate-300' }}"> <a href="{{ route('admin.products.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.products.*') ? 'bg-amber-100 text-amber-950 font-black' : 'text-slate-700 hover:bg-slate-50' }}">
📦 Producten 📦 Producten
</a> </a>
<a href="{{ route('admin.makerworld.staging') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.makerworld.staging*') ? 'bg-amber-400 text-slate-950' : 'text-slate-300' }}"> <a href="{{ route('admin.makerworld.staging') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.makerworld.staging*') ? 'bg-amber-100 text-amber-950 font-black' : 'text-slate-700 hover:bg-slate-50' }}">
⚡ Artikel Beheer (Geïmporteerd) ⚡ Artikel Beheer (Geïmporteerd)
@if(($draftBadgeCount ?? 0) > 0) @if(($draftBadgeCount ?? 0) > 0)
<span class="ml-2 px-1.5 py-0.5 rounded-full text-xs font-black bg-amber-400 text-slate-950"> <span class="ml-2 px-1.5 py-0.5 rounded-full text-xs font-black bg-amber-200 text-amber-950">
{{ $draftBadgeCount }} nieuw {{ $draftBadgeCount }} nieuw
</span> </span>
@endif @endif
</a> </a>
<a href="{{ route('admin.categories.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.categories.*') ? 'bg-amber-400 text-slate-950' : 'text-slate-300' }}"> <a href="{{ route('admin.categories.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.categories.*') ? 'bg-amber-100 text-amber-950 font-black' : 'text-slate-700 hover:bg-slate-50' }}">
📁 Categorieën 📁 Categorieën
</a> </a>
<a href="{{ route('admin.orders.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.orders.*') ? 'bg-amber-400 text-slate-950' : 'text-slate-300' }}"> <a href="{{ route('admin.orders.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.orders.*') ? 'bg-amber-100 text-amber-950 font-black' : 'text-slate-700 hover:bg-slate-50' }}">
🛒 Bestellingen 🛒 Bestellingen
</a> </a>
<a href="{{ route('admin.makerworld.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.makerworld.*') ? 'bg-amber-400 text-slate-950' : 'text-slate-300' }}"> <a href="{{ route('admin.makerworld.index') }}" class="block px-3 py-2 rounded-xl {{ request()->routeIs('admin.makerworld.*') ? 'bg-amber-100 text-amber-950 font-black' : 'text-slate-700 hover:bg-slate-50' }}">
🤖 MakerWorld Importer 🤖 MakerWorld Importer
</a> </a>
</div> </div>
@@ -141,15 +141,15 @@
<!-- Prominent Offline Alert Banner --> <!-- Prominent Offline Alert Banner -->
@if($isShopOffline ?? (\App\Models\Setting::get('shop_offline', '0') === '1')) @if($isShopOffline ?? (\App\Models\Setting::get('shop_offline', '0') === '1'))
<div class="bg-gradient-to-r from-rose-700 via-rose-600 to-rose-700 text-white px-4 py-3 text-xs font-bold border-b-2 border-rose-800 shadow-xl flex flex-col sm:flex-row items-center justify-between gap-3 sticky top-16 z-20"> <div class="bg-rose-500 text-white px-4 py-2.5 text-xs font-bold border-b border-rose-600 shadow-md flex flex-col sm:flex-row items-center justify-between gap-3 sticky top-16 z-20">
<div class="flex items-center space-x-2.5"> <div class="flex items-center space-x-2.5">
<span class="w-3 h-3 rounded-full bg-white animate-ping shrink-0"></span> <span class="w-2.5 h-2.5 rounded-full bg-white animate-ping shrink-0"></span>
<span class="px-2 py-0.5 rounded-md bg-white text-rose-800 font-black text-[10px] uppercase tracking-wider shrink-0">Offline Actief</span> <span class="px-2 py-0.5 rounded-md bg-white text-rose-700 font-black text-[10px] uppercase tracking-wider shrink-0">Offline Actief</span>
<span>De webshop staat momenteel <strong>OFFLINE</strong> voor bezoekers. Bezoekers zien de grote rode onderhoudsbanner. Alleen jij als beheerder kunt de website bezoeken.</span> <span>De webshop staat momenteel <strong>OFFLINE</strong> voor bezoekers (rode onderhoudsbanner actief). Alleen jij als beheerder kunt de website bezoeken.</span>
</div> </div>
<form action="{{ route('admin.toggle-offline') }}" method="POST" class="shrink-0"> <form action="{{ route('admin.toggle-offline') }}" method="POST" class="shrink-0">
@csrf @csrf
<button type="submit" class="px-4 py-1.5 bg-white hover:bg-slate-100 text-rose-700 font-black rounded-xl text-xs transition shadow-md cursor-pointer flex items-center space-x-1.5"> <button type="submit" class="px-3.5 py-1.5 bg-white hover:bg-slate-50 text-rose-700 font-black rounded-xl text-xs transition shadow-xs cursor-pointer flex items-center space-x-1.5">
<span class="w-2 h-2 rounded-full bg-emerald-500"></span> <span class="w-2 h-2 rounded-full bg-emerald-500"></span>
<span>Zet Webshop Nu Weer Online &rarr;</span> <span>Zet Webshop Nu Weer Online &rarr;</span>
</button> </button>
@@ -162,12 +162,12 @@
<!-- Flash Alerts --> <!-- Flash Alerts -->
@if(session('success')) @if(session('success'))
<div class="mb-6 p-4 rounded-2xl bg-emerald-950/80 border border-emerald-500/40 text-emerald-200 text-xs sm:text-sm font-bold flex items-center justify-between shadow-lg"> <div class="mb-6 p-4 rounded-2xl bg-emerald-50 border border-emerald-200/80 text-emerald-900 text-xs sm:text-sm font-bold flex items-center justify-between shadow-2xs">
<div class="flex items-center space-x-2.5"> <div class="flex items-center space-x-2.5">
<i data-lucide="check-circle" class="w-5 h-5 text-emerald-400"></i> <i data-lucide="check-circle" class="w-5 h-5 text-emerald-600 shrink-0"></i>
<span>{{ session('success') }}</span> <span>{{ session('success') }}</span>
</div> </div>
<a href="{{ route('home') }}" target="_blank" class="px-3 py-1 rounded-xl bg-emerald-500 text-slate-950 font-black text-xs hover:bg-emerald-400 transition flex items-center space-x-1"> <a href="{{ route('home') }}" target="_blank" class="px-3 py-1 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs transition flex items-center space-x-1 shrink-0">
<span>Bekijk Live</span> <span>Bekijk Live</span>
<i data-lucide="arrow-right" class="w-3.5 h-3.5"></i> <i data-lucide="arrow-right" class="w-3.5 h-3.5"></i>
</a> </a>
@@ -175,8 +175,8 @@
@endif @endif
@if(session('error')) @if(session('error'))
<div class="mb-6 p-4 rounded-2xl bg-rose-950/80 border border-rose-500/40 text-rose-200 text-xs sm:text-sm font-bold flex items-center space-x-2.5 shadow-lg"> <div class="mb-6 p-4 rounded-2xl bg-rose-50 border border-rose-200/80 text-rose-900 text-xs sm:text-sm font-bold flex items-center space-x-2.5 shadow-2xs">
<i data-lucide="alert-circle" class="w-5 h-5 text-rose-400"></i> <i data-lucide="alert-circle" class="w-5 h-5 text-rose-600 shrink-0"></i>
<span>{{ session('error') }}</span> <span>{{ session('error') }}</span>
</div> </div>
@endif @endif
@@ -184,7 +184,7 @@
@yield('content') @yield('content')
</main> </main>
<footer class="border-t border-slate-800/80 py-4 text-center text-xs text-slate-500"> <footer class="border-t border-slate-200/80 py-4 text-center text-xs text-slate-400 bg-white/50">
Jouwdriedee.nl Beheerpaneel &bull; Direct gekoppeld aan live webshop Jouwdriedee.nl Beheerpaneel &bull; Direct gekoppeld aan live webshop
</footer> </footer>
+42 -42
View File
@@ -23,14 +23,14 @@
} }
</style> </style>
</head> </head>
<body class="min-h-full flex flex-col bg-[#FBFBFA] text-slate-800 selection:bg-amber-300 selection:text-slate-900" x-data="{ mobileMenuOpen: false }"> <body class="min-h-full flex flex-col bg-[#FAF9F6] text-slate-800 selection:bg-amber-300 selection:text-slate-900" x-data="{ mobileMenuOpen: false }">
<!-- Admin Offline Preview Notice --> <!-- Admin Offline Preview Notice -->
@if(\App\Models\Setting::get('shop_offline', '0') === '1' && auth()->check() && auth()->user()->isAdmin()) @if(\App\Models\Setting::get('shop_offline', '0') === '1' && auth()->check() && auth()->user()->isAdmin())
<div class="bg-rose-600 text-white px-4 py-2 text-xs font-black flex flex-col sm:flex-row items-center justify-between sticky top-0 z-50 border-b border-rose-700 shadow-lg gap-2"> <div class="bg-rose-600 text-white px-4 py-2 text-xs font-bold flex flex-col sm:flex-row items-center justify-between sticky top-0 z-50 border-b border-rose-700 shadow-md gap-2">
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<span class="w-2.5 h-2.5 rounded-full bg-white animate-pulse"></span> <span class="w-2.5 h-2.5 rounded-full bg-white animate-pulse"></span>
<span>⚠️ OFFLINE BEHEERDERS-PREVIEW: De webshop staat momenteel OFFLINE voor gewone bezoekers. Alleen jij als beheerder ziet deze pagina.</span> <span>⚠️ OFFLINE BEHEERDERS-PREVIEW: Webshop staat momenteel OFFLINE voor bezoekers. Alleen jij als beheerder ziet deze pagina.</span>
</div> </div>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<a href="{{ route('admin.content.index') }}" class="px-2.5 py-1 rounded-lg bg-rose-700 hover:bg-rose-800 text-white text-[11px] font-bold transition"> <a href="{{ route('admin.content.index') }}" class="px-2.5 py-1 rounded-lg bg-rose-700 hover:bg-rose-800 text-white text-[11px] font-bold transition">
@@ -38,7 +38,7 @@
</a> </a>
<form action="{{ route('admin.toggle-offline') }}" method="POST" class="inline"> <form action="{{ route('admin.toggle-offline') }}" method="POST" class="inline">
@csrf @csrf
<button type="submit" class="px-3 py-1 rounded-lg bg-white text-rose-700 hover:bg-rose-50 text-[11px] font-black shadow transition"> <button type="submit" class="px-3 py-1 rounded-lg bg-white text-rose-700 hover:bg-rose-50 text-[11px] font-bold shadow-xs transition">
Zet Nu Weer Online Zet Nu Weer Online
</button> </button>
</form> </form>
@@ -46,23 +46,23 @@
</div> </div>
@endif @endif
<!-- Top Announcement Bar (Inschakelbaar en bewerkbaar via Admin) --> <!-- Top Announcement Bar (Subtiel, Warm & Vriendelijk) -->
@if(setting('announcement_enabled', '1') == '1') @if(setting('announcement_enabled', '1') == '1')
<div class="bg-slate-900 text-white text-xs py-2 px-3 sm:px-4 font-bold border-b border-slate-800"> <div class="bg-slate-900 text-slate-300 text-xs py-2 px-3 sm:px-4 font-semibold border-b border-slate-800/80">
<div class="max-w-7xl mx-auto flex items-center justify-between"> <div class="max-w-7xl mx-auto flex items-center justify-between">
<div class="flex items-center space-x-2 mx-auto sm:mx-0 text-center sm:text-left text-slate-300"> <div class="flex items-center space-x-2 mx-auto sm:mx-0 text-center sm:text-left">
<span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> <span class="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"></span>
<span>{{ setting('announcement_text', 'Nederlandse 3D Printstudio • Haarscherpe afwerking op Bambu Lab printers') }}</span> <span>{{ setting('announcement_text', 'Nederlandse 3D Printstudio • Haarscherpe afwerking op Bambu Lab printers') }}</span>
</div> </div>
<div class="hidden md:flex items-center space-x-5 text-[11px] text-slate-300"> <div class="hidden md:flex items-center space-x-4 text-[11px] text-slate-300 font-medium">
@if(setting('announcement_badge_1')) @if(setting('announcement_badge_1'))
<span class="flex items-center space-x-1"> <span class="flex items-center space-x-1.5">
<i data-lucide="truck" class="w-3.5 h-3.5 text-amber-400"></i> <i data-lucide="truck" class="w-3.5 h-3.5 text-amber-400"></i>
<span>{{ setting('announcement_badge_1', 'Gratis verzending vanaf € 50,-') }}</span> <span>{{ setting('announcement_badge_1', 'Gratis verzending vanaf € 50,-') }}</span>
</span> </span>
@endif @endif
@if(setting('announcement_badge_2')) @if(setting('announcement_badge_2'))
<span class="flex items-center space-x-1"> <span class="flex items-center space-x-1.5">
<i data-lucide="clock" class="w-3.5 h-3.5 text-sky-400"></i> <i data-lucide="clock" class="w-3.5 h-3.5 text-sky-400"></i>
<span>{{ setting('announcement_badge_2', 'Verzending binnen 2-3 werkdagen') }}</span> <span>{{ setting('announcement_badge_2', 'Verzending binnen 2-3 werkdagen') }}</span>
</span> </span>
@@ -73,18 +73,18 @@
@endif @endif
<!-- Main Navigation Header --> <!-- Main Navigation Header -->
<header class="sticky top-0 z-40 bg-white/95 backdrop-blur-md border-b border-slate-200/80 shadow-xs"> <header class="sticky top-0 z-40 bg-white/95 backdrop-blur-md border-b border-slate-200/70 shadow-xs">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16 sm:h-20"> <div class="flex items-center justify-between h-16 sm:h-20">
<!-- Catchy Brand Logo --> <!-- Friendly Brand Logo -->
<div class="flex items-center space-x-4 sm:space-x-8"> <div class="flex items-center space-x-4 sm:space-x-8">
<a href="{{ route('home') }}" class="flex items-center group py-1"> <a href="{{ route('home') }}" class="flex items-center group py-1">
<img src="/images/logo.svg" alt="{{ setting('site_name', 'Jouwdriedee.nl') }}" class="h-9 sm:h-11 w-auto transition-transform duration-200 group-hover:scale-105"> <img src="/images/logo.svg" alt="{{ setting('site_name', 'Jouwdriedee.nl') }}" class="h-9 sm:h-11 w-auto transition-transform duration-200 group-hover:scale-105">
</a> </a>
<!-- Desktop Nav Links --> <!-- Desktop Nav Links -->
<nav class="hidden lg:flex items-center space-x-1.5 text-sm font-bold text-slate-700"> <nav class="hidden lg:flex items-center space-x-1 text-xs font-bold text-slate-600">
<a href="{{ route('home') }}" class="px-3.5 py-2 rounded-xl transition {{ request()->routeIs('home') ? 'bg-amber-100 text-amber-950 font-black' : 'hover:bg-slate-100 text-slate-700' }}"> <a href="{{ route('home') }}" class="px-3.5 py-2 rounded-xl transition {{ request()->routeIs('home') ? 'bg-amber-100 text-amber-950 font-black' : 'hover:bg-slate-100 text-slate-700' }}">
Home Home
</a> </a>
@@ -97,7 +97,7 @@
<a href="{{ route('catalog', ['category' => 'film-popculture']) }}" class="px-3.5 py-2 rounded-xl transition {{ request('category') === 'film-popculture' ? 'bg-rose-100 text-rose-900 font-black' : 'hover:bg-rose-50 hover:text-rose-700 text-slate-700' }}"> <a href="{{ route('catalog', ['category' => 'film-popculture']) }}" class="px-3.5 py-2 rounded-xl transition {{ request('category') === 'film-popculture' ? 'bg-rose-100 text-rose-900 font-black' : 'hover:bg-rose-50 hover:text-rose-700 text-slate-700' }}">
Film & Gaming Film & Gaming
</a> </a>
<a href="{{ route('catalog') }}" class="px-3.5 py-2 rounded-xl transition {{ request()->routeIs('catalog') && !request('category') ? 'bg-slate-200 text-slate-900 font-black' : 'hover:bg-slate-100 text-slate-700' }}"> <a href="{{ route('catalog') }}" class="px-3.5 py-2 rounded-xl transition {{ request()->routeIs('catalog') && !request('category') ? 'bg-slate-100 text-slate-900 font-black' : 'hover:bg-slate-100 text-slate-700' }}">
Alle Prints Alle Prints
</a> </a>
</nav> </nav>
@@ -113,7 +113,7 @@
name="q" name="q"
value="{{ request('q') }}" value="{{ request('q') }}"
placeholder="Zoek print, model..." placeholder="Zoek print, model..."
class="w-44 lg:w-56 bg-slate-100/90 border border-slate-200/80 rounded-xl pl-8 pr-3 py-2 text-xs font-semibold text-slate-800 placeholder-slate-400 focus:bg-white focus:outline-none focus:ring-2 focus:ring-rose-400 transition" class="w-44 lg:w-56 bg-slate-50 border border-slate-200 rounded-xl pl-8 pr-3 py-2 text-xs font-medium text-slate-800 placeholder-slate-400 focus:bg-white focus:outline-none focus:ring-2 focus:ring-amber-400 transition"
> >
<i data-lucide="search" class="w-3.5 h-3.5 text-slate-400 absolute left-2.5 pointer-events-none"></i> <i data-lucide="search" class="w-3.5 h-3.5 text-slate-400 absolute left-2.5 pointer-events-none"></i>
</form> </form>
@@ -123,7 +123,7 @@
$cartItems = is_array(session('cart')) ? session('cart') : []; $cartItems = is_array(session('cart')) ? session('cart') : [];
$cartCount = array_sum(array_column($cartItems, 'quantity')); $cartCount = array_sum(array_column($cartItems, 'quantity'));
@endphp @endphp
<a href="{{ route('cart.index') }}" class="px-3.5 sm:px-4 py-2 rounded-2xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs flex items-center space-x-1.5 sm:space-x-2 transition shadow-xs"> <a href="{{ route('cart.index') }}" class="px-3.5 sm:px-4 py-2 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-bold text-xs flex items-center space-x-1.5 sm:space-x-2 transition shadow-xs">
<i data-lucide="shopping-bag" class="w-4 h-4 text-slate-900"></i> <i data-lucide="shopping-bag" class="w-4 h-4 text-slate-900"></i>
<span class="hidden sm:inline">Winkelwagen</span> <span class="hidden sm:inline">Winkelwagen</span>
@if($cartCount > 0) @if($cartCount > 0)
@@ -135,14 +135,14 @@
<!-- User Account / Login Button --> <!-- User Account / Login Button -->
@guest @guest
<a href="{{ route('login') }}" class="px-3 py-2 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-800 font-bold text-xs flex items-center space-x-1.5 transition"> <a href="{{ route('login') }}" class="px-3 py-2 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-700 font-bold text-xs flex items-center space-x-1.5 transition">
<i data-lucide="user" class="w-4 h-4 text-slate-700"></i> <i data-lucide="user" class="w-4 h-4 text-slate-600"></i>
<span class="hidden sm:inline">Inloggen</span> <span class="hidden sm:inline">Inloggen</span>
</a> </a>
@else @else
<div class="relative" x-data="{ userMenu: false }"> <div class="relative" x-data="{ userMenu: false }">
<button @click="userMenu = !userMenu" type="button" class="px-3 py-2 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-900 font-black text-xs flex items-center space-x-1.5 transition cursor-pointer"> <button @click="userMenu = !userMenu" type="button" class="px-3 py-2 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-800 font-bold text-xs flex items-center space-x-1.5 transition cursor-pointer">
<i data-lucide="user-check" class="w-4 h-4 text-amber-500"></i> <i data-lucide="user-check" class="w-4 h-4 text-amber-600"></i>
<span class="hidden sm:inline">{{ Str::limit(auth()->user()->name, 12) }}</span> <span class="hidden sm:inline">{{ Str::limit(auth()->user()->name, 12) }}</span>
<i data-lucide="chevron-down" class="w-3 h-3 text-slate-500"></i> <i data-lucide="chevron-down" class="w-3 h-3 text-slate-500"></i>
</button> </button>
@@ -154,7 +154,7 @@
</a> </a>
@if(auth()->user()->isAdmin()) @if(auth()->user()->isAdmin())
<a href="{{ route('admin.content.index') }}" class="flex items-center space-x-2 px-4 py-2 hover:bg-amber-50 text-amber-900 font-black"> <a href="{{ route('admin.content.index') }}" class="flex items-center space-x-2 px-4 py-2 hover:bg-amber-50 text-amber-900 font-black">
<i data-lucide="shield" class="w-4 h-4 text-amber-500"></i> <i data-lucide="shield" class="w-4 h-4 text-amber-600"></i>
<span>Admin Beheer</span> <span>Admin Beheer</span>
</a> </a>
@endif @endif
@@ -171,7 +171,7 @@
@endguest @endguest
<!-- Mobile Menu Trigger --> <!-- Mobile Menu Trigger -->
<button @click="mobileMenuOpen = !mobileMenuOpen" type="button" class="lg:hidden p-2 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-800 transition"> <button @click="mobileMenuOpen = !mobileMenuOpen" type="button" class="lg:hidden p-2 rounded-xl bg-slate-100 hover:bg-slate-200 text-slate-700 transition">
<i data-lucide="menu" class="w-5 h-5"></i> <i data-lucide="menu" class="w-5 h-5"></i>
</button> </button>
</div> </div>
@@ -186,7 +186,7 @@
name="q" name="q"
value="{{ request('q') }}" value="{{ request('q') }}"
placeholder="Zoek print, model..." placeholder="Zoek print, model..."
class="w-full bg-slate-100 border border-slate-200 rounded-xl pl-9 pr-3.5 py-2.5 text-xs font-semibold placeholder-slate-400" class="w-full bg-slate-50 border border-slate-200 rounded-xl pl-9 pr-3.5 py-2.5 text-xs font-medium placeholder-slate-400"
> >
<i data-lucide="search" class="w-4 h-4 text-slate-400 absolute left-3"></i> <i data-lucide="search" class="w-4 h-4 text-slate-400 absolute left-3"></i>
</form> </form>
@@ -216,7 +216,7 @@
<i data-lucide="log-in" class="w-3.5 h-3.5"></i> <i data-lucide="log-in" class="w-3.5 h-3.5"></i>
<span>Inloggen</span> <span>Inloggen</span>
</a> </a>
<a href="{{ route('register') }}" class="py-2.5 px-3 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 text-center font-black text-xs flex items-center justify-center space-x-1.5"> <a href="{{ route('register') }}" class="py-2.5 px-3 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 text-center font-bold text-xs flex items-center justify-center space-x-1.5 shadow-xs">
<i data-lucide="user-plus" class="w-3.5 h-3.5"></i> <i data-lucide="user-plus" class="w-3.5 h-3.5"></i>
<span>Registreren</span> <span>Registreren</span>
</a> </a>
@@ -225,7 +225,7 @@
<div class="space-y-1"> <div class="space-y-1">
<a href="{{ route('account.index') }}" class="px-3.5 py-2.5 rounded-xl hover:bg-slate-50 flex items-center justify-between text-slate-900 font-bold"> <a href="{{ route('account.index') }}" class="px-3.5 py-2.5 rounded-xl hover:bg-slate-50 flex items-center justify-between text-slate-900 font-bold">
<span>Mijn Account ({{ auth()->user()->name }})</span> <span>Mijn Account ({{ auth()->user()->name }})</span>
<i data-lucide="user" class="w-4 h-4 text-amber-500"></i> <i data-lucide="user" class="w-4 h-4 text-amber-600"></i>
</a> </a>
@if(auth()->user()->isAdmin()) @if(auth()->user()->isAdmin())
<a href="{{ route('admin.content.index') }}" class="px-3.5 py-2.5 rounded-xl bg-amber-50 hover:bg-amber-100 flex items-center justify-between text-amber-950 font-black"> <a href="{{ route('admin.content.index') }}" class="px-3.5 py-2.5 rounded-xl bg-amber-50 hover:bg-amber-100 flex items-center justify-between text-amber-950 font-black">
@@ -255,7 +255,7 @@
<i data-lucide="check-circle" class="w-4 h-4 text-emerald-600"></i> <i data-lucide="check-circle" class="w-4 h-4 text-emerald-600"></i>
<span>{{ session('success') }}</span> <span>{{ session('success') }}</span>
</div> </div>
<a href="{{ route('cart.index') }}" class="px-3 py-1 bg-white border border-emerald-300 rounded-xl text-xs font-black text-emerald-800 hover:bg-emerald-100 transition">Winkelwagen &rarr;</a> <a href="{{ route('cart.index') }}" class="px-3 py-1 bg-white border border-emerald-300 rounded-xl text-xs font-bold text-emerald-800 hover:bg-emerald-100 transition">Winkelwagen &rarr;</a>
</div> </div>
</div> </div>
@endif @endif
@@ -274,17 +274,17 @@
@yield('content') @yield('content')
</main> </main>
<!-- Footer (Licht, Helder, Aanpasbaar via Admin) --> <!-- Footer (Licht, Verfijnd, Vriendelijk) -->
<footer class="bg-slate-900 text-white border-t border-slate-800 pt-12 pb-10 mt-16"> <footer class="bg-slate-900 text-slate-200 border-t border-slate-800 pt-12 pb-10 mt-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-8 pb-10 border-b border-slate-800"> <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-8 pb-10 border-b border-slate-800/80">
<!-- Col 1: Brand & Info --> <!-- Col 1: Brand & Info -->
<div class="space-y-3 sm:col-span-1"> <div class="space-y-3 sm:col-span-1">
<div class="flex items-center space-x-2.5"> <div class="flex items-center space-x-2">
<img src="/images/logo.svg" alt="{{ setting('site_name', 'Jouwdriedee.nl') }}" class="h-8 w-auto brightness-200 invert"> <img src="/images/logo.svg" alt="{{ setting('site_name', 'Jouwdriedee.nl') }}" class="h-8 w-auto">
</div> </div>
<p class="text-xs text-slate-300 leading-relaxed font-medium"> <p class="text-xs text-slate-300 leading-relaxed font-normal">
{{ setting('footer_description', 'Gespecialiseerd in het 3D printen van gedetailleerde modellen, soepel bewegende fidget toys en iconische popculture figuren. Gemaakt met hoogwaardige filamenten op hypermoderne FDM-printers.') }} {{ setting('footer_description', 'Gespecialiseerd in het 3D printen van gedetailleerde modellen, soepel bewegende fidget toys en iconische popculture figuren. Gemaakt met hoogwaardige filamenten op hypermoderne FDM-printers.') }}
</p> </p>
<div class="inline-flex items-center space-x-1.5 text-[11px] font-bold text-emerald-400 bg-emerald-950/60 px-2.5 py-1 rounded-full border border-emerald-500/30"> <div class="inline-flex items-center space-x-1.5 text-[11px] font-bold text-emerald-400 bg-emerald-950/60 px-2.5 py-1 rounded-full border border-emerald-500/30">
@@ -296,7 +296,7 @@
<!-- Col 2: Categorieën --> <!-- Col 2: Categorieën -->
<div class="space-y-2"> <div class="space-y-2">
<h3 class="font-heading text-xs font-black uppercase tracking-wider text-amber-300">Collectie</h3> <h3 class="font-heading text-xs font-black uppercase tracking-wider text-amber-300">Collectie</h3>
<ul class="space-y-1.5 text-xs text-slate-300 font-semibold"> <ul class="space-y-1.5 text-xs text-slate-300 font-medium">
<li><a href="{{ route('catalog', ['category' => 'fidget-toys']) }}" class="hover:text-white transition">Fidget Toys & Slugs</a></li> <li><a href="{{ route('catalog', ['category' => 'fidget-toys']) }}" class="hover:text-white transition">Fidget Toys & Slugs</a></li>
<li><a href="{{ route('catalog', ['category' => 'pokemon-3d']) }}" class="hover:text-white transition">Pokémon 3D Prints</a></li> <li><a href="{{ route('catalog', ['category' => 'pokemon-3d']) }}" class="hover:text-white transition">Pokémon 3D Prints</a></li>
<li><a href="{{ route('catalog', ['category' => 'film-popculture']) }}" class="hover:text-white transition">Film, Gaming & Collectibles</a></li> <li><a href="{{ route('catalog', ['category' => 'film-popculture']) }}" class="hover:text-white transition">Film, Gaming & Collectibles</a></li>
@@ -309,19 +309,19 @@
<h3 class="font-heading text-xs font-black uppercase tracking-wider text-sky-300">Materialen & Kwaliteit</h3> <h3 class="font-heading text-xs font-black uppercase tracking-wider text-sky-300">Materialen & Kwaliteit</h3>
<ul class="space-y-1.5 text-xs text-slate-300 font-medium"> <ul class="space-y-1.5 text-xs text-slate-300 font-medium">
<li class="flex items-center space-x-1.5"> <li class="flex items-center space-x-1.5">
<span class="w-2 h-2 rounded-full bg-emerald-400"></span> <span class="w-1.5 h-1.5 rounded-full bg-emerald-400"></span>
<span><strong>Bio-PLA+</strong>: Biologisch & geurloos</span> <span><strong>Bio-PLA+</strong>: Biologisch & geurloos</span>
</li> </li>
<li class="flex items-center space-x-1.5"> <li class="flex items-center space-x-1.5">
<span class="w-2 h-2 rounded-full bg-sky-400"></span> <span class="w-1.5 h-1.5 rounded-full bg-sky-400"></span>
<span><strong>PETG Tough</strong>: Slagvast & stevig</span> <span><strong>PETG Tough</strong>: Slagvast & stevig</span>
</li> </li>
<li class="flex items-center space-x-1.5"> <li class="flex items-center space-x-1.5">
<span class="w-2 h-2 rounded-full bg-amber-400"></span> <span class="w-1.5 h-1.5 rounded-full bg-amber-400"></span>
<span><strong>TPU 95A</strong>: Flexibel rubber</span> <span><strong>TPU 95A</strong>: Flexibel rubber</span>
</li> </li>
<li class="flex items-center space-x-1.5"> <li class="flex items-center space-x-1.5">
<span class="w-2 h-2 rounded-full bg-purple-400"></span> <span class="w-1.5 h-1.5 rounded-full bg-purple-400"></span>
<span><strong>Silk Glans</strong>: Prachtige glans</span> <span><strong>Silk Glans</strong>: Prachtige glans</span>
</li> </li>
</ul> </ul>
@@ -330,8 +330,8 @@
<!-- Col 4: Contact --> <!-- Col 4: Contact -->
<div class="space-y-2"> <div class="space-y-2">
<h3 class="font-heading text-xs font-black uppercase tracking-wider text-rose-300">Bestelling & Verzending</h3> <h3 class="font-heading text-xs font-black uppercase tracking-wider text-rose-300">Bestelling & Verzending</h3>
<p class="text-xs text-slate-300 font-medium">Heb je een vraag over een print of levertijd van jouw bestelling?</p> <p class="text-xs text-slate-300 font-normal">Heb je een vraag over een print of levertijd van jouw bestelling?</p>
<div class="p-3 rounded-2xl bg-slate-800/80 border border-slate-700 text-xs space-y-1 font-bold"> <div class="p-3 rounded-2xl bg-slate-800/60 border border-slate-700/80 text-xs space-y-1 font-semibold">
<div class="flex items-center space-x-1.5 text-slate-200"> <div class="flex items-center space-x-1.5 text-slate-200">
<i data-lucide="mail" class="w-3.5 h-3.5 text-amber-400"></i> <i data-lucide="mail" class="w-3.5 h-3.5 text-amber-400"></i>
<span>{{ setting('footer_email', 'info@jouwdriedee.nl') }}</span> <span>{{ setting('footer_email', 'info@jouwdriedee.nl') }}</span>
@@ -345,9 +345,9 @@
</div> </div>
<div class="pt-6 flex flex-col sm:flex-row items-center justify-between text-xs text-slate-400 font-bold gap-3 text-center sm:text-left"> <div class="pt-6 flex flex-col sm:flex-row items-center justify-between text-xs text-slate-400 font-medium gap-3 text-center sm:text-left">
<p>&copy; {{ date('Y') }} {{ setting('site_name', 'Jouwdriedee.nl') }}. {{ setting('footer_copyright', 'Alle 3D prints worden op bestelling vervaardigd.') }}</p> <p>&copy; {{ date('Y') }} {{ setting('site_name', 'Jouwdriedee.nl') }}. {{ setting('footer_copyright', 'Alle 3D prints worden op bestelling vervaardigd.') }}</p>
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3 text-slate-400">
<span>{{ setting('footer_payment_text', 'iDEAL & Bancontact • PostNL Track & Trace') }}</span> <span>{{ setting('footer_payment_text', 'iDEAL & Bancontact • PostNL Track & Trace') }}</span>
</div> </div>
</div> </div>
+23 -23
View File
@@ -4,17 +4,17 @@
@section('content') @section('content')
<!-- Page Header --> <!-- Page Header -->
<div class="bg-slate-50 border-b border-slate-200/80 py-6 sm:py-8"> <div class="bg-gradient-to-b from-amber-50/40 via-white to-[#FAF9F6] border-b border-slate-200/60 py-6 sm:py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row sm:items-center justify-between gap-3"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div> <div>
<div class="inline-flex items-center space-x-1.5 px-3 py-1 rounded-full bg-white border border-slate-200 text-xs font-bold text-slate-700 shadow-2xs mb-2"> <div class="inline-flex items-center space-x-1.5 px-3 py-1 rounded-full bg-white border border-slate-200/80 text-xs font-semibold text-slate-700 shadow-xs mb-2">
<span>Overzicht 3D Modellen</span> <span>Overzicht 3D Modellen</span>
</div> </div>
<h1 class="font-heading text-2xl sm:text-4xl font-black text-slate-900 tracking-tight">Onze 3D Print Collectie</h1> <h1 class="font-heading text-2xl sm:text-4xl font-black text-slate-900 tracking-tight">Onze 3D Print Collectie</h1>
<p class="text-xs text-slate-500 mt-1">Fidget toys, Pokémon figuren en popculture modellen. Geprint op aanvraag in jouw gewenste kleur.</p> <p class="text-xs text-slate-500 mt-1">Fidget toys, Pokémon figuren en popculture modellen. Geprint op aanvraag in jouw gewenste kleur.</p>
</div> </div>
@if(request()->hasAny(['category', 'material', 'q', 'sort'])) @if(request()->hasAny(['category', 'material', 'q', 'sort']))
<a href="{{ route('catalog') }}" class="playful-btn px-3 py-1.5 rounded-xl bg-white border border-slate-200 text-xs font-bold text-rose-600 hover:bg-rose-50 flex items-center space-x-1 self-start shadow-2xs"> <a href="{{ route('catalog') }}" class="px-3 py-1.5 rounded-xl bg-white border border-slate-200 text-xs font-bold text-rose-600 hover:bg-rose-50 flex items-center space-x-1 self-start shadow-xs transition">
<span>✕ Filters Wissen</span> <span>✕ Filters Wissen</span>
</a> </a>
@endif @endif
@@ -23,17 +23,17 @@
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-10" x-data="{ mobileFiltersOpen: false }"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-10" x-data="{ mobileFiltersOpen: false }">
<!-- Mobile Filter Toggle Bar (Zodat mobiele gebruikers niet 600px omlaag hoeven te scrollen!) --> <!-- Mobile Filter Toggle Bar -->
<div class="lg:hidden mb-4 flex items-center justify-between gap-2"> <div class="lg:hidden mb-4 flex items-center justify-between gap-2">
<button <button
type="button" type="button"
@click="mobileFiltersOpen = !mobileFiltersOpen" @click="mobileFiltersOpen = !mobileFiltersOpen"
class="flex-1 py-2.5 px-4 rounded-xl bg-white border border-slate-200 text-slate-800 text-xs font-bold flex items-center justify-center space-x-2 shadow-2xs" class="flex-1 py-2.5 px-4 rounded-xl bg-white border border-slate-200 text-slate-800 text-xs font-bold flex items-center justify-center space-x-2 shadow-xs"
> >
<i data-lucide="sliders-horizontal" class="w-4 h-4 text-rose-500"></i> <i data-lucide="sliders-horizontal" class="w-4 h-4 text-amber-600"></i>
<span x-text="mobileFiltersOpen ? 'Verberg Filters' : 'Filters & Categorieën'"></span> <span x-text="mobileFiltersOpen ? 'Verberg Filters' : 'Filters & Categorieën'"></span>
@if(request()->hasAny(['category', 'material', 'q'])) @if(request()->hasAny(['category', 'material', 'q']))
<span class="w-2 h-2 rounded-full bg-rose-500"></span> <span class="w-2 h-2 rounded-full bg-amber-500"></span>
@endif @endif
</button> </button>
@@ -42,7 +42,7 @@
@if(request('category')) <input type="hidden" name="category" value="{{ request('category') }}"> @endif @if(request('category')) <input type="hidden" name="category" value="{{ request('category') }}"> @endif
@if(request('material')) <input type="hidden" name="material" value="{{ request('material') }}"> @endif @if(request('material')) <input type="hidden" name="material" value="{{ request('material') }}"> @endif
@if(request('q')) <input type="hidden" name="q" value="{{ request('q') }}"> @endif @if(request('q')) <input type="hidden" name="q" value="{{ request('q') }}"> @endif
<select name="sort" onchange="this.form.submit()" class="w-full bg-white border border-slate-200 rounded-xl px-2.5 py-2.5 text-xs font-bold text-slate-700 shadow-2xs"> <select name="sort" onchange="this.form.submit()" class="w-full bg-white border border-slate-200 rounded-xl px-2.5 py-2.5 text-xs font-bold text-slate-700 shadow-xs">
<option value="newest" {{ request('sort') === 'newest' ? 'selected' : '' }}>Nieuwste</option> <option value="newest" {{ request('sort') === 'newest' ? 'selected' : '' }}>Nieuwste</option>
<option value="price_asc" {{ request('sort') === 'price_asc' ? 'selected' : '' }}>Prijs &uarr;</option> <option value="price_asc" {{ request('sort') === 'price_asc' ? 'selected' : '' }}>Prijs &uarr;</option>
<option value="price_desc" {{ request('sort') === 'price_desc' ? 'selected' : '' }}>Prijs &darr;</option> <option value="price_desc" {{ request('sort') === 'price_desc' ? 'selected' : '' }}>Prijs &darr;</option>
@@ -52,16 +52,16 @@
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6 lg:gap-8"> <div class="grid grid-cols-1 lg:grid-cols-4 gap-6 lg:gap-8">
<!-- Filters Sidebar (Op mobiel inklapbaar via Alpine.js) --> <!-- Filters Sidebar -->
<aside :class="mobileFiltersOpen ? 'block' : 'hidden lg:block'" class="space-y-4"> <aside :class="mobileFiltersOpen ? 'block' : 'hidden lg:block'" class="space-y-4">
<div class="p-5 sm:p-6 rounded-3xl bg-white border border-slate-100 shadow-sm space-y-5"> <div class="p-5 sm:p-6 rounded-2xl bg-white border border-slate-200/80 shadow-xs space-y-5">
<div class="flex items-center justify-between pb-3 border-b border-slate-100"> <div class="flex items-center justify-between pb-3 border-b border-slate-100">
<span class="font-heading text-xs font-black text-slate-900 uppercase tracking-wider flex items-center space-x-1.5"> <span class="font-heading text-xs font-black text-slate-900 uppercase tracking-wider flex items-center space-x-1.5">
<i data-lucide="filter" class="w-3.5 h-3.5 text-rose-500"></i> <i data-lucide="filter" class="w-3.5 h-3.5 text-amber-600"></i>
<span>Filters & Zoeken</span> <span>Filters & Zoeken</span>
</span> </span>
@if(request()->hasAny(['category', 'material', 'q', 'sort'])) @if(request()->hasAny(['category', 'material', 'q', 'sort']))
<a href="{{ route('catalog') }}" class="text-[11px] font-bold text-rose-500 hover:underline">Wissen</a> <a href="{{ route('catalog') }}" class="text-[11px] font-bold text-rose-600 hover:underline">Wissen</a>
@endif @endif
</div> </div>
@@ -78,7 +78,7 @@
name="q" name="q"
value="{{ request('q') }}" value="{{ request('q') }}"
placeholder="Draak, Pikachu, Grogu..." placeholder="Draak, Pikachu, Grogu..."
class="w-full bg-slate-50 border border-slate-200 rounded-xl pl-8 pr-3 py-2 text-xs font-bold text-slate-900 placeholder-slate-400 focus:bg-white focus:outline-none focus:ring-1 focus:ring-rose-400" class="w-full bg-slate-50 border border-slate-200 rounded-xl pl-8 pr-3 py-2 text-xs font-medium text-slate-900 placeholder-slate-400 focus:bg-white focus:outline-none focus:ring-2 focus:ring-amber-400"
> >
<i data-lucide="search" class="w-3.5 h-3.5 text-slate-400 absolute left-2.5 top-1/2 -translate-y-1/2"></i> <i data-lucide="search" class="w-3.5 h-3.5 text-slate-400 absolute left-2.5 top-1/2 -translate-y-1/2"></i>
</div> </div>
@@ -117,7 +117,7 @@
<!-- Sortering Desktop --> <!-- Sortering Desktop -->
<div class="hidden lg:block"> <div class="hidden lg:block">
<label class="block text-xs font-bold text-slate-700 mb-1.5">Sorteren</label> <label class="block text-xs font-bold text-slate-700 mb-1.5">Sorteren</label>
<select name="sort" onchange="this.form.submit()" class="w-full bg-slate-50 border border-slate-200 rounded-xl px-3 py-2 text-xs font-bold text-slate-700 focus:bg-white focus:outline-none"> <select name="sort" onchange="this.form.submit()" class="w-full bg-slate-50 border border-slate-200 rounded-xl px-3 py-2 text-xs font-bold text-slate-700 focus:bg-white focus:outline-none focus:ring-2 focus:ring-amber-400">
<option value="newest" {{ request('sort') === 'newest' ? 'selected' : '' }}>Nieuwste toevoegingen</option> <option value="newest" {{ request('sort') === 'newest' ? 'selected' : '' }}>Nieuwste toevoegingen</option>
<option value="price_asc" {{ request('sort') === 'price_asc' ? 'selected' : '' }}>Prijs: Laag naar hoog</option> <option value="price_asc" {{ request('sort') === 'price_asc' ? 'selected' : '' }}>Prijs: Laag naar hoog</option>
<option value="price_desc" {{ request('sort') === 'price_desc' ? 'selected' : '' }}>Prijs: Hoog naar laag</option> <option value="price_desc" {{ request('sort') === 'price_desc' ? 'selected' : '' }}>Prijs: Hoog naar laag</option>
@@ -127,21 +127,21 @@
</div> </div>
</aside> </aside>
<!-- Producten Overzicht (2 kolommen op mobiel, 3 op desktop) --> <!-- Producten Overzicht -->
<div class="lg:col-span-3 space-y-6"> <div class="lg:col-span-3 space-y-6">
@if($products->isEmpty()) @if($products->isEmpty())
<div class="p-10 text-center rounded-3xl bg-white border border-slate-100 shadow-sm space-y-3"> <div class="p-10 text-center rounded-3xl bg-white border border-slate-200/80 shadow-xs space-y-3">
<div class="w-14 h-14 rounded-2xl bg-amber-50 text-slate-800 flex items-center justify-center mx-auto text-3xl"> <div class="w-14 h-14 rounded-2xl bg-amber-50 text-slate-800 flex items-center justify-center mx-auto text-3xl">
🙈 🙈
</div> </div>
<h3 class="font-heading text-lg font-black text-slate-900">Geen artikelen gevonden</h3> <h3 class="font-heading text-lg font-black text-slate-900">Geen artikelen gevonden</h3>
<p class="text-xs text-slate-500 max-w-sm mx-auto">Er zijn geen prints gevonden die voldoen aan je huidige filters. Wis je filters of bekijk alle categorieën!</p> <p class="text-xs text-slate-500 max-w-sm mx-auto">Er zijn geen prints gevonden die voldoen aan je huidige filters. Wis je filters of bekijk alle categorieën!</p>
<a href="{{ route('catalog') }}" class="playful-btn inline-flex px-5 py-2.5 rounded-xl bg-amber-400 text-slate-950 text-xs font-black shadow-xs">Alle prints bekijken 🎁</a> <a href="{{ route('catalog') }}" class="inline-flex px-5 py-2.5 rounded-xl bg-amber-400 text-slate-950 text-xs font-bold shadow-xs">Alle prints bekijken 🎁</a>
</div> </div>
@else @else
<div class="grid grid-cols-2 md:grid-cols-3 gap-3 sm:gap-6"> <div class="grid grid-cols-2 md:grid-cols-3 gap-3 sm:gap-6">
@foreach($products as $product) @foreach($products as $product)
<div class="playful-card rounded-2xl sm:rounded-3xl bg-white border border-slate-100 shadow-xs hover:shadow-lg overflow-hidden flex flex-col justify-between group"> <div class="rounded-2xl sm:rounded-3xl bg-white border border-slate-200/80 shadow-xs hover:shadow-md transition-all duration-200 overflow-hidden flex flex-col justify-between group">
<div> <div>
<div class="relative h-40 sm:h-56 bg-slate-100 overflow-hidden"> <div class="relative h-40 sm:h-56 bg-slate-100 overflow-hidden">
<img <img
@@ -151,29 +151,29 @@
loading="lazy" loading="lazy"
> >
@if($product->category) @if($product->category)
<span class="absolute top-2 left-2 sm:top-3 sm:left-3 px-2 py-0.5 rounded-md bg-white/90 backdrop-blur-xs text-[9px] sm:text-[10px] font-black text-slate-800 shadow-2xs"> <span class="absolute top-2 left-2 sm:top-3 sm:left-3 px-2 py-0.5 rounded-md bg-white/95 backdrop-blur-xs text-[9px] sm:text-[10px] font-bold text-slate-800 shadow-xs border border-slate-200/60">
{{ $product->category->name }} {{ $product->category->name }}
</span> </span>
@endif @endif
</div> </div>
<div class="p-3 sm:p-4"> <div class="p-3 sm:p-4">
<h3 class="font-heading text-xs sm:text-sm font-black text-slate-900 group-hover:text-rose-600 transition line-clamp-2"> <h3 class="font-heading text-xs sm:text-sm font-black text-slate-900 group-hover:text-amber-700 transition line-clamp-2">
<a href="{{ route('product.show', $product->slug) }}"> <a href="{{ route('product.show', $product->slug) }}">
{{ $product->name }} {{ $product->name }}
</a> </a>
</h3> </h3>
<p class="hidden sm:block text-xs text-slate-500 mt-1 line-clamp-2 leading-relaxed"> <p class="hidden sm:block text-xs text-slate-500 mt-1 line-clamp-2 leading-relaxed font-normal">
{{ $product->short_description }} {{ $product->short_description }}
</p> </p>
</div> </div>
</div> </div>
<div class="p-3 sm:p-4 pt-0 border-t border-slate-100/80 mt-1 flex items-center justify-between"> <div class="p-3 sm:p-4 pt-0 border-t border-slate-100 mt-1 flex items-center justify-between">
<div> <div>
<span class="text-[9px] sm:text-[10px] text-slate-400 block font-bold uppercase">Vanaf</span> <span class="text-[9px] sm:text-[10px] text-slate-400 block font-bold uppercase">Vanaf</span>
<span class="font-heading text-sm sm:text-base font-black text-slate-900">{{ $product->formatted_price }}</span> <span class="font-heading text-sm sm:text-base font-black text-slate-900">{{ $product->formatted_price }}</span>
</div> </div>
<a href="{{ route('product.show', $product->slug) }}" class="playful-btn px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 text-[10px] sm:text-xs font-black transition flex items-center space-x-1 shadow-2xs"> <a href="{{ route('product.show', $product->slug) }}" class="px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 text-[10px] sm:text-xs font-bold transition flex items-center space-x-1 shadow-xs">
<span>Kleur 🎨</span> <span>Kleur 🎨</span>
</a> </a>
</div> </div>
+51 -51
View File
@@ -3,15 +3,15 @@
@section('title', setting('site_name', 'Jouwdriedee.nl') . ' | ' . setting('site_tagline', 'Kleurrijke 3D Prints, Fidget Toys & Collectibles')) @section('title', setting('site_name', 'Jouwdriedee.nl') . ' | ' . setting('site_tagline', 'Kleurrijke 3D Prints, Fidget Toys & Collectibles'))
@section('content') @section('content')
<!-- Hero Section (Helder, Kleurrijk, Aanpasbaar via Admin) --> <!-- Hero Section (Subtiel, Vriendelijk, Kleurrijk) -->
<section class="relative overflow-hidden bg-gradient-to-b from-amber-50/50 via-white to-[#FBFBFA] pt-8 pb-12 sm:pt-14 sm:pb-18 border-b border-slate-200/70"> <section class="relative overflow-hidden bg-gradient-to-b from-amber-50/40 via-white to-[#FAF9F6] pt-8 pb-12 sm:pt-14 sm:pb-18 border-b border-slate-200/60">
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 items-center"> <div class="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 items-center">
<!-- Left Hero Content --> <!-- Left Hero Content -->
<div class="lg:col-span-7 space-y-4 sm:space-y-6 text-center lg:text-left"> <div class="lg:col-span-7 space-y-4 sm:space-y-6 text-center lg:text-left">
<div class="inline-flex items-center space-x-2 px-3.5 py-1.5 rounded-full bg-white border border-slate-200/80 shadow-2xs text-slate-800 text-xs font-bold"> <div class="inline-flex items-center space-x-2 px-3.5 py-1.5 rounded-full bg-white border border-slate-200/80 shadow-xs text-slate-800 text-xs font-semibold">
<span class="w-2 h-2 rounded-full bg-emerald-500 animate-pulse"></span> <span class="w-2 h-2 rounded-full bg-emerald-500 animate-pulse"></span>
<span>{{ setting('hero_badge', 'Nederlandse 3D Printstudio • Bambu Lab Precisie') }}</span> <span>{{ setting('hero_badge', 'Nederlandse 3D Printstudio • Bambu Lab Precisie') }}</span>
</div> </div>
@@ -26,11 +26,11 @@
<!-- Action CTA's --> <!-- Action CTA's -->
<div class="flex flex-col sm:flex-row items-center justify-center lg:justify-start gap-3 pt-2"> <div class="flex flex-col sm:flex-row items-center justify-center lg:justify-start gap-3 pt-2">
<a href="{{ setting('hero_primary_btn_url', route('catalog')) }}" class="w-full sm:w-auto px-7 py-3.5 rounded-2xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-sm flex items-center justify-center space-x-2 transition shadow-xs"> <a href="{{ setting('hero_primary_btn_url', route('catalog')) }}" class="w-full sm:w-auto px-7 py-3.5 rounded-2xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-bold text-sm flex items-center justify-center space-x-2 transition shadow-xs">
<span>{{ setting('hero_primary_btn_text', 'Bekijk de Collectie') }}</span> <span>{{ setting('hero_primary_btn_text', 'Bekijk de Collectie') }}</span>
<i data-lucide="arrow-right" class="w-4 h-4"></i> <i data-lucide="arrow-right" class="w-4 h-4"></i>
</a> </a>
<a href="{{ setting('hero_secondary_btn_url', route('catalog', ['category' => 'fidget-toys'])) }}" class="w-full sm:w-auto px-6 py-3.5 rounded-2xl bg-violet-100 hover:bg-violet-200 text-violet-900 font-bold text-sm flex items-center justify-center space-x-2 transition"> <a href="{{ setting('hero_secondary_btn_url', route('catalog', ['category' => 'fidget-toys'])) }}" class="w-full sm:w-auto px-6 py-3.5 rounded-2xl bg-violet-50 hover:bg-violet-100 text-violet-900 font-bold text-sm flex items-center justify-center space-x-2 transition border border-violet-200/50">
<span>{{ setting('hero_secondary_btn_text', 'Fidget Toys & Slugs') }}</span> <span>{{ setting('hero_secondary_btn_text', 'Fidget Toys & Slugs') }}</span>
<i data-lucide="sparkles" class="w-4 h-4 text-violet-600"></i> <i data-lucide="sparkles" class="w-4 h-4 text-violet-600"></i>
</a> </a>
@@ -38,7 +38,7 @@
<!-- Technische specificaties / Kwaliteitsbadges --> <!-- Technische specificaties / Kwaliteitsbadges -->
<div class="pt-4 grid grid-cols-3 gap-2 sm:gap-4 max-w-md mx-auto lg:mx-0"> <div class="pt-4 grid grid-cols-3 gap-2 sm:gap-4 max-w-md mx-auto lg:mx-0">
<div class="p-3 bg-white border border-slate-200/80 rounded-2xl shadow-2xs text-center"> <div class="p-3 bg-white border border-slate-200/70 rounded-2xl shadow-xs text-center">
<div class="text-base sm:text-xl font-black text-slate-900 font-heading"> <div class="text-base sm:text-xl font-black text-slate-900 font-heading">
{{ setting('hero_stat_1_val', '0.12 mm') }} {{ setting('hero_stat_1_val', '0.12 mm') }}
</div> </div>
@@ -46,7 +46,7 @@
{{ setting('hero_stat_1_lbl', 'Minimale laaghoogte') }} {{ setting('hero_stat_1_lbl', 'Minimale laaghoogte') }}
</div> </div>
</div> </div>
<div class="p-3 bg-white border border-slate-200/80 rounded-2xl shadow-2xs text-center"> <div class="p-3 bg-white border border-slate-200/70 rounded-2xl shadow-xs text-center">
<div class="text-base sm:text-xl font-black text-slate-900 font-heading"> <div class="text-base sm:text-xl font-black text-slate-900 font-heading">
{{ setting('hero_stat_2_val', 'Bambu Lab') }} {{ setting('hero_stat_2_val', 'Bambu Lab') }}
</div> </div>
@@ -54,7 +54,7 @@
{{ setting('hero_stat_2_lbl', 'X1-Carbon Precisie') }} {{ setting('hero_stat_2_lbl', 'X1-Carbon Precisie') }}
</div> </div>
</div> </div>
<div class="p-3 bg-white border border-slate-200/80 rounded-2xl shadow-2xs text-center"> <div class="p-3 bg-white border border-slate-200/70 rounded-2xl shadow-xs text-center">
<div class="text-base sm:text-xl font-black text-slate-900 font-heading"> <div class="text-base sm:text-xl font-black text-slate-900 font-heading">
{{ setting('hero_stat_3_val', '2-3 Dagen') }} {{ setting('hero_stat_3_val', '2-3 Dagen') }}
</div> </div>
@@ -68,9 +68,9 @@
<!-- Right Hero Card / Werkelijke 3D Print Productfoto --> <!-- Right Hero Card / Werkelijke 3D Print Productfoto -->
<div class="lg:col-span-5 relative"> <div class="lg:col-span-5 relative">
<div class="relative rounded-3xl bg-white p-3 sm:p-4 border border-slate-200/90 shadow-lg"> <div class="relative rounded-3xl bg-white p-3 sm:p-4 border border-slate-200/80 shadow-md">
<div class="absolute top-6 right-6 bg-slate-900/90 backdrop-blur-xs text-white font-bold text-[10px] sm:text-xs px-3 py-1 rounded-full shadow-sm z-10"> <div class="absolute top-5 right-5 bg-white/95 backdrop-blur-xs text-amber-900 border border-amber-200/80 font-bold text-[10px] sm:text-xs px-3 py-1 rounded-full shadow-xs z-10">
{{ setting('hero_card_badge', 'Meest gekozen print') }} {{ setting('hero_card_badge', 'Meest gekozen print') }}
</div> </div>
@@ -81,9 +81,9 @@
class="w-full h-full object-cover object-center hover:scale-105 transition-transform duration-500" class="w-full h-full object-cover object-center hover:scale-105 transition-transform duration-500"
> >
<div class="absolute bottom-2.5 left-2.5 right-2.5 p-3 rounded-xl bg-white/95 backdrop-blur-xs border border-slate-100 shadow-sm flex items-center justify-between"> <div class="absolute bottom-2.5 left-2.5 right-2.5 p-3 rounded-xl bg-white/95 backdrop-blur-xs border border-slate-200/80 shadow-xs flex items-center justify-between">
<div> <div>
<span class="text-[10px] font-bold uppercase tracking-wider text-rose-500 block">Fidget & Sensory</span> <span class="text-[10px] font-bold uppercase tracking-wider text-rose-600 block">Fidget & Sensory</span>
<h3 class="font-heading font-black text-slate-900 text-xs sm:text-sm">Articulated Kristaldraak</h3> <h3 class="font-heading font-black text-slate-900 text-xs sm:text-sm">Articulated Kristaldraak</h3>
</div> </div>
<div class="text-right"> <div class="text-right">
@@ -93,12 +93,12 @@
</div> </div>
</div> </div>
<div class="mt-3 flex items-center justify-between text-[11px] text-slate-500 font-semibold px-1"> <div class="mt-3 flex items-center justify-between text-[11px] text-slate-500 font-medium px-1">
<span class="flex items-center space-x-1"> <span class="flex items-center space-x-1.5">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500"></span> <span class="w-1.5 h-1.5 rounded-full bg-emerald-500"></span>
<span>Print-in-place scharnieren</span> <span>Print-in-place scharnieren</span>
</span> </span>
<span class="flex items-center space-x-1"> <span class="flex items-center space-x-1 text-slate-600">
<span>Kleur naar keuze selecteerbaar</span> <span>Kleur naar keuze selecteerbaar</span>
</span> </span>
</div> </div>
@@ -109,12 +109,12 @@
</div> </div>
</section> </section>
<!-- 3 Categorieën Grid (Plat, Helder & Aanpasbaar) --> <!-- 3 Categorieën Grid (Subtiel, Warm & Vriendelijk) -->
<section class="py-12 sm:py-16 bg-white border-b border-slate-200/70"> <section class="py-12 sm:py-16 bg-white border-b border-slate-200/60">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-col sm:flex-row sm:items-end justify-between mb-8 gap-2"> <div class="flex flex-col sm:flex-row sm:items-end justify-between mb-8 gap-2">
<div> <div>
<span class="text-xs font-bold uppercase tracking-wider text-rose-500"> <span class="text-xs font-bold uppercase tracking-wider text-amber-700">
{{ setting('categories_tag', 'Collecties') }} {{ setting('categories_tag', 'Collecties') }}
</span> </span>
<h2 class="font-heading text-2xl sm:text-3xl font-black text-slate-900 tracking-tight"> <h2 class="font-heading text-2xl sm:text-3xl font-black text-slate-900 tracking-tight">
@@ -124,7 +124,7 @@
{{ setting('categories_subtitle', 'Selecteer een categorie voor alle beschikbare modellen, formaten en kleurstalen.') }} {{ setting('categories_subtitle', 'Selecteer een categorie voor alle beschikbare modellen, formaten en kleurstalen.') }}
</p> </p>
</div> </div>
<a href="{{ route('catalog') }}" class="text-xs font-bold text-rose-600 hover:text-rose-700 flex items-center space-x-1 self-start sm:self-auto"> <a href="{{ route('catalog') }}" class="text-xs font-bold text-amber-700 hover:text-amber-800 flex items-center space-x-1 self-start sm:self-auto">
<span>Alle prints bekijken</span> <span>Alle prints bekijken</span>
<i data-lucide="chevron-right" class="w-3.5 h-3.5"></i> <i data-lucide="chevron-right" class="w-3.5 h-3.5"></i>
</a> </a>
@@ -132,15 +132,15 @@
<div class="grid grid-cols-1 md:grid-cols-3 gap-6"> <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- 1. Fidget Toys --> <!-- 1. Fidget Toys -->
<a href="{{ route('catalog', ['category' => 'fidget-toys']) }}" class="rounded-3xl bg-[#FAF7FD] border border-violet-100 hover:border-violet-200 transition-all duration-300 overflow-hidden group shadow-2xs hover:shadow-md flex flex-col justify-between"> <a href="{{ route('catalog', ['category' => 'fidget-toys']) }}" class="rounded-3xl bg-[#FAF7FD] border border-violet-100 hover:border-violet-200 hover:shadow-md transition-all duration-300 overflow-hidden group shadow-xs flex flex-col justify-between">
<div class="h-48 overflow-hidden bg-violet-50"> <div class="h-48 overflow-hidden bg-violet-50">
<img src="/images/categories/fidget-toys.jpg" alt="Fidget Toys 3D Prints" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"> <img src="/images/categories/fidget-toys.jpg" alt="Fidget Toys 3D Prints" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500">
</div> </div>
<div class="p-5"> <div class="p-5">
<span class="text-[10px] font-bold text-violet-600 uppercase tracking-wider block">Beweegbare Scharnieren</span> <span class="text-[10px] font-bold text-violet-700 uppercase tracking-wider block">Beweegbare Scharnieren</span>
<h3 class="font-heading text-lg font-black text-slate-900 group-hover:text-violet-700 transition">Fidget Toys & Sensory</h3> <h3 class="font-heading text-lg font-black text-slate-900 group-hover:text-violet-700 transition">Fidget Toys & Sensory</h3>
<p class="text-xs text-slate-500 mt-1.5 line-clamp-2 leading-relaxed">Kristaldraken, wriemel-slakken en compacte folding cubes met soepel klikgeluid.</p> <p class="text-xs text-slate-500 mt-1.5 line-clamp-2 leading-relaxed font-normal">Kristaldraken, wriemel-slakken en compacte folding cubes met soepel klikgeluid.</p>
<div class="mt-4 flex items-center text-xs font-black text-violet-600 group-hover:translate-x-1 transition-transform"> <div class="mt-4 flex items-center text-xs font-bold text-violet-700 group-hover:translate-x-1 transition-transform">
<span>Bekijk collectie</span> <span>Bekijk collectie</span>
<i data-lucide="arrow-right" class="w-3.5 h-3.5 ml-1"></i> <i data-lucide="arrow-right" class="w-3.5 h-3.5 ml-1"></i>
</div> </div>
@@ -148,15 +148,15 @@
</a> </a>
<!-- 2. Pokémon --> <!-- 2. Pokémon -->
<a href="{{ route('catalog', ['category' => 'pokemon-3d']) }}" class="rounded-3xl bg-[#FFFBF0] border border-amber-100 hover:border-amber-200 transition-all duration-300 overflow-hidden group shadow-2xs hover:shadow-md flex flex-col justify-between"> <a href="{{ route('catalog', ['category' => 'pokemon-3d']) }}" class="rounded-3xl bg-[#FFFBF0] border border-amber-100 hover:border-amber-200 hover:shadow-md transition-all duration-300 overflow-hidden group shadow-xs flex flex-col justify-between">
<div class="h-48 overflow-hidden bg-amber-50"> <div class="h-48 overflow-hidden bg-amber-50">
<img src="/images/categories/pokemon-3d.jpg" alt="Pokémon 3D Prints" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"> <img src="/images/categories/pokemon-3d.jpg" alt="Pokémon 3D Prints" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500">
</div> </div>
<div class="p-5"> <div class="p-5">
<span class="text-[10px] font-bold text-amber-600 uppercase tracking-wider block">Gedetailleerde Figuren</span> <span class="text-[10px] font-bold text-amber-700 uppercase tracking-wider block">Gedetailleerde Figuren</span>
<h3 class="font-heading text-lg font-black text-slate-900 group-hover:text-amber-700 transition">Pokémon 3D Prints</h3> <h3 class="font-heading text-lg font-black text-slate-900 group-hover:text-amber-700 transition">Pokémon 3D Prints</h3>
<p class="text-xs text-slate-500 mt-1.5 line-clamp-2 leading-relaxed">Low-poly Pikachu planters, beweegbare Charizard, sluitende Pokéballs en Gengar.</p> <p class="text-xs text-slate-500 mt-1.5 line-clamp-2 leading-relaxed font-normal">Low-poly Pikachu planters, beweegbare Charizard, sluitende Pokéballs en Gengar.</p>
<div class="mt-4 flex items-center text-xs font-black text-amber-700 group-hover:translate-x-1 transition-transform"> <div class="mt-4 flex items-center text-xs font-bold text-amber-800 group-hover:translate-x-1 transition-transform">
<span>Bekijk collectie</span> <span>Bekijk collectie</span>
<i data-lucide="arrow-right" class="w-3.5 h-3.5 ml-1"></i> <i data-lucide="arrow-right" class="w-3.5 h-3.5 ml-1"></i>
</div> </div>
@@ -164,15 +164,15 @@
</a> </a>
<!-- 3. Film & Gaming --> <!-- 3. Film & Gaming -->
<a href="{{ route('catalog', ['category' => 'film-popculture']) }}" class="rounded-3xl bg-[#FFF5F5] border border-rose-100 hover:border-rose-200 transition-all duration-300 overflow-hidden group shadow-2xs hover:shadow-md flex flex-col justify-between"> <a href="{{ route('catalog', ['category' => 'film-popculture']) }}" class="rounded-3xl bg-[#FFF5F5] border border-rose-100 hover:border-rose-200 hover:shadow-md transition-all duration-300 overflow-hidden group shadow-xs flex flex-col justify-between">
<div class="h-48 overflow-hidden bg-rose-50"> <div class="h-48 overflow-hidden bg-rose-50">
<img src="/images/categories/film-popculture.jpg" alt="Film & Gaming 3D Prints" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"> <img src="/images/categories/film-popculture.jpg" alt="Film & Gaming 3D Prints" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500">
</div> </div>
<div class="p-5"> <div class="p-5">
<span class="text-[10px] font-bold text-rose-500 uppercase tracking-wider block">Collectibles & Display</span> <span class="text-[10px] font-bold text-rose-600 uppercase tracking-wider block">Collectibles & Display</span>
<h3 class="font-heading text-lg font-black text-slate-900 group-hover:text-rose-600 transition">Film, Gaming & Props</h3> <h3 class="font-heading text-lg font-black text-slate-900 group-hover:text-rose-600 transition">Film, Gaming & Props</h3>
<p class="text-xs text-slate-500 mt-1.5 line-clamp-2 leading-relaxed">Lightsaber standaarden, Thor Mjolnir bureau-organizers en gedetailleerde Grogu figuren.</p> <p class="text-xs text-slate-500 mt-1.5 line-clamp-2 leading-relaxed font-normal">Lightsaber standaarden, Thor Mjolnir bureau-organizers en gedetailleerde figuren.</p>
<div class="mt-4 flex items-center text-xs font-black text-rose-600 group-hover:translate-x-1 transition-transform"> <div class="mt-4 flex items-center text-xs font-bold text-rose-600 group-hover:translate-x-1 transition-transform">
<span>Bekijk collectie</span> <span>Bekijk collectie</span>
<i data-lucide="arrow-right" class="w-3.5 h-3.5 ml-1"></i> <i data-lucide="arrow-right" class="w-3.5 h-3.5 ml-1"></i>
</div> </div>
@@ -182,12 +182,12 @@
</div> </div>
</section> </section>
<!-- Uitgelichte Prints Showcase --> <!-- Uitgelichte Prints Showcase (Subtiel & Vriendelijk) -->
<section class="py-12 sm:py-16 bg-[#FBFBFA]"> <section class="py-12 sm:py-16 bg-[#FAF9F6]">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-col sm:flex-row sm:items-end justify-between mb-8 gap-3"> <div class="flex flex-col sm:flex-row sm:items-end justify-between mb-8 gap-3">
<div> <div>
<span class="text-xs font-bold uppercase tracking-wider text-rose-500"> <span class="text-xs font-bold uppercase tracking-wider text-amber-700">
{{ setting('featured_tag', 'Selectie') }} {{ setting('featured_tag', 'Selectie') }}
</span> </span>
<h2 class="font-heading text-2xl sm:text-3xl font-black text-slate-900 tracking-tight"> <h2 class="font-heading text-2xl sm:text-3xl font-black text-slate-900 tracking-tight">
@@ -197,7 +197,7 @@
{{ setting('featured_subtitle', 'Elk item wordt op bestelling geprint in jouw gewenste kleur en met de hand nagekeken.') }} {{ setting('featured_subtitle', 'Elk item wordt op bestelling geprint in jouw gewenste kleur en met de hand nagekeken.') }}
</p> </p>
</div> </div>
<a href="{{ route('catalog') }}" class="px-4 py-2 rounded-xl bg-white border border-slate-200 text-slate-800 text-xs font-bold hover:bg-slate-50 transition flex items-center space-x-1.5 self-start sm:self-auto shadow-2xs"> <a href="{{ route('catalog') }}" class="px-4 py-2 rounded-xl bg-white border border-slate-200 text-slate-700 text-xs font-bold hover:bg-slate-50 transition flex items-center space-x-1.5 self-start sm:self-auto shadow-xs">
<span>Volledige catalogus</span> <span>Volledige catalogus</span>
<i data-lucide="arrow-right" class="w-3.5 h-3.5"></i> <i data-lucide="arrow-right" class="w-3.5 h-3.5"></i>
</a> </a>
@@ -206,7 +206,7 @@
<!-- 2 Kolommen mobiel, 3 op desktop --> <!-- 2 Kolommen mobiel, 3 op desktop -->
<div class="grid grid-cols-2 md:grid-cols-3 gap-3 sm:gap-6"> <div class="grid grid-cols-2 md:grid-cols-3 gap-3 sm:gap-6">
@foreach($featuredProducts as $product) @foreach($featuredProducts as $product)
<div class="rounded-2xl sm:rounded-3xl bg-white border border-slate-200/80 shadow-2xs hover:shadow-md transition-all duration-200 overflow-hidden flex flex-col justify-between group"> <div class="rounded-2xl sm:rounded-3xl bg-white border border-slate-200/80 shadow-xs hover:shadow-md transition-all duration-200 overflow-hidden flex flex-col justify-between group">
<!-- Afbeelding & Categorie Badge --> <!-- Afbeelding & Categorie Badge -->
<div class="relative h-44 sm:h-60 overflow-hidden bg-slate-100"> <div class="relative h-44 sm:h-60 overflow-hidden bg-slate-100">
@@ -218,15 +218,15 @@
> >
@if($product->category) @if($product->category)
<div class="absolute top-2 left-2 sm:top-3 sm:left-3"> <div class="absolute top-2 left-2 sm:top-3 sm:left-3">
<span class="px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-md bg-white/90 backdrop-blur-xs text-slate-900 text-[9px] sm:text-[10px] font-black shadow-2xs"> <span class="px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-md bg-white/95 backdrop-blur-xs text-slate-800 text-[9px] sm:text-[10px] font-bold shadow-xs border border-slate-200/60">
{{ $product->category->name }} {{ $product->category->name }}
</span> </span>
</div> </div>
@endif @endif
@if($product->print_time_hours) @if($product->print_time_hours)
<div class="hidden sm:flex absolute bottom-2.5 left-2.5 px-2 py-0.5 rounded-md bg-slate-900/80 backdrop-blur-xs text-white text-[10px] font-medium items-center space-x-1"> <div class="hidden sm:flex absolute bottom-2.5 left-2.5 px-2 py-0.5 rounded-md bg-white/95 backdrop-blur-xs text-slate-700 border border-slate-200/60 text-[10px] font-medium items-center space-x-1 shadow-xs">
<i data-lucide="clock" class="w-3 h-3 text-sky-400"></i> <i data-lucide="clock" class="w-3 h-3 text-sky-600"></i>
<span>{{ $product->print_time_hours }}u printtijd</span> <span>{{ $product->print_time_hours }}u printtijd</span>
</div> </div>
@endif @endif
@@ -235,12 +235,12 @@
<!-- Inhoud --> <!-- Inhoud -->
<div class="p-3.5 sm:p-5 flex-1 flex flex-col justify-between space-y-3"> <div class="p-3.5 sm:p-5 flex-1 flex flex-col justify-between space-y-3">
<div> <div>
<h3 class="font-heading text-xs sm:text-base font-black text-slate-900 group-hover:text-rose-600 transition tracking-tight line-clamp-2"> <h3 class="font-heading text-xs sm:text-base font-black text-slate-900 group-hover:text-amber-700 transition tracking-tight line-clamp-2">
<a href="{{ route('product.show', $product->slug) }}"> <a href="{{ route('product.show', $product->slug) }}">
{{ $product->name }} {{ $product->name }}
</a> </a>
</h3> </h3>
<p class="hidden sm:block text-xs text-slate-500 mt-1 line-clamp-2 leading-relaxed"> <p class="hidden sm:block text-xs text-slate-500 mt-1 line-clamp-2 leading-relaxed font-normal">
{{ $product->short_description }} {{ $product->short_description }}
</p> </p>
</div> </div>
@@ -254,7 +254,7 @@
</span> </span>
</div> </div>
<a href="{{ route('product.show', $product->slug) }}" class="px-3 sm:px-3.5 py-1.5 sm:py-2 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 text-[10px] sm:text-xs font-bold transition flex items-center space-x-1"> <a href="{{ route('product.show', $product->slug) }}" class="px-3 sm:px-3.5 py-1.5 sm:py-2 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 text-[10px] sm:text-xs font-bold transition flex items-center space-x-1 shadow-xs">
<span>Kies Kleur</span> <span>Kies Kleur</span>
<i data-lucide="chevron-right" class="w-3 h-3"></i> <i data-lucide="chevron-right" class="w-3 h-3"></i>
</a> </a>
@@ -267,18 +267,18 @@
</div> </div>
</section> </section>
<!-- Heldere Kwaliteits & Voordelen Banner (Aanpasbaar via Admin) --> <!-- Subtiele Kwaliteits & Voordelen Banner (Aanpasbaar via Admin) -->
<section class="py-12 sm:py-16 bg-white border-t border-slate-200/80"> <section class="py-12 sm:py-16 bg-white border-t border-slate-200/60">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center max-w-2xl mx-auto mb-10"> <div class="text-center max-w-2xl mx-auto mb-10">
<span class="text-xs font-bold uppercase tracking-wider text-rose-500"> <span class="text-xs font-bold uppercase tracking-wider text-amber-700">
{{ setting('usp_tag', 'Waarom Jouwdriedee.nl?') }} {{ setting('usp_tag', 'Waarom Jouwdriedee.nl?') }}
</span> </span>
<h2 class="font-heading text-2xl sm:text-3xl font-black text-slate-900 tracking-tight mt-1"> <h2 class="font-heading text-2xl sm:text-3xl font-black text-slate-900 tracking-tight mt-1">
{{ setting('usp_title', 'Kwaliteit en passie in elke printlaag') }} {{ setting('usp_title', 'Kwaliteit en passie in elke printlaag') }}
</h2> </h2>
<p class="text-xs sm:text-sm text-slate-500 mt-2 leading-relaxed"> <p class="text-xs sm:text-sm text-slate-500 mt-2 leading-relaxed font-normal">
{{ setting('usp_subtitle', 'Geen vage levertijden of ruwe prints. Elk model wordt in eigen werkplaats gekalibreerd, geprint en gecontroleerd voordat het veilig verpakt op de post gaat.') }} {{ setting('usp_subtitle', 'Geen vage levertijden of ruwe prints. Elk model wordt in eigen werkplaats gekalibreerd, geprint en gecontroleerd voordat het veilig verpakt op de post gaat.') }}
</p> </p>
</div> </div>
@@ -292,7 +292,7 @@
<h3 class="font-heading font-black text-slate-900 text-sm"> <h3 class="font-heading font-black text-slate-900 text-sm">
{{ setting('usp_1_title', 'Bambu Lab Precisie') }} {{ setting('usp_1_title', 'Bambu Lab Precisie') }}
</h3> </h3>
<p class="text-xs text-slate-500 leading-relaxed font-medium"> <p class="text-xs text-slate-500 leading-relaxed font-normal">
{{ setting('usp_1_desc', 'Haarscherpe lagen vanaf 0.12mm. Vaste toleranties waardoor scharnieren moeiteloos bewegen.') }} {{ setting('usp_1_desc', 'Haarscherpe lagen vanaf 0.12mm. Vaste toleranties waardoor scharnieren moeiteloos bewegen.') }}
</p> </p>
</div> </div>
@@ -305,7 +305,7 @@
<h3 class="font-heading font-black text-slate-900 text-sm"> <h3 class="font-heading font-black text-slate-900 text-sm">
{{ setting('usp_2_title', '16+ Kleurvariaties') }} {{ setting('usp_2_title', '16+ Kleurvariaties') }}
</h3> </h3>
<p class="text-xs text-slate-500 leading-relaxed font-medium"> <p class="text-xs text-slate-500 leading-relaxed font-normal">
{{ setting('usp_2_desc', 'Van glanzend Silk Goud en Galaxy Zwart tot mat pastel en glow-in-the-dark filament.') }} {{ setting('usp_2_desc', 'Van glanzend Silk Goud en Galaxy Zwart tot mat pastel en glow-in-the-dark filament.') }}
</p> </p>
</div> </div>
@@ -318,7 +318,7 @@
<h3 class="font-heading font-black text-slate-900 text-sm"> <h3 class="font-heading font-black text-slate-900 text-sm">
{{ setting('usp_3_title', 'Veilig & Duurzaam PLA+') }} {{ setting('usp_3_title', 'Veilig & Duurzaam PLA+') }}
</h3> </h3>
<p class="text-xs text-slate-500 leading-relaxed font-medium"> <p class="text-xs text-slate-500 leading-relaxed font-normal">
{{ setting('usp_3_desc', 'Gemaakt van hernieuwbare plantaardige zetmelen. Geurloos, gifvrij en stevig in gebruik.') }} {{ setting('usp_3_desc', 'Gemaakt van hernieuwbare plantaardige zetmelen. Geurloos, gifvrij en stevig in gebruik.') }}
</p> </p>
</div> </div>
@@ -331,14 +331,14 @@
<h3 class="font-heading font-black text-slate-900 text-sm"> <h3 class="font-heading font-black text-slate-900 text-sm">
{{ setting('usp_4_title', 'Snelle Verzending') }} {{ setting('usp_4_title', 'Snelle Verzending') }}
</h3> </h3>
<p class="text-xs text-slate-500 leading-relaxed font-medium"> <p class="text-xs text-slate-500 leading-relaxed font-normal">
{{ setting('usp_4_desc', 'Met zorg verpakt in beschermend materiaal. Binnen 2-3 werkdagen verzonden via PostNL.') }} {{ setting('usp_4_desc', 'Met zorg verpakt in beschermend materiaal. Binnen 2-3 werkdagen verzonden via PostNL.') }}
</p> </p>
</div> </div>
</div> </div>
<div class="mt-8 text-center"> <div class="mt-8 text-center">
<a href="{{ route('catalog') }}" class="inline-flex items-center space-x-2 px-6 py-3 rounded-2xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-black text-xs transition shadow-xs"> <a href="{{ route('catalog') }}" class="inline-flex items-center space-x-2 px-6 py-3 rounded-2xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-bold text-xs transition shadow-xs">
<span>{{ setting('usp_btn_text', 'Bekijk alle 3D prints in de winkel') }}</span> <span>{{ setting('usp_btn_text', 'Bekijk alle 3D prints in de winkel') }}</span>
<i data-lucide="arrow-right" class="w-4 h-4"></i> <i data-lucide="arrow-right" class="w-4 h-4"></i>
</a> </a>
+43 -43
View File
@@ -4,17 +4,17 @@
@section('content') @section('content')
<!-- Breadcrumbs --> <!-- Breadcrumbs -->
<div class="bg-amber-50/50 border-b border-slate-200/80 py-2.5 sm:py-3"> <div class="bg-white/80 border-b border-slate-200/60 py-2.5 sm:py-3">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-xs font-bold text-slate-500 flex items-center space-x-1.5 overflow-x-auto whitespace-nowrap"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-xs font-semibold text-slate-500 flex items-center space-x-1.5 overflow-x-auto whitespace-nowrap">
<a href="{{ route('home') }}" class="hover:text-rose-600">Home 🏠</a> <a href="{{ route('home') }}" class="hover:text-amber-700">Home</a>
<span>/</span> <span>/</span>
<a href="{{ route('catalog') }}" class="hover:text-rose-600">Catalogus 🎁</a> <a href="{{ route('catalog') }}" class="hover:text-amber-700">Catalogus</a>
@if($product->category) @if($product->category)
<span>/</span> <span>/</span>
<a href="{{ route('catalog', ['category' => $product->category->slug]) }}" class="hover:text-rose-600">{{ $product->category->name }}</a> <a href="{{ route('catalog', ['category' => $product->category->slug]) }}" class="hover:text-amber-700">{{ $product->category->name }}</a>
@endif @endif
<span>/</span> <span>/</span>
<span class="font-black text-slate-900 truncate">{{ $product->name }}</span> <span class="font-bold text-slate-900 truncate">{{ $product->name }}</span>
</div> </div>
</div> </div>
@@ -92,7 +92,7 @@
} }
}"> }">
<div class="rounded-3xl bg-white border border-slate-100 overflow-hidden shadow-sm relative group"> <div class="rounded-3xl bg-white border border-slate-200/80 overflow-hidden shadow-xs relative group">
<div class="relative h-72 sm:h-96 md:h-112 bg-slate-100 flex items-center justify-center overflow-hidden"> <div class="relative h-72 sm:h-96 md:h-112 bg-slate-100 flex items-center justify-center overflow-hidden">
<img <img
:src="activeImage" :src="activeImage"
@@ -102,8 +102,8 @@
<div class="absolute top-3 left-3 flex flex-col gap-1.5 z-10"> <div class="absolute top-3 left-3 flex flex-col gap-1.5 z-10">
@if($product->is_featured) @if($product->is_featured)
<span class="px-3 py-1 rounded-full bg-gradient-to-r from-rose-500 to-amber-500 text-white font-heading text-xs font-black shadow-sm flex items-center space-x-1"> <span class="px-3 py-1 rounded-full bg-white/95 backdrop-blur-xs text-amber-800 border border-amber-200 text-xs font-bold shadow-xs flex items-center space-x-1">
<span>🔥</span> <span>★</span>
<span>Populair</span> <span>Populair</span>
</span> </span>
@endif @endif
@@ -112,7 +112,7 @@
<!-- Foto Counter Badge --> <!-- Foto Counter Badge -->
@if(count($gallery) > 1) @if(count($gallery) > 1)
<div class="absolute bottom-3 right-3 z-10"> <div class="absolute bottom-3 right-3 z-10">
<span class="px-2.5 py-1 rounded-full bg-slate-900/80 backdrop-blur-md text-white font-mono text-[11px] font-bold shadow-sm"> <span class="px-2.5 py-1 rounded-full bg-white/95 backdrop-blur-xs text-slate-700 border border-slate-200 font-mono text-[11px] font-bold shadow-xs">
<span x-text="currentIndex + 1"></span> / {{ count($gallery) }} <span x-text="currentIndex + 1"></span> / {{ count($gallery) }}
</span> </span>
</div> </div>
@@ -147,7 +147,7 @@
type="button" type="button"
@click="selectImage(idx)" @click="selectImage(idx)"
class="relative w-16 h-16 sm:w-20 sm:h-20 rounded-2xl overflow-hidden shrink-0 border-2 transition-all duration-200 cursor-pointer" class="relative w-16 h-16 sm:w-20 sm:h-20 rounded-2xl overflow-hidden shrink-0 border-2 transition-all duration-200 cursor-pointer"
:class="currentIndex === idx ? 'border-amber-400 ring-2 ring-amber-400/40 shadow-sm scale-105' : 'border-slate-200 opacity-60 hover:opacity-100 hover:border-slate-400'" :class="currentIndex === idx ? 'border-amber-400 ring-2 ring-amber-400/40 shadow-xs scale-105' : 'border-slate-200 opacity-60 hover:opacity-100 hover:border-slate-300'"
> >
<img :src="img" alt="Thumbnail" class="w-full h-full object-cover"> <img :src="img" alt="Thumbnail" class="w-full h-full object-cover">
</button> </button>
@@ -157,35 +157,35 @@
</div> </div>
<!-- Print Specificaties Card (Minder lomp, verfijnd) --> <!-- Print Specificaties Card (Minder lomp, verfijnd) -->
<div class="p-4 sm:p-6 rounded-3xl bg-white border border-slate-100 shadow-xs space-y-3"> <div class="p-4 sm:p-6 rounded-2xl bg-white border border-slate-200/80 shadow-xs space-y-3">
<h3 class="font-heading text-xs font-black text-slate-900 uppercase tracking-wider flex items-center space-x-1.5"> <h3 class="font-heading text-xs font-black text-slate-900 uppercase tracking-wider flex items-center space-x-1.5">
<span>🖨️</span> <span>🖨️</span>
<span>Print Details & Formaat</span> <span>Print Details & Formaat</span>
</h3> </h3>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-2 sm:gap-3 text-xs"> <div class="grid grid-cols-2 sm:grid-cols-4 gap-2 sm:gap-3 text-xs">
<div class="p-3 rounded-2xl bg-slate-50 text-center"> <div class="p-3 rounded-xl bg-slate-50 border border-slate-100 text-center">
<span class="text-slate-400 block text-[10px] uppercase font-bold">📏 Formaat</span> <span class="text-slate-400 block text-[10px] uppercase font-bold">Formaat</span>
<span class="font-black text-slate-800 mt-0.5 block font-heading">{{ $product->dimensions ?? 'Standaard' }}</span> <span class="font-black text-slate-800 mt-0.5 block font-heading">{{ $product->dimensions ?? 'Standaard' }}</span>
</div> </div>
<div class="p-3 rounded-2xl bg-slate-50 text-center"> <div class="p-3 rounded-xl bg-slate-50 border border-slate-100 text-center">
<span class="text-slate-400 block text-[10px] uppercase font-bold">⚖️ Gewicht</span> <span class="text-slate-400 block text-[10px] uppercase font-bold">Gewicht</span>
<span class="font-black text-slate-800 mt-0.5 block font-heading">~{{ $product->weight_grams ?? '80' }}g</span> <span class="font-black text-slate-800 mt-0.5 block font-heading">~{{ $product->weight_grams ?? '80' }}g</span>
</div> </div>
<div class="p-3 rounded-2xl bg-slate-50 text-center"> <div class="p-3 rounded-xl bg-slate-50 border border-slate-100 text-center">
<span class="text-slate-400 block text-[10px] uppercase font-bold">⏱️ Printtijd</span> <span class="text-slate-400 block text-[10px] uppercase font-bold">Printtijd</span>
<span class="font-black text-slate-800 mt-0.5 block font-heading">{{ $product->print_time_hours ?? '4.0' }} uur</span> <span class="font-black text-slate-800 mt-0.5 block font-heading">{{ $product->print_time_hours ?? '4.0' }} u</span>
</div> </div>
<div class="p-3 rounded-2xl bg-slate-50 text-center"> <div class="p-3 rounded-xl bg-slate-50 border border-slate-100 text-center">
<span class="text-slate-400 block text-[10px] uppercase font-bold">🔍 Resolutie</span> <span class="text-slate-400 block text-[10px] uppercase font-bold">Resolutie</span>
<span class="font-black text-slate-800 mt-0.5 block font-heading">0.12 - 0.16 mm</span> <span class="font-black text-slate-800 mt-0.5 block font-heading">0.12 - 0.16 mm</span>
</div> </div>
</div> </div>
<div class="pt-2 flex flex-col sm:flex-row items-center justify-between text-[11px] text-slate-500 font-bold border-t border-slate-100 gap-1.5"> <div class="pt-2 flex flex-col sm:flex-row items-center justify-between text-[11px] text-slate-500 font-medium border-t border-slate-100 gap-1.5">
<span class="flex items-center space-x-1"> <span class="flex items-center space-x-1">
<span>✨</span> <span>✨</span>
<span>Geprint op precisie Bambu Lab printers</span> <span>Geprint op precisie Bambu Lab printers</span>
@@ -198,12 +198,12 @@
</div> </div>
<!-- Productomschrijving --> <!-- Productomschrijving -->
<div class="p-4 sm:p-6 rounded-3xl bg-white border border-slate-100 shadow-xs space-y-2"> <div class="p-4 sm:p-6 rounded-2xl bg-white border border-slate-200/80 shadow-xs space-y-2">
<h3 class="font-heading text-sm font-black text-slate-900 flex items-center space-x-1.5"> <h3 class="font-heading text-sm font-black text-slate-900 flex items-center space-x-1.5">
<span>📝</span> <span>📝</span>
<span>Over deze 3D print</span> <span>Over deze 3D print</span>
</h3> </h3>
<div class="text-xs text-slate-600 leading-relaxed space-y-1.5 font-medium"> <div class="text-xs text-slate-600 leading-relaxed space-y-1.5 font-normal">
{!! nl2br(strip_tags($product->description, '<p><br><strong><b><em><i><u><ul><ol><li><h3><h4>')) !!} {!! nl2br(strip_tags($product->description, '<p><br><strong><b><em><i><u><ul><ol><li><h3><h4>')) !!}
</div> </div>
</div> </div>
@@ -211,11 +211,11 @@
<!-- Rechterkant: Interactive Configurator & Bestelmodule --> <!-- Rechterkant: Interactive Configurator & Bestelmodule -->
<div class="lg:col-span-6 space-y-6"> <div class="lg:col-span-6 space-y-6">
<div class="p-5 sm:p-8 rounded-3xl bg-white border border-slate-100 shadow-lg shadow-slate-200/40 space-y-5 sm:space-y-6"> <div class="p-5 sm:p-8 rounded-3xl bg-white border border-slate-200/80 shadow-sm space-y-5 sm:space-y-6">
<div> <div>
@if($product->category) @if($product->category)
<span class="inline-block px-2.5 py-0.5 rounded-full bg-amber-100 text-[10px] sm:text-xs font-black text-amber-900 mb-1.5"> <span class="inline-block px-2.5 py-0.5 rounded-full bg-amber-100 text-[10px] sm:text-xs font-bold text-amber-900 mb-1.5">
{{ $product->category->name }} {{ $product->category->name }}
</span> </span>
@endif @endif
@@ -224,7 +224,7 @@
</div> </div>
<!-- Prijs Display --> <!-- Prijs Display -->
<div class="p-3.5 sm:p-4 rounded-2xl bg-gradient-to-r from-amber-50 to-rose-50 border border-amber-200/60 flex items-center justify-between"> <div class="p-3.5 sm:p-4 rounded-2xl bg-amber-50/70 border border-amber-200/60 flex items-center justify-between">
<div> <div>
<span class="text-[10px] text-slate-500 block font-bold uppercase">Stukprijs</span> <span class="text-[10px] text-slate-500 block font-bold uppercase">Stukprijs</span>
<div class="flex items-baseline space-x-1.5"> <div class="flex items-baseline space-x-1.5">
@@ -233,7 +233,7 @@
</div> </div>
</div> </div>
<div class="text-right"> <div class="text-right">
<span class="px-2.5 py-1 rounded-full bg-emerald-100 text-emerald-800 text-[11px] font-black inline-flex items-center space-x-1"> <span class="px-2.5 py-1 rounded-full bg-emerald-100 text-emerald-800 text-[11px] font-bold inline-flex items-center space-x-1">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500"></span> <span class="w-1.5 h-1.5 rounded-full bg-emerald-500"></span>
<span>Print-klaar</span> <span>Print-klaar</span>
</span> </span>
@@ -253,8 +253,8 @@
<!-- 1. Materiaal Keuze --> <!-- 1. Materiaal Keuze -->
<div> <div>
<div class="flex items-center justify-between mb-1.5"> <div class="flex items-center justify-between mb-1.5">
<label class="text-xs font-black text-slate-800 uppercase tracking-wider">1. Kies Materiaal</label> <label class="text-xs font-bold text-slate-800 uppercase tracking-wider">1. Kies Materiaal</label>
<span class="text-xs font-black text-rose-500" x-text="selectedMaterial.toUpperCase()"></span> <span class="text-xs font-bold text-amber-700" x-text="selectedMaterial.toUpperCase()"></span>
</div> </div>
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
@foreach($product->available_materials ?? ['pla'] as $matCode) @foreach($product->available_materials ?? ['pla'] as $matCode)
@@ -263,7 +263,7 @@
<button <button
type="button" type="button"
@click="selectedMaterial = '{{ $mat->code }}'" @click="selectedMaterial = '{{ $mat->code }}'"
:class="selectedMaterial === '{{ $mat->code }}' ? 'border-amber-400 bg-amber-50 ring-2 ring-amber-400 font-black text-slate-950' : 'border-slate-200 bg-white hover:bg-slate-50 text-slate-700'" :class="selectedMaterial === '{{ $mat->code }}' ? 'border-amber-400 bg-amber-50/80 ring-2 ring-amber-400/50 font-bold text-slate-950 shadow-xs' : 'border-slate-200 bg-white hover:bg-slate-50 text-slate-700'"
class="p-2.5 rounded-xl border text-left transition flex flex-col justify-between cursor-pointer" class="p-2.5 rounded-xl border text-left transition flex flex-col justify-between cursor-pointer"
> >
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
@@ -272,7 +272,7 @@
<span class="text-[9px] px-1 py-0.2 rounded bg-slate-100 font-mono text-slate-600">+{{ round(($mat->price_multiplier - 1) * 100) }}%</span> <span class="text-[9px] px-1 py-0.2 rounded bg-slate-100 font-mono text-slate-600">+{{ round(($mat->price_multiplier - 1) * 100) }}%</span>
@endif @endif
</div> </div>
<p class="text-[10px] text-slate-500 mt-0.5 line-clamp-1">{{ $mat->description }}</p> <p class="text-[10px] text-slate-500 mt-0.5 line-clamp-1 font-normal">{{ $mat->description }}</p>
</button> </button>
@endif @endif
@endforeach @endforeach
@@ -282,15 +282,15 @@
<!-- 2. Kleur Selectie (Swatches met mooie afwerking) --> <!-- 2. Kleur Selectie (Swatches met mooie afwerking) -->
<div> <div>
<div class="flex items-center justify-between mb-1.5"> <div class="flex items-center justify-between mb-1.5">
<label class="text-xs font-black text-slate-800 uppercase tracking-wider">2. Kies Jouw Kleur</label> <label class="text-xs font-bold text-slate-800 uppercase tracking-wider">2. Kies Jouw Kleur</label>
<span class="text-xs font-bold text-rose-600" x-text="selectedColor"></span> <span class="text-xs font-bold text-amber-700" x-text="selectedColor"></span>
</div> </div>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
@foreach($allColors as $c) @foreach($allColors as $c)
<button <button
type="button" type="button"
@click="selectedColor = '{{ $c->name }}'" @click="selectedColor = '{{ $c->name }}'"
:class="selectedColor === '{{ $c->name }}' ? 'ring-3 ring-rose-500 ring-offset-2 scale-110' : 'hover:scale-105 border-slate-300'" :class="selectedColor === '{{ $c->name }}' ? 'ring-2 ring-amber-400 ring-offset-2 scale-110 shadow-xs' : 'hover:scale-105 border-slate-200'"
class="w-8 h-8 rounded-xl border shadow-xs transition flex items-center justify-center cursor-pointer relative" class="w-8 h-8 rounded-xl border shadow-xs transition flex items-center justify-center cursor-pointer relative"
style="background-color: {{ $c->hex }};" style="background-color: {{ $c->hex }};"
title="{{ $c->name }}" title="{{ $c->name }}"
@@ -306,14 +306,14 @@
<!-- 3. Formaat & Schaal --> <!-- 3. Formaat & Schaal -->
<div> <div>
<div class="flex items-center justify-between mb-1.5"> <div class="flex items-center justify-between mb-1.5">
<label class="text-xs font-black text-slate-800 uppercase tracking-wider">3. Formaat / Schaal</label> <label class="text-xs font-bold text-slate-800 uppercase tracking-wider">3. Formaat / Schaal</label>
<span class="text-xs font-bold text-slate-700" x-text="selectedScale"></span> <span class="text-xs font-bold text-slate-700" x-text="selectedScale"></span>
</div> </div>
<div class="grid grid-cols-3 gap-2 text-xs"> <div class="grid grid-cols-3 gap-2 text-xs">
<button <button
type="button" type="button"
@click="selectedScale = '100%'" @click="selectedScale = '100%'"
:class="selectedScale === '100%' ? 'border-rose-400 bg-rose-50 ring-2 ring-rose-300 font-black text-slate-900' : 'border-slate-200 font-bold hover:bg-slate-50'" :class="selectedScale === '100%' ? 'border-amber-400 bg-amber-50/80 ring-2 ring-amber-400/50 font-bold text-slate-900 shadow-xs' : 'border-slate-200 font-medium hover:bg-slate-50 text-slate-700'"
class="p-2.5 rounded-xl border text-center transition cursor-pointer" class="p-2.5 rounded-xl border text-center transition cursor-pointer"
> >
<span>100% (Normaal)</span> <span>100% (Normaal)</span>
@@ -321,7 +321,7 @@
<button <button
type="button" type="button"
@click="selectedScale = '150%'" @click="selectedScale = '150%'"
:class="selectedScale === '150%' ? 'border-rose-400 bg-rose-50 ring-2 ring-rose-300 font-black text-slate-900' : 'border-slate-200 font-bold hover:bg-slate-50'" :class="selectedScale === '150%' ? 'border-amber-400 bg-amber-50/80 ring-2 ring-amber-400/50 font-bold text-slate-900 shadow-xs' : 'border-slate-200 font-medium hover:bg-slate-50 text-slate-700'"
class="p-2.5 rounded-xl border text-center transition cursor-pointer" class="p-2.5 rounded-xl border text-center transition cursor-pointer"
> >
<span>150% (+45%)</span> <span>150% (+45%)</span>
@@ -329,7 +329,7 @@
<button <button
type="button" type="button"
@click="selectedScale = '200%'" @click="selectedScale = '200%'"
:class="selectedScale === '200%' ? 'border-rose-400 bg-rose-50 ring-2 ring-rose-300 font-black text-slate-900' : 'border-slate-200 font-bold hover:bg-slate-50'" :class="selectedScale === '200%' ? 'border-amber-400 bg-amber-50/80 ring-2 ring-amber-400/50 font-bold text-slate-900 shadow-xs' : 'border-slate-200 font-medium hover:bg-slate-50 text-slate-700'"
class="p-2.5 rounded-xl border text-center transition cursor-pointer" class="p-2.5 rounded-xl border text-center transition cursor-pointer"
> >
<span>200% (+95%)</span> <span>200% (+95%)</span>
@@ -342,19 +342,19 @@
<div class="flex items-center space-x-2.5"> <div class="flex items-center space-x-2.5">
<!-- Stepper --> <!-- Stepper -->
<div class="flex items-center border border-slate-200 rounded-xl bg-slate-50 overflow-hidden"> <div class="flex items-center border border-slate-200 rounded-xl bg-slate-50 overflow-hidden">
<button type="button" @click="if (quantity > 1) quantity--" class="px-3 py-2.5 hover:bg-slate-200 transition text-slate-700 font-black cursor-pointer text-sm">-</button> <button type="button" @click="if (quantity > 1) quantity--" class="px-3 py-2.5 hover:bg-slate-200 transition text-slate-700 font-bold cursor-pointer text-sm">-</button>
<span class="px-3.5 py-2.5 font-heading font-black text-xs text-slate-900" x-text="quantity"></span> <span class="px-3.5 py-2.5 font-heading font-black text-xs text-slate-900" x-text="quantity"></span>
<button type="button" @click="if (quantity < 50) quantity++" class="px-3 py-2.5 hover:bg-slate-200 transition text-slate-700 font-black cursor-pointer text-sm">+</button> <button type="button" @click="if (quantity < 50) quantity++" class="px-3 py-2.5 hover:bg-slate-200 transition text-slate-700 font-bold cursor-pointer text-sm">+</button>
</div> </div>
<!-- Knop Desktop --> <!-- Knop Desktop -->
<button type="submit" class="playful-btn flex-1 py-3 px-5 rounded-2xl bg-gradient-to-r from-amber-400 to-rose-400 hover:from-amber-500 hover:to-rose-500 text-slate-950 font-black text-xs sm:text-sm transition flex items-center justify-center space-x-1.5 shadow-md shadow-rose-500/20 cursor-pointer"> <button type="submit" class="flex-1 py-3 px-5 rounded-xl bg-amber-400 hover:bg-amber-300 text-slate-950 font-bold text-xs sm:text-sm transition flex items-center justify-center space-x-1.5 shadow-xs cursor-pointer">
<span>🛒</span> <span>🛒</span>
<span>In Winkelmandje • € <span x-text="calculatedPrice"></span></span> <span>In Winkelmandje • € <span x-text="calculatedPrice"></span></span>
</button> </button>
</div> </div>
<div class="flex items-center justify-center space-x-3 text-[11px] text-slate-500 font-bold pt-1"> <div class="flex items-center justify-center space-x-3 text-[11px] text-slate-500 font-medium pt-1">
<span>🚚 Gratis verzending vanaf €50</span> <span>🚚 Gratis verzending vanaf €50</span>
<span>•</span> <span>•</span>
<span>📦 PostNL Track & Trace</span> <span>📦 PostNL Track & Trace</span>
+1
View File
@@ -124,6 +124,7 @@ Route::prefix('admin')->name('admin.')->middleware(['auth', 'admin'])->group(fun
// Producten Beheer // Producten Beheer
Route::post('/products/upload-image', [AdminProductController::class, 'uploadImage'])->name('products.upload-image'); Route::post('/products/upload-image', [AdminProductController::class, 'uploadImage'])->name('products.upload-image');
Route::post('/products/calculate-price', [AdminProductController::class, 'calculatePrice'])->name('products.calculate-price');
Route::resource('products', AdminProductController::class)->except(['show']); Route::resource('products', AdminProductController::class)->except(['show']);
// Categorieën Beheer // Categorieën Beheer
+96
View File
@@ -7,6 +7,7 @@ use App\Models\Order;
use App\Models\Product; use App\Models\Product;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
use App\Services\PriceCalculatorService;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@@ -311,4 +312,99 @@ class AdminContentTest extends TestCase
$response->assertHeader('X-Content-Type-Options', 'nosniff'); $response->assertHeader('X-Content-Type-Options', 'nosniff');
$response->assertHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); $response->assertHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
} }
public function test_3d_print_price_calculator_service_calculates_correctly(): void
{
$calculator = app(PriceCalculatorService::class);
// 100 grams, 4 hours, 1 color, PLA, 40% margin
$result = $calculator->calculate(
weightGrams: 100,
printTimeHours: 4.0,
colorCount: 1,
material: 'pla',
customMarkupPercent: 40.0
);
$this->assertArrayHasKey('suggested_price', $result);
$this->assertArrayHasKey('suggested_price_formatted', $result);
$this->assertArrayHasKey('breakdown', $result);
$this->assertArrayHasKey('material_cost', $result['breakdown']);
$this->assertArrayHasKey('electricity_cost', $result['breakdown']);
$this->assertArrayHasKey('machine_cost', $result['breakdown']);
$this->assertArrayHasKey('total_cost_price', $result['breakdown']);
$this->assertGreaterThan(5.0, $result['suggested_price']);
// Multi-color test: 4 colors should cost more than 1 color due to purge waste & setup
$multiColorResult = $calculator->calculate(
weightGrams: 100,
printTimeHours: 4.0,
colorCount: 4,
material: 'pla',
customMarkupPercent: 40.0
);
$this->assertGreaterThan($result['suggested_price'], $multiColorResult['suggested_price']);
$this->assertGreaterThan($result['breakdown']['material_cost'], $multiColorResult['breakdown']['material_cost']);
}
public function test_admin_can_use_calculate_price_api_endpoint(): void
{
$response = $this->actingAs($this->admin)->postJson(route('admin.products.calculate-price'), [
'weight_grams' => 120,
'print_time_hours' => 5.5,
'color_count' => 2,
'material' => 'petg',
'markup_percent' => 50,
]);
$response->assertStatus(200);
$response->assertJsonStructure([
'success',
'data' => [
'inputs',
'rates',
'breakdown' => [
'effective_weight_grams',
'material_cost',
'electricity_cost',
'machine_cost',
'handling_cost',
'failure_cost',
'total_cost_price',
'profit_amount',
'price_excl_vat',
'vat_amount',
'price_incl_vat_exact',
],
'suggested_price',
'suggested_price_formatted',
],
]);
}
public function test_admin_can_save_calculator_tariffs_in_content_settings(): void
{
$response = $this->actingAs($this->admin)->post(route('admin.content.update'), [
'calc_filament_price_kg' => '22.50',
'calc_electricity_kwh' => '0.40',
'calc_printer_wattage' => '180',
'calc_machine_hourly_rate' => '0.65',
'calc_handling_flat' => '2.00',
'calc_default_markup' => '45',
'calc_failure_rate' => '8',
]);
$response->assertRedirect(route('admin.content.index'));
Setting::flushCache();
$this->assertEquals(22.50, (float) Setting::get('calc_filament_price_kg'));
$this->assertEquals(0.40, (float) Setting::get('calc_electricity_kwh'));
$this->assertEquals(180, (int) Setting::get('calc_printer_wattage'));
$this->assertEquals(0.65, (float) Setting::get('calc_machine_hourly_rate'));
$this->assertEquals(2.00, (float) Setting::get('calc_handling_flat'));
$this->assertEquals(45, (int) Setting::get('calc_default_markup'));
$this->assertEquals(8, (int) Setting::get('calc_failure_rate'));
}
} }