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 --}}
-
+
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 --}}
-
+
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 --}}
-
-
-
-
-
- {{-- Content --}}
-
-
Lokasi Butik
-
Temukan Butik Terdekat
-
- Rasakan pengalaman langsung kemewahan aroma kami. Kunjungi butik kami untuk konsultasi personal.
-
-
- {{-- Search Bar --}}
-
-
-
-
-
-
-
+
+ {{-- Background Image --}}
+
+
+
-
-
-
+ {{-- Content --}}
+
+
Temukan Outlet Terdekat
+
+ Rasakan pengalaman langsung kemewahan aroma kami. Kunjungi outlet kami untuk konsultasi personal.
+
+
+ {{-- Search Bar --}}
+
+
+
+
+
@foreach ($outlets as $outlet)
-
+
-
- {{ $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'] }}
-
-
-
+
+ @if ($outlet['maps_url'])
+
+ Petunjuk Arah
+
+ @endif
+ {{-- Share Menu --}}
+
+
+
+
+
+
+
-
-
- Petunjuk Arah
-
-
-
-
-
-
-
+ {{-- Dropdown Menu --}}
+
+
+
+ Bagikan Ke
+
+
+ {{-- WhatsApp --}}
+
+
+
+
+ WhatsApp
+
+
+ {{-- Facebook --}}
+
+
+
+
+ Facebook
+
+
+ {{-- X (Twitter) --}}
+
+
+
+
+ X (Twitter)
+
+
+ {{-- Copy Link --}}
+
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">
+
+
+
+
+ Salin Tautan
+
+
+
@@ -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">
-
-
Cakupan Area
-
Indonesia & Asia Tenggara
-
-
+
+ {{-- 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
+
+
+
+
+
+ Detail
+ Outlet
+
{{ $selectedOutlet->name }}
+
+
+
+
+
+
+
+ {{ $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
-
-
-
+ @foreach ($latestMembers as $member)
+
+ @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)
-
+
@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'] ?? '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.
-
-
- Pelajari Selengkapnya
-
-
-
-
-
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 --}}
-
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 --}}
-
+
@@ -34,7 +34,7 @@ class="absolute right-2 top-2 bottom-2 w-10 h-10 bg-home-primary rounded-full fl
-
+
{{ $voucher->end_date }}
-
Klaim Voucher
-
Lihat Detail
@@ -167,8 +167,153 @@ class="flex-1 border border-home-foreground/20 text-home-foreground font-bold py
- {{ $vouchers->links() }}
+ {{ $vouchers->links('vendor.livewire.custom-pagination') }}
-
+
+ {{-- Voucher Detail Modal --}}
+ @if ($showDetailModal && $selectedVoucher)
+
+ {{-- Backdrop --}}
+
+
+ {{-- Modal Content --}}
+
+ {{-- Modal Header --}}
+
+
+
+
+
+ {{ $selectedVoucher->tags }}
+
+
{{ $selectedVoucher->name }}
+
+
+
+
+
+
+
+
+ {{-- Modal Body --}}
+
+
+ {{-- Modal Footer --}}
+
+
+ Tutup
+
+
+ Klaim Sekarang
+
+
+
+
+ @endif