fix(plans): prevent deletion of plans with active subscriptions
- Fix bulk delete and individual delete actions using before() hook with halt() - Add daily/weekly billing cycle options to plan resource and Polar provider - Enhance payment confirmation with dynamic polling and loading states - Add graceful handling for deleted plans in subscription display - Update Polar provider to support dynamic billing cycles
This commit is contained in:
@@ -56,11 +56,13 @@ class PlanResource extends Resource
|
|||||||
Select::make('billing_cycle_days')
|
Select::make('billing_cycle_days')
|
||||||
->label('Billing Cycle')
|
->label('Billing Cycle')
|
||||||
->options([
|
->options([
|
||||||
|
1 => 'Daily',
|
||||||
|
7 => 'Weekly',
|
||||||
30 => 'Monthly',
|
30 => 'Monthly',
|
||||||
90 => 'Quarterly',
|
|
||||||
365 => 'Yearly',
|
|
||||||
60 => 'Bi-Monthly',
|
60 => 'Bi-Monthly',
|
||||||
|
90 => 'Quarterly',
|
||||||
180 => 'Semi-Annual',
|
180 => 'Semi-Annual',
|
||||||
|
365 => 'Yearly',
|
||||||
])
|
])
|
||||||
->default(30)
|
->default(30)
|
||||||
->required(),
|
->required(),
|
||||||
@@ -225,8 +227,12 @@ class PlanResource extends Resource
|
|||||||
Tables\Filters\SelectFilter::make('billing_cycle_days')
|
Tables\Filters\SelectFilter::make('billing_cycle_days')
|
||||||
->label('Billing Cycle')
|
->label('Billing Cycle')
|
||||||
->options([
|
->options([
|
||||||
|
1 => 'Daily',
|
||||||
|
7 => 'Weekly',
|
||||||
30 => 'Monthly',
|
30 => 'Monthly',
|
||||||
|
60 => 'Bi-Monthly',
|
||||||
90 => 'Quarterly',
|
90 => 'Quarterly',
|
||||||
|
180 => 'Semi-Annual',
|
||||||
365 => 'Yearly',
|
365 => 'Yearly',
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@@ -239,22 +245,76 @@ class PlanResource extends Resource
|
|||||||
ViewAction::make(),
|
ViewAction::make(),
|
||||||
EditAction::make(),
|
EditAction::make(),
|
||||||
DeleteAction::make()
|
DeleteAction::make()
|
||||||
->before(function (Plan $record) {
|
->requiresConfirmation()
|
||||||
// Prevent deletion if plan has active subscriptions
|
->before(function (DeleteAction $action, Plan $record) {
|
||||||
if ($record->subscriptions()->where('status', 'active')->exists()) {
|
// Prevent deletion if plan has any subscriptions (active, cancelled, etc.)
|
||||||
Log::error('Cannot delete plan with active subscriptions');
|
if ($record->subscriptions()->exists()) {
|
||||||
|
Log::warning('Attempted to delete plan with existing subscriptions', [
|
||||||
|
'plan_id' => $record->id,
|
||||||
|
'plan_name' => $record->name,
|
||||||
|
'subscription_count' => $record->subscriptions()->count(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Show warning notification
|
||||||
|
\Filament\Notifications\Notification::make()
|
||||||
|
->warning()
|
||||||
|
->title('Cannot Delete Plan')
|
||||||
|
->body("Plan '{$record->name}' has {$record->subscriptions()->count()} subscription(s). Please cancel or remove all subscriptions first.")
|
||||||
|
->persistent()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
// Halt the deletion process
|
||||||
|
$action->halt();
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
->action(function (Plan $record) {
|
||||||
|
// This action will only run if not halted
|
||||||
|
$record->delete();
|
||||||
|
|
||||||
|
// Show success notification
|
||||||
|
\Filament\Notifications\Notification::make()
|
||||||
|
->success()
|
||||||
|
->title('Plan Deleted')
|
||||||
|
->body("Plan '{$record->name}' has been deleted successfully.")
|
||||||
|
->send();
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
->toolbarActions([
|
->bulkActions([
|
||||||
BulkActionGroup::make([
|
BulkActionGroup::make([
|
||||||
DeleteBulkAction::make()
|
DeleteBulkAction::make()
|
||||||
->before(function ($records) {
|
->requiresConfirmation()
|
||||||
|
->before(function (DeleteBulkAction $action, $records) {
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
if ($record->subscriptions()->where('status', 'active')->exists()) {
|
if ($record->subscriptions()->exists()) {
|
||||||
Log::error('Cannot delete plan(s) with active subscriptions');
|
Log::warning('Attempted to bulk delete plan with existing subscriptions', [
|
||||||
|
'plan_id' => $record->id,
|
||||||
|
'plan_name' => $record->name,
|
||||||
|
'subscription_count' => $record->subscriptions()->count(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
\Filament\Notifications\Notification::make()
|
||||||
|
->warning()
|
||||||
|
->title('Cannot Delete Plans')
|
||||||
|
->body("Plan '{$record->name}' has {$record->subscriptions()->count()} subscription(s). Please cancel or remove all subscriptions first.")
|
||||||
|
->persistent()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
// Halt the bulk deletion process
|
||||||
|
$action->halt();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
->action(function ($records) {
|
||||||
|
foreach ($records as $record) {
|
||||||
|
$record->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
\Filament\Notifications\Notification::make()
|
||||||
|
->success()
|
||||||
|
->title('Plans Deleted')
|
||||||
|
->body(count($records).' plan(s) have been deleted successfully.')
|
||||||
|
->send();
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -62,12 +62,15 @@ class EditPlan extends EditRecord
|
|||||||
Select::make('billing_cycle_days')
|
Select::make('billing_cycle_days')
|
||||||
->label('Billing Cycle')
|
->label('Billing Cycle')
|
||||||
->options([
|
->options([
|
||||||
|
1 => 'Daily',
|
||||||
|
7 => 'Weekly',
|
||||||
30 => 'Monthly',
|
30 => 'Monthly',
|
||||||
90 => 'Quarterly',
|
|
||||||
365 => 'Yearly',
|
|
||||||
60 => 'Bi-Monthly',
|
60 => 'Bi-Monthly',
|
||||||
|
90 => 'Quarterly',
|
||||||
180 => 'Semi-Annual',
|
180 => 'Semi-Annual',
|
||||||
|
365 => 'Yearly',
|
||||||
])
|
])
|
||||||
|
->default(30)
|
||||||
->required(),
|
->required(),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Models\Subscription;
|
use App\Models\Subscription;
|
||||||
use App\Services\Payments\PaymentOrchestrator;
|
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@@ -25,6 +24,8 @@ class PaymentConfirmation extends Component
|
|||||||
|
|
||||||
public $errorMessage = null;
|
public $errorMessage = null;
|
||||||
|
|
||||||
|
public $isChecking = false;
|
||||||
|
|
||||||
protected $listeners = ['$refresh'];
|
protected $listeners = ['$refresh'];
|
||||||
|
|
||||||
public function mount($subscription = null, $sessionToken = null): void
|
public function mount($subscription = null, $sessionToken = null): void
|
||||||
@@ -100,63 +101,41 @@ class PaymentConfirmation extends Component
|
|||||||
$this->redirect(route('dashboard'));
|
$this->redirect(route('dashboard'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->isChecking = false;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set loading state
|
||||||
|
$this->isChecking = true;
|
||||||
|
|
||||||
// Increment poll count first
|
// Increment poll count first
|
||||||
$this->pollCount++;
|
$this->pollCount++;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$orchestrator = app(PaymentOrchestrator::class);
|
Log::info('PaymentConfirmation: Syncing subscription with provider', [
|
||||||
$user = auth()->user();
|
|
||||||
|
|
||||||
Log::info('PaymentConfirmation: Checking subscription status', [
|
|
||||||
'subscription_id' => $this->subscription->id,
|
'subscription_id' => $this->subscription->id,
|
||||||
'provider_subscription_id' => $this->subscription->provider_subscription_id,
|
'provider_subscription_id' => $this->subscription->provider_subscription_id,
|
||||||
'provider' => $this->subscription->provider,
|
'provider' => $this->subscription->provider,
|
||||||
'poll_count' => $this->pollCount,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Check status via provider (only if we have a provider subscription ID)
|
|
||||||
if (empty($this->subscription->provider_subscription_id)) {
|
|
||||||
Log::info('PaymentConfirmation: Skipping provider status check - no provider subscription ID yet', [
|
|
||||||
'subscription_id' => $this->subscription->id,
|
|
||||||
'provider' => $this->subscription->provider,
|
|
||||||
'provider_checkout_id' => $this->subscription->provider_checkout_id,
|
'provider_checkout_id' => $this->subscription->provider_checkout_id,
|
||||||
'poll_count' => $this->pollCount,
|
'poll_count' => $this->pollCount,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Don't update status, just continue polling
|
// Use the same sync method as the billing page
|
||||||
return;
|
$syncSuccess = $this->subscription->syncWithProvider();
|
||||||
}
|
|
||||||
|
|
||||||
$statusResult = $orchestrator->checkSubscriptionStatus(
|
// Refresh the subscription from database to get updated status
|
||||||
$user,
|
$this->subscription->refresh();
|
||||||
$this->subscription->provider,
|
|
||||||
$this->subscription->provider_subscription_id
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($statusResult['success']) {
|
Log::info('PaymentConfirmation: Subscription sync completed', [
|
||||||
$providerStatus = $statusResult['status'];
|
|
||||||
|
|
||||||
Log::info('PaymentConfirmation: Provider status received', [
|
|
||||||
'provider_status' => $providerStatus,
|
|
||||||
'subscription_id' => $this->subscription->id,
|
'subscription_id' => $this->subscription->id,
|
||||||
|
'sync_success' => $syncSuccess,
|
||||||
|
'current_status' => $this->subscription->status,
|
||||||
|
'provider_subscription_id' => $this->subscription->provider_subscription_id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Update local subscription if status changed
|
|
||||||
if ($providerStatus !== $this->subscription->status) {
|
|
||||||
$this->subscription->status = $providerStatus;
|
|
||||||
$this->subscription->save();
|
|
||||||
|
|
||||||
Log::info('PaymentConfirmation: Updated local subscription status', [
|
|
||||||
'old_status' => $this->subscription->getOriginal('status'),
|
|
||||||
'new_status' => $providerStatus,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if subscription is now active
|
// Check if subscription is now active
|
||||||
if ($providerStatus === 'active') {
|
if (in_array($this->subscription->status, ['active', 'trialing'])) {
|
||||||
$this->status = 'activated';
|
$this->status = 'activated';
|
||||||
$this->showConfetti = true;
|
$this->showConfetti = true;
|
||||||
|
|
||||||
@@ -165,30 +144,17 @@ class PaymentConfirmation extends Component
|
|||||||
|
|
||||||
Log::info('PaymentConfirmation: Subscription activated successfully', [
|
Log::info('PaymentConfirmation: Subscription activated successfully', [
|
||||||
'subscription_id' => $this->subscription->id,
|
'subscription_id' => $this->subscription->id,
|
||||||
|
'final_status' => $this->subscription->status,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Handle status check failure
|
|
||||||
Log::warning('PaymentConfirmation: Provider status check failed', [
|
|
||||||
'error' => $statusResult['error'] ?? 'Unknown error',
|
|
||||||
'subscription_id' => $this->subscription->id,
|
|
||||||
'retry_suggested' => $statusResult['retry_suggested'] ?? false,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// If retry is suggested (e.g., webhook not processed yet), continue polling
|
|
||||||
if (! ($statusResult['retry_suggested'] ?? false)) {
|
|
||||||
// If retry is not suggested, we might want to show an error
|
|
||||||
// but for now, continue polling to be safe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue polling if not active and max polls not reached
|
// Continue polling if not active and max polls not reached
|
||||||
if ($this->pollCount < $this->maxPolls) {
|
if ($this->pollCount < $this->maxPolls) {
|
||||||
$this->status = 'verifying';
|
$this->status = 'verifying';
|
||||||
} else {
|
} else {
|
||||||
// Max polls reached, check final status
|
// Max polls reached, determine final status
|
||||||
$this->status = in_array($this->subscription->status, ['active', 'trialing'])
|
$this->status = in_array($this->subscription->status, ['active', 'trialing'])
|
||||||
? 'activated'
|
? 'activated'
|
||||||
: 'pending';
|
: 'pending';
|
||||||
@@ -200,7 +166,7 @@ class PaymentConfirmation extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error('PaymentConfirmation: Error checking subscription status', [
|
Log::error('PaymentConfirmation: Error syncing subscription', [
|
||||||
'subscription_id' => $this->subscription->id,
|
'subscription_id' => $this->subscription->id,
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
'poll_count' => $this->pollCount,
|
'poll_count' => $this->pollCount,
|
||||||
@@ -211,6 +177,9 @@ class PaymentConfirmation extends Component
|
|||||||
$this->errorMessage = 'Unable to verify payment status after multiple attempts. Please check your subscription page.';
|
$this->errorMessage = 'Unable to verify payment status after multiple attempts. Please check your subscription page.';
|
||||||
$this->status = 'error';
|
$this->status = 'error';
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
// Always reset loading state
|
||||||
|
$this->isChecking = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -690,4 +690,59 @@ class Subscription extends Model
|
|||||||
// Fallback to legacy monthly_billing
|
// Fallback to legacy monthly_billing
|
||||||
return $this->plan && $this->plan->monthly_billing ? 30 : 365;
|
return $this->plan && $this->plan->monthly_billing ? 30 : 365;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get plan display name, handles deleted plans gracefully
|
||||||
|
*/
|
||||||
|
public function getPlanDisplayName(): string
|
||||||
|
{
|
||||||
|
if ($this->plan) {
|
||||||
|
return $this->plan->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check provider data for stored plan name
|
||||||
|
if ($this->provider_data) {
|
||||||
|
// Check for Polar plan name
|
||||||
|
if (isset($this->provider_data['polar_subscription']['product']['name'])) {
|
||||||
|
return $this->provider_data['polar_subscription']['product']['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for stored plan details
|
||||||
|
if (isset($this->provider_data['plan_details']['name'])) {
|
||||||
|
return $this->provider_data['plan_details']['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check metadata
|
||||||
|
if (isset($this->provider_data['plan_name'])) {
|
||||||
|
return $this->provider_data['plan_name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Deleted Plan';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get plan price, handles deleted plans gracefully
|
||||||
|
*/
|
||||||
|
public function getPlanPrice(): float
|
||||||
|
{
|
||||||
|
if ($this->plan) {
|
||||||
|
return $this->plan->price;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check provider data for stored plan price
|
||||||
|
if ($this->provider_data) {
|
||||||
|
// Check for Polar subscription amount
|
||||||
|
if (isset($this->provider_data['polar_subscription']['amount'])) {
|
||||||
|
return $this->provider_data['polar_subscription']['amount'] / 100; // Convert from cents
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for stored plan details
|
||||||
|
if (isset($this->provider_data['plan_details']['price'])) {
|
||||||
|
return (float) $this->provider_data['plan_details']['price'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1106,24 +1106,28 @@ class PolarProvider implements PaymentProviderContract
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert billing cycle to Polar's format
|
||||||
|
$billingInterval = $this->convertBillingCycleToPolarInterval($plan->billing_cycle_days ?? 30);
|
||||||
|
|
||||||
// Create new product with correct structure
|
// Create new product with correct structure
|
||||||
$productData = [
|
$productData = [
|
||||||
'name' => $plan->name,
|
'name' => $plan->name,
|
||||||
'description' => $plan->description ?? 'Subscription plan',
|
'description' => $plan->description ?? 'Subscription plan',
|
||||||
'recurring_interval' => 'month',
|
'recurring_interval' => $billingInterval['interval'],
|
||||||
'recurring_interval_count' => 1,
|
'recurring_interval_count' => $billingInterval['interval_count'],
|
||||||
'prices' => [
|
'prices' => [
|
||||||
[
|
[
|
||||||
'amount_type' => 'fixed',
|
'amount_type' => 'fixed',
|
||||||
'price_amount' => (int) ($plan->price * 100), // Convert to cents
|
'price_amount' => (int) ($plan->price * 100), // Convert to cents
|
||||||
'price_currency' => 'usd',
|
'price_currency' => 'usd',
|
||||||
'recurring_interval' => 'month',
|
'recurring_interval' => $billingInterval['interval'],
|
||||||
'recurring_interval_count' => 1,
|
'recurring_interval_count' => $billingInterval['interval_count'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'plan_id' => $plan->id,
|
'plan_id' => $plan->id,
|
||||||
'plan_name' => $plan->name,
|
'plan_name' => $plan->name,
|
||||||
|
'billing_cycle_days' => $plan->billing_cycle_days ?? 30,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -2361,4 +2365,21 @@ class PolarProvider implements PaymentProviderContract
|
|||||||
// For now, return basic stats - could be expanded with database tracking
|
// For now, return basic stats - could be expanded with database tracking
|
||||||
return $stats;
|
return $stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert billing cycle days to Polar's recurring interval format
|
||||||
|
*/
|
||||||
|
protected function convertBillingCycleToPolarInterval(int $billingCycleDays): array
|
||||||
|
{
|
||||||
|
return match ($billingCycleDays) {
|
||||||
|
1 => ['interval' => 'day', 'interval_count' => 1], // Daily
|
||||||
|
7 => ['interval' => 'week', 'interval_count' => 1], // Weekly
|
||||||
|
30 => ['interval' => 'month', 'interval_count' => 1], // Monthly
|
||||||
|
60 => ['interval' => 'month', 'interval_count' => 2], // Bi-monthly
|
||||||
|
90 => ['interval' => 'month', 'interval_count' => 3], // Quarterly
|
||||||
|
180 => ['interval' => 'month', 'interval_count' => 6], // Semi-annual
|
||||||
|
365 => ['interval' => 'year', 'interval_count' => 1], // Yearly
|
||||||
|
default => ['interval' => 'month', 'interval_count' => 1], // Default to monthly
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,32 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-500">
|
<p class="text-sm text-gray-500 dark:text-gray-500">
|
||||||
Check {{ $pollCount }} of {{ $maxPolls }} • Retrying in 30 seconds...
|
Check {{ $pollCount }} of {{ $maxPolls }} • Checking every 30 seconds...
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@if ($pollCount > 1)
|
||||||
|
<p class="text-xs text-gray-400 dark:text-gray-600 mt-1">
|
||||||
|
This is taking longer than expected. Webhook processing may be delayed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<button wire:click="checkSubscriptionStatus"
|
||||||
|
wire:loading.attr="disabled"
|
||||||
|
class="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||||
|
@if ($isChecking)
|
||||||
|
<svg class="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||||||
|
</svg>
|
||||||
|
Checking...
|
||||||
|
@else
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||||||
|
</svg>
|
||||||
|
Check Now
|
||||||
|
@endif
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@@ -198,11 +222,11 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- Polling Indicator - temporarily disabled -->
|
<!-- Polling Indicator -->
|
||||||
@if ($this->shouldContinuePolling)
|
@if ($this->shouldContinuePolling)
|
||||||
<!-- <div wire:poll.30000ms
|
<div wire:poll.30000ms="checkSubscriptionStatus"
|
||||||
wire:key="subscription-poll-{{ $subscription->id ?? 'none' }}-{{ $pollCount }}">
|
wire:key="subscription-poll-{{ $subscription->id ?? 'none' }}-{{ $pollCount }}">
|
||||||
</div> -->
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<div class="flex items-start justify-between">
|
<div class="flex items-start justify-between">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<flux:heading level="3">{{ $latestActiveSubscription->plan->name }}</flux:heading>
|
<flux:heading level="3">{{ $latestActiveSubscription->getPlanDisplayName() }}</flux:heading>
|
||||||
<flux:badge variant="success">
|
<flux:badge variant="success">
|
||||||
{{ ucfirst($latestActiveSubscription->status) }}
|
{{ ucfirst($latestActiveSubscription->status) }}
|
||||||
</flux:badge>
|
</flux:badge>
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<flux:text class="mt-2 text-gray-600 dark:text-gray-400">
|
<flux:text class="mt-2 text-gray-600 dark:text-gray-400">
|
||||||
{{ $latestActiveSubscription->plan->description ?? 'Subscription plan' }}
|
{{ $latestActiveSubscription->plan?->description ?? 'Subscription plan' }}
|
||||||
</flux:text>
|
</flux:text>
|
||||||
|
|
||||||
@if($latestActiveSubscription->isActive())
|
@if($latestActiveSubscription->isActive())
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
<div class="flex items-start justify-between">
|
<div class="flex items-start justify-between">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<flux:heading level="4">{{ $subscription->plan->name }}</flux:heading>
|
<flux:heading level="4">{{ $subscription->getPlanDisplayName() }}</flux:heading>
|
||||||
<flux:badge
|
<flux:badge
|
||||||
variant="{{ $subscription->isActive() ? 'success' : ($subscription->isCancelled() ? 'danger' : 'warning') }}"
|
variant="{{ $subscription->isActive() ? 'success' : ($subscription->isCancelled() ? 'danger' : 'warning') }}"
|
||||||
>
|
>
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
|
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<flux:text class="text-lg font-semibold">
|
<flux:text class="text-lg font-semibold">
|
||||||
${{ number_format($subscription->plan->price, 2) }}
|
${{ number_format($subscription->getPlanPrice(), 2) }}
|
||||||
</flux:text>
|
</flux:text>
|
||||||
|
|
||||||
@if($isWithin30Minutes && $subscription->id === $latestSubscription->id)
|
@if($isWithin30Minutes && $subscription->id === $latestSubscription->id)
|
||||||
@@ -232,7 +232,7 @@
|
|||||||
|
|
||||||
@if($subscriptionToCancel)
|
@if($subscriptionToCancel)
|
||||||
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-md">
|
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-md">
|
||||||
<flux:text class="font-medium">{{ $subscriptionToCancel->plan->name }}</flux:text>
|
<flux:text class="font-medium">{{ $subscriptionToCancel->getPlanDisplayName() }}</flux:text>
|
||||||
<flux:text class="text-sm text-gray-600 dark:text-gray-400">
|
<flux:text class="text-sm text-gray-600 dark:text-gray-400">
|
||||||
{{ $subscriptionToCancel->getProviderDisplayName() }}
|
{{ $subscriptionToCancel->getProviderDisplayName() }}
|
||||||
</flux:text>
|
</flux:text>
|
||||||
|
|||||||
Reference in New Issue
Block a user