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
@@ -0,0 +1,85 @@
'use client';
import * as React from 'react';
import { useIsDesktop } from '@/hooks/use-media-query';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet';
interface ResponsiveDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
title: string;
description?: string;
children: React.ReactNode;
footer?: React.ReactNode;
className?: string;
}
/**
* Um único componente para modal (Etapa 12): centralizado no desktop
* (`Dialog`), bottom sheet no mobile (`Sheet` com `side="bottom"`) —
* escolhido em vez de tela cheia porque a maioria dos formulários deste
* projeto (confirmações, cadastro rápido de cliente, edição de parcela)
* é curta o bastante para caber sem precisar ocupar a tela inteira;
* `max-h-[85vh]` com scroll interno cobre os casos mais longos. Alterna
* no mesmo breakpoint `md` usado pela `Table`.
*/
function ResponsiveDialog({
open,
onOpenChange,
title,
description,
children,
footer,
className,
}: ResponsiveDialogProps) {
const isDesktop = useIsDesktop();
if (isDesktop) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className={className}>
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
{description && <DialogDescription>{description}</DialogDescription>}
</DialogHeader>
{children}
{footer && <DialogFooter>{footer}</DialogFooter>}
</DialogContent>
</Dialog>
);
}
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent
side="bottom"
className={`max-h-[85vh] overflow-y-auto rounded-t-xl ${className ?? ''}`}
>
<SheetHeader>
<SheetTitle>{title}</SheetTitle>
{description && <SheetDescription>{description}</SheetDescription>}
</SheetHeader>
<div className="px-4">{children}</div>
{footer && <SheetFooter>{footer}</SheetFooter>}
</SheetContent>
</Sheet>
);
}
export { ResponsiveDialog };