diff --git a/app/Livewire/Home/Index.php b/app/Livewire/Home/Index.php index cf6e449..e08940b 100644 --- a/app/Livewire/Home/Index.php +++ b/app/Livewire/Home/Index.php @@ -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; diff --git a/app/Livewire/Home/Outlet.php b/app/Livewire/Home/Outlet.php index 2001f6c..be80702 100644 --- a/app/Livewire/Home/Outlet.php +++ b/app/Livewire/Home/Outlet.php @@ -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', + ]); } } diff --git a/app/Livewire/Home/Voucher.php b/app/Livewire/Home/Voucher.php index 8328a3b..4c2dfee 100644 --- a/app/Livewire/Home/Voucher.php +++ b/app/Livewire/Home/Voucher.php @@ -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) { diff --git a/app/Providers/ViewServiceProvider.php b/app/Providers/ViewServiceProvider.php index f9366c1..8da9bf7 100644 --- a/app/Providers/ViewServiceProvider.php +++ b/app/Providers/ViewServiceProvider.php @@ -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), ]); }); } diff --git a/resources/views/components/sections/ui/home/_footer.blade.php b/resources/views/components/sections/ui/home/_footer.blade.php index e2e5b6d..8cd4b65 100644 --- a/resources/views/components/sections/ui/home/_footer.blade.php +++ b/resources/views/components/sections/ui/home/_footer.blade.php @@ -40,26 +40,32 @@ class="w-10 h-10 rounded-full bg-white/5 flex items-center justify-center text-g
-

Quick Links

+

Tautan Cepat

-

Contact Us

+

Kontak Kami

