197 lines
12 KiB
PHP
197 lines
12 KiB
PHP
@extends('dashboard.app')
|
|
|
|
@section('page_title', 'Admin Subscriptions')
|
|
@section('page_subtitle', 'Grant, revoke, and audit Pro access.')
|
|
|
|
@section('dashboard_content')
|
|
@if (session('status'))
|
|
<div class="mb-6 rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-700 dark:border-emerald-500/30 dark:bg-emerald-500/15 dark:text-emerald-200">
|
|
{{ session('status') }}
|
|
</div>
|
|
@endif
|
|
|
|
<div class="grid gap-6 lg:grid-cols-3">
|
|
@foreach ([
|
|
['label' => 'Active', 'value' => number_format(\App\Models\Subscription::where('status', 'active')->count()), 'note' => 'Subscriptions'],
|
|
['label' => 'Revoked', 'value' => number_format(\App\Models\Subscription::where('status', 'revoked')->count()), 'note' => 'All time'],
|
|
['label' => 'Pending', 'value' => number_format(\App\Models\Subscription::where('status', 'pending')->count()), 'note' => 'Manual review'],
|
|
] as $card)
|
|
<div class="rounded-2xl glass-card p-5">
|
|
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">{{ $card['label'] }}</div>
|
|
<div class="mt-3 text-3xl font-semibold text-white">{{ $card['value'] }}</div>
|
|
<div class="mt-2 text-sm text-gray-400">{{ $card['note'] }}</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
<div class="mt-8 rounded-2xl glass-card p-6">
|
|
<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
|
<div>
|
|
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Filters</div>
|
|
<div class="mt-2 text-lg font-semibold text-white">Refine subscriptions</div>
|
|
</div>
|
|
<form method="GET" class="flex w-full flex-wrap gap-3 md:w-auto md:justify-end">
|
|
<input type="hidden" name="sort" value="{{ $sort ?? 'id' }}">
|
|
<input type="hidden" name="dir" value="{{ $dir ?? 'desc' }}">
|
|
<input type="number" name="user_id" value="{{ $filters['user_id'] ?? '' }}" placeholder="User ID"
|
|
class="w-full md:w-32 rounded-xl border border-white/10 px-4 py-2 text-sm text-gray-200 placeholder-gray-500 theme-surface">
|
|
<input type="search" name="email" value="{{ $filters['email'] ?? '' }}" placeholder="User email"
|
|
class="w-full md:w-64 rounded-xl border border-white/10 px-4 py-2 text-sm text-gray-200 placeholder-gray-500 theme-surface">
|
|
<select name="status" class="rounded-xl border border-white/10 px-4 py-2 text-sm text-gray-200 theme-surface">
|
|
<option value="">All statuses</option>
|
|
<option value="active" @selected(($filters['status'] ?? '') === 'active')>Active</option>
|
|
<option value="pending" @selected(($filters['status'] ?? '') === 'pending')>Pending</option>
|
|
<option value="revoked" @selected(($filters['status'] ?? '') === 'revoked')>Revoked</option>
|
|
<option value="cancelled" @selected(($filters['status'] ?? '') === 'cancelled')>Cancelled</option>
|
|
<option value="suspended" @selected(($filters['status'] ?? '') === 'suspended')>Suspended</option>
|
|
</select>
|
|
<button class="rounded-xl border border-white/10 px-4 py-2 text-sm font-semibold text-gray-200 hover:bg-white/5 transition-colors">Filter</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-6 grid gap-6 lg:grid-cols-2">
|
|
<div class="rounded-2xl glass-card p-6">
|
|
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Grant access</div>
|
|
<div class="mt-2 text-lg font-semibold text-white">Create subscription</div>
|
|
<form method="POST" action="{{ route('dashboard.admin.subscriptions.grant') }}" class="mt-5 grid gap-4 text-sm text-gray-300">
|
|
@csrf
|
|
<div class="grid gap-3 md:grid-cols-2">
|
|
<label class="block">
|
|
User email
|
|
<input name="email" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 placeholder-gray-500 theme-surface" placeholder="user@email.com">
|
|
</label>
|
|
<label class="block">
|
|
User ID
|
|
<input name="user_id" type="number" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 placeholder-gray-500 theme-surface" placeholder="Optional">
|
|
</label>
|
|
</div>
|
|
<div class="grid gap-3 md:grid-cols-2">
|
|
<label class="block">
|
|
Plan
|
|
<input name="plan" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 placeholder-gray-500 theme-surface" value="personal">
|
|
</label>
|
|
<label class="block">
|
|
Status
|
|
<select name="status" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 theme-surface">
|
|
<option value="active">Active</option>
|
|
<option value="pending">Pending</option>
|
|
<option value="revoked">Revoked</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
<div class="grid gap-3 md:grid-cols-2">
|
|
<label class="block">
|
|
Started at
|
|
<input name="started_at" type="date" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 theme-surface">
|
|
</label>
|
|
<label class="block">
|
|
Expires at
|
|
<input name="expires_at" type="date" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 theme-surface">
|
|
</label>
|
|
</div>
|
|
<button class="rounded-xl bg-white/10 border border-white/10 px-4 py-2 text-sm font-semibold text-white hover:bg-white/20 transition-colors">Grant</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="rounded-2xl glass-card p-6">
|
|
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Revoke access</div>
|
|
<div class="mt-2 text-lg font-semibold text-white">Cancel subscription</div>
|
|
<form method="POST" action="{{ route('dashboard.admin.subscriptions.revoke') }}" class="mt-5 grid gap-4 text-sm text-gray-300">
|
|
@csrf
|
|
<label class="block">
|
|
Subscription ID
|
|
<input name="subscription_id" type="number" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 placeholder-gray-500 theme-surface" placeholder="Optional">
|
|
</label>
|
|
<label class="block">
|
|
User email
|
|
<input name="email" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 placeholder-gray-500 theme-surface" placeholder="user@email.com">
|
|
</label>
|
|
<label class="block">
|
|
User ID
|
|
<input name="user_id" type="number" class="mt-2 w-full rounded-xl border border-white/10 px-3 py-2 text-gray-200 placeholder-gray-500 theme-surface" placeholder="Optional">
|
|
</label>
|
|
<button class="rounded-xl border border-white/10 px-4 py-2 text-sm font-semibold text-gray-200 hover:bg-white/5 transition-colors">Revoke</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-8 rounded-2xl glass-card p-6">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Subscription list</div>
|
|
<div class="mt-2 text-lg font-semibold text-white">Recent subscriptions</div>
|
|
</div>
|
|
</div>
|
|
<div class="mt-6 overflow-x-auto">
|
|
<table class="min-w-full text-left text-sm">
|
|
<thead class="text-xs uppercase tracking-[0.15em] text-gray-400">
|
|
<tr>
|
|
@php
|
|
$sortParam = $sort ?? 'id';
|
|
$dirParam = $dir ?? 'desc';
|
|
$toggle = fn ($field) => ($sortParam === $field && $dirParam === 'asc') ? 'desc' : 'asc';
|
|
$sortUrl = fn ($field) => request()->fullUrlWithQuery(['sort' => $field, 'dir' => $toggle($field)]);
|
|
@endphp
|
|
<th class="py-3 pr-4">Subscriber</th>
|
|
<th class="py-3 pr-4">
|
|
<a href="{{ $sortUrl('plan') }}" class="inline-flex items-center gap-1 hover:text-white">
|
|
Plan
|
|
<i data-lucide="arrow-up-down" class="w-3 h-3"></i>
|
|
</a>
|
|
</th>
|
|
<th class="py-3 pr-4">
|
|
<a href="{{ $sortUrl('status') }}" class="inline-flex items-center gap-1 hover:text-white">
|
|
Status
|
|
<i data-lucide="arrow-up-down" class="w-3 h-3"></i>
|
|
</a>
|
|
</th>
|
|
<th class="py-3 pr-4">
|
|
<a href="{{ $sortUrl('expires_at') }}" class="inline-flex items-center gap-1 hover:text-white">
|
|
Next billing
|
|
<i data-lucide="arrow-up-down" class="w-3 h-3"></i>
|
|
</a>
|
|
</th>
|
|
<th class="py-3 pr-4 text-right">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-white/10 text-gray-300">
|
|
@forelse ($subscriptions ?? [] as $row)
|
|
<tr>
|
|
<td class="py-4 pr-4">
|
|
<div class="font-semibold text-white">{{ $row->user?->name ?? '—' }}</div>
|
|
<div class="text-xs text-gray-400">{{ $row->user?->email ?? '—' }}</div>
|
|
</td>
|
|
<td class="py-4 pr-4">{{ $row->plan }}</td>
|
|
<td class="py-4 pr-4">
|
|
@php
|
|
$inactive = in_array($row->status, ['revoked', 'cancelled', 'suspended'], true);
|
|
$pill = $inactive
|
|
? ['bg' => 'bg-rose-100 dark:bg-rose-500/20', 'text' => 'text-rose-800 dark:text-rose-200']
|
|
: ($row->status === 'pending'
|
|
? ['bg' => 'bg-amber-100 dark:bg-amber-500/20', 'text' => 'text-amber-800 dark:text-amber-200']
|
|
: ['bg' => 'bg-emerald-100 dark:bg-emerald-500/20', 'text' => 'text-emerald-800 dark:text-emerald-200']);
|
|
@endphp
|
|
<span class="rounded-full {{ $pill['bg'] }} px-3 py-1 text-xs font-semibold {{ $pill['text'] }}">
|
|
{{ $row->status }}
|
|
</span>
|
|
</td>
|
|
<td class="py-4 pr-4 text-xs">{{ $row->expires_at?->toDateString() ?? '—' }}</td>
|
|
<td class="py-4 pr-4 text-right">
|
|
<a href="{{ route('dashboard.admin.subscriptions.show', $row->id) }}" class="rounded-full border border-white/10 px-3 py-1 text-xs font-semibold text-gray-200 hover:bg-white/5 transition-colors">View</a>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="5" class="py-6 text-center text-sm text-gray-400">No subscriptions found.</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="mt-6">
|
|
{{ $subscriptions->links('vendor.pagination.dashboard') }}
|
|
</div>
|
|
</div>
|
|
@endsection
|