Files
tiagoandClaude Sonnet 5 323912748f 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>
2026-08-16 09:41:13 -03:00

41 lines
1.3 KiB
TypeScript

import type { LucideIcon } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton';
import { cn } from '@/lib/utils';
interface StatCardProps {
label: string;
value: string;
icon?: LucideIcon;
loading?: boolean;
tone?: 'default' | 'success' | 'warning' | 'danger';
className?: string;
}
const TONE_CLASSNAMES: Record<NonNullable<StatCardProps['tone']>, string> = {
default: 'text-foreground',
success: 'text-success',
warning: 'text-warning',
danger: 'text-danger',
};
/** Card de resumo numérico (dashboards) — Etapa 15. */
export function StatCard({ label, value, icon: Icon, loading, tone = 'default', className }: StatCardProps) {
return (
<Card className={className}>
<CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0">
<CardTitle className="text-sm font-medium text-muted-foreground">{label}</CardTitle>
{Icon && <Icon className="size-4 text-muted-foreground" aria-hidden />}
</CardHeader>
<CardContent>
{loading ? (
<Skeleton className="h-8 w-24" />
) : (
<p className={cn('text-2xl font-semibold tabular-nums', TONE_CLASSNAMES[tone])}>{value}</p>
)}
</CardContent>
</Card>
);
}