Agent skill

layout-navigation-components

Componentes de UI para layout e navegação seguindo o padrão de components do Easy Budget.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/layout-navigation-components

SKILL.md

Componentes de UI para Layout e Navegação

Esta skill define os componentes Blade específicos para layout e navegação no Easy Budget, seguindo o padrão de components estabelecido no sistema.

Estrutura de Components

resources/views/components/
├── layout/
│   ├── app.blade.php               # Layout principal do sistema
│   ├── admin.blade.php             # Layout para área administrativa
│   ├── auth.blade.php              # Layout para páginas de autenticação
│   ├── dashboard.blade.php         # Layout para dashboard
│   ├── sidebar.blade.php           # Barra lateral de navegação
│   ├── navbar.blade.php            # Barra de navegação superior
│   ├── breadcrumbs.blade.php       # Breadcrumb de navegação
│   ├── footer.blade.php            # Rodapé do sistema
│   ├── theme-switcher.blade.php    # Troca de tema
│   └── notifications.blade.php     # Sistema de notificações
└── ...

1. App Layout Component

Componente para layout principal do sistema.

Uso Básico

blade
<x-layout.app :title="$title" :sidebar="$sidebar" :navbar="$navbar" />

Parâmetros

Parâmetro Tipo Descrição Padrão
title string Título da página Obrigatório
sidebar bool Exibir barra lateral true
navbar bool Exibir barra superior true
theme string Tema do sistema auto

Estrutura

blade
@props([
    'title',
    'sidebar' => true,
    'navbar' => true,
    'theme' => 'auto'
])

<!DOCTYPE html>
<html lang="pt-BR" data-theme="{{ $theme }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ $title }} - {{ tenant('name') ?? 'Easy Budget' }}</title>

    <!-- Favicon -->
    <link rel="icon" href="{{ asset('favicon.ico') }}" type="image/x-icon">

    <!-- Fonts -->
    <link rel="preconnect" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

    <!-- Scripts -->
    @vite(['resources/css/app.css', 'resources/js/app.js'])

    <!-- Custom Styles -->
    @stack('styles')

    <!-- Meta Tags -->
    <meta name="description" content="Sistema de gestão empresarial completo">
    <meta name="keywords" content="gestão, orçamentos, faturas, clientes, estoque">
    <meta name="author" content="Easy Budget">

    <!-- Open Graph -->
    <meta property="og:title" content="{{ $title }}">
    <meta property="og:description" content="Sistema de gestão empresarial">
    <meta property="og:type" content="website">

    <!-- CSRF Token -->
    <script>
        window.Laravel = {
            csrfToken: '{{ csrf_token() }}',
            baseUrl: '{{ url('/') }}'
        };
    </script>
</head>
<body class="font-sans antialiased">
    <div class="min-h-screen bg-gray-100">
        @if($navbar)
            <x-layout.navbar />
        @endif

        <div class="flex">
            @if($sidebar)
                <x-layout.sidebar />
            @endif

            <main class="flex-1 overflow-x-hidden overflow-y-auto">
                <div class="container mx-auto px-6 py-8">
                    {{ $slot }}
                </div>
            </main>
        </div>
    </div>

    <!-- Flash Messages -->
    @if(session('success'))
        <div id="flash-message" class="fixed top-4 right-4 z-50" data-message="{{ session('success') }}" data-type="success"></div>
    @elseif(session('error'))
        <div id="flash-message" class="fixed top-4 right-4 z-50" data-message="{{ session('error') }}" data-type="error"></div>
    @endif

    <!-- Scripts -->
    @stack('scripts')

    <!-- Flash Message Script -->
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const flashMessage = document.getElementById('flash-message');
            if (flashMessage) {
                const message = flashMessage.getAttribute('data-message');
                const type = flashMessage.getAttribute('data-type');
                showFlashMessage(message, type);
            }
        });
    </script>
</body>
</html>

2. Admin Layout Component

Componente para layout da área administrativa.

Uso Básico

blade
<x-layout.admin :title="$title" :breadcrumbs="$breadcrumbs" />

Parâmetros

Parâmetro Tipo Descrição Padrão
title string Título da página Obrigatório
breadcrumbs array Breadcrumb da página []
sidebar bool Exibir barra lateral true

Estrutura

blade
@props([
    'title',
    'breadcrumbs' => [],
    'sidebar' => true
])

