From 8a222afb0e3e1ad7139c65806b2e0b3411d32394 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 6 Jul 2026 09:54:25 +0200 Subject: [PATCH 1/2] chore: gitignore .worktrees/ for local parallel dev worktrees --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7868101a..f80656a9 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ apps/api/uploads/ # Entorno local personal (scripts, seeds, docker extra) — no subir al repo .local/ + +# Local git worktrees for parallel agent work — never commit +.worktrees/ From 3c9ffc5acb5917ddc6f32881c034070de377c49a Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 6 Jul 2026 09:59:43 +0200 Subject: [PATCH 2/2] perf(web): deduplicar fetch de auth/notifications en AppBar (#277) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AppBar hacía sus propias llamadas a /auth/me y /notifications/mine en cada página pública para usuarios autenticados, duplicando las llamadas que otros consumidores (dashboard-layout, páginas de recepción/recursos, etc.) ya hacen en la misma request a través de getMe()/getNotificationUnread() en navigation-data.ts, ambas ya envueltas en React cache(). Se reemplazan las llamadas directas a la API por esos loaders cacheados, reutilizando el mismo round-trip dentro de una request en vez de repetirlo por página. --- apps/web/src/components/organisms/app-bar.tsx | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/apps/web/src/components/organisms/app-bar.tsx b/apps/web/src/components/organisms/app-bar.tsx index 0602d468..71980ac8 100644 --- a/apps/web/src/components/organisms/app-bar.tsx +++ b/apps/web/src/components/organisms/app-bar.tsx @@ -1,7 +1,7 @@ import Link from 'next/link'; -import { api } from '@/lib/api'; -import { getToken, authHeaders, loginHref as buildLoginHref } from '@/lib/auth'; +import { loginHref as buildLoginHref } from '@/lib/auth'; import { getT } from '@/i18n/server'; +import { getMe, getNotificationUnread } from '@/lib/navigation-data'; import { BrandLogo } from '@/components/molecules/brand-logo'; import { LanguageSwitcher } from '@/components/molecules/language-switcher'; import { AccountControl } from '@/components/molecules/account-control'; @@ -28,20 +28,14 @@ const STATUS_DOT: Record<'active' | 'paused' | 'closed', string> = { export async function AppBar({ variant, slug, emergency, backHref }: AppBarProps) { const { t } = await getT(); const ta = t.appbar; - const token = await getToken(); - - let user: { name: string; email: string; isAdmin: boolean } | null = null; - let unreadCount = 0; - if (token != null) { - const [meRes, notifRes] = await Promise.all([ - api.GET('/auth/me', { headers: authHeaders(token) }), - api.GET('/notifications/mine', { headers: authHeaders(token) }), - ]); - if (meRes.data != null) { - user = { name: meRes.data.name, email: meRes.data.email, isAdmin: meRes.data.isAdmin === true }; - } - if (notifRes.data != null) unreadCount = notifRes.data.unreadCount; - } + + // getMe()/getNotificationUnread() are wrapped in React `cache()` (see + // navigation-data.ts), so this dedupes with any other consumer that reads + // the same principal/notifications data within this request instead of + // re-hitting /auth/me and /notifications/mine per page (#277). + const [me, unreadCount] = await Promise.all([getMe(), getNotificationUnread()]); + const user = + me != null ? { name: me.name, email: me.email, isAdmin: me.isAdmin === true } : null; const currentPath = slug != null ? `/e/${slug}` : '/'; const loginHref = buildLoginHref(currentPath);