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:
@@ -1,9 +1,24 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SidebarNav } from "./SidebarNav";
|
||||
import { WatchlistSidebar } from "./WatchlistSidebar";
|
||||
import { SymbolHeader } from "./SymbolHeader";
|
||||
import { MobileTabNav } from "./MobileTabNav";
|
||||
import { CollapsibleProvider, useCollapsibleContext } from "@/lib/collapsible-context";
|
||||
import { useKeyboardShortcuts } from "@/lib/useKeyboardShortcuts";
|
||||
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
const TABLET_BREAKPOINT = 1024;
|
||||
|
||||
function useViewport() {
|
||||
const [width, setWidth] = useState(typeof window !== "undefined" ? window.innerWidth : 1200);
|
||||
useEffect(() => {
|
||||
const handler = () => setWidth(window.innerWidth);
|
||||
window.addEventListener("resize", handler);
|
||||
return () => window.removeEventListener("resize", handler);
|
||||
}, []);
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner component that attaches the global Escape listener.
|
||||
@@ -11,6 +26,21 @@ import { CollapsibleProvider, useCollapsibleContext } from "@/lib/collapsible-co
|
||||
*/
|
||||
function LayoutShellInner({ children }: { children: React.ReactNode }) {
|
||||
const { openSectionId, setOpenSectionId } = useCollapsibleContext();
|
||||
const viewportWidth = useViewport();
|
||||
const isMobile = viewportWidth < MOBILE_BREAKPOINT;
|
||||
const isTablet = viewportWidth >= MOBILE_BREAKPOINT && viewportWidth < TABLET_BREAKPOINT;
|
||||
|
||||
// Keyboard shortcuts: Escape closes open sections, modals
|
||||
useKeyboardShortcuts([
|
||||
{
|
||||
key: "Escape",
|
||||
handler: () => {
|
||||
if (openSectionId) {
|
||||
setOpenSectionId(null);
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Global Escape listener: collapse the currently open section.
|
||||
useEffect(() => {
|
||||
@@ -31,21 +61,56 @@ function LayoutShellInner({ children }: { children: React.ReactNode }) {
|
||||
{/* Symbol Header — always visible */}
|
||||
<SymbolHeader />
|
||||
|
||||
{/* Main Content Area */}
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
{/* Left Sidebar Navigation — hidden on tablet/mobile */}
|
||||
<SidebarNav />
|
||||
|
||||
{/* Watchlist Sidebar — hidden on mobile */}
|
||||
<WatchlistSidebar />
|
||||
|
||||
{/* Content */}
|
||||
<main className="flex-1 overflow-y-auto p-4 sm:p-6">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
{children}
|
||||
{/* Main Content Area — responsive layout */}
|
||||
{isMobile ? (
|
||||
/* Mobile: single column, no sidebar */
|
||||
<>
|
||||
<main className="flex-1 overflow-y-auto p-4 pb-20">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
<MobileTabNav />
|
||||
</>
|
||||
) : isTablet ? (
|
||||
/* Tablet: 2-column layout, watchlist accessible via compact bar */
|
||||
<div className="flex flex-1 overflow-hidden flex-col">
|
||||
{/* Compact horizontal watchlist bar */}
|
||||
<div className="hidden md:block border-b border-[#2a2b3a] bg-[#121318] px-4 py-2 overflow-x-auto">
|
||||
<WatchlistSidebar compact />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
{/* Center content */}
|
||||
<main className="flex-1 overflow-y-auto p-4 sm:p-6">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
{/* Right analytics — drops below on tablet */}
|
||||
<aside className="hidden lg:block w-80 border-l border-[#2a2b3a] bg-[#121318] overflow-y-auto">
|
||||
<div className="p-4 space-y-4">
|
||||
{/* Right column content placeholder */}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* Desktop: 3-column layout */
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
{/* Left Sidebar Navigation */}
|
||||
<SidebarNav />
|
||||
|
||||
{/* Watchlist Sidebar */}
|
||||
<WatchlistSidebar />
|
||||
|
||||
{/* Content */}
|
||||
<main className="flex-1 overflow-y-auto p-4 sm:p-6">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -54,7 +119,7 @@ function LayoutShellInner({ children }: { children: React.ReactNode }) {
|
||||
* Main layout shell for the redesigned terminal.
|
||||
* - Desktop (>1024px): Sidebar + Watchlist + Header + Content (3-column)
|
||||
* - Tablet (768-1024px): Watchlist + Header + Content (2-column, sidebar hidden)
|
||||
* - Mobile (<768px): Header + Content (single column)
|
||||
* - Mobile (<768px): Header + Content (single column) + MobileTabNav
|
||||
*/
|
||||
export function LayoutShell({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user