feat: Implement member's "My Vouchers" page with new Livewire component, view, sidebar navigation, and route.
This commit is contained in:
parent
6706c191ed
commit
ca92fc9089
74
app/Livewire/Member/MyVouchers.php
Normal file
74
app/Livewire/Member/MyVouchers.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Member;
|
||||
|
||||
use App\Enums\VoucherType;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Voucher Saya')]
|
||||
#[Layout('components.layouts.member')]
|
||||
class MyVouchers extends Component
|
||||
{
|
||||
public array $myVouchers = [];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->myVouchers = auth()->user()->vouchers()
|
||||
->withPivot('quantity', 'created_at')
|
||||
->orderBy('user_voucher.created_at', 'desc')
|
||||
->get()
|
||||
->map(function ($voucher) {
|
||||
// Status Logic
|
||||
$now = now();
|
||||
$isActive = $voucher->start_date <= $now && ($voucher->end_date === null || $voucher->end_date >= $now);
|
||||
$isExpired = $voucher->end_date && $voucher->end_date < $now;
|
||||
$isUpcoming = $voucher->start_date > $now;
|
||||
|
||||
$status = 'active';
|
||||
if ($isExpired) {
|
||||
$status = 'expired';
|
||||
} elseif ($isUpcoming) {
|
||||
$status = 'upcoming';
|
||||
}
|
||||
|
||||
// Discount Display Logic
|
||||
if ($voucher->type === VoucherType::PERCENTAGE) {
|
||||
$discountDisplay = "Diskon {$voucher->discount_amount}%";
|
||||
if ($voucher->max_discount) {
|
||||
$discountDisplay .= ' (Max Rp'.formatCurrencyNumber($voucher->max_discount).')';
|
||||
}
|
||||
} else {
|
||||
$discountDisplay = 'Potongan Rp'.formatCurrencyNumber($voucher->discount_amount);
|
||||
}
|
||||
|
||||
// Date Display Logic
|
||||
$dateDisplay = formatDateLocalized($voucher->start_date);
|
||||
if ($voucher->end_date) {
|
||||
$dateDisplay .= ' - '.formatDateLocalized($voucher->end_date);
|
||||
} else {
|
||||
$dateDisplay .= ' - Tidak ada batas waktu';
|
||||
}
|
||||
|
||||
$voucher->claimed_at_display = formatDateTimeLocalized($voucher->pivot->created_at);
|
||||
$voucher->date_display = $dateDisplay;
|
||||
$voucher->min_purchase_display = $voucher->min_purchase ? 'Min. Belanja: Rp'.formatCurrencyNumber($voucher->min_purchase) : null;
|
||||
$voucher->discount_display = $discountDisplay;
|
||||
$voucher->status = $status;
|
||||
$voucher->quantity = $voucher->pivot->quantity;
|
||||
|
||||
return $voucher;
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.member.my-vouchers', [
|
||||
'pageTitle' => 'Voucher Saya',
|
||||
'pageDescription' => 'Daftar voucher yang telah Anda klaim dan dapat digunakan ketika belanja di toko.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,9 @@ class="bg-zinc-50 dark:bg-zinc-900 border-r rtl:border-r-0 rtl:border-l border-z
|
||||
<flux:navlist.item icon="user-circle" href="{{ route('member.membership') }}"
|
||||
:current="request()->routeIs('member.membership')" wire:navigate>
|
||||
Membership</flux:navlist.item>
|
||||
<flux:navlist.item icon="ticket" href="{{ route('member.my_vouchers') }}"
|
||||
:current="request()->routeIs('member.my_vouchers')" wire:navigate>
|
||||
Voucher Saya</flux:navlist.item>
|
||||
<flux:navlist.item icon="check-badge" href="{{ route('member.redeem_point') }}"
|
||||
:current="request()->routeIs('member.redeem_point')" wire:navigate>
|
||||
Penukaran Poin</flux:navlist.item>
|
||||
|
||||
90
resources/views/livewire/member/my-vouchers.blade.php
Normal file
90
resources/views/livewire/member/my-vouchers.blade.php
Normal file
@ -0,0 +1,90 @@
|
||||
<flux:main>
|
||||
<flux:heading size="xl" level="1">{{ $pageTitle }}</flux:heading>
|
||||
<flux:text class="mb-6 mt-2 text-base">{{ $pageDescription }}</flux:text>
|
||||
|
||||
@if (empty($myVouchers))
|
||||
<flux:card class="text-center py-12">
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<flux:icon.ticket variant="outline" class="size-16 text-zinc-400 mb-4" />
|
||||
<flux:heading size="lg" class="mb-2">Belum Ada Voucher</flux:heading>
|
||||
<flux:text class="text-zinc-500 mb-6">
|
||||
Anda belum memiliki voucher. Klaim voucher di halaman utama untuk mendapatkan diskon menarik!
|
||||
</flux:text>
|
||||
<flux:button href="{{ route('homepage') }}" wire:navigate>
|
||||
Klaim Voucher Sekarang
|
||||
</flux:button>
|
||||
</div>
|
||||
</flux:card>
|
||||
@else
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach ($myVouchers as $voucher)
|
||||
<flux:card class="relative overflow-hidden">
|
||||
{{-- Badge Status --}}
|
||||
<div class="absolute top-4 right-4">
|
||||
@if ($voucher['status'] === 'active')
|
||||
<flux:badge color="emerald" size="sm">Aktif</flux:badge>
|
||||
@elseif ($voucher['status'] === 'expired')
|
||||
<flux:badge color="red" size="sm">Kadaluarsa</flux:badge>
|
||||
@elseif ($voucher['status'] === 'upcoming')
|
||||
<flux:badge color="blue" size="sm">Segera</flux:badge>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Voucher Content --}}
|
||||
<div class="mb-4">
|
||||
<flux:heading size="lg" class="mb-2">{{ $voucher['name'] }}</flux:heading>
|
||||
|
||||
{{-- Voucher Code --}}
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<flux:badge color="zinc" size="lg" class="font-mono text-lg">
|
||||
{{ $voucher['code'] }}
|
||||
</flux:badge>
|
||||
@if ($voucher['quantity'] > 1)
|
||||
<flux:badge color="blue" size="sm">
|
||||
{{ $voucher['quantity'] }}x
|
||||
</flux:badge>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Summary --}}
|
||||
<flux:text class="text-sm mb-3">
|
||||
{{ $voucher['summary'] }}
|
||||
</flux:text>
|
||||
|
||||
{{-- Discount Info --}}
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<flux:icon.tag class="size-4 text-emerald-600" />
|
||||
<flux:text class="text-sm font-semibold text-emerald-600">
|
||||
{{ $voucher['discount_display'] }}
|
||||
</flux:text>
|
||||
</div>
|
||||
|
||||
{{-- Min Purchase --}}
|
||||
@if ($voucher['min_purchase_display'])
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<flux:icon.shopping-cart class="size-4" />
|
||||
<flux:text class="text-sm">
|
||||
{{ $voucher['min_purchase_display'] }}
|
||||
</flux:text>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Valid Period --}}
|
||||
<div class="flex items-center gap-2">
|
||||
<flux:icon.calendar class="size-4" />
|
||||
<flux:text class="text-sm">
|
||||
{{ $voucher['date_display'] }}
|
||||
</flux:text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Claimed Date --}}
|
||||
<flux:separator class="my-4" />
|
||||
<flux:text class="text-xs">
|
||||
Diklaim pada: {{ $voucher['claimed_at_display'] }}
|
||||
</flux:text>
|
||||
</flux:card>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</flux:main>
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Member\Membership;
|
||||
use App\Livewire\Member\MyVouchers;
|
||||
use App\Livewire\Member\Overview;
|
||||
use App\Livewire\Member\RedeemPoint;
|
||||
use App\Livewire\Member\Referral;
|
||||
@ -14,6 +15,7 @@
|
||||
Route::get('overview', Overview::class)->name('overview');
|
||||
|
||||
Route::get('memberships', Membership::class)->name('membership');
|
||||
Route::get('my-vouchers', MyVouchers::class)->name('my_vouchers');
|
||||
Route::get('redeem-points', RedeemPoint::class)->name('redeem_point');
|
||||
Route::get('referrals', Referral::class)->name('referral');
|
||||
Route::get('settings', Account::class)->name('setting');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user