Update pricing UX, billing flows, and API rules
This commit is contained in:
127
app/resources/views/dashboard/user/billing.blade.php
Normal file
127
app/resources/views/dashboard/user/billing.blade.php
Normal file
@@ -0,0 +1,127 @@
|
||||
@extends('dashboard.app')
|
||||
|
||||
@section('title', 'Billing')
|
||||
@section('page_title', 'Billing')
|
||||
@section('page_subtitle', 'Subscription status and plan details.')
|
||||
|
||||
@section('dashboard_content')
|
||||
@php
|
||||
$user = $user ?? auth()->user();
|
||||
$subscription = $subscription ?? null;
|
||||
$hasSub = $subscription !== null;
|
||||
$orders = $orders ?? collect();
|
||||
$payments = $payments ?? collect();
|
||||
@endphp
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-3">
|
||||
<div class="rounded-3xl glass-card p-6">
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Plan</div>
|
||||
<div class="mt-3 text-3xl font-semibold text-white">{{ $hasSub ? ucfirst($subscription->plan ?? 'Personal') : 'Free' }}</div>
|
||||
<div class="mt-2 text-sm text-gray-400">{{ $hasSub ? ucfirst($subscription->status ?? 'active') : 'No subscription' }}</div>
|
||||
</div>
|
||||
<div class="rounded-3xl glass-card p-6">
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Renewal</div>
|
||||
<div class="mt-3 text-2xl font-semibold text-white">
|
||||
{{ $subscription?->next_renewal_at?->toDateString() ?? '—' }}
|
||||
</div>
|
||||
<div class="mt-2 text-sm text-gray-400">Next charge date</div>
|
||||
</div>
|
||||
<div class="rounded-3xl glass-card p-6">
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Status</div>
|
||||
<div class="mt-3 text-2xl font-semibold text-white">
|
||||
{{ $subscription?->expires_at?->toDateString() ?? 'Active' }}
|
||||
</div>
|
||||
<div class="mt-2 text-sm text-gray-400">Access ends</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 rounded-3xl glass-card p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Billing summary</div>
|
||||
<div class="mt-2 text-xl font-semibold text-white">Subscription details</div>
|
||||
</div>
|
||||
<span class="rounded-full border border-white/10 px-3 py-1 text-xs text-gray-300 theme-surface">Current cycle</span>
|
||||
</div>
|
||||
<div class="mt-4 text-sm text-gray-300">
|
||||
@if ($hasSub)
|
||||
Provider: {{ $subscription->provider ?? 'direct' }} · Started {{ $subscription->started_at?->toDateString() }}
|
||||
@else
|
||||
You are on the free plan. Upgrade to unlock private keywords and sync.
|
||||
@endif
|
||||
</div>
|
||||
<div class="mt-3 text-xs text-gray-400">
|
||||
Downgrading to Free revokes any active API keys immediately.
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid gap-4 md:grid-cols-2">
|
||||
<div class="rounded-2xl bg-white/5 border border-white/10 p-4">
|
||||
<div class="text-sm font-semibold text-gray-200">Payment method</div>
|
||||
<div class="mt-2 text-2xl font-semibold text-white">Card</div>
|
||||
<div class="mt-1 text-xs text-gray-400">Coming soon</div>
|
||||
</div>
|
||||
<div class="rounded-2xl bg-white/5 border border-white/10 p-4">
|
||||
<div class="text-sm font-semibold text-gray-200">Invoices</div>
|
||||
<div class="mt-2 text-2xl font-semibold text-white">0</div>
|
||||
<div class="mt-1 text-xs text-gray-400">Billing history</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($payments->count() > 0)
|
||||
<div class="mt-6">
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Recent payments</div>
|
||||
<div class="mt-3 overflow-hidden rounded-2xl border border-white/10">
|
||||
<table class="min-w-full text-sm text-gray-300">
|
||||
<thead class="bg-white/5 text-xs uppercase tracking-[0.2em] text-gray-400">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left">Provider</th>
|
||||
<th class="px-4 py-3 text-left">Plan</th>
|
||||
<th class="px-4 py-3 text-left">Amount</th>
|
||||
<th class="px-4 py-3 text-left">Status</th>
|
||||
<th class="px-4 py-3 text-left">Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-white/10">
|
||||
@foreach ($payments as $payment)
|
||||
@php
|
||||
$status = $payment->status ?? 'pending';
|
||||
$pill = $status === 'paid'
|
||||
? ['bg' => 'bg-emerald-100 dark:bg-emerald-500/20', 'text' => 'text-emerald-800 dark:text-emerald-200']
|
||||
: ($status === 'failed'
|
||||
? ['bg' => 'bg-rose-100 dark:bg-rose-500/20', 'text' => 'text-rose-800 dark:text-rose-200']
|
||||
: ['bg' => 'bg-amber-100 dark:bg-amber-500/20', 'text' => 'text-amber-800 dark:text-amber-200']);
|
||||
@endphp
|
||||
<tr>
|
||||
<td class="px-4 py-3">{{ $payment->provider ?? '—' }}</td>
|
||||
<td class="px-4 py-3">{{ $payment->plan_code ?? '—' }}</td>
|
||||
<td class="px-4 py-3">{{ $payment->currency ?? 'USD' }} {{ number_format((float) ($payment->amount ?? 0), 2) }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="rounded-full {{ $pill['bg'] }} px-3 py-1 text-xs font-semibold {{ $pill['text'] }}">{{ $status }}</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-xs text-gray-400">{{ $payment->created_at?->toDateString() ?? '—' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if ($payments->contains(fn ($payment) => in_array($payment->status, ['pending', 'failed'], true)))
|
||||
<div class="mt-4 rounded-2xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800 dark:border-amber-400/30 dark:bg-amber-400/10 dark:text-amber-200">
|
||||
Pending or failed payments need a new checkout. Start a fresh transaction from the pricing page.
|
||||
</div>
|
||||
<a href="{{ route('pricing') }}" class="mt-3 inline-flex items-center justify-center rounded-full border border-amber-300 px-4 py-2 text-xs font-semibold text-amber-800 hover:bg-amber-100 dark:border-amber-300/40 dark:text-amber-200 dark:hover:bg-amber-400/10">
|
||||
Start new checkout
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (!$hasSub || (string) $user?->tier !== 'personal')
|
||||
<div class="mt-6 rounded-2xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800 dark:border-brand-sun/30 dark:bg-brand-sun/10 dark:text-brand-sun">
|
||||
Upgrade to Personal for private keywords and synced personalization.
|
||||
</div>
|
||||
<a href="{{ route('pricing') }}" class="mt-4 inline-flex items-center justify-center rounded-full bg-brand-sun text-black font-semibold px-4 py-2 text-sm">
|
||||
Upgrade to Personal
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
@endsection
|
||||
Reference in New Issue
Block a user