Add email templates, page layouts, and shop themes (template system)
Deploy to Production (witstore.wende.it) / deploy (push) Successful in 7s
Deploy to Production (witstore.wende.it) / deploy (push) Successful in 7s
- E-Mail-Templates: DB-gespeicherte HTML-Templates mit {{placeholder}}-System,
Admin-UI zum Bearbeiten von Betreff und Body, alle 4 Mailables aktualisiert
- Seiten-Layouts: 4 Layouts für CMS-Seiten (Standard, Sidebar, Volle Breite, Landing),
Layout-Auswahl im Admin-Formular
- Shop-Themes: 5 Farbthemen via CSS-Variablen, Picker in Einstellungen
- Deployment: EmailTemplateSeeder wird automatisch beim Deploy ausgeführt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,33 @@
|
||||
<title>@yield('title', \App\Models\Setting::get('shop_name', 'Shop'))</title>
|
||||
<meta name="description" content="@yield('meta_description', '')">
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
@php
|
||||
$themes = config('themes');
|
||||
$themeName = \App\Models\Setting::get('theme_name', 'default');
|
||||
$theme = $themes[$themeName] ?? $themes['default'];
|
||||
@endphp
|
||||
<style>
|
||||
:root {
|
||||
--primary: {{ $theme['primary'] }};
|
||||
--primary-dark: {{ $theme['dark'] }};
|
||||
--primary-light: {{ $theme['light'] }};
|
||||
--primary-text: {{ $theme['text'] }};
|
||||
}
|
||||
.btn-primary { background-color: var(--primary); color: #fff; }
|
||||
.btn-primary:hover { background-color: var(--primary-dark); }
|
||||
.text-primary { color: var(--primary); }
|
||||
.bg-primary { background-color: var(--primary); }
|
||||
.bg-primary-dark { background-color: var(--primary-dark); }
|
||||
.bg-primary-light { background-color: var(--primary-light); }
|
||||
.border-primary { border-color: var(--primary); }
|
||||
.hover-primary:hover { background-color: var(--primary-light); color: var(--primary-text); }
|
||||
.cat-bar { background-color: var(--primary); }
|
||||
.cat-bar a { color: rgba(255,255,255,0.85); }
|
||||
.cat-bar a:hover { background-color: var(--primary-dark); color: #fff; }
|
||||
.cat-bar a.active { background-color: var(--primary-dark); color: #fff; }
|
||||
.cart-badge { color: var(--primary); }
|
||||
.shop-name-text { color: var(--primary); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 text-gray-800 flex flex-col min-h-screen">
|
||||
|
||||
@@ -15,7 +42,7 @@
|
||||
<p class="text-sm">Wir verwenden Cookies, um Ihnen die bestmögliche Nutzererfahrung zu bieten. Mit dem Klick auf „Akzeptieren" stimmen Sie der Verwendung aller Cookies zu. <a href="{{ route('page', 'datenschutz') }}" class="underline">Mehr erfahren</a></p>
|
||||
<div class="flex gap-2 shrink-0">
|
||||
<button onclick="setCookieConsent(false)" class="px-4 py-2 text-sm border border-gray-500 rounded hover:bg-gray-700">Ablehnen</button>
|
||||
<button onclick="setCookieConsent(true)" class="px-4 py-2 text-sm bg-blue-500 rounded hover:bg-blue-600">Akzeptieren</button>
|
||||
<button onclick="setCookieConsent(true)" class="px-4 py-2 text-sm btn-primary rounded">Akzeptieren</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
@@ -27,73 +54,73 @@
|
||||
@endif
|
||||
|
||||
@php
|
||||
$legalSlugs = ['impressum','datenschutz','agb','widerrufsbelehrung'];
|
||||
$navPages = \App\Models\Page::where('published', true)
|
||||
->where('show_in_nav', true)
|
||||
->orderBy('nav_sort')
|
||||
->orderBy('title')
|
||||
->get();
|
||||
$legalSlugs = ['impressum','datenschutz','agb','widerrufsbelehrung'];
|
||||
$navPages = \App\Models\Page::where('published', true)
|
||||
->where('show_in_nav', true)
|
||||
->orderBy('nav_sort')
|
||||
->orderBy('title')
|
||||
->get();
|
||||
$navCategories = \App\Models\Category::whereNull('parent_id')
|
||||
->where('active', true)
|
||||
->where('show_in_nav', true)
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
->where('active', true)
|
||||
->where('show_in_nav', true)
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
@endphp
|
||||
|
||||
{{-- Header: Logo-Leiste --}}
|
||||
<header class="bg-white border-b shadow-sm">
|
||||
<div class="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between gap-4">
|
||||
|
||||
{{-- Logo: Bild (wenn hinterlegt) oder Shop-Name als Text --}}
|
||||
{{-- Logo --}}
|
||||
<a href="{{ url('/') }}" class="shrink-0 flex items-center">
|
||||
@php $logo = \App\Models\Setting::get('shop_logo'); @endphp
|
||||
@if($logo)
|
||||
<img src="{{ asset('storage/'.$logo) }}" alt="{{ \App\Models\Setting::get('shop_name', 'Shop') }}" class="h-12 w-auto object-contain">
|
||||
@else
|
||||
<span class="text-2xl font-bold text-blue-700 tracking-tight">
|
||||
<span class="text-2xl font-bold tracking-tight shop-name-text">
|
||||
{{ \App\Models\Setting::get('shop_name', 'Shop') }}
|
||||
</span>
|
||||
@endif
|
||||
</a>
|
||||
|
||||
{{-- Haupt-Navigation rechts --}}
|
||||
{{-- Haupt-Navigation --}}
|
||||
<nav class="flex items-center gap-1">
|
||||
<a href="{{ route('shop.products.index') }}"
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition">
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover-primary transition">
|
||||
Alle Produkte
|
||||
</a>
|
||||
@foreach($navCategories as $navCat)
|
||||
<a href="{{ route('shop.category', $navCat->slug) }}"
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition
|
||||
{{ request()->route('slug') === $navCat->slug ? 'bg-blue-50 text-blue-700' : '' }}">
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover-primary transition
|
||||
{{ request()->route('slug') === $navCat->slug ? 'bg-primary-light text-primary-text' : '' }}">
|
||||
{{ $navCat->name }}
|
||||
</a>
|
||||
@endforeach
|
||||
@foreach($navPages as $navPage)
|
||||
<a href="{{ route('page', $navPage->slug) }}"
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition
|
||||
{{ request()->route('slug') === $navPage->slug ? 'bg-blue-50 text-blue-700' : '' }}">
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover-primary transition
|
||||
{{ request()->route('slug') === $navPage->slug ? 'bg-primary-light text-primary-text' : '' }}">
|
||||
{{ $navPage->title }}
|
||||
</a>
|
||||
@endforeach
|
||||
@auth
|
||||
<a href="{{ route('account.index') }}"
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition">
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover-primary transition">
|
||||
Mein Konto
|
||||
</a>
|
||||
@if(auth()->user()->isAdmin())
|
||||
<a href="{{ route('admin.dashboard') }}"
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition">
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover-primary transition">
|
||||
Admin
|
||||
</a>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('login') }}"
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition">
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover-primary transition">
|
||||
Anmelden
|
||||
</a>
|
||||
<a href="{{ route('register') }}"
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition">
|
||||
class="px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover-primary transition">
|
||||
Registrieren
|
||||
</a>
|
||||
@endauth
|
||||
@@ -108,14 +135,14 @@
|
||||
</a>
|
||||
|
||||
<a href="{{ route('cart.index') }}"
|
||||
class="relative ml-2 px-3 py-2 rounded-lg text-sm font-medium bg-blue-700 text-white hover:bg-blue-800 transition flex items-center gap-1.5">
|
||||
class="relative ml-2 px-3 py-2 rounded-lg text-sm font-medium btn-primary transition flex items-center gap-1.5">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"/>
|
||||
</svg>
|
||||
Warenkorb
|
||||
@php $cartCount = array_sum(array_column(session('cart', []), 'qty')); @endphp
|
||||
@if($cartCount > 0)
|
||||
<span class="bg-white text-blue-700 text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center leading-none">{{ $cartCount }}</span>
|
||||
<span class="bg-white cart-badge text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center leading-none">{{ $cartCount }}</span>
|
||||
@endif
|
||||
</a>
|
||||
</nav>
|
||||
@@ -124,12 +151,12 @@
|
||||
{{-- Kategorien-Navigation --}}
|
||||
@php $navCats = \App\Models\Category::whereNull('parent_id')->where('active', true)->orderBy('sort_order')->get(); @endphp
|
||||
@if($navCats->count())
|
||||
<div class="bg-blue-700">
|
||||
<div class="cat-bar">
|
||||
<div class="max-w-6xl mx-auto px-4 flex gap-1 overflow-x-auto">
|
||||
@foreach($navCats as $cat)
|
||||
<a href="{{ route('shop.category', $cat->slug) }}"
|
||||
class="whitespace-nowrap px-4 py-2.5 text-sm font-medium text-blue-100 hover:text-white hover:bg-blue-600 transition
|
||||
{{ request()->route('slug') === $cat->slug ? 'bg-blue-800 text-white' : '' }}">
|
||||
class="whitespace-nowrap px-4 py-2.5 text-sm font-medium transition
|
||||
{{ request()->route('slug') === $cat->slug ? 'active' : '' }}">
|
||||
{{ $cat->name }}
|
||||
</a>
|
||||
@endforeach
|
||||
@@ -154,20 +181,20 @@
|
||||
|
||||
{{-- Footer --}}
|
||||
@php
|
||||
$footerPages = \App\Models\Page::where('published', true)
|
||||
->where('type', 'static')
|
||||
->where('show_in_footer', true)
|
||||
->whereNotIn('slug', $legalSlugs)
|
||||
->orderBy('title')
|
||||
->get();
|
||||
$legalPages = \App\Models\Page::where('published', true)
|
||||
->where('type', 'static')
|
||||
->whereIn('slug', $legalSlugs)
|
||||
->get()
|
||||
->sortBy(fn($p) => array_search($p->slug, $legalSlugs))
|
||||
->values();
|
||||
$hasBlog = \App\Models\Page::where('type','blog')->where('published', true)->exists();
|
||||
$hasFaq = \App\Models\Faq::exists();
|
||||
$footerPages = \App\Models\Page::where('published', true)
|
||||
->where('type', 'static')
|
||||
->where('show_in_footer', true)
|
||||
->whereNotIn('slug', $legalSlugs)
|
||||
->orderBy('title')
|
||||
->get();
|
||||
$legalPages = \App\Models\Page::where('published', true)
|
||||
->where('type', 'static')
|
||||
->whereIn('slug', $legalSlugs)
|
||||
->get()
|
||||
->sortBy(fn($p) => array_search($p->slug, $legalSlugs))
|
||||
->values();
|
||||
$hasBlog = \App\Models\Page::where('type','blog')->where('published', true)->exists();
|
||||
$hasFaq = \App\Models\Faq::exists();
|
||||
@endphp
|
||||
<footer class="bg-gray-900 text-gray-400 text-sm mt-12">
|
||||
<div class="max-w-6xl mx-auto px-4 py-8 grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
|
||||
Reference in New Issue
Block a user