style(ui): refina indicadores e componentes visuais

This commit is contained in:
Felipe Coutinho
2026-05-21 13:47:53 +00:00
parent 7ca3f92467
commit 1a75662120
9 changed files with 62 additions and 46 deletions

View File

@@ -41,9 +41,9 @@
--input: var(--border); --input: var(--border);
--ring: var(--primary); --ring: var(--primary);
--chart-1: var(--color-emerald-500); --chart-1: var(--color-orange-600);
--chart-2: var(--color-red-500); --chart-2: var(--color-orange-400);
--chart-3: var(--color-amber-500); --chart-3: var(--color-orange-200);
--chart-4: var(--color-blue-500); --chart-4: var(--color-blue-500);
--chart-5: var(--color-pink-500); --chart-5: var(--color-pink-500);
--chart-6: var(--color-stone-500); --chart-6: var(--color-stone-500);
@@ -117,13 +117,13 @@
--destructive: oklch(62% 0.2 28); --destructive: oklch(62% 0.2 28);
--destructive-foreground: oklch(98% 0.005 30); --destructive-foreground: oklch(98% 0.005 30);
--border: oklch(24.957% 0.00355 48.274); --border: oklch(31.987% 0.00462 39.069);
--input: var(--border); --input: var(--border);
--ring: var(--primary); --ring: var(--primary);
--chart-1: var(--color-emerald-500); --chart-1: var(--color-orange-600);
--chart-2: var(--color-orange-500); --chart-2: var(--color-orange-400);
--chart-3: var(--color-indigo-500); --chart-3: var(--color-orange-200);
--chart-4: var(--color-amber-500); --chart-4: var(--color-amber-500);
--chart-5: var(--color-pink-500); --chart-5: var(--color-pink-500);
--chart-6: var(--color-stone-500); --chart-6: var(--color-stone-500);

View File

