chore: remover seções vazias de mudanças de código

Este commit remove seções vazias de mudanças de código do arquivo de
mudanças. Isso ajuda a manter o histórico de mudanças mais limpo e
organizado, facilitando a leitura e a compreensão das alterações
realizadas no projeto.
This commit is contained in:
Felipe Coutinho
2025-12-16 23:20:47 +00:00
parent 0767636eed
commit e7cb9c9db1
37 changed files with 1350 additions and 920 deletions

View File

@@ -1,9 +1,8 @@
"use client";
import { cn } from "@/lib/utils/ui";
import type { CalendarEvent } from "@/components/calendario/types";
import { EVENT_TYPE_STYLES } from "@/components/calendario/day-cell";
import type { CalendarEvent } from "@/components/calendario/types";
import { cn } from "@/lib/utils/ui";
const LEGEND_ITEMS: Array<{
type: CalendarEvent["type"];
@@ -16,17 +15,12 @@ const LEGEND_ITEMS: Array<{
export function CalendarLegend() {
return (
<div className="flex flex-wrap gap-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-2 text-[11px] font-medium text-muted-foreground">
<div className="flex flex-wrap gap-3 rounded-sm border border-border/60 bg-muted/20 p-2 text-xs font-medium text-muted-foreground">
{LEGEND_ITEMS.map((item) => {
const style = EVENT_TYPE_STYLES[item.type];
return (
<span key={item.type} className="flex items-center gap-2">
<span
className={cn(
"size-2.5 rounded-full border border-black/10 dark:border-white/20",
style.dot
)}
/>
<span className={cn("size-3 rounded-full", style.dot)} />
{item.label}
</span>
);

View File

@@ -1,10 +1,10 @@
"use client";
import type { CalendarDay, CalendarEvent } from "@/components/calendario/types";
import { currencyFormatter } from "@/lib/lancamentos/formatting-helpers";
import { cn } from "@/lib/utils/ui";
import { RiAddLine } from "@remixicon/react";
import type { KeyboardEvent, MouseEvent } from "react";
import type { CalendarDay, CalendarEvent } from "@/components/calendario/types";
import { currencyFormatter } from "@/lib/lancamentos/formatting-helpers";
type DayCellProps = {
day: CalendarDay;
@@ -17,17 +17,18 @@ export const EVENT_TYPE_STYLES: Record<
{ wrapper: string; dot: string; accent?: string }
> = {
lancamento: {
wrapper: "bg-cyan-600 text-cyan-50 dark:bg-cyan-500/30 dark:text-cyan-200",
dot: "bg-cyan-600",
wrapper:
"bg-orange-100 text-orange-600 dark:bg-orange-800 dark:text-orange-50",
dot: "bg-orange-600",
},
boleto: {
wrapper: "bg-red-600 text-red-50 dark:bg-red-500/30 dark:text-red-200",
dot: "bg-red-600",
wrapper:
"bg-emerald-100 text-emerald-600 dark:bg-emerald-800 dark:text-emerald-50",
dot: "bg-emerald-600",
},
cartao: {
wrapper:
"bg-violet-600 text-violet-50 dark:bg-violet-500/30 dark:text-violet-200",
dot: "bg-violet-600",
wrapper: "bg-blue-100 text-blue-600 dark:bg-blue-800 dark:text-blue-50",
dot: "bg-blue-600",
},
};
@@ -82,11 +83,12 @@ const DayEventPreview = ({ event }: { event: CalendarEvent }) => {
return (
<div
className={cn(
"flex w-full items-center justify-between gap-2 rounded p-1 text-xs leading-tight",
"flex w-full items-center justify-between gap-2 rounded p-1 text-xs",
style.wrapper
)}
>
<div className="flex min-w-0">
<div className="flex min-w-0 items-center gap-1">
<span className={cn("size-1.5 rounded-full", style.dot)} />
<span className="truncate">{label}</span>
</div>
{complement ? (

View File

@@ -87,7 +87,7 @@ export function CategoryDetailHeader({
<Card className="px-4">
<div className="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
<div className="flex items-start gap-3">
<span className="flex size-12 items-center justify-center rounded-xl bg-muted border text-primary">
<span className="flex size-12 items-center justify-center rounded-xl bg-muted">
{IconComponent ? (
<IconComponent className="size-6" aria-hidden />
) : (

View File

@@ -19,6 +19,7 @@ import type { ChangelogEntry } from "@/lib/changelog/data";
import {
getCategoryLabel,
groupEntriesByCategory,
parseSafariCompatibleDate,
} from "@/lib/changelog/utils";
import { cn } from "@/lib/utils";
import { RiMegaphoneLine } from "@remixicon/react";
@@ -115,7 +116,7 @@ export function ChangelogNotification({
{entry.title}
</p>
<p className="text-xs text-muted-foreground">
{formatDistanceToNow(new Date(entry.date), {
{formatDistanceToNow(parseSafariCompatibleDate(entry.date), {
addSuffix: true,
locale: ptBR,
})}

View File

@@ -205,7 +205,7 @@ export function CategoryHistoryWidget({ data }: CategoryHistoryWidgetProps) {
<CardContent className="space-y-2.5">
<div className="space-y-2">
{selectedCategoryDetails.length > 0 && (
<div className="flex items-start justify-between gap-4">
<div className="flex items-start justify-between gap-4 mb-4">
<div className="flex flex-wrap gap-2">
{selectedCategoryDetails.map((category) => {
if (!category) return null;

View File

@@ -130,7 +130,7 @@ export function InstallmentAnalysisPage({
return (
<div className="flex flex-col gap-4">
{/* Card de resumo principal */}
<Card className="border-primary/20 bg-primary/5">
<Card className="border-none bg-primary/15">
<CardContent className="flex flex-col items-start justify-center gap-2 py-2">
<p className="text-sm font-medium text-muted-foreground">
Se você pagar tudo que está selecionado:

View File

@@ -7,6 +7,7 @@ import {
CardTitle,
} from "@/components/ui/card";
import type { DashboardCardMetrics } from "@/lib/dashboard/metrics";
import { title_font } from "@/public/fonts/font_index";
import {
RiArrowDownLine,
RiArrowUpLine,
@@ -15,7 +16,6 @@ import {
RiSubtractLine,
} from "@remixicon/react";
import MoneyValues from "../money-values";
import { title_font } from "@/public/fonts/font_index";
type SectionCardsProps = {
metrics: DashboardCardMetrics;
@@ -73,7 +73,7 @@ export function SectionCards({ metrics }: SectionCardsProps) {
<Card key={label} className="@container/card gap-2">
<CardHeader>
<CardTitle className="flex items-center gap-1">
<Icon className="size-4" />
<Icon className="size-4 text-primary" />
{label}
</CardTitle>
<MoneyValues className="text-2xl" amount={metric.current} />
@@ -84,9 +84,9 @@ export function SectionCards({ metrics }: SectionCardsProps) {
</Badge>
</CardAction>
</CardHeader>
<CardFooter className="flex-col items-start gap-1.5 text-sm">
<div className="line-clamp-1 flex gap-2 text-sm">
Mês anterior:
<CardFooter className="flex-col items-start gap-2 text-sm">
<div className="line-clamp-1 flex gap-2 text-xs">
Mês anterior
</div>
<div className="text-muted-foreground">
<MoneyValues amount={metric.previous} />

View File

@@ -1,25 +1,30 @@
"use client";
import {
deleteSavedInsightsAction,
generateInsightsAction,
loadSavedInsightsAction,
saveInsightsAction,
deleteSavedInsightsAction,
} from "@/app/(dashboard)/insights/actions";
import { DEFAULT_MODEL } from "@/app/(dashboard)/insights/data";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { Alert, AlertDescription } from "@/components/ui/alert";
import type { InsightsResponse } from "@/lib/schemas/insights";
import { RiDeleteBinLine, RiSaveLine, RiSparklingLine, RiAlertLine } from "@remixicon/react";
import {
RiAlertLine,
RiDeleteBinLine,
RiSaveLine,
RiSparklingLine,
} from "@remixicon/react";
import { format } from "date-fns";
import { ptBR } from "date-fns/locale";
import { useEffect, useState, useTransition } from "react";
import { toast } from "sonner";
import { EmptyState } from "../empty-state";
import { InsightsGrid } from "./insights-grid";
import { ModelSelector } from "./model-selector";
import { format } from "date-fns";
import { ptBR } from "date-fns/locale";
interface InsightsPageProps {
period: string;
@@ -129,11 +134,14 @@ export function InsightsPage({ period, onAnalyze }: InsightsPageProps) {
return (
<div className="flex flex-col gap-6">
{/* Privacy Warning */}
<Alert>
<Alert className="border-none">
<RiAlertLine className="size-4" />
<AlertDescription className="text-sm">
<strong>Aviso de privacidade:</strong> Ao gerar insights, seus dados financeiros serão enviados para o provedor de IA selecionado
(Anthropic, OpenAI, Google ou OpenRouter) para processamento. Certifique-se de que você confia no provedor escolhido antes de prosseguir.
<strong>Aviso de privacidade:</strong> Ao gerar insights, seus dados
financeiros serão enviados para o provedor de IA selecionado
(Anthropic, OpenAI, Google ou OpenRouter) para processamento.
Certifique-se de que você confia no provedor escolhido antes de
prosseguir.
</AlertDescription>
</Alert>

View File

@@ -30,7 +30,6 @@ import { createMonthOptions } from "@/lib/utils/period";
import { RiAddLine, RiDeleteBinLine } from "@remixicon/react";
import { useMemo, useState } from "react";
import { toast } from "sonner";
import { EstabelecimentoInput } from "../shared/estabelecimento-input";
import type { SelectOption } from "../../types";
import {
CategoriaSelectContent,
@@ -40,6 +39,7 @@ import {
PaymentMethodSelectContent,
TransactionTypeSelectContent,
} from "../select-items";
import { EstabelecimentoInput } from "../shared/estabelecimento-input";
interface MassAddDialogProps {
open: boolean;
@@ -57,7 +57,6 @@ interface MassAddDialogProps {
export interface MassAddFormData {
fixedFields: {
transactionType?: string;
pagadorId?: string;
paymentMethod?: string;
condition?: string;
period?: string;
@@ -69,6 +68,7 @@ export interface MassAddFormData {
name: string;
amount: string;
categoriaId?: string;
pagadorId?: string;
}>;
}
@@ -78,6 +78,7 @@ interface TransactionRow {
name: string;
amount: string;
categoriaId: string | undefined;
pagadorId: string | undefined;
}
export function MassAddDialog({
@@ -96,9 +97,6 @@ export function MassAddDialog({
// Fixed fields state (sempre ativos, sem checkboxes)
const [transactionType, setTransactionType] = useState<string>("Despesa");
const [pagadorId, setPagadorId] = useState<string | undefined>(
defaultPagadorId ?? undefined
);
const [paymentMethod, setPaymentMethod] = useState<string>(
LANCAMENTO_PAYMENT_METHODS[0]
);
@@ -115,6 +113,7 @@ export function MassAddDialog({
name: "",
amount: "",
categoriaId: undefined,
pagadorId: defaultPagadorId ?? undefined,
},
]);
@@ -141,6 +140,7 @@ export function MassAddDialog({
name: "",
amount: "",
categoriaId: undefined,
pagadorId: defaultPagadorId ?? undefined,
},
]);
};
@@ -191,7 +191,6 @@ export function MassAddDialog({
const formData: MassAddFormData = {
fixedFields: {
transactionType,
pagadorId,
paymentMethod,
condition,
period,
@@ -203,6 +202,7 @@ export function MassAddDialog({
name: t.name.trim(),
amount: t.amount.trim(),
categoriaId: t.categoriaId,
pagadorId: t.pagadorId,
})),
};
@@ -212,7 +212,6 @@ export function MassAddDialog({
onOpenChange(false);
// Reset form
setTransactionType("Despesa");
setPagadorId(defaultPagadorId ?? undefined);
setPaymentMethod(LANCAMENTO_PAYMENT_METHODS[0]);
setCondition("À vista");
setPeriod(selectedPeriod);
@@ -225,6 +224,7 @@ export function MassAddDialog({
name: "",
amount: "",
categoriaId: undefined,
pagadorId: defaultPagadorId ?? undefined,
},
]);
} catch (_error) {
@@ -236,7 +236,7 @@ export function MassAddDialog({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto p-6 sm:px-8">
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto p-6 sm:px-8">
<DialogHeader>
<DialogTitle>Adicionar múltiplos lançamentos</DialogTitle>
<DialogDescription>
@@ -248,7 +248,7 @@ export function MassAddDialog({
{/* Fixed Fields Section */}
<div className="space-y-4">
<h3 className="text-sm font-semibold">Valores Padrão</h3>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-5">
{/* Transaction Type */}
<div className="space-y-2">
<Label htmlFor="transaction-type">Tipo de Transação</Label>
@@ -274,39 +274,6 @@ export function MassAddDialog({
</Select>
</div>
{/* Pagador */}
<div className="space-y-2">
<Label htmlFor="pagador">Pagador</Label>
<Select value={pagadorId} onValueChange={setPagadorId}>
<SelectTrigger id="pagador" className="w-full">
<SelectValue placeholder="Selecione o pagador">
{pagadorId &&
(() => {
const selectedOption = pagadorOptions.find(
(opt) => opt.value === pagadorId
);
return selectedOption ? (
<PagadorSelectContent
label={selectedOption.label}
avatarUrl={selectedOption.avatarUrl}
/>
) : null;
})()}
</SelectValue>
</SelectTrigger>
<SelectContent>
{pagadorOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
<PagadorSelectContent
label={option.label}
avatarUrl={option.avatarUrl}
/>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Payment Method */}
<div className="space-y-2">
<Label htmlFor="payment-method">Forma de Pagamento</Label>
@@ -489,6 +456,7 @@ export function MassAddDialog({
)
}
placeholder="Data"
className="w-32 truncate"
required
/>
</div>
@@ -528,6 +496,52 @@ export function MassAddDialog({
required
/>
</div>
<div className="w-full">
<Label
htmlFor={`pagador-${transaction.id}`}
className="sr-only"
>
Pagador {index + 1}
</Label>
<Select
value={transaction.pagadorId}
onValueChange={(value) =>
updateTransaction(transaction.id, "pagadorId", value)
}
>
<SelectTrigger
id={`pagador-${transaction.id}`}
className="w-32 truncate"
>
<SelectValue placeholder="Pagador">
{transaction.pagadorId &&
(() => {
const selectedOption = pagadorOptions.find(
(opt) => opt.value === transaction.pagadorId
);
return selectedOption ? (
<PagadorSelectContent
label={selectedOption.label}
avatarUrl={selectedOption.avatarUrl}
/>
) : null;
})()}
</SelectValue>
</SelectTrigger>
<SelectContent>
{pagadorOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
<PagadorSelectContent
label={option.label}
avatarUrl={option.avatarUrl}
/>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="w-full">
<Label
htmlFor={`categoria-${transaction.id}`}
@@ -547,7 +561,7 @@ export function MassAddDialog({
>
<SelectTrigger
id={`categoria-${transaction.id}`}
className="w-42 truncate"
className="w-32 truncate"
>
<SelectValue placeholder="Categoria" />
</SelectTrigger>

View File

@@ -712,17 +712,24 @@ export function LancamentosTable({
</Button>
) : null}
{onMassAdd ? (
<Button
onClick={onMassAdd}
variant="outline"
size="icon"
className="shrink-0"
>
<RiAddCircleFill className="size-4" />
<span className="sr-only">
Adicionar múltiplos lançamentos
</span>
</Button>
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={onMassAdd}
variant="outline"
size="icon"
className="shrink-0"
>
<RiAddCircleFill className="size-4" />
<span className="sr-only">
Adicionar múltiplos lançamentos
</span>
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Adicionar múltiplos lançamentos</p>
</TooltipContent>
</Tooltip>
) : null}
</div>
) : (

View File

@@ -21,7 +21,7 @@ export function Logo({ variant = "full", className }: LogoProps) {
}
return (
<div className={cn("flex items-center", className)}>
<div className={cn("flex items-center py-4", className)}>
<Image
src="/logo_small.png"
alt="Opensheets"

View File

@@ -22,7 +22,7 @@ function MoneyValues({ amount, className }: Props) {
<span
className={cn(
money_font.className,
"inline-flex items-baseline font-medium transition-all duration-200",
"inline-flex items-baseline transition-all duration-200 tracking-tighter",
privacyMode &&
"blur-[6px] select-none hover:blur-none focus-within:blur-none",
className

View File

@@ -89,7 +89,7 @@ export default function MonthPicker() {
<div className="flex items-center">
<div
className="mx-1 space-x-1 capitalize font-medium"
className="mx-1 space-x-1 capitalize font-bold"
aria-current={!isDifferentFromCurrent ? "date" : undefined}
aria-label={`Período selecionado: ${currentMonthLabel} de ${currentYear}`}
>

View File

@@ -51,7 +51,7 @@ export function AppSidebar({
<SidebarMenuItem>
<SidebarMenuButton
asChild
className="data-[slot=sidebar-menu-button]:px-1.5! hover:bg-transparent"
className="data-[slot=sidebar-menu-button]:px-1.5! hover:bg-transparent active:bg-transparent pt-4 justify-center hover:scale-105 transition-all duration-200"
>
<a href="/dashboard">
<LogoContent />

View File

@@ -48,6 +48,14 @@ type NavSection = {
items: NavItem[];
};
const MONTH_PERIOD_PARAM = "periodo";
const PERIOD_AWARE_PATHS = new Set([
"/dashboard",
"/lancamentos",
"/orcamentos",
]);
export function NavMain({ sections }: { sections: NavSection[] }) {
const pathname = usePathname();
const searchParams = useSearchParams();
@@ -167,7 +175,7 @@ export function NavMain({ sections }: { sections: NavSection[] }) {
src={avatarSrc}
alt={`Avatar de ${subItem.title}`}
/>
<AvatarFallback className="text-[10px] font-medium uppercase">
<AvatarFallback className="text-xs font-medium uppercase">
{initial}
</AvatarFallback>
</Avatar>
@@ -196,11 +204,3 @@ export function NavMain({ sections }: { sections: NavSection[] }) {
</>
);
}
const MONTH_PERIOD_PARAM = "periodo";
const PERIOD_AWARE_PATHS = new Set([
"/dashboard",
"/lancamentos",
"/orcamentos",
]);

View File

@@ -12,10 +12,10 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { title_font } from "@/public/fonts/font_index";
import { RiExpandDiagonalLine } from "@remixicon/react";
import { useCallback, useEffect, useRef, useState } from "react";
import { Button } from "./ui/button";
import { title_font } from "@/public/fonts/font_index";
const OVERFLOW_THRESHOLD_PX = 16;
const OVERFLOW_CHECK_DEBOUNCE_MS = 100;
@@ -82,7 +82,7 @@ export default function WidgetCard({
<CardTitle
className={`${title_font.className} flex items-center gap-1`}
>
{icon}
<span className="text-primary">{icon}</span>
{title}
</CardTitle>
<CardDescription className="text-muted-foreground text-sm capitalize mt-1">