mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-03-10 04:51:47 +00:00
perf: otimizar dashboard com indexes, cache e consolidação de queries (v1.3.0)
- Adicionar indexes compostos em lancamentos para queries frequentes - Eliminar ~20 JOINs com pagadores via helper cacheado getAdminPagadorId() - Consolidar queries: income-expense-balance (12→1), payment-status (2→1), categories (4→2) - Adicionar cache cross-request via unstable_cache com tag-based invalidation - Limitar scan de métricas a 24 meses - Deduplicar auth session por request via React.cache() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -67,9 +67,7 @@ export function TransferDialog({
|
||||
);
|
||||
|
||||
// Source account info
|
||||
const fromAccount = accounts.find(
|
||||
(account) => account.id === fromAccountId,
|
||||
);
|
||||
const fromAccount = accounts.find((account) => account.id === fromAccountId);
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -19,7 +19,8 @@ type PurchasesByCategoryWidgetProps = {
|
||||
data: PurchasesByCategoryData;
|
||||
};
|
||||
|
||||
const formatTransactionDate = (date: Date) => {
|
||||
const formatTransactionDate = (date: Date | string) => {
|
||||
const d = date instanceof Date ? date : new Date(date);
|
||||
const formatter = new Intl.DateTimeFormat("pt-BR", {
|
||||
weekday: "short",
|
||||
day: "2-digit",
|
||||
@@ -27,7 +28,7 @@ const formatTransactionDate = (date: Date) => {
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
const formatted = formatter.format(date);
|
||||
const formatted = formatter.format(d);
|
||||
// Capitaliza a primeira letra do dia da semana
|
||||
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
|
||||
};
|
||||
|
||||
@@ -8,7 +8,8 @@ type RecentTransactionsWidgetProps = {
|
||||
data: RecentTransactionsData;
|
||||
};
|
||||
|
||||
const formatTransactionDate = (date: Date) => {
|
||||
const formatTransactionDate = (date: Date | string) => {
|
||||
const d = date instanceof Date ? date : new Date(date);
|
||||
const formatter = new Intl.DateTimeFormat("pt-BR", {
|
||||
weekday: "short",
|
||||
day: "2-digit",
|
||||
@@ -16,7 +17,7 @@ const formatTransactionDate = (date: Date) => {
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
const formatted = formatter.format(date);
|
||||
const formatted = formatter.format(d);
|
||||
// Capitaliza a primeira letra do dia da semana
|
||||
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import type { DashboardCardMetrics } from "@/lib/dashboard/metrics";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
import MoneyValues from "../money-values";
|
||||
|
||||
type SectionCardsProps = {
|
||||
@@ -61,9 +60,7 @@ const getPercentChange = (current: number, previous: number): string => {
|
||||
|
||||
export function SectionCards({ metrics }: SectionCardsProps) {
|
||||
return (
|
||||
<div
|
||||
className={`${title_font.className} *:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-3 @xl/main:grid-cols-2 @5xl/main:grid-cols-4`}
|
||||
>
|
||||
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-3 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||
{CARDS.map(({ label, key, icon: Icon }) => {
|
||||
const metric = metrics[key];
|
||||
const trend = getTrend(metric.current, metric.previous);
|
||||
|
||||
@@ -16,7 +16,8 @@ type TopExpensesWidgetProps = {
|
||||
cardOnlyExpenses: TopExpensesData;
|
||||
};
|
||||
|
||||
const formatTransactionDate = (date: Date) => {
|
||||
const formatTransactionDate = (date: Date | string) => {
|
||||
const d = date instanceof Date ? date : new Date(date);
|
||||
const formatter = new Intl.DateTimeFormat("pt-BR", {
|
||||
weekday: "short",
|
||||
day: "2-digit",
|
||||
@@ -24,7 +25,7 @@ const formatTransactionDate = (date: Date) => {
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
const formatted = formatter.format(date);
|
||||
const formatted = formatter.format(d);
|
||||
// Capitaliza a primeira letra do dia da semana
|
||||
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
|
||||
};
|
||||
|
||||
@@ -72,7 +72,6 @@ import { getAvatarSrc } from "@/lib/pagadores/utils";
|
||||
import { formatDate } from "@/lib/utils/date";
|
||||
import { getConditionIcon, getPaymentMethodIcon } from "@/lib/utils/icons";
|
||||
import { cn } from "@/lib/utils/ui";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
import { LancamentosExport } from "../lancamentos-export";
|
||||
import { EstabelecimentoLogo } from "../shared/estabelecimento-logo";
|
||||
import type {
|
||||
@@ -928,7 +927,7 @@ export function LancamentosTable({
|
||||
<>
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader className={`${title_font.className}`}>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
|
||||
@@ -89,7 +89,7 @@ export default function MonthNavigation() {
|
||||
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className="mx-1 space-x-1 capitalize font-bold"
|
||||
className="mx-1 space-x-1 capitalize font-semibold"
|
||||
aria-current={!isDifferentFromCurrent ? "date" : undefined}
|
||||
aria-label={`Período selecionado: ${currentMonthLabel} de ${currentYear}`}
|
||||
>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { WidgetEmptyState } from "@/components/widget-empty-state";
|
||||
import type { CardDetailData } from "@/lib/relatorios/cartoes-report";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
|
||||
type CardCategoryBreakdownProps = {
|
||||
data: CardDetailData["categoryBreakdown"];
|
||||
@@ -18,9 +17,7 @@ export function CardCategoryBreakdown({ data }: CardCategoryBreakdownProps) {
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiPieChartLine className="size-4 text-primary" />
|
||||
Gastos por Categoria
|
||||
</CardTitle>
|
||||
@@ -41,9 +38,7 @@ export function CardCategoryBreakdown({ data }: CardCategoryBreakdownProps) {
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiPieChartLine className="size-4 text-primary" />
|
||||
Gastos por Categoria
|
||||
</CardTitle>
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
} from "@/components/ui/tooltip";
|
||||
import type { CardDetailData } from "@/lib/relatorios/cartoes-report";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
|
||||
type CardInvoiceStatusProps = {
|
||||
data: CardDetailData["invoiceStatus"];
|
||||
@@ -75,9 +74,7 @@ export function CardInvoiceStatus({ data }: CardInvoiceStatusProps) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiCalendarCheckLine className="size-4 text-primary" />
|
||||
Faturas
|
||||
</CardTitle>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { WidgetEmptyState } from "@/components/widget-empty-state";
|
||||
import type { CardDetailData } from "@/lib/relatorios/cartoes-report";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
|
||||
type CardTopExpensesProps = {
|
||||
data: CardDetailData["topExpenses"];
|
||||
@@ -18,9 +17,7 @@ export function CardTopExpenses({ data }: CardTopExpensesProps) {
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiShoppingBag3Line className="size-4 text-primary" />
|
||||
Top 10 Gastos do Mês
|
||||
</CardTitle>
|
||||
@@ -43,9 +40,7 @@ export function CardTopExpenses({ data }: CardTopExpensesProps) {
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiShoppingBag3Line className="size-4 text-primary" />
|
||||
Top 10 Gastos do Mês
|
||||
</CardTitle>
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
ChartTooltip,
|
||||
} from "@/components/ui/chart";
|
||||
import type { CardDetailData } from "@/lib/relatorios/cartoes-report";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
|
||||
type CardUsageChartProps = {
|
||||
data: CardDetailData["monthlyUsage"];
|
||||
@@ -82,9 +81,7 @@ export function CardUsageChart({ data, limit, card }: CardUsageChartProps) {
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiBarChartBoxLine className="size-4 text-primary" />
|
||||
Histórico de Uso
|
||||
</CardTitle>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import type { CategoryReportData, CategoryReportItem } from "@/lib/relatorios/types";
|
||||
import type {
|
||||
CategoryReportData,
|
||||
CategoryReportItem,
|
||||
} from "@/lib/relatorios/types";
|
||||
import { CategoryTable } from "./category-table";
|
||||
|
||||
interface CategoryReportTableProps {
|
||||
|
||||
@@ -78,9 +78,6 @@ function LogoContent() {
|
||||
const isCollapsed = state === "collapsed";
|
||||
|
||||
return (
|
||||
<Logo
|
||||
variant={isCollapsed ? "small" : "full"}
|
||||
showVersion={!isCollapsed}
|
||||
/>
|
||||
<Logo variant={isCollapsed ? "small" : "full"} showVersion={!isCollapsed} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { WidgetEmptyState } from "@/components/widget-empty-state";
|
||||
import type { TopEstabelecimentosData } from "@/lib/top-estabelecimentos/fetch-data";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
import { Progress } from "../ui/progress";
|
||||
|
||||
type EstablishmentsListProps = {
|
||||
@@ -32,9 +31,7 @@ export function EstablishmentsList({
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiStore2Line className="size-4 text-primary" />
|
||||
Top Estabelecimentos
|
||||
</CardTitle>
|
||||
@@ -55,9 +52,7 @@ export function EstablishmentsList({
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiStore2Line className="size-4 text-primary" />
|
||||
Top Estabelecimentos por Frequência
|
||||
</CardTitle>
|
||||
|
||||
@@ -6,7 +6,6 @@ import MoneyValues from "@/components/money-values";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { WidgetEmptyState } from "@/components/widget-empty-state";
|
||||
import type { TopEstabelecimentosData } from "@/lib/top-estabelecimentos/fetch-data";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
import { Progress } from "../ui/progress";
|
||||
|
||||
type TopCategoriesProps = {
|
||||
@@ -18,9 +17,7 @@ export function TopCategories({ categories }: TopCategoriesProps) {
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiPriceTag3Line className="size-4 text-primary" />
|
||||
Principais Categorias
|
||||
</CardTitle>
|
||||
@@ -41,9 +38,7 @@ export function TopCategories({ categories }: TopCategoriesProps) {
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1.5 text-base`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1.5 text-base">
|
||||
<RiPriceTag3Line className="size-4 text-primary" />
|
||||
Principais Categorias
|
||||
</CardTitle>
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { title_font } from "@/public/fonts/font_index";
|
||||
import { Button } from "./ui/button";
|
||||
|
||||
const OVERFLOW_THRESHOLD_PX = 16;
|
||||
@@ -79,9 +78,7 @@ export default function WidgetCard({
|
||||
<CardHeader className="border-b [.border-b]:pb-2">
|
||||
<div className="flex w-full items-start justify-between">
|
||||
<div>
|
||||
<CardTitle
|
||||
className={`${title_font.className} flex items-center gap-1`}
|
||||
>
|
||||
<CardTitle className="flex items-center gap-1">
|
||||
<span className="text-primary">{icon}</span>
|
||||
{title}
|
||||
</CardTitle>
|
||||
|
||||
Reference in New Issue
Block a user