Frontend completo (Etapas 11-18) e extensões de backend (Etapas 13 e 18)

Adiciona o frontend Next.js em frontend/ (setup, design system,
autenticação/tema, layout/navegação, área master, área tenant completa:
clientes, produtos, vendas, recebimentos, whatsapp, e-mail, alertas,
equipe e perfil). Estende o backend de forma aditiva e autorizada:
cookie httpOnly para refresh token (Etapa 13) e CRUD de equipe +
edição de perfil do tenant (Etapa 18, módulo src/modules/equipe/ novo
e PUT /tenant/perfil). Detalhes completos em Etapas/V2/*.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 09:41:13 -03:00
co-authored by Claude Sonnet 5
parent f9738e54da
commit 323912748f
179 changed files with 29312 additions and 22 deletions
+81
View File
@@ -0,0 +1,81 @@
import {
LayoutDashboard,
Building2,
Package,
Receipt,
MessageSquare,
Bell,
Settings,
Users,
ShoppingCart,
Wallet,
Mail,
UsersRound,
UserRound,
type LucideIcon,
} from 'lucide-react';
import type { UserRole } from '@/lib/auth/jwt';
export interface NavItem {
href: string;
label: string;
icon: LucideIcon;
/** Sem `roles`, visível para qualquer papel autenticado da área. */
roles?: UserRole[];
/** Grupo expansível (ex: Alertas → Templates/Logs); `href` é o destino padrão. */
children?: { href: string; label: string }[];
}
export const MASTER_NAV_ITEMS: NavItem[] = [
{ href: '/master/dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ href: '/master/tenants', label: 'Tenants', icon: Building2 },
{ href: '/master/planos', label: 'Planos', icon: Package },
{ href: '/master/faturas', label: 'Faturas', icon: Receipt },
{ href: '/master/evolution-config', label: 'Evolution API', icon: MessageSquare },
{
href: '/master/alertas/templates',
label: 'Alertas',
icon: Bell,
children: [
{ href: '/master/alertas/templates', label: 'Templates' },
{ href: '/master/alertas/logs', label: 'Logs' },
],
},
{ href: '/master/configuracoes', label: 'Configurações', icon: Settings },
];
/** 4 principais da bottom tab bar do master (Etapa 14) — o resto entra em "Mais". */
export const MASTER_BOTTOM_HREFS = [
'/master/dashboard',
'/master/tenants',
'/master/faturas',
'/master/alertas/templates',
];
export const TENANT_NAV_ITEMS: NavItem[] = [
{ href: '/app/dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ href: '/app/clientes', label: 'Clientes', icon: Users },
{ href: '/app/produtos', label: 'Produtos', icon: Package },
{ href: '/app/vendas', label: 'Vendas', icon: ShoppingCart },
{ href: '/app/recebimentos', label: 'Recebimentos', icon: Wallet },
{ href: '/app/whatsapp', label: 'WhatsApp', icon: MessageSquare },
{ href: '/app/email-config', label: 'E-mail', icon: Mail },
{
href: '/app/alertas/templates',
label: 'Alertas',
icon: Bell,
children: [
{ href: '/app/alertas/templates', label: 'Templates' },
{ href: '/app/alertas/logs', label: 'Logs' },
],
},
{ href: '/app/equipe', label: 'Equipe', icon: UsersRound, roles: ['tenant_admin'] },
{ href: '/app/perfil', label: 'Perfil', icon: UserRound },
];
/** 4 principais da bottom tab bar do tenant (Etapa 14) — o resto entra em "Mais". */
export const TENANT_BOTTOM_HREFS = ['/app/dashboard', '/app/clientes', '/app/vendas', '/app/recebimentos'];
export function filterByRole(items: NavItem[], role: UserRole | undefined): NavItem[] {
return items.filter((item) => !item.roles || (role && item.roles.includes(role)));
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Preferência de sidebar colapsada/expandida (Etapa 14), não sensível.
* Lida no servidor (`app/master/layout.tsx`/`app/app/layout.tsx`, via
* `next/headers`) para renderizar já no estado certo sem "flash" de layout,
* e persistida no client a cada toggle — mesmo padrão de
* `lib/auth/session-cookie.ts` (Etapa 13).
*/
export const SIDEBAR_COOKIE = 'sc_sidebar_collapsed';
const MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
export function readSidebarCollapsedCookie(value: string | undefined): boolean {
return value === '1';
}
export function persistSidebarCollapsed(collapsed: boolean): void {
document.cookie = `${SIDEBAR_COOKIE}=${collapsed ? '1' : '0'}; Path=/; Max-Age=${MAX_AGE_SECONDS}; SameSite=Lax`;
}