feat(tier reward): implementasi crud untuk hadiah tier
This commit is contained in:
parent
2f62af0f76
commit
be4ff665df
76
app/Livewire/Forms/Studio/Loyalty/Tier/RewardForm.php
Normal file
76
app/Livewire/Forms/Studio/Loyalty/Tier/RewardForm.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Forms\Studio\Loyalty\Tier;
|
||||||
|
|
||||||
|
use App\Models\TierReward;
|
||||||
|
use App\Rules\UnsignedInteger;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Livewire\Form;
|
||||||
|
|
||||||
|
class RewardForm extends Form
|
||||||
|
{
|
||||||
|
public ?TierReward $tierReward = null;
|
||||||
|
|
||||||
|
public string $tier_id = '';
|
||||||
|
|
||||||
|
public string $name = '';
|
||||||
|
|
||||||
|
public string $value = '';
|
||||||
|
|
||||||
|
public ?string $how_to_get = null;
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'tier_id' => ['required', Rule::exists('tiers', 'id')],
|
||||||
|
'name' => ['required', 'string', 'max:100'],
|
||||||
|
'value' => ['required', new UnsignedInteger],
|
||||||
|
'how_to_get' => 'nullable',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validationAttributes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'nama',
|
||||||
|
'value' => 'nilia',
|
||||||
|
'how_to_get' => 'cara mendapatkan',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRewards(TierReward $tierReward)
|
||||||
|
{
|
||||||
|
$this->tierReward = $tierReward;
|
||||||
|
|
||||||
|
$this->tier_id = $tierReward->tier_id;
|
||||||
|
$this->name = $tierReward->name;
|
||||||
|
$this->value = currency($tierReward->value);
|
||||||
|
$this->how_to_get = $tierReward->how_to_get;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
return TierReward::create($this->prepareSavedData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
$this->tierReward->update($this->prepareSavedData());
|
||||||
|
|
||||||
|
return $this->tierReward;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function prepareSavedData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'tier_id' => $this->tier_id,
|
||||||
|
'name' => $this->name,
|
||||||
|
'value' => replaceCurrency($this->value),
|
||||||
|
'how_to_get' => $this->how_to_get,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
95
app/Livewire/Studio/Loyalty/Tier/Reward.php
Normal file
95
app/Livewire/Studio/Loyalty/Tier/Reward.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Studio\Loyalty\Tier;
|
||||||
|
|
||||||
|
use App\Livewire\Forms\Studio\Loyalty\Tier\RewardForm;
|
||||||
|
use App\Models\TierReward;
|
||||||
|
use App\Traits\WithAuthorization;
|
||||||
|
use App\Traits\WithCloseModal;
|
||||||
|
use App\Traits\WithConfirmation;
|
||||||
|
use App\Traits\WithToast;
|
||||||
|
use App\Traits\WithUpdatedData;
|
||||||
|
use Flux\Flux;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
use Livewire\Attributes\Title;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
#[Title('Hadiah')]
|
||||||
|
class Reward extends Component
|
||||||
|
{
|
||||||
|
use WithAuthorization, WithCloseModal, WithConfirmation, WithToast, WithUpdatedData;
|
||||||
|
|
||||||
|
public RewardForm $form;
|
||||||
|
|
||||||
|
public string $modalTitle = '';
|
||||||
|
|
||||||
|
public string $method = '';
|
||||||
|
|
||||||
|
public array $rewards = [];
|
||||||
|
|
||||||
|
#[On('modal:open:reward')]
|
||||||
|
public function openModal(string $method, string $modalTitle, ?string $tier_id = null, ?string $id = null)
|
||||||
|
{
|
||||||
|
$this->resetValidation();
|
||||||
|
$this->resetErrorBag();
|
||||||
|
|
||||||
|
$this->method = $method;
|
||||||
|
$this->modalTitle = $modalTitle;
|
||||||
|
|
||||||
|
$this->form->tier_id = $tier_id;
|
||||||
|
|
||||||
|
$this->rewards = TierReward::where('tier_id', $tier_id)
|
||||||
|
->get()
|
||||||
|
->map(fn (TierReward $reward) => [
|
||||||
|
'hash' => $reward->hash,
|
||||||
|
'tier_id' => $reward->tier_id,
|
||||||
|
'name' => $reward->name,
|
||||||
|
'value' => currency($reward->value, 'Rp'),
|
||||||
|
])
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
if ($id) {
|
||||||
|
$this->form->setRewards(TierReward::byHashOrFail($id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
$this->canOrAbort('create tier reward');
|
||||||
|
|
||||||
|
$this->form->store();
|
||||||
|
|
||||||
|
$this->toast('Hadiah berhasil ditambahkan.');
|
||||||
|
|
||||||
|
Flux::modals()->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
$this->canOrAbort('update tier reward');
|
||||||
|
|
||||||
|
$this->form->update();
|
||||||
|
|
||||||
|
$this->toast('Hadiah berhasil diperbarui.');
|
||||||
|
|
||||||
|
Flux::modals()->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(TierReward $reward)
|
||||||
|
{
|
||||||
|
$reward->delete();
|
||||||
|
|
||||||
|
$this->toast('Hadiah berhasil dihapus.');
|
||||||
|
|
||||||
|
Flux::modal('delete')->close();
|
||||||
|
|
||||||
|
$this->rewards = array_values(array_filter($this->rewards, fn ($item) => $item['hash'] !== $reward->hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.studio.loyalty.tier.reward', [
|
||||||
|
'pageTitle' => 'Hadiah',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,4 +29,9 @@ public function memberships(): HasMany
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Membership::class);
|
return $this->hasMany(Membership::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function rewards(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(TierReward::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
app/Models/TierReward.php
Normal file
20
app/Models/TierReward.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Veelasky\LaravelHashId\Eloquent\HashableId;
|
||||||
|
|
||||||
|
class TierReward extends Model
|
||||||
|
{
|
||||||
|
use HashableId, SoftDeletes;
|
||||||
|
|
||||||
|
protected $guarded = ['id'];
|
||||||
|
|
||||||
|
public function tier(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Tier::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
<?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('tier_rewards', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('tier_id')->constrained()->cascadeOnDelete();
|
||||||
|
$table->string('name', 100);
|
||||||
|
$table->unsignedInteger('value');
|
||||||
|
$table->text('how_to_get')->nullable();
|
||||||
|
$table->timestamp('created_at')->useCurrent();
|
||||||
|
$table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate();
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('tier_rewards');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -49,6 +49,11 @@ public function run(): void
|
|||||||
'update tier',
|
'update tier',
|
||||||
'delete tier',
|
'delete tier',
|
||||||
|
|
||||||
|
'view tier reward',
|
||||||
|
'create tier reward',
|
||||||
|
'update tier reward',
|
||||||
|
'delete tier reward',
|
||||||
|
|
||||||
'view voucher',
|
'view voucher',
|
||||||
'create voucher',
|
'create voucher',
|
||||||
'update voucher',
|
'update voucher',
|
||||||
|
|||||||
@ -0,0 +1,87 @@
|
|||||||
|
<div>
|
||||||
|
<flux:modal name="reward-view-modal" class="w-[95%] max-w-sm md:max-w-xl mx-auto"
|
||||||
|
@close="closeModal('reward-view-modal')">
|
||||||
|
<div class="p-4 space-y-6">
|
||||||
|
<flux:heading size="lg">{{ $modalTitle }}</flux:heading>
|
||||||
|
|
||||||
|
<flux:table class="bo">
|
||||||
|
<flux:table.columns>
|
||||||
|
<flux:table.column>Nama</flux:table.column>
|
||||||
|
<flux:table.column>Nilai</flux:table.column>
|
||||||
|
<flux:table.column>Aksi</flux:table.column>
|
||||||
|
</flux:table.columns>
|
||||||
|
|
||||||
|
<flux:table.rows>
|
||||||
|
@foreach ($rewards as $reward)
|
||||||
|
<flux:table.row>
|
||||||
|
<flux:table.cell>{{ $reward['name'] }}</flux:table.cell>
|
||||||
|
<flux:table.cell>{{ $reward['value'] }}</flux:table.cell>
|
||||||
|
<flux:table.cell>
|
||||||
|
@can('update tier reward')
|
||||||
|
<flux:modal.trigger name="reward-form-modal">
|
||||||
|
<flux:tooltip content="Ubah">
|
||||||
|
<flux:button variant="primary" color="yellow" icon="pencil-square" size="sm"
|
||||||
|
wire:click="$dispatch('modal:open:reward', {method: 'update', 'modalTitle': 'Ubah Hadiah', 'tier_id': '{{ $reward['tier_id'] }}', 'id' : '{{ $reward['hash'] }}'})">
|
||||||
|
</flux:button>
|
||||||
|
</flux:tooltip>
|
||||||
|
</flux:modal.trigger>
|
||||||
|
@endcan
|
||||||
|
|
||||||
|
@can('update tier reward')
|
||||||
|
<flux:modal.trigger name="delete">
|
||||||
|
<flux:tooltip content="Hapus">
|
||||||
|
<flux:button variant="danger" icon="trash" size="sm"
|
||||||
|
wire:click="$dispatch('fn:confirmDelete', {id: '{{ $reward['hash'] }}'})">
|
||||||
|
</flux:button>
|
||||||
|
</flux:tooltip>
|
||||||
|
</flux:modal.trigger>
|
||||||
|
@endcan
|
||||||
|
</flux:table.cell>
|
||||||
|
</flux:table.row>
|
||||||
|
@endforeach
|
||||||
|
</flux:table.rows>
|
||||||
|
</flux:table>
|
||||||
|
</div>
|
||||||
|
</flux:modal>
|
||||||
|
|
||||||
|
<flux:modal name="reward-form-modal" class="w-[95%] max-w-sm md:max-w-xl mx-auto"
|
||||||
|
@close="closeModal('reward-form-modal')">
|
||||||
|
<div class="p-4 space-y-6">
|
||||||
|
<flux:heading size="lg">{{ $modalTitle }}</flux:heading>
|
||||||
|
|
||||||
|
<flux:input name="form.tier_id" type="hidden"></flux:input>
|
||||||
|
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Nama <span class="text-red-500 ms-1">*</span></flux:label>
|
||||||
|
<flux:input placeholder="Voucher Belanja 20k" wire:model.live.debounce.500ms="form.name" autofocus
|
||||||
|
autocomplete="off" />
|
||||||
|
<flux:error name="form.name" />
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Nilai <span class="text-red-500 ms-1">*</span>
|
||||||
|
</flux:label>
|
||||||
|
<flux:description>Nilai hanya berupa rupiah, bukan persentase.</flux:description>
|
||||||
|
<flux:input.group>
|
||||||
|
<flux:input.group.prefix>Rp</flux:input.group.prefix>
|
||||||
|
<flux:input placeholder="xxx" x-mask:dynamic="$money($input, ',')"
|
||||||
|
wire:model.live.debounce.500ms="form.value" autocomplete="off" />
|
||||||
|
</flux:input.group>
|
||||||
|
<flux:error name="form.value" />
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
<flux:editor label="Cara Mendapatkan" wire:model="form.how_to_get"
|
||||||
|
placeholder="Jelaskan kepada user cara mendapatkan / claim hadiah nya." auotocomplete="off"
|
||||||
|
class="**:data-[slot=content]:min-h-[100px]!" />
|
||||||
|
|
||||||
|
<div class="flex">
|
||||||
|
<flux:spacer />
|
||||||
|
<flux:button variant="primary" class="sm:w-auto cursor-pointer" wire:click="{{ $method }}">
|
||||||
|
Simpan
|
||||||
|
</flux:button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</flux:modal>
|
||||||
|
|
||||||
|
@include('components.confirmation.delete')
|
||||||
|
</div>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 auto-rows-auto items-start">
|
||||||
@foreach ($tiers as $tier)
|
@foreach ($tiers as $tier)
|
||||||
<flux:card
|
<flux:card
|
||||||
class="hover:shadow-lg transition-shadow duration-200 bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
class="hover:shadow-lg transition-shadow duration-200 bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
||||||
@ -74,12 +74,31 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2 pt-2 border-gray-200 dark:border-gray-700">
|
<div class="flex gap-2 pt-2 border-gray-200 dark:border-gray-700">
|
||||||
|
@can('view tier reward')
|
||||||
|
<flux:modal.trigger name="reward-view-modal">
|
||||||
|
<flux:tooltip content="Lihat Hadiah">
|
||||||
|
<flux:button variant="primary" color="blue" icon="eye" size="sm"
|
||||||
|
wire:click="$dispatch('modal:open:reward', {method: 'create', 'modalTitle': '{{ $tier['name'] }}', 'tier_id': '{{ $tier['id'] }}'})">
|
||||||
|
</flux:button>
|
||||||
|
</flux:tooltip>
|
||||||
|
</flux:modal.trigger>
|
||||||
|
@endcan
|
||||||
|
|
||||||
|
@can('create tier reward')
|
||||||
|
<flux:modal.trigger name="reward-form-modal">
|
||||||
|
<flux:tooltip content="Tambah Hadiah">
|
||||||
|
<flux:button variant="primary" color="primary" icon="plus" size="sm"
|
||||||
|
wire:click="$dispatch('modal:open:reward', {method: 'create', 'modalTitle': 'Tambah Hadiah', 'tier_id': '{{ $tier['id'] }}'})">
|
||||||
|
</flux:button>
|
||||||
|
</flux:tooltip>
|
||||||
|
</flux:modal.trigger>
|
||||||
|
@endcan
|
||||||
|
|
||||||
@can('update tier')
|
@can('update tier')
|
||||||
<flux:modal.trigger name="form-modal">
|
<flux:modal.trigger name="form-modal">
|
||||||
<flux:tooltip content="Ubah">
|
<flux:tooltip content="Ubah">
|
||||||
<flux:button variant="primary" color="yellow" icon="pencil-square" size="sm"
|
<flux:button variant="primary" color="yellow" icon="pencil-square" size="sm"
|
||||||
wire:click="$dispatch('modal:open', {method: 'update', 'modalTitle': 'Ubah Tier', 'id': '{{ $tier['hash'] }}'})">
|
wire:click="$dispatch('modal:open', {method: 'update', 'modalTitle': 'Ubah Tier', 'id': '{{ $tier['hash'] }}'})">
|
||||||
Ubah
|
|
||||||
</flux:button>
|
</flux:button>
|
||||||
</flux:tooltip>
|
</flux:tooltip>
|
||||||
</flux:modal.trigger>
|
</flux:modal.trigger>
|
||||||
@ -90,7 +109,6 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
|
|||||||
<flux:tooltip content="Hapus">
|
<flux:tooltip content="Hapus">
|
||||||
<flux:button variant="danger" icon="trash" size="sm"
|
<flux:button variant="danger" icon="trash" size="sm"
|
||||||
wire:click="$dispatch('fn:confirmDelete', {id: '{{ $tier['hash'] }}'})">
|
wire:click="$dispatch('fn:confirmDelete', {id: '{{ $tier['hash'] }}'})">
|
||||||
Hapus
|
|
||||||
</flux:button>
|
</flux:button>
|
||||||
</flux:tooltip>
|
</flux:tooltip>
|
||||||
</flux:modal.trigger>
|
</flux:modal.trigger>
|
||||||
@ -109,14 +127,14 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
|
|||||||
|
|
||||||
<flux:field>
|
<flux:field>
|
||||||
<flux:label>Nama <span class="text-red-500 ms-1">*</span></flux:label>
|
<flux:label>Nama <span class="text-red-500 ms-1">*</span></flux:label>
|
||||||
<flux:input placeholder="Legenda" wire:model.live.debounce.500ms="form.name" autofocus
|
<flux:input placeholder="Gold" wire:model.live.debounce.500ms="form.name" autofocus
|
||||||
autocomplete="off" />
|
autocomplete="off" />
|
||||||
<flux:error name="form.name" />
|
<flux:error name="form.name" />
|
||||||
</flux:field>
|
</flux:field>
|
||||||
|
|
||||||
<flux:field>
|
<flux:field>
|
||||||
<flux:label>Min. Poin <span class="text-red-500 ms-1">*</span></flux:label>
|
<flux:label>Min. Poin <span class="text-red-500 ms-1">*</span></flux:label>
|
||||||
<flux:input placeholder="101" wire:model.live.debounce.500ms="form.min_points" autocomplete="off"
|
<flux:input placeholder="100" wire:model.live.debounce.500ms="form.min_points" autocomplete="off"
|
||||||
x-mask:dynamic="$money($input, ',')" />
|
x-mask:dynamic="$money($input, ',')" />
|
||||||
<flux:error name="form.min_points" />
|
<flux:error name="form.min_points" />
|
||||||
</flux:field>
|
</flux:field>
|
||||||
@ -137,4 +155,7 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
|
|||||||
</div>
|
</div>
|
||||||
</flux:modal>
|
</flux:modal>
|
||||||
|
|
||||||
|
@can('create tier reward')
|
||||||
|
@livewire('studio.loyalty.tier.reward')
|
||||||
|
@endcan
|
||||||
</flux:main>
|
</flux:main>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user