From ac0b01bfca7d081862cdf659466c2459a435a259 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Sat, 27 Dec 2025 18:05:41 +0700 Subject: [PATCH 1/9] update --- 0001-update.patch | 280 ++++++++++++++++++ ...ckets_replace_prize_id_with_prize_name.php | 64 ++++ 2 files changed, 344 insertions(+) create mode 100644 0001-update.patch create mode 100644 database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php diff --git a/0001-update.patch b/0001-update.patch new file mode 100644 index 0000000..71e6e0b --- /dev/null +++ b/0001-update.patch @@ -0,0 +1,280 @@ +From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Sat, 27 Dec 2025 17:56:40 +0700 +Subject: [PATCH] update + +--- + app/Filament/Resources/PrizeResource.php | 21 +++-- + app/Livewire/Wheel.php | 12 ++- + app/Models/Prize.php | 4 +- + app/Policies/ContactPolicy.php | 108 +++++++++++++++++++++++ + resources/views/livewire/wheel.blade.php | 2 +- + 5 files changed, 129 insertions(+), 18 deletions(-) + create mode 100644 app/Policies/ContactPolicy.php + +diff --git a/app/Filament/Resources/PrizeResource.php b/app/Filament/Resources/PrizeResource.php +index 147c193..d4f5609 100644 +--- a/app/Filament/Resources/PrizeResource.php ++++ b/app/Filament/Resources/PrizeResource.php +@@ -22,7 +22,7 @@ class PrizeResource extends Resource + + public static function canCreate(): bool + { +- return false; ++ return true; + } + + public static function form(Form $form): Form +@@ -35,9 +35,10 @@ public static function form(Form $form): Form + ->maxLength(255), + Forms\Components\TextInput::make('sort') + ->label(__('Sort')) +- ->required() + ->numeric() +- ->disabledOn('edit'), ++ ->default(fn() => (Prize::max('sort') ?? 0) + 1) ++ ->readOnly() // tetap disimpan ke DB walau disabled ++ ->required(), + Forms\Components\TextInput::make('chance_percentage') + ->label(__('Chance Percentage')) + ->required() +@@ -63,12 +64,10 @@ public static function table(Table $table): Table + ->searchable(), + Tables\Columns\TextColumn::make('sort') + ->label(__('Sort')) +- ->numeric() +- ->sortable(), ++ ->numeric(), + Tables\Columns\TextColumn::make('chance_percentage') + ->label(__('Chance Percentage')) + ->numeric() +- ->sortable() + ->formatStateUsing(fn($state): string => $state . "%") + ->summarize([ + Sum::make()->formatStateUsing(fn($state): string => (round($state * 100)) / 100 . "%"), +@@ -76,7 +75,6 @@ public static function table(Table $table): Table + Tables\Columns\TextColumn::make('winable') + ->label(__('Status')) + ->badge() +- ->badge() + ->color(fn(string $state): string => match ($state) { + 'winable' => 'success', + 'unwinable' => 'danger', +@@ -85,20 +83,21 @@ public static function table(Table $table): Table + Tables\Columns\TextColumn::make('created_at') + ->label(__('Created At')) + ->dateTime() +- ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->label(__('Updated At')) + ->dateTime() +- ->sortable() + ->toggleable(isToggledHiddenByDefault: true), +- + ]) ++ ->defaultSort('sort') ++ ->reorderable('sort') ++ ->paginated(false) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), ++ Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([]) + ->paginated(false);; +@@ -129,4 +128,4 @@ public static function getModelLabel(): string + { + return __('Prize'); + } +-} +\ No newline at end of file ++} +diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php +index 602970c..bfb1dd7 100644 +--- a/app/Livewire/Wheel.php ++++ b/app/Livewire/Wheel.php +@@ -18,8 +18,12 @@ class Wheel extends Component + public $rotationFinal = 0; + public $state = 'ready'; + public $prize; ++ public $totalPrize = 0; + +- ++ public function mount() ++ { ++ $this->totalPrize = Prize::count(); ++ } + + public function redeem() + { +@@ -71,7 +75,7 @@ public function redeem() + $this->code = ""; + + if ($ticket->type == 'prize') { +- $total = (rand(7, 9) * 8) - 1 + $ticket->prize->sort; ++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $ticket->prize->sort; + $this->state = $ticket->prize->winable; + $this->prize = $ticket->prize; + $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; +@@ -104,7 +108,7 @@ public function redeem() + $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; + $ticket->spin_time = date('Y-m-d H:i:s'); + $ticket->save(); +- $total = (rand(7, 9) * 8) - 1 + $final->sort; ++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $final->sort; + $this->dispatch('spin', $total); + return; + } +@@ -114,4 +118,4 @@ public function render() + $history = Ticket::where('status', 'win')->orderBy('spin_time', 'desc')->take(10)->get(); + return view('livewire.wheel', compact('history')); + } +-} +\ No newline at end of file ++} +diff --git a/app/Models/Prize.php b/app/Models/Prize.php +index 6d5071c..758b4b1 100644 +--- a/app/Models/Prize.php ++++ b/app/Models/Prize.php +@@ -6,5 +6,5 @@ + + class Prize extends Model + { +- protected $fillable = ['prize', 'chance_percentage', 'winable']; +-} +\ No newline at end of file ++ protected $fillable = ['prize', 'chance_percentage', 'winable', 'sort']; ++} +diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php +new file mode 100644 +index 0000000..97aae7e +--- /dev/null ++++ b/app/Policies/ContactPolicy.php +@@ -0,0 +1,108 @@ ++can('view_any_contact'); ++ } ++ ++ /** ++ * Determine whether the user can view the model. ++ */ ++ public function view(User $user, Contact $contact): bool ++ { ++ return $user->can('view_contact'); ++ } ++ ++ /** ++ * Determine whether the user can create models. ++ */ ++ public function create(User $user): bool ++ { ++ return $user->can('create_contact'); ++ } ++ ++ /** ++ * Determine whether the user can update the model. ++ */ ++ public function update(User $user, Contact $contact): bool ++ { ++ return $user->can('update_contact'); ++ } ++ ++ /** ++ * Determine whether the user can delete the model. ++ */ ++ public function delete(User $user, Contact $contact): bool ++ { ++ return $user->can('delete_contact'); ++ } ++ ++ /** ++ * Determine whether the user can bulk delete. ++ */ ++ public function deleteAny(User $user): bool ++ { ++ return $user->can('delete_any_contact'); ++ } ++ ++ /** ++ * Determine whether the user can permanently delete. ++ */ ++ public function forceDelete(User $user, Contact $contact): bool ++ { ++ return $user->can('force_delete_contact'); ++ } ++ ++ /** ++ * Determine whether the user can permanently bulk delete. ++ */ ++ public function forceDeleteAny(User $user): bool ++ { ++ return $user->can('force_delete_any_contact'); ++ } ++ ++ /** ++ * Determine whether the user can restore. ++ */ ++ public function restore(User $user, Contact $contact): bool ++ { ++ return $user->can('restore_contact'); ++ } ++ ++ /** ++ * Determine whether the user can bulk restore. ++ */ ++ public function restoreAny(User $user): bool ++ { ++ return $user->can('restore_any_contact'); ++ } ++ ++ /** ++ * Determine whether the user can replicate. ++ */ ++ public function replicate(User $user, Contact $contact): bool ++ { ++ return $user->can('replicate_contact'); ++ } ++ ++ /** ++ * Determine whether the user can reorder. ++ */ ++ public function reorder(User $user): bool ++ { ++ return $user->can('reorder_contact'); ++ } ++} +diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php +index da3bdea..2f1806d 100644 +--- a/resources/views/livewire/wheel.blade.php ++++ b/resources/views/livewire/wheel.blade.php +@@ -29,7 +29,7 @@ + this.audio.currentTime = 0; // Mulai dari awal + this.audio.play(); // Mainkan suara + const rotations = total; +- const totalRotate = rotations * -45; ++ const totalRotate = rotations * -(360 / {{ $totalPrize }}); + this.shift = -totalRotate; + const duration = 8000; // 8 detik + const start = performance.now(); +-- +2.50.0 + diff --git a/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php new file mode 100644 index 0000000..938d967 --- /dev/null +++ b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php @@ -0,0 +1,64 @@ +string('prize_name')->nullable()->after('member_id'); + }); + + // 2. Copy data dari prizes.prize ke tickets.prize_name + // (aman walau ada prize_id null) + DB::statement(" + UPDATE tickets t + LEFT JOIN prizes p ON p.id = t.prize_id + SET t.prize_name = p.prize + WHERE t.prize_id IS NOT NULL + "); + + // 3. Drop foreign key + kolom prize_id + Schema::table('tickets', function (Blueprint $table) { + // default Laravel FK name + $table->dropForeign(['prize_id']); + $table->dropColumn('prize_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // 1. Balikin kolom prize_id + Schema::table('tickets', function (Blueprint $table) { + $table->foreignId('prize_id') + ->nullable() + ->after('member_id') + ->constrained() + ->onDelete('cascade'); + }); + + // 2. Isi ulang prize_id dari prize_name (kalau nama masih sama) + DB::statement(" + UPDATE tickets t + JOIN prizes p ON p.prize = t.prize_name + SET t.prize_id = p.id + WHERE t.prize_name IS NOT NULL + "); + + // 3. Hapus snapshot + Schema::table('tickets', function (Blueprint $table) { + $table->dropColumn('prize_name'); + }); + } +}; -- 2.50.0 From b6e1f41b06c016b2560a510248b03840bca73603 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Sat, 27 Dec 2025 18:07:15 +0700 Subject: [PATCH 2/9] update --- 0001-update.patch | 2 +- 0002-update.patch | 371 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 0002-update.patch diff --git a/0001-update.patch b/0001-update.patch index 71e6e0b..a7d1126 100644 --- a/0001-update.patch +++ b/0001-update.patch @@ -1,7 +1,7 @@ From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Sat, 27 Dec 2025 17:56:40 +0700 -Subject: [PATCH] update +Subject: [PATCH 1/2] update --- app/Filament/Resources/PrizeResource.php | 21 +++-- diff --git a/0002-update.patch b/0002-update.patch new file mode 100644 index 0000000..4f8729a --- /dev/null +++ b/0002-update.patch @@ -0,0 +1,371 @@ +From ac0b01bfca7d081862cdf659466c2459a435a259 Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Sat, 27 Dec 2025 18:05:41 +0700 +Subject: [PATCH 2/2] update + +--- + 0001-update.patch | 280 ++++++++++++++++++ + ...ckets_replace_prize_id_with_prize_name.php | 64 ++++ + 2 files changed, 344 insertions(+) + create mode 100644 0001-update.patch + create mode 100644 database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php + +diff --git a/0001-update.patch b/0001-update.patch +new file mode 100644 +index 0000000..71e6e0b +--- /dev/null ++++ b/0001-update.patch +@@ -0,0 +1,280 @@ ++From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 ++From: Sinji Prasetio ++Date: Sat, 27 Dec 2025 17:56:40 +0700 ++Subject: [PATCH] update ++ ++--- ++ app/Filament/Resources/PrizeResource.php | 21 +++-- ++ app/Livewire/Wheel.php | 12 ++- ++ app/Models/Prize.php | 4 +- ++ app/Policies/ContactPolicy.php | 108 +++++++++++++++++++++++ ++ resources/views/livewire/wheel.blade.php | 2 +- ++ 5 files changed, 129 insertions(+), 18 deletions(-) ++ create mode 100644 app/Policies/ContactPolicy.php ++ ++diff --git a/app/Filament/Resources/PrizeResource.php b/app/Filament/Resources/PrizeResource.php ++index 147c193..d4f5609 100644 ++--- a/app/Filament/Resources/PrizeResource.php +++++ b/app/Filament/Resources/PrizeResource.php ++@@ -22,7 +22,7 @@ class PrizeResource extends Resource ++ ++ public static function canCreate(): bool ++ { ++- return false; +++ return true; ++ } ++ ++ public static function form(Form $form): Form ++@@ -35,9 +35,10 @@ public static function form(Form $form): Form ++ ->maxLength(255), ++ Forms\Components\TextInput::make('sort') ++ ->label(__('Sort')) ++- ->required() ++ ->numeric() ++- ->disabledOn('edit'), +++ ->default(fn() => (Prize::max('sort') ?? 0) + 1) +++ ->readOnly() // tetap disimpan ke DB walau disabled +++ ->required(), ++ Forms\Components\TextInput::make('chance_percentage') ++ ->label(__('Chance Percentage')) ++ ->required() ++@@ -63,12 +64,10 @@ public static function table(Table $table): Table ++ ->searchable(), ++ Tables\Columns\TextColumn::make('sort') ++ ->label(__('Sort')) ++- ->numeric() ++- ->sortable(), +++ ->numeric(), ++ Tables\Columns\TextColumn::make('chance_percentage') ++ ->label(__('Chance Percentage')) ++ ->numeric() ++- ->sortable() ++ ->formatStateUsing(fn($state): string => $state . "%") ++ ->summarize([ ++ Sum::make()->formatStateUsing(fn($state): string => (round($state * 100)) / 100 . "%"), ++@@ -76,7 +75,6 @@ public static function table(Table $table): Table ++ Tables\Columns\TextColumn::make('winable') ++ ->label(__('Status')) ++ ->badge() ++- ->badge() ++ ->color(fn(string $state): string => match ($state) { ++ 'winable' => 'success', ++ 'unwinable' => 'danger', ++@@ -85,20 +83,21 @@ public static function table(Table $table): Table ++ Tables\Columns\TextColumn::make('created_at') ++ ->label(__('Created At')) ++ ->dateTime() ++- ->sortable() ++ ->toggleable(isToggledHiddenByDefault: true), ++ Tables\Columns\TextColumn::make('updated_at') ++ ->label(__('Updated At')) ++ ->dateTime() ++- ->sortable() ++ ->toggleable(isToggledHiddenByDefault: true), ++- ++ ]) +++ ->defaultSort('sort') +++ ->reorderable('sort') +++ ->paginated(false) ++ ->filters([ ++ // ++ ]) ++ ->actions([ ++ Tables\Actions\EditAction::make(), +++ Tables\Actions\DeleteAction::make(), ++ ]) ++ ->bulkActions([]) ++ ->paginated(false);; ++@@ -129,4 +128,4 @@ public static function getModelLabel(): string ++ { ++ return __('Prize'); ++ } ++-} ++\ No newline at end of file +++} ++diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php ++index 602970c..bfb1dd7 100644 ++--- a/app/Livewire/Wheel.php +++++ b/app/Livewire/Wheel.php ++@@ -18,8 +18,12 @@ class Wheel extends Component ++ public $rotationFinal = 0; ++ public $state = 'ready'; ++ public $prize; +++ public $totalPrize = 0; ++ ++- +++ public function mount() +++ { +++ $this->totalPrize = Prize::count(); +++ } ++ ++ public function redeem() ++ { ++@@ -71,7 +75,7 @@ public function redeem() ++ $this->code = ""; ++ ++ if ($ticket->type == 'prize') { ++- $total = (rand(7, 9) * 8) - 1 + $ticket->prize->sort; +++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $ticket->prize->sort; ++ $this->state = $ticket->prize->winable; ++ $this->prize = $ticket->prize; ++ $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; ++@@ -104,7 +108,7 @@ public function redeem() ++ $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; ++ $ticket->spin_time = date('Y-m-d H:i:s'); ++ $ticket->save(); ++- $total = (rand(7, 9) * 8) - 1 + $final->sort; +++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $final->sort; ++ $this->dispatch('spin', $total); ++ return; ++ } ++@@ -114,4 +118,4 @@ public function render() ++ $history = Ticket::where('status', 'win')->orderBy('spin_time', 'desc')->take(10)->get(); ++ return view('livewire.wheel', compact('history')); ++ } ++-} ++\ No newline at end of file +++} ++diff --git a/app/Models/Prize.php b/app/Models/Prize.php ++index 6d5071c..758b4b1 100644 ++--- a/app/Models/Prize.php +++++ b/app/Models/Prize.php ++@@ -6,5 +6,5 @@ ++ ++ class Prize extends Model ++ { ++- protected $fillable = ['prize', 'chance_percentage', 'winable']; ++-} ++\ No newline at end of file +++ protected $fillable = ['prize', 'chance_percentage', 'winable', 'sort']; +++} ++diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php ++new file mode 100644 ++index 0000000..97aae7e ++--- /dev/null +++++ b/app/Policies/ContactPolicy.php ++@@ -0,0 +1,108 @@ +++can('view_any_contact'); +++ } +++ +++ /** +++ * Determine whether the user can view the model. +++ */ +++ public function view(User $user, Contact $contact): bool +++ { +++ return $user->can('view_contact'); +++ } +++ +++ /** +++ * Determine whether the user can create models. +++ */ +++ public function create(User $user): bool +++ { +++ return $user->can('create_contact'); +++ } +++ +++ /** +++ * Determine whether the user can update the model. +++ */ +++ public function update(User $user, Contact $contact): bool +++ { +++ return $user->can('update_contact'); +++ } +++ +++ /** +++ * Determine whether the user can delete the model. +++ */ +++ public function delete(User $user, Contact $contact): bool +++ { +++ return $user->can('delete_contact'); +++ } +++ +++ /** +++ * Determine whether the user can bulk delete. +++ */ +++ public function deleteAny(User $user): bool +++ { +++ return $user->can('delete_any_contact'); +++ } +++ +++ /** +++ * Determine whether the user can permanently delete. +++ */ +++ public function forceDelete(User $user, Contact $contact): bool +++ { +++ return $user->can('force_delete_contact'); +++ } +++ +++ /** +++ * Determine whether the user can permanently bulk delete. +++ */ +++ public function forceDeleteAny(User $user): bool +++ { +++ return $user->can('force_delete_any_contact'); +++ } +++ +++ /** +++ * Determine whether the user can restore. +++ */ +++ public function restore(User $user, Contact $contact): bool +++ { +++ return $user->can('restore_contact'); +++ } +++ +++ /** +++ * Determine whether the user can bulk restore. +++ */ +++ public function restoreAny(User $user): bool +++ { +++ return $user->can('restore_any_contact'); +++ } +++ +++ /** +++ * Determine whether the user can replicate. +++ */ +++ public function replicate(User $user, Contact $contact): bool +++ { +++ return $user->can('replicate_contact'); +++ } +++ +++ /** +++ * Determine whether the user can reorder. +++ */ +++ public function reorder(User $user): bool +++ { +++ return $user->can('reorder_contact'); +++ } +++} ++diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php ++index da3bdea..2f1806d 100644 ++--- a/resources/views/livewire/wheel.blade.php +++++ b/resources/views/livewire/wheel.blade.php ++@@ -29,7 +29,7 @@ ++ this.audio.currentTime = 0; // Mulai dari awal ++ this.audio.play(); // Mainkan suara ++ const rotations = total; ++- const totalRotate = rotations * -45; +++ const totalRotate = rotations * -(360 / {{ $totalPrize }}); ++ this.shift = -totalRotate; ++ const duration = 8000; // 8 detik ++ const start = performance.now(); ++-- ++2.50.0 ++ +diff --git a/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php +new file mode 100644 +index 0000000..938d967 +--- /dev/null ++++ b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php +@@ -0,0 +1,64 @@ ++string('prize_name')->nullable()->after('member_id'); ++ }); ++ ++ // 2. Copy data dari prizes.prize ke tickets.prize_name ++ // (aman walau ada prize_id null) ++ DB::statement(" ++ UPDATE tickets t ++ LEFT JOIN prizes p ON p.id = t.prize_id ++ SET t.prize_name = p.prize ++ WHERE t.prize_id IS NOT NULL ++ "); ++ ++ // 3. Drop foreign key + kolom prize_id ++ Schema::table('tickets', function (Blueprint $table) { ++ // default Laravel FK name ++ $table->dropForeign(['prize_id']); ++ $table->dropColumn('prize_id'); ++ }); ++ } ++ ++ /** ++ * Reverse the migrations. ++ */ ++ public function down(): void ++ { ++ // 1. Balikin kolom prize_id ++ Schema::table('tickets', function (Blueprint $table) { ++ $table->foreignId('prize_id') ++ ->nullable() ++ ->after('member_id') ++ ->constrained() ++ ->onDelete('cascade'); ++ }); ++ ++ // 2. Isi ulang prize_id dari prize_name (kalau nama masih sama) ++ DB::statement(" ++ UPDATE tickets t ++ JOIN prizes p ON p.prize = t.prize_name ++ SET t.prize_id = p.id ++ WHERE t.prize_name IS NOT NULL ++ "); ++ ++ // 3. Hapus snapshot ++ Schema::table('tickets', function (Blueprint $table) { ++ $table->dropColumn('prize_name'); ++ }); ++ } ++}; +-- +2.50.0 + -- 2.50.0 From 085158048211008965f9839ac0e5c6a71b6e4906 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Sun, 28 Dec 2025 21:40:18 +0700 Subject: [PATCH 3/9] update --- app/Livewire/Wheel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php index bfb1dd7..4f82c71 100644 --- a/app/Livewire/Wheel.php +++ b/app/Livewire/Wheel.php @@ -17,7 +17,7 @@ class Wheel extends Component public $code; public $rotationFinal = 0; public $state = 'ready'; - public $prize; + public $prize = ''; public $totalPrize = 0; public function mount() -- 2.50.0 From 1d3c9eb95337d0a095c841d702e6efc2fe666854 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Sun, 28 Dec 2025 21:44:19 +0700 Subject: [PATCH 4/9] update --- app/Livewire/Wheel.php | 2 +- resources/views/livewire/wheel.blade.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php index 4f82c71..bfb1dd7 100644 --- a/app/Livewire/Wheel.php +++ b/app/Livewire/Wheel.php @@ -17,7 +17,7 @@ class Wheel extends Component public $code; public $rotationFinal = 0; public $state = 'ready'; - public $prize = ''; + public $prize; public $totalPrize = 0; public function mount() diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php index 2f1806d..6be22cb 100644 --- a/resources/views/livewire/wheel.blade.php +++ b/resources/views/livewire/wheel.blade.php @@ -212,7 +212,7 @@ class="absolute p-2 rounded-full bg-gradient-to-b from-violet-800 to-violet-900 {{ $i + 1 }} {{ format()->maskUsername($h->member->username) }} - {{ $h->prize->prize }} + {{ $h->prize }} @endforeach -- 2.50.0 From c31e4d00e2edbb1a56c80dd1222df99e169e8588 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Mon, 29 Dec 2025 20:43:47 +0700 Subject: [PATCH 5/9] update --- resources/views/livewire/wheel.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php index 6be22cb..df4debd 100644 --- a/resources/views/livewire/wheel.blade.php +++ b/resources/views/livewire/wheel.blade.php @@ -212,7 +212,7 @@ class="absolute p-2 rounded-full bg-gradient-to-b from-violet-800 to-violet-900 {{ $i + 1 }} {{ format()->maskUsername($h->member->username) }} - {{ $h->prize }} + {{ $h->prize_name }} @endforeach -- 2.50.0 From e7ce486136475b99d445b712a236199122ae10b1 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Mon, 29 Dec 2025 20:46:53 +0700 Subject: [PATCH 6/9] update --- app/Livewire/Wheel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php index bfb1dd7..e80fb06 100644 --- a/app/Livewire/Wheel.php +++ b/app/Livewire/Wheel.php @@ -103,7 +103,7 @@ public function redeem() } } while (!$final); - $ticket->prize_id = $this->prize->id; + $ticket->prize_name = $this->prize->prize; $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; $ticket->spin_time = date('Y-m-d H:i:s'); -- 2.50.0 From eff0c8c3abd02d1b4ca7051093968b3797dbcee5 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Mon, 29 Dec 2025 21:02:01 +0700 Subject: [PATCH 7/9] update --- app/Filament/Resources/TicketResource.php | 6 +++- app/Livewire/Wheel.php | 2 ++ ...9_205204_add_prize_id_to_tickets_table.php | 28 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php diff --git a/app/Filament/Resources/TicketResource.php b/app/Filament/Resources/TicketResource.php index 8534b13..08ab7e1 100644 --- a/app/Filament/Resources/TicketResource.php +++ b/app/Filament/Resources/TicketResource.php @@ -106,6 +106,10 @@ public static function table(Table $table): Table }) ->label(__('Win Type')), Tables\Columns\TextColumn::make('prize.prize') + ->label(__('Prize Set')) + ->sortable() + ->default('-'), + Tables\Columns\TextColumn::make('prize_name') ->label(__('Prize')) ->sortable() ->default('-'), @@ -203,4 +207,4 @@ public static function getModelLabel(): string { return __('Ticket'); } -} \ No newline at end of file +} diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php index e80fb06..1011564 100644 --- a/app/Livewire/Wheel.php +++ b/app/Livewire/Wheel.php @@ -79,6 +79,7 @@ public function redeem() $this->state = $ticket->prize->winable; $this->prize = $ticket->prize; $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; + $ticket->prize_name = $this->prize->prize; $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; $ticket->spin_time = date('Y-m-d H:i:s'); $ticket->save(); @@ -103,6 +104,7 @@ public function redeem() } } while (!$final); + $ticket->prize_id = $this->prize->id; $ticket->prize_name = $this->prize->prize; $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; diff --git a/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php b/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php new file mode 100644 index 0000000..6175276 --- /dev/null +++ b/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php @@ -0,0 +1,28 @@ +foreignId('prize_id')->nullable()->after('member_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('tickets', function (Blueprint $table) { + $table->dropColumn('prize_id'); + }); + } +}; -- 2.50.0 From 092010da3306f164cea7ab17b0585ee9cbd474a5 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Fri, 2 Jan 2026 09:31:30 +0700 Subject: [PATCH 8/9] update --- 0001-update.patch | 280 ------------- 0002-update.patch | 371 ----------------- all-changes.patch | 986 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 986 insertions(+), 651 deletions(-) delete mode 100644 0001-update.patch delete mode 100644 0002-update.patch create mode 100644 all-changes.patch diff --git a/0001-update.patch b/0001-update.patch deleted file mode 100644 index a7d1126..0000000 --- a/0001-update.patch +++ /dev/null @@ -1,280 +0,0 @@ -From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Sat, 27 Dec 2025 17:56:40 +0700 -Subject: [PATCH 1/2] update - ---- - app/Filament/Resources/PrizeResource.php | 21 +++-- - app/Livewire/Wheel.php | 12 ++- - app/Models/Prize.php | 4 +- - app/Policies/ContactPolicy.php | 108 +++++++++++++++++++++++ - resources/views/livewire/wheel.blade.php | 2 +- - 5 files changed, 129 insertions(+), 18 deletions(-) - create mode 100644 app/Policies/ContactPolicy.php - -diff --git a/app/Filament/Resources/PrizeResource.php b/app/Filament/Resources/PrizeResource.php -index 147c193..d4f5609 100644 ---- a/app/Filament/Resources/PrizeResource.php -+++ b/app/Filament/Resources/PrizeResource.php -@@ -22,7 +22,7 @@ class PrizeResource extends Resource - - public static function canCreate(): bool - { -- return false; -+ return true; - } - - public static function form(Form $form): Form -@@ -35,9 +35,10 @@ public static function form(Form $form): Form - ->maxLength(255), - Forms\Components\TextInput::make('sort') - ->label(__('Sort')) -- ->required() - ->numeric() -- ->disabledOn('edit'), -+ ->default(fn() => (Prize::max('sort') ?? 0) + 1) -+ ->readOnly() // tetap disimpan ke DB walau disabled -+ ->required(), - Forms\Components\TextInput::make('chance_percentage') - ->label(__('Chance Percentage')) - ->required() -@@ -63,12 +64,10 @@ public static function table(Table $table): Table - ->searchable(), - Tables\Columns\TextColumn::make('sort') - ->label(__('Sort')) -- ->numeric() -- ->sortable(), -+ ->numeric(), - Tables\Columns\TextColumn::make('chance_percentage') - ->label(__('Chance Percentage')) - ->numeric() -- ->sortable() - ->formatStateUsing(fn($state): string => $state . "%") - ->summarize([ - Sum::make()->formatStateUsing(fn($state): string => (round($state * 100)) / 100 . "%"), -@@ -76,7 +75,6 @@ public static function table(Table $table): Table - Tables\Columns\TextColumn::make('winable') - ->label(__('Status')) - ->badge() -- ->badge() - ->color(fn(string $state): string => match ($state) { - 'winable' => 'success', - 'unwinable' => 'danger', -@@ -85,20 +83,21 @@ public static function table(Table $table): Table - Tables\Columns\TextColumn::make('created_at') - ->label(__('Created At')) - ->dateTime() -- ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - Tables\Columns\TextColumn::make('updated_at') - ->label(__('Updated At')) - ->dateTime() -- ->sortable() - ->toggleable(isToggledHiddenByDefault: true), -- - ]) -+ ->defaultSort('sort') -+ ->reorderable('sort') -+ ->paginated(false) - ->filters([ - // - ]) - ->actions([ - Tables\Actions\EditAction::make(), -+ Tables\Actions\DeleteAction::make(), - ]) - ->bulkActions([]) - ->paginated(false);; -@@ -129,4 +128,4 @@ public static function getModelLabel(): string - { - return __('Prize'); - } --} -\ No newline at end of file -+} -diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php -index 602970c..bfb1dd7 100644 ---- a/app/Livewire/Wheel.php -+++ b/app/Livewire/Wheel.php -@@ -18,8 +18,12 @@ class Wheel extends Component - public $rotationFinal = 0; - public $state = 'ready'; - public $prize; -+ public $totalPrize = 0; - -- -+ public function mount() -+ { -+ $this->totalPrize = Prize::count(); -+ } - - public function redeem() - { -@@ -71,7 +75,7 @@ public function redeem() - $this->code = ""; - - if ($ticket->type == 'prize') { -- $total = (rand(7, 9) * 8) - 1 + $ticket->prize->sort; -+ $total = (rand(7, 9) * $this->totalPrize) - 1 + $ticket->prize->sort; - $this->state = $ticket->prize->winable; - $this->prize = $ticket->prize; - $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; -@@ -104,7 +108,7 @@ public function redeem() - $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; - $ticket->spin_time = date('Y-m-d H:i:s'); - $ticket->save(); -- $total = (rand(7, 9) * 8) - 1 + $final->sort; -+ $total = (rand(7, 9) * $this->totalPrize) - 1 + $final->sort; - $this->dispatch('spin', $total); - return; - } -@@ -114,4 +118,4 @@ public function render() - $history = Ticket::where('status', 'win')->orderBy('spin_time', 'desc')->take(10)->get(); - return view('livewire.wheel', compact('history')); - } --} -\ No newline at end of file -+} -diff --git a/app/Models/Prize.php b/app/Models/Prize.php -index 6d5071c..758b4b1 100644 ---- a/app/Models/Prize.php -+++ b/app/Models/Prize.php -@@ -6,5 +6,5 @@ - - class Prize extends Model - { -- protected $fillable = ['prize', 'chance_percentage', 'winable']; --} -\ No newline at end of file -+ protected $fillable = ['prize', 'chance_percentage', 'winable', 'sort']; -+} -diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php -new file mode 100644 -index 0000000..97aae7e ---- /dev/null -+++ b/app/Policies/ContactPolicy.php -@@ -0,0 +1,108 @@ -+can('view_any_contact'); -+ } -+ -+ /** -+ * Determine whether the user can view the model. -+ */ -+ public function view(User $user, Contact $contact): bool -+ { -+ return $user->can('view_contact'); -+ } -+ -+ /** -+ * Determine whether the user can create models. -+ */ -+ public function create(User $user): bool -+ { -+ return $user->can('create_contact'); -+ } -+ -+ /** -+ * Determine whether the user can update the model. -+ */ -+ public function update(User $user, Contact $contact): bool -+ { -+ return $user->can('update_contact'); -+ } -+ -+ /** -+ * Determine whether the user can delete the model. -+ */ -+ public function delete(User $user, Contact $contact): bool -+ { -+ return $user->can('delete_contact'); -+ } -+ -+ /** -+ * Determine whether the user can bulk delete. -+ */ -+ public function deleteAny(User $user): bool -+ { -+ return $user->can('delete_any_contact'); -+ } -+ -+ /** -+ * Determine whether the user can permanently delete. -+ */ -+ public function forceDelete(User $user, Contact $contact): bool -+ { -+ return $user->can('force_delete_contact'); -+ } -+ -+ /** -+ * Determine whether the user can permanently bulk delete. -+ */ -+ public function forceDeleteAny(User $user): bool -+ { -+ return $user->can('force_delete_any_contact'); -+ } -+ -+ /** -+ * Determine whether the user can restore. -+ */ -+ public function restore(User $user, Contact $contact): bool -+ { -+ return $user->can('restore_contact'); -+ } -+ -+ /** -+ * Determine whether the user can bulk restore. -+ */ -+ public function restoreAny(User $user): bool -+ { -+ return $user->can('restore_any_contact'); -+ } -+ -+ /** -+ * Determine whether the user can replicate. -+ */ -+ public function replicate(User $user, Contact $contact): bool -+ { -+ return $user->can('replicate_contact'); -+ } -+ -+ /** -+ * Determine whether the user can reorder. -+ */ -+ public function reorder(User $user): bool -+ { -+ return $user->can('reorder_contact'); -+ } -+} -diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php -index da3bdea..2f1806d 100644 ---- a/resources/views/livewire/wheel.blade.php -+++ b/resources/views/livewire/wheel.blade.php -@@ -29,7 +29,7 @@ - this.audio.currentTime = 0; // Mulai dari awal - this.audio.play(); // Mainkan suara - const rotations = total; -- const totalRotate = rotations * -45; -+ const totalRotate = rotations * -(360 / {{ $totalPrize }}); - this.shift = -totalRotate; - const duration = 8000; // 8 detik - const start = performance.now(); --- -2.50.0 - diff --git a/0002-update.patch b/0002-update.patch deleted file mode 100644 index 4f8729a..0000000 --- a/0002-update.patch +++ /dev/null @@ -1,371 +0,0 @@ -From ac0b01bfca7d081862cdf659466c2459a435a259 Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Sat, 27 Dec 2025 18:05:41 +0700 -Subject: [PATCH 2/2] update - ---- - 0001-update.patch | 280 ++++++++++++++++++ - ...ckets_replace_prize_id_with_prize_name.php | 64 ++++ - 2 files changed, 344 insertions(+) - create mode 100644 0001-update.patch - create mode 100644 database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php - -diff --git a/0001-update.patch b/0001-update.patch -new file mode 100644 -index 0000000..71e6e0b ---- /dev/null -+++ b/0001-update.patch -@@ -0,0 +1,280 @@ -+From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 -+From: Sinji Prasetio -+Date: Sat, 27 Dec 2025 17:56:40 +0700 -+Subject: [PATCH] update -+ -+--- -+ app/Filament/Resources/PrizeResource.php | 21 +++-- -+ app/Livewire/Wheel.php | 12 ++- -+ app/Models/Prize.php | 4 +- -+ app/Policies/ContactPolicy.php | 108 +++++++++++++++++++++++ -+ resources/views/livewire/wheel.blade.php | 2 +- -+ 5 files changed, 129 insertions(+), 18 deletions(-) -+ create mode 100644 app/Policies/ContactPolicy.php -+ -+diff --git a/app/Filament/Resources/PrizeResource.php b/app/Filament/Resources/PrizeResource.php -+index 147c193..d4f5609 100644 -+--- a/app/Filament/Resources/PrizeResource.php -++++ b/app/Filament/Resources/PrizeResource.php -+@@ -22,7 +22,7 @@ class PrizeResource extends Resource -+ -+ public static function canCreate(): bool -+ { -+- return false; -++ return true; -+ } -+ -+ public static function form(Form $form): Form -+@@ -35,9 +35,10 @@ public static function form(Form $form): Form -+ ->maxLength(255), -+ Forms\Components\TextInput::make('sort') -+ ->label(__('Sort')) -+- ->required() -+ ->numeric() -+- ->disabledOn('edit'), -++ ->default(fn() => (Prize::max('sort') ?? 0) + 1) -++ ->readOnly() // tetap disimpan ke DB walau disabled -++ ->required(), -+ Forms\Components\TextInput::make('chance_percentage') -+ ->label(__('Chance Percentage')) -+ ->required() -+@@ -63,12 +64,10 @@ public static function table(Table $table): Table -+ ->searchable(), -+ Tables\Columns\TextColumn::make('sort') -+ ->label(__('Sort')) -+- ->numeric() -+- ->sortable(), -++ ->numeric(), -+ Tables\Columns\TextColumn::make('chance_percentage') -+ ->label(__('Chance Percentage')) -+ ->numeric() -+- ->sortable() -+ ->formatStateUsing(fn($state): string => $state . "%") -+ ->summarize([ -+ Sum::make()->formatStateUsing(fn($state): string => (round($state * 100)) / 100 . "%"), -+@@ -76,7 +75,6 @@ public static function table(Table $table): Table -+ Tables\Columns\TextColumn::make('winable') -+ ->label(__('Status')) -+ ->badge() -+- ->badge() -+ ->color(fn(string $state): string => match ($state) { -+ 'winable' => 'success', -+ 'unwinable' => 'danger', -+@@ -85,20 +83,21 @@ public static function table(Table $table): Table -+ Tables\Columns\TextColumn::make('created_at') -+ ->label(__('Created At')) -+ ->dateTime() -+- ->sortable() -+ ->toggleable(isToggledHiddenByDefault: true), -+ Tables\Columns\TextColumn::make('updated_at') -+ ->label(__('Updated At')) -+ ->dateTime() -+- ->sortable() -+ ->toggleable(isToggledHiddenByDefault: true), -+- -+ ]) -++ ->defaultSort('sort') -++ ->reorderable('sort') -++ ->paginated(false) -+ ->filters([ -+ // -+ ]) -+ ->actions([ -+ Tables\Actions\EditAction::make(), -++ Tables\Actions\DeleteAction::make(), -+ ]) -+ ->bulkActions([]) -+ ->paginated(false);; -+@@ -129,4 +128,4 @@ public static function getModelLabel(): string -+ { -+ return __('Prize'); -+ } -+-} -+\ No newline at end of file -++} -+diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php -+index 602970c..bfb1dd7 100644 -+--- a/app/Livewire/Wheel.php -++++ b/app/Livewire/Wheel.php -+@@ -18,8 +18,12 @@ class Wheel extends Component -+ public $rotationFinal = 0; -+ public $state = 'ready'; -+ public $prize; -++ public $totalPrize = 0; -+ -+- -++ public function mount() -++ { -++ $this->totalPrize = Prize::count(); -++ } -+ -+ public function redeem() -+ { -+@@ -71,7 +75,7 @@ public function redeem() -+ $this->code = ""; -+ -+ if ($ticket->type == 'prize') { -+- $total = (rand(7, 9) * 8) - 1 + $ticket->prize->sort; -++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $ticket->prize->sort; -+ $this->state = $ticket->prize->winable; -+ $this->prize = $ticket->prize; -+ $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; -+@@ -104,7 +108,7 @@ public function redeem() -+ $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; -+ $ticket->spin_time = date('Y-m-d H:i:s'); -+ $ticket->save(); -+- $total = (rand(7, 9) * 8) - 1 + $final->sort; -++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $final->sort; -+ $this->dispatch('spin', $total); -+ return; -+ } -+@@ -114,4 +118,4 @@ public function render() -+ $history = Ticket::where('status', 'win')->orderBy('spin_time', 'desc')->take(10)->get(); -+ return view('livewire.wheel', compact('history')); -+ } -+-} -+\ No newline at end of file -++} -+diff --git a/app/Models/Prize.php b/app/Models/Prize.php -+index 6d5071c..758b4b1 100644 -+--- a/app/Models/Prize.php -++++ b/app/Models/Prize.php -+@@ -6,5 +6,5 @@ -+ -+ class Prize extends Model -+ { -+- protected $fillable = ['prize', 'chance_percentage', 'winable']; -+-} -+\ No newline at end of file -++ protected $fillable = ['prize', 'chance_percentage', 'winable', 'sort']; -++} -+diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php -+new file mode 100644 -+index 0000000..97aae7e -+--- /dev/null -++++ b/app/Policies/ContactPolicy.php -+@@ -0,0 +1,108 @@ -++can('view_any_contact'); -++ } -++ -++ /** -++ * Determine whether the user can view the model. -++ */ -++ public function view(User $user, Contact $contact): bool -++ { -++ return $user->can('view_contact'); -++ } -++ -++ /** -++ * Determine whether the user can create models. -++ */ -++ public function create(User $user): bool -++ { -++ return $user->can('create_contact'); -++ } -++ -++ /** -++ * Determine whether the user can update the model. -++ */ -++ public function update(User $user, Contact $contact): bool -++ { -++ return $user->can('update_contact'); -++ } -++ -++ /** -++ * Determine whether the user can delete the model. -++ */ -++ public function delete(User $user, Contact $contact): bool -++ { -++ return $user->can('delete_contact'); -++ } -++ -++ /** -++ * Determine whether the user can bulk delete. -++ */ -++ public function deleteAny(User $user): bool -++ { -++ return $user->can('delete_any_contact'); -++ } -++ -++ /** -++ * Determine whether the user can permanently delete. -++ */ -++ public function forceDelete(User $user, Contact $contact): bool -++ { -++ return $user->can('force_delete_contact'); -++ } -++ -++ /** -++ * Determine whether the user can permanently bulk delete. -++ */ -++ public function forceDeleteAny(User $user): bool -++ { -++ return $user->can('force_delete_any_contact'); -++ } -++ -++ /** -++ * Determine whether the user can restore. -++ */ -++ public function restore(User $user, Contact $contact): bool -++ { -++ return $user->can('restore_contact'); -++ } -++ -++ /** -++ * Determine whether the user can bulk restore. -++ */ -++ public function restoreAny(User $user): bool -++ { -++ return $user->can('restore_any_contact'); -++ } -++ -++ /** -++ * Determine whether the user can replicate. -++ */ -++ public function replicate(User $user, Contact $contact): bool -++ { -++ return $user->can('replicate_contact'); -++ } -++ -++ /** -++ * Determine whether the user can reorder. -++ */ -++ public function reorder(User $user): bool -++ { -++ return $user->can('reorder_contact'); -++ } -++} -+diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php -+index da3bdea..2f1806d 100644 -+--- a/resources/views/livewire/wheel.blade.php -++++ b/resources/views/livewire/wheel.blade.php -+@@ -29,7 +29,7 @@ -+ this.audio.currentTime = 0; // Mulai dari awal -+ this.audio.play(); // Mainkan suara -+ const rotations = total; -+- const totalRotate = rotations * -45; -++ const totalRotate = rotations * -(360 / {{ $totalPrize }}); -+ this.shift = -totalRotate; -+ const duration = 8000; // 8 detik -+ const start = performance.now(); -+-- -+2.50.0 -+ -diff --git a/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php -new file mode 100644 -index 0000000..938d967 ---- /dev/null -+++ b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php -@@ -0,0 +1,64 @@ -+string('prize_name')->nullable()->after('member_id'); -+ }); -+ -+ // 2. Copy data dari prizes.prize ke tickets.prize_name -+ // (aman walau ada prize_id null) -+ DB::statement(" -+ UPDATE tickets t -+ LEFT JOIN prizes p ON p.id = t.prize_id -+ SET t.prize_name = p.prize -+ WHERE t.prize_id IS NOT NULL -+ "); -+ -+ // 3. Drop foreign key + kolom prize_id -+ Schema::table('tickets', function (Blueprint $table) { -+ // default Laravel FK name -+ $table->dropForeign(['prize_id']); -+ $table->dropColumn('prize_id'); -+ }); -+ } -+ -+ /** -+ * Reverse the migrations. -+ */ -+ public function down(): void -+ { -+ // 1. Balikin kolom prize_id -+ Schema::table('tickets', function (Blueprint $table) { -+ $table->foreignId('prize_id') -+ ->nullable() -+ ->after('member_id') -+ ->constrained() -+ ->onDelete('cascade'); -+ }); -+ -+ // 2. Isi ulang prize_id dari prize_name (kalau nama masih sama) -+ DB::statement(" -+ UPDATE tickets t -+ JOIN prizes p ON p.prize = t.prize_name -+ SET t.prize_id = p.id -+ WHERE t.prize_name IS NOT NULL -+ "); -+ -+ // 3. Hapus snapshot -+ Schema::table('tickets', function (Blueprint $table) { -+ $table->dropColumn('prize_name'); -+ }); -+ } -+}; --- -2.50.0 - diff --git a/all-changes.patch b/all-changes.patch new file mode 100644 index 0000000..fbfe2cb --- /dev/null +++ b/all-changes.patch @@ -0,0 +1,986 @@ +From ac0b01bfca7d081862cdf659466c2459a435a259 Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Sat, 27 Dec 2025 18:05:41 +0700 +Subject: [PATCH 1/7] update + +--- + 0001-update.patch | 280 ++++++++++++++++++ + ...ckets_replace_prize_id_with_prize_name.php | 64 ++++ + 2 files changed, 344 insertions(+) + create mode 100644 0001-update.patch + create mode 100644 database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php + +diff --git a/0001-update.patch b/0001-update.patch +new file mode 100644 +index 0000000..71e6e0b +--- /dev/null ++++ b/0001-update.patch +@@ -0,0 +1,280 @@ ++From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 ++From: Sinji Prasetio ++Date: Sat, 27 Dec 2025 17:56:40 +0700 ++Subject: [PATCH] update ++ ++--- ++ app/Filament/Resources/PrizeResource.php | 21 +++-- ++ app/Livewire/Wheel.php | 12 ++- ++ app/Models/Prize.php | 4 +- ++ app/Policies/ContactPolicy.php | 108 +++++++++++++++++++++++ ++ resources/views/livewire/wheel.blade.php | 2 +- ++ 5 files changed, 129 insertions(+), 18 deletions(-) ++ create mode 100644 app/Policies/ContactPolicy.php ++ ++diff --git a/app/Filament/Resources/PrizeResource.php b/app/Filament/Resources/PrizeResource.php ++index 147c193..d4f5609 100644 ++--- a/app/Filament/Resources/PrizeResource.php +++++ b/app/Filament/Resources/PrizeResource.php ++@@ -22,7 +22,7 @@ class PrizeResource extends Resource ++ ++ public static function canCreate(): bool ++ { ++- return false; +++ return true; ++ } ++ ++ public static function form(Form $form): Form ++@@ -35,9 +35,10 @@ public static function form(Form $form): Form ++ ->maxLength(255), ++ Forms\Components\TextInput::make('sort') ++ ->label(__('Sort')) ++- ->required() ++ ->numeric() ++- ->disabledOn('edit'), +++ ->default(fn() => (Prize::max('sort') ?? 0) + 1) +++ ->readOnly() // tetap disimpan ke DB walau disabled +++ ->required(), ++ Forms\Components\TextInput::make('chance_percentage') ++ ->label(__('Chance Percentage')) ++ ->required() ++@@ -63,12 +64,10 @@ public static function table(Table $table): Table ++ ->searchable(), ++ Tables\Columns\TextColumn::make('sort') ++ ->label(__('Sort')) ++- ->numeric() ++- ->sortable(), +++ ->numeric(), ++ Tables\Columns\TextColumn::make('chance_percentage') ++ ->label(__('Chance Percentage')) ++ ->numeric() ++- ->sortable() ++ ->formatStateUsing(fn($state): string => $state . "%") ++ ->summarize([ ++ Sum::make()->formatStateUsing(fn($state): string => (round($state * 100)) / 100 . "%"), ++@@ -76,7 +75,6 @@ public static function table(Table $table): Table ++ Tables\Columns\TextColumn::make('winable') ++ ->label(__('Status')) ++ ->badge() ++- ->badge() ++ ->color(fn(string $state): string => match ($state) { ++ 'winable' => 'success', ++ 'unwinable' => 'danger', ++@@ -85,20 +83,21 @@ public static function table(Table $table): Table ++ Tables\Columns\TextColumn::make('created_at') ++ ->label(__('Created At')) ++ ->dateTime() ++- ->sortable() ++ ->toggleable(isToggledHiddenByDefault: true), ++ Tables\Columns\TextColumn::make('updated_at') ++ ->label(__('Updated At')) ++ ->dateTime() ++- ->sortable() ++ ->toggleable(isToggledHiddenByDefault: true), ++- ++ ]) +++ ->defaultSort('sort') +++ ->reorderable('sort') +++ ->paginated(false) ++ ->filters([ ++ // ++ ]) ++ ->actions([ ++ Tables\Actions\EditAction::make(), +++ Tables\Actions\DeleteAction::make(), ++ ]) ++ ->bulkActions([]) ++ ->paginated(false);; ++@@ -129,4 +128,4 @@ public static function getModelLabel(): string ++ { ++ return __('Prize'); ++ } ++-} ++\ No newline at end of file +++} ++diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php ++index 602970c..bfb1dd7 100644 ++--- a/app/Livewire/Wheel.php +++++ b/app/Livewire/Wheel.php ++@@ -18,8 +18,12 @@ class Wheel extends Component ++ public $rotationFinal = 0; ++ public $state = 'ready'; ++ public $prize; +++ public $totalPrize = 0; ++ ++- +++ public function mount() +++ { +++ $this->totalPrize = Prize::count(); +++ } ++ ++ public function redeem() ++ { ++@@ -71,7 +75,7 @@ public function redeem() ++ $this->code = ""; ++ ++ if ($ticket->type == 'prize') { ++- $total = (rand(7, 9) * 8) - 1 + $ticket->prize->sort; +++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $ticket->prize->sort; ++ $this->state = $ticket->prize->winable; ++ $this->prize = $ticket->prize; ++ $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; ++@@ -104,7 +108,7 @@ public function redeem() ++ $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; ++ $ticket->spin_time = date('Y-m-d H:i:s'); ++ $ticket->save(); ++- $total = (rand(7, 9) * 8) - 1 + $final->sort; +++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $final->sort; ++ $this->dispatch('spin', $total); ++ return; ++ } ++@@ -114,4 +118,4 @@ public function render() ++ $history = Ticket::where('status', 'win')->orderBy('spin_time', 'desc')->take(10)->get(); ++ return view('livewire.wheel', compact('history')); ++ } ++-} ++\ No newline at end of file +++} ++diff --git a/app/Models/Prize.php b/app/Models/Prize.php ++index 6d5071c..758b4b1 100644 ++--- a/app/Models/Prize.php +++++ b/app/Models/Prize.php ++@@ -6,5 +6,5 @@ ++ ++ class Prize extends Model ++ { ++- protected $fillable = ['prize', 'chance_percentage', 'winable']; ++-} ++\ No newline at end of file +++ protected $fillable = ['prize', 'chance_percentage', 'winable', 'sort']; +++} ++diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php ++new file mode 100644 ++index 0000000..97aae7e ++--- /dev/null +++++ b/app/Policies/ContactPolicy.php ++@@ -0,0 +1,108 @@ +++can('view_any_contact'); +++ } +++ +++ /** +++ * Determine whether the user can view the model. +++ */ +++ public function view(User $user, Contact $contact): bool +++ { +++ return $user->can('view_contact'); +++ } +++ +++ /** +++ * Determine whether the user can create models. +++ */ +++ public function create(User $user): bool +++ { +++ return $user->can('create_contact'); +++ } +++ +++ /** +++ * Determine whether the user can update the model. +++ */ +++ public function update(User $user, Contact $contact): bool +++ { +++ return $user->can('update_contact'); +++ } +++ +++ /** +++ * Determine whether the user can delete the model. +++ */ +++ public function delete(User $user, Contact $contact): bool +++ { +++ return $user->can('delete_contact'); +++ } +++ +++ /** +++ * Determine whether the user can bulk delete. +++ */ +++ public function deleteAny(User $user): bool +++ { +++ return $user->can('delete_any_contact'); +++ } +++ +++ /** +++ * Determine whether the user can permanently delete. +++ */ +++ public function forceDelete(User $user, Contact $contact): bool +++ { +++ return $user->can('force_delete_contact'); +++ } +++ +++ /** +++ * Determine whether the user can permanently bulk delete. +++ */ +++ public function forceDeleteAny(User $user): bool +++ { +++ return $user->can('force_delete_any_contact'); +++ } +++ +++ /** +++ * Determine whether the user can restore. +++ */ +++ public function restore(User $user, Contact $contact): bool +++ { +++ return $user->can('restore_contact'); +++ } +++ +++ /** +++ * Determine whether the user can bulk restore. +++ */ +++ public function restoreAny(User $user): bool +++ { +++ return $user->can('restore_any_contact'); +++ } +++ +++ /** +++ * Determine whether the user can replicate. +++ */ +++ public function replicate(User $user, Contact $contact): bool +++ { +++ return $user->can('replicate_contact'); +++ } +++ +++ /** +++ * Determine whether the user can reorder. +++ */ +++ public function reorder(User $user): bool +++ { +++ return $user->can('reorder_contact'); +++ } +++} ++diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php ++index da3bdea..2f1806d 100644 ++--- a/resources/views/livewire/wheel.blade.php +++++ b/resources/views/livewire/wheel.blade.php ++@@ -29,7 +29,7 @@ ++ this.audio.currentTime = 0; // Mulai dari awal ++ this.audio.play(); // Mainkan suara ++ const rotations = total; ++- const totalRotate = rotations * -45; +++ const totalRotate = rotations * -(360 / {{ $totalPrize }}); ++ this.shift = -totalRotate; ++ const duration = 8000; // 8 detik ++ const start = performance.now(); ++-- ++2.50.0 ++ +diff --git a/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php +new file mode 100644 +index 0000000..938d967 +--- /dev/null ++++ b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php +@@ -0,0 +1,64 @@ ++string('prize_name')->nullable()->after('member_id'); ++ }); ++ ++ // 2. Copy data dari prizes.prize ke tickets.prize_name ++ // (aman walau ada prize_id null) ++ DB::statement(" ++ UPDATE tickets t ++ LEFT JOIN prizes p ON p.id = t.prize_id ++ SET t.prize_name = p.prize ++ WHERE t.prize_id IS NOT NULL ++ "); ++ ++ // 3. Drop foreign key + kolom prize_id ++ Schema::table('tickets', function (Blueprint $table) { ++ // default Laravel FK name ++ $table->dropForeign(['prize_id']); ++ $table->dropColumn('prize_id'); ++ }); ++ } ++ ++ /** ++ * Reverse the migrations. ++ */ ++ public function down(): void ++ { ++ // 1. Balikin kolom prize_id ++ Schema::table('tickets', function (Blueprint $table) { ++ $table->foreignId('prize_id') ++ ->nullable() ++ ->after('member_id') ++ ->constrained() ++ ->onDelete('cascade'); ++ }); ++ ++ // 2. Isi ulang prize_id dari prize_name (kalau nama masih sama) ++ DB::statement(" ++ UPDATE tickets t ++ JOIN prizes p ON p.prize = t.prize_name ++ SET t.prize_id = p.id ++ WHERE t.prize_name IS NOT NULL ++ "); ++ ++ // 3. Hapus snapshot ++ Schema::table('tickets', function (Blueprint $table) { ++ $table->dropColumn('prize_name'); ++ }); ++ } ++}; +-- +2.50.0 + + +From b6e1f41b06c016b2560a510248b03840bca73603 Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Sat, 27 Dec 2025 18:07:15 +0700 +Subject: [PATCH 2/7] update + +--- + 0001-update.patch | 2 +- + 0002-update.patch | 371 ++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 372 insertions(+), 1 deletion(-) + create mode 100644 0002-update.patch + +diff --git a/0001-update.patch b/0001-update.patch +index 71e6e0b..a7d1126 100644 +--- a/0001-update.patch ++++ b/0001-update.patch +@@ -1,7 +1,7 @@ + From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 + From: Sinji Prasetio + Date: Sat, 27 Dec 2025 17:56:40 +0700 +-Subject: [PATCH] update ++Subject: [PATCH 1/2] update + + --- + app/Filament/Resources/PrizeResource.php | 21 +++-- +diff --git a/0002-update.patch b/0002-update.patch +new file mode 100644 +index 0000000..4f8729a +--- /dev/null ++++ b/0002-update.patch +@@ -0,0 +1,371 @@ ++From ac0b01bfca7d081862cdf659466c2459a435a259 Mon Sep 17 00:00:00 2001 ++From: Sinji Prasetio ++Date: Sat, 27 Dec 2025 18:05:41 +0700 ++Subject: [PATCH 2/2] update ++ ++--- ++ 0001-update.patch | 280 ++++++++++++++++++ ++ ...ckets_replace_prize_id_with_prize_name.php | 64 ++++ ++ 2 files changed, 344 insertions(+) ++ create mode 100644 0001-update.patch ++ create mode 100644 database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php ++ ++diff --git a/0001-update.patch b/0001-update.patch ++new file mode 100644 ++index 0000000..71e6e0b ++--- /dev/null +++++ b/0001-update.patch ++@@ -0,0 +1,280 @@ +++From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 +++From: Sinji Prasetio +++Date: Sat, 27 Dec 2025 17:56:40 +0700 +++Subject: [PATCH] update +++ +++--- +++ app/Filament/Resources/PrizeResource.php | 21 +++-- +++ app/Livewire/Wheel.php | 12 ++- +++ app/Models/Prize.php | 4 +- +++ app/Policies/ContactPolicy.php | 108 +++++++++++++++++++++++ +++ resources/views/livewire/wheel.blade.php | 2 +- +++ 5 files changed, 129 insertions(+), 18 deletions(-) +++ create mode 100644 app/Policies/ContactPolicy.php +++ +++diff --git a/app/Filament/Resources/PrizeResource.php b/app/Filament/Resources/PrizeResource.php +++index 147c193..d4f5609 100644 +++--- a/app/Filament/Resources/PrizeResource.php ++++++ b/app/Filament/Resources/PrizeResource.php +++@@ -22,7 +22,7 @@ class PrizeResource extends Resource +++ +++ public static function canCreate(): bool +++ { +++- return false; ++++ return true; +++ } +++ +++ public static function form(Form $form): Form +++@@ -35,9 +35,10 @@ public static function form(Form $form): Form +++ ->maxLength(255), +++ Forms\Components\TextInput::make('sort') +++ ->label(__('Sort')) +++- ->required() +++ ->numeric() +++- ->disabledOn('edit'), ++++ ->default(fn() => (Prize::max('sort') ?? 0) + 1) ++++ ->readOnly() // tetap disimpan ke DB walau disabled ++++ ->required(), +++ Forms\Components\TextInput::make('chance_percentage') +++ ->label(__('Chance Percentage')) +++ ->required() +++@@ -63,12 +64,10 @@ public static function table(Table $table): Table +++ ->searchable(), +++ Tables\Columns\TextColumn::make('sort') +++ ->label(__('Sort')) +++- ->numeric() +++- ->sortable(), ++++ ->numeric(), +++ Tables\Columns\TextColumn::make('chance_percentage') +++ ->label(__('Chance Percentage')) +++ ->numeric() +++- ->sortable() +++ ->formatStateUsing(fn($state): string => $state . "%") +++ ->summarize([ +++ Sum::make()->formatStateUsing(fn($state): string => (round($state * 100)) / 100 . "%"), +++@@ -76,7 +75,6 @@ public static function table(Table $table): Table +++ Tables\Columns\TextColumn::make('winable') +++ ->label(__('Status')) +++ ->badge() +++- ->badge() +++ ->color(fn(string $state): string => match ($state) { +++ 'winable' => 'success', +++ 'unwinable' => 'danger', +++@@ -85,20 +83,21 @@ public static function table(Table $table): Table +++ Tables\Columns\TextColumn::make('created_at') +++ ->label(__('Created At')) +++ ->dateTime() +++- ->sortable() +++ ->toggleable(isToggledHiddenByDefault: true), +++ Tables\Columns\TextColumn::make('updated_at') +++ ->label(__('Updated At')) +++ ->dateTime() +++- ->sortable() +++ ->toggleable(isToggledHiddenByDefault: true), +++- +++ ]) ++++ ->defaultSort('sort') ++++ ->reorderable('sort') ++++ ->paginated(false) +++ ->filters([ +++ // +++ ]) +++ ->actions([ +++ Tables\Actions\EditAction::make(), ++++ Tables\Actions\DeleteAction::make(), +++ ]) +++ ->bulkActions([]) +++ ->paginated(false);; +++@@ -129,4 +128,4 @@ public static function getModelLabel(): string +++ { +++ return __('Prize'); +++ } +++-} +++\ No newline at end of file ++++} +++diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php +++index 602970c..bfb1dd7 100644 +++--- a/app/Livewire/Wheel.php ++++++ b/app/Livewire/Wheel.php +++@@ -18,8 +18,12 @@ class Wheel extends Component +++ public $rotationFinal = 0; +++ public $state = 'ready'; +++ public $prize; ++++ public $totalPrize = 0; +++ +++- ++++ public function mount() ++++ { ++++ $this->totalPrize = Prize::count(); ++++ } +++ +++ public function redeem() +++ { +++@@ -71,7 +75,7 @@ public function redeem() +++ $this->code = ""; +++ +++ if ($ticket->type == 'prize') { +++- $total = (rand(7, 9) * 8) - 1 + $ticket->prize->sort; ++++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $ticket->prize->sort; +++ $this->state = $ticket->prize->winable; +++ $this->prize = $ticket->prize; +++ $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; +++@@ -104,7 +108,7 @@ public function redeem() +++ $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; +++ $ticket->spin_time = date('Y-m-d H:i:s'); +++ $ticket->save(); +++- $total = (rand(7, 9) * 8) - 1 + $final->sort; ++++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $final->sort; +++ $this->dispatch('spin', $total); +++ return; +++ } +++@@ -114,4 +118,4 @@ public function render() +++ $history = Ticket::where('status', 'win')->orderBy('spin_time', 'desc')->take(10)->get(); +++ return view('livewire.wheel', compact('history')); +++ } +++-} +++\ No newline at end of file ++++} +++diff --git a/app/Models/Prize.php b/app/Models/Prize.php +++index 6d5071c..758b4b1 100644 +++--- a/app/Models/Prize.php ++++++ b/app/Models/Prize.php +++@@ -6,5 +6,5 @@ +++ +++ class Prize extends Model +++ { +++- protected $fillable = ['prize', 'chance_percentage', 'winable']; +++-} +++\ No newline at end of file ++++ protected $fillable = ['prize', 'chance_percentage', 'winable', 'sort']; ++++} +++diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php +++new file mode 100644 +++index 0000000..97aae7e +++--- /dev/null ++++++ b/app/Policies/ContactPolicy.php +++@@ -0,0 +1,108 @@ ++++can('view_any_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can view the model. ++++ */ ++++ public function view(User $user, Contact $contact): bool ++++ { ++++ return $user->can('view_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can create models. ++++ */ ++++ public function create(User $user): bool ++++ { ++++ return $user->can('create_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can update the model. ++++ */ ++++ public function update(User $user, Contact $contact): bool ++++ { ++++ return $user->can('update_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can delete the model. ++++ */ ++++ public function delete(User $user, Contact $contact): bool ++++ { ++++ return $user->can('delete_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can bulk delete. ++++ */ ++++ public function deleteAny(User $user): bool ++++ { ++++ return $user->can('delete_any_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can permanently delete. ++++ */ ++++ public function forceDelete(User $user, Contact $contact): bool ++++ { ++++ return $user->can('force_delete_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can permanently bulk delete. ++++ */ ++++ public function forceDeleteAny(User $user): bool ++++ { ++++ return $user->can('force_delete_any_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can restore. ++++ */ ++++ public function restore(User $user, Contact $contact): bool ++++ { ++++ return $user->can('restore_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can bulk restore. ++++ */ ++++ public function restoreAny(User $user): bool ++++ { ++++ return $user->can('restore_any_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can replicate. ++++ */ ++++ public function replicate(User $user, Contact $contact): bool ++++ { ++++ return $user->can('replicate_contact'); ++++ } ++++ ++++ /** ++++ * Determine whether the user can reorder. ++++ */ ++++ public function reorder(User $user): bool ++++ { ++++ return $user->can('reorder_contact'); ++++ } ++++} +++diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php +++index da3bdea..2f1806d 100644 +++--- a/resources/views/livewire/wheel.blade.php ++++++ b/resources/views/livewire/wheel.blade.php +++@@ -29,7 +29,7 @@ +++ this.audio.currentTime = 0; // Mulai dari awal +++ this.audio.play(); // Mainkan suara +++ const rotations = total; +++- const totalRotate = rotations * -45; ++++ const totalRotate = rotations * -(360 / {{ $totalPrize }}); +++ this.shift = -totalRotate; +++ const duration = 8000; // 8 detik +++ const start = performance.now(); +++-- +++2.50.0 +++ ++diff --git a/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php ++new file mode 100644 ++index 0000000..938d967 ++--- /dev/null +++++ b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php ++@@ -0,0 +1,64 @@ +++string('prize_name')->nullable()->after('member_id'); +++ }); +++ +++ // 2. Copy data dari prizes.prize ke tickets.prize_name +++ // (aman walau ada prize_id null) +++ DB::statement(" +++ UPDATE tickets t +++ LEFT JOIN prizes p ON p.id = t.prize_id +++ SET t.prize_name = p.prize +++ WHERE t.prize_id IS NOT NULL +++ "); +++ +++ // 3. Drop foreign key + kolom prize_id +++ Schema::table('tickets', function (Blueprint $table) { +++ // default Laravel FK name +++ $table->dropForeign(['prize_id']); +++ $table->dropColumn('prize_id'); +++ }); +++ } +++ +++ /** +++ * Reverse the migrations. +++ */ +++ public function down(): void +++ { +++ // 1. Balikin kolom prize_id +++ Schema::table('tickets', function (Blueprint $table) { +++ $table->foreignId('prize_id') +++ ->nullable() +++ ->after('member_id') +++ ->constrained() +++ ->onDelete('cascade'); +++ }); +++ +++ // 2. Isi ulang prize_id dari prize_name (kalau nama masih sama) +++ DB::statement(" +++ UPDATE tickets t +++ JOIN prizes p ON p.prize = t.prize_name +++ SET t.prize_id = p.id +++ WHERE t.prize_name IS NOT NULL +++ "); +++ +++ // 3. Hapus snapshot +++ Schema::table('tickets', function (Blueprint $table) { +++ $table->dropColumn('prize_name'); +++ }); +++ } +++}; ++-- ++2.50.0 ++ +-- +2.50.0 + + +From 085158048211008965f9839ac0e5c6a71b6e4906 Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Sun, 28 Dec 2025 21:40:18 +0700 +Subject: [PATCH 3/7] update + +--- + app/Livewire/Wheel.php | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php +index bfb1dd7..4f82c71 100644 +--- a/app/Livewire/Wheel.php ++++ b/app/Livewire/Wheel.php +@@ -17,7 +17,7 @@ class Wheel extends Component + public $code; + public $rotationFinal = 0; + public $state = 'ready'; +- public $prize; ++ public $prize = ''; + public $totalPrize = 0; + + public function mount() +-- +2.50.0 + + +From 1d3c9eb95337d0a095c841d702e6efc2fe666854 Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Sun, 28 Dec 2025 21:44:19 +0700 +Subject: [PATCH 4/7] update + +--- + app/Livewire/Wheel.php | 2 +- + resources/views/livewire/wheel.blade.php | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php +index 4f82c71..bfb1dd7 100644 +--- a/app/Livewire/Wheel.php ++++ b/app/Livewire/Wheel.php +@@ -17,7 +17,7 @@ class Wheel extends Component + public $code; + public $rotationFinal = 0; + public $state = 'ready'; +- public $prize = ''; ++ public $prize; + public $totalPrize = 0; + + public function mount() +diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php +index 2f1806d..6be22cb 100644 +--- a/resources/views/livewire/wheel.blade.php ++++ b/resources/views/livewire/wheel.blade.php +@@ -212,7 +212,7 @@ class="absolute p-2 rounded-full bg-gradient-to-b from-violet-800 to-violet-900 + + {{ $i + 1 }} + {{ format()->maskUsername($h->member->username) }} +- {{ $h->prize->prize }} ++ {{ $h->prize }} + + @endforeach + +-- +2.50.0 + + +From c31e4d00e2edbb1a56c80dd1222df99e169e8588 Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Mon, 29 Dec 2025 20:43:47 +0700 +Subject: [PATCH 5/7] update + +--- + resources/views/livewire/wheel.blade.php | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php +index 6be22cb..df4debd 100644 +--- a/resources/views/livewire/wheel.blade.php ++++ b/resources/views/livewire/wheel.blade.php +@@ -212,7 +212,7 @@ class="absolute p-2 rounded-full bg-gradient-to-b from-violet-800 to-violet-900 + + {{ $i + 1 }} + {{ format()->maskUsername($h->member->username) }} +- {{ $h->prize }} ++ {{ $h->prize_name }} + + @endforeach + +-- +2.50.0 + + +From e7ce486136475b99d445b712a236199122ae10b1 Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Mon, 29 Dec 2025 20:46:53 +0700 +Subject: [PATCH 6/7] update + +--- + app/Livewire/Wheel.php | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php +index bfb1dd7..e80fb06 100644 +--- a/app/Livewire/Wheel.php ++++ b/app/Livewire/Wheel.php +@@ -103,7 +103,7 @@ public function redeem() + } + } while (!$final); + +- $ticket->prize_id = $this->prize->id; ++ $ticket->prize_name = $this->prize->prize; + $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; + $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; + $ticket->spin_time = date('Y-m-d H:i:s'); +-- +2.50.0 + + +From eff0c8c3abd02d1b4ca7051093968b3797dbcee5 Mon Sep 17 00:00:00 2001 +From: Sinji Prasetio +Date: Mon, 29 Dec 2025 21:02:01 +0700 +Subject: [PATCH 7/7] update + +--- + app/Filament/Resources/TicketResource.php | 6 +++- + app/Livewire/Wheel.php | 2 ++ + ...9_205204_add_prize_id_to_tickets_table.php | 28 +++++++++++++++++++ + 3 files changed, 35 insertions(+), 1 deletion(-) + create mode 100644 database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php + +diff --git a/app/Filament/Resources/TicketResource.php b/app/Filament/Resources/TicketResource.php +index 8534b13..08ab7e1 100644 +--- a/app/Filament/Resources/TicketResource.php ++++ b/app/Filament/Resources/TicketResource.php +@@ -106,6 +106,10 @@ public static function table(Table $table): Table + }) + ->label(__('Win Type')), + Tables\Columns\TextColumn::make('prize.prize') ++ ->label(__('Prize Set')) ++ ->sortable() ++ ->default('-'), ++ Tables\Columns\TextColumn::make('prize_name') + ->label(__('Prize')) + ->sortable() + ->default('-'), +@@ -203,4 +207,4 @@ public static function getModelLabel(): string + { + return __('Ticket'); + } +-} +\ No newline at end of file ++} +diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php +index e80fb06..1011564 100644 +--- a/app/Livewire/Wheel.php ++++ b/app/Livewire/Wheel.php +@@ -79,6 +79,7 @@ public function redeem() + $this->state = $ticket->prize->winable; + $this->prize = $ticket->prize; + $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; ++ $ticket->prize_name = $this->prize->prize; + $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; + $ticket->spin_time = date('Y-m-d H:i:s'); + $ticket->save(); +@@ -103,6 +104,7 @@ public function redeem() + } + } while (!$final); + ++ $ticket->prize_id = $this->prize->id; + $ticket->prize_name = $this->prize->prize; + $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; + $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; +diff --git a/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php b/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php +new file mode 100644 +index 0000000..6175276 +--- /dev/null ++++ b/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php +@@ -0,0 +1,28 @@ ++foreignId('prize_id')->nullable()->after('member_id'); ++ }); ++ } ++ ++ /** ++ * Reverse the migrations. ++ */ ++ public function down(): void ++ { ++ Schema::table('tickets', function (Blueprint $table) { ++ $table->dropColumn('prize_id'); ++ }); ++ } ++}; +-- +2.50.0 + -- 2.50.0 From 1a853651baf8c12b7aca27ddae54e1550bc6a105 Mon Sep 17 00:00:00 2001 From: Sinji Prasetio Date: Fri, 2 Jan 2026 09:31:48 +0700 Subject: [PATCH 9/9] update --- all-changes.patch | 986 ---------------------------------------------- 1 file changed, 986 deletions(-) delete mode 100644 all-changes.patch diff --git a/all-changes.patch b/all-changes.patch deleted file mode 100644 index fbfe2cb..0000000 --- a/all-changes.patch +++ /dev/null @@ -1,986 +0,0 @@ -From ac0b01bfca7d081862cdf659466c2459a435a259 Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Sat, 27 Dec 2025 18:05:41 +0700 -Subject: [PATCH 1/7] update - ---- - 0001-update.patch | 280 ++++++++++++++++++ - ...ckets_replace_prize_id_with_prize_name.php | 64 ++++ - 2 files changed, 344 insertions(+) - create mode 100644 0001-update.patch - create mode 100644 database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php - -diff --git a/0001-update.patch b/0001-update.patch -new file mode 100644 -index 0000000..71e6e0b ---- /dev/null -+++ b/0001-update.patch -@@ -0,0 +1,280 @@ -+From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 -+From: Sinji Prasetio -+Date: Sat, 27 Dec 2025 17:56:40 +0700 -+Subject: [PATCH] update -+ -+--- -+ app/Filament/Resources/PrizeResource.php | 21 +++-- -+ app/Livewire/Wheel.php | 12 ++- -+ app/Models/Prize.php | 4 +- -+ app/Policies/ContactPolicy.php | 108 +++++++++++++++++++++++ -+ resources/views/livewire/wheel.blade.php | 2 +- -+ 5 files changed, 129 insertions(+), 18 deletions(-) -+ create mode 100644 app/Policies/ContactPolicy.php -+ -+diff --git a/app/Filament/Resources/PrizeResource.php b/app/Filament/Resources/PrizeResource.php -+index 147c193..d4f5609 100644 -+--- a/app/Filament/Resources/PrizeResource.php -++++ b/app/Filament/Resources/PrizeResource.php -+@@ -22,7 +22,7 @@ class PrizeResource extends Resource -+ -+ public static function canCreate(): bool -+ { -+- return false; -++ return true; -+ } -+ -+ public static function form(Form $form): Form -+@@ -35,9 +35,10 @@ public static function form(Form $form): Form -+ ->maxLength(255), -+ Forms\Components\TextInput::make('sort') -+ ->label(__('Sort')) -+- ->required() -+ ->numeric() -+- ->disabledOn('edit'), -++ ->default(fn() => (Prize::max('sort') ?? 0) + 1) -++ ->readOnly() // tetap disimpan ke DB walau disabled -++ ->required(), -+ Forms\Components\TextInput::make('chance_percentage') -+ ->label(__('Chance Percentage')) -+ ->required() -+@@ -63,12 +64,10 @@ public static function table(Table $table): Table -+ ->searchable(), -+ Tables\Columns\TextColumn::make('sort') -+ ->label(__('Sort')) -+- ->numeric() -+- ->sortable(), -++ ->numeric(), -+ Tables\Columns\TextColumn::make('chance_percentage') -+ ->label(__('Chance Percentage')) -+ ->numeric() -+- ->sortable() -+ ->formatStateUsing(fn($state): string => $state . "%") -+ ->summarize([ -+ Sum::make()->formatStateUsing(fn($state): string => (round($state * 100)) / 100 . "%"), -+@@ -76,7 +75,6 @@ public static function table(Table $table): Table -+ Tables\Columns\TextColumn::make('winable') -+ ->label(__('Status')) -+ ->badge() -+- ->badge() -+ ->color(fn(string $state): string => match ($state) { -+ 'winable' => 'success', -+ 'unwinable' => 'danger', -+@@ -85,20 +83,21 @@ public static function table(Table $table): Table -+ Tables\Columns\TextColumn::make('created_at') -+ ->label(__('Created At')) -+ ->dateTime() -+- ->sortable() -+ ->toggleable(isToggledHiddenByDefault: true), -+ Tables\Columns\TextColumn::make('updated_at') -+ ->label(__('Updated At')) -+ ->dateTime() -+- ->sortable() -+ ->toggleable(isToggledHiddenByDefault: true), -+- -+ ]) -++ ->defaultSort('sort') -++ ->reorderable('sort') -++ ->paginated(false) -+ ->filters([ -+ // -+ ]) -+ ->actions([ -+ Tables\Actions\EditAction::make(), -++ Tables\Actions\DeleteAction::make(), -+ ]) -+ ->bulkActions([]) -+ ->paginated(false);; -+@@ -129,4 +128,4 @@ public static function getModelLabel(): string -+ { -+ return __('Prize'); -+ } -+-} -+\ No newline at end of file -++} -+diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php -+index 602970c..bfb1dd7 100644 -+--- a/app/Livewire/Wheel.php -++++ b/app/Livewire/Wheel.php -+@@ -18,8 +18,12 @@ class Wheel extends Component -+ public $rotationFinal = 0; -+ public $state = 'ready'; -+ public $prize; -++ public $totalPrize = 0; -+ -+- -++ public function mount() -++ { -++ $this->totalPrize = Prize::count(); -++ } -+ -+ public function redeem() -+ { -+@@ -71,7 +75,7 @@ public function redeem() -+ $this->code = ""; -+ -+ if ($ticket->type == 'prize') { -+- $total = (rand(7, 9) * 8) - 1 + $ticket->prize->sort; -++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $ticket->prize->sort; -+ $this->state = $ticket->prize->winable; -+ $this->prize = $ticket->prize; -+ $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; -+@@ -104,7 +108,7 @@ public function redeem() -+ $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; -+ $ticket->spin_time = date('Y-m-d H:i:s'); -+ $ticket->save(); -+- $total = (rand(7, 9) * 8) - 1 + $final->sort; -++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $final->sort; -+ $this->dispatch('spin', $total); -+ return; -+ } -+@@ -114,4 +118,4 @@ public function render() -+ $history = Ticket::where('status', 'win')->orderBy('spin_time', 'desc')->take(10)->get(); -+ return view('livewire.wheel', compact('history')); -+ } -+-} -+\ No newline at end of file -++} -+diff --git a/app/Models/Prize.php b/app/Models/Prize.php -+index 6d5071c..758b4b1 100644 -+--- a/app/Models/Prize.php -++++ b/app/Models/Prize.php -+@@ -6,5 +6,5 @@ -+ -+ class Prize extends Model -+ { -+- protected $fillable = ['prize', 'chance_percentage', 'winable']; -+-} -+\ No newline at end of file -++ protected $fillable = ['prize', 'chance_percentage', 'winable', 'sort']; -++} -+diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php -+new file mode 100644 -+index 0000000..97aae7e -+--- /dev/null -++++ b/app/Policies/ContactPolicy.php -+@@ -0,0 +1,108 @@ -++can('view_any_contact'); -++ } -++ -++ /** -++ * Determine whether the user can view the model. -++ */ -++ public function view(User $user, Contact $contact): bool -++ { -++ return $user->can('view_contact'); -++ } -++ -++ /** -++ * Determine whether the user can create models. -++ */ -++ public function create(User $user): bool -++ { -++ return $user->can('create_contact'); -++ } -++ -++ /** -++ * Determine whether the user can update the model. -++ */ -++ public function update(User $user, Contact $contact): bool -++ { -++ return $user->can('update_contact'); -++ } -++ -++ /** -++ * Determine whether the user can delete the model. -++ */ -++ public function delete(User $user, Contact $contact): bool -++ { -++ return $user->can('delete_contact'); -++ } -++ -++ /** -++ * Determine whether the user can bulk delete. -++ */ -++ public function deleteAny(User $user): bool -++ { -++ return $user->can('delete_any_contact'); -++ } -++ -++ /** -++ * Determine whether the user can permanently delete. -++ */ -++ public function forceDelete(User $user, Contact $contact): bool -++ { -++ return $user->can('force_delete_contact'); -++ } -++ -++ /** -++ * Determine whether the user can permanently bulk delete. -++ */ -++ public function forceDeleteAny(User $user): bool -++ { -++ return $user->can('force_delete_any_contact'); -++ } -++ -++ /** -++ * Determine whether the user can restore. -++ */ -++ public function restore(User $user, Contact $contact): bool -++ { -++ return $user->can('restore_contact'); -++ } -++ -++ /** -++ * Determine whether the user can bulk restore. -++ */ -++ public function restoreAny(User $user): bool -++ { -++ return $user->can('restore_any_contact'); -++ } -++ -++ /** -++ * Determine whether the user can replicate. -++ */ -++ public function replicate(User $user, Contact $contact): bool -++ { -++ return $user->can('replicate_contact'); -++ } -++ -++ /** -++ * Determine whether the user can reorder. -++ */ -++ public function reorder(User $user): bool -++ { -++ return $user->can('reorder_contact'); -++ } -++} -+diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php -+index da3bdea..2f1806d 100644 -+--- a/resources/views/livewire/wheel.blade.php -++++ b/resources/views/livewire/wheel.blade.php -+@@ -29,7 +29,7 @@ -+ this.audio.currentTime = 0; // Mulai dari awal -+ this.audio.play(); // Mainkan suara -+ const rotations = total; -+- const totalRotate = rotations * -45; -++ const totalRotate = rotations * -(360 / {{ $totalPrize }}); -+ this.shift = -totalRotate; -+ const duration = 8000; // 8 detik -+ const start = performance.now(); -+-- -+2.50.0 -+ -diff --git a/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php -new file mode 100644 -index 0000000..938d967 ---- /dev/null -+++ b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php -@@ -0,0 +1,64 @@ -+string('prize_name')->nullable()->after('member_id'); -+ }); -+ -+ // 2. Copy data dari prizes.prize ke tickets.prize_name -+ // (aman walau ada prize_id null) -+ DB::statement(" -+ UPDATE tickets t -+ LEFT JOIN prizes p ON p.id = t.prize_id -+ SET t.prize_name = p.prize -+ WHERE t.prize_id IS NOT NULL -+ "); -+ -+ // 3. Drop foreign key + kolom prize_id -+ Schema::table('tickets', function (Blueprint $table) { -+ // default Laravel FK name -+ $table->dropForeign(['prize_id']); -+ $table->dropColumn('prize_id'); -+ }); -+ } -+ -+ /** -+ * Reverse the migrations. -+ */ -+ public function down(): void -+ { -+ // 1. Balikin kolom prize_id -+ Schema::table('tickets', function (Blueprint $table) { -+ $table->foreignId('prize_id') -+ ->nullable() -+ ->after('member_id') -+ ->constrained() -+ ->onDelete('cascade'); -+ }); -+ -+ // 2. Isi ulang prize_id dari prize_name (kalau nama masih sama) -+ DB::statement(" -+ UPDATE tickets t -+ JOIN prizes p ON p.prize = t.prize_name -+ SET t.prize_id = p.id -+ WHERE t.prize_name IS NOT NULL -+ "); -+ -+ // 3. Hapus snapshot -+ Schema::table('tickets', function (Blueprint $table) { -+ $table->dropColumn('prize_name'); -+ }); -+ } -+}; --- -2.50.0 - - -From b6e1f41b06c016b2560a510248b03840bca73603 Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Sat, 27 Dec 2025 18:07:15 +0700 -Subject: [PATCH 2/7] update - ---- - 0001-update.patch | 2 +- - 0002-update.patch | 371 ++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 372 insertions(+), 1 deletion(-) - create mode 100644 0002-update.patch - -diff --git a/0001-update.patch b/0001-update.patch -index 71e6e0b..a7d1126 100644 ---- a/0001-update.patch -+++ b/0001-update.patch -@@ -1,7 +1,7 @@ - From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 - From: Sinji Prasetio - Date: Sat, 27 Dec 2025 17:56:40 +0700 --Subject: [PATCH] update -+Subject: [PATCH 1/2] update - - --- - app/Filament/Resources/PrizeResource.php | 21 +++-- -diff --git a/0002-update.patch b/0002-update.patch -new file mode 100644 -index 0000000..4f8729a ---- /dev/null -+++ b/0002-update.patch -@@ -0,0 +1,371 @@ -+From ac0b01bfca7d081862cdf659466c2459a435a259 Mon Sep 17 00:00:00 2001 -+From: Sinji Prasetio -+Date: Sat, 27 Dec 2025 18:05:41 +0700 -+Subject: [PATCH 2/2] update -+ -+--- -+ 0001-update.patch | 280 ++++++++++++++++++ -+ ...ckets_replace_prize_id_with_prize_name.php | 64 ++++ -+ 2 files changed, 344 insertions(+) -+ create mode 100644 0001-update.patch -+ create mode 100644 database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php -+ -+diff --git a/0001-update.patch b/0001-update.patch -+new file mode 100644 -+index 0000000..71e6e0b -+--- /dev/null -++++ b/0001-update.patch -+@@ -0,0 +1,280 @@ -++From 2a6f3eebcbda4961e38ecf6e97a16926333386cd Mon Sep 17 00:00:00 2001 -++From: Sinji Prasetio -++Date: Sat, 27 Dec 2025 17:56:40 +0700 -++Subject: [PATCH] update -++ -++--- -++ app/Filament/Resources/PrizeResource.php | 21 +++-- -++ app/Livewire/Wheel.php | 12 ++- -++ app/Models/Prize.php | 4 +- -++ app/Policies/ContactPolicy.php | 108 +++++++++++++++++++++++ -++ resources/views/livewire/wheel.blade.php | 2 +- -++ 5 files changed, 129 insertions(+), 18 deletions(-) -++ create mode 100644 app/Policies/ContactPolicy.php -++ -++diff --git a/app/Filament/Resources/PrizeResource.php b/app/Filament/Resources/PrizeResource.php -++index 147c193..d4f5609 100644 -++--- a/app/Filament/Resources/PrizeResource.php -+++++ b/app/Filament/Resources/PrizeResource.php -++@@ -22,7 +22,7 @@ class PrizeResource extends Resource -++ -++ public static function canCreate(): bool -++ { -++- return false; -+++ return true; -++ } -++ -++ public static function form(Form $form): Form -++@@ -35,9 +35,10 @@ public static function form(Form $form): Form -++ ->maxLength(255), -++ Forms\Components\TextInput::make('sort') -++ ->label(__('Sort')) -++- ->required() -++ ->numeric() -++- ->disabledOn('edit'), -+++ ->default(fn() => (Prize::max('sort') ?? 0) + 1) -+++ ->readOnly() // tetap disimpan ke DB walau disabled -+++ ->required(), -++ Forms\Components\TextInput::make('chance_percentage') -++ ->label(__('Chance Percentage')) -++ ->required() -++@@ -63,12 +64,10 @@ public static function table(Table $table): Table -++ ->searchable(), -++ Tables\Columns\TextColumn::make('sort') -++ ->label(__('Sort')) -++- ->numeric() -++- ->sortable(), -+++ ->numeric(), -++ Tables\Columns\TextColumn::make('chance_percentage') -++ ->label(__('Chance Percentage')) -++ ->numeric() -++- ->sortable() -++ ->formatStateUsing(fn($state): string => $state . "%") -++ ->summarize([ -++ Sum::make()->formatStateUsing(fn($state): string => (round($state * 100)) / 100 . "%"), -++@@ -76,7 +75,6 @@ public static function table(Table $table): Table -++ Tables\Columns\TextColumn::make('winable') -++ ->label(__('Status')) -++ ->badge() -++- ->badge() -++ ->color(fn(string $state): string => match ($state) { -++ 'winable' => 'success', -++ 'unwinable' => 'danger', -++@@ -85,20 +83,21 @@ public static function table(Table $table): Table -++ Tables\Columns\TextColumn::make('created_at') -++ ->label(__('Created At')) -++ ->dateTime() -++- ->sortable() -++ ->toggleable(isToggledHiddenByDefault: true), -++ Tables\Columns\TextColumn::make('updated_at') -++ ->label(__('Updated At')) -++ ->dateTime() -++- ->sortable() -++ ->toggleable(isToggledHiddenByDefault: true), -++- -++ ]) -+++ ->defaultSort('sort') -+++ ->reorderable('sort') -+++ ->paginated(false) -++ ->filters([ -++ // -++ ]) -++ ->actions([ -++ Tables\Actions\EditAction::make(), -+++ Tables\Actions\DeleteAction::make(), -++ ]) -++ ->bulkActions([]) -++ ->paginated(false);; -++@@ -129,4 +128,4 @@ public static function getModelLabel(): string -++ { -++ return __('Prize'); -++ } -++-} -++\ No newline at end of file -+++} -++diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php -++index 602970c..bfb1dd7 100644 -++--- a/app/Livewire/Wheel.php -+++++ b/app/Livewire/Wheel.php -++@@ -18,8 +18,12 @@ class Wheel extends Component -++ public $rotationFinal = 0; -++ public $state = 'ready'; -++ public $prize; -+++ public $totalPrize = 0; -++ -++- -+++ public function mount() -+++ { -+++ $this->totalPrize = Prize::count(); -+++ } -++ -++ public function redeem() -++ { -++@@ -71,7 +75,7 @@ public function redeem() -++ $this->code = ""; -++ -++ if ($ticket->type == 'prize') { -++- $total = (rand(7, 9) * 8) - 1 + $ticket->prize->sort; -+++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $ticket->prize->sort; -++ $this->state = $ticket->prize->winable; -++ $this->prize = $ticket->prize; -++ $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; -++@@ -104,7 +108,7 @@ public function redeem() -++ $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; -++ $ticket->spin_time = date('Y-m-d H:i:s'); -++ $ticket->save(); -++- $total = (rand(7, 9) * 8) - 1 + $final->sort; -+++ $total = (rand(7, 9) * $this->totalPrize) - 1 + $final->sort; -++ $this->dispatch('spin', $total); -++ return; -++ } -++@@ -114,4 +118,4 @@ public function render() -++ $history = Ticket::where('status', 'win')->orderBy('spin_time', 'desc')->take(10)->get(); -++ return view('livewire.wheel', compact('history')); -++ } -++-} -++\ No newline at end of file -+++} -++diff --git a/app/Models/Prize.php b/app/Models/Prize.php -++index 6d5071c..758b4b1 100644 -++--- a/app/Models/Prize.php -+++++ b/app/Models/Prize.php -++@@ -6,5 +6,5 @@ -++ -++ class Prize extends Model -++ { -++- protected $fillable = ['prize', 'chance_percentage', 'winable']; -++-} -++\ No newline at end of file -+++ protected $fillable = ['prize', 'chance_percentage', 'winable', 'sort']; -+++} -++diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php -++new file mode 100644 -++index 0000000..97aae7e -++--- /dev/null -+++++ b/app/Policies/ContactPolicy.php -++@@ -0,0 +1,108 @@ -+++can('view_any_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can view the model. -+++ */ -+++ public function view(User $user, Contact $contact): bool -+++ { -+++ return $user->can('view_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can create models. -+++ */ -+++ public function create(User $user): bool -+++ { -+++ return $user->can('create_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can update the model. -+++ */ -+++ public function update(User $user, Contact $contact): bool -+++ { -+++ return $user->can('update_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can delete the model. -+++ */ -+++ public function delete(User $user, Contact $contact): bool -+++ { -+++ return $user->can('delete_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can bulk delete. -+++ */ -+++ public function deleteAny(User $user): bool -+++ { -+++ return $user->can('delete_any_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can permanently delete. -+++ */ -+++ public function forceDelete(User $user, Contact $contact): bool -+++ { -+++ return $user->can('force_delete_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can permanently bulk delete. -+++ */ -+++ public function forceDeleteAny(User $user): bool -+++ { -+++ return $user->can('force_delete_any_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can restore. -+++ */ -+++ public function restore(User $user, Contact $contact): bool -+++ { -+++ return $user->can('restore_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can bulk restore. -+++ */ -+++ public function restoreAny(User $user): bool -+++ { -+++ return $user->can('restore_any_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can replicate. -+++ */ -+++ public function replicate(User $user, Contact $contact): bool -+++ { -+++ return $user->can('replicate_contact'); -+++ } -+++ -+++ /** -+++ * Determine whether the user can reorder. -+++ */ -+++ public function reorder(User $user): bool -+++ { -+++ return $user->can('reorder_contact'); -+++ } -+++} -++diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php -++index da3bdea..2f1806d 100644 -++--- a/resources/views/livewire/wheel.blade.php -+++++ b/resources/views/livewire/wheel.blade.php -++@@ -29,7 +29,7 @@ -++ this.audio.currentTime = 0; // Mulai dari awal -++ this.audio.play(); // Mainkan suara -++ const rotations = total; -++- const totalRotate = rotations * -45; -+++ const totalRotate = rotations * -(360 / {{ $totalPrize }}); -++ this.shift = -totalRotate; -++ const duration = 8000; // 8 detik -++ const start = performance.now(); -++-- -++2.50.0 -++ -+diff --git a/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php -+new file mode 100644 -+index 0000000..938d967 -+--- /dev/null -++++ b/database/migrations/2025_12_27_180431_update_tickets_replace_prize_id_with_prize_name.php -+@@ -0,0 +1,64 @@ -++string('prize_name')->nullable()->after('member_id'); -++ }); -++ -++ // 2. Copy data dari prizes.prize ke tickets.prize_name -++ // (aman walau ada prize_id null) -++ DB::statement(" -++ UPDATE tickets t -++ LEFT JOIN prizes p ON p.id = t.prize_id -++ SET t.prize_name = p.prize -++ WHERE t.prize_id IS NOT NULL -++ "); -++ -++ // 3. Drop foreign key + kolom prize_id -++ Schema::table('tickets', function (Blueprint $table) { -++ // default Laravel FK name -++ $table->dropForeign(['prize_id']); -++ $table->dropColumn('prize_id'); -++ }); -++ } -++ -++ /** -++ * Reverse the migrations. -++ */ -++ public function down(): void -++ { -++ // 1. Balikin kolom prize_id -++ Schema::table('tickets', function (Blueprint $table) { -++ $table->foreignId('prize_id') -++ ->nullable() -++ ->after('member_id') -++ ->constrained() -++ ->onDelete('cascade'); -++ }); -++ -++ // 2. Isi ulang prize_id dari prize_name (kalau nama masih sama) -++ DB::statement(" -++ UPDATE tickets t -++ JOIN prizes p ON p.prize = t.prize_name -++ SET t.prize_id = p.id -++ WHERE t.prize_name IS NOT NULL -++ "); -++ -++ // 3. Hapus snapshot -++ Schema::table('tickets', function (Blueprint $table) { -++ $table->dropColumn('prize_name'); -++ }); -++ } -++}; -+-- -+2.50.0 -+ --- -2.50.0 - - -From 085158048211008965f9839ac0e5c6a71b6e4906 Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Sun, 28 Dec 2025 21:40:18 +0700 -Subject: [PATCH 3/7] update - ---- - app/Livewire/Wheel.php | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php -index bfb1dd7..4f82c71 100644 ---- a/app/Livewire/Wheel.php -+++ b/app/Livewire/Wheel.php -@@ -17,7 +17,7 @@ class Wheel extends Component - public $code; - public $rotationFinal = 0; - public $state = 'ready'; -- public $prize; -+ public $prize = ''; - public $totalPrize = 0; - - public function mount() --- -2.50.0 - - -From 1d3c9eb95337d0a095c841d702e6efc2fe666854 Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Sun, 28 Dec 2025 21:44:19 +0700 -Subject: [PATCH 4/7] update - ---- - app/Livewire/Wheel.php | 2 +- - resources/views/livewire/wheel.blade.php | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php -index 4f82c71..bfb1dd7 100644 ---- a/app/Livewire/Wheel.php -+++ b/app/Livewire/Wheel.php -@@ -17,7 +17,7 @@ class Wheel extends Component - public $code; - public $rotationFinal = 0; - public $state = 'ready'; -- public $prize = ''; -+ public $prize; - public $totalPrize = 0; - - public function mount() -diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php -index 2f1806d..6be22cb 100644 ---- a/resources/views/livewire/wheel.blade.php -+++ b/resources/views/livewire/wheel.blade.php -@@ -212,7 +212,7 @@ class="absolute p-2 rounded-full bg-gradient-to-b from-violet-800 to-violet-900 - - {{ $i + 1 }} - {{ format()->maskUsername($h->member->username) }} -- {{ $h->prize->prize }} -+ {{ $h->prize }} - - @endforeach - --- -2.50.0 - - -From c31e4d00e2edbb1a56c80dd1222df99e169e8588 Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Mon, 29 Dec 2025 20:43:47 +0700 -Subject: [PATCH 5/7] update - ---- - resources/views/livewire/wheel.blade.php | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/resources/views/livewire/wheel.blade.php b/resources/views/livewire/wheel.blade.php -index 6be22cb..df4debd 100644 ---- a/resources/views/livewire/wheel.blade.php -+++ b/resources/views/livewire/wheel.blade.php -@@ -212,7 +212,7 @@ class="absolute p-2 rounded-full bg-gradient-to-b from-violet-800 to-violet-900 - - {{ $i + 1 }} - {{ format()->maskUsername($h->member->username) }} -- {{ $h->prize }} -+ {{ $h->prize_name }} - - @endforeach - --- -2.50.0 - - -From e7ce486136475b99d445b712a236199122ae10b1 Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Mon, 29 Dec 2025 20:46:53 +0700 -Subject: [PATCH 6/7] update - ---- - app/Livewire/Wheel.php | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php -index bfb1dd7..e80fb06 100644 ---- a/app/Livewire/Wheel.php -+++ b/app/Livewire/Wheel.php -@@ -103,7 +103,7 @@ public function redeem() - } - } while (!$final); - -- $ticket->prize_id = $this->prize->id; -+ $ticket->prize_name = $this->prize->prize; - $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; - $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; - $ticket->spin_time = date('Y-m-d H:i:s'); --- -2.50.0 - - -From eff0c8c3abd02d1b4ca7051093968b3797dbcee5 Mon Sep 17 00:00:00 2001 -From: Sinji Prasetio -Date: Mon, 29 Dec 2025 21:02:01 +0700 -Subject: [PATCH 7/7] update - ---- - app/Filament/Resources/TicketResource.php | 6 +++- - app/Livewire/Wheel.php | 2 ++ - ...9_205204_add_prize_id_to_tickets_table.php | 28 +++++++++++++++++++ - 3 files changed, 35 insertions(+), 1 deletion(-) - create mode 100644 database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php - -diff --git a/app/Filament/Resources/TicketResource.php b/app/Filament/Resources/TicketResource.php -index 8534b13..08ab7e1 100644 ---- a/app/Filament/Resources/TicketResource.php -+++ b/app/Filament/Resources/TicketResource.php -@@ -106,6 +106,10 @@ public static function table(Table $table): Table - }) - ->label(__('Win Type')), - Tables\Columns\TextColumn::make('prize.prize') -+ ->label(__('Prize Set')) -+ ->sortable() -+ ->default('-'), -+ Tables\Columns\TextColumn::make('prize_name') - ->label(__('Prize')) - ->sortable() - ->default('-'), -@@ -203,4 +207,4 @@ public static function getModelLabel(): string - { - return __('Ticket'); - } --} -\ No newline at end of file -+} -diff --git a/app/Livewire/Wheel.php b/app/Livewire/Wheel.php -index e80fb06..1011564 100644 ---- a/app/Livewire/Wheel.php -+++ b/app/Livewire/Wheel.php -@@ -79,6 +79,7 @@ public function redeem() - $this->state = $ticket->prize->winable; - $this->prize = $ticket->prize; - $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; -+ $ticket->prize_name = $this->prize->prize; - $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; - $ticket->spin_time = date('Y-m-d H:i:s'); - $ticket->save(); -@@ -103,6 +104,7 @@ public function redeem() - } - } while (!$final); - -+ $ticket->prize_id = $this->prize->id; - $ticket->prize_name = $this->prize->prize; - $ticket->status = $this->state == 'winable' ? 'win' : 'lose'; - $ticket->prize_status = $this->state == 'winable' ? 'pending' : 'unavailable'; -diff --git a/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php b/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php -new file mode 100644 -index 0000000..6175276 ---- /dev/null -+++ b/database/migrations/2025_12_29_205204_add_prize_id_to_tickets_table.php -@@ -0,0 +1,28 @@ -+foreignId('prize_id')->nullable()->after('member_id'); -+ }); -+ } -+ -+ /** -+ * Reverse the migrations. -+ */ -+ public function down(): void -+ { -+ Schema::table('tickets', function (Blueprint $table) { -+ $table->dropColumn('prize_id'); -+ }); -+ } -+}; --- -2.50.0 - -- 2.50.0