homepage, new newsletter section, multilang support

This commit is contained in:
elvira
2026-08-08 14:50:10 +03:00
parent bf8b9219fc
commit e46276a31b
26 changed files with 1580 additions and 547 deletions
@@ -0,0 +1,7 @@
@extends('layouts.app')
@section('content')
<div style="height: 120vh"></div>
<x-newsletter-split class="py-20" />
<div style="height: 120vh"></div>
@endsection
+14 -7
View File
@@ -1,9 +1,9 @@
<header class="sticky top-0 z-50 bg-neutral-200 border-b border-black">
<header class="sticky top-0 z-50 bg-neutral-200 border-b border-black lg:h-26">
<div class="flex items-center gap-14 px-10">
{{-- Logo --}}
<a href="/" class="shrink-0">
<img src="/images/logo.png" alt="{{ config('app.name') }}" class="h-16 w-auto">
<a href="{{ url('/'.app()->getLocale()) }}" class="shrink-0 lg:py-4">
<img src="/images/logo.png" alt="{{ config('app.name') }}" class="h-18 w-auto">
</a>
{{-- Primary nav --}}
@@ -11,8 +11,8 @@
{{-- Products (CSS-only hover dropdown) --}}
<div class="group relative h-full flex items-center">
<a href="/products" class="nav-link uppercase inline-flex items-center gap-1 relative font-display font-extrabold italic text-black no-underline py-8">
<span>Products</span>
<a href="{{ url('/'.app()->getLocale().'/products') }}" class="nav-link uppercase inline-flex items-center gap-1 relative font-display font-extrabold italic text-black no-underline py-8">
<span>{{ __('general.nav.products') }}</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-4 not-italic transition-transform group-hover:rotate-180">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
</svg>
@@ -27,15 +27,22 @@
</div>
{{-- Contact --}}
<a href="/contact" class="nav-link uppercase inline-flex items-center gap-1 relative font-display font-extrabold italic text-black no-underline"><span>Contact</span></a>
<a href="{{ url('/'.app()->getLocale().'/contact') }}" class="nav-link uppercase inline-flex items-center gap-1 relative font-display font-extrabold italic text-black no-underline"><span>{{ __('general.nav.contact') }}</span></a>
</nav>
{{-- Right side actions --}}
<div class="ml-auto flex items-center gap-8">
{{-- Language switcher --}}
@isset($altLocale)
<a href="{{ $altLocaleUrl }}" class="uppercase font-display font-extrabold text-sm hover:opacity-70 transition-opacity" hreflang="{{ $altLocale }}" aria-label="{{ $altLocale === 'el' ? 'Δες τη σελίδα στα Ελληνικά' : 'View this page in English' }}">
{{ $altLocale }}
</a>
@endisset
{{-- Cart --}}
<a href="/cart" class="flex items-center justify-center w-10 h-10 hover:opacity-70 transition-opacity" aria-label="Cart">
<a href="{{ url('/'.app()->getLocale().'/cart') }}" class="flex items-center justify-center w-10 h-10 hover:opacity-70 transition-opacity" aria-label="Cart">
<x-ui.icon name="bag" :size="40" />
</a>
@@ -0,0 +1,62 @@
@props([
'image' => null,
])
<section {{ $attributes }}>
<div class="max-w-6xl mx-auto grid grid-cols-1 lg:grid-cols-2 border border-black">
<div class="flex flex-col justify-center gap-8 px-8 py-16 lg:px-16">
<h2
class="text-h2 leading-tight"
aria-label="Γράψου στο newsletter μας"
data-controller="appear"
data-appear-visible-class="is-visible"
>
<span class="text-flicker" data-text="Γράψου">Γράψου</span>
στο
<span class="text-flicker" data-text="newsletter">newsletter</span>
μας
</h2>
<p class="max-w-md text-neutral-600">
Γίνε μέλος της λίστας μας και κέρδισε <span class="font-bold">5% έκπτωση</span> στην πρώτη σου παραγγελία, μαζί με αποκλειστικές προσφορές και νέα.
</p>
<form method="POST" action="#" class="flex flex-col gap-6 max-w-md">
@csrf
<div class="relative">
<x-ui.input
name="email"
type="email"
:required="true"
autocomplete="email"
placeholder="Το email σου"
class="pr-10 text-lg"
placeholderClass="placeholder:text-black/40"
aria-label="Το email σου"
/>
<button
type="submit"
aria-label="Εγγραφή στο newsletter"
class="absolute right-0 bottom-2 cursor-pointer"
>
<x-ui.icon name="arrow-right" size="22" />
</button>
</div>
<x-ui.checkbox name="gdpr_consent" :required="true" id="newsletter_split_gdpr_consent" class="after:mix-blend-multiply">
<span class="text-sm text-left">Επιθυμώ διακαώς 🔥 να εγγραφώ στη λίστα αποστολής ενημερωτικού υλικού</span>
</x-ui.checkbox>
</form>
</div>
<div class="relative bg-neutral-300 min-h-[320px] lg:min-h-full border-t border-black lg:border-t-0 lg:border-l">
@if($image)
<img src="{{ $image }}" alt="" aria-hidden="true" loading="lazy" width="800" height="800" class="w-full h-full object-cover">
@else
<div class="absolute inset-0 flex items-center justify-center text-neutral-500 text-sm">
Χωρίς εικόνα
</div>
@endif
</div>
</div>
</section>
@@ -29,7 +29,7 @@ class="flex items-center gap-1.5 text-brand"
@if ($showCount && $count > 0)
<span class="text-sm text-neutral-500">
({{ $count }} customer {{ $count === 1 ? 'review' : 'reviews' }})
({{ trans_choice('general.customer_reviews', $count, ['count' => $count]) }})
</span>
@endif
@@ -1,33 +1,5 @@
@props(['variants', 'option' => null])
@php
$colorMap = [
'light blue' => 'rgb(0, 189, 255)',
'terracotta' => 'rgb(217, 104, 73)',
'red-black gradient' => 'rgb(198, 68, 68)',
'green-purple gradient'=> 'rgb(5, 170, 61)',
'metallic blue' => 'rgb(53, 121, 151)',
'copper' => 'rgb(242, 155, 55)',
'purple' => 'rgb(165, 77, 207)',
'red' => 'rgb(255, 0, 0)',
'black' => 'rgb(0, 0, 0)',
'gray' => 'rgb(128, 128, 128)',
'rainbow' => 'rgb(97, 195, 231)',
'white' => 'rgb(255, 255, 255)',
'gold' => 'rgb(212, 154, 6)',
'brown' => 'rgb(154, 86, 48)',
'blue' => 'rgb(0, 91, 211)',
'orange' => 'rgb(255, 138, 0)',
'pink' => 'rgb(255, 192, 203)',
'yellow' => 'rgb(255, 229, 0)',
'green' => 'rgb(5, 170, 61)',
'blue-purple gradient' => 'rgb(159, 113, 247)',
'silver' => 'rgb(211, 211, 211)',
'sparkly black' => 'rgb(5, 5, 5)',
'plum' => 'rgb(198, 34, 159)',
];
@endphp
<div {{ $attributes }}>
@if($option)
<p class="font-bold mb-3 text-sm uppercase tracking-wide">
@@ -42,11 +14,9 @@ class="flex flex-wrap gap-2"
>
@foreach($variants as $variant)
@php
$value = $variant->values->first();
$label = $value?->translate('name') ?? '';
$labelEn = mb_strtolower($value?->translate('name', 'en') ?? '');
$labelEl = mb_strtolower($value?->translate('name', 'el') ?? '');
$bg = $colorMap[$labelEl] ?? $colorMap[$labelEn] ?? '#cccccc';
$value = $variant->values->first();
$label = $value?->translate('name') ?? '';
$bg = $value?->meta['hex'] ?? '#cccccc';
@endphp
<button
type="button"
@@ -11,6 +11,7 @@
<img
src="{{ $image }}"
alt="{{ $name }}"
loading="lazy"
class="w-full h-auto block"
>
@else
+150
View File
@@ -0,0 +1,150 @@
@extends('layouts.app')
@section('title', config('app.name') . ' — Ό,τι φαντάζεσαι, τυπωμένο σε 3D')
@section('description', 'Gaming φιγούρες, κρανία, κλειδοθήκες και ό,τι πιο τρελό σου περνάει απ\' το μυαλό. Σχεδιασμένα και τυπωμένα σε 3D, ένα προς ένα.')
@section('content')
<div class="border-b border-black">
{{-- Hero --}}
<div class="grid grid-cols-1 lg:grid-cols-2 lg:divide-x lg:divide-black border-b border-black min-h-[calc(100vh-102px)]">
<div class="flex flex-col justify-center gap-8 px-8 py-16 lg:px-16">
<svg viewBox="0 0 135 124" class="hero-star w-[173px] h-[159px] text-[#f3d121]" aria-hidden="true" fill="currentColor">
<path d="m88.4 0 4 28.9 29.7-5.2-14.3 25.7L135 62l-27.2 12.6 14.3 25.7-29.7-5.2-4 28.9-20.9-21.1L46.6 124l-4-28.9-29.7 5.2 14.3-25.7L0 62l27.2-12.6-14.3-25.7 29.7 5.2 4-28.9 20.9 21.1L88.4 0z"/>
</svg>
<h1
class="font-display font-semibold text-6xl lg:text-7xl leading-tight"
aria-label="Ό,τι φαντάζεσαι, το τυπώνουμε"
data-controller="appear"
data-appear-visible-class="is-visible"
>
Ό,τι <span class="text-flicker" data-text="φαντάζεσαι">φαντάζεσαι</span>,<br>
το <span class="text-flicker" data-text="τυπώνουμε">τυπώνουμε</span>
</h1>
</div>
<div class="relative flex flex-col items-center justify-center px-8 py-10 lg:px-10" data-controller="carousel">
@if($heroProducts->isNotEmpty())
<button
type="button"
data-action="carousel#prev"
aria-label="Προηγούμενο προϊόν"
class="absolute left-4 lg:left-8 top-1/2 -translate-y-1/2 hover:opacity-60 transition-opacity"
>
<x-ui.icon name="arrow-left" :size="56" />
</button>
<div class="w-full ">
@foreach($heroProducts as $product)
<a
href="{{ $product['href'] }}"
data-carousel-target="slide"
class="{{ $loop->first ? '' : 'hidden' }} block"
>
<div class="flex items-center justify-center">
<div class="border border-black bg-white mb-16 flex items-center justify-center overflow-hidden max-w-sm">
@if($product['image'])
<img
src="{{ $product['image'] }}"
alt="{{ $product['name'] }}"
width="400"
height="400"
loading="eager"
fetchpriority="high"
class="w-full h-full object-cover"
>
@else
<span class="text-neutral-500 text-sm">Χωρίς εικόνα</span>
@endif
</div>
</div>
<div class="flex items-baseline justify-between gap-4">
<span class="font-display font-bold text-2xl">{{ $product['name'] }}</span>
@if($product['price'] !== null)
<span class="font-bold text-xl shrink-0">€{{ number_format($product['price'], 2) }}</span>
@endif
</div>
</a>
@endforeach
</div>
<button
type="button"
data-action="carousel#next"
aria-label="Επόμενο προϊόν"
class="absolute right-4 lg:right-8 top-1/2 -translate-y-1/2 hover:opacity-60 transition-opacity"
>
<x-ui.icon name="arrow-right" :size="56" />
</button>
@else
<p class="text-neutral-500">Δεν βρέθηκαν προϊόντα.</p>
@endif
</div>
</div>
{{-- Info / trivia --}}
<div class="grid grid-cols-1 lg:grid-cols-2 lg:divide-x lg:divide-black">
<div class="bg-neutral-300 min-h-[320px] lg:min-h-full flex items-center justify-center text-neutral-500 text-sm">
Χωρίς εικόνα
</div>
<div class="flex flex-col justify-center gap-6 px-8 py-16 lg:px-16 text-center">
<p class="max-w-md mx-auto text-neutral-600">
Gaming φιγούρες, κρανία, κλειδοθήκες, dark humor και ό,τι πιο τρελό σου περνάει απ' το μυαλό — όλα σχεδιασμένα και τυπωμένα σε 3D, ένα προς ένα, εδώ στην Ελλάδα.
</p>
<h2 class="font-display font-extrabold text-h2 leading-tight">
Στρώση-στρώση,<br>
<span class="italic">0.1mm τη φορά</span>
</h2>
<p class="max-w-md mx-auto text-neutral-600">
Τόσο λεπτή είναι κάθε στρώση υλικού σε ένα 3D εκτυπωμένο κομμάτι — πιο λεπτή κι από τρίχα. Δεν κρατάμε αποθέματα: κάθε παραγγελία τυπώνεται ειδικά για σένα, στο χρώμα και στο μέγεθος που θες.
</p>
<div>
<x-ui.button size="md" :href="url('/'.app()->getLocale().'/products')">Δες τα προϊόντα</x-ui.button>
</div>
</div>
</div>
</div>
{{-- Classics --}}
<div class="border-b border-black" data-controller="carousel">
<div class="flex items-center justify-between gap-4 px-8 py-8 border-b border-black">
<h2 class="font-display font-extrabold text-h3">Τα αγαπημένα μας</h2>
<x-ui.button size="sm" :href="url('/'.app()->getLocale().'/products')">Δες όλα</x-ui.button>
</div>
<div class="flex items-center gap-6 px-8 py-12">
<button type="button" data-action="carousel#prev" aria-label="Προηγούμενα προϊόντα" class="shrink-0 hover:opacity-60 transition-opacity">
<x-ui.icon name="arrow-left" :size="32" />
</button>
<div class="flex-1 overflow-hidden">
@forelse($classicsProducts->chunk(4) as $page)
<div data-carousel-target="slide" class="{{ $loop->first ? '' : 'hidden' }} grid grid-cols-2 md:grid-cols-4 gap-6">
@foreach($page as $product)
<x-ui.product-card
:name="$product['name']"
:price="$product['price']"
:image="$product['image']"
:href="$product['href']"
/>
@endforeach
</div>
@empty
<p class="text-neutral-500 text-center w-full">Δεν βρέθηκαν προϊόντα.</p>
@endforelse
</div>
<button type="button" data-action="carousel#next" aria-label="Επόμενα προϊόντα" class="shrink-0 hover:opacity-60 transition-opacity">
<x-ui.icon name="arrow-right" :size="32" />
</button>
</div>
</div>
<div class="py-20">
<x-newsletter-split />
</div>
@endsection
+9 -2
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="el">
<html lang="{{ $currentLocale ?? app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -15,6 +15,13 @@
<title>@yield('title', config('app.name'))</title>
<meta name="description" content="@yield('description', '')">
{{-- Reciprocal hreflang: self-reference + the alternate locale + x-default --}}
@isset($currentLocale)
<link rel="alternate" hreflang="{{ $currentLocale }}" href="{{ url()->current() }}" />
<link rel="alternate" hreflang="{{ $altLocale }}" href="{{ $altLocaleUrl }}" />
<link rel="alternate" hreflang="x-default" href="{{ url('/') }}" />
@endisset
@stack('seo')
@vite(['resources/css/app.css', 'resources/js/app.js'])
@@ -24,7 +31,7 @@
<body class="min-h-screen antialiased">
<x-header />
<main id="main-content" class="px-4 sm:px-8 min-h-[calc(100vh-12rem)] sm:min-h-[calc(100vh-8rem)]">
<main id="main-content" class="min-h-[calc(100vh-12rem)] sm:min-h-[calc(100vh-8rem)]">
@yield('content')
</main>
+4 -4
View File
@@ -4,7 +4,7 @@
@section('description', $product->translateAttribute('description'))
@section('content')
<div class="max-w-7xl mx-auto py-12">
<div class="max-w-7xl mx-auto px-4 sm:px-8 py-12">
@php $collection = $product->collections->first(); @endphp
@@ -182,9 +182,9 @@ class="absolute bottom-6 right-8 text-white text-sm"
</div>
<x-ui.tabs class="mt-16" size="lg" :tabs="[
['id' => 'description', 'label' => 'Περιγραφή'],
// ['id' => 'reviews', 'label' => 'Αξιολογήσεις (' . count($reviews) . ')'],
['id' => 'reviews', 'label' => 'Αξιολογήσεις (3)'],
['id' => 'description', 'label' => __('general.product.description')],
// ['id' => 'reviews', 'label' => __('general.product.reviews') . ' (' . count($reviews) . ')'],
['id' => 'reviews', 'label' => __('general.product.reviews') . ' (3)'],
]">
<x-slot name="description">
<div class="leading-7 [&_p]:mt-4">
+1 -1
View File
@@ -4,7 +4,7 @@
@section('description', $product->translateAttribute('description'))
@section('content')
<div class="max-w-7xl mx-auto py-12">
<div class="max-w-7xl mx-auto px-4 sm:px-8 py-12">
@php $collection = $product->collections->first(); @endphp
File diff suppressed because one or more lines are too long