diff --git a/app/Livewire/Datatable/Loyalty/VouchersTable.php b/app/Livewire/Datatable/Loyalty/VouchersTable.php
index bdd215a..006012d 100644
--- a/app/Livewire/Datatable/Loyalty/VouchersTable.php
+++ b/app/Livewire/Datatable/Loyalty/VouchersTable.php
@@ -41,43 +41,50 @@ public function columns(): array
->sortable(),
Column::make('Min. Belanja', 'min_purchase')
- ->format(fn ($value) => formatCurrencyNumber($value, 'Rp'))
+ ->format(fn($value) => formatCurrencyNumber($value, 'Rp'))
->searchable()
->sortable(),
+ ArrayColumn::make('Tier')
+ ->data(
+ fn($value, $row) => $row->tiers->pluck('name')->toArray() ?: ['Semua Tier']
+ )
+ ->outputFormat(fn($index, $value) => Blade::render('' . $value . ''))
+ ->flexRow(['class' => 'gap-2 flex-wrap']),
+
Column::make('Jumlah Diskon', 'discount_amount')
- ->label(fn ($row, $column) => formatDiscount($row->discount_amount, $row->type))
+ ->label(fn($row, $column) => formatDiscount($row->discount_amount, $row->type))
->searchable()
->sortable(),
Column::make('Maks. Diskon')
- ->label(fn ($row, $column) => formatCurrencyNumber($row->max_discount, 'Rp'))
+ ->label(fn($row, $column) => formatCurrencyNumber($row->max_discount, 'Rp'))
->searchable()
->sortable(),
Column::make('Kuota', 'quota')
- ->format(fn ($value) => $value ? formatCurrencyNumber($value) : 'Tidak Terbatas')
+ ->format(fn($value) => $value ? formatCurrencyNumber($value) : 'Tidak Terbatas')
->searchable()
->sortable(),
Column::make('Sisa Voucher', 'available_count')
- ->format(fn ($value) => $value ? formatCurrencyNumber($value) : 'Tidak Terbatas')
+ ->format(fn($value) => $value ? formatCurrencyNumber($value) : 'Tidak Terbatas')
->searchable()
->sortable(),
Column::make('Batas Per Pembali', 'limit_per_user')
- ->format(fn ($value) => $value ? $value : 'Tidak Terbatas')
+ ->format(fn($value) => $value ? $value : 'Tidak Terbatas')
->searchable()
->sortable(),
ArrayColumn::make('Outlet')
->data(
- fn ($value, $row) => $row->outlets->map(fn ($outlet) => [
+ fn($value, $row) => $row->outlets->map(fn($outlet) => [
'name' => $outlet->name,
'stock' => formatCurrencyNumber($outlet->pivot->stock, ''),
])->toArray()
)
- ->outputFormat(fn ($index, $value) => Blade::render('
+ ->outputFormat(fn($index, $value) => Blade::render('
{{ $value["name"] }}
', ['value' => $value]))
@@ -134,11 +141,11 @@ public function columns(): array
public function builder(): Builder
{
- return Voucher::select('id', 'name', 'code', 'tags', 'min_purchase', 'discount_amount', 'max_discount', 'quota', 'available_count', 'limit_per_user', 'start_date', 'end_date', 'type', 'created_at')
- ->with('outlets')
+ return Voucher::select('vouchers.id', 'vouchers.name', 'code', 'tags', 'min_purchase', 'discount_amount', 'max_discount', 'quota', 'available_count', 'limit_per_user', 'start_date', 'end_date', 'type', 'vouchers.created_at')
+ ->with(['outlets', 'tiers'])
->whereHas(
'outlets',
- fn ($q) => $q->whereIn('outlets.id', auth()->user()->outlets()->pluck('outlets.id'))
+ fn($q) => $q->whereIn('outlets.id', auth()->user()->outlets()->pluck('outlets.id'))
);
}
}
diff --git a/app/Livewire/Forms/Studio/Loyalty/VoucherForm.php b/app/Livewire/Forms/Studio/Loyalty/VoucherForm.php
index 1f2d9bc..a5c71f4 100644
--- a/app/Livewire/Forms/Studio/Loyalty/VoucherForm.php
+++ b/app/Livewire/Forms/Studio/Loyalty/VoucherForm.php
@@ -37,6 +37,8 @@ class VoucherForm extends Form
public ?string $end_date = null;
+ public array $tier_ids = [];
+
public array $outlet_ids = [];
public function rules(): array
@@ -73,6 +75,8 @@ public function rules(): array
'summary' => ['required', 'string', 'max:200'],
'start_date' => ['required', 'date', 'after:yesterday'],
'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
+ 'tier_ids' => ['nullable', 'array'],
+ 'tier_ids.*' => Rule::exists('tiers', 'id'),
'outlet_ids' => ['required', 'array', 'min:1'],
'outlet_ids.*' => Rule::exists('outlets', 'id'),
];
@@ -92,6 +96,8 @@ public function validationAttributes(): array
'discount_amount' => 'Jumlah Diskon',
'start_date' => 'tanggal mulai',
'end_date' => 'tanggal selesai',
+ 'tier_ids' => 'tier',
+ 'tier_ids.*' => 'tier',
'outlet_ids' => 'outlet',
'outlet_ids.*' => 'outlet',
];
@@ -99,7 +105,7 @@ public function validationAttributes(): array
public function setVoucher(Voucher $voucher): void
{
- $voucher->load('outlets');
+ $voucher->load(['outlets', 'tiers']);
$this->voucher = $voucher;
@@ -115,6 +121,8 @@ public function setVoucher(Voucher $voucher): void
$this->summary = $this->voucher->summary;
$this->start_date = $this->voucher->start_date;
$this->end_date = $this->voucher->end_date;
+
+ $this->tier_ids = $this->voucher->tiers->pluck('id')->toArray();
$this->outlet_ids = $this->voucher->outlets->pluck('id')->toArray();
}
@@ -135,6 +143,7 @@ public function store(): void
$voucher = Voucher::create($data);
$voucher->outlets()->attach($this->outlet_ids);
+ $voucher->tiers()->attach($this->tier_ids);
});
}
@@ -149,6 +158,7 @@ public function update(): void
$this->voucher->update($data);
$this->voucher->outlets()->sync($this->outlet_ids);
+ $this->voucher->tiers()->sync($this->tier_ids);
});
}
diff --git a/app/Livewire/Studio/Loyalty/Voucher/Create.php b/app/Livewire/Studio/Loyalty/Voucher/Create.php
index 35a9075..41a64a2 100644
--- a/app/Livewire/Studio/Loyalty/Voucher/Create.php
+++ b/app/Livewire/Studio/Loyalty/Voucher/Create.php
@@ -6,6 +6,7 @@
use App\Livewire\Forms\Studio\Loyalty\VoucherForm;
use App\Models\Outlet;
use App\Models\PushNotification;
+use App\Models\Tier;
use App\Traits\Authorization\WithAuthorization;
use App\Traits\Components\WithOutletSelector;
use App\Traits\Components\WithToast;
@@ -23,9 +24,12 @@ class Create extends Component
public array $outlets;
+ public array $tiers;
+
public function mount(): void
{
$this->outlets = Outlet::pluck('name', 'id')->toArray();
+ $this->tiers = Tier::pluck('name', 'id')->toArray();
}
public function save(): void
diff --git a/app/Livewire/Studio/Loyalty/Voucher/Edit.php b/app/Livewire/Studio/Loyalty/Voucher/Edit.php
index d5839bb..90b48d5 100644
--- a/app/Livewire/Studio/Loyalty/Voucher/Edit.php
+++ b/app/Livewire/Studio/Loyalty/Voucher/Edit.php
@@ -4,6 +4,7 @@
use App\Livewire\Forms\Studio\Loyalty\VoucherForm;
use App\Models\Outlet;
+use App\Models\Tier;
use App\Models\Voucher;
use App\Traits\Authorization\WithAuthorization;
use App\Traits\Components\WithOutletSelector;
@@ -22,11 +23,14 @@ class Edit extends Component
public array $outlets;
+ public array $tiers;
+
public function mount(Voucher $voucher): void
{
$this->form->setVoucher($voucher);
$this->outlets = Outlet::pluck('name', 'id')->toArray();
+ $this->tiers = Tier::pluck('name', 'id')->toArray();
}
public function save(): void
diff --git a/app/Models/Tier.php b/app/Models/Tier.php
index c9b4973..a1ab30c 100644
--- a/app/Models/Tier.php
+++ b/app/Models/Tier.php
@@ -5,6 +5,7 @@
use Dyrynda\Database\Support\CascadeSoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Veelasky\LaravelHashId\Eloquent\HashableId;
@@ -48,6 +49,11 @@ public function rewards(): HasMany
return $this->hasMany(TierReward::class);
}
+ public function vouchers(): BelongsToMany
+ {
+ return $this->belongsToMany(Voucher::class);
+ }
+
public static function getHighestTier()
{
return self::orderByDesc('min_points')->first();
diff --git a/app/Models/TierVoucher.php b/app/Models/TierVoucher.php
new file mode 100644
index 0000000..7574904
--- /dev/null
+++ b/app/Models/TierVoucher.php
@@ -0,0 +1,12 @@
+whereDate('start_date', '>', now());
}
+ #[Scope]
+ public function forTier(Builder $query, ?int $tierId): void
+ {
+ $query->where(function ($q) use ($tierId) {
+ $q->whereDoesntHave('tiers')
+ ->when($tierId, fn ($query) => $query->orWhereHas('tiers', fn ($q) => $q->where('tiers.id', $tierId)));
+ });
+ }
+
public function outlets(): BelongsToMany
{
return $this->belongsToMany(Outlet::class);
@@ -63,6 +72,11 @@ public function orders(): HasMany
return $this->hasMany(Order::class);
}
+ public function tiers(): BelongsToMany
+ {
+ return $this->belongsToMany(Tier::class);
+ }
+
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
diff --git a/database/migrations/2025_12_18_134500_create_tier_voucher_table.php b/database/migrations/2025_12_18_134500_create_tier_voucher_table.php
new file mode 100644
index 0000000..de6fb7f
--- /dev/null
+++ b/database/migrations/2025_12_18_134500_create_tier_voucher_table.php
@@ -0,0 +1,30 @@
+id();
+ $table->foreignId('tier_id')->constrained()->cascadeOnDelete();
+ $table->foreignId('voucher_id')->constrained()->cascadeOnDelete();
+ $table->timestamp('created_at')->useCurrent();
+ $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('tier_voucher');
+ }
+};
diff --git a/database/seeders/VoucherSeeder.php b/database/seeders/VoucherSeeder.php
index 9427ce3..5887130 100644
--- a/database/seeders/VoucherSeeder.php
+++ b/database/seeders/VoucherSeeder.php
@@ -4,6 +4,7 @@
use App\Enums\VoucherType;
use App\Models\Outlet;
+use App\Models\Tier;
use App\Models\Voucher;
use Illuminate\Database\Seeder;
@@ -11,12 +12,16 @@ class VoucherSeeder extends Seeder
{
public function run(): void
{
+ $tiers = Tier::all();
+ $tierIds = $tiers->pluck('id')->toArray();
+
$vouchers = [
[
'name' => 'Diskon 10%',
'code' => 'DISC10',
'type' => VoucherType::PERCENTAGE,
'tags' => 'DISC 10%',
+ 'tier_ids' => [], // Voucher umum untuk semua tier
'summary' => 'Diskon 10% untuk pembelian minimal Rp50.000',
'discount_amount' => 10,
'min_purchase' => 50000,
@@ -32,6 +37,7 @@ public function run(): void
'code' => 'DISC20',
'type' => VoucherType::PERCENTAGE,
'tags' => 'DISC 20%',
+ 'tier_ids' => isset($tierIds[0]) ? [$tierIds[0]] : [], // Voucher khusus tier pertama
'summary' => 'Diskon 20% untuk pembelian minimal Rp100.000',
'discount_amount' => 20,
'min_purchase' => 100000,
@@ -44,9 +50,10 @@ public function run(): void
],
[
'name' => 'Potongan Rp25K',
- 'code' => 'SAVE 25K',
+ 'code' => 'SAVE25K',
'type' => VoucherType::FIXED,
'tags' => 'SAVE 25K',
+ 'tier_ids' => [], // Voucher umum
'summary' => 'Potongan Rp25K untuk pembelian minimal Rp100.000',
'discount_amount' => 25000,
'min_purchase' => 100000,
@@ -62,6 +69,7 @@ public function run(): void
'code' => 'SAVE50K',
'type' => VoucherType::FIXED,
'tags' => 'SAVE 50K',
+ 'tier_ids' => isset($tierIds[1]) ? [$tierIds[1]] : [], // Voucher khusus tier kedua
'summary' => 'Potongan Rp50K untuk pembelian minimal Rp200.000',
'discount_amount' => 50000,
'min_purchase' => 200000,
@@ -77,6 +85,7 @@ public function run(): void
'code' => 'ONGKIR',
'type' => VoucherType::FIXED,
'tags' => 'GRATIS ONGKIR',
+ 'tier_ids' => [], // Voucher umum
'summary' => 'Gratis Ongkir untuk pembelian minimal Rp50.000',
'discount_amount' => 15000,
'min_purchase' => 50000,
@@ -92,6 +101,7 @@ public function run(): void
'code' => 'FLASH30',
'type' => VoucherType::PERCENTAGE,
'tags' => 'FLASH 30%',
+ 'tier_ids' => isset($tierIds[2]) ? [$tierIds[2]] : [], // Voucher khusus tier ketiga
'summary' => 'Diskon 30% untuk pembelian minimal Rp75.000',
'discount_amount' => 30,
'min_purchase' => 75000,
@@ -107,6 +117,7 @@ public function run(): void
'code' => 'MEMBER15',
'type' => VoucherType::PERCENTAGE,
'tags' => 'Serial Member',
+ 'tier_ids' => [], // Voucher umum untuk semua member
'summary' => 'Diskon 15% untuk pembelian minimal Rp50.000',
'discount_amount' => 15,
'min_purchase' => 50000,
@@ -119,8 +130,12 @@ public function run(): void
];
foreach ($vouchers as $voucher) {
+ $tierIdsToAttach = $voucher['tier_ids'];
+ unset($voucher['tier_ids']);
+
$newVoucher = Voucher::create($voucher);
+ $newVoucher->tiers()->attach($tierIdsToAttach);
$newVoucher->outlets()->attach(Outlet::inRandomOrder()->first()->id);
}
}
diff --git a/resources/views/livewire/home/vouchers.blade.php b/resources/views/livewire/home/vouchers.blade.php
index 2fee8fb..b8571ec 100644
--- a/resources/views/livewire/home/vouchers.blade.php
+++ b/resources/views/livewire/home/vouchers.blade.php
@@ -67,7 +67,13 @@ class="absolute top-0 right-0 w-24 h-24 bg-home-primary/10 rounded-full blur-2xl
class="inline-flex items-center gap-2 bg-white px-3 py-1 rounded-full border border-home-primary/10 shadow-sm mb-4">
{{ $voucher->tags }}
+ class="text-xs font-bold text-home-primary uppercase tracking-wider">{{ $voucher->tags }}
+ @if ($voucher->tiers->isNotEmpty())
+
+
+ Khusus {{ $voucher->tiers->take(2)->pluck('name')->join(', ') }}{{ $voucher->tiers->count() > 2 ? ' +'.($voucher->tiers->count() - 2) : '' }}
+
+ @endif
- Min. Belanja {{ $voucher['min_purchase'] }}
+ Min. Belanja {{ $voucher['min_purchase'] }} • {{ $voucher['tier_name'] }}
- Min. Belanja
-
- Rp
-
-
-
-
+ Min. Belanja
+
+ Rp
+
+
+
+
-
+
-
+
@@ -88,7 +88,7 @@
Jumlah Diskon *
-
+
@foreach (\App\Enums\VoucherType::cases() as $item)
{{ $item->label() }}
@@ -142,6 +142,18 @@
+
+
+ Tier
+ Jika tidak memilih, maka voucher berlaku tanpa membatasi tier.
+
+ @foreach ($tiers as $key => $value)
+ {{ $value }}
+ @endforeach
+
+
+
+