feat: add full project - backend, frontend, docker, specs and configs
This commit is contained in:
parent
b77c7d5a01
commit
e6cb06255b
24489 changed files with 61341 additions and 36 deletions
433
.specify/features/013-header-only-nav/plan.md
Normal file
433
.specify/features/013-header-only-nav/plan.md
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
# Implementation Plan: Navegação Unificada no Header (Remoção do Sidebar)
|
||||
|
||||
**Branch**: `013-header-only-nav` | **Date**: 2026-04-14 | **Spec**: [spec.md](spec.md)
|
||||
**Depends On**: Nenhuma dependência de feature anterior — alterações puramente de layout/componente frontend.
|
||||
|
||||
## Summary
|
||||
|
||||
Remoção dos sidebars de `AdminLayout` e `ClientLayout`, consolidando todos os itens de navegação no `Navbar` (header fixo) via dropdowns contextuais. O dropdown "Admin ▾" aparece apenas para `isAdmin`; o dropdown "Minha Conta ▾" aparece apenas para usuários autenticados não-admin. Nenhuma rota nova, nenhuma alteração de backend. Alterações em 3 arquivos frontend: `Navbar.tsx`, `AdminLayout.tsx` e `ClientLayout.tsx`.
|
||||
|
||||
**Observação sobre ClientLayout + Navbar**: `ClientLayout.tsx` atualmente **não** importa nem renderiza `<Navbar />` (ao contrário de `AdminLayout`). O `App.tsx` também não possui um `<Navbar />` global. Para que o dropdown "Minha Conta ▾" seja exibido nas rotas `/area-do-cliente/*`, **`ClientLayout` deve passar a renderizar `<Navbar />`** como parte desta feature.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.5 (frontend)
|
||||
**Primary Dependencies**: React 18, react-router-dom v6, Tailwind CSS 3.4 (já utilizados — sem novas dependências)
|
||||
**Storage**: Nenhuma alteração de banco de dados
|
||||
**Testing**: Vite build check (frontend) — sem testes unitários para componentes de layout no projeto atual
|
||||
**Target Platform**: SPA (React) servida via Vite proxy
|
||||
**Project Type**: Web SPA (React)
|
||||
**Performance Goals**: Sem re-renders desnecessários no toggle de dropdown; fechamento por clique externo via `useRef + useEffect`
|
||||
**Constraints**: Sem bibliotecas externas novas; dropdowns implementados com React + Tailwind puro; mobile: itens expandidos inline no menu hamburger (sem toggle aninhado)
|
||||
**Scale/Scope**: Refactor de layout puro — 3 arquivos frontend, sem impacto em rotas ou dados
|
||||
|
||||
## Constitution Check
|
||||
|
||||
| Princípio | Status | Observação |
|
||||
|-----------|--------|------------|
|
||||
| I. Design-First | ✅ PASS | Tokens `bg-[#0f1011]`, `border-white/[0.06]`, `bg-white/[0.06]`, `shadow-xl`, `rounded-xl` são idênticos aos já utilizados no projeto conforme `DESIGN.md`. |
|
||||
| II. Separation of Concerns | ✅ PASS | Navbar contém lógica de navegação; layouts são thin wrappers sem lógica própria. |
|
||||
| III. Spec-Driven | ✅ PASS | spec.md aprovado → plan.md (este) → implementação. |
|
||||
| IV. Data Integrity | ✅ PASS | Nenhuma alteração de dados — feature puramente de UI. |
|
||||
| V. Security | ✅ PASS | Visibilidade dos dropdowns controlada pelos mesmos guards já existentes (`isAdmin`, `isAuthenticated`). Nenhuma rota protegida exposta. |
|
||||
| VI. Simplicity First | ✅ PASS | Remoção de código (sidebars) > adição. Sem abstração nova, sem nova lib. Alterações cirúrgicas em 3 arquivos. |
|
||||
|
||||
**POST-DESIGN RE-CHECK**: ✅ Adicionar `useRef + useEffect` ao Navbar e `<Navbar />` ao ClientLayout são as únicas adições de infraestrutura — totalmente justificadas pelo requisito de UX.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
App.tsx (inalterado)
|
||||
├── /area-do-cliente → ProtectedRoute → ClientLayout → <Outlet />
|
||||
│ ClientLayout: <> <Navbar /> <main pt-14><Outlet /></main> </>
|
||||
│ ↑ adicionado nesta feature
|
||||
│
|
||||
└── /admin → AdminRoute → AdminLayout → <Outlet />
|
||||
AdminLayout: <> <Navbar /> <main pt-14><Outlet /></main> </>
|
||||
(sidebar removido; flex wrapper removido)
|
||||
|
||||
Navbar.tsx (ampliado)
|
||||
├── adminNavItems[] — 7 itens /admin/*
|
||||
├── clientNavItems[] — 5 itens /area-do-cliente/*
|
||||
├── adminDropdownOpen (useState)
|
||||
├── clientDropdownOpen (useState)
|
||||
├── adminDropdownRef (useRef) + useEffect para fechar no outside click
|
||||
├── clientDropdownRef (useRef) + useEffect para fechar no outside click
|
||||
├── Desktop: dropdown "Admin ▾" (isAdmin) / "Minha Conta ▾" (isAuthenticated não-admin)
|
||||
└── Mobile: itens admin ou cliente expandidos inline no menu hamburger
|
||||
```
|
||||
|
||||
## Frontend Changes
|
||||
|
||||
### 1. `Navbar.tsx` — `frontend/src/components/Navbar.tsx`
|
||||
|
||||
#### 1.1 Imports adicionais
|
||||
|
||||
```tsx
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { Link, NavLink } from 'react-router-dom'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { ThemeToggle } from './ThemeToggle'
|
||||
```
|
||||
|
||||
> `NavLink` substitui `Link` nos itens de dropdown para suporte a `isActive`.
|
||||
|
||||
#### 1.2 Arrays de navegação
|
||||
|
||||
```tsx
|
||||
const adminNavItems = [
|
||||
{ to: '/admin/properties', label: 'Imóveis' },
|
||||
{ to: '/admin/clientes', label: 'Clientes' },
|
||||
{ to: '/admin/boletos', label: 'Boletos' },
|
||||
{ to: '/admin/visitas', label: 'Visitas' },
|
||||
{ to: '/admin/favoritos', label: 'Favoritos' },
|
||||
{ to: '/admin/cidades', label: 'Cidades' },
|
||||
{ to: '/admin/amenidades', label: 'Amenidades' },
|
||||
]
|
||||
|
||||
const clientNavItems = [
|
||||
{ to: '/area-do-cliente', label: 'Painel', end: true },
|
||||
{ to: '/area-do-cliente/favoritos',label: 'Favoritos',end: false },
|
||||
{ to: '/area-do-cliente/comparar', label: 'Comparar', end: false },
|
||||
{ to: '/area-do-cliente/visitas', label: 'Visitas', end: false },
|
||||
{ to: '/area-do-cliente/boletos', label: 'Boletos', end: false },
|
||||
]
|
||||
```
|
||||
|
||||
#### 1.3 Estado e refs no componente
|
||||
|
||||
```tsx
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const [adminDropdownOpen, setAdminDropdownOpen] = useState(false)
|
||||
const [clientDropdownOpen, setClientDropdownOpen] = useState(false)
|
||||
|
||||
const adminDropdownRef = useRef<HTMLDivElement>(null)
|
||||
const clientDropdownRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Fechar dropdown ao clicar fora
|
||||
useEffect(() => {
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
if (adminDropdownRef.current && !adminDropdownRef.current.contains(e.target as Node)) {
|
||||
setAdminDropdownOpen(false)
|
||||
}
|
||||
if (clientDropdownRef.current && !clientDropdownRef.current.contains(e.target as Node)) {
|
||||
setClientDropdownOpen(false)
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside)
|
||||
}, [])
|
||||
```
|
||||
|
||||
#### 1.4 Desktop — Dropdown "Admin ▾"
|
||||
|
||||
Substituir o `<li>` do link "Painel Admin" no `navLinks` map por:
|
||||
|
||||
```tsx
|
||||
{isAdmin && (
|
||||
<li className="relative" ref={adminDropdownRef}>
|
||||
<button
|
||||
onClick={() => setAdminDropdownOpen(prev => !prev)}
|
||||
className="flex items-center gap-1 text-sm text-[#5e6ad2] hover:text-[#7170ff] font-semibold transition-colors"
|
||||
>
|
||||
Admin
|
||||
<span className="text-[10px] opacity-70">▾</span>
|
||||
</button>
|
||||
{adminDropdownOpen && (
|
||||
<div className="absolute top-full right-0 mt-2 w-44 bg-[#111] rounded-xl border border-white/[0.06] shadow-xl py-2 z-50">
|
||||
{adminNavItems.map(item => (
|
||||
<NavLink
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
onClick={() => setAdminDropdownOpen(false)}
|
||||
className={({ isActive }) =>
|
||||
`block px-4 py-2 text-sm transition-colors ${
|
||||
isActive
|
||||
? 'bg-white/[0.06] text-white font-medium'
|
||||
: 'text-white/60 hover:text-white hover:bg-white/[0.04]'
|
||||
}`
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
```
|
||||
|
||||
#### 1.5 Desktop — Auth section refatorada
|
||||
|
||||
Substituir o bloco `isAuthenticated && user` na auth section (atualmente com "Admin" button + avatar link + "Sair"):
|
||||
|
||||
```tsx
|
||||
{isAuthenticated && user ? (
|
||||
<>
|
||||
{isAdmin ? (
|
||||
/* Dropdown Admin já está no navLinks — apenas avatar + sair aqui */
|
||||
<>
|
||||
<span className="flex h-7 w-7 items-center justify-center rounded-full bg-[#5e6ad2] text-xs font-semibold text-white flex-shrink-0">
|
||||
{user.name.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
<button
|
||||
onClick={logout}
|
||||
className="text-sm text-text-secondary hover:text-text-primary transition-colors duration-150 font-medium"
|
||||
>
|
||||
Sair
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
/* Dropdown "Minha Conta ▾" para cliente */
|
||||
<div className="relative" ref={clientDropdownRef}>
|
||||
<button
|
||||
onClick={() => setClientDropdownOpen(prev => !prev)}
|
||||
className="flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary transition-colors duration-150"
|
||||
>
|
||||
<span className="flex h-7 w-7 items-center justify-center rounded-full bg-[#5e6ad2] text-xs font-semibold text-white flex-shrink-0">
|
||||
{user.name.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
<span className="max-w-[100px] truncate font-medium">{user.name}</span>
|
||||
<span className="text-[10px] opacity-70">▾</span>
|
||||
</button>
|
||||
{clientDropdownOpen && (
|
||||
<div className="absolute top-full right-0 mt-2 w-48 bg-[#111] rounded-xl border border-white/[0.06] shadow-xl py-2 z-50">
|
||||
{clientNavItems.map(item => (
|
||||
<NavLink
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
end={item.end}
|
||||
onClick={() => setClientDropdownOpen(false)}
|
||||
className={({ isActive }) =>
|
||||
`block px-4 py-2 text-sm transition-colors ${
|
||||
isActive
|
||||
? 'bg-white/[0.06] text-white font-medium'
|
||||
: 'text-white/60 hover:text-white hover:bg-white/[0.04]'
|
||||
}`
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
</NavLink>
|
||||
))}
|
||||
<div className="my-1 border-t border-white/[0.06]" />
|
||||
<button
|
||||
onClick={() => { setClientDropdownOpen(false); logout() }}
|
||||
className="block w-full text-left px-4 py-2 text-sm text-white/50 hover:text-white hover:bg-white/[0.04] transition-colors"
|
||||
>
|
||||
Sair
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Link
|
||||
to="/login"
|
||||
className="rounded-lg bg-[#5e6ad2] px-4 py-1.5 text-sm font-medium text-white transition hover:bg-[#6872d8]"
|
||||
>
|
||||
Entrar
|
||||
</Link>
|
||||
)}
|
||||
```
|
||||
|
||||
> **Nota**: O link "Admin" (botão amarelo) e o link "Painel Admin" em `navLinks` são removidos. O dropdown no botão admin já contém todos os itens.
|
||||
|
||||
#### 1.6 Mobile — Itens admin/cliente inline
|
||||
|
||||
No mobile menu, substituir o bloco auth existente por:
|
||||
|
||||
```tsx
|
||||
{!isLoading && (
|
||||
isAuthenticated && user ? (
|
||||
<>
|
||||
{isAdmin
|
||||
? adminNavItems.map(item => (
|
||||
<li key={item.to}>
|
||||
<NavLink
|
||||
to={item.to}
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className={({ isActive }) =>
|
||||
`block py-2.5 text-sm font-medium transition-colors ${
|
||||
isActive
|
||||
? 'text-[#5e6ad2]'
|
||||
: 'text-[#5e6ad2]/70 hover:text-[#5e6ad2]'
|
||||
}`
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
</NavLink>
|
||||
</li>
|
||||
))
|
||||
: clientNavItems.map(item => (
|
||||
<li key={item.to}>
|
||||
<NavLink
|
||||
to={item.to}
|
||||
end={item.end}
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className={({ isActive }) =>
|
||||
`block py-2.5 text-sm transition-colors ${
|
||||
isActive
|
||||
? 'text-white font-medium'
|
||||
: 'text-text-secondary hover:text-text-primary'
|
||||
}`
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
</NavLink>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<li>
|
||||
<button
|
||||
onClick={() => { setMenuOpen(false); logout() }}
|
||||
className="block py-2.5 text-sm text-text-secondary hover:text-text-primary transition-colors duration-150 font-medium w-full text-left"
|
||||
>
|
||||
Sair
|
||||
</button>
|
||||
</li>
|
||||
</>
|
||||
) : (
|
||||
<li>
|
||||
<Link
|
||||
to="/login"
|
||||
className="block py-2.5 text-sm font-medium text-[#5e6ad2] hover:text-[#7170ff] transition-colors duration-150"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
>
|
||||
Entrar
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. `AdminLayout.tsx` — `frontend/src/layouts/AdminLayout.tsx`
|
||||
|
||||
Remover `<aside>` e wrapper `<div className="flex ...">`. O layout resultante é um thin wrapper:
|
||||
|
||||
**Antes:**
|
||||
```tsx
|
||||
export default function AdminLayout() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<div className="flex min-h-screen pt-14 bg-[#08090a]">
|
||||
<aside className="hidden lg:flex w-56 flex-col border-r border-white/[0.06] bg-panel px-3 py-6">
|
||||
<nav className="flex flex-1 flex-col gap-0.5">
|
||||
{adminNav.map(item => (
|
||||
<NavLink key={item.to} to={item.to} className={...}>
|
||||
{item.label}
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
<main className="flex-1 min-w-0 overflow-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Depois:**
|
||||
```tsx
|
||||
export default function AdminLayout() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="pt-14 min-h-screen bg-[#08090a]">
|
||||
<Outlet />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Imports removidos**: `NavLink` (não utilizado no layout após simplificação). Manter `Outlet`, `Navbar`.
|
||||
|
||||
> A constante `adminNav` no topo do arquivo é removida pois os itens migram para `Navbar.tsx`.
|
||||
|
||||
---
|
||||
|
||||
### 3. `ClientLayout.tsx` — `frontend/src/layouts/ClientLayout.tsx`
|
||||
|
||||
Remover `<aside>`, o mobile nav bar e toda a lógica de navegação local. Adicionar `<Navbar />` (atualmente ausente no ClientLayout).
|
||||
|
||||
**Antes:**
|
||||
```tsx
|
||||
import { NavLink, Outlet, useNavigate } from 'react-router-dom';
|
||||
import { ThemeToggle } from '../components/ThemeToggle';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
// ... navItems, adminNavItems arrays ...
|
||||
|
||||
export default function ClientLayout() {
|
||||
const { user, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
// ...
|
||||
return (
|
||||
<div className="flex min-h-screen bg-[#08090a] pt-14">
|
||||
<aside ...> {/* sidebar desktop */} </aside>
|
||||
<main className="flex-1 min-w-0 overflow-auto">
|
||||
<div className="lg:hidden ..."> {/* mobile nav bar */} </div>
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Depois:**
|
||||
```tsx
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import Navbar from '../components/Navbar';
|
||||
|
||||
export default function ClientLayout() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="pt-14 min-h-screen bg-[#08090a]">
|
||||
<Outlet />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Imports removidos**: `NavLink`, `useNavigate`, `ThemeToggle`, `useAuth` (toda a lógica de user/logout migra para Navbar).
|
||||
**Imports adicionados**: `Navbar`.
|
||||
|
||||
> As constantes `navItems` e `adminNavItems` são removidas — os itens migram para `Navbar.tsx`.
|
||||
|
||||
---
|
||||
|
||||
## Backend Changes
|
||||
|
||||
**Nenhuma alteração necessária.** Feature puramente frontend.
|
||||
|
||||
---
|
||||
|
||||
## Files Changed Summary
|
||||
|
||||
| Arquivo | Tipo | Operação |
|
||||
|---------|------|----------|
|
||||
| `frontend/src/components/Navbar.tsx` | Frontend | Modificar — adicionar arrays, estados, refs, dropdowns |
|
||||
| `frontend/src/layouts/AdminLayout.tsx` | Frontend | Simplificar — remover aside + flex wrapper |
|
||||
| `frontend/src/layouts/ClientLayout.tsx` | Frontend | Simplificar — remover aside + mobile nav, adicionar Navbar |
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria Checklist
|
||||
|
||||
- [ ] `AdminLayout.tsx`: sem `<aside>`, sem `div.flex`, main ocupa 100% da largura
|
||||
- [ ] `ClientLayout.tsx`: sem `<aside>`, sem mobile nav bar, `<Navbar />` presente
|
||||
- [ ] `Navbar.tsx`: dropdown "Admin ▾" visível apenas para `isAdmin`
|
||||
- [ ] `Navbar.tsx`: dropdown "Minha Conta ▾" visível apenas para `isAuthenticated` não-admin
|
||||
- [ ] Dropdown fecha ao clicar fora (`useRef + useEffect`)
|
||||
- [ ] Item ativo destacado no dropdown via `NavLink isActive`
|
||||
- [ ] Mobile: itens admin ou cliente expandidos inline, sem toggle aninhado
|
||||
- [ ] Sem dependências externas novas
|
||||
- [ ] `vite build` sem erros de TypeScript
|
||||
45
.specify/features/013-header-only-nav/spec.md
Normal file
45
.specify/features/013-header-only-nav/spec.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Feature Specification: Navegação Unificada no Header (remoção do Sidebar)
|
||||
|
||||
**Feature Branch**: `013-header-only-nav`
|
||||
**Created**: 2026-04-14
|
||||
**Status**: In Progress
|
||||
|
||||
## Contexto
|
||||
|
||||
Atualmente o painel admin (`AdminLayout`) e a área do cliente (`ClientLayout`) possuem sidebars verticais com os itens de navegação. O layout com sidebar ocupa espaço horizontal desnecessário e fragmenta a navegação entre header e sidebar. Esta feature remove os sidebars de ambos os layouts e move todos os itens de navegação para o Navbar (header fixo) através de dropdowns, mantendo a experiência consistente e o layout de tela cheia para o conteúdo principal.
|
||||
|
||||
### Estado atual
|
||||
|
||||
- `AdminLayout.tsx`: sidebar com 7 itens (Imóveis, Clientes, Boletos, Visitas, Favoritos, Cidades, Amenidades) + Navbar
|
||||
- `ClientLayout.tsx`: sidebar com 5 itens (Painel, Favoritos, Comparar, Visitas, Boletos) + link admin + Navbar
|
||||
- `Navbar.tsx`: links públicos + botão "Painel Admin" único sem dropdown
|
||||
|
||||
## User Stories
|
||||
|
||||
### US1 — Admin sem sidebar (P1)
|
||||
**Given** admin autenticado acessando qualquer rota `/admin/*`, **When** visualiza o header, **Then** vê um dropdown "Admin ▾" com todos os 7 itens de navegação admin, sem sidebar lateral.
|
||||
|
||||
### US2 — Área do cliente sem sidebar (P1)
|
||||
**Given** cliente autenticado acessando qualquer rota `/area-do-cliente/*`, **When** visualiza o header, **Then** vê um dropdown "Minha Conta ▾" com todos os 5 itens de navegação da área do cliente, sem sidebar lateral.
|
||||
|
||||
### US3 — Conteúdo usa 100% da largura (P1)
|
||||
**Given** qualquer rota admin ou cliente, **When** sidebar é removido, **Then** o main content ocupa 100% da largura disponível (sem `w-56` reservado para sidebar).
|
||||
|
||||
### US4 — Dropdown fecha ao clicar fora (P2)
|
||||
**Given** dropdown aberto no header, **When** usuário clica fora do dropdown, **Then** dropdown fecha automaticamente.
|
||||
|
||||
### US5 — Navegação mobile (P1)
|
||||
**Given** usuário em dispositivo mobile, **When** abre o menu hamburger, **Then** vê todos os itens admin ou cliente no menu mobile expandido.
|
||||
|
||||
### US6 — Item ativo destacado no dropdown (P2)
|
||||
**Given** usuário em rota ativa (ex: `/admin/clientes`), **When** abre o dropdown admin, **Then** o item correspondente aparece destacado visualmente.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- `AdminLayout.tsx`: remover `<aside>` sidebar, remover estrutura `pt-14/flex` redundante, main ocupa 100% da largura
|
||||
- `ClientLayout.tsx`: remover `<aside>` sidebar, remover mobile nav bar extra, main ocupa 100% da largura
|
||||
- `Navbar.tsx`: adicionar dropdown "Admin ▾" visível apenas para `isAdmin`, com os 7 itens do admin
|
||||
- `Navbar.tsx`: adicionar dropdown "Minha Conta ▾" visível apenas para `isAuthenticated` e não-admin, com os 5 itens do cliente
|
||||
- Dropdown fecha ao clicar fora (`useEffect` + `ref` ou `onBlur`)
|
||||
- Sem dependências externas novas (apenas React + react-router-dom + Tailwind já utilizados)
|
||||
- Mobile: itens de dropdown aparecem expandidos no menu hamburger
|
||||
23
.specify/features/013-header-only-nav/tasks.md
Normal file
23
.specify/features/013-header-only-nav/tasks.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Tasks: Navegação Unificada no Header (Remoção do Sidebar)
|
||||
|
||||
**Feature**: `013-header-only-nav`
|
||||
**Generated**: 2026-04-14
|
||||
**Depends On**: Nenhuma — alterações puramente de layout/componente frontend
|
||||
|
||||
---
|
||||
|
||||
## Frontend — Navbar.tsx
|
||||
- [x] T001 Adicionar arrays `adminNavItems` e `clientNavItems` no Navbar em `frontend/src/components/Navbar.tsx`
|
||||
- [x] T002 Adicionar estados `adminDropdownOpen` / `clientDropdownOpen` + refs para fechar ao clicar fora em `frontend/src/components/Navbar.tsx`
|
||||
- [x] T003 Substituir botão "Painel Admin" por dropdown "Admin ▾" com 7 itens (apenas `isAdmin`) em `frontend/src/components/Navbar.tsx`
|
||||
- [x] T004 Adicionar dropdown "Minha Conta ▾" com 5 itens do cliente + botão Sair (apenas `isAuthenticated` não-admin) em `frontend/src/components/Navbar.tsx`
|
||||
- [x] T005 Expandir mobile menu para incluir itens admin/cliente inline em `frontend/src/components/Navbar.tsx`
|
||||
|
||||
## Frontend — AdminLayout.tsx
|
||||
- [x] T006 Remover `<aside>` sidebar do AdminLayout em `frontend/src/layouts/AdminLayout.tsx`
|
||||
- [x] T007 Simplificar estrutura: Navbar + main full-width com `pt-14` em `frontend/src/layouts/AdminLayout.tsx`
|
||||
|
||||
## Frontend — ClientLayout.tsx
|
||||
- [x] T008 Remover `<aside>` sidebar e mobile nav bar do ClientLayout em `frontend/src/layouts/ClientLayout.tsx`
|
||||
- [x] T009 Adicionar `<Navbar />` ao ClientLayout (atualmente ausente) em `frontend/src/layouts/ClientLayout.tsx`
|
||||
- [x] T010 Simplificar estrutura: main full-width com `pt-14` em `frontend/src/layouts/ClientLayout.tsx`
|
||||
Loading…
Add table
Add a link
Reference in a new issue