form->fill(); } protected function getFormSchema(): array { return [ Select::make('plan_id') ->label('Select Plan') ->options(function () { return Plan::with('planTier') ->get() ->map(function ($plan) { $tierName = $plan->planTier ? $plan->planTier->name : ''; $billingCycle = $plan->getBillingCycleDisplay(); $name = $plan->name; if ($tierName) { $name .= " ({$tierName})"; } if ($billingCycle) { $name .= " - {$billingCycle}"; } return $name; }) ->toArray(); }) ->required() ->reactive() ->afterStateUpdated(fn ($state, callable $set) => $set('quantity', 1)), TextInput::make('quantity') ->label('Number of Keys') ->numeric() ->minValue(1) ->maxValue(100) ->default(1) ->required() ->helperText('Generate multiple keys for bulk distribution'), Textarea::make('notes') ->label('Generation Notes (Optional)') ->rows(2) ->placeholder('Add notes about this batch of keys...') ->helperText('These notes will be stored with the generated keys for tracking purposes'), ]; } public function generate(): void { try { $data = $this->form->getState(); $plan = Plan::with('planTier')->findOrFail($data['plan_id']); $generatedKeys = []; $batchId = 'BATCH_'.now()->format('YmdHis').'_'.Str::random(8); for ($i = 0; $i < $data['quantity']; $i++) { $activationKey = ActivationKey::query()->create([ 'price_id' => $plan->pricing_id, 'activation_key' => $this->generateUniqueActivationKey($plan), 'is_activated' => false, ]); $generatedKeys[] = $activationKey->activation_key; } // Log the generation for audit purposes \Log::info('Activation keys generated', [ 'batch_id' => $batchId, 'plan_id' => $plan->id, 'plan_name' => $plan->name, 'quantity' => $data['quantity'], 'generated_by' => auth()->id(), 'notes' => $data['notes'] ?? null, ]); Notification::make() ->title("{$data['quantity']} activation key(s) generated successfully") ->body("For {$plan->name} - Batch ID: {$batchId}") ->success() ->send(); $this->form->fill(); // Reset form } catch (\Exception $exception) { $this->form->fill(); Notification::make() ->title('Something went wrong') ->body("Error: {$exception->getMessage()}") ->danger() ->send(); } } private function generateUniqueActivationKey(Plan $plan): string { do { // Create a more structured key format: PLAN_PREFIX + RANDOM $prefix = strtoupper(substr(str_replace([' ', '-'], '', $plan->name), 0, 3)); $random = strtoupper(Str::random(13)); $key = $prefix.$random; } while (ActivationKey::where('activation_key', $key)->exists()); return $key; } // === Table Setup === protected function getTableQuery(): Builder { return ActivationKey::query()->latest(); } protected function getTableColumns(): array { return [ TextColumn::make('activation_key') ->label('Activation Key') ->copyable() ->searchable() ->weight('font-semibold'), BadgeColumn::make('status') ->label('Status') ->getStateUsing(function ($record): string { return $record->is_activated ? 'Activated' : 'Unused'; }) ->colors([ 'success' => 'Activated', 'warning' => 'Unused', ]), TextColumn::make('plan.name') ->label('Plan') ->getStateUsing(function ($record): string { $plan = Plan::where('pricing_id', $record->price_id)->first(); return $plan ? $plan->name : 'Unknown Plan'; }) ->searchable(), TextColumn::make('plan.planTier.name') ->label('Tier') ->getStateUsing(function ($record): ?string { $plan = Plan::where('pricing_id', $record->price_id)->first(); return $plan && $plan->planTier ? $plan->planTier->name : null; }), TextColumn::make('user.email') ->label('Activated By') ->default('Not activated') ->searchable(), TextColumn::make('billing_cycle') ->label('Billing Cycle') ->getStateUsing(function ($record): string { $plan = Plan::where('pricing_id', $record->price_id)->first(); return $plan ? $plan->getBillingCycleDisplay() : 'Unknown'; }), TextColumn::make('created_at') ->label('Generated') ->dateTime() ->sortable(), ]; } protected function getTableFilters(): array { return [ SelectFilter::make('is_activated') ->label('Status') ->options([ true => 'Activated', false => 'Not Activated', ]), SelectFilter::make('price_id') ->label('Plan') ->options(function () { return Plan::with('planTier') ->get() ->mapWithKeys(function ($plan) { $tierName = $plan->planTier ? " ({$plan->planTier->name})" : ''; return [$plan->pricing_id => $plan->name.$tierName]; }) ->toArray(); }), SelectFilter::make('created_at') ->label('Generation Date') ->options([ 'today' => 'Today', 'this_week' => 'This Week', 'this_month' => 'This Month', 'last_month' => 'Last Month', ]) ->query(function (Builder $query, array $data): Builder { if ($data['value'] === 'today') { return $query->whereDate('created_at', today()); } elseif ($data['value'] === 'this_week') { return $query->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()]); } elseif ($data['value'] === 'this_month') { return $query->whereMonth('created_at', now()->month) ->whereYear('created_at', now()->year); } elseif ($data['value'] === 'last_month') { return $query->whereMonth('created_at', now()->subMonth()->month) ->whereYear('created_at', now()->subMonth()->year); } return $query; }), ]; } protected function getTableBulkActions(): array { return [ BulkAction::make('Download Keys') ->action(fn (Collection $records): BinaryFileResponse => $this->downloadKeys($records)) ->deselectRecordsAfterCompletion() ->requiresConfirmation() ->modalDescription('Download selected activation keys as a text file.'), BulkAction::make('Deactivate Keys') ->action(function (Collection $records) { $count = 0; foreach ($records as $record) { if ($record->is_activated) { // Deactivate the subscription if it exists $subscription = $record->user?->subscriptions() ->where('provider', 'activation_key') ->where('provider_subscription_id', $record->id) ->first(); if ($subscription) { $subscription->update([ 'status' => 'cancelled', 'ends_at' => now(), ]); } $record->update([ 'is_activated' => false, 'user_id' => null, ]); $count++; } } Notification::make() ->title("{$count} key(s) deactivated") ->success() ->send(); }) ->deselectRecordsAfterCompletion() ->requiresConfirmation() ->modalDescription('This will deactivate the selected keys and cancel associated subscriptions.') ->color('danger'), BulkAction::make('Delete Keys') ->action(function (Collection $records) { $count = $records->count(); // First, deactivate any associated subscriptions foreach ($records as $record) { $subscription = $record->user?->subscriptions() ->where('provider', 'activation_key') ->where('provider_subscription_id', $record->id) ->first(); if ($subscription) { $subscription->delete(); } } // Delete the keys $records->each->delete(); Notification::make() ->title("{$count} key(s) deleted") ->success() ->send(); }) ->deselectRecordsAfterCompletion() ->requiresConfirmation() ->modalDescription('This will permanently delete the selected keys and associated subscriptions.') ->color('danger'), ]; } public function downloadKeys(Collection $records): BinaryFileResponse { $content = "# Activation Keys\n"; $content .= '# Generated: '.now()->toDateTimeString()."\n"; $content .= '# Total Keys: '.$records->count()."\n\n"; foreach ($records as $record) { $plan = Plan::where('pricing_id', $record->price_id)->first(); $content .= "Key: {$record->activation_key}\n"; $content .= 'Plan: '.($plan->name ?? 'Unknown Plan')."\n"; $content .= 'Status: '.($record->is_activated ? 'Activated' : 'Unused')."\n"; $content .= 'Generated: '.$record->created_at->toDateTimeString()."\n"; if ($record->user) { $content .= 'Activated By: '.$record->user->email."\n"; } $content .= "---\n\n"; } $filename = 'activation_keys_'.now()->format('Y-m-d_H-i-s').'.txt'; $path = public_path("activation/{$filename}"); // Make sure the 'activation' folder exists if (! file_exists(public_path('activation')) && ! mkdir($concurrentDirectory = public_path('activation'), 0755, true) && ! is_dir($concurrentDirectory)) { Log::error('Failed to create activation keys file: '.$concurrentDirectory); } file_put_contents($path, $content); return response()->download($path)->deleteFileAfterSend(true); } }