diff --git a/src/features/calendar/components/day-cell.tsx b/src/features/calendar/components/day-cell.tsx
index 634f5a8..8af8eb4 100644
--- a/src/features/calendar/components/day-cell.tsx
+++ b/src/features/calendar/components/day-cell.tsx
@@ -136,6 +136,8 @@ export function DayCell({ day, onSelect, onCreate }: DayCellProps) {
onCreate(day);
};
+ const overflowCount = day.events.length - previewEvents.length;
+
return (
onSelect(day)}
onKeyDown={handleKeyDown}
className={cn(
- "flex h-full cursor-pointer flex-col gap-1.5 rounded-lg border border-transparent bg-card/80 p-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 hover:border-primary/40 hover:bg-primary/5 dark:hover:bg-accent transition-colors duration-300",
+ "group flex h-full cursor-pointer flex-col gap-1.5 rounded-lg border border-transparent bg-card/80 p-2 text-left transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 hover:border-primary/40 hover:bg-primary/5 dark:hover:bg-accent",
!day.isCurrentMonth && "opacity-60",
day.isToday && "border-primary/70 bg-primary/5 hover:border-primary",
)}
@@ -153,7 +155,7 @@ export function DayCell({ day, onSelect, onCreate }: DayCellProps) {
className={cn(
"text-sm font-semibold leading-none",
day.isToday
- ? "text-orange-100 bg-primary size-5 rounded-full flex items-center justify-center"
+ ? "text-primary-foreground bg-primary size-5 rounded-full flex items-center justify-center"
: "text-foreground/90",
)}
>
@@ -162,7 +164,7 @@ export function DayCell({ day, onSelect, onCreate }: DayCellProps) {
@@ -170,13 +172,14 @@ export function DayCell({ day, onSelect, onCreate }: DayCellProps) {
- {previewEvents.map((event) => (
-
- ))}
+ {day.isCurrentMonth &&
+ previewEvents.map((event) => (
+
+ ))}
- {hasOverflow ? (
+ {day.isCurrentMonth && hasOverflow ? (
- + ver mais
+ +{overflowCount} mais
) : null}
diff --git a/src/features/calendar/components/event-modal.tsx b/src/features/calendar/components/event-modal.tsx
index 4898afa..d72eda4 100644
--- a/src/features/calendar/components/event-modal.tsx
+++ b/src/features/calendar/components/event-modal.tsx
@@ -69,8 +69,6 @@ const renderLancamento = (
- {event.transaction.condition}
- {event.transaction.paymentMethod}
{event.transaction.categoriaName}
@@ -198,9 +196,6 @@ export function EventModal({ open, day, onClose, onCreate }: EventModalProps) {
-
- Cancelar
-
Novo lançamento
diff --git a/src/features/dashboard/components/category-history-widget.tsx b/src/features/dashboard/components/category-history-widget.tsx
index e4c6518..4cbaaf4 100644
--- a/src/features/dashboard/components/category-history-widget.tsx
+++ b/src/features/dashboard/components/category-history-widget.tsx
@@ -8,7 +8,6 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
import type { CategoryHistoryData } from "@/features/dashboard/categories/category-history-queries";
import { Button } from "@/shared/components/ui/button";
-import { Card, CardContent } from "@/shared/components/ui/card";
import {
type ChartConfig,
ChartContainer,
@@ -170,274 +169,272 @@ export function CategoryHistoryWidget({ data }: CategoryHistoryWidgetProps) {
}
return (
-
-
-
- {selectedCategoryDetails.length > 0 && (
-
-
- {selectedCategoryDetails.map((category) => {
- if (!category) return null;
- const IconComponent = category.icon
- ? getIconComponent(category.icon)
- : null;
- const colorIndex = selectedCategories.indexOf(category.id);
- const color = CHART_COLORS[colorIndex % CHART_COLORS.length];
+
+
+ {selectedCategoryDetails.length > 0 && (
+
+
+ {selectedCategoryDetails.map((category) => {
+ if (!category) return null;
+ const IconComponent = category.icon
+ ? getIconComponent(category.icon)
+ : null;
+ const colorIndex = selectedCategories.indexOf(category.id);
+ const color = CHART_COLORS[colorIndex % CHART_COLORS.length];
- return (
-
- {IconComponent ? (
-
-
-
- ) : (
-
- )}
-
{category.name}
-
handleRemoveCategory(category.id)}
- >
-
-
-
- );
- })}
-
-
-
- {selectedCategories.length}/5 selecionadas
-
-
- Limpar
-
-
-
- )}
-
- {selectedCategories.length < 5 && availableCategories.length > 0 && (
-
-
-
- Selecionar categorias
-
-
-
-
-
-
-
- Nenhuma categoria encontrada.
-
- {despesaCategories.length > 0 && (
-
- {despesaCategories.map((category) => {
- const IconComponent = category.icon
- ? getIconComponent(category.icon)
- : null;
- return (
- handleAddCategory(category.id)}
- className="gap-2"
- >
- {IconComponent ? (
-
- ) : (
-
- )}
- {category.name}
-
- );
- })}
-
- )}
-
- {receitaCategories.length > 0 && (
-
- {receitaCategories.map((category) => {
- const IconComponent = category.icon
- ? getIconComponent(category.icon)
- : null;
- return (
- handleAddCategory(category.id)}
- className="gap-2"
- >
- {IconComponent ? (
-
- ) : (
-
- )}
- {category.name}
-
- );
- })}
-
- )}
-
-
-
-
- )}
-
-
- {isEmpty ? (
-
-
- }
- title="Selecione categorias para visualizar"
- description="Escolha até 5 categorias para acompanhar o histórico dos últimos 8 meses, mês atual e próximo mês."
- />
-
- ) : (
-
-
-
- {filteredCategories.map((category) => (
-
-
-
-
- ))}
-
-
-
- formatCurrencyCompact(Number(value))}
- />
- {
- if (!active || !payload || payload.length === 0) {
- return null;
- }
+ {IconComponent ? (
+
+
+
+ ) : (
+
+ )}
+ {category.name}
+ handleRemoveCategory(category.id)}
+ >
+
+
+
+ );
+ })}
+
+
+
+ {selectedCategories.length}/5 selecionadas
+
+
+ Limpar
+
+
+
+ )}
- // Sort payload by value (descending)
- const sortedPayload = [...payload].sort(
- (a, b) => (b.value as number) - (a.value as number),
- );
+ {selectedCategories.length < 5 && availableCategories.length > 0 && (
+
+
+
+ Selecionar categorias
+
+
+
+
+
+
+
+ Nenhuma categoria encontrada.
- return (
-
-
- {payload[0].payload.month}
-
-
- {sortedPayload
- .filter((entry) => (entry.value as number) > 0)
- .map((entry) => {
- const config =
- chartConfig[
- entry.dataKey as keyof typeof chartConfig
- ];
- const value = entry.value as number;
+ {despesaCategories.length > 0 && (
+
+ {despesaCategories.map((category) => {
+ const IconComponent = category.icon
+ ? getIconComponent(category.icon)
+ : null;
+ return (
+ handleAddCategory(category.id)}
+ className="gap-2"
+ >
+ {IconComponent ? (
+
+ ) : (
+
+ )}
+ {category.name}
+
+ );
+ })}
+
+ )}
- return (
-
-
-
-
- {config?.label}
-
-
-
- {formatCurrency(value)}
+ {receitaCategories.length > 0 && (
+
+ {receitaCategories.map((category) => {
+ const IconComponent = category.icon
+ ? getIconComponent(category.icon)
+ : null;
+ return (
+ handleAddCategory(category.id)}
+ className="gap-2"
+ >
+ {IconComponent ? (
+
+ ) : (
+
+ )}
+ {category.name}
+
+ );
+ })}
+
+ )}
+
+
+
+
+ )}
+
+
+ {isEmpty ? (
+
+
+ }
+ title="Selecione categorias para visualizar"
+ description="Escolha até 5 categorias para acompanhar o histórico dos últimos 8 meses, mês atual e próximo mês."
+ />
+
+ ) : (
+
+
+
+ {filteredCategories.map((category) => (
+
+
+
+
+ ))}
+
+
+
+ formatCurrencyCompact(Number(value))}
+ />
+ {
+ if (!active || !payload || payload.length === 0) {
+ return null;
+ }
+
+ // Sort payload by value (descending)
+ const sortedPayload = [...payload].sort(
+ (a, b) => (b.value as number) - (a.value as number),
+ );
+
+ return (
+
+
+ {payload[0].payload.month}
+
+
+ {sortedPayload
+ .filter((entry) => (entry.value as number) > 0)
+ .map((entry) => {
+ const config =
+ chartConfig[
+ entry.dataKey as keyof typeof chartConfig
+ ];
+ const value = entry.value as number;
+
+ return (
+
+
+ {formatCurrency(value)}
+
+
+ );
+ })}
- );
- }}
- cursor={{
- stroke: "hsl(var(--muted-foreground))",
- strokeWidth: 1,
+
+ );
+ }}
+ cursor={{
+ stroke: "hsl(var(--muted-foreground))",
+ strokeWidth: 1,
+ }}
+ />
+ {filteredCategories.map((category) => (
+
- {filteredCategories.map((category) => (
-
- ))}
-
-
- )}
-
-
+ ))}
+
+
+ )}
+
);
}
diff --git a/src/features/dashboard/components/goals-progress/goal-progress-item.tsx b/src/features/dashboard/components/goals-progress/goal-progress-item.tsx
index 35a6640..218ef2d 100644
--- a/src/features/dashboard/components/goals-progress/goal-progress-item.tsx
+++ b/src/features/dashboard/components/goals-progress/goal-progress-item.tsx
@@ -25,8 +25,10 @@ export function GoalProgressItem({
const progressValue = clampGoalProgress(item.usedPercentage, 0, 100);
const percentageDelta = item.usedPercentage - 100;
+ const isExceeded = item.status === "exceeded";
+
return (
-
+
onEdit(item)}
aria-label={`Editar orçamento de ${item.categoryName}`}
>
@@ -63,7 +65,14 @@ export function GoalProgressItem({
);
diff --git a/src/features/dashboard/components/installment-expenses/installment-expense-list-item.tsx b/src/features/dashboard/components/installment-expenses/installment-expense-list-item.tsx
index 5810b09..1a8655f 100644
--- a/src/features/dashboard/components/installment-expenses/installment-expense-list-item.tsx
+++ b/src/features/dashboard/components/installment-expenses/installment-expense-list-item.tsx
@@ -8,6 +8,7 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/shared/components/ui/tooltip";
+import { getPaymentMethodIcon } from "@/shared/utils/icons";
type InstallmentExpenseListItemProps = {
expense: InstallmentExpense;
@@ -27,6 +28,10 @@ export function InstallmentExpenseListItem({
return (
+
+ {getPaymentMethodIcon(expense.paymentMethod)}
+
+
@@ -61,7 +66,7 @@ export function InstallmentExpenseListItem({
{endDate ? `Termina em ${endDate}` : null}
- {" | Restante "}
+ {" · Restante "}
{
- const parts = value.trim().split(/\s+/).filter(Boolean);
- if (parts.length === 0) {
- return "??";
- }
- if (parts.length === 1) {
- const firstPart = parts[0];
- return firstPart ? firstPart.slice(0, 2).toUpperCase() : "??";
- }
- const firstChar = parts[0]?.[0] ?? "";
- const secondChar = parts[1]?.[0] ?? "";
- return `${firstChar}${secondChar}`.toUpperCase() || "??";
-};
-
export function PayersWidget({ payers }: PayersWidgetProps) {
return (
-
+
{payers.length === 0 ? (
}
@@ -123,6 +109,6 @@ export function PayersWidget({ payers }: PayersWidgetProps) {
})}
)}
-
+
);
}
diff --git a/src/features/payers/components/details/payer-card-usage-card.tsx b/src/features/payers/components/details/payer-card-usage-card.tsx
index 3225ca7..7872a35 100644
--- a/src/features/payers/components/details/payer-card-usage-card.tsx
+++ b/src/features/payers/components/details/payer-card-usage-card.tsx
@@ -5,18 +5,7 @@ import { CardContent } from "@/shared/components/ui/card";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state";
import { resolveLogoSrc } from "@/shared/lib/logo";
import type { PayerCardUsageItem } from "@/shared/lib/payers/details";
-
-const buildInitials = (value: string) => {
- const parts = value.trim().split(/\s+/).filter(Boolean);
- if (parts.length === 0) return "CC";
- if (parts.length === 1) {
- const firstPart = parts[0];
- return firstPart ? firstPart.slice(0, 2).toUpperCase() : "CC";
- }
- const firstChar = parts[0]?.[0] ?? "";
- const secondChar = parts[1]?.[0] ?? "";
- return `${firstChar}${secondChar}`.toUpperCase() || "CC";
-};
+import { buildInitials } from "@/shared/utils/initials";
type PagadorCardUsageCardProps = {
items: PayerCardUsageItem[];
diff --git a/src/features/reports/components/establishments/establishments-list.tsx b/src/features/reports/components/establishments/establishments-list.tsx
index fd8b235..68d6909 100644
--- a/src/features/reports/components/establishments/establishments-list.tsx
+++ b/src/features/reports/components/establishments/establishments-list.tsx
@@ -12,23 +12,12 @@ import {
} from "@/shared/components/ui/card";
import { Progress } from "@/shared/components/ui/progress";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state";
+import { buildInitials } from "@/shared/utils/initials";
type EstablishmentsListProps = {
establishments: TopEstabelecimentosData["establishments"];
};
-const buildInitials = (value: string) => {
- const parts = value.trim().split(/\s+/).filter(Boolean);
- if (parts.length === 0) return "ES";
- if (parts.length === 1) {
- const firstPart = parts[0];
- return firstPart ? firstPart.slice(0, 2).toUpperCase() : "ES";
- }
- const firstChar = parts[0]?.[0] ?? "";
- const secondChar = parts[1]?.[0] ?? "";
- return `${firstChar}${secondChar}`.toUpperCase() || "ES";
-};
-
export function EstablishmentsList({
establishments,
}: EstablishmentsListProps) {
diff --git a/src/shared/components/widget-card.tsx b/src/shared/components/widget-card.tsx
index b477f39..4e10251 100644
--- a/src/shared/components/widget-card.tsx
+++ b/src/shared/components/widget-card.tsx
@@ -12,7 +12,7 @@ export type WidgetCardProps = {
title: string;
subtitle: string;
children: React.ReactNode;
- icon: React.ReactNode;
+ icon?: React.ReactNode;
action?: React.ReactNode;
};
@@ -37,11 +37,11 @@ export default function WidgetCard({
-
- {icon}
+
+ {icon && {icon} }
{title}
-
+
{subtitle}
diff --git a/src/shared/utils/initials.ts b/src/shared/utils/initials.ts
new file mode 100644
index 0000000..eb280f4
--- /dev/null
+++ b/src/shared/utils/initials.ts
@@ -0,0 +1,15 @@
+/**
+ * Builds a 2-character initials string from a name.
+ * Falls back to the provided `fallback` (default "??") when the name is empty.
+ */
+export function buildInitials(value: string, fallback = "??"): string {
+ const parts = value.trim().split(/\s+/).filter(Boolean);
+ if (parts.length === 0) return fallback;
+ if (parts.length === 1) {
+ const firstPart = parts[0];
+ return firstPart ? firstPart.slice(0, 2).toUpperCase() : fallback;
+ }
+ const firstChar = parts[0]?.[0] ?? "";
+ const secondChar = parts[1]?.[0] ?? "";
+ return `${firstChar}${secondChar}`.toUpperCase() || fallback;
+}