feat: Implement full voucher and outlet listing with search, detail modals, and UI enhancements
This commit is contained in:
parent
93d86a30c0
commit
71568a7ecd
@ -11,7 +11,6 @@
|
||||
use App\Models\PerfumeFeature;
|
||||
use App\Models\Testimonial;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
@ -50,7 +49,7 @@ public function mount(): void
|
||||
->take(2)
|
||||
->get()
|
||||
->map(function (Perfume $perfume) {
|
||||
$perfume->image = $perfume->getFirstMediaUrl('image') ? Storage::url($perfume->getFirstMediaUrl('image')) : asset('assets/images/logo.png');
|
||||
$perfume->image = $perfume->getFirstMediaUrl('image') ? $perfume->getFirstMediaUrl('image') : asset('assets/images/logo.png');
|
||||
$perfume->concentration_label = $perfume->concentration->label();
|
||||
|
||||
return $perfume;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Home;
|
||||
|
||||
use App\Models\Outlet as OutletModel;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
@ -11,8 +12,48 @@
|
||||
])]
|
||||
class Outlet extends Component
|
||||
{
|
||||
public array $outlets = [];
|
||||
|
||||
public string $search = '';
|
||||
|
||||
public ?OutletModel $selectedOutlet = null;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->outlets = OutletModel::with(['facilities', 'openingHours'])
|
||||
->operational()
|
||||
->when($this->search, function ($query) {
|
||||
$query->where(function ($query) {
|
||||
$query->where('name', 'like', '%'.$this->search.'%')
|
||||
->orWhere('address', 'like', '%'.$this->search.'%');
|
||||
});
|
||||
})
|
||||
->latest()
|
||||
->get()
|
||||
->map(function ($outlet) {
|
||||
$outlet->image = $outlet->getFirstMediaUrl('featured_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', 'media'])->find($outletId);
|
||||
}
|
||||
|
||||
public function closeModal(): void
|
||||
{
|
||||
$this->selectedOutlet = null;
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.home.outlets');
|
||||
return view('livewire.home.outlets', [
|
||||
'pageTitle' => 'Outlet',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
|
||||
use App\Models\Outlet;
|
||||
use App\Models\Voucher as VoucherModel;
|
||||
use App\Traits\Components\WithToast;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
@ -14,7 +16,7 @@
|
||||
])]
|
||||
class Voucher extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
use WithPagination, WithToast;
|
||||
|
||||
public $outlets;
|
||||
|
||||
@ -24,6 +26,10 @@ class Voucher extends Component
|
||||
|
||||
public ?string $sort = null;
|
||||
|
||||
public ?VoucherModel $selectedVoucher = null;
|
||||
|
||||
public bool $showDetailModal = false;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->outlets = Outlet::query()->operational()->pluck('name', 'id');
|
||||
@ -44,6 +50,91 @@ public function updatedSort(): void
|
||||
$this->resetPage();
|
||||
}
|
||||
|
||||
public function showDetail(string $hashId): void
|
||||
{
|
||||
$this->selectedVoucher = VoucherModel::byHash($hashId);
|
||||
$this->selectedVoucher->load('outlets');
|
||||
|
||||
$this->selectedVoucher->min_purchase_formatted = formatCurrencyNumber($this->selectedVoucher->min_purchase, 'Rp');
|
||||
$this->selectedVoucher->end_date_formatted = formatDateLocalized($this->selectedVoucher->end_date);
|
||||
|
||||
$this->showDetailModal = true;
|
||||
}
|
||||
|
||||
public function closeDetail(): void
|
||||
{
|
||||
$this->showDetailModal = false;
|
||||
$this->selectedVoucher = null;
|
||||
}
|
||||
|
||||
public function claim(string $hashId): void
|
||||
{
|
||||
if (! auth()->check()) {
|
||||
$this->redirect(route('login'), navigate: true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$voucher = VoucherModel::byHash($hashId);
|
||||
$user = auth()->user();
|
||||
|
||||
// Basic checks
|
||||
if (! $voucher) {
|
||||
$this->toast('Voucher tidak ditemukan.', 'Gagal', 'danger');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($voucher->quota !== null && $voucher->available_count >= $voucher->quota) {
|
||||
$this->toast('Klaim gagal! Kuota voucher sudah habis.', 'Gagal', 'danger');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user already reached limit
|
||||
$claimedCount = $user->vouchers()->where('voucher_id', $voucher->id)->exists();
|
||||
if ($voucher->limit_per_user !== null) {
|
||||
$claimedCount = $user->vouchers()->where('voucher_id', $voucher->id)->count();
|
||||
if ($claimedCount >= $voucher->limit_per_user) {
|
||||
$this->toast('Anda sudah mencapai batas klaim untuk voucher ini.', 'Peringatan', 'warning');
|
||||
|
||||
return;
|
||||
}
|
||||
} elseif ($claimedCount) {
|
||||
// If limit_per_user is null, assume it can only be claimed once?
|
||||
// Or can it be claimed multiple times?
|
||||
// Usually vouchers are one-time claim per user unless specified.
|
||||
// Based on common logic, if limit_per_user is null, maybe it's unlimited?
|
||||
// But usually it's 1. Let's stick to the limit_per_user check.
|
||||
}
|
||||
|
||||
// Check if voucher is for specific tier
|
||||
$userTierId = $user->membership?->tier_id;
|
||||
if ($voucher->tiers()->exists() && ! $voucher->tiers()->where('tiers.id', $userTierId)->exists()) {
|
||||
$this->toast('Voucher ini tidak tersedia untuk tier Anda.', 'Gagal', 'danger');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Claim with transaction
|
||||
DB::transaction(function () use ($user, $voucher) {
|
||||
// Re-check quota inside transaction
|
||||
$v = VoucherModel::where('id', $voucher->id)->lockForUpdate()->first();
|
||||
if ($v->quota !== null && $v->available_count >= $v->quota) {
|
||||
throw new \Exception('Kuota voucher sudah habis.');
|
||||
}
|
||||
|
||||
$user->vouchers()->attach($v->id);
|
||||
$v->increment('available_count');
|
||||
});
|
||||
|
||||
$this->toast('Voucher berhasil diklaim! Silakan cek di menu Voucher Saya.', 'Berhasil', 'success');
|
||||
} catch (\Throwable $e) {
|
||||
$this->toast($e->getMessage(), 'Gagal', 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$vouchers = VoucherModel::where(function ($query) {
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Enums\PriceRequestStatus;
|
||||
use App\Models\PriceRequest;
|
||||
use App\Settings\ContactSettings;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
@ -326,6 +327,7 @@ public function boot(): void
|
||||
$view->with([
|
||||
'sidebar' => $sidebar,
|
||||
'header' => $header,
|
||||
'setting' => app(ContactSettings::class),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
@ -40,26 +40,32 @@ 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">Quick Links</h3>
|
||||
<h3 class="text-white font-bold mb-6 text-sm uppercase tracking-wider">Tautan Cepat</h3>
|
||||
<ul class="space-y-4 text-sm text-gray-400">
|
||||
<li><a href="#"
|
||||
<li>
|
||||
<a href="{{ route('homepage') }}" wire:navigate
|
||||
class="hover:text-home-secondary transition-colors duration-300 flex items-center gap-2">Beranda</a>
|
||||
</li>
|
||||
<li><a href="#"
|
||||
<li>
|
||||
<a href="{{ route('product.index') }}" wire:navigate
|
||||
class="hover:text-home-secondary transition-colors duration-300 flex items-center gap-2">Katalog
|
||||
Produk</a></li>
|
||||
<li><a href="#"
|
||||
class="hover:text-home-secondary transition-colors duration-300 flex items-center gap-2">Tentang
|
||||
Kami</a></li>
|
||||
<li><a href="#"
|
||||
class="hover:text-home-secondary transition-colors duration-300 flex items-center gap-2">Kontak</a>
|
||||
Produk</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('outlet') }}" wire:navigate
|
||||
class="hover:text-home-secondary transition-colors duration-300 flex items-center gap-2">Outtlet
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('article.index') }}" wire:navigate
|
||||
class="hover:text-home-secondary transition-colors duration-300 flex items-center gap-2">Artikel</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<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">Contact Us</h3>
|
||||
<h3 class="text-white font-bold mb-6 text-sm uppercase tracking-wider">Kontak Kami</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"
|
||||
@ -70,8 +76,7 @@ class="hover:text-home-secondary transition-colors duration-300 flex items-cente
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
</svg></div>
|
||||
<span
|
||||
class="leading-relaxed">{{ $outlet->note ?? 'Jalan Wangi Semerbak No. 123, Kota Parfum, Indonesia' }}</span>
|
||||
<span class="leading-relaxed">{{ $setting->address }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div><svg class="w-5 h-5 text-home-primary" fill="none" stroke="currentColor"
|
||||
@ -80,7 +85,7 @@ class="leading-relaxed">{{ $outlet->note ?? 'Jalan Wangi Semerbak No. 123, Kota
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z">
|
||||
</path>
|
||||
</svg></div>
|
||||
<span>{{ $outlet->email ?? 'admin@yadiparfum.com' }}</span>
|
||||
<span>{{ $setting->email }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div><svg class="w-5 h-5 text-home-primary" fill="none" stroke="currentColor"
|
||||
@ -89,7 +94,7 @@ class="leading-relaxed">{{ $outlet->note ?? 'Jalan Wangi Semerbak No. 123, Kota
|
||||
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z">
|
||||
</path>
|
||||
</svg></div>
|
||||
<span>{{ $outlet->phone ?? '+62 812-3456-7890' }}</span>
|
||||
<span>{{ $setting->phone_number }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -98,12 +103,12 @@ class="leading-relaxed">{{ $outlet->note ?? 'Jalan Wangi Semerbak No. 123, Kota
|
||||
|
||||
<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. All rights reserved.
|
||||
© {{ date('Y') }} Yadi Parfum. Semua hak dilindungi undang-undang.
|
||||
</div>
|
||||
<div class="flex gap-6 text-sm text-gray-500">
|
||||
{{-- <div class="flex gap-6 text-sm text-gray-500">
|
||||
<a href="#" class="hover:text-white transition-colors">Privacy Policy</a>
|
||||
<a href="#" class="hover:text-white transition-colors">Terms of Service</a>
|
||||
</div>
|
||||
</div> --}}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
<section class="relative h-[500px] flex items-center justify-center overflow-hidden">
|
||||
{{-- Background Image --}}
|
||||
<div class="absolute inset-0 z-0">
|
||||
<img src="https://images.unsplash.com/photo-1752042143353-e6dcc822a023?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="Perfume Background" class="w-full h-full object-cover">
|
||||
<img src="https://images.pexels.com/photos/4057663/pexels-photo-4057663.jpeg" alt="Article Background"
|
||||
class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black/40"></div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
<section class="relative h-[500px] flex items-center justify-center overflow-hidden">
|
||||
{{-- Background Image --}}
|
||||
<div class="absolute inset-0 z-0">
|
||||
<img src="https://images.unsplash.com/photo-1752042143353-e6dcc822a023?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="Perfume Background" class="w-full h-full object-cover">
|
||||
<img src="https://images.pexels.com/photos/2789781/pexels-photo-2789781.jpeg" alt="Perfume Background"
|
||||
class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black/40"></div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,95 +1,57 @@
|
||||
<div>
|
||||
@php
|
||||
$outlets = [
|
||||
[
|
||||
'id' => 1,
|
||||
'name' => 'Yadi Parfum Central Park',
|
||||
'address' => 'Central Park Mall, Lt. LG, Unit 112, Jl. Letjen S. Parman No.28, Jakarta Barat',
|
||||
'city' => 'Jakarta Barat',
|
||||
'phone' => '+62 812-3456-7890',
|
||||
'hours' => '10:00 - 22:00 WIB',
|
||||
'image' => 'https://placehold.co/800x600/png?text=Boutique+Central+Park',
|
||||
'facilities' => ['Consultation', 'Refill Station', 'Engraving'],
|
||||
'status' => 'Buka',
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'name' => 'Yadi Parfum Senayan City',
|
||||
'address' => 'Senayan City, Lt. 1, Unit 45, Jl. Asia Afrika Lot 19, Jakarta Pusat',
|
||||
'city' => 'Jakarta Pusat',
|
||||
'phone' => '+62 812-9876-5432',
|
||||
'hours' => '10:00 - 22:00 WIB',
|
||||
'image' => 'https://placehold.co/800x600/png?text=Boutique+Senayan',
|
||||
'facilities' => ['VIP Lounge', 'Consultation'],
|
||||
'status' => 'Buka',
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'name' => 'Yadi Parfum Bandung',
|
||||
'address' => 'Paris Van Java, Resort Level, Jl. Sukajadi No.131-139, Bandung',
|
||||
'city' => 'Bandung',
|
||||
'phone' => '+62 822-1122-3344',
|
||||
'hours' => '10:00 - 21:00 WIB',
|
||||
'image' => 'https://placehold.co/800x600/png?text=Boutique+Bandung',
|
||||
'facilities' => ['Refill Station'],
|
||||
'status' => 'Tutup',
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div class="flex flex-col min-h-screen bg-white font-sans">
|
||||
|
||||
{{-- Hero Section --}}
|
||||
<section class="relative h-[500px] flex items-center justify-center overflow-hidden">
|
||||
{{-- Background Image --}}
|
||||
<div class="absolute inset-0 z-0">
|
||||
<img src="https://images.unsplash.com/photo-1541643600914-78b084683601?q=80&w=1469&auto=format&fit=crop"
|
||||
alt="Outlet Background" class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black/50"></div>
|
||||
</div>
|
||||
|
||||
{{-- Content --}}
|
||||
<div class="relative z-10 container mx-auto px-6 text-center">
|
||||
<span class="text-home-primary font-bold tracking-widest text-xs uppercase mb-3 block">Lokasi Butik</span>
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6">Temukan Butik Terdekat</h1>
|
||||
<p class="text-white/80 text-lg md:text-xl max-w-2xl mx-auto mb-10">
|
||||
Rasakan pengalaman langsung kemewahan aroma kami. Kunjungi butik kami untuk konsultasi personal.
|
||||
</p>
|
||||
|
||||
{{-- Search Bar --}}
|
||||
<div class="max-w-xl mx-auto relative group">
|
||||
<input type="text" placeholder="Masukkan kota atau nama area..."
|
||||
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
|
||||
class="absolute right-2 top-2 bottom-2 w-10 h-10 bg-home-primary rounded-full flex items-center justify-center text-white hover:bg-white hover:text-home-primary transition-all duration-300">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<section class="relative h-[500px] flex items-center justify-center overflow-hidden">
|
||||
{{-- Background Image --}}
|
||||
<div class="absolute inset-0 z-0">
|
||||
<img src="https://images.unsplash.com/photo-1761095870661-c4ae15cac605?q=80&w=880&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="Outlet Background" class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black/50"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<main class="flex-grow px-6 lg:px-32 xl:px-48 py-24">
|
||||
{{-- Content --}}
|
||||
<div class="relative z-10 container mx-auto px-6 text-center">
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6">Temukan Outlet Terdekat</h1>
|
||||
<p class="text-white/80 text-lg md:text-xl max-w-2xl mx-auto mb-10">
|
||||
Rasakan pengalaman langsung kemewahan aroma kami. Kunjungi outlet kami untuk konsultasi personal.
|
||||
</p>
|
||||
|
||||
{{-- Search Bar --}}
|
||||
<div class="max-w-xl mx-auto relative group">
|
||||
<input type="text" wire:model.live="search" placeholder="Masukkan kota atau nama area..."
|
||||
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
|
||||
class="absolute right-2 top-2 bottom-2 w-10 h-10 bg-home-primary rounded-full flex items-center justify-center text-white hover:bg-white hover:text-home-primary transition-all duration-300">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<main class="flex-grow px-6 lg:px-32 xl:px-48 py-24">
|
||||
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-24">
|
||||
@foreach ($outlets as $outlet)
|
||||
<article
|
||||
class="group bg-white rounded-3xl border border-home-foreground/10 overflow-hidden hover:shadow-2xl hover:shadow-home-primary/10 hover:-translate-y-2 transition-all duration-500 flex flex-col h-full">
|
||||
<article wire:click="showDetail({{ $outlet['id'] }})"
|
||||
class="group bg-white rounded-3xl border border-home-foreground/10 overflow-hidden hover:shadow-2xl hover:shadow-home-primary/10 hover:-translate-y-2 transition-all duration-500 flex flex-col h-full cursor-pointer">
|
||||
|
||||
<div class="relative h-64 overflow-hidden">
|
||||
<div
|
||||
class="absolute inset-0 bg-home-primary/10 mix-blend-multiply opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-10">
|
||||
</div>
|
||||
<img src="{{ $outlet['image'] }}" alt="{{ $outlet['name'] }}"
|
||||
<img src="{{ $outlet['image'] ?: 'https://placehold.co/800x600/png?text=' . urlencode($outlet['name']) }}"
|
||||
alt="{{ $outlet['name'] }}"
|
||||
class="w-full h-full object-cover transform group-hover:scale-110 transition-transform duration-700">
|
||||
|
||||
<div class="absolute top-4 left-4 z-20">
|
||||
<span
|
||||
class="px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-wide backdrop-blur-md bg-white/90 shadow-sm
|
||||
{{ $outlet['status'] == 'Buka' ? 'text-green-700' : 'text-red-700' }}">
|
||||
{{ $outlet['status'] }}
|
||||
text-{{ $outlet['status_color'] }}-700">
|
||||
{{ $outlet['status_label'] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -106,45 +68,91 @@ class="text-xl font-bold text-home-foreground mb-2 group-hover:text-home-primary
|
||||
@foreach ($outlet['facilities'] as $facility)
|
||||
<span
|
||||
class="text-[10px] px-2 py-1 border border-home-foreground/10 rounded text-home-foreground/50">
|
||||
{{ $facility }}
|
||||
{{ $facility['name'] }}
|
||||
</span>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid grid-cols-2 gap-4 text-xs text-home-foreground/70 mb-6 pt-4 border-t border-home-foreground/5">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-home-primary" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
{{ $outlet['hours'] }}
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-home-primary" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z">
|
||||
</path>
|
||||
</svg>
|
||||
Call Store
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-auto flex gap-3" x-on:click.stop>
|
||||
@if ($outlet['maps_url'])
|
||||
<a href="{{ $outlet['maps_url'] }}" target="_blank"
|
||||
class="flex-1 py-3 rounded-xl bg-home-primary text-white text-sm font-bold hover:bg-home-secondary hover:text-home-primary transition-colors shadow-lg shadow-home-primary/20 flex items-center justify-center">
|
||||
Petunjuk Arah
|
||||
</a>
|
||||
@endif
|
||||
{{-- Share Menu --}}
|
||||
<div class="relative" x-data="{ open: false }">
|
||||
<button @click="open = !open" @click.outside="open = false"
|
||||
class="w-12 h-12 rounded-xl border border-home-foreground/10 flex items-center justify-center text-home-foreground hover:border-home-primary hover:text-home-primary transition-all">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="mt-auto flex gap-3">
|
||||
<button
|
||||
class="flex-1 py-3 rounded-xl bg-home-primary text-white text-sm font-bold hover:bg-home-secondary hover:text-home-primary transition-colors shadow-lg shadow-home-primary/20">
|
||||
Petunjuk Arah
|
||||
</button>
|
||||
<button
|
||||
class="w-12 h-12 rounded-xl border border-home-foreground/10 flex items-center justify-center text-home-foreground hover:border-home-primary hover:text-home-primary transition-all">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
{{-- Dropdown Menu --}}
|
||||
<div x-show="open" x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95 translate-y-2"
|
||||
x-transition:enter-end="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 scale-95 translate-y-2"
|
||||
class="absolute bottom-full right-0 mb-3 w-48 bg-white rounded-2xl shadow-xl border border-home-foreground/5 py-2 z-30"
|
||||
style="display: none;">
|
||||
|
||||
<div
|
||||
class="px-4 py-2 text-[10px] font-bold text-home-foreground/40 uppercase tracking-widest border-b border-home-foreground/5 mb-1">
|
||||
Bagikan Ke
|
||||
</div>
|
||||
|
||||
{{-- WhatsApp --}}
|
||||
<a href="https://wa.me/?text={{ urlencode('Cek outlet ' . $outlet['name'] . ' di Pangestu Yadi Parfum: ' . ($outlet['maps_url'] ?: url()->current())) }}"
|
||||
target="_blank"
|
||||
class="flex items-center gap-3 px-4 py-2 text-sm text-home-foreground hover:bg-home-primary/5 hover:text-home-primary transition-colors">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L0 24l6.335-1.662c1.72.94 3.659 1.437 5.634 1.437h.005c6.515 0 11.825-5.309 11.828-11.825a11.823 11.823 0 00-3.414-8.369" />
|
||||
</svg>
|
||||
WhatsApp
|
||||
</a>
|
||||
|
||||
{{-- Facebook --}}
|
||||
<a href="https://www.facebook.com/sharer/sharer.php?u={{ urlencode($outlet['maps_url'] ?: url()->current()) }}"
|
||||
target="_blank"
|
||||
class="flex items-center gap-3 px-4 py-2 text-sm text-home-foreground hover:bg-home-primary/5 hover:text-home-primary transition-colors">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" />
|
||||
</svg>
|
||||
Facebook
|
||||
</a>
|
||||
|
||||
{{-- X (Twitter) --}}
|
||||
<a href="https://twitter.com/intent/tweet?url={{ urlencode($outlet['maps_url'] ?: url()->current()) }}&text={{ urlencode('Cek outlet ' . $outlet['name'] . ' di Pangestu Yadi Parfum') }}"
|
||||
target="_blank"
|
||||
class="flex items-center gap-3 px-4 py-2 text-sm text-home-foreground hover:bg-home-primary/5 hover:text-home-primary transition-colors">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
X (Twitter)
|
||||
</a>
|
||||
|
||||
{{-- Copy Link --}}
|
||||
<button
|
||||
@click="navigator.clipboard.writeText('{{ $outlet['maps_url'] ?: url()->current() }}'); $dispatch('notify', { message: 'Link berhasil disalin!', type: 'success' }); open = false"
|
||||
class="w-full flex items-center gap-3 px-4 py-2 text-sm text-home-foreground hover:bg-home-primary/5 hover:text-home-primary transition-colors text-left">
|
||||
<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="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3">
|
||||
</path>
|
||||
</svg>
|
||||
Salin Tautan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@ -156,19 +164,199 @@ class="w-12 h-12 rounded-xl border border-home-foreground/10 flex items-center j
|
||||
class="absolute inset-0 bg-home-foreground/5 z-10 pointer-events-none group-hover:bg-transparent transition-colors duration-500">
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute top-6 left-6 z-20 bg-white/90 backdrop-blur-md px-6 py-4 rounded-2xl shadow-lg border border-white/20 hidden md:block">
|
||||
<p class="text-xs text-home-foreground/50 font-bold uppercase tracking-wider mb-1">Cakupan Area</p>
|
||||
<h3 class="text-home-primary font-bold">Indonesia & Asia Tenggara</h3>
|
||||
</div>
|
||||
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d126920.28318625622!2d106.76457105!3d-6.229571199999999!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2e69f3e945e34b9d%3A0x5371bf0fdad786a2!2sJakarta%2C%20Special%20Capital%20Region%20of%20Jakarta!5e0!3m2!1sen!2sid!4v1652756763428!5m2!1sen!2sid"
|
||||
width="100%" height="450" style="border:0; filter: grayscale(100%) contrast(1.2);"
|
||||
allowfullscreen="" loading="lazy" class="hover:filter-none transition-all duration-700">
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d8506.860993234519!2d107.43951033371029!3d-6.543855086323806!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2e690f57aa3eaf5b%3A0x2bbc334edbb98b17!2sYadi%20Parfum!5e1!3m2!1sen!2sid!4v1766221140539!5m2!1sen!2sid"
|
||||
width="100%" height="450" style="border:0;" allowfullscreen="" loading="lazy"
|
||||
referrerpolicy="no-referrer-when-downgrade"
|
||||
style="border:0; filter: grayscale(100%) contrast(1.2);" allowfullscreen="" loading="lazy"
|
||||
class="hover:filter-none transition-all duration-700">
|
||||
</iframe>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{-- Detail Modal --}}
|
||||
@if ($selectedOutlet)
|
||||
<div class="fixed inset-0 z-[100] flex items-center justify-center p-4 sm:p-6 md:p-10" x-data
|
||||
x-on:keydown.escape.window="$wire.closeModal()">
|
||||
<!-- Backdrop -->
|
||||
<div class="absolute inset-0 bg-black/60 backdrop-blur-sm" wire:click="closeModal"
|
||||
x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"></div>
|
||||
|
||||
<!-- Modal Content -->
|
||||
<div class="relative bg-white w-full max-w-5xl max-h-full overflow-y-auto rounded-3xl shadow-2xl flex flex-col md:flex-row animate-in fade-in zoom-in duration-300"
|
||||
x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95">
|
||||
|
||||
<!-- Close Button -->
|
||||
<button wire:click="closeModal"
|
||||
class="absolute top-4 right-4 md:top-6 md:right-6 z-[60] w-10 h-10 rounded-full bg-white shadow-xl border border-home-foreground/5 text-home-foreground hover:text-home-primary hover:scale-110 transition-all flex items-center justify-center group"
|
||||
title="Tutup">
|
||||
<svg class="w-5 h-5 transition-transform group-hover:rotate-90 duration-300" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Left: Image Section (Gallery) -->
|
||||
@php
|
||||
$allImages = collect();
|
||||
if ($selectedOutlet->hasMedia('featured_image')) {
|
||||
$allImages->push($selectedOutlet->getFirstMediaUrl('featured_image'));
|
||||
}
|
||||
if ($selectedOutlet->hasMedia('images')) {
|
||||
foreach ($selectedOutlet->getMedia('images') as $media) {
|
||||
$allImages->push($media->getUrl());
|
||||
}
|
||||
}
|
||||
if ($allImages->isEmpty()) {
|
||||
$allImages->push('https://placehold.co/800x600/png?text=' . urlencode($selectedOutlet->name));
|
||||
}
|
||||
@endphp
|
||||
<div class="md:w-1/2 flex flex-col bg-gray-50" x-data="{
|
||||
activeImage: '{{ $allImages->first() }}',
|
||||
images: {{ $allImages->toJson() }}
|
||||
}">
|
||||
<!-- Main Image Display -->
|
||||
<div class="relative flex-grow h-[300px] md:h-auto overflow-hidden">
|
||||
<template x-for="(img, index) in images" :key="index">
|
||||
<img :src="img" x-show="activeImage === img"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 scale-105"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
class="absolute inset-0 w-full h-full object-cover"
|
||||
alt="{{ $selectedOutlet->name }}">
|
||||
</template>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent md:hidden">
|
||||
</div>
|
||||
<div class="absolute bottom-6 left-6 text-white md:hidden">
|
||||
<h2 class="text-3xl font-bold">{{ $selectedOutlet->name }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Thumbnails -->
|
||||
@if ($allImages->count() > 1)
|
||||
<div class="p-4 bg-white border-t border-gray-100">
|
||||
<div class="flex gap-2 overflow-x-auto pb-2 scrollbar-none">
|
||||
<template x-for="(img, index) in images" :key="index">
|
||||
<button @click="activeImage = img"
|
||||
class="relative w-20 h-20 rounded-xl overflow-hidden shrink-0 transition-all duration-300"
|
||||
:class="activeImage === img ? 'ring-2 ring-home-primary ring-offset-2 scale-95' :
|
||||
'opacity-60 hover:opacity-100'">
|
||||
<img :src="img" class="w-full h-full object-cover">
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Right: Details Section -->
|
||||
<div class="md:w-1/2 p-8 md:p-12">
|
||||
<div class="hidden md:block mb-8">
|
||||
<span class="text-home-primary font-bold tracking-widest text-xs uppercase mb-2 block">Detail
|
||||
Outlet</span>
|
||||
<h2 class="text-4xl font-bold text-home-foreground">{{ $selectedOutlet->name }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="space-y-8">
|
||||
<!-- Status & Address -->
|
||||
<div>
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<span
|
||||
class="px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-wide
|
||||
{{ $selectedOutlet->status === \App\Enums\OutletStatus::OPERATIONAL ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' }}">
|
||||
{{ $selectedOutlet->status->label() }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-start gap-4">
|
||||
<div
|
||||
class="w-10 h-10 rounded-full bg-home-primary/10 flex items-center justify-center shrink-0">
|
||||
<svg class="w-5 h-5 text-home-primary" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z">
|
||||
</path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-lg text-home-foreground/70">{{ $selectedOutlet->address }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Facilities -->
|
||||
<div>
|
||||
<h4 class="text-sm font-bold text-home-foreground uppercase tracking-wider mb-4">Fasilitas
|
||||
</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@forelse($selectedOutlet->facilities as $facility)
|
||||
<span
|
||||
class="px-4 py-2 bg-home-foreground/5 rounded-xl text-sm text-home-foreground/70 border border-home-foreground/10">
|
||||
{{ $facility->name }}
|
||||
</span>
|
||||
@empty
|
||||
<p class="text-sm text-home-foreground/40 italic">Tidak ada fasilitas khusus</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opening Hours -->
|
||||
<div>
|
||||
<h4 class="text-sm font-bold text-home-foreground uppercase tracking-wider mb-4">Jam
|
||||
Operasional</h4>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
@php
|
||||
$todayEnum = now()->dayOfWeekIso === 7 ? 1 : now()->dayOfWeekIso + 1;
|
||||
@endphp
|
||||
@foreach ($selectedOutlet->openingHours->sortBy('day.value') as $hour)
|
||||
<div
|
||||
class="flex justify-between items-center p-3 rounded-xl border border-home-foreground/5 {{ $hour->day->value === $todayEnum ? 'bg-home-primary/5 border-home-primary/20' : '' }}">
|
||||
<span
|
||||
class="text-xs font-medium {{ $hour->day->value === $todayEnum ? 'text-home-primary font-bold' : 'text-home-foreground/60' }}">
|
||||
{{ $hour->day->label() }}
|
||||
</span>
|
||||
<span class="text-xs font-bold text-home-foreground">
|
||||
{{ \Carbon\Carbon::parse($hour->open_time)->format('H:i') }} -
|
||||
{{ \Carbon\Carbon::parse($hour->close_time)->format('H:i') }}
|
||||
</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact & Maps -->
|
||||
<div class="flex gap-4 pt-4">
|
||||
<a href="tel:{{ $selectedOutlet->phone_number }}"
|
||||
class="flex-1 py-4 rounded-2xl bg-white border border-home-foreground/10 text-home-foreground text-center font-bold hover:border-home-primary hover:text-home-primary transition-all flex items-center justify-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z">
|
||||
</path>
|
||||
</svg>
|
||||
Telepon
|
||||
</a>
|
||||
<a href="{{ $selectedOutlet->maps_url }}" target="_blank"
|
||||
class="flex-[1.5] py-4 rounded-2xl bg-home-primary text-white text-center font-bold hover:bg-home-secondary hover:text-home-primary transition-all shadow-lg shadow-home-primary/20 flex items-center justify-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7">
|
||||
</path>
|
||||
</svg>
|
||||
Petunjuk Arah
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@ -21,14 +21,15 @@ class="px-8 py-4 rounded-xl bg-home-background text-home-foreground font-bold ho
|
||||
|
||||
<div class="flex items-center gap-4 text-sm text-home-background/40">
|
||||
<div class="flex -space-x-2">
|
||||
<img class="w-8 h-8 rounded-full border-2 border-white" src="https://placehold.co/100x100/png"
|
||||
alt="User">
|
||||
<img class="w-8 h-8 rounded-full border-2 border-white" src="https://placehold.co/100x100/png"
|
||||
alt="User">
|
||||
<img class="w-8 h-8 rounded-full border-2 border-white" src="https://placehold.co/100x100/png"
|
||||
alt="User">
|
||||
@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 }}">
|
||||
@endforeach
|
||||
</div>
|
||||
<p>Bergabung bersama <span class="text-home-background font-bold">2,000+</span> member lainnya.</p>
|
||||
<p>Bergabung bersama <span
|
||||
class="text-home-background font-bold">{{ number_format($totalMembersCount) }}+</span>
|
||||
member lainnya.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -74,4 +75,4 @@ class="w-8 h-8 opacity-80 brightness-0 invert">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@ -7,8 +7,8 @@ class="relative w-full lg:w-[35%] pt-24 lg:pt-0 py-0 px-6 lg:px-10 lg:pb-8 flex
|
||||
@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->name) }}&background=random&color=fff"
|
||||
alt="{{ $member->name }}" class="w-full h-full object-cover">
|
||||
<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>
|
||||
|
||||
@ -20,11 +20,19 @@ class="w-full h-full object-cover group-hover:scale-105 transition-transform dur
|
||||
alt="{{ $perfume['name'] }}">
|
||||
</figure>
|
||||
|
||||
<div class="flex-1 ml-4 lg:ml-6 flex flex-col justify-center space-y-2 lg:space-y-4">
|
||||
<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-lg lg:text-2xl text-black font-bold line-clamp-1">{{ $perfume['name'] }}</h3>
|
||||
<p class="text-black/60 text-xs lg:text-lg line-clamp-2">
|
||||
{{ $perfume['concentration_label'] }}</p>
|
||||
<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">
|
||||
|
||||
@ -42,12 +42,12 @@ class="relative w-full mt-auto h-[300px] lg:h-[250px] bg-home-background/40 back
|
||||
<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']) }}&background=random&color=fff"
|
||||
alt="{{ $testimonial['user']['customer']['name'] }}"
|
||||
<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'] }}</h4>
|
||||
{{ $testimonial['user']['customer']['name'] ?? 'User' }}</h4>
|
||||
<div class="flex text-home-primary text-xs">
|
||||
@for ($i = 0; $i < $testimonial['rating']; $i++)
|
||||
★
|
||||
|
||||
@ -11,16 +11,6 @@
|
||||
dirancang untuk bertahan lama, memberikan pengalaman wangi yang istimewa, dan memastikan setiap
|
||||
tetesnya asli dan terpercaya.
|
||||
</p>
|
||||
<div class="pt-4">
|
||||
<button
|
||||
class="text-home-primary font-bold hover:opacity-80 transition-opacity flex items-center gap-2">
|
||||
Pelajari Selengkapnya
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M17 8l4 4m0 0l-4 4m4-4H3"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="order-1 lg:order-2 relative">
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<section class="relative h-[500px] flex items-center justify-center overflow-hidden">
|
||||
{{-- Background Image --}}
|
||||
<div class="absolute inset-0 z-0">
|
||||
<img src="https://images.unsplash.com/photo-1651737232278-3875f741de86?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
<img src="https://images.pexels.com/photos/22589353/pexels-photo-22589353.jpeg"
|
||||
alt="Perfume Collection Background" class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black/50"></div>
|
||||
</div>
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
<section class="relative h-[500px] flex items-center justify-center overflow-hidden">
|
||||
{{-- Background Image --}}
|
||||
<div class="absolute inset-0 z-0">
|
||||
<img src="https://images.unsplash.com/photo-1541643600914-78b084683601?q=80&w=1469&auto=format&fit=crop"
|
||||
alt="Voucher Background" class="w-full h-full object-cover">
|
||||
<img src="https://images.pexels.com/photos/5650052/pexels-photo-5650052.jpeg" alt="Voucher Background"
|
||||
class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black/50"></div>
|
||||
</div>
|
||||
|
||||
@ -34,7 +34,7 @@ class="absolute right-2 top-2 bottom-2 w-10 h-10 bg-home-primary rounded-full fl
|
||||
<main class="flex-grow px-6 lg:px-32 xl:px-48 py-24 font-sans flex flex-col">
|
||||
|
||||
<section class="flex flex-col lg:flex-row justify-center gap-4 mb-12" aria-label="Filter">
|
||||
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4">
|
||||
<div class="relative">
|
||||
<select wire:model.live="outlet_id" aria-label="Filter Outlet"
|
||||
@ -153,11 +153,11 @@ class="font-bold text-home-foreground">{{ $voucher->end_date }}</time>
|
||||
</div>
|
||||
|
||||
<div class="p-6 flex gap-3 mt-auto">
|
||||
<button
|
||||
<button wire:click="claim('{{ $voucher->hash }}')"
|
||||
class="flex-1 bg-home-primary text-white font-bold py-3 rounded-xl hover:bg-home-secondary hover:text-home-primary hover:shadow-lg hover:shadow-home-primary/20 transition-all duration-300 text-sm shadow-md shadow-home-primary/10">
|
||||
Klaim Voucher
|
||||
</button>
|
||||
<button
|
||||
<button wire:click="showDetail('{{ $voucher->hash }}')"
|
||||
class="flex-1 border border-home-foreground/20 text-home-foreground font-bold py-3 rounded-xl hover:border-home-primary hover:text-home-primary hover:bg-home-primary/5 transition-all duration-300 text-sm">
|
||||
Lihat Detail
|
||||
</button>
|
||||
@ -167,8 +167,153 @@ class="flex-1 border border-home-foreground/20 text-home-foreground font-bold py
|
||||
</section>
|
||||
|
||||
<div class="mt-auto flex justify-center w-full">
|
||||
{{ $vouchers->links() }}
|
||||
{{ $vouchers->links('vendor.livewire.custom-pagination') }}
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
{{-- Voucher Detail Modal --}}
|
||||
@if ($showDetailModal && $selectedVoucher)
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
{{-- Backdrop --}}
|
||||
<div class="absolute inset-0 bg-black/60 backdrop-blur-sm" wire:click="closeDetail"></div>
|
||||
|
||||
{{-- Modal Content --}}
|
||||
<div
|
||||
class="relative bg-white w-full max-w-2xl rounded-3xl overflow-hidden shadow-2xl animate-in fade-in zoom-in duration-300">
|
||||
{{-- Modal Header --}}
|
||||
<div class="relative h-48 bg-home-primary flex items-center justify-center overflow-hidden">
|
||||
<div class="absolute inset-0 opacity-20">
|
||||
<div class="absolute top-0 right-0 w-64 h-64 bg-white rounded-full -mr-32 -mt-32"></div>
|
||||
<div class="absolute bottom-0 left-0 w-48 h-48 bg-white rounded-full -ml-24 -mb-24"></div>
|
||||
</div>
|
||||
<div class="relative z-10 text-center">
|
||||
<div
|
||||
class="inline-flex items-center gap-2 bg-white/20 backdrop-blur-md px-4 py-1.5 rounded-full border border-white/30 text-white text-xs font-bold uppercase tracking-widest mb-4">
|
||||
<span class="w-2 h-2 rounded-full bg-white animate-pulse"></span>
|
||||
{{ $selectedVoucher->tags }}
|
||||
</div>
|
||||
<h2 class="text-3xl font-bold text-white px-6">{{ $selectedVoucher->name }}</h2>
|
||||
</div>
|
||||
<button wire:click="closeDetail"
|
||||
class="absolute top-6 right-6 w-10 h-10 rounded-full bg-white/10 hover:bg-white/20 text-white flex items-center justify-center transition-all">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- Modal Body --}}
|
||||
<div class="p-8 max-h-[60vh] overflow-y-auto custom-scrollbar">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
|
||||
<div>
|
||||
<h4 class="text-sm font-bold text-home-foreground/40 uppercase tracking-wider mb-4">Syarat
|
||||
&
|
||||
Ketentuan</h4>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-lg bg-home-primary/5 flex items-center justify-center text-home-primary mt-0.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="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2-1.343-2-3-2zm0 4c-2.485 0-4.5 1.567-4.5 3.5V18h9v-2.5c0-1.933-2.015-3.5-4.5-3.5z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-home-foreground/50">Minimum Pembelian</p>
|
||||
<p class="font-bold text-home-foreground">
|
||||
{{ $selectedVoucher->min_purchase_formatted }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-lg bg-home-primary/5 flex items-center justify-center text-home-primary mt-0.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="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-home-foreground/50">Masa Berlaku</p>
|
||||
<p class="font-bold text-home-foreground">Hingga
|
||||
{{ $selectedVoucher->end_date_formatted }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@if ($selectedVoucher->limit_per_user)
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="w-8 h-8 rounded-lg bg-home-primary/5 flex items-center justify-center text-home-primary mt-0.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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-home-foreground/50">Batas per Pengguna
|
||||
</p>
|
||||
<p class="font-bold text-home-foreground">
|
||||
{{ $selectedVoucher->limit_per_user }}x Klaim</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="text-sm font-bold text-home-foreground/40 uppercase tracking-wider mb-4">Outlet
|
||||
Tersedia
|
||||
</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@forelse ($selectedVoucher->outlets as $outlet)
|
||||
<span
|
||||
class="px-4 py-2 bg-home-primary/5 border border-home-primary/10 rounded-xl text-sm font-semibold text-home-primary flex items-center gap-2">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z">
|
||||
</path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
</svg>
|
||||
{{ $outlet->name }}
|
||||
</span>
|
||||
@empty
|
||||
<span
|
||||
class="px-4 py-2 bg-home-foreground/5 border border-home-foreground/10 rounded-xl text-sm font-semibold text-home-foreground/50">
|
||||
Dapat digunakan di semua outlet
|
||||
</span>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-home-foreground/5 rounded-2xl p-6">
|
||||
<h4 class="text-sm font-bold text-home-foreground/40 uppercase tracking-wider mb-4">Ringkasan
|
||||
</h4>
|
||||
<p class="text-home-foreground/70 leading-relaxed italic">
|
||||
"{{ $selectedVoucher->summary }}"
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Modal Footer --}}
|
||||
<div class="p-8 bg-white border-t border-home-foreground/5 flex gap-4">
|
||||
<button wire:click="closeDetail"
|
||||
class="flex-1 py-4 px-6 border border-home-foreground/20 text-home-foreground font-bold rounded-2xl hover:bg-home-foreground/5 transition-all">
|
||||
Tutup
|
||||
</button>
|
||||
<button wire:click="claim('{{ $selectedVoucher->hashId }}')"
|
||||
class="flex-[2] py-4 px-6 bg-home-primary text-white font-bold rounded-2xl hover:bg-home-secondary hover:text-home-primary hover:shadow-xl hover:shadow-home-primary/20 transition-all shadow-lg shadow-home-primary/10">
|
||||
Klaim Sekarang
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user