@@ -330,7 +330,7 @@ export function DashboardGridEditable({
> >
<div className="relative"> <div className="relative">
{isEditing && ( {isEditing && (
<div className="absolute inset-0 z-10 bg-background/50 backdrop-blur-xs rounded-lg border border-dashed border-primary flex items-center justify-center"> <div className="absolute inset-0 z-10 bg-background/60 backdrop-blur-[1.5px] rounded-lg border border-dashed border-primary flex items-center justify-center">
<div className="flex flex-col items-center gap-2"> <div className="flex flex-col items-center gap-2">
<RiDragMove2Line className="size-8 text-primary" /> <RiDragMove2Line className="size-8 text-primary" />
<span className="text-xs font-medium"> <span className="text-xs font-medium">

View File

@@ -8,6 +8,7 @@ import { MetricsCardInfoButton } from "@/features/dashboard/components/metrics-c
import { PercentageChangeIndicator } from "@/features/dashboard/components/percentage-change-indicator"; import { PercentageChangeIndicator } from "@/features/dashboard/components/percentage-change-indicator";
import type { DashboardCardMetrics } from "@/features/dashboard/overview/dashboard-metrics-queries"; import type { DashboardCardMetrics } from "@/features/dashboard/overview/dashboard-metrics-queries";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { Badge } from "@/shared/components/ui/badge";
import { import {
Card, Card,
CardContent, CardContent,
@@ -102,21 +103,22 @@ const getTrend = (current: number, previous: number): Trend => {
return "flat"; return "flat";
}; };
const getPercentChange = (current: number, previous: number): string => { const getPercentChange = (current: number, previous: number): string | null => {
const EPSILON = 0.01; const EPSILON = 0.01;
if (Math.abs(previous) < EPSILON) { if (Math.abs(previous) < EPSILON) {
if (Math.abs(current) < EPSILON) return "0%"; if (Math.abs(current) < EPSILON) return "0%";
return "—"; return null;
} }
const change = ((current - previous) / Math.abs(previous)) * 100; const change = ((current - previous) / Math.abs(previous)) * 100;
if (!Number.isFinite(change)) return "—"; if (!Number.isFinite(change)) return null;
if (Math.abs(change) < TREND_THRESHOLD) return "0%";
if (change > 999) return "+999%"; if (change > 999) return "+999%";
if (change < -999) return "-999%"; if (change < -999) return "-999%";
return formatPercentage(change, { return formatPercentage(change, {
maximumFractionDigits: 2, maximumFractionDigits: 0,
minimumFractionDigits: 2, minimumFractionDigits: 0,
signDisplay: "always", signDisplay: "always",
}); });
}; };
@@ -160,28 +162,45 @@ export function DashboardMetricsCards({ metrics }: DashboardMetricsCardsProps) {
<Separator className="mt-1" /> <Separator className="mt-1" />
</CardHeader> </CardHeader>
<CardContent className="flex flex-col gap-3"> <CardContent className="flex flex-col">
<div className="flex flex-wrap items-center justify-between gap-2 mt-1"> <div className="flex items-start justify-between mt-1">
<div className="flex flex-col gap-2 min-w-0">
<div className="flex flex-wrap items-center">
<MoneyValues <MoneyValues
className="text-2xl leading-none font-medium" className="text-2xl leading-none"
amount={metric.current} amount={metric.current}
/> />
</div>
<div className="text-xs text-muted-foreground gap-1 flex items-center">
<span className="text-muted-foreground/50">vs</span>
<MoneyValues
className="inline text-xs"
amount={metric.previous}
/>
<Badge
variant="secondary"
aria-hidden={!percentChange}
className={cn(
"w-14 justify-center px-0 text-xs",
!percentChange && "invisible",
)}
>
{percentChange ? (
<PercentageChangeIndicator <PercentageChangeIndicator
trend={trend} trend={trend}
label={percentChange} label={percentChange}
positiveTrend={invertTrend ? "down" : "up"} positiveTrend={invertTrend ? "down" : "up"}
showFlatIcon showFlatIcon={false}
className="gap-1" className="shrink-0 justify-center text-center text-xs tabular-nums"
iconClassName="size-3.5" iconClassName="hidden"
/> />
) : (
<span className="tabular-nums">0%</span>
)}
</Badge>
</div>
</div> </div>
<div className="text-xs text-muted-foreground">
<MoneyValues
className="inline text-xs font-medium text-muted-foreground"
amount={metric.previous}
/>
<span className="ml-1">no mês anterior</span>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -19,15 +19,15 @@ type IncomeExpenseBalanceWidgetProps = {
const chartConfig = { const chartConfig = {
receita: { receita: {
label: "Receita", label: "Receita",
color: "var(--success)", color: "var(--chart-1)",
}, },
despesa: { despesa: {
label: "Despesa", label: "Despesa",
color: "var(--destructive)", color: "var(--chart-2)",
}, },
balanco: { balanco: {
label: "Balanço", label: "Balanço",
color: "var(--warning)", color: "var(--chart-3)",
}, },
} satisfies ChartConfig; } satisfies ChartConfig;

View File

@@ -213,8 +213,8 @@ export const InboxCard = memo(function InboxCard({
variant="ghost" variant="ghost"
onClick={() => onViewDetails?.(item)} onClick={() => onViewDetails?.(item)}
className="text-muted-foreground hover:text-foreground" className="text-muted-foreground hover:text-foreground"
aria-label="Ver detalhes" aria-label="detalhes"
title="Ver detalhes" title="detalhes"
> >
<RiFileList2Line className="size-4" /> <RiFileList2Line className="size-4" />
</Button> </Button>

View File

@@ -169,7 +169,7 @@ export function DeleteAccountForm() {
> >
<DialogHeader> <DialogHeader>
<DialogTitle> <DialogTitle>
{isResetAction ? "Zerar sua conta?" : "Você tem certeza?"} {isResetAction ? "ZERAR sua conta?" : "Você tem certeza?"}
</DialogTitle> </DialogTitle>
<DialogDescription> <DialogDescription>
{isResetAction {isResetAction

View File

@@ -36,9 +36,6 @@ export function NotificationBellTrigger({
"group relative shadow-none transition-all duration-200", "group relative shadow-none transition-all duration-200",
"hover:border-black/20 hover:bg-black/10 hover:text-black focus-visible:ring-2 focus-visible:ring-black/20 dark:hover:border-white/20 dark:hover:bg-white/10 dark:hover:text-white dark:focus-visible:ring-white/20", "hover:border-black/20 hover:bg-black/10 hover:text-black focus-visible:ring-2 focus-visible:ring-black/20 dark:hover:border-white/20 dark:hover:bg-white/10 dark:hover:text-white dark:focus-visible:ring-white/20",
"data-[state=open]:bg-black/10 data-[state=open]:text-black dark:data-[state=open]:bg-white/10 dark:data-[state=open]:text-white", "data-[state=open]:bg-black/10 data-[state=open]:text-black dark:data-[state=open]:bg-white/10 dark:data-[state=open]:text-white",
hasAnySourceItems
? "text-black dark:text-white"
: "text-black/75 dark:text-white/75",
)} )}
> >
<RiNotification2Line <RiNotification2Line
@@ -55,7 +52,7 @@ export function NotificationBellTrigger({
> >
{displayCount} {displayCount}
</span> </span>
<span className="absolute -right-1.5 -top-1.5 size-5 animate-ping rounded-full bg-destructive/5 [animation-iteration-count:3]" /> <span className="absolute -right-1.5 -top-1.5 size-5 animate-ping rounded-full bg-destructive/5 repeat-3" />
</> </>
) : null} ) : null}
</button> </button>

View File

@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card" data-slot="card"
className={cn( className={cn(
"bg-card text-card-foreground flex flex-col gap-4 border border-transparent shadow-sm dark:border-border py-6 rounded-lg hover:border-primary/50 transition-colors duration-200", "bg-card text-card-foreground flex flex-col gap-4 border border-transparent shadow-xs dark:border-border py-6 rounded-lg hover:border-primary/50 transition-colors duration-200",
className, className,
)} )}
{...props} {...props}

View File

@@ -17,7 +17,7 @@ function Separator({
decorative={decorative} decorative={decorative}
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px", "bg-border/50 dark:bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
className, className,
)} )}
{...props} {...props}