<!DOCTYPE html>
<html lang="pt-BR" data-theme="admin">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ $title }} - Administração - {{ tenant('name') ?? 'Easy Budget' }}</title>

    <!-- Favicon -->
    <link rel="icon" href="{{ asset('favicon.ico') }}" type="image/x-icon">

    <!-- Fonts -->
    <link rel="preconnect" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

    <!-- Admin Styles -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">

    <!-- Custom Admin Styles -->
    <style>
        :root {
            --admin-bg: #f8f9fa;
            --admin-sidebar: #2c3e50;
            --admin-sidebar-active: #34495e;
            --admin-primary: #3498db;
            --admin-danger: #e74c3c;
            --admin-warning: #f39c12;
            --admin-success: #27ae60;
        }

        .admin-layout {
            min-height: 100vh;
            background-color: var(--admin-bg);
        }

        .admin-sidebar {
            background-color: var(--admin-sidebar);
            min-height: 100vh;
            border-right: 1px solid #dee2e6;
        }

        .admin-sidebar .nav-link {
            color: #bdc3c7;
            border-radius: 0;
            padding: 1rem 1.5rem;
            transition: all 0.3s ease;
        }

        .admin-sidebar .nav-link:hover {
            background-color: var(--admin-sidebar-active);
            color: #fff;
        }

        .admin-sidebar .nav-link.active {
            background-color: var(--admin-primary);
            color: #fff;
            border-left: 4px solid #fff;
        }

        .admin-header {
            background-color: #fff;
            border-bottom: 1px solid #dee2e6;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        .admin-content {
            padding: 2rem;
        }

        .admin-card {
            background: #fff;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
        }

        .admin-stat-card {
            background: linear-gradient(135deg, var(--admin-primary), #2980b9);
            color: white;
            border: none;
        }

        .admin-stat-card .stat-value {
            font-size: 2rem;
            font-weight: bold;
        }

        .admin-stat-card .stat-label {
            font-size: 0.9rem;
            opacity: 0.8;
        }
    </style>

    <!-- Scripts -->
    @stack('styles')
</head>
<body class="admin-layout">
    <!-- Header -->
    <header class="admin-header">
        <div class="container-fluid">
            <div class="row align-items-center">
                <div class="col-md-6">
                    <h1 class="h4 mb-0 text-dark">{{ $title }}</h1>
                </div>
                <div class="col-md-6 text-end">
                    <div class="dropdown">
                        <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
                            <i class="bi bi-person-circle me-2"></i>{{ auth()->user()->name }}
                        </button>
                        <ul class="dropdown-menu">
                            <li><a class="dropdown-item" href="{{ route('profile.edit') }}"><i class="bi bi-person me-2"></i>Perfil</a></li>
                            <li><hr class="dropdown-divider"></li>
                            <li>
                                <form method="POST" action="{{ route('logout') }}">
                                    @csrf
                                    <button type="submit" class="dropdown-item"><i class="bi bi-box-arrow-right me-2"></i>Sair</button>
                                </form>
                            </li>
                        </ul>
                    </div>
                </div>
            </div>
        </div>
    </header>

    <div class="container-fluid">
        <div class="row">
            @if($sidebar)
                <x-layout.sidebar />
            @endif

            <main class="col {{ $sidebar ? 'col-md-9' : 'col-md-12' }} ms-sm-auto px-md-4">
                <div class="admin-content">
                    <!-- Breadcrumb -->
                    @if(!empty($breadcrumbs))
                        <x-layout.breadcrumbs :breadcrumbs="$breadcrumbs" />
                    @endif

                    <!-- Content -->
                    {{ $slot }}
                </div>
            </main>
        </div>
    </div>

    <!-- Scripts -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    @stack('scripts')
</body>
</html>

3. Auth Layout Component

Componente para layout de páginas de autenticação.

Uso Básico

blade
<x-layout.auth :title="$title" :showLogo="$showLogo" />

Parâmetros

Parâmetro Tipo Descrição Padrão
title string Título da página Obrigatório
showLogo bool Exibir logo da empresa true
background string Imagem de fundo null

Estrutura

blade
@props([
    'title',
    'showLogo' => true,
    'background' => null
])

<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ $title }} - {{ tenant('name') ?? 'Easy Budget' }}</title>

    <!-- Fonts -->
    <link rel="preconnect" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

    <!-- Styles -->
    <style>
        :root {
            --auth-bg: #f8f9fa;
            --auth-card: #ffffff;
            --auth-border: #e9ecef;
            --auth-primary: #0d6efd;
            --auth-text: #333333;
            --auth-muted: #6c757d;
        }

        body {
            background-color: var(--auth-bg);
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            margin: 0;
        }

        .auth-container {
            width: 100%;
            max-width: 400px;
            margin: 0 auto;
            padding: 2rem;
        }

        .auth-card {
            background: var(--auth-card);
            border: 1px solid var(--auth-border);
            border-radius: 12px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            padding: 2rem;
            position: relative;
            overflow: hidden;
        }

        .auth-card::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 4px;
            background: linear-gradient(90deg, var(--auth-primary), #6610f2);
        }

        .auth-header {
            text-align: center;
            margin-bottom: 2rem;
        }

        .auth-logo {
            margin-bottom: 1rem;
        }

        .auth-logo img {
            max-height: 60px;
        }

        .auth-title {
            font-size: 1.5rem;
            font-weight: 600;
            color: var(--auth-text);
            margin: 0;
        }

        .auth-subtitle {
            color: var(--auth-muted);
            font-size: 0.9rem;
            margin-top: 0.5rem;
        }

        .auth-form {
            margin-top: 2rem;
        }

        .form-group {
            margin-bottom: 1.5rem;
        }

        .form-label {
            display: block;
            margin-bottom: 0.5rem;
            font-weight: 500;
            color: var(--auth-text);
            font-size: 0.875rem;
        }

        .form-control {
            width: 100%;
            padding: 0.75rem;
            border: 1px solid var(--auth-border);
            border-radius: 8px;
            font-size: 1rem;
            transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
            background-color: #fff;
        }

        .form-control:focus {
            border-color: var(--auth-primary);
            box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
            outline: none;
        }

        .form-control.is-invalid {
            border-color: #dc3545;
        }

        .invalid-feedback {
            display: block;
            width: 100%;
            margin-top: 0.25rem;
            font-size: 0.875em;
            color: #dc3545;
        }

        .btn-auth {
            width: 100%;
            padding: 0.75rem;
            border: none;
            border-radius: 8px;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s ease;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .btn-primary {
            background-color: var(--auth-primary);
            color: white;
        }

        .btn-primary:hover {
            background-color: #0b5ed7;
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(13, 110, 253, 0.3);
        }

        .btn-secondary {
            background-color: #6c757d;
            color: white;
        }

        .btn-secondary:hover {
            background-color: #5c636a;
            transform: translateY(-1px);
        }

        .auth-links {
            text-align: center;
            margin-top: 1.5rem;
            font-size: 0.9rem;
            color: var(--auth-muted);
        }

        .auth-links a {
            color: var(--auth-primary);
            text-decoration: none;
            font-weight: 500;
        }

        .auth-links a:hover {
            text-decoration: underline;
        }

        .auth-footer {
            text-align: center;
            margin-top: 2rem;
            font-size: 0.8rem;
            color: var(--auth-muted);
            border-top: 1px solid var(--auth-border);
            padding-top: 1.5rem;
        }

        /* Responsive */
        @media (max-width: 480px) {
            .auth-container {
                padding: 1rem;
            }

            .auth-card {
                padding: 1.5rem;
            }
        }
    </style>

    @stack('styles')
</head>
<body>
    <div class="auth-container">
        <div class="auth-card">
            <!-- Header -->
            <div class="auth-header">
                @if($showLogo && tenant('logo'))
                    <div class="auth-logo">
                        <img src="{{ asset('storage/' . tenant('logo')) }}" alt="{{ tenant('name') }}">
                    </div>
                @endif

                <h1 class="auth-title">{{ $title }}</h1>
                <p class="auth-subtitle">Bem-vindo ao {{ tenant('name') ?? 'Easy Budget' }}</p>
            </div>

            <!-- Content -->
            <div class="auth-form">
                {{ $slot }}
            </div>

            <!-- Footer -->
            <div class="auth-footer">
                <p>© {{ date('Y') }} {{ tenant('name') ?? 'Easy Budget' }}. Todos os direitos reservados.</p>
            </div>
        </div>
    </div>

    <!-- Scripts -->
    @stack('scripts')
</body>
</html>

4. Dashboard Layout Component

Componente para layout de dashboard.

Uso Básico

blade
<x-layout.dashboard :title="$title" :stats="$stats" :charts="$charts" />

Parâmetros

Parâmetro Tipo Descrição Padrão
title string Título do dashboard Obrigatório
stats array Estatísticas do dashboard []
charts array Gráficos do dashboard []
filters bool Exibir filtros true

Estrutura

blade
@props([
    'title',
    'stats' => [],
    'charts' => [],
    'filters' => true
])

<x-layout.app :title="$title">
    <div class="dashboard-container">
        <!-- Header -->
        <div class="dashboard-header">
            <div class="d-flex justify-content-between align-items-center">
                <div>
                    <h1 class="h2 mb-0">{{ $title }}</h1>
                    <p class="text-muted mb-0">Visão geral do seu negócio</p>
                </div>

                @if($filters)
                    <div class="dashboard-filters">
                        <div class="btn-group" role="group">
                            <button type="button" class="btn btn-outline-primary btn-sm active" data-filter="today">Hoje</button>
                            <button type="button" class="btn btn-outline-primary btn-sm" data-filter="week">Semana</button>
                            <button type="button" class="btn btn-outline-primary btn-sm" data-filter="month">Mês</button>
                            <button type="button" class="btn btn-outline-primary btn-sm" data-filter="year">Ano</button>
                        </div>
                    </div>
                @endif
            </div>
        </div>

        <!-- Stats -->
        @if(!empty($stats))
            <div class="dashboard-stats">
                <div class="row g-4">
                    @foreach($stats as $stat)
                        <div class="col-md-3">
                            <div class="stat-card">
                                <div class="stat-icon {{ $stat['icon_color'] ?? 'bg-primary' }}">
                                    <i class="bi {{ $stat['icon'] }}"></i>
                                </div>
                                <div class="stat-content">
                                    <div class="stat-value">{{ $stat['value'] }}</div>
                                    <div class="stat-label">{{ $stat['label'] }}</div>
                                    @if(isset($stat['trend']))
                                        <div class="stat-trend {{ $stat['trend'] >= 0 ? 'trend-up' : 'trend-down' }}">
                                            <i class="bi {{ $stat['trend'] >= 0 ? 'bi-arrow-up' : 'bi-arrow-down' }}"></i>
                                            {{ abs($stat['trend']) }}%
                                        </div>
                                    @endif
                                </div>
                            </div>
                        </div>
                    @endforeach
                </div>
            </div>
        @endif

        <!-- Charts -->
        @if(!empty($charts))
            <div class="dashboard-charts">
                <div class="row g-4">
                    @foreach($charts as $chart)
                        <div class="col-md-6">
                            <div class="chart-card">
                                <div class="chart-header">
                                    <h5 class="mb-0">{{ $chart['title'] }}</h5>
                                </div>
                                <div class="chart-body">
                                    <canvas id="{{ $chart['id'] }}" width="400" height="200"></canvas>
                                </div>
                            </div>
                        </div>
                    @endforeach
                </div>
            </div>
        @endif

        <!-- Content -->
        <div class="dashboard-content">
            {{ $slot }}
        </div>
    </div>

    @push('scripts')
    <script>
        // Dashboard interactivity
        document.addEventListener('DOMContentLoaded', function() {
            const filterButtons = document.querySelectorAll('.dashboard-filters .btn');
            filterButtons.forEach(button => {
                button.addEventListener('click', function() {
                    filterButtons.forEach(btn => btn.classList.remove('active'));
                    this.classList.add('active');

                    // Trigger filter change
                    const filter = this.getAttribute('data-filter');
                    applyDashboardFilter(filter);
                });
            });

            // Initialize charts
            @foreach($charts as $chart)
                initChart('{{ $chart['id'] }}', @json($chart['data']));
            @endforeach
        });

        function applyDashboardFilter(filter) {
            // Implement filter logic
            console.log('Applying filter:', filter);
        }

        function initChart(canvasId, data) {
            const ctx = document.getElementById(canvasId).getContext('2d');
            new Chart(ctx, {
                type: data.type || 'line',
                data: data,
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            position: 'top',
                        }
                    }
                }
            });
        }
    </script>
    @endpush
