feat(web): Google-Finance-style web dashboard for Azoth VN data - #91
Merged
Conversation
Add `web/` — a self-contained React + Vite dashboard that clones Google Finance's UI/UX over Azoth's Vietnam-market data (HOSE/HNX/UPCOM), backed by a dependency-light Node http API server that reuses the existing `src/data/**` clients (DNSE / SSI / VNDirect / CafeF). Frontend (React 18 + TS, lightweight-charts, react-router): - Home: market-indices strip with sparklines, VN30 movers (gainers/losers/ active), market-news feed, Lists watchlist sidebar, Research panel. - Quote: price header, interactive chart (Area/Candles, ranges, SMA/Bollinger, prev-close line, Compare multi-ticker % overlay), and Overview/Financials/News tabs. Financials tab shows annual key metrics (EPS/BVPS/ROE/ROA/margins/P/E) from CafeF as a chart + table. - Ticker search, light/dark themes, responsive 3-column layout. Server (web/server/index.ts): JSON API under /api/* (indices, movers, watchlist, market-news, search, quote, ohlcv, indicators, news, about, financials), TTL- cached via Azoth's SQLite cache. No secrets — all sources are public. Notes: - SSI ref/ceiling/floor are plain VND; normalized to board (thousand VND) units. - DNSE index symbols are HNX/UPCOM (not HNXINDEX/UPCOMINDEX). - Intraday chart timestamps shifted +7h to show ICT exchange time. - Scope is data/visualization only; the Research (AI) panel is a preview and live broker/portfolio is intentionally not wired. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the dashboard with the remaining Google Finance features, all backed by Azoth's Vietnam-market data: - Persistent user watchlists: multiple lists, create/rename/delete, add/remove symbols. New web_* SQLite tables (web/server/store.ts) + method-aware REST endpoints (watchlists/portfolios/holdings). - Manual Portfolio page: holdings (shares + cost basis) with live gain/loss, day change, and weights. Google-Finance style, NOT broker-linked. - Market Trends pages: active / gainers / losers / indexes with a Liquid vs VN30 universe toggle. - "Add to list" popover on the quote header (follow/save). - "You may be interested in" discover strip on Home. - Shared client state via UserDataProvider (useWatchlists / usePortfolios) and the new routes. Persistence uses its own web_* tables in the shared Azoth SQLite DB and does not touch Azoth's managed schema.sql. Frontend typecheck is clean; all endpoints verified against live VN data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Match Google Finance's actual composition and visual weight:
Home
- Default to light theme (GF's default), FOUC-safe via an inline
pre-hydration script; toggle still honored/persisted.
- Compact index cards (dense name/value/spark) replacing the tall
big-price cards; drop the dead Vietnam/Global toggle.
- Remove the VN30 movers table from home (movers live at /markets);
add a GF-style market-summary news block (source chips + times).
- Add a sector-performance rail to the sidebar ("Stock sectors"),
backed by a new GET /api/sectors (avg constituent change + a
synthetic normalized sector sparkline).
Stock page
- One-line performance summary + a 1M/3M/YTD row under the price,
from new quote.stats.change_pct_1m/_3m/_ytd (computed from bars).
- Chart toolbar becomes GF-style dropdowns (Chart type / Indicators /
Compare); range row unchanged; all chart behavior preserved.
- Denser multi-column key-stats grid.
Also: light/typography tuning in the design tokens, and remove the
now-orphaned MoversTable component. Frontend typecheck clean;
/api/sectors and perf fields verified against live VN data.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
web/— a self-contained Google-Finance-style web dashboard for Azoth's Vietnam-market data (HOSE/HNX/UPCOM). It reuses the existingsrc/data/**clients (DNSE / SSI / VNDirect / CafeF) through a thin NodehttpAPI server and renders a React + Vite frontend that clones Google Finance's information architecture and visual language (light + dark).lightweight-charts,react-router):web/server/index.ts) — JSON API under/api/*(indices, movers, watchlist, market-news, search, quote, ohlcv, indicators, news, about, financials), TTL-cached via Azoth's SQLite cache. No secrets — all sources are public.Reviewer notes
web/; nosrc/**files changed.web/is a private sub-project (not part of the published@toreleon/azothpackage) and not a pnpm-workspace member. Run withcd web && pnpm install && pnpm dev(requires rootpnpm installfirst, since the server imports../src).HNX/UPCOM(notHNXINDEX/UPCOMINDEX); intraday chart timestamps shifted +7h to show ICT exchange time; CafeF's ratio feed is annual-only (presented as annual, not a fake quarterly toggle).Validation
pnpm typecheck— root clean;web/has its owntsc --noEmit, also cleanpnpm test— 87 passed, 4 skipped (unaffected; change is isolated toweb/)pnpm build— succeedsweb/is private and not published)🤖 Generated with Claude Code