291 lines
20 KiB
PHP
291 lines
20 KiB
PHP
@extends('site.layout')
|
|
|
|
@php
|
|
$user = auth()->user();
|
|
$isAdmin = Gate::allows('admin');
|
|
|
|
$navUser = [
|
|
['label' => 'Overview', 'route' => 'dashboard.overview', 'icon' => 'layout-dashboard'],
|
|
['label' => 'My Keywords', 'route' => 'dashboard.keywords', 'icon' => 'hash'],
|
|
['label' => 'API Keys', 'route' => 'dashboard.api-keys', 'icon' => 'key-round'],
|
|
['label' => 'Billing', 'route' => 'dashboard.billing', 'icon' => 'badge-dollar-sign'],
|
|
['label' => 'Preferences', 'route' => 'dashboard.preferences', 'icon' => 'settings-2'],
|
|
['label' => 'Profile', 'route' => 'profile.edit', 'icon' => 'user-round'],
|
|
];
|
|
|
|
$navAdmin = [
|
|
['label' => 'Overview', 'route' => 'dashboard.overview', 'icon' => 'layout-dashboard'],
|
|
['label' => 'Users', 'route' => 'dashboard.admin.users', 'icon' => 'users'],
|
|
['label' => 'Subscriptions', 'route' => 'dashboard.admin.subscriptions', 'icon' => 'credit-card'],
|
|
['label' => 'Pricing', 'route' => 'dashboard.admin.pricing', 'icon' => 'badge-dollar-sign'],
|
|
['label' => 'Catalog', 'route' => 'dashboard.admin.catalog', 'icon' => 'package-search'],
|
|
['label' => 'Webhooks', 'route' => 'dashboard.admin.webhooks', 'icon' => 'webhook'],
|
|
['label' => 'Audit Logs', 'route' => 'dashboard.admin.audit_logs', 'icon' => 'list-checks'],
|
|
['label' => 'Settings', 'route' => 'dashboard.admin.settings', 'icon' => 'settings'],
|
|
['label' => 'Profile', 'route' => 'profile.edit', 'icon' => 'user-round'],
|
|
];
|
|
|
|
$nav = $isAdmin ? $navAdmin : $navUser;
|
|
$exportQuery = request()->query();
|
|
@endphp
|
|
|
|
@section('content')
|
|
<div class="flex h-screen w-full">
|
|
<aside class="hidden lg:flex w-20 lg:w-64 h-full glass-panel flex-col justify-between p-4 z-50 shrink-0">
|
|
<div>
|
|
<div class="flex items-center gap-3 px-2 mb-8 mt-2">
|
|
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-white to-gray-300 flex items-center justify-center shadow-lg shadow-white/20 shrink-0">
|
|
<img src="/assets/logo/logo-mark.svg" alt="Dewemoji logo" class="w-7 h-7 object-contain" />
|
|
</div>
|
|
<div class="hidden lg:block">
|
|
<div class="text-xs uppercase tracking-[0.2em] text-gray-400">Dewemoji</div>
|
|
<div class="font-display font-bold text-lg tracking-tight">Dashboard</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="px-2 mb-6">
|
|
<div class="rounded-2xl bg-[#0b0b0f]/90 theme-surface border border-white/5 px-4 py-3">
|
|
<div class="text-xs text-gray-400">
|
|
Signed in as {{ $isAdmin ? 'Admin' : 'User' }}
|
|
</div>
|
|
<div class="mt-1 font-semibold text-gray-200">{{ $user?->name ?? 'Guest' }}</div>
|
|
<div class="text-xs text-gray-400">{{ $user?->email ?? 'no-session' }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="space-y-1">
|
|
<div class="px-3 text-[11px] uppercase tracking-[0.3em] text-gray-500 mb-2">Menu</div>
|
|
@foreach ($nav as $item)
|
|
@php
|
|
$active = request()->routeIs($item['route']);
|
|
$classes = $active
|
|
? 'bg-white/10 text-brand-sun border border-white/5'
|
|
: 'text-gray-400 hover:text-white hover:bg-white/5 border border-transparent';
|
|
@endphp
|
|
<a href="{{ route($item['route']) }}" class="flex items-center gap-4 px-3 py-3 rounded-xl transition-all group {{ $classes }}">
|
|
<i data-lucide="{{ $item['icon'] }}" class="w-5 h-5 group-hover:scale-110 transition-transform"></i>
|
|
<span class="text-sm font-medium hidden lg:block">{{ $item['label'] }}</span>
|
|
</a>
|
|
@endforeach
|
|
</nav>
|
|
</div>
|
|
|
|
<div class="space-y-1">
|
|
<a href="{{ route('home') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
|
|
<i data-lucide="layout-grid" class="w-5 h-5"></i>
|
|
<span class="text-sm font-medium hidden lg:block">Discover</span>
|
|
</a>
|
|
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
|
|
<i data-lucide="life-buoy" class="w-5 h-5"></i>
|
|
<span class="text-sm font-medium hidden lg:block">Support</span>
|
|
</a>
|
|
@auth
|
|
<form method="POST" action="{{ route('logout') }}">
|
|
@csrf
|
|
<button type="submit" class="w-full flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
|
|
<i data-lucide="log-out" class="w-5 h-5"></i>
|
|
<span class="text-sm font-medium hidden lg:block">Logout</span>
|
|
</button>
|
|
</form>
|
|
@endauth
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="flex-1 flex flex-col h-full min-w-0 relative">
|
|
<header class="glass-header px-6 py-6 shrink-0 sticky top-0 z-40">
|
|
<div class="w-full flex flex-col gap-4">
|
|
<div class="flex flex-col md:flex-row gap-4 md:items-center justify-between">
|
|
<div>
|
|
<div class="text-xs uppercase tracking-[0.3em] text-gray-400">Workspace</div>
|
|
<h1 class="mt-2 text-3xl font-bold">{{ trim($__env->yieldContent('page_title')) ?: 'Dashboard Overview' }}</h1>
|
|
<p class="mt-1 text-sm text-gray-400">{{ trim($__env->yieldContent('page_subtitle')) ?: 'A shared layout with role-based navigation.' }}</p>
|
|
</div>
|
|
<div class="flex items-center gap-3 shrink-0">
|
|
<div class="relative">
|
|
<button id="quick-action-btn" class="rounded-full bg-white/10 text-white border border-white/10 px-5 py-2 text-sm font-semibold hover:bg-white/20 transition-colors">
|
|
Quick action
|
|
</button>
|
|
<div id="quick-action-menu" class="hidden absolute right-0 mt-2 w-60 rounded-2xl border border-slate-200 bg-white p-2 shadow-xl dark:border-white/10 dark:bg-[#0b0b0f]/95 dark:backdrop-blur">
|
|
<div class="text-[11px] uppercase tracking-[0.3em] text-slate-500 px-3 py-2 dark:text-gray-500">Actions</div>
|
|
@if ($isAdmin)
|
|
<a href="{{ route('dashboard.admin.users') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="users" class="w-4 h-4"></i><span>Manage users</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.admin.subscriptions') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="credit-card" class="w-4 h-4"></i><span>Grant subscription</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.admin.catalog') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="package-search" class="w-4 h-4"></i><span>Manage catalog</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.admin.webhooks') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="webhook" class="w-4 h-4"></i><span>Review webhooks</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.admin.audit_logs') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="list-checks" class="w-4 h-4"></i><span>Audit logs</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.admin.settings') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="settings" class="w-4 h-4"></i><span>Update settings</span>
|
|
</a>
|
|
<a href="{{ route('profile.edit') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="user-round" class="w-4 h-4"></i><span>Edit profile</span>
|
|
</a>
|
|
@else
|
|
<a href="{{ route('dashboard.keywords') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="hash" class="w-4 h-4"></i><span>My keywords</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.keywords') }}#add" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="plus-circle" class="w-4 h-4"></i><span>Add keyword</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.api-keys') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="key-round" class="w-4 h-4"></i><span>Manage API keys</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.billing') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="badge-dollar-sign" class="w-4 h-4"></i><span>Billing overview</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.preferences') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="settings-2" class="w-4 h-4"></i><span>Preferences</span>
|
|
</a>
|
|
<a href="{{ route('profile.edit') }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="user-round" class="w-4 h-4"></i><span>Edit profile</span>
|
|
</a>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@if ($isAdmin)
|
|
<div class="relative">
|
|
<button id="export-btn" class="rounded-full border border-white/10 px-5 py-2 text-sm font-semibold text-gray-200 hover:bg-white/5 transition-colors">
|
|
Export
|
|
</button>
|
|
<div id="export-menu" class="hidden absolute right-0 mt-2 w-56 rounded-2xl border border-slate-200 bg-white p-2 shadow-xl dark:border-white/10 dark:bg-[#0b0b0f]/95 dark:backdrop-blur">
|
|
<div class="text-[11px] uppercase tracking-[0.3em] text-slate-500 px-3 py-2 dark:text-gray-500">Export CSV</div>
|
|
<a href="{{ route('dashboard.admin.export', array_merge(['type' => 'users'], $exportQuery)) }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="users" class="w-4 h-4"></i><span>Users</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.admin.export', array_merge(['type' => 'subscriptions'], $exportQuery)) }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="credit-card" class="w-4 h-4"></i><span>Subscriptions</span>
|
|
</a>
|
|
<a href="{{ route('dashboard.admin.export', array_merge(['type' => 'webhooks'], $exportQuery)) }}" class="flex items-center gap-3 px-3 py-2 rounded-xl text-sm text-slate-700 hover:bg-slate-100 dark:text-gray-200 dark:hover:bg-white/10">
|
|
<i data-lucide="webhook" class="w-4 h-4"></i><span>Webhooks</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
<button id="theme-toggle" class="w-10 h-10 rounded-full theme-surface border border-white/10 shadow-lg flex items-center justify-center text-gray-300 hover:text-white transition-colors">
|
|
<span class="sr-only">Toggle theme</span>
|
|
<i data-lucide="moon" class="w-4 h-4" data-theme-icon="dark"></i>
|
|
<i data-lucide="sun" class="w-4 h-4 hidden" data-theme-icon="light"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="flex-1 overflow-y-auto p-4 sm:p-6 pb-24 lg:pb-6">
|
|
@if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail())
|
|
<div class="mb-6 rounded-2xl border border-amber-300/40 bg-amber-400/10 px-4 py-4 text-sm text-amber-200">
|
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
|
<div>
|
|
<div class="font-semibold text-amber-100">Verify your email to unlock billing and API keys.</div>
|
|
<div class="mt-1 text-xs text-amber-200/80">No reminders will be sent automatically.</div>
|
|
</div>
|
|
<form method="POST" action="{{ route('verification.send') }}">
|
|
@csrf
|
|
<button class="rounded-full border border-amber-200/40 px-4 py-2 text-xs font-semibold text-amber-100 hover:bg-amber-200/10 transition-colors">
|
|
Resend verification
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
@yield('dashboard_content')
|
|
</div>
|
|
</main>
|
|
</div>
|
|
@endsection
|
|
|
|
@section('mobile_nav')
|
|
<nav class="lg:hidden fixed bottom-0 inset-x-0 z-50 border-t border-white/10 bg-[#0b0b0f]/95 backdrop-blur px-2 py-2 theme-nav">
|
|
<div class="grid grid-cols-4 gap-1 text-[11px]">
|
|
<a href="{{ route('dashboard.overview') }}" class="flex flex-col items-center justify-center gap-1 rounded-lg py-2 {{ request()->routeIs('dashboard.overview') ? 'text-brand-sun bg-white/5' : 'text-gray-300 hover:bg-white/5' }}">
|
|
<i data-lucide="layout-dashboard" class="w-4 h-4"></i><span>Overview</span>
|
|
</a>
|
|
<a href="{{ $isAdmin ? route('dashboard.admin.users') : route('dashboard.keywords') }}" class="flex flex-col items-center justify-center gap-1 rounded-lg py-2 {{ request()->routeIs('dashboard.admin.users') || request()->routeIs('dashboard.keywords') ? 'text-brand-sun bg-white/5' : 'text-gray-300 hover:bg-white/5' }}">
|
|
<i data-lucide="{{ $isAdmin ? 'users' : 'hash' }}" class="w-4 h-4"></i><span>{{ $isAdmin ? 'Users' : 'Keywords' }}</span>
|
|
</a>
|
|
<a href="{{ $isAdmin ? route('dashboard.admin.pricing') : route('dashboard.billing') }}" class="flex flex-col items-center justify-center gap-1 rounded-lg py-2 {{ request()->routeIs('dashboard.admin.pricing') || request()->routeIs('dashboard.billing') ? 'text-brand-sun bg-white/5' : 'text-gray-300 hover:bg-white/5' }}">
|
|
<i data-lucide="badge-dollar-sign" class="w-4 h-4"></i><span>Plans</span>
|
|
</a>
|
|
<button id="more-menu-btn" class="flex flex-col items-center justify-center gap-1 rounded-lg py-2 text-gray-300 hover:bg-white/5">
|
|
<i data-lucide="more-horizontal" class="w-4 h-4"></i><span>More</span>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
@endsection
|
|
|
|
@section('more_menu')
|
|
<div id="more-menu-backdrop" class="lg:hidden fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden"></div>
|
|
<div id="more-menu" class="lg:hidden fixed bottom-16 left-4 right-4 glass-panel rounded-2xl p-4 z-50 hidden">
|
|
<div class="flex items-center justify-between mb-3">
|
|
<span class="text-sm font-semibold">More</span>
|
|
<button id="more-menu-close" class="w-8 h-8 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center">
|
|
<i data-lucide="x" class="w-4 h-4"></i>
|
|
</button>
|
|
</div>
|
|
<div class="flex flex-col gap-2 text-sm">
|
|
@foreach ($nav as $item)
|
|
<a href="{{ route($item['route']) }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
|
|
<i data-lucide="{{ $item['icon'] }}" class="w-4 h-4"></i><span>{{ $item['label'] }}</span>
|
|
</a>
|
|
@endforeach
|
|
<a href="{{ route('home') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
|
|
<i data-lucide="layout-grid" class="w-4 h-4"></i><span>Discover</span>
|
|
</a>
|
|
@auth
|
|
<form method="POST" action="{{ route('logout') }}" class="flex">
|
|
@csrf
|
|
<button type="submit" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10 w-full text-left">
|
|
<i data-lucide="log-out" class="w-4 h-4"></i><span>Logout</span>
|
|
</button>
|
|
</form>
|
|
@endauth
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
(() => {
|
|
const quickBtn = document.getElementById('quick-action-btn');
|
|
const quickMenu = document.getElementById('quick-action-menu');
|
|
const exportBtn = document.getElementById('export-btn');
|
|
const exportMenu = document.getElementById('export-menu');
|
|
|
|
const closeMenus = () => {
|
|
if (quickMenu) quickMenu.classList.add('hidden');
|
|
if (exportMenu) exportMenu.classList.add('hidden');
|
|
};
|
|
|
|
if (quickBtn && quickMenu) {
|
|
quickBtn.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
const open = !quickMenu.classList.contains('hidden');
|
|
closeMenus();
|
|
if (!open) quickMenu.classList.remove('hidden');
|
|
});
|
|
}
|
|
|
|
if (exportBtn && exportMenu) {
|
|
exportBtn.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
const open = !exportMenu.classList.contains('hidden');
|
|
closeMenus();
|
|
if (!open) exportMenu.classList.remove('hidden');
|
|
});
|
|
}
|
|
|
|
document.addEventListener('click', closeMenus);
|
|
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeMenus(); });
|
|
})();
|
|
</script>
|
|
@endpush
|