From 0394f4e795b79a1a72a6e2a57a1f892bd2451dc6 Mon Sep 17 00:00:00 2001 From: francy51 Date: Fri, 13 Mar 2026 19:05:17 -0400 Subject: [PATCH] Add company overview skeleton and cache --- app/analysis/page.tsx | 24 +- .../analysis/company-analysis-skeleton.tsx | 141 ++++++++ drizzle/0012_company_overview_cache.sql | 15 + e2e/analysis.spec.ts | 127 +++++++ lib/api.ts | 5 +- lib/query/options.ts | 4 +- lib/server/api/app.ts | 146 +------- .../api/task-workflow-hybrid.e2e.test.ts | 224 +++++++++++- lib/server/company-analysis.test.ts | 251 +++++++++++++ lib/server/company-analysis.ts | 336 ++++++++++++++++++ lib/server/db/index.test.ts | 1 + lib/server/db/index.ts | 4 + lib/server/db/schema.ts | 15 + lib/server/prices.test.ts | 64 ++++ lib/server/prices.ts | 65 +++- .../repos/company-overview-cache.test.ts | 202 +++++++++++ lib/server/repos/company-overview-cache.ts | 102 ++++++ lib/server/repos/tasks.test.ts | 3 +- 18 files changed, 1571 insertions(+), 158 deletions(-) create mode 100644 components/analysis/company-analysis-skeleton.tsx create mode 100644 drizzle/0012_company_overview_cache.sql create mode 100644 e2e/analysis.spec.ts create mode 100644 lib/server/company-analysis.test.ts create mode 100644 lib/server/company-analysis.ts create mode 100644 lib/server/prices.test.ts create mode 100644 lib/server/repos/company-overview-cache.test.ts create mode 100644 lib/server/repos/company-overview-cache.ts diff --git a/app/analysis/page.tsx b/app/analysis/page.tsx index e4f5cc3..5908b0a 100644 --- a/app/analysis/page.tsx +++ b/app/analysis/page.tsx @@ -4,6 +4,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { Suspense, useCallback, useEffect, useMemo, useState } from 'react'; import { useSearchParams } from 'next/navigation'; import { AppShell } from '@/components/shell/app-shell'; +import { CompanyAnalysisSkeleton } from '@/components/analysis/company-analysis-skeleton'; import { AnalysisToolbar } from '@/components/analysis/analysis-toolbar'; import { BullBearPanel } from '@/components/analysis/bull-bear-panel'; import { CompanyOverviewCard } from '@/components/analysis/company-overview-card'; @@ -55,21 +56,28 @@ function AnalysisPageContent() { setTicker(normalized); }, [searchParams]); - const loadAnalysis = useCallback(async (symbol: string) => { - const options = companyAnalysisQueryOptions(symbol); + const loadAnalysis = useCallback(async (symbol: string, options?: { refresh?: boolean }) => { + const queryOptions = companyAnalysisQueryOptions(symbol, options); - if (!queryClient.getQueryData(options.queryKey)) { + if (!queryClient.getQueryData(queryOptions.queryKey)) { setLoading(true); } setError(null); try { - const response = await queryClient.fetchQuery(options); + const response = await queryClient.fetchQuery(queryOptions); setAnalysis(response.analysis); } catch (err) { setError(err instanceof Error ? err.message : 'Unable to load company overview'); - setAnalysis(null); + setAnalysis((current) => { + const normalizedTicker = symbol.trim().toUpperCase(); + if (options?.refresh && current?.company.ticker === normalizedTicker) { + return current; + } + + return null; + }); } finally { setLoading(false); } @@ -116,7 +124,7 @@ function AnalysisPageContent() { onRefresh={() => { const normalizedTicker = activeTicker.trim().toUpperCase(); void queryClient.invalidateQueries({ queryKey: queryKeys.companyAnalysis(normalizedTicker) }); - void loadAnalysis(normalizedTicker); + void loadAnalysis(normalizedTicker, { refresh: true }); }} quickLinks={quickLinks} onLinkPrefetch={() => prefetchResearchTicker(activeTicker)} @@ -128,7 +136,9 @@ function AnalysisPageContent() { ) : null} - {analysis ? ( + {!analysis && loading ? ( + + ) : analysis ? ( <>