- {{ $outlet->note ?? 'Jalan Wangi Semerbak No. 123, Kota Parfum, Indonesia' }} + {{ $setting->address }}
{{ $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">
- {{ $outlet->email ?? 'admin@yadiparfum.com' }} + {{ $setting->email }}
{{ $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">
- {{ $outlet->phone ?? '+62 812-3456-7890' }} + {{ $setting->phone_number }}
@@ -98,12 +103,12 @@ class="leading-relaxed">{{ $outlet->note ?? 'Jalan Wangi Semerbak No. 123, Kota
- © {{ date('Y') }} Yadi Parfum. All rights reserved. + © {{ date('Y') }} Yadi Parfum. Semua hak dilindungi undang-undang.
- --}}
diff --git a/resources/views/livewire/home/article/index.blade.php b/resources/views/livewire/home/article/index.blade.php index ecf78dc..0a7786a 100644 --- a/resources/views/livewire/home/article/index.blade.php +++ b/resources/views/livewire/home/article/index.blade.php @@ -3,8 +3,8 @@
{{-- Background Image --}}
- Perfume Background + Article Background
diff --git a/resources/views/livewire/home/faq/index.blade.php b/resources/views/livewire/home/faq/index.blade.php index 49966a5..eaeaa42 100644 --- a/resources/views/livewire/home/faq/index.blade.php +++ b/resources/views/livewire/home/faq/index.blade.php @@ -3,8 +3,8 @@
{{-- Background Image --}}
- Perfume Background + Perfume Background
diff --git a/resources/views/livewire/home/outlets.blade.php b/resources/views/livewire/home/outlets.blade.php index 093a586..ec2e16e 100644 --- a/resources/views/livewire/home/outlets.blade.php +++ b/resources/views/livewire/home/outlets.blade.php @@ -1,95 +1,57 @@
- @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 -
{{-- Hero Section --}} -
- {{-- Background Image --}} -
- Outlet Background -
-
- - {{-- Content --}} -
- Lokasi Butik -

Temukan Butik Terdekat

-

- Rasakan pengalaman langsung kemewahan aroma kami. Kunjungi butik kami untuk konsultasi personal. -

- - {{-- Search Bar --}} -
- - +
+ {{-- Background Image --}} +
+ Outlet Background +
-
-
-
+ {{-- Content --}} +
+

Temukan Outlet Terdekat

+

+ Rasakan pengalaman langsung kemewahan aroma kami. Kunjungi outlet kami untuk konsultasi personal. +

+ + {{-- Search Bar --}} +
+ + +
+
+
+ +
@foreach ($outlets as $outlet) -
+
- {{ $outlet['name'] }}
- {{ $outlet['status'] }} + text-{{ $outlet['status_color'] }}-700"> + {{ $outlet['status_label'] }}
@@ -106,45 +68,91 @@ class="text-xl font-bold text-home-foreground mb-2 group-hover:text-home-primary @foreach ($outlet['facilities'] as $facility) - {{ $facility }} + {{ $facility['name'] }} @endforeach -
-
- - - - {{ $outlet['hours'] }} -
-
- - - - - Call Store -
-
+
+ @if ($outlet['maps_url']) + + Petunjuk Arah + + @endif + {{-- Share Menu --}} +
+ -
- - + {{-- Dropdown Menu --}} + +
@@ -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"> - -
+ + {{-- Detail Modal --}} + @if ($selectedOutlet) +
+ +
+ + +
+ + + + + + @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 +
+ +
+ + +
+
+
+

{{ $selectedOutlet->name }}

+
+
+ + + @if ($allImages->count() > 1) +
+
+ +
+
+ @endif +
+ + +
+ + +
+ +
+
+ + {{ $selectedOutlet->status->label() }} + +
+
+
+ + + + + +
+

{{ $selectedOutlet->address }}

+
+
+ + +
+

Fasilitas +

+
+ @forelse($selectedOutlet->facilities as $facility) + + {{ $facility->name }} + + @empty +

Tidak ada fasilitas khusus

+ @endforelse +
+
+ + +
+

Jam + Operasional

+
+ @php + $todayEnum = now()->dayOfWeekIso === 7 ? 1 : now()->dayOfWeekIso + 1; + @endphp + @foreach ($selectedOutlet->openingHours->sortBy('day.value') as $hour) +
+ + {{ $hour->day->label() }} + + + {{ \Carbon\Carbon::parse($hour->open_time)->format('H:i') }} - + {{ \Carbon\Carbon::parse($hour->close_time)->format('H:i') }} + +
+ @endforeach +
+
+ + + +
+
+
+
+ @endif diff --git a/resources/views/livewire/home/partials/cta.blade.php b/resources/views/livewire/home/partials/cta.blade.php index 74a23cf..cd2a429 100644 --- a/resources/views/livewire/home/partials/cta.blade.php +++ b/resources/views/livewire/home/partials/cta.blade.php @@ -21,14 +21,15 @@ class="px-8 py-4 rounded-xl bg-home-background text-home-foreground font-bold ho
- User - User - User + @foreach ($latestMembers as $member) + {{ $member->name }} + @endforeach
-

Bergabung bersama 2,000+ member lainnya.

+

Bergabung bersama {{ number_format($totalMembersCount) }}+ + member lainnya.

@@ -74,4 +75,4 @@ class="w-8 h-8 opacity-80 brightness-0 invert"> -
\ No newline at end of file + diff --git a/resources/views/livewire/home/partials/jumbotron/left.blade.php b/resources/views/livewire/home/partials/jumbotron/left.blade.php index 8099714..405aa5f 100644 --- a/resources/views/livewire/home/partials/jumbotron/left.blade.php +++ b/resources/views/livewire/home/partials/jumbotron/left.blade.php @@ -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)
- {{ $member->name }} + {{ $member->user->name ?? $member->name }}
@endforeach diff --git a/resources/views/livewire/home/partials/jumbotron/right.blade.php b/resources/views/livewire/home/partials/jumbotron/right.blade.php index 5f8641f..4da313a 100644 --- a/resources/views/livewire/home/partials/jumbotron/right.blade.php +++ b/resources/views/livewire/home/partials/jumbotron/right.blade.php @@ -20,11 +20,19 @@ class="w-full h-full object-cover group-hover:scale-105 transition-transform dur alt="{{ $perfume['name'] }}"> -
+
-

{{ $perfume['name'] }}

-

- {{ $perfume['concentration_label'] }}

+

{{ $perfume['name'] }}

+
+ + Rp {{ number_format($perfume['sale_price'], 0, ',', '.') }} + +
+
+ + {{ number_format($perfume['items_sum_quantity'], 0, ',', '.') }} Terjual + +
diff --git a/resources/views/livewire/home/partials/jumbotron/testimonial.blade.php b/resources/views/livewire/home/partials/jumbotron/testimonial.blade.php index a4d5e25..15bb0f1 100644 --- a/resources/views/livewire/home/partials/jumbotron/testimonial.blade.php +++ b/resources/views/livewire/home/partials/jumbotron/testimonial.blade.php @@ -42,12 +42,12 @@ class="relative w-full mt-auto h-[300px] lg:h-[250px] bg-home-background/40 back
- {{ $testimonial['user']['customer']['name'] }}

- {{ $testimonial['user']['customer']['name'] }}

+ {{ $testimonial['user']['customer']['name'] ?? 'User' }}
@for ($i = 0; $i < $testimonial['rating']; $i++) ★ diff --git a/resources/views/livewire/home/partials/why.blade.php b/resources/views/livewire/home/partials/why.blade.php index e9aabc2..43e5e6a 100644 --- a/resources/views/livewire/home/partials/why.blade.php +++ b/resources/views/livewire/home/partials/why.blade.php @@ -11,16 +11,6 @@ dirancang untuk bertahan lama, memberikan pengalaman wangi yang istimewa, dan memastikan setiap tetesnya asli dan terpercaya.

-
- -
diff --git a/resources/views/livewire/home/product/index.blade.php b/resources/views/livewire/home/product/index.blade.php index f16fa22..ab32194 100644 --- a/resources/views/livewire/home/product/index.blade.php +++ b/resources/views/livewire/home/product/index.blade.php @@ -4,7 +4,7 @@
{{-- Background Image --}}
- Perfume Collection Background
diff --git a/resources/views/livewire/home/vouchers.blade.php b/resources/views/livewire/home/vouchers.blade.php index 25e0094..c584c3c 100644 --- a/resources/views/livewire/home/vouchers.blade.php +++ b/resources/views/livewire/home/vouchers.blade.php @@ -3,8 +3,8 @@
{{-- Background Image --}}
- Voucher Background + Voucher Background
@@ -34,7 +34,7 @@ class="absolute right-2 top-2 bottom-2 w-10 h-10 bg-home-primary rounded-full fl
- +