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:
@@ -0,0 +1,128 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { format } from 'date-fns';
|
||||
import { ptBR } from 'date-fns/locale';
|
||||
import { CalendarIcon } from 'lucide-react';
|
||||
import type { DateRange } from 'react-day-picker';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Calendar } from '@/components/ui/calendar';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
|
||||
interface DatePickerProps {
|
||||
value?: Date;
|
||||
onChange: (date: Date | undefined) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
id?: string;
|
||||
'aria-invalid'?: boolean;
|
||||
'aria-describedby'?: string;
|
||||
}
|
||||
|
||||
/** Seleção de data única — usada em formulários (ex: vencimento de parcela). */
|
||||
function DatePicker({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = 'Selecionar data',
|
||||
disabled,
|
||||
className,
|
||||
id,
|
||||
...aria
|
||||
}: DatePickerProps) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
id={id}
|
||||
variant="outline"
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'w-full justify-start font-normal',
|
||||
!value && 'text-muted-foreground',
|
||||
className,
|
||||
)}
|
||||
{...aria}
|
||||
>
|
||||
<CalendarIcon />
|
||||
{value ? format(value, "dd 'de' MMMM 'de' yyyy", { locale: ptBR }) : placeholder}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={value}
|
||||
onSelect={(date) => {
|
||||
onChange(date);
|
||||
setOpen(false);
|
||||
}}
|
||||
locale={ptBR}
|
||||
autoFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
interface DateRangePickerProps {
|
||||
value?: DateRange;
|
||||
onChange: (range: DateRange | undefined) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
/** Seleção de período — usada nos filtros de dashboard/listagens (seção 7 do schema). */
|
||||
function DateRangePicker({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = 'Selecionar período',
|
||||
disabled,
|
||||
className,
|
||||
id,
|
||||
}: DateRangePickerProps) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const label = React.useMemo(() => {
|
||||
if (!value?.from) return placeholder;
|
||||
if (!value.to) return format(value.from, 'dd/MM/yyyy', { locale: ptBR });
|
||||
return `${format(value.from, 'dd/MM/yyyy', { locale: ptBR })} – ${format(value.to, 'dd/MM/yyyy', { locale: ptBR })}`;
|
||||
}, [value, placeholder]);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
id={id}
|
||||
variant="outline"
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'w-full justify-start font-normal',
|
||||
!value?.from && 'text-muted-foreground',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<CalendarIcon />
|
||||
{label}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
mode="range"
|
||||
selected={value}
|
||||
onSelect={onChange}
|
||||
locale={ptBR}
|
||||
numberOfMonths={2}
|
||||
autoFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
export { DatePicker, DateRangePicker };
|
||||
Reference in New Issue
Block a user