feat: implement comprehensive enhanced plan management system

- Create 7 new models with full relationships and business logic:
     * PlanFeature: Define available features with categories and types
     * PlanFeatureLimit: Manage usage limits per plan with trial overrides
     * PlanPermission: Granular permissions system for features
     * PlanProvider: Multi-provider payment configuration
     * PlanTier: Hierarchical plan structure with upgrade paths
     * PlanUsage: Real-time usage tracking and analytics
     * TrialConfiguration: Advanced trial settings per plan

   - Enhance Plan model with 25+ new methods:
     * Feature checking: hasFeature(), canUseFeature(), getRemainingUsage()
     * Permission system: hasPermission() with trial support
     * Payment providers: getAllowedProviders(), supportsProvider()
     * Trial management: hasTrial(), getTrialConfig()
     * Upgrade paths: isUpgradeFrom(), getUpgradePath()
     * Utility methods: getBillingCycleDisplay(), metadata handling

   - Completely redesign PlanResource with tabbed interface:
     * Basic Info: Core plan configuration with dynamic billing cycles
     * Features & Limits: Dynamic feature management with trial overrides
     * Payment Providers: Multi-provider configuration (Stripe, Lemon Squeezy, etc.)
     * Trial Settings: Advanced trial configuration with always-visible toggle

   - Create new Filament resources:
     * PlanFeatureResource: Manage available features by category
     * PlanTierResource: Hierarchical tier management with parent-child relationships

   - Implement comprehensive data migration:
     * Migrate legacy plan data to new enhanced system
     * Create default features (mailbox accounts, email forwarding, etc.)
     * Preserve existing payment provider configurations
     * Set up trial configurations (disabled for legacy plans)
     * Handle duplicate data gracefully with rollback support

   - Add proper database constraints and indexes:
     * Unique constraints on plan-feature relationships
     * Foreign key constraints with cascade deletes
     * Performance indexes for common queries
     * JSON metadata columns for flexible configuration

   - Fix trial configuration form handling:
     * Add required validation for numeric fields
     * Implement proper null handling with defaults
     * Add type casting for all numeric fields
     * Ensure database constraint compliance
This commit is contained in:
idevakk
2025-11-21 07:59:21 -08:00
parent 5f5da23a40
commit b497f7796d
27 changed files with 2664 additions and 76 deletions

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Filament\Resources\PlanTierResource\Pages;
use App\Filament\Resources\PlanTierResource;
use Filament\Resources\Pages\CreateRecord;
class CreatePlanTier extends CreateRecord
{
protected static string $resource = PlanTierResource::class;
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Filament\Resources\PlanTierResource\Pages;
use App\Filament\Resources\PlanTierResource;
use App\Models\PlanTier;
use Filament\Actions;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
class EditPlanTier extends EditRecord
{
protected static string $resource = PlanTierResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make()
->before(function (PlanTier $record) {
// Prevent deletion if tier has child tiers or plans
if ($record->childTiers()->exists()) {
throw new \Exception('Cannot delete tier that has child tiers');
}
if ($record->plans()->exists()) {
throw new \Exception('Cannot delete tier that has plans assigned');
}
}),
];
}
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
protected function getSavedNotification(): ?Notification
{
return Notification::make()
->success()
->title('Tier updated')
->body('Plan tier updated successfully');
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\PlanTierResource\Pages;
use App\Filament\Resources\PlanTierResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListPlanTiers extends ListRecords
{
protected static string $resource = PlanTierResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}