From ac0b01bfca7d081862cdf659466c2459a435a259 Mon Sep 17 00:00:00 2001
From: Sinji Prasetio <sinjiprasetio@outlook.com>
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 <sinjiprasetio@outlook.com>
+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 @@
++<?php
++
++namespace App\Policies;
++
++use App\Models\User;
++use App\Models\Contact;
++use Illuminate\Auth\Access\HandlesAuthorization;
++
++class ContactPolicy
++{
++    use HandlesAuthorization;
++
++    /**
++     * Determine whether the user can view any models.
++     */
++    public function viewAny(User $user): bool
++    {
++        return $user->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 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        // 1. Tambah kolom prize_name (snapshot)
+        Schema::table('tickets', function (Blueprint $table) {
+            $table->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 <sinjiprasetio@outlook.com>
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 <sinjiprasetio@outlook.com>
 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 <sinjiprasetio@outlook.com>
+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 <sinjiprasetio@outlook.com>
++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 @@
+++<?php
+++
+++namespace App\Policies;
+++
+++use App\Models\User;
+++use App\Models\Contact;
+++use Illuminate\Auth\Access\HandlesAuthorization;
+++
+++class ContactPolicy
+++{
+++    use HandlesAuthorization;
+++
+++    /**
+++     * Determine whether the user can view any models.
+++     */
+++    public function viewAny(User $user): bool
+++    {
+++        return $user->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 @@
++<?php
++
++use Illuminate\Database\Migrations\Migration;
++use Illuminate\Database\Schema\Blueprint;
++use Illuminate\Support\Facades\Schema;
++use Illuminate\Support\Facades\DB;
++
++return new class extends Migration
++{
++    /**
++     * Run the migrations.
++     */
++    public function up(): void
++    {
++        // 1. Tambah kolom prize_name (snapshot)
++        Schema::table('tickets', function (Blueprint $table) {
++            $table->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 <sinjiprasetio@outlook.com>
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 <sinjiprasetio@outlook.com>
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
                                     <tr>
                                         <td class="px-4 py-2">{{ $i + 1 }}</td>
                                         <td class="px-4 py-2">{{ format()->maskUsername($h->member->username) }}</td>
-                                        <td class="px-4 py-2">{{ $h->prize->prize }}</td>
+                                        <td class="px-4 py-2">{{ $h->prize }}</td>
                                     </tr>
                                 @endforeach
                             </tbody>
-- 
2.50.0


From c31e4d00e2edbb1a56c80dd1222df99e169e8588 Mon Sep 17 00:00:00 2001
From: Sinji Prasetio <sinjiprasetio@outlook.com>
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
                                     <tr>
                                         <td class="px-4 py-2">{{ $i + 1 }}</td>
                                         <td class="px-4 py-2">{{ format()->maskUsername($h->member->username) }}</td>
-                                        <td class="px-4 py-2">{{ $h->prize }}</td>
+                                        <td class="px-4 py-2">{{ $h->prize_name }}</td>
                                     </tr>
                                 @endforeach
                             </tbody>
-- 
2.50.0


From e7ce486136475b99d445b712a236199122ae10b1 Mon Sep 17 00:00:00 2001
From: Sinji Prasetio <sinjiprasetio@outlook.com>
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 <sinjiprasetio@outlook.com>
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 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::table('tickets', function (Blueprint $table) {
+            $table->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 <sinjiprasetio@outlook.com>
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 <sinjiprasetio@outlook.com>
-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 @@
-+<?php
-+
-+namespace App\Policies;
-+
-+use App\Models\User;
-+use App\Models\Contact;
-+use Illuminate\Auth\Access\HandlesAuthorization;
-+
-+class ContactPolicy
-+{
-+    use HandlesAuthorization;
-+
-+    /**
-+     * Determine whether the user can view any models.
-+     */
-+    public function viewAny(User $user): bool
-+    {
-+        return $user->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 <sinjiprasetio@outlook.com>
-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 <sinjiprasetio@outlook.com>
-+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 @@
-++<?php
-++
-++namespace App\Policies;
-++
-++use App\Models\User;
-++use App\Models\Contact;
-++use Illuminate\Auth\Access\HandlesAuthorization;
-++
-++class ContactPolicy
-++{
-++    use HandlesAuthorization;
-++
-++    /**
-++     * Determine whether the user can view any models.
-++     */
-++    public function viewAny(User $user): bool
-++    {
-++        return $user->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 @@
-+<?php
-+
-+use Illuminate\Database\Migrations\Migration;
-+use Illuminate\Database\Schema\Blueprint;
-+use Illuminate\Support\Facades\Schema;
-+use Illuminate\Support\Facades\DB;
-+
-+return new class extends Migration
-+{
-+    /**
-+     * Run the migrations.
-+     */
-+    public function up(): void
-+    {
-+        // 1. Tambah kolom prize_name (snapshot)
-+        Schema::table('tickets', function (Blueprint $table) {
-+            $table->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 <sinjiprasetio@outlook.com>
+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 <sinjiprasetio@outlook.com>
++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 @@
+++<?php
+++
+++namespace App\Policies;
+++
+++use App\Models\User;
+++use App\Models\Contact;
+++use Illuminate\Auth\Access\HandlesAuthorization;
+++
+++class ContactPolicy
+++{
+++    use HandlesAuthorization;
+++
+++    /**
+++     * Determine whether the user can view any models.
+++     */
+++    public function viewAny(User $user): bool
+++    {
+++        return $user->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 @@
++<?php
++
++use Illuminate\Database\Migrations\Migration;
++use Illuminate\Database\Schema\Blueprint;
++use Illuminate\Support\Facades\Schema;
++use Illuminate\Support\Facades\DB;
++
++return new class extends Migration
++{
++    /**
++     * Run the migrations.
++     */
++    public function up(): void
++    {
++        // 1. Tambah kolom prize_name (snapshot)
++        Schema::table('tickets', function (Blueprint $table) {
++            $table->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 <sinjiprasetio@outlook.com>
+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 <sinjiprasetio@outlook.com>
+ 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 <sinjiprasetio@outlook.com>
++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 <sinjiprasetio@outlook.com>
+++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 @@
++++<?php
++++
++++namespace App\Policies;
++++
++++use App\Models\User;
++++use App\Models\Contact;
++++use Illuminate\Auth\Access\HandlesAuthorization;
++++
++++class ContactPolicy
++++{
++++    use HandlesAuthorization;
++++
++++    /**
++++     * Determine whether the user can view any models.
++++     */
++++    public function viewAny(User $user): bool
++++    {
++++        return $user->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 @@
+++<?php
+++
+++use Illuminate\Database\Migrations\Migration;
+++use Illuminate\Database\Schema\Blueprint;
+++use Illuminate\Support\Facades\Schema;
+++use Illuminate\Support\Facades\DB;
+++
+++return new class extends Migration
+++{
+++    /**
+++     * Run the migrations.
+++     */
+++    public function up(): void
+++    {
+++        // 1. Tambah kolom prize_name (snapshot)
+++        Schema::table('tickets', function (Blueprint $table) {
+++            $table->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 <sinjiprasetio@outlook.com>
+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 <sinjiprasetio@outlook.com>
+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
+                                     <tr>
+                                         <td class="px-4 py-2">{{ $i + 1 }}</td>
+                                         <td class="px-4 py-2">{{ format()->maskUsername($h->member->username) }}</td>
+-                                        <td class="px-4 py-2">{{ $h->prize->prize }}</td>
++                                        <td class="px-4 py-2">{{ $h->prize }}</td>
+                                     </tr>
+                                 @endforeach
+                             </tbody>
+-- 
+2.50.0
+
+
+From c31e4d00e2edbb1a56c80dd1222df99e169e8588 Mon Sep 17 00:00:00 2001
+From: Sinji Prasetio <sinjiprasetio@outlook.com>
+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
+                                     <tr>
+                                         <td class="px-4 py-2">{{ $i + 1 }}</td>
+                                         <td class="px-4 py-2">{{ format()->maskUsername($h->member->username) }}</td>
+-                                        <td class="px-4 py-2">{{ $h->prize }}</td>
++                                        <td class="px-4 py-2">{{ $h->prize_name }}</td>
+                                     </tr>
+                                 @endforeach
+                             </tbody>
+-- 
+2.50.0
+
+
+From e7ce486136475b99d445b712a236199122ae10b1 Mon Sep 17 00:00:00 2001
+From: Sinji Prasetio <sinjiprasetio@outlook.com>
+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 <sinjiprasetio@outlook.com>
+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 @@
++<?php
++
++use Illuminate\Database\Migrations\Migration;
++use Illuminate\Database\Schema\Blueprint;
++use Illuminate\Support\Facades\Schema;
++
++return new class extends Migration
++{
++    /**
++     * Run the migrations.
++     */
++    public function up(): void
++    {
++        Schema::table('tickets', function (Blueprint $table) {
++            $table->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 <sinjiprasetio@outlook.com>
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 <sinjiprasetio@outlook.com>
-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 <sinjiprasetio@outlook.com>
-+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 @@
-++<?php
-++
-++namespace App\Policies;
-++
-++use App\Models\User;
-++use App\Models\Contact;
-++use Illuminate\Auth\Access\HandlesAuthorization;
-++
-++class ContactPolicy
-++{
-++    use HandlesAuthorization;
-++
-++    /**
-++     * Determine whether the user can view any models.
-++     */
-++    public function viewAny(User $user): bool
-++    {
-++        return $user->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 @@
-+<?php
-+
-+use Illuminate\Database\Migrations\Migration;
-+use Illuminate\Database\Schema\Blueprint;
-+use Illuminate\Support\Facades\Schema;
-+use Illuminate\Support\Facades\DB;
-+
-+return new class extends Migration
-+{
-+    /**
-+     * Run the migrations.
-+     */
-+    public function up(): void
-+    {
-+        // 1. Tambah kolom prize_name (snapshot)
-+        Schema::table('tickets', function (Blueprint $table) {
-+            $table->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 <sinjiprasetio@outlook.com>
-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 <sinjiprasetio@outlook.com>
- 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 <sinjiprasetio@outlook.com>
-+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 <sinjiprasetio@outlook.com>
-++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 @@
-+++<?php
-+++
-+++namespace App\Policies;
-+++
-+++use App\Models\User;
-+++use App\Models\Contact;
-+++use Illuminate\Auth\Access\HandlesAuthorization;
-+++
-+++class ContactPolicy
-+++{
-+++    use HandlesAuthorization;
-+++
-+++    /**
-+++     * Determine whether the user can view any models.
-+++     */
-+++    public function viewAny(User $user): bool
-+++    {
-+++        return $user->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 @@
-++<?php
-++
-++use Illuminate\Database\Migrations\Migration;
-++use Illuminate\Database\Schema\Blueprint;
-++use Illuminate\Support\Facades\Schema;
-++use Illuminate\Support\Facades\DB;
-++
-++return new class extends Migration
-++{
-++    /**
-++     * Run the migrations.
-++     */
-++    public function up(): void
-++    {
-++        // 1. Tambah kolom prize_name (snapshot)
-++        Schema::table('tickets', function (Blueprint $table) {
-++            $table->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 <sinjiprasetio@outlook.com>
-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 <sinjiprasetio@outlook.com>
-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
-                                     <tr>
-                                         <td class="px-4 py-2">{{ $i + 1 }}</td>
-                                         <td class="px-4 py-2">{{ format()->maskUsername($h->member->username) }}</td>
--                                        <td class="px-4 py-2">{{ $h->prize->prize }}</td>
-+                                        <td class="px-4 py-2">{{ $h->prize }}</td>
-                                     </tr>
-                                 @endforeach
-                             </tbody>
--- 
-2.50.0
-
-
-From c31e4d00e2edbb1a56c80dd1222df99e169e8588 Mon Sep 17 00:00:00 2001
-From: Sinji Prasetio <sinjiprasetio@outlook.com>
-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
-                                     <tr>
-                                         <td class="px-4 py-2">{{ $i + 1 }}</td>
-                                         <td class="px-4 py-2">{{ format()->maskUsername($h->member->username) }}</td>
--                                        <td class="px-4 py-2">{{ $h->prize }}</td>
-+                                        <td class="px-4 py-2">{{ $h->prize_name }}</td>
-                                     </tr>
-                                 @endforeach
-                             </tbody>
--- 
-2.50.0
-
-
-From e7ce486136475b99d445b712a236199122ae10b1 Mon Sep 17 00:00:00 2001
-From: Sinji Prasetio <sinjiprasetio@outlook.com>
-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 <sinjiprasetio@outlook.com>
-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 @@
-+<?php
-+
-+use Illuminate\Database\Migrations\Migration;
-+use Illuminate\Database\Schema\Blueprint;
-+use Illuminate\Support\Facades\Schema;
-+
-+return new class extends Migration
-+{
-+    /**
-+     * Run the migrations.
-+     */
-+    public function up(): void
-+    {
-+        Schema::table('tickets', function (Blueprint $table) {
-+            $table->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