</x-layout.app>

5. Sidebar Component

Componente para barra lateral de navegação.

Uso Básico

blade
<x-layout.sidebar :menu="$menu" :active="$active" />

Parâmetros

Parâmetro Tipo Descrição Padrão
menu array Estrutura do menu []
active string Item ativo ''
user User Usuário logado null

Estrutura

blade
@props([
    'menu' => [],
    'active' => '',
    'user' => null
])

<aside class="sidebar" id="sidebar">
    <!-- Logo -->
    <div class="sidebar-header">
        <div class="sidebar-logo">
            @if(tenant('logo'))
                <img src="{{ asset('storage/' . tenant('logo')) }}" alt="{{ tenant('name') }}" class="logo-img">
            @else
                <div class="logo-text">{{ tenant('name') ?? 'Easy Budget' }}</div>
            @endif
        </div>
        <button class="sidebar-toggle" id="sidebarToggle">
            <i class="bi bi-list"></i>
        </button>
    </div>

    <!-- User Info -->
    @if($user)
        <div class="sidebar-user">
            <div class="user-avatar">
                @if($user->avatar)
                    <img src="{{ asset('storage/' . $user->avatar) }}" alt="{{ $user->name }}">
                @else
                    <div class="avatar-placeholder">{{ substr($user->name, 0, 2) }}</div>
                @endif
            </div>
            <div class="user-info">
                <div class="user-name">{{ $user->name }}</div>
                <div class="user-role">{{ $user->role ?? 'Usuário' }}</div>
            </div>
        </div>
    @endif

    <!-- Navigation -->
    <nav class="sidebar-nav">
        <ul class="nav flex-column">
            @foreach($menu as $item)
                @if(isset($item['divider']))
                    <li class="nav-divider">{{ $item['divider'] }}</li>
                @elseif(isset($item['header']))
                    <li class="nav-header">{{ $item['header'] }}</li>
                @else
                    <li class="nav-item">
                        @if(isset($item['children']))
                            <a class="nav-link nav-dropdown {{ in_array($active, $item['children']) ? 'active' : '' }}" href="#" data-bs-toggle="collapse" data-bs-target="#collapse-{{ Str::slug($item['label']) }}">
                                <i class="bi {{ $item['icon'] }}"></i>
                                <span>{{ $item['label'] }}</span>
                                <i class="bi bi-chevron-down dropdown-icon"></i>
                            </a>
                            <div class="collapse {{ in_array($active, $item['children']) ? 'show' : '' }}" id="collapse-{{ Str::slug($item['label']) }}">
                                <ul class="nav flex-column sub-menu">
                                    @foreach($item['children'] as $child)
                                        <li class="nav-item">
                                            <a class="nav-link {{ $active === $child ? 'active' : '' }}" href="{{ route($child) }}">
                                                <i class="bi bi-circle"></i>
                                                <span>{{ $item['labels'][$child] ?? $child }}</span>
                                            </a>
                                        </li>
                                    @endforeach
                                </ul>
                            </div>
                        @else
                            <a class="nav-link {{ $active === $item['route'] ? 'active' : '' }}" href="{{ route($item['route']) }}">
                                <i class="bi {{ $item['icon'] }}"></i>
                                <span>{{ $item['label'] }}</span>
                            </a>
                        @endif
                    </li>
                @endif
            @endforeach
        </ul>
    </nav>

    <!-- Footer -->
    <div class="sidebar-footer">
        <div class="sidebar-version">
            <small class="text-muted">Versão {{ config('app.version', '1.0.0') }}</small>
        </div>
        <div class="sidebar-actions">
            <a href="{{ route('profile.edit') }}" class="sidebar-action" title="Perfil">
                <i class="bi bi-person"></i>
            </a>
            <a href="{{ route('settings.index') }}" class="sidebar-action" title="Configurações">
                <i class="bi bi-gear"></i>
            </a>
            <form method="POST" action="{{ route('logout') }}" class="sidebar-action" title="Sair">
                @csrf
                <button type="submit" class="btn-sidebar-action">
                    <i class="bi bi-box-arrow-right"></i>
                </button>
            </form>
        </div>
    </div>
