TellWang
Dashboard

A stocks dashboard from one prompt

The demo panel on our homepage shows Wang being told "Build a stocks dashboard with live prices and a watchlist." That panel isn't an artist's impression of a session we wish had happened — the app exists, we run it at stocks.tellwang.com, and this post walks through what one prompt actually produced: a watchlist with live prices and day change, an add-ticker box, and updates that land in open tabs without a reload.

The prompt

The whole specification fit in one chat message:

prompt
Build a stocks dashboard on a new Wok called stocks. Schema: a
tickers table (symbol PK, name), a prices table (symbol PK →
tickers, price, prev_close, change_pct, currency, updated_at),
and a trades log. Seed AAPL/MSFT/NVDA/GOOGL/AMZN/TSLA. RLS: anon
reads all; anon inserts tickers + trades; only the service role
writes prices. Deploy an edge function refresh-quotes that reads
tickers, fetches each symbol's quote, computes change%, and
upserts prices. Add prices to realtime. Deploy a polished dark
frontend (watchlist table, live price + green/red change%,
add-ticker box, auto-refresh) wired to the Wok with the anon key.
Map it to stocks.tellwang.com, then browser-check it and send me
the link.

That message is the entire handoff. Everything below is what came back.

The schema, and who gets to write what

Three tables: tickers, prices, trades. The interesting part is the write boundary. The prompt states the policy in one sentence, and the agent turned it into actual row-level security: anyone can read the watchlist, a visitor can add a ticker or log a trade, but prices accepts writes only from the service role — so the quote feed is the single source of price data and a visitor can't insert a fake one. That rule lives in the database, not in frontend code that a curious user can bypass.

Live prices with nothing extra to operate

Quotes come from refresh-quotes, an edge function deployed next to the data. It reads the tickers table, fetches each symbol's quote from a public market-data API, computes the day change against the previous close, and upserts prices. The outbound call happens inside the function, which is the pattern worth stealing: the market-data credential never reaches the browser, and the database doesn't need an HTTP extension to go fetch things itself.

Because prices is wired into realtime, an update lands in every open tab over a websocket. When the socket can't connect, the page falls back to a 30-second poll — slower, still correct.

Keeping it fresh when nobody's watching

A dashboard with no visitors has nothing triggering a refresh. The fix is a scheduled job that POSTs to the function every few minutes — the scheduler is part of the same Wok, so this is one more ask in the same chat, not a cron box to maintain.

Shipped means opened in a real browser

Before handing back the link, the agent opens the deployed URL in an actual browser and reads the console and network log. A page that returns 200 and renders blank doesn't pass; neither does one whose websocket dies quietly. That check is the last step in the homepage demo for a reason — it's the difference between "deployed" and "works".

Build your own

The same flow runs from the dashboard chat or from your editor. If your code lives in Claude Code or Cursor, Deploy a Claude Code project shows the MCP setup; if you're starting from a description, the quickstart is the shortest path to a live URL.