mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-06-09 23:06:01 +00:00
style(ui): refina indicadores e componentes visuais
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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">
|
||||||
<MoneyValues
|
<div className="flex flex-col gap-2 min-w-0">
|
||||||
className="text-2xl leading-none font-medium"
|
<div className="flex flex-wrap items-center">
|
||||||
amount={metric.current}
|
<MoneyValues
|
||||||
/>
|
className="text-2xl leading-none"
|
||||||
<PercentageChangeIndicator
|
amount={metric.current}
|
||||||
trend={trend}
|
/>
|
||||||
label={percentChange}
|
</div>
|
||||||
positiveTrend={invertTrend ? "down" : "up"}
|
|
||||||
showFlatIcon
|
|
||||||
className="gap-1"
|
|
||||||
iconClassName="size-3.5"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground gap-1 flex items-center">
|
||||||
<MoneyValues
|
<span className="text-muted-foreground/50">vs</span>
|
||||||
className="inline text-xs font-medium text-muted-foreground"
|
<MoneyValues
|
||||||
amount={metric.previous}
|
className="inline text-xs"
|
||||||
/>
|
amount={metric.previous}
|
||||||
<span className="ml-1">no mês anterior</span>
|
/>
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
aria-hidden={!percentChange}
|
||||||
|
className={cn(
|
||||||
|
"w-14 justify-center px-0 text-xs",
|
||||||
|
!percentChange && "invisible",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{percentChange ? (
|
||||||
|
<PercentageChangeIndicator
|
||||||
|
trend={trend}
|
||||||
|
label={percentChange}
|
||||||
|
positiveTrend={invertTrend ? "down" : "up"}
|
||||||
|
showFlatIcon={false}
|
||||||
|
className="shrink-0 justify-center text-center text-xs tabular-nums"
|
||||||
|
iconClassName="hidden"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="tabular-nums">0%</span>
|
||||||
|
)}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user