mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
perf(logos): pré-resolver mapeamentos Logo.dev no servidor
Cada EstablishmentLogo dispara um GET para /api/logo/mapping por nome único (deduplicado pelo React Query, mas ainda N requests por página). Em /dashboard, /transactions e /payers/[payerId] agora fazemos uma única query SQL em batch (fetchEstablishmentLogoMap) e semeamos o cache do React Query antes do primeiro render via novo LogoPrefetchProvider — eliminando os requests da rede. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -5,3 +5,4 @@ export type {
|
||||
export { CategoryIconBadge } from "./category-icon-badge";
|
||||
export { EstablishmentLogo } from "./establishment-logo";
|
||||
export { EstablishmentLogoPicker } from "./establishment-logo-picker";
|
||||
export { LogoPrefetchProvider } from "./logo-prefetch-provider";
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { type ReactNode, useRef } from "react";
|
||||
import { logoQueryKeys } from "@/shared/lib/logo";
|
||||
import type { LogoPrefetchEntry } from "@/shared/lib/logo/types";
|
||||
|
||||
type LogoPrefetchProviderProps = {
|
||||
mappings: LogoPrefetchEntry[];
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Semeia o cache do React Query com mapeamentos de logo já resolvidos
|
||||
* no servidor. Evita que cada `EstablishmentLogo` dispare seu próprio
|
||||
* GET para `/api/logo/mapping` no primeiro render.
|
||||
*/
|
||||
export function LogoPrefetchProvider({
|
||||
mappings,
|
||||
children,
|
||||
}: LogoPrefetchProviderProps) {
|
||||
const queryClient = useQueryClient();
|
||||
const seeded = useRef(false);
|
||||
|
||||
if (!seeded.current) {
|
||||
for (const { nameKey, domain, logoUrl } of mappings) {
|
||||
queryClient.setQueryData(logoQueryKeys.mapping(nameKey), {
|
||||
domain,
|
||||
logoUrl,
|
||||
});
|
||||
}
|
||||
seeded.current = true;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
31
src/shared/lib/logo/prefetch-server.ts
Normal file
31
src/shared/lib/logo/prefetch-server.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import "server-only";
|
||||
|
||||
import { fetchEstablishmentLogoMap } from "./establishment-logo-queries";
|
||||
import { toNameKey } from "./index";
|
||||
import { buildLogoDevUrl } from "./server";
|
||||
import type { LogoPrefetchEntry } from "./types";
|
||||
|
||||
export async function prefetchLogoMappings(
|
||||
userId: string,
|
||||
names: string[],
|
||||
): Promise<LogoPrefetchEntry[]> {
|
||||
const uniqueNames = [
|
||||
...new Set(
|
||||
names.filter((n) => typeof n === "string" && n.trim().length > 0),
|
||||
),
|
||||
];
|
||||
if (uniqueNames.length === 0) return [];
|
||||
|
||||
const map = await fetchEstablishmentLogoMap(userId, uniqueNames);
|
||||
|
||||
const seen = new Set<string>();
|
||||
const entries: LogoPrefetchEntry[] = [];
|
||||
for (const name of uniqueNames) {
|
||||
const nameKey = toNameKey(name);
|
||||
if (seen.has(nameKey)) continue;
|
||||
seen.add(nameKey);
|
||||
const domain = map.get(nameKey) ?? null;
|
||||
entries.push({ nameKey, domain, logoUrl: buildLogoDevUrl(domain) });
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
5
src/shared/lib/logo/types.ts
Normal file
5
src/shared/lib/logo/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export type LogoPrefetchEntry = {
|
||||
nameKey: string;
|
||||
domain: string | null;
|
||||
logoUrl: string | null;
|
||||
};
|
||||
Reference in New Issue
Block a user