feat(voucher): mengaitkan voucher dengan outlet dengan relasi many to many
-membuat tabel pivot -menyesuaikan seeder -menyesuaikan logika yang berkaitan dengan perubahan tersebut
This commit is contained in:
parent
6b74908d45
commit
ef05482000
@ -10,6 +10,7 @@
|
|||||||
use Illuminate\Support\Facades\Blade;
|
use Illuminate\Support\Facades\Blade;
|
||||||
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
||||||
use Rappasoft\LaravelLivewireTables\Views\Column;
|
use Rappasoft\LaravelLivewireTables\Views\Column;
|
||||||
|
use Rappasoft\LaravelLivewireTables\Views\Columns\ArrayColumn;
|
||||||
|
|
||||||
class VouchersTable extends DataTableComponent
|
class VouchersTable extends DataTableComponent
|
||||||
{
|
{
|
||||||
@ -54,6 +55,11 @@ public function columns(): array
|
|||||||
|
|
||||||
Column::make('Batas Per Pembali', 'limit_per_user')->searchable()->sortable(),
|
Column::make('Batas Per Pembali', 'limit_per_user')->searchable()->sortable(),
|
||||||
|
|
||||||
|
ArrayColumn::make('Outlet')
|
||||||
|
->data(fn ($value, $row) => $row->outlets->pluck('name')->toArray())
|
||||||
|
->outputFormat(fn ($index, $value) => Blade::render('<flux:badge color="zinc" dot>'.$value.'</flux:badge>'))
|
||||||
|
->flexRow(['class' => 'gap-2 flex-wrap']),
|
||||||
|
|
||||||
Column::make('Tanggal')
|
Column::make('Tanggal')
|
||||||
->label(function ($row) {
|
->label(function ($row) {
|
||||||
$startDate = formatDate($row->start_date);
|
$startDate = formatDate($row->start_date);
|
||||||
@ -105,6 +111,6 @@ public function columns(): array
|
|||||||
|
|
||||||
public function builder(): Builder
|
public function builder(): Builder
|
||||||
{
|
{
|
||||||
return Voucher::select('id', 'name', 'code', 'min_purchase', 'discount_amount', 'max_discount', 'quota', 'limit_per_user', 'start_date', 'end_date', 'is_active', 'type');
|
return Voucher::select('id', 'name', 'code', 'min_purchase', 'discount_amount', 'max_discount', 'quota', 'limit_per_user', 'start_date', 'end_date', 'is_active', 'type')->with('outlets');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
use App\Enums\VoucherType;
|
use App\Enums\VoucherType;
|
||||||
use App\Models\Voucher;
|
use App\Models\Voucher;
|
||||||
use App\Rules\UnsignedInteger;
|
use App\Rules\UnsignedInteger;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
use Livewire\Form;
|
use Livewire\Form;
|
||||||
|
|
||||||
@ -37,6 +38,8 @@ class VoucherForm extends Form
|
|||||||
|
|
||||||
public string $is_active = '1';
|
public string $is_active = '1';
|
||||||
|
|
||||||
|
public array $outlet_ids = [];
|
||||||
|
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -71,6 +74,8 @@ public function rules(): array
|
|||||||
'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
|
'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
|
||||||
'terms_conditions' => ['nullable', 'string'],
|
'terms_conditions' => ['nullable', 'string'],
|
||||||
'is_active' => ['required', Rule::in(IsActive::values())],
|
'is_active' => ['required', Rule::in(IsActive::values())],
|
||||||
|
'outlet_ids' => ['required', 'array', 'min:1'],
|
||||||
|
'outlet_ids.*' => Rule::exists('outlets', 'id'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +99,8 @@ public function validationAttributes(): array
|
|||||||
|
|
||||||
public function setVoucher(Voucher $voucher)
|
public function setVoucher(Voucher $voucher)
|
||||||
{
|
{
|
||||||
|
$voucher->load('outlets');
|
||||||
|
|
||||||
$this->voucher = $voucher;
|
$this->voucher = $voucher;
|
||||||
|
|
||||||
$this->name = $this->voucher->name;
|
$this->name = $this->voucher->name;
|
||||||
@ -108,6 +115,8 @@ public function setVoucher(Voucher $voucher)
|
|||||||
$this->start_date = $this->voucher->start_date;
|
$this->start_date = $this->voucher->start_date;
|
||||||
$this->end_date = $this->voucher->end_date;
|
$this->end_date = $this->voucher->end_date;
|
||||||
$this->is_active = $this->voucher->is_active->value;
|
$this->is_active = $this->voucher->is_active->value;
|
||||||
|
|
||||||
|
$this->outlet_ids = $this->voucher->outlets->pluck('id')->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store()
|
public function store()
|
||||||
@ -116,7 +125,11 @@ public function store()
|
|||||||
|
|
||||||
$data = $this->prepareSavedData();
|
$data = $this->prepareSavedData();
|
||||||
|
|
||||||
Voucher::create($data);
|
DB::transaction(function () use ($data) {
|
||||||
|
$voucher = Voucher::create($data);
|
||||||
|
|
||||||
|
$voucher->outlets()->attach($this->outlet_ids);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update()
|
public function update()
|
||||||
@ -124,8 +137,11 @@ public function update()
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
|
|
||||||
$data = $this->prepareSavedData();
|
$data = $this->prepareSavedData();
|
||||||
|
DB::transaction(function () use ($data) {
|
||||||
|
$this->voucher->update($data);
|
||||||
|
|
||||||
$this->voucher->update($data);
|
$this->voucher->outlets()->sync($this->outlet_ids);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prepareSavedData()
|
private function prepareSavedData()
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
namespace App\Livewire\Studio\Loyalty\Voucher;
|
namespace App\Livewire\Studio\Loyalty\Voucher;
|
||||||
|
|
||||||
use App\Livewire\Forms\VoucherForm;
|
use App\Livewire\Forms\VoucherForm;
|
||||||
|
use App\Models\Outlet;
|
||||||
|
use App\Traits\Voucher\WithOutletSelector;
|
||||||
use App\Traits\WithUpdatedData;
|
use App\Traits\WithUpdatedData;
|
||||||
use Flux\Flux;
|
use Flux\Flux;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
@ -11,10 +13,17 @@
|
|||||||
#[Title('Tambah Voucher')]
|
#[Title('Tambah Voucher')]
|
||||||
class Create extends Component
|
class Create extends Component
|
||||||
{
|
{
|
||||||
use WithUpdatedData;
|
use WithOutletSelector, WithUpdatedData;
|
||||||
|
|
||||||
public VoucherForm $form;
|
public VoucherForm $form;
|
||||||
|
|
||||||
|
public array $outlets;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->outlets = Outlet::pluck('name', 'id')->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
public function save()
|
public function save()
|
||||||
{
|
{
|
||||||
$this->form->store();
|
$this->form->store();
|
||||||
|
|||||||
@ -3,7 +3,9 @@
|
|||||||
namespace App\Livewire\Studio\Loyalty\Voucher;
|
namespace App\Livewire\Studio\Loyalty\Voucher;
|
||||||
|
|
||||||
use App\Livewire\Forms\VoucherForm;
|
use App\Livewire\Forms\VoucherForm;
|
||||||
|
use App\Models\Outlet;
|
||||||
use App\Models\Voucher;
|
use App\Models\Voucher;
|
||||||
|
use App\Traits\Voucher\WithOutletSelector;
|
||||||
use App\Traits\WithUpdatedData;
|
use App\Traits\WithUpdatedData;
|
||||||
use Flux\Flux;
|
use Flux\Flux;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
@ -12,13 +14,17 @@
|
|||||||
#[Title('Ubah Voucher')]
|
#[Title('Ubah Voucher')]
|
||||||
class Edit extends Component
|
class Edit extends Component
|
||||||
{
|
{
|
||||||
use WithUpdatedData;
|
use WithOutletSelector, WithUpdatedData;
|
||||||
|
|
||||||
public VoucherForm $form;
|
public VoucherForm $form;
|
||||||
|
|
||||||
|
public array $outlets;
|
||||||
|
|
||||||
public function mount(Voucher $voucher)
|
public function mount(Voucher $voucher)
|
||||||
{
|
{
|
||||||
$this->form->setVoucher($voucher);
|
$this->form->setVoucher($voucher);
|
||||||
|
|
||||||
|
$this->outlets = Outlet::pluck('name', 'id')->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save()
|
public function save()
|
||||||
|
|||||||
@ -47,4 +47,9 @@ public function facilities(): HasMany
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Facility::class);
|
return $this->hasMany(Facility::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function vouchers(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Voucher::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
app/Models/OutletVoucher.php
Normal file
12
app/Models/OutletVoucher.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class OutletVoucher extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'outlet_voucher';
|
||||||
|
|
||||||
|
protected $guarded = ['id'];
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@
|
|||||||
use App\Enums\VoucherType;
|
use App\Enums\VoucherType;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class Voucher extends Model
|
class Voucher extends Model
|
||||||
@ -26,4 +27,9 @@ protected function casts(): array
|
|||||||
'is_active' => IsActive::class,
|
'is_active' => IsActive::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function outlets(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Outlet::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
app/Traits/Voucher/WithOutletSelector.php
Normal file
16
app/Traits/Voucher/WithOutletSelector.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Traits\Voucher;
|
||||||
|
|
||||||
|
trait WithOutletSelector
|
||||||
|
{
|
||||||
|
public function selectAll()
|
||||||
|
{
|
||||||
|
$this->form->outlet_ids = array_keys($this->outlets);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deselectAll()
|
||||||
|
{
|
||||||
|
$this->form->outlet_ids = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('outlet_voucher', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('outlet_id')->constrained()->cascadeOnDelete();
|
||||||
|
$table->foreignId('voucher_id')->constrained()->cascadeOnDelete();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('outlet_voucher');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Enums\IsActive;
|
use App\Enums\IsActive;
|
||||||
use App\Enums\VoucherType;
|
use App\Enums\VoucherType;
|
||||||
|
use App\Models\Outlet;
|
||||||
use App\Models\Voucher;
|
use App\Models\Voucher;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ class VoucherSeeder extends Seeder
|
|||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
Voucher::insert([
|
$vouchers = [
|
||||||
[
|
[
|
||||||
'name' => 'Diskon 10%',
|
'name' => 'Diskon 10%',
|
||||||
'code' => 'DISC10',
|
'code' => 'DISC10',
|
||||||
@ -103,6 +104,12 @@ public function run(): void
|
|||||||
'end_date' => null,
|
'end_date' => null,
|
||||||
'is_active' => IsActive::ACTIVE,
|
'is_active' => IsActive::ACTIVE,
|
||||||
],
|
],
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
foreach ($vouchers as $voucher) {
|
||||||
|
$newVoucher = Voucher::create($voucher);
|
||||||
|
|
||||||
|
$newVoucher->outlets()->attach(Outlet::inRandomOrder()->first()->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,6 +125,20 @@ class="**:data-[slot=content]:min-h-[100px]!" />
|
|||||||
@endforeach
|
@endforeach
|
||||||
</flux:radio.group>
|
</flux:radio.group>
|
||||||
</flux:card>
|
</flux:card>
|
||||||
|
|
||||||
|
<flux:card class="space-y-6 p-6">
|
||||||
|
<flux:select variant="listbox" multiple searchable placeholder="Pilih Outlet" label="Outlet"
|
||||||
|
wire:model.live.debounce.500ms="form.outlet_ids">
|
||||||
|
<flux:select.option wire:click="selectAll" wire:ignore>Pilih Semua
|
||||||
|
</flux:select.option>
|
||||||
|
<flux:select.option wire:click="deselectAll" wire:ignore>Hapus Semua
|
||||||
|
</flux:select.option>
|
||||||
|
@foreach ($outlets as $key => $value)
|
||||||
|
<flux:select.option value="{{ $key }}">{{ $value }}
|
||||||
|
</flux:select.option>
|
||||||
|
@endforeach
|
||||||
|
</flux:select>
|
||||||
|
</flux:card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user