Update pricing UX, billing flows, and API rules
This commit is contained in:
182
app/resources/views/dashboard/index.blade.php
Normal file
182
app/resources/views/dashboard/index.blade.php
Normal file
@@ -0,0 +1,182 @@
|
||||
@extends('dashboard.app')
|
||||
|
||||
@section('title', 'Dashboard')
|
||||
@section('page_title', 'Dashboard Overview')
|
||||
@section('page_subtitle', 'A shared layout with role-based navigation.')
|
||||
|
||||
@section('dashboard_content')
|
||||
@php
|
||||
$isAdmin = Gate::allows('admin');
|
||||
$metrics = $overviewMetrics ?? [
|
||||
'users_total' => 0,
|
||||
'users_personal' => 0,
|
||||
'subscriptions_active' => 0,
|
||||
'subscriptions_total' => 0,
|
||||
'webhook_total' => 0,
|
||||
'webhook_errors' => 0,
|
||||
];
|
||||
$personalPct = $metrics['users_total'] > 0
|
||||
? round(($metrics['users_personal'] / $metrics['users_total']) * 100)
|
||||
: 0;
|
||||
@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">Total users</div>
|
||||
<div class="mt-3 text-3xl font-semibold text-white">{{ number_format($metrics['users_total']) }}</div>
|
||||
<div class="mt-2 text-sm text-gray-400">{{ number_format($metrics['users_personal']) }} personal users</div>
|
||||
<div class="mt-4 h-2 w-full overflow-hidden rounded-full bg-white/10">
|
||||
<div class="h-full rounded-full bg-brand-ocean" style="width: {{ $personalPct }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-3xl glass-card p-6">
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Active subscriptions</div>
|
||||
<div class="mt-3 text-3xl font-semibold text-white">{{ number_format($metrics['subscriptions_active']) }}</div>
|
||||
<div class="mt-2 text-sm text-gray-400">{{ number_format($metrics['subscriptions_total']) }} total subscriptions</div>
|
||||
<div class="mt-4 flex items-center gap-2 text-sm text-brand-sun">
|
||||
<span class="inline-flex h-2 w-2 rounded-full bg-brand-sun"></span>
|
||||
{{ $metrics['subscriptions_active'] > 0 ? 'Live access enabled' : 'No active subs' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-3xl glass-card p-6">
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Webhook events</div>
|
||||
<div class="mt-3 text-3xl font-semibold text-white">{{ number_format($metrics['webhook_total']) }}</div>
|
||||
<div class="mt-2 text-sm text-gray-400">{{ number_format($metrics['webhook_errors']) }} failures</div>
|
||||
<div class="mt-4 flex items-center gap-2 text-sm text-gray-400">
|
||||
<span class="inline-flex h-2 w-2 rounded-full {{ $metrics['webhook_errors'] > 0 ? 'bg-amber-400' : 'bg-emerald-400' }}"></span>
|
||||
{{ $metrics['webhook_errors'] > 0 ? 'Needs review' : 'All clear' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<div class="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">Insights</div>
|
||||
<div class="mt-2 text-xl font-semibold text-white">Usage highlights</div>
|
||||
</div>
|
||||
<span class="rounded-full border border-white/10 px-3 py-1 text-xs text-gray-300 theme-surface">Last 7 days</span>
|
||||
</div>
|
||||
<div class="mt-6 grid gap-4 md:grid-cols-2">
|
||||
<div class="rounded-2xl theme-surface border border-white/10 p-4 shadow-sm">
|
||||
<div class="text-sm font-semibold text-gray-200">Top search</div>
|
||||
<div class="mt-2 text-2xl font-semibold text-white">"smile"</div>
|
||||
<div class="mt-1 text-xs text-gray-400">14% of queries</div>
|
||||
</div>
|
||||
<div class="rounded-2xl theme-surface border border-white/10 p-4 shadow-sm">
|
||||
<div class="text-sm font-semibold text-gray-200">Most used category</div>
|
||||
<div class="mt-2 text-2xl font-semibold text-white">Smileys</div>
|
||||
<div class="mt-1 text-xs text-gray-400">8,214 views</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 rounded-2xl border border-white/10 theme-surface p-4 shadow-sm">
|
||||
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">New users</div>
|
||||
<div class="mt-2 text-sm text-gray-300">Last 7 days</div>
|
||||
<div class="mt-4">
|
||||
<div id="overview-users-chart" class="h-36"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 grid gap-4 md:grid-cols-2">
|
||||
<div class="rounded-2xl border border-white/10 theme-surface p-4 shadow-sm">
|
||||
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Subscriptions</div>
|
||||
<div class="mt-2 text-sm text-gray-300">Created last 7 days</div>
|
||||
<div class="mt-3">
|
||||
<div id="overview-subs-chart" class="h-28"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-white/10 theme-surface p-4 shadow-sm">
|
||||
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Webhooks</div>
|
||||
<div class="mt-2 text-sm text-gray-300">Events last 7 days</div>
|
||||
<div class="mt-3">
|
||||
<div id="overview-webhooks-chart" class="h-28"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
(() => {
|
||||
const waitForApex = (cb) => {
|
||||
if (window.ApexCharts) return cb();
|
||||
let tries = 0;
|
||||
const t = setInterval(() => {
|
||||
if (window.ApexCharts) {
|
||||
clearInterval(t);
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
tries += 1;
|
||||
if (tries > 50) {
|
||||
clearInterval(t);
|
||||
console.warn('ApexCharts not loaded.');
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const root = document.documentElement;
|
||||
const isDark = () => root.classList.contains('dark');
|
||||
const palette = () => ({
|
||||
text: isDark() ? '#e2e8f0' : '#334155',
|
||||
grid: isDark() ? 'rgba(148,163,184,0.15)' : 'rgba(15,23,42,0.08)',
|
||||
blue: isDark() ? '#60a5fa' : '#2563eb',
|
||||
amber: isDark() ? '#f59e0b' : '#d97706',
|
||||
mint: isDark() ? '#34d399' : '#10b981',
|
||||
});
|
||||
|
||||
const labels = @json($chartLabels ?? []);
|
||||
const users = @json($chartValues ?? []);
|
||||
const subs = @json($chartSubs ?? []);
|
||||
const webhooks = @json($chartWebhooks ?? []);
|
||||
|
||||
waitForApex(() => {
|
||||
const colors = palette();
|
||||
const base = {
|
||||
chart: {
|
||||
type: 'area',
|
||||
sparkline: { enabled: true },
|
||||
toolbar: { show: false },
|
||||
animations: { enabled: true }
|
||||
},
|
||||
stroke: { curve: 'smooth', width: 2 },
|
||||
fill: { opacity: 0.25 },
|
||||
grid: { show: false },
|
||||
tooltip: { theme: isDark() ? 'dark' : 'light' },
|
||||
xaxis: { categories: labels },
|
||||
yaxis: { show: false }
|
||||
};
|
||||
|
||||
const usersEl = document.getElementById('overview-users-chart');
|
||||
if (usersEl) {
|
||||
new ApexCharts(usersEl, {
|
||||
...base,
|
||||
colors: [colors.blue],
|
||||
series: [{ name: 'New users', data: users }]
|
||||
}).render();
|
||||
}
|
||||
|
||||
const subsEl = document.getElementById('overview-subs-chart');
|
||||
if (subsEl) {
|
||||
new ApexCharts(subsEl, {
|
||||
...base,
|
||||
colors: [colors.amber],
|
||||
series: [{ name: 'Subscriptions', data: subs }]
|
||||
}).render();
|
||||
}
|
||||
|
||||
const webhooksEl = document.getElementById('overview-webhooks-chart');
|
||||
if (webhooksEl) {
|
||||
new ApexCharts(webhooksEl, {
|
||||
...base,
|
||||
colors: [colors.mint],
|
||||
series: [{ name: 'Webhooks', data: webhooks }]
|
||||
}).render();
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@endpush
|
||||
Reference in New Issue
Block a user