# Web Redesign β€” Systematic Audit & Fix Plan Scope: everything introduced by commits `f62c3b1` (UI shell + 6 endpoints) and `938ed86` (live data wiring + strategy builder + screener). Reviewed code, design, UX, backend integration, mobile parity, security, and tests. Legend: πŸ”΄ critical (broken / data loss / security) Β· 🟠 high (clearly wrong but not catastrophic) Β· 🟑 medium (UX or polish) Β· 🟒 low (nice-to-have). Status: ⬜ open Β· 🟦 in PR Β· βœ… fixed (commit hash on the right). --- ## A. Critical β€” Broken integrations (will 404 / 401 / corrupt data) | # | Issue | Severity | Status | Fix commit | | --- | --------------------------------------------------------------------------------------------------------------------------------------- | :------: | :----: | ---------- | | A1 | `CodeStrategyEditor` POSTs to `/api/backtest`. Real endpoint is `/api/backtest/run`. Result: every "Run Backtest" returns 404. | πŸ”΄ | βœ… | bucket A | | A2 | `CodeStrategyEditor` payload sends `{symbol, strategyCode, mode}`. Backend `/api/backtest/run` requires `{symbols[], strategyConfig}`. | πŸ”΄ | βœ… | bucket A | | A3 | `VisualRuleBuilder` save β†’ `/api/profiles` body uses `{strategyType, visualRules, description}`. `saveTradeProfileForUser` expects `strategy_config` shape. Result: 400 or silently-discarded fields. | πŸ”΄ | βœ… | bucket A | | A4 | `RightPanel.NewsFeed` calls `fetch()` with no `Authorization` header. `/api/news` is `requireAuth`. Result: 401 every render. | πŸ”΄ | βœ… | bucket A | | A5 | `RightPanel.NewsFeed` reads `import.meta.env.VITE_TRADING_API_URL` directly instead of `tradingRuntime.tradingApiUrl`. Breaks in prod where the runtime resolver is the source of truth. | 🟠 | βœ… | bucket A | | A6 | Backend `/api/chart/bars` previously crashed on crypto symbols (`BTC/USD`) because `/v2/stocks` rejects them. Verified in 938ed86: `encodeURIComponent('BTC/USD')` β†’ `BTC%2FUSD` (correct for query string), and the response lookup `cryptoBars[symbol]` uses the un-encoded key (matches Alpaca's response). | 🟠 | βœ… | 938ed86 | ## B. Functional gaps (feature exists in plan but not implemented) | # | Issue | Severity | Status | Fix commit | | --- | ---------------------------------------------------------------------------------------------------------------------------------- | :------: | :----: | ---------- | | B1 | Phase 2 plan called for RSI / MACD / Bollinger Band overlay toggles below `StockChart`. Not built. | 🟠 | βœ… | c54b9f2 | | B2 | `TickerHeader` "β˜… Watchlist" and πŸ”” buttons have no `onClick` β€” purely cosmetic. | 🟠 | βœ… | ed8175e | | B3 | `TickerHeader` company name is the symbol (placeholder). Should pull from `fetchResearchProfile`. | 🟠 | βœ… | ed8175e | | B4 | `TickerHeader` "as of" timestamp uses `new Date()` (always = now). Should use the latest bar timestamp. | 🟑 | ⬜ | | | B5 | `QuickStats` reads RSI/EMA only from `botState.symbols[symbol].indicators`. The bot only computes these for symbols it actively tracks β†’ user-searched tickers always show `β€”`. Need an indicator service or compute from bars client-side. | 🟠 | ⬜ | | | B6 | `CodeStrategyEditor` "Save" button writes to `localStorage` only, then shows "Saved!". Misleading β€” no persistence. | 🟠 | βœ… | da79682 | | B7 | `CodeStrategyEditor` defines `editorRef` but never uses it. | 🟒 | ⬜ | | | B8 | `VisualRuleBuilder` exposes `onBacktest` prop but `ResearchView` doesn't pass one β€” backtest button never appears. | 🟑 | ⬜ | | | B9 | `ResearchView` "Strategies" tab still shows the legacy `MyStrategiesTab`; visual/code-saved strategies don't appear there. | 🟑 | ⬜ | | | B10 | No 404 / catch-all React Router route β€” invalid paths render blank. | 🟑 | ⬜ | | ## C. Security & correctness | # | Issue | Severity | Status | Fix commit | | --- | ---------------------------------------------------------------------------------------------------------------------------------- | :------: | :----: | ---------- | | C1 | Backend posts arbitrary user JS (`strategyCode`) to `/api/backtest` if A1+A2 are "fixed" naively. Must sandbox or refuse. | πŸ”΄ | βœ… | 6aa001a | | C2 | No FMP response cache. Free tier = 250 req/day. Every Home view load = 3 req. 80 page loads/day β†’ quota burnt by lunch. | 🟠 | βœ… | 0828007 | | C3 | `/api/screener` passes `sector` query through to FMP without an allow-list. Low-impact injection, but should validate. | 🟑 | βœ… | c173aeb | | C4 | `/api/news` passes `symbols` through to Alpaca without validation. | 🟑 | βœ… | 7c4b08c | | C5 | Header `fetchMarketIndices` polls every 60 s even when the tab is hidden. Should pause via `document.visibilityState`. | 🟑 | βœ… | e089832 | | C6 | `backend/.env.example` keeps `FMP_API_KEY=demo` AND `apiServer.ts` falls back to `'demo'`. Two sources of truth. Demo key is shared globally and rate-limited. | 🟑 | βœ… | 1377bf2 | | C7 | FMP `apikey` is sent as a query string β†’ leaks into proxy / CDN logs. FMP doesn't support headers, so the only mitigation is server-side caching (see C2). | 🟑 | βœ… | e2e189e | ## D. UX / UI polish | # | Issue | Severity | Status | Fix commit | | --- | ---------------------------------------------------------------------------------------------------------------------------------- | :------: | :----: | ---------- | | D1 | News, ResearchCards, Screener show plain "Loading…" text. Should be skeleton placeholders to prevent layout jank. | 🟑 | βœ… | 0fb1d63 | | D2 | Light theme broke contrast in legacy tabs (`SettingsTab`, `ConfigTab`, etc.) which were styled for dark. | 🟑 | ⬜ | | | D3 | `TickerHeader` exchange is hard-coded "NASDAQ". | 🟒 | ⬜ | | | D4 | `ScreenerView` "More sectors…" `