- Add comprehensive feature limits enforcement middleware - Implement subscription dashboard with usage analytics - Create reusable plan card component with feature badges - Add trial configuration support with limit overrides - Fix payment controller null safety issues - Improve pricing page UI with proper feature display
148 lines
7.4 KiB
PHP
148 lines
7.4 KiB
PHP
<div class="rounded-2xl border dark:border-white/[0.1] border-black/[0.3]p-6 shadow-xs ring-1 ring-white/[0.5] sm:px-8 lg:p-12 relative @if($plan->planTier && $plan->planTier->sort_order > 1) dark:ring-blue-400 @endif">
|
|
|
|
@if($plan->planTier && $plan->planTier->sort_order > 1)
|
|
<div class="absolute -top-4 left-1/2 transform -translate-x-1/2">
|
|
<flux:badge variant="solid" size="sm" color="blue">Most Popular</flux:badge>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="text-center">
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-gray-100">{{ $plan->name }}</h2>
|
|
@if($plan->planTier)
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">{{ $plan->planTier->name }} Tier</p>
|
|
@endif
|
|
|
|
<p class="mt-4 sm:mt-6">
|
|
<strong class="text-4xl font-bold text-gray-900 dark:text-gray-100">${{ number_format($plan->price, 2) }}</strong>
|
|
<span class="text-sm font-medium text-gray-700 dark:text-gray-400">/{{ $billingCycle }}</span>
|
|
</p>
|
|
|
|
@if($plan->description)
|
|
<p class="mt-3 text-sm text-gray-600 dark:text-gray-400">{{ $plan->description }}</p>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Features List -->
|
|
@if($features)
|
|
<ul class="mt-6 space-y-3">
|
|
@foreach($features as $featureData)
|
|
<li class="flex items-start gap-2">
|
|
<flux:icon.check-circle class="w-5 h-5 text-green-500 mt-0.5 flex-shrink-0" />
|
|
<div>
|
|
<span class="text-gray-700 dark:text-gray-300 font-medium">
|
|
{{ $featureData['feature']['display_name'] ?? 'Unknown Feature' }}
|
|
</span>
|
|
|
|
{{-- Simple badge display --}}
|
|
@if(isset($featureData['limit']['limit_value']))
|
|
@if($featureData['limit']['limit_value'] === null)
|
|
<flux:badge variant="outline" size="sm" color="purple" class="ml-2">Unlimited</flux:badge>
|
|
@else
|
|
@php
|
|
$limitValue = (int) $featureData['limit']['limit_value'];
|
|
$limitType = $featureData['limit']['limit_type'] ?? '';
|
|
$suffix = '';
|
|
|
|
if ($limitType === 'monthly') {
|
|
$suffix = '/month';
|
|
} elseif ($limitType === 'daily') {
|
|
$suffix = '/day';
|
|
} elseif ($limitType === 'weekly') {
|
|
$suffix = '/week';
|
|
} elseif ($limitType === 'yearly') {
|
|
$suffix = '/year';
|
|
}
|
|
// Don't show suffix for 'total' or empty
|
|
@endphp
|
|
<flux:badge variant="outline" size="sm" color="blue" class="ml-2">
|
|
{{ $limitValue }}{{ $suffix }}
|
|
</flux:badge>
|
|
@endif
|
|
@endif
|
|
|
|
{{-- Trial limit badge --}}
|
|
@if($hasTrial && isset($featureData['limit']['trial_limit_value']) && $featureData['limit']['trial_limit_value'] > 0 && isset($featureData['limit']['applies_during_trial']) && $featureData['limit']['applies_during_trial'])
|
|
@php
|
|
$trialValue = (int) $featureData['limit']['trial_limit_value'];
|
|
$trialSuffix = '';
|
|
|
|
if (isset($featureData['limit']['limit_type'])) {
|
|
$trialType = $featureData['limit']['limit_type'];
|
|
if ($trialType === 'monthly') {
|
|
$trialSuffix = '/month';
|
|
} elseif ($trialType === 'daily') {
|
|
$trialSuffix = '/day';
|
|
} elseif ($trialType === 'weekly') {
|
|
$trialSuffix = '/week';
|
|
} elseif ($trialType === 'yearly') {
|
|
$trialSuffix = '/year';
|
|
}
|
|
// Don't show suffix for 'total' or empty
|
|
}
|
|
@endphp
|
|
<flux:badge variant="outline" size="sm" color="yellow" class="mt-1">
|
|
Trial: {{ $trialValue }}{{ $trialSuffix }}
|
|
</flux:badge>
|
|
@endif
|
|
</div>
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
@else
|
|
<div class="mt-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 text-center">
|
|
No features configured for this plan.
|
|
</p>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Trial Information -->
|
|
@if($hasTrial && $trialConfig)
|
|
<div class="mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800">
|
|
<div class="flex items-center gap-2 text-blue-700 dark:text-blue-300">
|
|
<flux:icon.clock class="w-4 h-4" />
|
|
<span class="text-sm font-medium">{{ $trialConfig['duration_days'] }}-day free trial</span>
|
|
</div>
|
|
@if($trialConfig['requires_payment_method'])
|
|
<p class="text-xs text-blue-600 dark:text-blue-400 mt-1">Payment method required</p>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Payment Provider Buttons -->
|
|
<div class="mt-6 space-y-2">
|
|
@foreach($providers as $provider)
|
|
@if($provider === 'stripe')
|
|
@if($hasTrial && $trialConfig)
|
|
<flux:button variant="primary" class="w-full" wire:click="startTrial({{ $plan->id }}, '{{ $provider }}')">
|
|
Start Free Trial
|
|
</flux:button>
|
|
@endif
|
|
<flux:button variant="{{ $hasTrial && $trialConfig ? 'outline' : 'primary' }}" class="w-full" wire:click="choosePlan({{ $plan->id }}, '{{ $provider }}')">
|
|
Pay with Card
|
|
</flux:button>
|
|
@elseif($provider === 'lemon_squeezy')
|
|
<flux:button variant="filled" class="w-full" wire:click="choosePlan({{ $plan->id }}, '{{ $provider }}')">
|
|
Pay with Lemon Squeezy
|
|
</flux:button>
|
|
@elseif($provider === 'polar')
|
|
<flux:button variant="filled" class="w-full" wire:click="choosePlan({{ $plan->id }}, '{{ $provider }}')">
|
|
Pay with Polar.sh
|
|
</flux:button>
|
|
@elseif($provider === 'oxapay')
|
|
<flux:button variant="filled" class="w-full" wire:click="choosePlan({{ $plan->id }}, '{{ $provider }}')">
|
|
Pay with OxaPay
|
|
</flux:button>
|
|
@elseif($provider === 'crypto')
|
|
<flux:button variant="filled" class="w-full" wire:click="choosePlan({{ $plan->id }}, '{{ $provider }}')">
|
|
Pay with Crypto
|
|
</flux:button>
|
|
@elseif($provider === 'activation_key')
|
|
<flux:button variant="outline" class="w-full" disabled>
|
|
Activate via Activation Key
|
|
</flux:button>
|
|
@endif
|
|
@endforeach
|
|
</div>
|
|
</div>
|