refactor: Consolidate home page UI into new hero and brands components and improve stock opname quantity handling.
This commit is contained in:
parent
f844bb1fb7
commit
307213c7d2
@ -54,7 +54,7 @@ public function update(): void
|
||||
continue;
|
||||
}
|
||||
|
||||
$item->qty_physical = $qtyPhysical;
|
||||
$item->qty_physical = str_replace('.', '', (string) $qtyPhysical);
|
||||
$item->save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,8 @@ class Index extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
|
||||
public array $testimonials = [];
|
||||
|
||||
public array $bestSellingPerfumes = [];
|
||||
|
||||
public array $brands = [];
|
||||
@ -34,23 +36,39 @@ class Index extends Component
|
||||
|
||||
public int $totalMembersCount = 0;
|
||||
|
||||
public $latestMembers = [];
|
||||
public string $formattedTotalMembersCount = '';
|
||||
|
||||
public array $testimonials = [];
|
||||
public array $latestMembers = [];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->testimonials = Testimonial::with(['user', 'user.customer'])
|
||||
->show()
|
||||
->latest()
|
||||
->take(5)
|
||||
->get()
|
||||
->map(function (Testimonial $testimonial) {
|
||||
$name = $testimonial->user->customer->name ?? 'User';
|
||||
$testimonial->customer_name = $name;
|
||||
$testimonial->avatar_url = 'https://ui-avatars.com/api/?name='.urlencode($name).'&background=random&color=fff';
|
||||
|
||||
return $testimonial;
|
||||
})
|
||||
->toArray();
|
||||
|
||||
$this->bestSellingPerfumes = Perfume::withSum(['items' => function ($query) {
|
||||
$query->whereHas('order', function ($q) {
|
||||
$q->paid();
|
||||
});
|
||||
}], 'quantity')
|
||||
->orderByDesc('items_sum_quantity')
|
||||
->take(2)
|
||||
->take(5)
|
||||
->get()
|
||||
->map(function (Perfume $perfume) {
|
||||
$perfume->image = $perfume->getFirstMediaUrl('image') ? $perfume->getFirstMediaUrl('image') : asset('assets/images/logo.png');
|
||||
$perfume->image = $perfume->getFirstMediaUrl('image') ?: asset('assets/images/logo.png');
|
||||
$perfume->concentration_label = $perfume->concentration->label();
|
||||
$perfume->formatted_price = formatCurrencyNumber($perfume->sale_price, 'Rp');
|
||||
$perfume->formatted_sold_count = formatCurrencyNumber($perfume->items_sum_quantity);
|
||||
|
||||
return $perfume;
|
||||
})
|
||||
@ -60,7 +78,7 @@ public function mount(): void
|
||||
$this->brands = Brand::orderBy('sort_order')
|
||||
->get()
|
||||
->map(function (Brand $brand) {
|
||||
$brand->image = $brand->getFirstMediaUrl('image') ? $brand->getFirstMediaUrl('image') : asset('assets/images/dark-logo.png');
|
||||
$brand->image = $brand->getFirstMediaUrl('image') ?: asset('assets/images/dark-logo.png');
|
||||
|
||||
return $brand;
|
||||
})
|
||||
@ -70,33 +88,27 @@ public function mount(): void
|
||||
|
||||
$this->perfumeFeatures = PerfumeFeature::orderBy('sort_order')->get()->toArray();
|
||||
|
||||
$this->faqs = Faq::orderBy('sort_order')
|
||||
->get()
|
||||
->map(fn (Faq $faq) => [
|
||||
'question' => $faq->question,
|
||||
'answer' => $faq->answer,
|
||||
])
|
||||
->toArray();
|
||||
$this->faqs = Faq::orderBy('sort_order')->get()->toArray();
|
||||
|
||||
$this->totalMembersCount = Customer::count();
|
||||
$this->formattedTotalMembersCount = number_format($this->totalMembersCount);
|
||||
|
||||
$this->latestMembers = Customer::whereNotNull('user_id')
|
||||
->inRandomOrder()
|
||||
->take(3)
|
||||
->get();
|
||||
|
||||
$this->testimonials = Testimonial::with(['user', 'user.customer'])
|
||||
->show()
|
||||
->latest()
|
||||
->take(5)
|
||||
->get()
|
||||
->map(function (Customer $member) {
|
||||
$member->avatar_url = 'https://ui-avatars.com/api/?name='.urlencode($member->name).'&background=random&color=fff';
|
||||
|
||||
return $member;
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.home.index', [
|
||||
'pageTitle' => 'Beranda',
|
||||
'pageTitle' => 'Selamat Datang! ✨',
|
||||
'categories' => Category::perfume()
|
||||
->orderBy('sort_order')
|
||||
->paginate(12)
|
||||
|
||||
@ -12,15 +12,30 @@
|
||||
])]
|
||||
class Outlet extends Component
|
||||
{
|
||||
public array $outlets = [];
|
||||
|
||||
public string $search = '';
|
||||
|
||||
public ?OutletModel $selectedOutlet = null;
|
||||
|
||||
public function mount(): void
|
||||
public function showDetail(OutletModel $outletModel): void
|
||||
{
|
||||
$this->outlets = OutletModel::with(['facilities', 'openingHours'])
|
||||
$this->selectedOutlet = $outletModel;
|
||||
$this->selectedOutlet->load(['openingHours' => function ($query) {
|
||||
$query->orderBy('day');
|
||||
}]);
|
||||
$this->selectedOutlet->openingHours->each(function ($hour) {
|
||||
$hour->formatted_open_time = \Carbon\Carbon::parse($hour->open_time)->format('H:i');
|
||||
$hour->formatted_close_time = \Carbon\Carbon::parse($hour->close_time)->format('H:i');
|
||||
});
|
||||
}
|
||||
|
||||
public function closeModal(): void
|
||||
{
|
||||
$this->selectedOutlet = null;
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$outlets = OutletModel::with(['facilities', 'openingHours'])
|
||||
->operational()
|
||||
->when($this->search, function ($query) {
|
||||
$query->where(function ($query) {
|
||||
@ -31,29 +46,17 @@ public function mount(): void
|
||||
->latest()
|
||||
->get()
|
||||
->map(function ($outlet) {
|
||||
$outlet->image = $outlet->getFirstMediaUrl('featured_image') ? $outlet->getFirstMediaUrl('featured_image') : asset('assets/images/dark-logo.png');
|
||||
$outlet->image = $outlet->getFirstMediaUrl('featured_image') ?: asset('assets/images/dark-logo.png');
|
||||
$outlet->status_label = $outlet->status->label();
|
||||
$outlet->status_color = $outlet->status->color();
|
||||
|
||||
return $outlet;
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
});
|
||||
|
||||
public function showDetail(int $outletId): void
|
||||
{
|
||||
$this->selectedOutlet = OutletModel::with(['facilities', 'openingHours'])->find($outletId);
|
||||
}
|
||||
|
||||
public function closeModal(): void
|
||||
{
|
||||
$this->selectedOutlet = null;
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.home.outlets', [
|
||||
'pageTitle' => 'Outlet',
|
||||
'pageTitle' => 'Outlet Terdekat ✨',
|
||||
'pageDesc' => 'Temukan outlet kami di sekitarmu dan rasakan langsung kemewahan aromanya! 🚗🏢',
|
||||
'outlets' => $outlets,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,13 +30,7 @@ protected function loadItems(): void
|
||||
->where('period_month', now()->format('Y-m'))
|
||||
->get()
|
||||
->map(function ($stockOpname) {
|
||||
$outlet = $stockOpname->outlet;
|
||||
|
||||
$parfumCount = $outlet->perfumes()->count();
|
||||
$botolCount = $outlet->bottles()->count();
|
||||
$produkCount = $outlet->products()->count();
|
||||
|
||||
$total = $parfumCount + $botolCount + $produkCount;
|
||||
$total = $stockOpname->items()->count();
|
||||
|
||||
$filled = $stockOpname->items()
|
||||
->whereNotNull('qty_physical')
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
use App\Models\StockOpnameItem;
|
||||
use App\Traits\Authorization\WithAuthorization;
|
||||
use App\Traits\Components\WithConfirmation;
|
||||
use App\Traits\Components\WithToast;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Livewire\Attributes\Title;
|
||||
@ -16,7 +17,7 @@
|
||||
#[Title('Kelola Stock Opname')]
|
||||
class Manage extends Component
|
||||
{
|
||||
use WithAuthorization, WithConfirmation;
|
||||
use WithAuthorization, WithConfirmation, WithToast;
|
||||
|
||||
public StockOpnameForm $form;
|
||||
|
||||
@ -46,19 +47,27 @@ public function updated(string $propertyName, $value): void
|
||||
{
|
||||
$this->canOrAbort('manage stock opname');
|
||||
|
||||
$this->form->update();
|
||||
|
||||
if (str_starts_with($propertyName, 'form.outlet_stock.')) {
|
||||
$itemId = explode('.', $propertyName)[2];
|
||||
|
||||
$item = $this->items->firstWhere('id', $itemId);
|
||||
if ($item) {
|
||||
$item->qty_physical = $value;
|
||||
$item->difference = $value - $item->qty_system;
|
||||
$normalizedValue = str_replace('.', '', (string) $value);
|
||||
$item->qty_physical = (int) $normalizedValue;
|
||||
$item->difference = (int) $normalizedValue - $item->qty_system;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->canOrAbort('manage stock opname');
|
||||
|
||||
$this->form->update();
|
||||
|
||||
$this->toast('Data stock opname berhasil disimpan.', 'Berhasil');
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.studio.stock.stock-opname.manage', [
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<div class="flex justify-center flex-col">
|
||||
<div class="flex justify-center flex-col mb-10">
|
||||
<dotlottie-wc src="{{ asset('assets/animations/boxing-cat.json') }}" autoplay loop
|
||||
style="width: 200px; height: 200px; margin: 0 auto;">
|
||||
</dotlottie-wc>
|
||||
<p class="block text-center italic mt-5">
|
||||
{!! isset($message) ? $message : 'Yahhh, sepertinya belum ada data yang tersedia.' !!}
|
||||
<p class="block text-center italic mt-5 text-home-foreground font-medium">
|
||||
{!! $message ?? 'Yahhh, sepertinya belum ada data yang tersedia. 😉✨' !!}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@include('components.sections.ui.home._header')
|
||||
<x-sections.ui.home.header :header="$header" />
|
||||
|
||||
@persist('toast')
|
||||
<flux:toast position="bottom end" class="ps-6" />
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
{{ $slot }}
|
||||
|
||||
@include('components.sections.ui.home._footer')
|
||||
<x-sections.ui.home.footer :generalSettings="$generalSettings" :contactSettings="$contactSettings" />
|
||||
|
||||
@fluxScripts
|
||||
|
||||
|
||||
@ -12,11 +12,11 @@ class="w-full h-[400px] lg:h-[600px] object-cover rounded-2xl shadow-lg">
|
||||
|
||||
<header>
|
||||
<h2 class="text-3xl md:text-4xl lg:text-5xl font-bold text-home-foreground leading-tight mb-6">
|
||||
Keunggulan Produk Kami.
|
||||
Kenapa Harus Kami? 🏆✨
|
||||
</h2>
|
||||
<p class="text-base md:text-lg text-home-foreground/70 leading-relaxed">
|
||||
Setiap parfum dibuat dengan standar tinggi agar kamu mendapatkan kualitas terbaik dan pengalaman
|
||||
wangi yang tak terlupakan.
|
||||
Tiap tetesnya dibikin spesial buat kamu, biar wangimu tahan lama dan bikin happy sepanjang hari!
|
||||
💖✨
|
||||
</p>
|
||||
</header>
|
||||
|
||||
18
resources/views/components/sections/ui/home/brands.blade.php
Normal file
18
resources/views/components/sections/ui/home/brands.blade.php
Normal file
@ -0,0 +1,18 @@
|
||||
<section class="bg-home-foreground py-8 overflow-hidden" id="brand-slider">
|
||||
<div class="swiper brand-swiper overflow-visible">
|
||||
<div class="swiper-wrapper !ease-linear">
|
||||
@foreach (array_merge($brands, $brands) as $brand)
|
||||
<div class="swiper-slide !w-auto">
|
||||
<div class="flex flex-col items-center px-10 group">
|
||||
<img src="{{ $brand['image'] }}" alt="{{ $brand['name'] }}"
|
||||
class="w-10 h-10 lg:w-14 lg:h-14 object-contain brightness-0 invert opacity-50 group-hover:opacity-100 transition-all duration-500 filter grayscale hover:grayscale-0">
|
||||
<h3
|
||||
class="mt-3 text-[10px] lg:text-xs font-bold text-center text-white/30 uppercase tracking-[0.2em] group-hover:text-white transition-all duration-500">
|
||||
{{ $brand['name'] }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -4,11 +4,10 @@
|
||||
|
||||
<header class="text-center mb-16">
|
||||
<h2 class="text-3xl md:text-4xl lg:text-5xl font-bold text-home-background leading-tight mb-4">
|
||||
Temukan Aroma yang <br class="hidden sm:block">
|
||||
Cocok dengan Kepribadianmu.
|
||||
Temukan Aroma yang Kamu Banget! 💃✨
|
||||
</h2>
|
||||
<p class="text-gray-500 text-lg max-w-2xl mx-auto">
|
||||
Pilih kategori aroma yang paling menggambarkan dirimu dan ciptakan kesan tak terlupakan.
|
||||
Pilih kategori aroma yang paling menggambarkan dirimu dan bikin kesan tak terlupakan! 🌹
|
||||
</p>
|
||||
</header>
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
@props([
|
||||
'totalMembersCount' => 0,
|
||||
'formattedTotalMembersCount' => '',
|
||||
'latestMembers' => [],
|
||||
])
|
||||
|
||||
<section class="bg-home-primary text-home-foreground font-sans py-24 relative overflow-hidden">
|
||||
|
||||
<div class="container mx-auto px-4 md:px-8 lg:px-24 xl:px-32 relative z-10">
|
||||
@ -5,31 +11,30 @@
|
||||
|
||||
<div class="space-y-8">
|
||||
<h2 class="text-4xl md:text-5xl font-bold tracking-tight leading-tight text-home-background">
|
||||
Bergabung dengan Member Eksklusif.
|
||||
Jadi Bagian Keluarga Eksklusif Kami! 👑✨
|
||||
</h2>
|
||||
<p class="text-home-background/40 text-lg leading-relaxed max-w-md">
|
||||
Dapatkan akses awal ke koleksi terbaru, diskon member khusus, dan tips perawatan parfum langsung
|
||||
dari ahlinya.
|
||||
Dapetin akses VIP ke koleksi terbaru, diskon spesial, dan tips wangi-wangian eksklusif cuma buat
|
||||
kamu! 😉🎉
|
||||
</p>
|
||||
|
||||
<form onsubmit="event.preventDefault();" class="flex flex-col sm:flex-row gap-4 w-full">
|
||||
<button type="submit"
|
||||
class="px-8 py-4 rounded-xl bg-home-background text-home-foreground font-bold hover:bg-home-secondary hover:text-home-foreground transition-all duration-300 shadow-lg shadow-home-background/20 whitespace-nowrap">
|
||||
Gabung Sekarang
|
||||
Join Sekarang Yuk! 🚀
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="flex items-center gap-4 text-sm text-home-background/40">
|
||||
<div class="flex -space-x-2">
|
||||
@foreach ($latestMembers as $member)
|
||||
<img class="w-8 h-8 rounded-full border-2 border-white"
|
||||
src="https://ui-avatars.com/api/?name={{ urlencode($member->name) }}&background=random&color=fff"
|
||||
alt="{{ $member->name }}">
|
||||
<img class="w-8 h-8 rounded-full border-2 border-white" src="{{ $member['avatar_url'] }}"
|
||||
alt="{{ $member['name'] }}">
|
||||
@endforeach
|
||||
</div>
|
||||
<p>Bergabung bersama <span
|
||||
class="text-home-background font-bold">{{ number_format($totalMembersCount) }}+</span>
|
||||
member lainnya.</p>
|
||||
<p>Gabung sama <span
|
||||
class="text-home-background font-bold">{{ $formattedTotalMembersCount }}+</span>
|
||||
parfum lovers lainnya! 💕</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -39,7 +39,7 @@ class="w-10 h-10 rounded-full bg-white/5 flex items-center justify-center text-g
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-3 md:pl-8">
|
||||
<h3 class="text-white font-bold mb-6 text-sm uppercase tracking-wider">Tautan Cepat</h3>
|
||||
<h3 class="text-white font-bold mb-6 text-sm uppercase tracking-wider">Cek Link Ini ✨</h3>
|
||||
<ul class="space-y-4 text-sm text-gray-400">
|
||||
<li>
|
||||
<a href="{{ route('homepage') }}" wire:navigate
|
||||
@ -64,7 +64,7 @@ class="hover:text-home-secondary transition-colors duration-300 flex items-cente
|
||||
|
||||
<div class="md:col-span-5 flex flex-col gap-6">
|
||||
<div>
|
||||
<h3 class="text-white font-bold mb-6 text-sm uppercase tracking-wider">Kontak Kami</h3>
|
||||
<h3 class="text-white font-bold mb-6 text-sm uppercase tracking-wider">Sapa Kami Yuk! 👋</h3>
|
||||
<div class="space-y-5 text-sm text-gray-400">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="mt-1"><svg class="w-5 h-5 text-home-primary" fill="none"
|
||||
@ -102,7 +102,7 @@ class="hover:text-home-secondary transition-colors duration-300 flex items-cente
|
||||
|
||||
<div class="border-t border-white/10 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
|
||||
<div class="text-sm text-gray-500">
|
||||
© {{ date('Y') }} Yadi Parfum. Semua hak dilindungi undang-undang.
|
||||
© {{ date('Y') }} Yadi Parfum. Dibuat dengan cinta untuk kamu! ❤️✨
|
||||
</div>
|
||||
{{-- <div class="flex gap-6 text-sm text-gray-500">
|
||||
<a href="#" class="hover:text-white transition-colors">Privacy Policy</a>
|
||||
@ -62,7 +62,7 @@ class="{{ request()->routeIs($item['match']) ? 'font-bold text-home-primary' : '
|
||||
'border-home-foreground text-home-foreground': !scrolled || isOpen
|
||||
}"
|
||||
class="hidden lg:flex items-center border px-4 py-2 rounded-full transition-all duration-200 hover:scale-105 font-medium">
|
||||
Masuk
|
||||
Yuk Masuk! ✨
|
||||
</a>
|
||||
|
||||
<a href="{{ route('profile.redirect') }}" id="profile-mobile" wire:navigate
|
||||
193
resources/views/components/sections/ui/home/hero.blade.php
Normal file
193
resources/views/components/sections/ui/home/hero.blade.php
Normal file
@ -0,0 +1,193 @@
|
||||
@props([
|
||||
'totalMembersCount' => 0,
|
||||
'formattedTotalMembersCount' => '',
|
||||
'latestMembers' => [],
|
||||
'testimonials' => [],
|
||||
'bestSellingPerfumes' => [],
|
||||
])
|
||||
|
||||
<main class="flex flex-col lg:flex-row justify-between lg:min-h-screen w-full relative overflow-x-hidden">
|
||||
{{-- Left Section --}}
|
||||
<section
|
||||
class="relative w-full lg:w-[35%] pt-24 lg:pt-0 py-0 px-6 lg:px-10 lg:pb-8 flex flex-col lg:min-h-screen lg:justify-between space-y-6 lg:space-y-0 z-20 bg-white">
|
||||
<div class="flex flex-col justify-center flex-1 space-y-6 lg:pt-48">
|
||||
@if ($totalMembersCount > 0)
|
||||
<header class="flex flex-wrap items-center gap-4">
|
||||
<div class="flex items-center">
|
||||
@foreach ($latestMembers as $member)
|
||||
<figure
|
||||
class="border-2 border-home-background w-8 h-8 lg:w-10 lg:h-10 rounded-full overflow-hidden shadow-sm {{ !$loop->first ? '-ms-2' : '' }}">
|
||||
<img src="{{ $member['avatar_url'] }}" alt="{{ $member['name'] }}"
|
||||
class="w-full h-full object-cover">
|
||||
</figure>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="border border-home-primary/30 py-1 px-3 rounded-full text-sm lg:text-lg text-home-primary">
|
||||
<span class="text-home-primary font-bold">{{ $formattedTotalMembersCount }}</span>+ Customer
|
||||
Happy &
|
||||
Percaya
|
||||
Kami 😍
|
||||
</div>
|
||||
</header>
|
||||
@endif
|
||||
|
||||
<h1 class="text-home-foreground text-4xl lg:text-6xl leading-tight text-start font-bold">
|
||||
Temukan<br>
|
||||
<span class="italic font-serif font-light text-home-primary">
|
||||
signature scent-mu
|
||||
</span> hari ini ✨
|
||||
</h1>
|
||||
|
||||
<p class="text-lg w-[90%] lg:w-full lg:text-lg text-home-foreground/80">
|
||||
Tiap parfum punya cerita lho. Pilih aroma yang pas sama gayamu dan biarkan wangimu bercerita! 😉🌸
|
||||
</p>
|
||||
|
||||
<div class="flex items-center">
|
||||
<a href="{{ route('product.index') }}" wire:navigate
|
||||
class="bg-home-primary text-home-background py-3 px-6 rounded-full hover:shadow-lg hover:shadow-home-primary/30 transition-all font-medium">
|
||||
Mulai Belanja 🛍️
|
||||
</a>
|
||||
<span class="w-8 h-[2px] bg-home-primary/20"></span>
|
||||
<a href="{{ route('product.index') }}" wire:navigate
|
||||
class="flex items-center justify-center w-12 h-12 border border-home-primary rounded-full hover:bg-home-background transition-colors group bg-transparent">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" class="w-5 h-5 text-home-primary">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Testimonial Section --}}
|
||||
<div class="my-3">
|
||||
@if (!empty($testimonials))
|
||||
<div
|
||||
class="testimonials-swiper-container relative w-full mt-auto h-[300px] lg:h-[250px] bg-home-background/40 backdrop-blur-md border border-home-secondary rounded-2xl z-30 shadow-sm overflow-hidden transition-all duration-300">
|
||||
<div class="swiper testimonials-swiper w-full h-full">
|
||||
<div class="swiper-wrapper">
|
||||
@foreach ($testimonials as $testimonial)
|
||||
<div class="swiper-slide flex flex-col justify-center py-0 px-6 lg:px-10">
|
||||
<div class="flex items-center gap-4 my-3">
|
||||
<img src="{{ $testimonial['avatar_url'] }}"
|
||||
alt="{{ $testimonial['customer_name'] }}"
|
||||
class="w-12 h-12 rounded-full object-cover ring-2 ring-home-background">
|
||||
<div>
|
||||
<h4 class="font-bold text-home-foreground text-sm lg:text-base">
|
||||
{{ $testimonial['customer_name'] }}</h4>
|
||||
<div class="flex text-home-primary text-xs">
|
||||
@for ($i = 0; $i < $testimonial['rating']; $i++)
|
||||
★
|
||||
@endfor
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
class="text-home-neutral text-base lg:text-lg italic leading-relaxed break-words line-clamp-4 lg:line-clamp-3">
|
||||
"{{ $testimonial['content'] }}"
|
||||
</p>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-4 right-4 flex items-center gap-2 z-30">
|
||||
<button
|
||||
class="testimonials-button-prev w-10 h-10 rounded-full bg-home-background/80 backdrop-blur border border-home-background shadow-sm hover:bg-home-background flex justify-center items-center transition-colors group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-home-primary group-hover:scale-110 transition-transform"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="testimonials-button-next w-10 h-10 rounded-full bg-home-primary shadow-md hover:bg-home-primary/80 flex justify-center items-center transition-colors group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-home-background group-hover:scale-110 transition-transform"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{-- Right Section --}}
|
||||
<section
|
||||
class="relative w-full lg:w-[65%] flex flex-col lg:flex-row justify-end items-end py-6 lg:pt-12 lg:pb-8 pr-0 lg:px-10 z-10">
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1591925463023-1ca6b0636780?q=80&w=1632&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D')] bg-cover bg-center bg-no-repeat">
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-t from-home-background/90 via-home-background/50 to-transparent lg:hidden">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full px-6 lg:px-0 my-3 overflow-hidden">
|
||||
<div class="swiper-container best-selling-swiper">
|
||||
<div class="swiper overflow-visible">
|
||||
<div class="swiper-wrapper !ease-linear">
|
||||
@foreach ($bestSellingPerfumes as $perfume)
|
||||
<div class="swiper-slide">
|
||||
<article
|
||||
class="bg-white/90 backdrop-blur-md rounded-2xl shadow-lg hover:shadow-xl transition-all flex flex-row items-center p-4 h-auto lg:h-[250px] group border border-white/20">
|
||||
|
||||
<figure
|
||||
class="w-[40%] lg:w-auto h-32 lg:h-full overflow-hidden rounded-xl bg-gray-100">
|
||||
<img src="{{ $perfume['image'] }}"
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
alt="{{ $perfume['name'] }}">
|
||||
</figure>
|
||||
|
||||
<div
|
||||
class="flex-1 ml-4 lg:ml-6 flex flex-col justify-center space-y-2 lg:space-y-3">
|
||||
<div>
|
||||
<h3 class="text-base lg:text-xl text-black font-bold line-clamp-1">
|
||||
{{ $perfume['name'] }}
|
||||
</h3>
|
||||
<div class="flex items-center gap-2 mt-1">
|
||||
<span class="text-home-primary font-bold text-sm lg:text-lg">
|
||||
{{ $perfume['formatted_price'] }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 mt-0.5">
|
||||
<span
|
||||
class="text-[10px] lg:text-xs font-medium text-gray-400 capitalize">
|
||||
{{ $perfume['formatted_sold_count'] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 mt-auto">
|
||||
<a href="{{ route('product.show', ['slug' => $perfume['slug']]) }}"
|
||||
wire:navigate
|
||||
class="flex-1 flex justify-center items-center py-2 px-3 bg-home-primary rounded-full text-xs lg:text-sm font-medium hover:bg-home-primary/90 hover:scale-[1.02] transition-all text-home-background shadow-sm">
|
||||
Checkout!
|
||||
</a>
|
||||
<a href="{{ route('product.show', ['slug' => $perfume['slug']]) }}"
|
||||
wire:navigate
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 bg-home-primary/10 flex justify-center items-center rounded-full hover:bg-home-primary hover:text-white transition-all flex-shrink-0 group/icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
|
||||
class="size-4 lg:size-5 text-home-primary group-hover/icon:text-white transition-colors">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@ -4,12 +4,11 @@
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20 items-center mb-20">
|
||||
<div class="order-2 lg:order-1 space-y-6">
|
||||
<h2 class="text-3xl md:text-4xl lg:text-5xl font-bold text-gray-900 leading-tight">
|
||||
Temukan Parfum Favorit dengan Kualitas Terbaik
|
||||
Parfum Favorit Kualitas Juara! 🏅✨
|
||||
</h2>
|
||||
<p class="text-base md:text-lg text-gray-600 leading-relaxed">
|
||||
Kami menghadirkan parfum original berkualitas tinggi langsung dari distributor resmi. Setiap aroma
|
||||
dirancang untuk bertahan lama, memberikan pengalaman wangi yang istimewa, dan memastikan setiap
|
||||
tetesnya asli dan terpercaya.
|
||||
Kami bawa parfum original kualitas terbaik langsung dari sumbernya! Wanginya awet, bikin kamu makin
|
||||
pede dan happy seharian! 💃💖
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -8,58 +8,17 @@
|
||||
<x-sections.ui.home.cta :$totalMembersCount :$formattedTotalMembersCount :$latestMembers />
|
||||
</div>
|
||||
|
||||
|
||||
@assets
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
|
||||
<style>
|
||||
#brand-slider img {
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
filter: brightness(0) invert(1);
|
||||
opacity: 0.6;
|
||||
transition: all 0.3s ease;
|
||||
pointer-events: auto;
|
||||
.swiper-wrapper.\!ease-linear {
|
||||
transition-timing-function: linear !important;
|
||||
}
|
||||
|
||||
#brand-slider img:hover {
|
||||
/* filter: brightness(0) invert(1); */
|
||||
filter: none;
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.cursor-grab {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.cursor-grabbing {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.fade-enter {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.fade-enter-active {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
transition: all .4s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(12px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.category-item {
|
||||
opacity: 0;
|
||||
animation: fadeUp 0.45s ease forwards;
|
||||
.brand-swiper .swiper-wrapper {
|
||||
transition-timing-function: linear !important;
|
||||
}
|
||||
</style>
|
||||
@endassets
|
||||
@ -67,130 +26,54 @@
|
||||
@script
|
||||
<script>
|
||||
document.addEventListener('livewire:navigated', () => {
|
||||
const track = document.getElementById('brand-track');
|
||||
const originalChildren = Array.from(track.children);
|
||||
const originalHTML = track.innerHTML;
|
||||
|
||||
if (originalChildren.length === 0) return;
|
||||
|
||||
const itemWidth = originalChildren[0].offsetWidth;
|
||||
const gapStyle = window.getComputedStyle(track).gap;
|
||||
const gap = parseFloat(gapStyle) || 0;
|
||||
const singleSetWidth = (itemWidth + gap) * originalChildren.length;
|
||||
|
||||
let currentWidth = singleSetWidth;
|
||||
while (currentWidth < window.innerWidth + singleSetWidth * 2) {
|
||||
track.innerHTML += originalHTML;
|
||||
currentWidth += singleSetWidth;
|
||||
}
|
||||
|
||||
let position = 0;
|
||||
const speed = 1;
|
||||
let isDown = false;
|
||||
let startX;
|
||||
let scrollLeft;
|
||||
let animationId;
|
||||
|
||||
track.addEventListener('mousedown', (e) => {
|
||||
isDown = true;
|
||||
track.classList.add('cursor-grabbing');
|
||||
track.classList.remove('cursor-grab');
|
||||
startX = e.pageX - track.offsetLeft;
|
||||
scrollLeft = position;
|
||||
e.preventDefault();
|
||||
|
||||
cancelAnimationFrame(animationId);
|
||||
// Initialize Hero Best Selling Swiper
|
||||
const bestSellingSwiper = new Swiper('.best-selling-swiper .swiper', {
|
||||
loop: true,
|
||||
speed: 10000,
|
||||
autoplay: {
|
||||
delay: 0,
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
slidesPerView: 1,
|
||||
spaceBetween: 24,
|
||||
breakpoints: {
|
||||
1024: {
|
||||
slidesPerView: 2,
|
||||
}
|
||||
},
|
||||
allowTouchMove: true,
|
||||
grabCursor: true,
|
||||
freeMode: true,
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', () => {
|
||||
if (!isDown) return;
|
||||
isDown = false;
|
||||
track.classList.remove('cursor-grabbing');
|
||||
track.classList.add('cursor-grab');
|
||||
animate();
|
||||
// Initialize Testimonials Swiper
|
||||
const testimonialsSwiper = new Swiper('.testimonials-swiper', {
|
||||
loop: true,
|
||||
speed: 800,
|
||||
autoplay: {
|
||||
delay: 5000,
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
navigation: {
|
||||
nextEl: '.testimonials-button-next',
|
||||
prevEl: '.testimonials-button-prev',
|
||||
},
|
||||
slidesPerView: 1,
|
||||
grabCursor: true,
|
||||
});
|
||||
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
if (!isDown) return;
|
||||
e.preventDefault();
|
||||
const x = e.pageX - track.offsetLeft;
|
||||
const walk = (x - startX) * 1;
|
||||
position = scrollLeft + walk;
|
||||
track.style.transform = `translateX(${position}px)`;
|
||||
// Initialize Brand Swiper
|
||||
const brandSwiper = new Swiper('.brand-swiper', {
|
||||
loop: true,
|
||||
speed: 3000,
|
||||
autoplay: {
|
||||
delay: 0,
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
slidesPerView: 'auto',
|
||||
allowTouchMove: true,
|
||||
grabCursor: true,
|
||||
});
|
||||
|
||||
function animate() {
|
||||
if (isDown) return;
|
||||
|
||||
position -= speed;
|
||||
|
||||
if (position <= -singleSetWidth) {
|
||||
position += singleSetWidth;
|
||||
} else if (position > 0) {
|
||||
position -= singleSetWidth;
|
||||
}
|
||||
|
||||
track.style.transform = `translateX(${position}px)`;
|
||||
animationId = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
animate();
|
||||
|
||||
const links = document.querySelectorAll('.pagination-link');
|
||||
const container = document.getElementById('category-container');
|
||||
|
||||
if (links.length > 0 && container) {
|
||||
links.forEach(link => {
|
||||
link.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
|
||||
const targetPage = new URL(link.href).searchParams.get('page') || '1';
|
||||
|
||||
// Update all dots (mobile & desktop)
|
||||
links.forEach(l => {
|
||||
const lPage = new URL(l.href).searchParams.get('page') || '1';
|
||||
const isActive = lPage === targetPage;
|
||||
|
||||
const isMobile = l.closest('nav').classList.contains(
|
||||
'lg:hidden');
|
||||
|
||||
if (isActive) {
|
||||
l.classList.add('bg-home-primary');
|
||||
if (isMobile) {
|
||||
l.classList.add('w-8');
|
||||
l.classList.remove('w-2', 'bg-gray-300');
|
||||
} else {
|
||||
l.classList.add('h-12');
|
||||
l.classList.remove('h-6', 'bg-gray-200');
|
||||
}
|
||||
} else {
|
||||
l.classList.remove('bg-home-primary', 'w-8', 'h-12');
|
||||
if (isMobile) {
|
||||
l.classList.add('bg-gray-300', 'w-2');
|
||||
} else {
|
||||
l.classList.add('bg-gray-200', 'h-6');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fetch(link.href)
|
||||
.then(res => res.text())
|
||||
.then(html => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
const newContent = doc.querySelector('#category-container')
|
||||
.innerHTML;
|
||||
|
||||
container.classList.add('fade-enter');
|
||||
setTimeout(() => {
|
||||
container.innerHTML = newContent;
|
||||
container.classList.remove('fade-enter');
|
||||
container.classList.add('fade-enter-active');
|
||||
}, 120);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endscript
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
<section class="overflow-hidden relative pt-4 bg-home-foreground" id="brand-slider" aria-label="Brand Slider">
|
||||
<div class="flex gap-6 items-center cursor-grab active:cursor-grabbing" id="brand-track" style="touch-action: pan-y;">
|
||||
@foreach ($brands as $brand)
|
||||
<div class="flex flex-col items-center flex-shrink-0 py-5">
|
||||
<img src="{{ $brand['image'] }}" alt="{{ $brand['name'] }}" class="w-12 h-12 object-contain rounded-xl">
|
||||
<h3 class="mt-1 text-base font-bold text-center text-white">
|
||||
{{ $brand['name'] }}
|
||||
</h3>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@ -1,4 +0,0 @@
|
||||
<main class="flex flex-col lg:flex-row justify-between lg:min-h-screen w-full relative overflow-x-hidden">
|
||||
@include('livewire.home.partials.jumbotron.left')
|
||||
@include('livewire.home.partials.jumbotron.right')
|
||||
</main>
|
||||
@ -1,49 +0,0 @@
|
||||
<section
|
||||
class="relative w-full lg:w-[35%] pt-24 lg:pt-0 py-0 px-6 lg:px-10 lg:pb-8 flex flex-col lg:min-h-screen lg:justify-between space-y-6 lg:space-y-0 z-20 bg-white">
|
||||
<div class="flex flex-col justify-center flex-1 space-y-6 lg:pt-48">
|
||||
@if ($totalMembersCount > 0)
|
||||
<header class="flex flex-wrap items-center gap-4">
|
||||
<div class="flex items-center">
|
||||
@foreach ($latestMembers as $member)
|
||||
<figure
|
||||
class="border-2 border-home-background w-8 h-8 lg:w-10 lg:h-10 rounded-full overflow-hidden shadow-sm {{ !$loop->first ? '-ms-2' : '' }}">
|
||||
<img src="https://ui-avatars.com/api/?name={{ urlencode($member->user->name ?? $member->name) }}&background=random&color=fff"
|
||||
alt="{{ $member->user->name ?? $member->name }}" class="w-full h-full object-cover">
|
||||
</figure>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="border border-home-primary/30 py-1 px-3 rounded-full text-sm lg:text-lg text-home-primary">
|
||||
<span class="text-home-primary font-bold">{{ $totalMembersCount }}</span>+ Customer Mempercayai Kami
|
||||
</div>
|
||||
</header>
|
||||
@endif
|
||||
|
||||
<h1 class="text-home-foreground text-4xl lg:text-6xl leading-tight text-start font-bold">
|
||||
Temukan<br>
|
||||
<span class="italic font-serif font-light text-home-primary">
|
||||
signature scent-mu
|
||||
</span> hari ini
|
||||
</h1>
|
||||
|
||||
<p class="text-lg w-[90%] lg:w-full lg:text-lg text-home-foreground/80">
|
||||
Setiap parfum punya cerita. Pilih aroma yang sesuai dengan gaya hidupmu dan kesan yang ingin kamu tampilkan.
|
||||
</p>
|
||||
|
||||
<div class="flex items-center">
|
||||
<a href="{{ route('product.index') }}" wire:navigate
|
||||
class="bg-home-primary text-home-background py-3 px-6 rounded-full hover:shadow-lg hover:shadow-home-primary/30 transition-all font-medium">
|
||||
Jelajahi Toko
|
||||
</a>
|
||||
<span class="w-8 h-[2px] bg-home-primary/20"></span>
|
||||
<a href="{{ route('product.index') }}" wire:navigate
|
||||
class="flex items-center justify-center w-12 h-12 border border-home-primary rounded-full hover:bg-home-background transition-colors group bg-transparent">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" class="w-5 h-5 text-home-primary">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@include('livewire.home.partials.jumbotron.testimonial')
|
||||
</section>
|
||||
@ -1,58 +0,0 @@
|
||||
<section
|
||||
class="relative w-full lg:w-[65%] min-h-[600px] lg:min-h-screen hidden lg:flex flex-col lg:flex-row justify-end items-end pb-8 pr-0 lg:px-10 z-10">
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1591925463023-1ca6b0636780?q=80&w=1632&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D')] bg-cover bg-center bg-no-repeat">
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-t from-home-background/90 via-home-background/50 to-transparent lg:hidden">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full px-6 lg:px-0 grid grid-cols-1 lg:grid-cols-2 gap-6 mb-24 lg:mb-0">
|
||||
|
||||
@foreach ($bestSellingPerfumes as $perfume)
|
||||
<article
|
||||
class="bg-white backdrop-blur-md rounded-2xl shadow-lg hover:shadow-xl transition-shadow flex flex-row items-center p-4 h-auto lg:h-[250px] group">
|
||||
|
||||
<figure class="w-[40%] lg:w-auto h-32 lg:h-full overflow-hidden rounded-xl bg-gray-100">
|
||||
<img src="{{ $perfume['image'] }}"
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
alt="{{ $perfume['name'] }}">
|
||||
</figure>
|
||||
|
||||
<div class="flex-1 ml-4 lg:ml-6 flex flex-col justify-center space-y-2 lg:space-y-3">
|
||||
<div>
|
||||
<h3 class="text-base lg:text-xl text-black font-bold line-clamp-1">{{ $perfume['name'] }}
|
||||
</h3>
|
||||
<div class="flex items-center gap-2 mt-1">
|
||||
<span class="text-home-primary font-bold text-sm lg:text-lg">
|
||||
Rp {{ number_format($perfume['sale_price'], 0, ',', '.') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 mt-0.5">
|
||||
<span class="text-[10px] lg:text-xs font-medium text-gray-400 capitalize">
|
||||
{{ number_format($perfume['items_sum_quantity'], 0, ',', '.') }} Terjual
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 mt-auto">
|
||||
<a href="{{ route('product.show', ['slug' => $perfume['slug']]) }}" wire:navigate
|
||||
class="flex-1 justify-center items-center border border-gray-200 py-2 px-3 bg-home-primary rounded-full text-xs lg:text-sm font-medium hover:bg-home-primary/80 transition-colors text-home-background">
|
||||
Belanja Sekarang
|
||||
</a>
|
||||
<a href="{{ route('product.show', ['slug' => $perfume['slug']]) }}" wire:navigate
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 bg-home-primary flex justify-center items-center rounded-full hover:bg-home-primary/80 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="size-4 lg:size-5 text-home-background">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
</section>
|
||||
@ -1,85 +0,0 @@
|
||||
<div class="mb-5 lg:mb-0">
|
||||
@if (!empty($testimonials))
|
||||
<article x-data="{
|
||||
current: 0,
|
||||
total: {{ count($testimonials) }},
|
||||
timer: null,
|
||||
start() {
|
||||
this.timer = setInterval(() => this.next(), 6000);
|
||||
},
|
||||
stop() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
next() {
|
||||
this.current = (this.current + 1) % this.total;
|
||||
this.stop();
|
||||
this.start();
|
||||
},
|
||||
prev() {
|
||||
this.current = (this.current - 1 + this.total) % this.total;
|
||||
this.stop();
|
||||
this.start();
|
||||
},
|
||||
getClass(index) {
|
||||
const isCurrent = this.current === index;
|
||||
let isBefore = index < this.current;
|
||||
|
||||
if (this.current === 0 && index === this.total - 1) isBefore = true;
|
||||
if (this.current === this.total - 1 && index === 0) isBefore = false;
|
||||
|
||||
return {
|
||||
'opacity-100 z-10 translate-x-0': isCurrent,
|
||||
'opacity-0 z-0': !isCurrent,
|
||||
'-translate-x-full': !isCurrent && isBefore,
|
||||
'translate-x-full': !isCurrent && !isBefore
|
||||
};
|
||||
}
|
||||
}" x-init="start()"
|
||||
class="relative w-full mt-auto h-[300px] lg:h-[250px] bg-home-background/40 backdrop-blur-md border border-home-secondary rounded-2xl z-30 shadow-sm overflow-hidden transition-all duration-300">
|
||||
|
||||
<div class="relative w-full h-full py-0 px-6">
|
||||
@foreach ($testimonials as $index => $testimonial)
|
||||
<div class="testimonial-slide absolute inset-0 py-0 px-6 flex flex-col justify-center transition-all duration-500 ease-in-out {{ $index === 0 ? 'opacity-100 z-10 translate-x-0' : 'opacity-0 z-0 translate-x-full' }}"
|
||||
:class="getClass({{ $index }})">
|
||||
<div class="flex items-center gap-4 mb-3">
|
||||
<img src="https://ui-avatars.com/api/?name={{ urlencode($testimonial['user']['customer']['name'] ?? 'User') }}&background=random&color=fff"
|
||||
alt="{{ $testimonial['user']['customer']['name'] ?? 'User' }}"
|
||||
class="w-12 h-12 rounded-full object-cover ring-2 ring-home-background">
|
||||
<div>
|
||||
<h4 class="font-bold text-home-foreground text-sm lg:text-base">
|
||||
{{ $testimonial['user']['customer']['name'] ?? 'User' }}</h4>
|
||||
<div class="flex text-home-primary text-xs">
|
||||
@for ($i = 0; $i < $testimonial['rating']; $i++)
|
||||
★
|
||||
@endfor
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-home-neutral text-base lg:text-lg italic leading-relaxed">
|
||||
"{{ $testimonial['content'] }}"
|
||||
</p>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-4 right-4 flex items-center gap-2 z-30">
|
||||
<button @click="prev()"
|
||||
class="w-10 h-10 rounded-full bg-home-background/80 backdrop-blur border border-home-background shadow-sm hover:bg-home-background flex justify-center items-center transition-colors group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-home-primary group-hover:scale-110 transition-transform" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<button @click="next()"
|
||||
class="w-10 h-10 rounded-full bg-home-primary shadow-md hover:bg-home-primary/80 flex justify-center items-center transition-colors group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-4 h-4 text-home-background group-hover:scale-110 transition-transform" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
@endif
|
||||
</div>
|
||||
@ -17,7 +17,7 @@ class="w-full h-full object-cover">
|
||||
|
||||
{{-- Search Bar --}}
|
||||
<div class="max-w-xl mx-auto relative group">
|
||||
<input wire:model.live.debounce.500="search" type="search" placeholder="Cari produk..."
|
||||
<input wire:model.live.debounce.500="search" type="search" placeholder="Cari produk impianmu... 🎁✨"
|
||||
aria-label="Cari produk"
|
||||
class="w-full py-4 pl-6 pr-14 rounded-full bg-white/10 backdrop-blur-md border border-white/20 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-home-primary focus:bg-white/20 transition-all shadow-lg text-lg">
|
||||
<button
|
||||
@ -38,7 +38,8 @@ class="absolute right-2 top-2 bottom-2 w-10 h-10 bg-home-primary rounded-full fl
|
||||
<aside class="hidden lg:block lg:col-span-1 space-y-8">
|
||||
<div class="bg-white rounded-2xl border border-home-foreground/10 p-6 shadow-sm sticky top-32">
|
||||
{{-- Product type filter --}}
|
||||
<h3 class="font-bold text-home-primary mb-6 border-b border-home-foreground/10 pb-4">Jenis Produk
|
||||
<h3 class="font-bold text-home-primary mb-6 border-b border-home-foreground/10 pb-4">Mau Cari Apa?
|
||||
🛍️
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
@foreach ([['value' => 'perfume', 'label' => 'Parfum'], ['value' => 'bottle', 'label' => 'Botol'], ['value' => 'arabian', 'label' => 'Arabian']] as $type)
|
||||
@ -55,7 +56,8 @@ class="ml-3 text-home-foreground/70 group-hover:text-home-primary transition-col
|
||||
<div class="border-t border-home-foreground/10 my-8"></div>
|
||||
|
||||
@if ($productType === 'perfume')
|
||||
<h3 class="font-bold text-home-primary mb-6 border-b border-home-foreground/10 pb-4">Kategori
|
||||
<h3 class="font-bold text-home-primary mb-6 border-b border-home-foreground/10 pb-4">Pilih
|
||||
Kategori ✨
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
@foreach ($availableCategories as $id => $name)
|
||||
@ -72,7 +74,8 @@ class="ml-3 text-home-foreground/70 group-hover:text-home-primary transition-col
|
||||
<div class="border-t border-home-foreground/10 my-8"></div>
|
||||
@endif
|
||||
|
||||
<h3 class="font-bold text-home-primary mb-6 border-b border-home-foreground/10 pb-4">Harga</h3>
|
||||
<h3 class="font-bold text-home-primary mb-6 border-b border-home-foreground/10 pb-4">Budget Kamu 💰
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
@foreach (['< 100k', '100k - 500k', '500k - 1jt', '> 1jt'] as $price)
|
||||
<label class="flex items-center cursor-pointer group">
|
||||
@ -220,7 +223,7 @@ class="w-10 h-10 rounded-lg bg-home-foreground text-white flex items-center just
|
||||
|
||||
<section class="mt-24 pt-16 border-t border-home-foreground/10">
|
||||
<div class="flex items-center justify-between mb-12">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-home-primary">Mungkin Anda Suka</h2>
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-home-primary">Pilihan Spesial Buat Kamu ❤️</h2>
|
||||
<div class="flex gap-3">
|
||||
<button id="slideLeft"
|
||||
class="w-12 h-12 rounded-full border border-home-foreground/10 flex items-center justify-center text-home-foreground hover:bg-home-primary hover:text-white hover:border-home-primary transition-all shadow-sm">
|
||||
|
||||
@ -103,7 +103,7 @@ class="text-xs font-bold text-home-primary bg-home-primary/10 px-3 py-1 rounded-
|
||||
{{-- Description --}}
|
||||
@if ($product->description)
|
||||
<div class="space-y-3">
|
||||
<h3 class="text-lg font-bold text-home-foreground">Deskripsi</h3>
|
||||
<h3 class="text-lg font-bold text-home-foreground">Tentang Produk ✨</h3>
|
||||
<p class="text-home-foreground/70 leading-relaxed text-justify">
|
||||
{!! $product->description !!}
|
||||
</p>
|
||||
@ -133,7 +133,7 @@ class="text-xs font-bold text-home-primary bg-home-primary/10 px-3 py-1 rounded-
|
||||
|
||||
{{-- Share Buttons --}}
|
||||
<div class="flex items-center gap-4 pt-6 border-t border-home-foreground/10">
|
||||
<span class="text-sm font-bold text-home-foreground">Bagikan:</span>
|
||||
<span class="text-sm font-bold text-home-foreground">Bagikan ke temanmu! 📲</span>
|
||||
|
||||
{{-- WhatsApp --}}
|
||||
<a href="https://wa.me/?text={{ urlencode($product->name . ' - ' . url()->current()) }}"
|
||||
@ -174,8 +174,8 @@ class="w-10 h-10 rounded-full border border-home-foreground/10 flex items-center
|
||||
@if (count($relatedProducts) > 0)
|
||||
<section class="mt-24 pt-16 border-t border-home-foreground/10">
|
||||
<div class="mb-12">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-home-primary">Produk Terkait</h2>
|
||||
<p class="text-home-foreground/60 mt-2">Produk lain yang mungkin Anda suka</p>
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-home-primary">Produk Lainnya 🌟</h2>
|
||||
<p class="text-home-foreground/60 mt-2">Mungkin kamu juga suka ini nih! 😉</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
|
||||
@ -11,6 +11,11 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<div class="mb-6">
|
||||
<flux:input icon="magnifying-glass" placeholder="Cari item..." wire:model.live.debounce.500ms="search"
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<flux:card>
|
||||
<flux:table>
|
||||
@ -27,7 +32,7 @@
|
||||
<flux:table.cell>{{ $item->itemable?->name }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $item->qty_system }}</flux:table.cell>
|
||||
<flux:table.cell>
|
||||
<flux:input wire:model.blur="form.outlet_stock.{{ $item->id }}"
|
||||
<flux:input wire:model.live.debounce.500ms="form.outlet_stock.{{ $item->id }}"
|
||||
x-mask:dynamic="$money($input, ',')" autocomplete="off" />
|
||||
</flux:table.cell>
|
||||
<flux:table.cell variant="strong">
|
||||
@ -40,9 +45,15 @@
|
||||
</flux:card>
|
||||
|
||||
<flux:card>
|
||||
<flux:textarea label="Catatan" placeholder="Blue emotion tumpah 10ml" wire:model.blur="form.note"
|
||||
<flux:textarea label="Catatan" placeholder="Blue emotion tumpah 10ml" wire:model="form.note"
|
||||
autocomplete="off" rows="2" />
|
||||
</flux:card>
|
||||
|
||||
<div class="flex justify-start">
|
||||
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="save">
|
||||
Simpan
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</flux:main>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user