</aside>

@push('styles')
<style>
    .sidebar {
        width: 250px;
        background-color: #fff;
        border-right: 1px solid #e9ecef;
        height: 100vh;
        position: fixed;
        top: 0;
        left: 0;
        z-index: 1000;
        transition: transform 0.3s ease;
        box-shadow: 2px 0 10px rgba(0,0,0,0.1);
    }

    .sidebar-header {
        padding: 20px;
        border-bottom: 1px solid #e9ecef;
        display: flex;
        align-items: center;
        justify-content: space-between;
    }

    .logo-img {
        max-height: 40px;
        width: auto;
    }

    .logo-text {
        font-size: 1.2rem;
        font-weight: bold;
        color: #0d6efd;
    }

    .sidebar-toggle {
        display: none;
        background: none;
        border: none;
        font-size: 1.2rem;
        color: #6c757d;
        cursor: pointer;
    }

    .sidebar-user {
        padding: 20px;
        border-bottom: 1px solid #e9ecef;
        display: flex;
        align-items: center;
        gap: 15px;
    }

    .user-avatar img {
        width: 40px;
        height: 40px;
        border-radius: 50%;
        object-fit: cover;
    }

    .avatar-placeholder {
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background-color: #0d6efd;
        color: white;
        display: flex;
        align-items: center;
        justify-content: center;
        font-weight: bold;
        font-size: 1rem;
    }

    .user-info {
        flex: 1;
    }

    .user-name {
        font-weight: 600;
        font-size: 1rem;
    }

    .user-role {
        font-size: 0.8rem;
        color: #6c757d;
    }

    .sidebar-nav {
        padding: 20px;
        flex: 1;
        overflow-y: auto;
    }

    .nav-link {
        color: #333;
        padding: 12px 15px;
        border-radius: 8px;
        margin-bottom: 5px;
        transition: all 0.2s ease;
        display: flex;
        align-items: center;
        gap: 10px;
        text-decoration: none;
    }

    .nav-link:hover {
        background-color: #f8f9fa;
        color: #0d6efd;
    }

    .nav-link.active {
        background-color: #e7f1ff;
        color: #0d6efd;
        border-left: 4px solid #0d6efd;
    }

    .nav-link i {
        font-size: 1.1rem;
        width: 20px;
        text-align: center;
    }

    .nav-dropdown {
        position: relative;
    }

    .dropdown-icon {
        margin-left: auto;
        transition: transform 0.2s ease;
    }

    .nav-dropdown[aria-expanded="true"] .dropdown-icon {
        transform: rotate(180deg);
    }

    .sub-menu {
        padding-left: 30px;
    }

    .sub-menu .nav-link {
        padding: 8px 15px;
        font-size: 0.9rem;
        border-left: 2px solid transparent;
    }

    .sub-menu .nav-link.active {
        border-left-color: #0d6efd;
        background-color: transparent;
    }

    .nav-divider {
        padding: 10px 20px;
        font-size: 0.8rem;
        text-transform: uppercase;
        color: #6c757d;
        font-weight: 600;
        letter-spacing: 0.5px;
    }

    .nav-header {
        padding: 15px 20px;
        font-size: 0.9rem;
        color: #6c757d;
        font-weight: 600;
        border-bottom: 1px solid #e9ecef;
    }

    .sidebar-footer {
        padding: 20px;
        border-top: 1px solid #e9ecef;
        display: flex;
        flex-direction: column;
        gap: 15px;
    }

    .sidebar-actions {
        display: flex;
        gap: 10px;
    }

    .sidebar-action {
        background: none;
        border: none;
        color: #6c757d;
        cursor: pointer;
        padding: 8px;
        border-radius: 6px;
        transition: all 0.2s ease;
        text-decoration: none;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    .sidebar-action:hover {
        background-color: #f8f9fa;
        color: #0d6efd;
    }

    .btn-sidebar-action {
        background: none;
        border: none;
        color: #6c757d;
        cursor: pointer;
        padding: 8px;
        border-radius: 6px;
        transition: all 0.2s ease;
        width: 100%;
        text-align: left;
    }

    .btn-sidebar-action:hover {
        background-color: #f8f9fa;
        color: #0d6efd;
    }

    /* Responsive */
    @media (max-width: 768px) {
        .sidebar {
            transform: translateX(-100%);
        }

        .sidebar.show {
            transform: translateX(0);
        }

        .sidebar-toggle {
            display: block;
        }

        main {
            margin-left: 0 !important;
        }
    }
</style>
@endpush

@push('scripts')
<script>
    document.addEventListener('DOMContentLoaded', function() {
        const sidebar = document.getElementById('sidebar');
        const sidebarToggle = document.getElementById('sidebarToggle');

        // Toggle sidebar
        if (sidebarToggle) {
            sidebarToggle.addEventListener('click', function() {
                sidebar.classList.toggle('show');
            });
        }

        // Close sidebar on mobile when clicking outside
        document.addEventListener('click', function(event) {
            if (window.innerWidth <= 768) {
                const isClickInsideSidebar = sidebar.contains(event.target);
                const isClickOnToggle = sidebarToggle.contains(event.target);

                if (!isClickInsideSidebar && !isClickOnToggle) {
                    sidebar.classList.remove('show');
                }
            }
        });

        // Handle dropdowns
        const dropdowns = document.querySelectorAll('.nav-dropdown');
        dropdowns.forEach(dropdown => {
            dropdown.addEventListener('click', function(e) {
                e.preventDefault();
                const target = this.getAttribute('data-bs-target');
                const collapse = document.querySelector(target);

                if (collapse) {
                    const isExpanded = collapse.classList.contains('show');

                    // Close all other dropdowns
                    document.querySelectorAll('.sub-menu.show').forEach(other => {
                        if (other !== collapse) {
                            other.classList.remove('show');
                        }
                    });

                    // Toggle current dropdown
                    if (isExpanded) {
                        collapse.classList.remove('show');
                    } else {
                        collapse.classList.add('show');
                    }
                }
            });
        });
    });
</script>
@endpush

6. Navbar Component

Componente para barra de navegação superior.

Uso Básico

blade
<x-layout.navbar :user="$user" :notifications="$notifications" />

Parâmetros

Parâmetro Tipo Descrição Padrão
user User Usuário logado Obrigatório
notifications array Notificações []
search bool Exibir campo de busca true

Estrutura

blade
@props([
    'user',
    'notifications' => [],
    'search' => true
])

<nav class="navbar navbar-expand-lg navbar-light bg-white border-bottom">
    <div class="container-fluid">
        <!-- Brand -->
        <a class="navbar-brand d-flex align-items-center" href="{{ route('dashboard') }}">
            @if(tenant('logo'))
                <img src="{{ asset('storage/' . tenant('logo')) }}" alt="{{ tenant('name') }}" class="brand-logo">
            @else
                <span class="brand-text">{{ tenant('name') ?? 'Easy Budget' }}</span>
            @endif
        </a>

        <!-- Toggler -->
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent">
            <span class="navbar-toggler-icon"></span>
        </button>

        <!-- Content -->
        <div class="collapse navbar-collapse" id="navbarContent">
            <!-- Search -->
            @if($search)
                <form class="d-flex me-auto" style="max-width: 400px;">
                    <div class="input-group">
                        <input type="text" class="form-control" placeholder="Buscar..." aria-label="Buscar">
                        <button class="btn btn-outline-secondary" type="button">
                            <i class="bi bi-search"></i>
                        </button>
                    </div>
                </form>
            @endif

            <!-- Right Menu -->
            <ul class="navbar-nav ms-auto align-items-center">
                <!-- Notifications -->
                <li class="nav-item dropdown me-3">
                    <a class="nav-link position-relative" href="#" role="button" data-bs-toggle="dropdown">
                        <i class="bi bi-bell fs-5"></i>
                        @if(count($notifications) > 0)
                            <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
                                {{ count($notifications) }}
                            </span>
                        @endif
                    </a>
                    <ul class="dropdown-menu dropdown-menu-end">
                        <li><h6 class="dropdown-header">Notificações</h6></li>
                        @forelse($notifications as $notification)
                            <li>
                                <a class="dropdown-item" href="{{ $notification['url'] }}">
                                    <div class="d-flex w-100 justify-content-between">
                                        <div>
                                            <strong>{{ $notification['title'] }}</strong>
                                            <div class="small text-muted">{{ $notification['message'] }}</div>
                                        </div>
                                        <small class="text-muted">{{ $notification['time'] }}</small>
                                    </div>
                                </a>
                            </li>
                        @empty
                            <li><span class="dropdown-item text-muted">Nenhuma notificação</span></li>
                        @endforelse
                        <li><hr class="dropdown-divider"></li>
                        <li><a class="dropdown-item text-primary" href="{{ route('notifications.index') }}">Ver todas</a></li>
                    </ul>
                </li>

                <!-- User Menu -->
                <li class="nav-item dropdown">
                    <a class="nav-link d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
                        @if($user->avatar)
                            <img src="{{ asset('storage/' . $user->avatar) }}" alt="{{ $user->name }}" class="rounded-circle me-2" style="width: 32px; height: 32px; object-fit: cover;">
                        @else
                            <div class="avatar-circle me-2">{{ substr($user->name, 0, 2) }}</div>
                        @endif
                        <span class="d-none d-md-inline">{{ $user->name }}</span>
                        <i class="bi bi-chevron-down ms-1"></i>
                    </a>
                    <ul class="dropdown-menu dropdown-menu-end">
                        <li><a class="dropdown-item" href="{{ route('profile.edit') }}"><i class="bi bi-person me-2"></i>Perfil</a></li>
                        <li><a class="dropdown-item" href="{{ route('settings.index') }}"><i class="bi bi-gear me-2"></i>Configurações</a></li>
                        <li><hr class="dropdown-divider"></li>
                        <li>
                            <form method="POST" action="{{ route('logout') }}">
                                @csrf
                                <button type="submit" class="dropdown-item"><i class="bi bi-box-arrow-right me-2"></i>Sair</button>
                            </form>
                        </li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>

@push('styles')
<style>
    .navbar {
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        position: sticky;
        top: 0;
        z-index: 100;
    }

    .brand-logo {
        max-height: 30px;
        margin-right: 10px;
    }

    .brand-text {
        font-weight: bold;
        color: #0d6efd;
        font-size: 1.2rem;
    }

    .avatar-circle {
        width: 32px;
        height: 32px;
        border-radius: 50%;
        background-color: #0d6efd;
        color: white;
        display: flex;
        align-items: center;
        justify-content: center;
        font-weight: bold;
        font-size: 0.9rem;
    }

    .navbar-nav .nav-link {
        color: #333;
        padding: 0.5rem 1rem;
        border-radius: 6px;
        transition: all 0.2s ease;
    }

    .navbar-nav .nav-link:hover {
        background-color: #f8f9fa;
        color: #0d6efd;
    }

    .dropdown-menu {
        border: 1px solid #e9ecef;
        box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    }

    .dropdown-item {
        padding: 0.75rem 1rem;
        transition: all 0.2s ease;
    }

    .dropdown-item:hover {
        background-color: #f8f9fa;
    }

    /* Responsive */
    @media (max-width: 768px) {
        .brand-text {
            display: none;
        }

        .navbar-nav {
            margin-top: 1rem;
        }

        .navbar-nav .nav-link {
            padding: 0.75rem 1rem;
        }
    }
</style>
@endpush

7. Breadcrumbs Component

Componente para breadcrumb de navegação.

Uso Básico

blade
<x-layout.breadcrumbs :breadcrumbs="$breadcrumbs" />

Parâmetros

Parâmetro Tipo Descrição Padrão
breadcrumbs array Estrutura do breadcrumb Obrigatório

Estrutura

blade
@props(['breadcrumbs'])

@if(!empty($breadcrumbs))
    <nav aria-label="breadcrumb">
        <ol class="breadcrumb">
            @foreach($breadcrumbs as $breadcrumb)
                @if($loop->last)
                    <li class="breadcrumb-item active" aria-current="page">
                        @if(isset($breadcrumb['icon']))
                            <i class="bi {{ $breadcrumb['icon'] }} me-2"></i>
                        @endif
                        {{ $breadcrumb['label'] }}
                    </li>
                @else
                    <li class="breadcrumb-item">
                        <a href="{{ $breadcrumb['url'] }}">
                            @if(isset($breadcrumb['icon']))
                                <i class="bi {{ $breadcrumb['icon'] }} me-2"></i>
                            @endif
                            {{ $breadcrumb['label'] }}
                        </a>
                    </li>
                @endif
            @endforeach
        </ol>
    </nav>
@endif

@push('styles')
<style>
    .breadcrumb {
        background-color: transparent;
        padding: 0;
        margin-bottom: 1.5rem;
        border-radius: 0;
    }

    .breadcrumb-item {
        font-size: 0.9rem;
        color: #6c757d;
    }

    .breadcrumb-item a {
        color: #0d6efd;
        text-decoration: none;
        transition: color 0.2s ease;
    }

    .breadcrumb-item a:hover {
        color: #0b5ed7;
        text-decoration: underline;
    }

    .breadcrumb-item.active {
        color: #333;
        font-weight: 500;
    }

    .breadcrumb-item + .breadcrumb-item::before {
        color: #6c757d;
    }
</style>
@endpush

8. Footer Component

Componente para rodapé do sistema.

Uso Básico

blade
<x-layout.footer :links="$links" :social="$social" />

Parâmetros

Parâmetro Tipo Descrição Padrão
links array Links do rodapé []
social array Links sociais []
showCopyright bool Exibir copyright true

Estrutura

blade
@props([
    'links' => [],
    'social' => [],
    'showCopyright' => true
])

<footer class="footer bg-light border-top">
    <div class="container">
        <div class="row">
            <!-- Company Info -->
            <div class="col-md-4 mb-3 mb-md-0">
                <div class="footer-logo">
                    @if(tenant('logo'))
                        <img src="{{ asset('storage/' . tenant('logo')) }}" alt="{{ tenant('name') }}" class="footer-logo-img">
                    @else
                        <h5 class="footer-title">{{ tenant('name') ?? 'Easy Budget' }}</h5>
                    @endif
                </div>
                <p class="footer-description text-muted">
                    {{ tenant('description') ?? 'Sua solução de gestão empresarial completa.' }}
                </p>
            </div>

            <!-- Quick Links -->
            @if(!empty($links))
                <div class="col-md-2 mb-3 mb-md-0">
                    <h6 class="footer-heading">Links Rápidos</h6>
                    <ul class="list-unstyled">
                        @foreach($links as $link)
                            <li><a href="{{ $link['url'] }}" class="footer-link">{{ $link['label'] }}</a></li>
                        @endforeach
                    </ul>
                </div>
            @endif

            <!-- Support -->
            <div class="col-md-3 mb-3 mb-md-0">
                <h6 class="footer-heading">Suporte</h6>
                <ul class="list-unstyled">
                    <li><a href="{{ route('support.create') }}" class="footer-link">Centro de Ajuda</a></li>
                    <li><a href="{{ route('docs.index') }}" class="footer-link">Documentação</a></li>
                    <li><a href="{{ route('contact.index') }}" class="footer-link">Contato</a></li>
                </ul>
            </div>

            <!-- Social -->
            @if(!empty($social))
                <div class="col-md-3">
                    <h6 class="footer-heading">Siga-nos</h6>
                    <div class="social-links">
                        @foreach($social as $socialLink)
                            <a href="{{ $socialLink['url'] }}" class="social-link" target="_blank" rel="noopener noreferrer">
                                <i class="bi bi-{{ $socialLink['icon'] }}"></i>
                            </a>
                        @endforeach
                    </div>
                </div>
            @endif
        </div>

        <!-- Bottom Bar -->
        <div class="row mt-4">
            <div class="col-12">
                <div class="footer-bottom">
                    @if($showCopyright)
                        <div class="copyright">
                            © {{ date('Y') }} {{ tenant('name') ?? 'Easy Budget' }}. Todos os direitos reservados.
                        </div>
                    @endif
                    <div class="footer-links">
                        <a href="{{ route('terms.index') }}" class="footer-link small">Termos de Uso</a>
                        <a href="{{ route('privacy.index') }}" class="footer-link small">Política de Privacidade</a>
                        <a href="{{ route('cookies.index') }}" class="footer-link small">Cookies</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</footer>

@push('styles')
<style>
    .footer {
        margin-top: auto;
    }

    .footer-logo-img {
        max-height: 40px;
        margin-bottom: 10px;
    }

    .footer-title {
        color: #0d6efd;
        margin-bottom: 10px;
    }

    .footer-description {
        font-size: 0.9rem;
        line-height: 1.5;
    }

    .footer-heading {
        font-size: 1rem;
        font-weight: 600;
        color: #333;
        margin-bottom: 15px;
        position: relative;
        padding-bottom: 10px;
    }

    .footer-heading::after {
        content: '';
        position: absolute;
        bottom: 0;
        left: 0;
        width: 40px;
        height: 2px;
        background-color: #0d6efd;
    }

    .footer-link {
        color: #6c757d;
        text-decoration: none;
        font-size: 0.9rem;
        transition: color 0.2s ease;
    }

    .footer-link:hover {
        color: #0d6efd;
        text-decoration: none;
    }

    .social-links {
        display: flex;
        gap: 10px;
    }

    .social-link {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 36px;
        height: 36px;
        background-color: #0d6efd;
        color: white;
        border-radius: 50%;
        text-decoration: none;
        transition: all 0.2s ease;
    }

    .social-link:hover {
        background-color: #0b5ed7;
        transform: translateY(-1px);
    }

    .footer-bottom {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding-top: 20px;
        border-top: 1px solid #e9ecef;
        margin-top: 20px;
    }

    .copyright {
        font-size: 0.8rem;
        color: #6c757d;
    }

    .footer-links {
        display: flex;
        gap: 15px;
    }

    /* Responsive */
    @media (max-width: 768px) {
        .footer-bottom {
            flex-direction: column;
            gap: 10px;
            text-align: center;
        }

        .footer-links {
            justify-content: center;
        }
    }
</style>
@endpush

9. Theme Switcher Component

Componente para troca de tema.

Uso Básico

blade
<x-layout.theme-switcher :currentTheme="$currentTheme" />

Parâmetros

Parâmetro Tipo Descrição Padrão
currentTheme string Tema atual auto
themes array Temas disponíveis []

Estrutura

blade
@props([
    'currentTheme' => 'auto',
    'themes' => [
        'auto' => ['name' => 'Automático', 'icon' => 'bi-circle-half'],
        'light' => ['name' => 'Claro', 'icon' => 'bi-sun'],
        'dark' => ['name' => 'Escuro', 'icon' => 'bi-moon']
    ]
])

<div class="theme-switcher" id="themeSwitcher">
    <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
        <i class="{{ $themes[$currentTheme]['icon'] }} me-2"></i>
        {{ $themes[$currentTheme]['name'] }}
    </button>
    <ul class="dropdown-menu">
        @foreach($themes as $key => $theme)
            <li>
                <button class="dropdown-item theme-option" type="button" data-theme="{{ $key }}">
                    <i class="{{ $theme['icon'] }} me-2"></i>
                    {{ $theme['name'] }}
                </button>
            </li>
        @endforeach
    </ul>
</div>

@push('styles')
<style>
    .theme-switcher {
        display: inline-block;
    }

    .theme-option {
        display: flex;
        align-items: center;
        gap: 10px;
        width: 100%;
        padding: 8px 16px;
        border: none;
        background: none;
        cursor: pointer;
        text-align: left;
        font-size: 0.9rem;
        color: #333;
        transition: all 0.2s ease;
    }

    .theme-option:hover {
        background-color: #f8f9fa;
        color: #0d6efd;
    }

    .theme-option.active {
        background-color: #e7f1ff;
        color: #0d6efd;
        font-weight: 500;
    }
</style>
@endpush

@push('scripts')
<script>
    document.addEventListener('DOMContentLoaded', function() {
        const themeSwitcher = document.getElementById('themeSwitcher');
        const themeOptions = document.querySelectorAll('.theme-option');
        const currentTheme = localStorage.getItem('theme') || 'auto';

        // Apply initial theme
        applyTheme(currentTheme);

        // Handle theme changes
        themeOptions.forEach(option => {
            option.addEventListener('click', function() {
                const theme = this.getAttribute('data-theme');
                applyTheme(theme);
                localStorage.setItem('theme', theme);
            });
        });

        function applyTheme(theme) {
            // Update data-theme attribute
            document.documentElement.setAttribute('data-theme', theme);

            // Update button text and icon
            const button = themeSwitcher.querySelector('button');
            const icon = button.querySelector('i');
            const text = button.querySelector('span');

            // Update button content based on theme
            const themeData = getThemeData(theme);
            icon.className = themeData.icon + ' me-2';

            // Update dropdown items
            themeOptions.forEach(option => {
                option.classList.remove('active');
                if (option.getAttribute('data-theme') === theme) {
                    option.classList.add('active');
                }
            });

            // Dispatch theme change event
            window.dispatchEvent(new CustomEvent('themechange', { detail: { theme } }));
        }

        function getThemeData(theme) {
            const themes = @json($themes);
            return themes[theme] || themes.auto;
        }

        // Listen for system theme changes
        const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
        mediaQuery.addEventListener('change', (e) => {
            if (currentTheme === 'auto') {
                applyTheme(e.matches ? 'dark' : 'light');
            }
        });
    });
</script>
@endpush

10. Notifications Component

Componente para sistema de notificações.

Uso Básico

blade
<x-layout.notifications :notifications="$notifications" />

Parâmetros

Parâmetro Tipo Descrição Padrão
notifications array Notificações []
maxVisible int Máximo de notificações visíveis 5

Estrutura

blade
@props([
    'notifications' => [],
    'maxVisible' => 5
])

<div class="notifications-container" id="notificationsContainer">
    @foreach($notifications as $notification)
        <div class="notification-item {{ $notification['type'] }}" data-id="{{ $notification['id'] }}">
            <div class="notification-content">
                <div class="notification-icon">
                    <i class="bi {{ $notification['icon'] }}"></i>
                </div>
                <div class="notification-body">
                    <div class="notification-title">{{ $notification['title'] }}</div>
                    <div class="notification-message">{{ $notification['message'] }}</div>
                    <div class="notification-time">{{ $notification['time'] }}</div>
                </div>
                <button class="notification-close" type="button" data-id="{{ $notification['id'] }}">
                    <i class="bi bi-x"></i>
                </button>
            </div>
        </div>
    @endforeach
</div>

@push('styles')
<style>
    .notifications-container {
        position: fixed;
        top: 20px;
        right: 20px;
        z-index: 1050;
        display: flex;
        flex-direction: column;
        gap: 10px;
        max-width: 350px;
    }

    .notification-item {
        background: white;
        border: 1px solid #e9ecef;
        border-left: 4px solid;
        border-radius: 8px;
        box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        animation: slideIn 0.3s ease;
        transform: translateX(100%);
        transition: transform 0.3s ease;
    }

    .notification-item.show {
        transform: translateX(0);
    }

    .notification-item.success {
        border-left-color: #27ae60;
    }

    .notification-item.error {
        border-left-color: #e74c3c;
    }

    .notification-item.warning {
        border-left-color: #f39c12;
    }

    .notification-item.info {
        border-left-color: #3498db;
    }

    .notification-content {
        display: flex;
        gap: 12px;
        padding: 15px;
        position: relative;
    }

    .notification-icon {
        width: 40px;
        height: 40px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 1.2rem;
        color: white;
        flex-shrink: 0;
    }

    .notification-item.success .notification-icon {
        background-color: #27ae60;
    }

    .notification-item.error .notification-icon {
        background-color: #e74c3c;
    }

    .notification-item.warning .notification-icon {
        background-color: #f39c12;
    }

    .notification-item.info .notification-icon {
        background-color: #3498db;
    }

    .notification-body {
        flex: 1;
        min-width: 0;
    }

    .notification-title {
        font-weight: 600;
        font-size: 0.9rem;
        margin-bottom: 4px;
        color: #333;
    }

    .notification-message {
        font-size: 0.85rem;
        color: #666;
        margin-bottom: 8px;
        line-height: 1.4;
    }

    .notification-time {
        font-size: 0.75rem;
        color: #999;
    }

    .notification-close {
        position: absolute;
        top: 10px;
        right: 10px;
        background: none;
        border: none;
        color: #999;
        cursor: pointer;
        width: 24px;
        height: 24px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: all 0.2s ease;
    }

    .notification-close:hover {
        background-color: #f8f9fa;
        color: #333;
    }

    @keyframes slideIn {
        from {
            transform: translateX(100%);
            opacity: 0;
        }
        to {
            transform: translateX(0);
            opacity: 1;
        }
    }

    @keyframes slideOut {
        from {
            transform: translateX(0);
            opacity: 1;
        }
        to {
            transform: translateX(100%);
            opacity: 0;
        }
    }

    .notification-item.removing {
        animation: slideOut 0.3s ease forwards;
    }

    /* Responsive */
    @media (max-width: 768px) {
        .notifications-container {
            right: 15px;
            left: 15px;
            max-width: none;
        }

        .notification-item {
            max-width: none;
        }
    }
</style>
@endpush

@push('scripts')
<script>
    class NotificationManager {
        constructor() {
            this.container = document.getElementById('notificationsContainer');
            this.notifications = new Map();
        }

        show(notification) {
            const id = notification.id || Date.now();
            const element = this.createNotificationElement(notification, id);

            this.container.appendChild(element);
            this.notifications.set(id, element);

            // Auto-hide after 5 seconds
            setTimeout(() => {
                this.hide(id);
            }, 5000);

            return id;
        }

        hide(id) {
            const element = this.notifications.get(id);
            if (element) {
                element.classList.add('removing');
                setTimeout(() => {
                    element.remove();
                    this.notifications.delete(id);
                }, 300);
            }
        }

        createNotificationElement(notification, id) {
            const item = document.createElement('div');
            item.className = `notification-item ${notification.type || 'info'} show`;
            item.setAttribute('data-id', id);

            item.innerHTML = `
                <div class="notification-content">
                    <div class="notification-icon">
                        <i class="bi ${notification.icon || 'bi-info-circle'}"></i>
                    </div>
                    <div class="notification-body">
                        <div class="notification-title">${notification.title}</div>
                        <div class="notification-message">${notification.message}</div>
                        <div class="notification-time">${notification.time || new Date().toLocaleTimeString()}</div>
                    </div>
                    <button class="notification-close" type="button" data-id="${id}">
                        <i class="bi bi-x"></i>
                    </button>
                </div>
            `;

            // Add close button event
            const closeButton = item.querySelector('.notification-close');
            closeButton.addEventListener('click', () => {
                this.hide(id);
            });

            return item;
        }
    }

    // Initialize notification manager
    window.notificationManager = new NotificationManager();

    // Handle existing notifications
    document.addEventListener('DOMContentLoaded', function() {
        const notifications = @json($notifications);
        notifications.forEach(notification => {
            window.notificationManager.show(notification);
        });
    });

    // Global function to show notifications
    window.showNotification = function(notification) {
        if (window.notificationManager) {
            return window.notificationManager.show(notification);
        }
    };
</script>
@endpush

11. Integração com Padrões Existentes

Uso em Views

blade
{{-- Layout Principal --}}
<x-layout.app :title="'Dashboard - ' . tenant('name')">
    <x-layout.breadcrumbs :breadcrumbs="[
        ['label' => 'Dashboard', 'url' => route('dashboard')],
        ['label' => 'Visão Geral']
    ]" />

    <div class="dashboard-content">
        <!-- Conteúdo da página -->
    </div>
