feat(ui): app layout with sidebar and topbar
Implement Nimuli-specific authenticated layout with: - Sidebar navigation with 7 main menu items (dashboard, links, qr codes, link-in-bio, analytics, domains, settings) - Topbar header with user email and logout button - Safe route checking using Route::has() for undefined routes - Workspace display in sidebar when available - Livewire AppLayout base class for future Livewire componentsmain
parent
a3930b5d25
commit
3929c48ea1
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\Layouts;
|
||||
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Layout('layouts.nimuli-app')]
|
||||
abstract class AppLayout extends Component
|
||||
{
|
||||
// Base layout class for authenticated Livewire components
|
||||
// Uses the Nimuli app layout with sidebar and topbar
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
@php
|
||||
$workspace = app()->bound('current_workspace') ? app('current_workspace') : null;
|
||||
$nav = [
|
||||
['label' => 'Dashboard', 'route' => 'dashboard', 'icon' => '⊞'],
|
||||
['label' => 'Links', 'route' => 'links.index', 'icon' => '↗'],
|
||||
['label' => 'QR Codes', 'route' => 'qr.index', 'icon' => '▦'],
|
||||
['label' => 'Link-in-Bio', 'route' => 'bio.index', 'icon' => '≡'],
|
||||
['label' => 'Analytics', 'route' => 'analytics.index', 'icon' => '↑'],
|
||||
['label' => 'Domains', 'route' => 'domains.index', 'icon' => '◎'],
|
||||
['label' => 'Settings', 'route' => 'settings.index', 'icon' => '⚙'],
|
||||
];
|
||||
@endphp
|
||||
<aside class="w-60 flex-shrink-0 bg-gray-900 border-r border-gray-800 flex flex-col">
|
||||
<div class="h-16 flex items-center px-5 border-b border-gray-800">
|
||||
<span class="text-lg font-bold tracking-tight">Nimuli</span>
|
||||
</div>
|
||||
@if($workspace)
|
||||
<div class="px-3 py-3 border-b border-gray-800">
|
||||
<div class="text-xs text-gray-500 uppercase tracking-wider mb-1">Workspace</div>
|
||||
<div class="text-sm font-medium truncate">{{ $workspace->name }}</div>
|
||||
</div>
|
||||
@endif
|
||||
<nav class="flex-1 px-2 py-3 space-y-0.5">
|
||||
@foreach($nav as $item)
|
||||
<a href="{{ Route::has($item['route']) ? route($item['route']) : '#' }}"
|
||||
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors
|
||||
{{ request()->routeIs($item['route'] . '*') ? 'bg-gray-800 text-white' : 'text-gray-400 hover:text-white hover:bg-gray-800' }}">
|
||||
<span class="w-5 text-center">{{ $item['icon'] }}</span>
|
||||
{{ $item['label'] }}
|
||||
</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
@if($workspace?->plan)
|
||||
<div class="px-3 py-3 border-t border-gray-800">
|
||||
<div class="text-xs text-gray-500">Plan: {{ ucfirst($workspace->plan->name) }}</div>
|
||||
</div>
|
||||
@endif
|
||||
</aside>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<header class="h-16 bg-gray-900 border-b border-gray-800 flex items-center justify-between px-6 flex-shrink-0">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-sm text-gray-400">{{ $title ?? '' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
@auth
|
||||
<span class="text-sm text-gray-400">{{ auth()->user()->email }}</span>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="text-sm text-gray-400 hover:text-white transition-colors">Logout</button>
|
||||
</form>
|
||||
@endauth
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}" data-theme="{{ auth()->user()?->theme ?? 'dark' }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>{{ $title ?? 'Nimuli' }}</title>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
@livewireStyles
|
||||
</head>
|
||||
<body class="bg-gray-950 text-gray-100 font-sans antialiased">
|
||||
<div class="flex h-screen overflow-hidden">
|
||||
<x-sidebar />
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<x-topbar :title="$title ?? ''" />
|
||||
<main class="flex-1 overflow-y-auto p-6">
|
||||
{{ $slot }}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@livewireScripts
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue