- 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
238 lines
6.4 KiB
PHP
238 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Dashboard;
|
|
|
|
use App\Models\PlanUsage;
|
|
use App\Models\TrialExtension;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Livewire\Component;
|
|
|
|
class SubscriptionDashboard extends Component
|
|
{
|
|
public $subscription;
|
|
|
|
public $plan;
|
|
|
|
public $usageData;
|
|
|
|
public $upgradePaths;
|
|
|
|
public $trialExtensions;
|
|
|
|
public $loading = true;
|
|
|
|
public function mount(): void
|
|
{
|
|
$this->loadSubscriptionData();
|
|
}
|
|
|
|
public function loadSubscriptionData(): void
|
|
{
|
|
$this->loading = true;
|
|
|
|
$user = Auth::user();
|
|
$this->subscription = $user->subscription('default');
|
|
|
|
if ($this->subscription) {
|
|
$this->plan = $this->subscription->plan->load([
|
|
'planFeatureLimits.planFeature',
|
|
'trialConfiguration',
|
|
'planTier',
|
|
]);
|
|
|
|
$this->loadUsageData();
|
|
$this->loadUpgradePaths();
|
|
$this->loadTrialExtensions();
|
|
}
|
|
|
|
$this->loading = false;
|
|
}
|
|
|
|
private function loadUsageData(): void
|
|
{
|
|
if (! $this->subscription) {
|
|
return;
|
|
}
|
|
|
|
$this->usageData = PlanUsage::where('user_id', Auth::id())
|
|
->where('plan_id', $this->subscription->plan_id)
|
|
->with('planFeature')
|
|
->get()
|
|
->map(function ($usage) {
|
|
$limit = $this->plan->planFeatureLimits
|
|
->where('plan_feature_id', $usage->plan_feature_id)
|
|
->first();
|
|
|
|
return [
|
|
'feature' => $usage->planFeature,
|
|
'usage' => $usage->usage_value,
|
|
'period' => $usage->period_type,
|
|
'limit' => $limit,
|
|
'remaining' => $limit ? $limit->getRemainingUsage($usage->usage_value, $this->subscription->onTrial()) : 0,
|
|
'percentage_used' => $limit && $limit->limit_value ?
|
|
min(100, ($usage->usage_value / $limit->limit_value) * 100) : 0,
|
|
];
|
|
});
|
|
}
|
|
|
|
private function loadUpgradePaths(): void
|
|
{
|
|
if (! $this->subscription) {
|
|
return;
|
|
}
|
|
|
|
$this->upgradePaths = $this->plan->getUpgradePath();
|
|
}
|
|
|
|
private function loadTrialExtensions(): void
|
|
{
|
|
if (! $this->subscription) {
|
|
return;
|
|
}
|
|
|
|
$this->trialExtensions = TrialExtension::where('subscription_id', $this->subscription->id)
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
}
|
|
|
|
public function requestTrialExtension(): void
|
|
{
|
|
if (! $this->subscription || ! $this->subscription->onTrial()) {
|
|
session()->flash('error', 'You can only request extensions for active trials.');
|
|
|
|
return;
|
|
}
|
|
|
|
$trialConfig = $this->plan->getTrialConfig();
|
|
if (! $trialConfig) {
|
|
session()->flash('error', 'This plan does not support trial extensions.');
|
|
|
|
return;
|
|
}
|
|
|
|
$currentExtensions = $this->trialExtensions->count();
|
|
if (! $trialConfig->canExtendTrial($currentExtensions)) {
|
|
session()->flash('error', 'You have reached the maximum number of trial extensions.');
|
|
|
|
return;
|
|
}
|
|
|
|
// Create trial extension request
|
|
TrialExtension::create([
|
|
'subscription_id' => $this->subscription->id,
|
|
'user_id' => Auth::id(),
|
|
'original_ends_at' => $this->subscription->trial_ends_at,
|
|
'extension_days' => $trialConfig->trial_duration_days,
|
|
'status' => 'pending',
|
|
'reason' => 'User requested extension via dashboard',
|
|
]);
|
|
|
|
session()->flash('success', 'Trial extension request submitted successfully.');
|
|
$this->loadTrialExtensions();
|
|
}
|
|
|
|
public function cancelSubscription(): void
|
|
{
|
|
if (! $this->subscription) {
|
|
return;
|
|
}
|
|
|
|
$this->subscription->cancel();
|
|
session()->flash('success', 'Subscription cancelled successfully.');
|
|
$this->loadSubscriptionData();
|
|
}
|
|
|
|
public function pauseSubscription(): void
|
|
{
|
|
if (! $this->subscription) {
|
|
return;
|
|
}
|
|
|
|
// Implement pause logic based on your business requirements
|
|
session()->flash('info', 'Pause functionality coming soon.');
|
|
}
|
|
|
|
public function resumeSubscription(): void
|
|
{
|
|
if (! $this->subscription) {
|
|
return;
|
|
}
|
|
|
|
$this->subscription->resume();
|
|
session()->flash('success', 'Subscription resumed successfully.');
|
|
$this->loadSubscriptionData();
|
|
}
|
|
|
|
public function getSubscriptionStatus(): string
|
|
{
|
|
if (! $this->subscription) {
|
|
return 'No Subscription';
|
|
}
|
|
|
|
return match ($this->subscription->status) {
|
|
'active' => 'Active',
|
|
'trialing' => 'Trial',
|
|
'cancelled' => 'Cancelled',
|
|
'past_due' => 'Past Due',
|
|
'unpaid' => 'Unpaid',
|
|
default => 'Unknown',
|
|
};
|
|
}
|
|
|
|
public function getSubscriptionStatusColor(): string
|
|
{
|
|
if (! $this->subscription) {
|
|
return 'gray';
|
|
}
|
|
|
|
return match ($this->subscription->status) {
|
|
'active' => 'green',
|
|
'trialing' => 'blue',
|
|
'cancelled' => 'red',
|
|
'past_due' => 'yellow',
|
|
'unpaid' => 'red',
|
|
default => 'gray',
|
|
};
|
|
}
|
|
|
|
public function getDaysRemaining(): int
|
|
{
|
|
if (! $this->subscription) {
|
|
return 0;
|
|
}
|
|
|
|
$endsAt = $this->subscription->trial_ends_at ?? $this->subscription->ends_at;
|
|
|
|
return $endsAt ? max(0, $endsAt->diffInDays(now())) : 0;
|
|
}
|
|
|
|
public function getNextBillingDate(): string
|
|
{
|
|
if (! $this->subscription || $this->subscription->cancelled()) {
|
|
return 'N/A';
|
|
}
|
|
|
|
return $this->subscription->ends_at?->format('M j, Y') ?? 'N/A';
|
|
}
|
|
|
|
public function getUsagePercentage($usageData): int
|
|
{
|
|
return (int) round($usageData['percentage_used']);
|
|
}
|
|
|
|
public function getUsageColor($percentage): string
|
|
{
|
|
return match (true) {
|
|
$percentage >= 90 => 'red',
|
|
$percentage >= 75 => 'yellow',
|
|
$percentage >= 50 => 'blue',
|
|
default => 'green',
|
|
};
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.dashboard.subscription-dashboard');
|
|
}
|
|
}
|