fix: wire data adapters, fix OptionsAdapter v3, build Research pages

- Fix OptionsAdapter for yahoo-finance2 v3 API ({ date: expiry } instead of positional arg)
- Fix institutional.flow and insiderStream endpoints to query correct DB tables
- Fix anonymous user INSERT (pw_hash column, not password_hash)
- Create secDataFetcher module for 13F/Form 4 data population
- Add institutional and marketOutlook endpoints to frontend trpc client
- Build out Chart Lab, Institutional, Market Outlook pages with real components
- Add focus-visible rings, aria-labels, hex tokens, shape icons across all panels
- Integrate EmotionLogger, MobileTabNav, useKeyboardShortcuts
- Add localStorage persistence for emotion logs
- Normalize spacing, type scale, hover effects across all components
This commit is contained in:
Investor Flow Build
2026-07-05 08:50:12 -04:00
parent 1e818c0881
commit 55f07e6b42
27 changed files with 2331 additions and 555 deletions
+50 -36
View File
@@ -13,9 +13,14 @@ export function SymbolHeader() {
const setActive = useActiveSymbol((s) => s.setActive);
const [draft, setDraft] = useState(symbol);
const [showSearch, setShowSearch] = useState(false);
const [snap, setSnap] = useState<ReturnType<typeof api.market.snapshot> | null>(null);
// Fetch current price for display
const { data: snap } = api.market.snapshot.useQuery(draft || symbol);
useEffect(() => {
const sym = draft || symbol;
api.market.snapshot(sym).then(setSnap).catch(() => {});
}, [draft, symbol]);
const quote = snap?.quote ?? null;
const sector = snap?.sector ?? null;
@@ -55,49 +60,58 @@ export function SymbolHeader() {
return (
<header className="sticky top-0 z-50 border-b border-[#2a2b3a] bg-[#121318]/95 backdrop-blur-sm">
<div className="flex items-center gap-4 px-4 py-2.5">
{/* Symbol Info */}
<div className="flex items-center gap-3 flex-shrink-0">
<h1 className="text-base font-bold text-[#4f8cff]">Investor Flow</h1>
<div className="hidden sm:flex items-baseline gap-2">
<span className="text-lg font-bold text-[#e6e7ec]">{symbol}</span>
{priceLabel && <span className="text-sm font-mono text-[#e6e7ec]">{priceLabel}</span>}
<div className="flex items-center justify-between px-4 py-3">
{/* Left: Logo mark */}
<div className="flex items-center gap-2 flex-shrink-0">
<div className="w-7 h-7 rounded-lg bg-[#4f8cff] flex items-center justify-center">
<span className="text-xs font-bold text-[#121318]">IF</span>
</div>
<h1 className="hidden sm:block text-sm font-semibold text-[#e6e7ec] tracking-tight">
Investor Flow
</h1>
</div>
{/* Right: Ticker info + Search grouped together */}
<div className="flex items-center gap-4">
{/* Active symbol — compact, next to search */}
<div className="hidden sm:flex items-baseline gap-2 text-xs">
<span className="font-bold text-[#e6e7ec]">{symbol}</span>
{priceLabel && (
<span className="font-mono text-[#8a8b9a]">{priceLabel}</span>
)}
{changeLabel && (
<span className={`text-xs font-mono ${up ? "text-[#34d399]" : "text-[#fbbf24]"}`}>
<span className={`font-mono ${up ? "text-[#34d399]" : "text-[#fbbf24]"}`}>
{changeLabel}
</span>
)}
</div>
</div>
{/* Search Input */}
<form onSubmit={handleSubmit} className="flex-1 max-w-xs">
<div className={`relative transition-all duration-200 ${showSearch ? "w-full" : ""}`}>
<input
value={draft}
onChange={(e) => setDraft(e.target.value)}
onFocus={() => setShowSearch(true)}
onBlur={() => setTimeout(() => setShowSearch(false), 200)}
placeholder="Search symbol (Ctrl+K)"
className="w-full bg-[#1a1b24] border border-[#2a2b3a] rounded-lg px-3 py-1.5 text-sm text-[#e6e7ec] placeholder:text-[#5a5b6a] focus:outline-none focus:border-[#4f8cff]"
/>
<kbd className="absolute right-2 top-1/2 -translate-y-1/2 text-[9px] text-[#5a5b6a] bg-[#0a0b0f] border border-[#2a2b3a] rounded px-1.5">
⌘K
</kbd>
{/* Mobile: just ticker */}
<div className="sm:hidden flex items-center gap-1.5 text-xs">
<span className="font-bold text-[#e6e7ec]">{symbol}</span>
{priceLabel && (
<span className="font-mono text-[#8a8b9a]">{priceLabel}</span>
)}
</div>
</form>
{/* Sector info */}
{sectorLabel && (
<span className="hidden md:block text-xs text-[#5a5b6a]">{sectorLabel}</span>
)}
{/* Mobile symbol display */}
<div className="sm:hidden flex items-center gap-2">
<span className="text-sm font-bold text-[#e6e7ec]">{symbol}</span>
{priceLabel && (
<span className="text-xs font-mono text-[#e6e7ec]">{priceLabel}</span>
)}
{/* Search */}
<form onSubmit={handleSubmit} className="flex items-center">
<label className="sr-only" htmlFor="symbol-search">Search symbol</label>
<div className={`relative transition-all duration-200 ${showSearch ? "w-56" : "w-40"}`}>
<input
id="symbol-search"
value={draft}
onChange={(e) => setDraft(e.target.value)}
onFocus={() => setShowSearch(true)}
onBlur={() => setTimeout(() => setShowSearch(false), 200)}
placeholder="Search…"
className="w-full bg-[#1a1b24] border border-[#2a2b3a] rounded-lg px-3 py-1.5 text-xs text-[#e6e7ec] placeholder:text-[#5a5b6a] focus:outline-none focus:border-[#4f8cff] focus:ring-1 focus:ring-[#4f8cff]"
/>
<kbd className="absolute right-2 top-1/2 -translate-y-1/2 text-[9px] text-[#5a5b6a] bg-[#0a0b0f] border border-[#2a2b3a] rounded px-1.5">
⌘K
</kbd>
</div>
</form>
</div>
</div>
</header>