</x-layout.app>

{{-- Dashboard --}}
<x-layout.dashboard
    :title="'Visão Geral do Negócio'"
    :stats="$stats"
    :charts="$charts"
/>

{{-- Autenticação --}}
<x-layout.auth :title="'Login'">
    <form method="POST" action="{{ route('login') }}">
        @csrf
        <!-- Campos do formulário -->
    </form>
</x-layout.auth>

Estilos CSS

css
/* Layout Components Styles */
.sidebar {
    width: 250px;
    background-color: #fff;
    border-right: 1px solid #e9ecef;
}

.navbar {
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    position: sticky;
    top: 0;
    z-index: 100;
}

.footer {
    margin-top: auto;
    background-color: #f8f9fa;
    border-top: 1px solid #e9ecef;
}

.notification-item {
    background: white;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}

12. JavaScript Interatividade

Sistema de Layout

javascript
// layout-system.js
document.addEventListener('DOMContentLoaded', function() {
    // Sidebar toggle
    const sidebarToggle = document.getElementById('sidebarToggle');
    const sidebar = document.getElementById('sidebar');

    if (sidebarToggle && sidebar) {
        sidebarToggle.addEventListener('click', function() {
            sidebar.classList.toggle('show');
        });
    }

    // Theme switcher
    const themeSwitcher = document.getElementById('themeSwitcher');
    if (themeSwitcher) {
        // Theme switching logic
    }

    // Responsive navigation
    window.addEventListener('resize', function() {
        if (window.innerWidth > 768) {
            sidebar.classList.remove('show');
        }
    });
});

13. Validação e Segurança

Autorização

php
// LayoutPolicy.php
public function viewSidebar(User $user)
{
    return $user->tenant_id === tenant('id');
}

public function viewNotifications(User $user)
{
    return $user->tenant_id === tenant('id');
}

public function changeTheme(User $user)
{
    return $user->can('manage-settings');
}

Validations

blade
{{-- Sidebar com validação de permissões --}}
@can('viewSidebar')
    <x-layout.sidebar :menu="$menu" :active="$active" />
@endcan

{{-- Theme Switcher com validação de permissões --}}
@can('changeTheme')
    <x-layout.theme-switcher :currentTheme="$currentTheme" />
@endcan

Este padrão de components para layout e navegação garante consistência visual, reutilização de código e manutenibilidade, seguindo os mesmos princípios estabelecidos nos outros components do sistema.

Didn't find tool you were looking for?

Be as detailed as possible for better results