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>
86 lines
2.2 KiB
TypeScript
86 lines
2.2 KiB
TypeScript
'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 };
|