Redesign logo & storefront, light subtle admin theme, and 3D print cost calculator
This commit is contained in:
@@ -3,390 +3,371 @@
|
||||
@section('title', 'Artikel Beheer (Geïmporteerde Modellen) | Jouwdriedee.nl Beheer')
|
||||
|
||||
@section('content')
|
||||
<div class="py-8 bg-slate-950 text-slate-100 min-h-screen" x-data="artikelBeheerApp()">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-6">
|
||||
<div class="space-y-6" x-data="artikelBeheerApp()">
|
||||
|
||||
<!-- 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>
|
||||
<div class="flex items-center space-x-2 text-xs text-amber-400 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>Staging & Curation</span>
|
||||
<span>•</span>
|
||||
<span>Geïmporteerde Modellen</span>
|
||||
</div>
|
||||
<h1 class="text-2xl sm:text-3xl font-black text-white 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">
|
||||
<i data-lucide="layers" class="w-6 h-6"></i>
|
||||
</span>
|
||||
<span>Artikel Beheer</span>
|
||||
</h1>
|
||||
<p class="mt-1 text-sm text-slate-400">
|
||||
Beoordeel automatisch binnengehaalde MakerWorld modellen. Stel de gewenste categorie en verkoopprijs in en publiceer ze met 1 klik naar de webshop!
|
||||
</p>
|
||||
<!-- 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-200/80">
|
||||
<div>
|
||||
<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 h-2 rounded-full bg-amber-500 animate-pulse"></span>
|
||||
<span>Staging & Curation</span>
|
||||
<span>•</span>
|
||||
<span>Geïmporteerde Modellen</span>
|
||||
</div>
|
||||
<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-amber-500 rounded-2xl text-slate-950 shadow-sm">
|
||||
<i data-lucide="layers" class="w-6 h-6"></i>
|
||||
</span>
|
||||
<span>Artikel Beheer</span>
|
||||
</h1>
|
||||
<p class="mt-1 text-xs sm:text-sm text-slate-500">
|
||||
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>
|
||||
</div>
|
||||
|
||||
<!-- Header Quick Actions -->
|
||||
<div class="flex items-center space-x-2.5 self-start md:self-auto">
|
||||
<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-600"></i>
|
||||
<span>Collecties & Sync</span>
|
||||
</a>
|
||||
|
||||
<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>
|
||||
<span>Actieve Producten</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status Filter Tabs -->
|
||||
<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-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>
|
||||
<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-100 text-slate-600' }}">
|
||||
{{ $countDraft ?? 0 }}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<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 {{ ($tab ?? 'draft') === 'published' ? 'bg-emerald-200' : 'bg-emerald-500' }}"></span>
|
||||
<span>Reeds Gepubliceerd in Webshop</span>
|
||||
<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 }}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<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>
|
||||
<span>Alle Binnengehaalde Modellen</span>
|
||||
<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 }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Filter & Search Toolbar -->
|
||||
<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-2.5 flex-1">
|
||||
<!-- Search input -->
|
||||
<div class="relative min-w-[220px] flex-1">
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
value="{{ request('q') }}"
|
||||
placeholder="Zoek op titel, maker of model-ID..."
|
||||
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>
|
||||
|
||||
<!-- Header Quick Actions -->
|
||||
<div class="flex items-center space-x-3">
|
||||
<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">
|
||||
<i data-lucide="refresh-cw" class="w-4 h-4 text-amber-400"></i>
|
||||
<span>Collecties & Auto-Sync</span>
|
||||
</a>
|
||||
<!-- Collection dropdown filter -->
|
||||
<div class="w-52">
|
||||
<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>
|
||||
@foreach($collections as $col)
|
||||
<option value="{{ $col->id }}" {{ request('collection_id') == $col->id ? 'selected' : '' }}>
|
||||
{{ $col->name }} ({{ $col->items_count }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<i data-lucide="box" class="w-4 h-4"></i>
|
||||
<span>Actieve Producten</span>
|
||||
<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
|
||||
</button>
|
||||
|
||||
@if(request()->hasAny(['q', 'collection_id']))
|
||||
<a href="{{ route('admin.makerworld.staging') }}" class="text-xs text-rose-600 hover:underline font-bold">
|
||||
Wissen
|
||||
</a>
|
||||
@endif
|
||||
</form>
|
||||
|
||||
<div class="flex items-center space-x-2 text-xs">
|
||||
<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
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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-md">
|
||||
<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>
|
||||
<span x-text="selectedIds.length + ' artikelen geselecteerd'"></span>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.makerworld.staging.bulk-publish') }}" method="POST" class="flex flex-wrap items-center gap-3">
|
||||
@csrf
|
||||
<template x-for="id in selectedIds" :key="id">
|
||||
<input type="hidden" name="product_ids[]" :value="id">
|
||||
</template>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-xs font-bold uppercase">Categorie:</label>
|
||||
<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)
|
||||
<option value="{{ $cat->id }}">{{ $cat->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-xs font-bold uppercase">Winstmarge %:</label>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<span>Publiceer Selectie naar Webshop</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form action="{{ route('admin.makerworld.staging.bulk-reject') }}" method="POST" onsubmit="return confirm('Weet je zeker dat je de geselecteerde concepten wilt verwijderen?');">
|
||||
@csrf
|
||||
<template x-for="id in selectedIds" :key="id">
|
||||
<input type="hidden" name="product_ids[]" :value="id">
|
||||
</template>
|
||||
<button type="submit" class="px-3 py-2 bg-rose-600 hover:bg-rose-700 text-white font-bold text-xs rounded-xl transition flex items-center space-x-1">
|
||||
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
|
||||
<span>Verwijderen</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Select All Trigger -->
|
||||
@if($stagedProducts->isNotEmpty())
|
||||
<div class="flex items-center justify-between text-xs text-slate-500 px-1">
|
||||
<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>
|
||||
<span x-text="selectedIds.length === {{ $stagedProducts->count() }} ? 'Deselecteer alles' : 'Selecteer alle {{ $stagedProducts->count() }} modellen op deze pagina'"></span>
|
||||
</button>
|
||||
<span>Pagina {{ $stagedProducts->currentPage() }} van {{ $stagedProducts->lastPage() }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Staged Products Grid -->
|
||||
@if($stagedProducts->isEmpty())
|
||||
<div class="text-center py-16 bg-white rounded-3xl border border-slate-200/80 p-8 shadow-sm">
|
||||
<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-8 h-8"></i>
|
||||
</div>
|
||||
<h3 class="text-lg font-black text-slate-900">Geen concepten in de wachtrij!</h3>
|
||||
<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.
|
||||
</p>
|
||||
<div class="mt-5 flex items-center justify-center space-x-3">
|
||||
<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
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
@foreach($stagedProducts as $product)
|
||||
@php
|
||||
$printInfo = $product->print_info ?? [];
|
||||
@endphp
|
||||
<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 -->
|
||||
<div>
|
||||
<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">
|
||||
|
||||
<!-- 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 -->
|
||||
<div class="flex items-center space-x-2 border-b border-slate-800 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' }}">
|
||||
<i data-lucide="layers" class="w-3.5 h-3.5"></i>
|
||||
<span>Wacht op Beoordeling (Concepten)</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' }}">
|
||||
{{ $countDraft ?? 0 }}
|
||||
</span>
|
||||
</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' }}">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400"></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' }}">
|
||||
{{ $countPublished ?? 0 }}
|
||||
</span>
|
||||
</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' }}">
|
||||
<i data-lucide="download-cloud" class="w-3.5 h-3.5"></i>
|
||||
<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' }}">
|
||||
{{ $countAll ?? 0 }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 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">
|
||||
<form method="GET" action="{{ route('admin.makerworld.staging') }}" class="flex flex-wrap items-center gap-3 flex-1">
|
||||
<!-- Search input -->
|
||||
<div class="relative min-w-[240px] 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
|
||||
type="text"
|
||||
name="q"
|
||||
value="{{ request('q') }}"
|
||||
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"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Collection dropdown filter -->
|
||||
<div class="w-48">
|
||||
<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">
|
||||
<option value="">Alle collecties</option>
|
||||
@foreach($collections as $col)
|
||||
<option value="{{ $col->id }}" {{ request('collection_id') == $col->id ? 'selected' : '' }}>
|
||||
{{ $col->name }} ({{ $col->items_count }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</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">
|
||||
Filteren
|
||||
</button>
|
||||
|
||||
@if(request()->hasAny(['q', 'collection_id']))
|
||||
<a href="{{ route('admin.makerworld.staging') }}" class="text-xs text-rose-400 hover:underline font-bold">
|
||||
Filters wissen
|
||||
</a>
|
||||
@endif
|
||||
</form>
|
||||
|
||||
<div class="flex items-center space-x-3 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">
|
||||
{{ $totalStaged }} concepten wachten op beoordeling
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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 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>
|
||||
<span x-text="selectedIds.length + ' artikelen geselecteerd'"></span>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.makerworld.staging.bulk-publish') }}" method="POST" class="flex flex-wrap items-center gap-3">
|
||||
@csrf
|
||||
<template x-for="id in selectedIds" :key="id">
|
||||
<input type="hidden" name="product_ids[]" :value="id">
|
||||
</template>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-xs font-black 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">
|
||||
@foreach($categories as $cat)
|
||||
<option value="{{ $cat->id }}">{{ $cat->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-xs font-black uppercase">Prijs-marge %:</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">
|
||||
</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">
|
||||
<i data-lucide="check" class="w-4 h-4"></i>
|
||||
<span>Publiceer Selectie naar Webshop</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form action="{{ route('admin.makerworld.staging.bulk-reject') }}" method="POST" onsubmit="return confirm('Weet je zeker dat je de geselecteerde concepten wilt verwijderen?');">
|
||||
@csrf
|
||||
<template x-for="id in selectedIds" :key="id">
|
||||
<input type="hidden" name="product_ids[]" :value="id">
|
||||
</template>
|
||||
<button type="submit" class="px-3 py-2 bg-rose-600 hover:bg-rose-700 text-white font-bold text-xs rounded-xl transition flex items-center space-x-1">
|
||||
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
|
||||
<span>Verwijderen</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Select All Trigger -->
|
||||
@if($stagedProducts->isNotEmpty())
|
||||
<div class="flex items-center justify-between text-xs text-slate-400 px-2">
|
||||
<button type="button" @click="toggleSelectAll()" class="hover:text-amber-400 font-bold transition flex items-center space-x-1.5">
|
||||
<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>
|
||||
</button>
|
||||
<span>Pagina {{ $stagedProducts->currentPage() }} van {{ $stagedProducts->lastPage() }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Staged Products Grid -->
|
||||
@if($stagedProducts->isEmpty())
|
||||
<div class="text-center py-20 bg-slate-900/60 rounded-3xl border border-slate-800 p-8">
|
||||
<div class="w-20 h-20 rounded-3xl bg-slate-800 flex items-center justify-center text-slate-400 mx-auto mb-4">
|
||||
<i data-lucide="sparkles" class="w-10 h-10 text-amber-400"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-black text-white">Geen concepten in de wachtrij!</h3>
|
||||
<p class="text-xs text-slate-400 max-w-md mx-auto mt-2">
|
||||
Alle binnengehaalde modellen zijn al beoordeeld en gepubliceerd, of er zijn nog geen nieuwe modellen toegevoegd aan je gevolgde collecties.
|
||||
</p>
|
||||
<div class="mt-6 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">
|
||||
Bekijk Collecties & Auto-Sync
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach($stagedProducts as $product)
|
||||
@php
|
||||
$printInfo = $product->print_info ?? [];
|
||||
@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">
|
||||
|
||||
<!-- Top Image Preview & Gallery -->
|
||||
<div>
|
||||
<div class="relative aspect-4/3 bg-slate-950 overflow-hidden group">
|
||||
<img src="{{ $product->image_url }}" alt="{{ $product->name }}" class="w-full h-full object-cover">
|
||||
|
||||
<!-- Selection Checkbox -->
|
||||
<div class="absolute top-3 left-3 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">
|
||||
<input type="checkbox" value="{{ $product->id }}" x-model="selectedIds" class="w-4 h-4 text-amber-500 rounded focus:ring-0">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Draft Badge -->
|
||||
<div class="absolute top-3 right-3 flex flex-col items-end gap-1.5">
|
||||
<span class="px-2.5 py-1 rounded-full text-[10px] font-black uppercase tracking-wider bg-amber-400 text-slate-950 shadow-md">
|
||||
Concept
|
||||
</span>
|
||||
@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]">
|
||||
{{ $product->makerworldCollection->name }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- 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">
|
||||
<i data-lucide="external-link" class="w-3.5 h-3.5"></i>
|
||||
</a>
|
||||
<!-- Selection Checkbox -->
|
||||
<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-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">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Photo Thumbnails Strip (All Pulled Pictures) -->
|
||||
@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">
|
||||
<span class="text-[10px] font-bold text-slate-500 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">
|
||||
@foreach(array_slice($product->gallery_images, 0, 6) 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">
|
||||
@endforeach
|
||||
@if(count($product->gallery_images) > 6)
|
||||
<span class="text-[10px] text-slate-400 font-bold shrink-0">+{{ count($product->gallery_images) - 6 }} meer</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- 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>
|
||||
<span class="text-[10px] font-bold text-slate-500 block uppercase">Printtijd</span>
|
||||
<span class="font-black text-slate-200">
|
||||
{{ $product->print_time_hours ? $product->print_time_hours . ' uur' : '-' }}
|
||||
<!-- Draft Badge -->
|
||||
<div class="absolute top-2.5 right-2.5 flex flex-col items-end gap-1">
|
||||
<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
|
||||
</span>
|
||||
@if($product->makerworldCollection)
|
||||
<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 }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[10px] font-bold text-slate-500 block uppercase">Gewicht</span>
|
||||
<span class="font-black text-slate-200">
|
||||
{{ $product->weight_grams ? $product->weight_grams . ' gram' : '-' }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[10px] font-bold text-amber-400 block uppercase">Adviesprijs</span>
|
||||
<span class="font-black text-amber-400">
|
||||
{{ $product->formatted_price }}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Meta Details -->
|
||||
<div class="p-4 space-y-2">
|
||||
<div class="text-[11px] text-slate-400 flex items-center justify-between">
|
||||
<span class="truncate">Maker: <strong class="text-slate-300">{{ $product->makerworld_author ?? 'MakerWorld Maker' }}</strong></span>
|
||||
<span class="font-mono text-slate-500 text-[10px]">#{{ $product->makerworld_id }}</span>
|
||||
</div>
|
||||
<!-- MakerWorld Link -->
|
||||
<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>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Photo Thumbnails Strip (All Pulled Pictures) -->
|
||||
@if(!empty($product->gallery_images) && count($product->gallery_images) > 0)
|
||||
<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-400 uppercase tracking-wider shrink-0">Foto's:</span>
|
||||
<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, 5) as $galleryPic)
|
||||
<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
|
||||
@if(count($product->gallery_images) > 5)
|
||||
<span class="text-[10px] text-slate-500 font-bold shrink-0">+{{ count($product->gallery_images) - 5 }} meer</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Print Specifications Strip -->
|
||||
<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>
|
||||
<span class="text-[10px] font-bold text-slate-400 block uppercase">Printtijd</span>
|
||||
<span class="font-bold text-slate-700">
|
||||
{{ $product->print_time_hours ? $product->print_time_hours . ' u' : '-' }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[10px] font-bold text-slate-400 block uppercase">Gewicht</span>
|
||||
<span class="font-bold text-slate-700">
|
||||
{{ $product->weight_grams ? $product->weight_grams . ' g' : '-' }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[10px] font-bold text-amber-700 block uppercase">Adviesprijs</span>
|
||||
<span class="font-black text-slate-900 font-heading">
|
||||
{{ $product->formatted_price }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($product->status === 'published')
|
||||
<!-- Already Published Info Card -->
|
||||
<div class="p-4 bg-slate-950 border-t border-slate-800 space-y-3">
|
||||
<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="flex items-center space-x-1.5 font-bold">
|
||||
<i data-lucide="check-circle" class="w-4 h-4 text-emerald-400"></i>
|
||||
<span>Gepubliceerd in Webshop</span>
|
||||
</div>
|
||||
<span class="font-black text-white">{{ $product->formatted_price }}</span>
|
||||
<!-- Meta Details -->
|
||||
<div class="p-3.5 space-y-1.5">
|
||||
<div class="text-[11px] text-slate-500 flex items-center justify-between">
|
||||
<span class="truncate">Maker: <strong class="text-slate-700">{{ $product->makerworld_author ?? 'MakerWorld' }}</strong></span>
|
||||
<span class="font-mono text-slate-400 text-[10px]">#{{ $product->makerworld_id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($product->status === 'published')
|
||||
<!-- Already Published Info Card -->
|
||||
<div class="p-3.5 bg-slate-50/80 border-t border-slate-100 space-y-2.5">
|
||||
<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">
|
||||
<i data-lucide="check-circle" class="w-4 h-4 text-emerald-600"></i>
|
||||
<span>In Webshop</span>
|
||||
</div>
|
||||
<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">
|
||||
<i data-lucide="pencil" class="w-3.5 h-3.5"></i>
|
||||
<span>Bewerken</span>
|
||||
</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">
|
||||
<i data-lucide="external-link" class="w-4 h-4"></i>
|
||||
</a>
|
||||
<span class="font-black text-slate-900">{{ $product->formatted_price }}</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<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>
|
||||
<span>Bewerken & Prijscalculator</span>
|
||||
</a>
|
||||
<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>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<!-- Curation / Publish Form (Inline) -->
|
||||
<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
|
||||
|
||||
<!-- Editable Product Name -->
|
||||
<div>
|
||||
<label class="block text-[10px] font-bold uppercase tracking-wider text-slate-500 mb-1">Producttitel</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value="{{ old('name', $product->name) }}"
|
||||
required
|
||||
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 class="grid grid-cols-2 gap-2">
|
||||
<!-- Category selector -->
|
||||
<div>
|
||||
<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 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)
|
||||
<option value="{{ $cat->id }}" {{ $product->category_id == $cat->id ? 'selected' : '' }}>
|
||||
{{ $cat->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Retail Price -->
|
||||
<div>
|
||||
<label class="block text-[10px] font-bold uppercase tracking-wider text-slate-500 mb-1">Verkoopprijs (€)</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
name="base_price"
|
||||
value="{{ old('base_price', $product->base_price) }}"
|
||||
required
|
||||
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>
|
||||
@else
|
||||
<!-- 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">
|
||||
@csrf
|
||||
|
||||
<!-- Editable Product Name -->
|
||||
<div>
|
||||
<label class="block text-[10px] font-black uppercase tracking-wider text-slate-400 mb-1">Producttitel</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value="{{ old('name', $product->name) }}"
|
||||
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"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<!-- Category selector -->
|
||||
<div>
|
||||
<label class="block text-[10px] font-black uppercase tracking-wider text-slate-400 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">
|
||||
@foreach($categories as $cat)
|
||||
<option value="{{ $cat->id }}" {{ $product->category_id == $cat->id ? 'selected' : '' }}>
|
||||
{{ $cat->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<!-- Action Buttons -->
|
||||
<div class="pt-1.5 flex items-center justify-between gap-2">
|
||||
<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>
|
||||
<span>Publiceer naar Webshop</span>
|
||||
</button>
|
||||
|
||||
<!-- Retail Price -->
|
||||
<div>
|
||||
<label class="block text-[10px] font-black uppercase tracking-wider text-slate-400 mb-1">Verkoopprijs (€)</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
name="base_price"
|
||||
value="{{ old('base_price', $product->base_price) }}"
|
||||
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"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<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="sliders" class="w-4 h-4"></i>
|
||||
</a>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="pt-2 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">
|
||||
<i data-lucide="check" class="w-4 h-4"></i>
|
||||
<span>Publiceer naar Webshop</span>
|
||||
</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">
|
||||
<i data-lucide="image" class="w-4 h-4"></i>
|
||||
</a>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
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"
|
||||
title="Concept afwijzen & verwijderen"
|
||||
>
|
||||
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<!-- Standalone Delete Form for this product -->
|
||||
<form id="reject-form-{{ $product->id }}" action="{{ route('admin.makerworld.staging.reject', $product) }}" method="POST" onsubmit="return confirm('Weet je zeker dat je dit concept wilt verwijderen?');" class="hidden">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button
|
||||
type="submit"
|
||||
form="reject-form-{{ $product->id }}"
|
||||
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"
|
||||
>
|
||||
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="mt-8">
|
||||
{{ $stagedProducts->links() }}
|
||||
</div>
|
||||
@endif
|
||||
<!-- Standalone Delete Form for this product -->
|
||||
<form id="reject-form-{{ $product->id }}" action="{{ route('admin.makerworld.staging.reject', $product) }}" method="POST" onsubmit="return confirm('Weet je zeker dat je dit concept wilt verwijderen?');" class="hidden">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
</form>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="mt-6">
|
||||
{{ $stagedProducts->links() }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -406,4 +387,3 @@ function artikelBeheerApp() {
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
|
||||
Reference in New Issue
Block a user