Asbir Stats is a full-screen dashboard for displaying live numbers from any JSON API — built for TV/kiosk displays. Each dashboard polls its own endpoint on a schedule, caches the result on the server, and lays the stats out in a grid that fills the screen with no scrollbars.
Every login uses a Dashboard Code and a Password, both verified on the server.
From Settings (admin) or a dashboard's own kebab menu, you can configure:
Your endpoint should return JSON in one of these shapes:
1. An array of stat objects
[
{
"label": "Daily Active Users",
"value": 8412,
"change": 18.4,
"changeLabel": "vs yesterday"
},
{
"label": "New Sign-ups",
"value": 964,
"change": -4.1,
"changeLabel": "vs yesterday",
"trend": [1080, 1050, 1020, 1005, 990, 980, 970, 964],
"color": "#f59e0b"
}
]2. A wrapper object with a stats/data/metrics array
{
"stats": [
{
"label": "Revenue",
"value": 45230,
"change": 12.5,
"unit": "$",
"changeLabel": "vs last month"
}
]
}This shape can also include a sibling meta.title, which overrides the dashboard's configured title for as long as this response is showing (falls back to the dashboard's own title if omitted, or if using the array or flat-object shapes above/below, which have no room for a sibling meta):
{
"meta": { "title": "Q3 Sales" },
"stats": [
{ "label": "Revenue", "value": 45230, "change": 12.5, "unit": "$" }
]
}3. A flat object of label → number pairs
{ "Revenue": 45230, "Users": 8412 }(Flat objects have no change value, so no arrow is shown for them.)
label (string, required)value (number or string, required) — aliases: current, count, totalchange (number, optional) — percentage vs. the previous period, shown with a green ↑ or red ↓. Aliases: delta, percentChange, diff. Omit it entirely for a stat that shouldn't show an arrow.changeLabel (string, optional) — a label shown beside the change value, e.g. "vs yesterday". Only used when change is present. Aliases: compareLabel, vsLabel.unit (string, optional) — e.g. $ or %. Everything except % is prefixed; % is suffixed.meta (string, optional) — a secondary line shown under the value, used only when change is omitted.trend (number[], optional) — a short history (oldest first) rendered as a soft area-chart background behind the card. Aliases: history, series. Omitted entirely if there's no history to show.color (string, optional) — accent color (any CSS color) for that card's trend background. If omitted, cards cycle through a fixed palette by position.If value is a string instead of a number, it renders as a highlighted name rather than a big number with an arrow — useful for a "top title this week" style stat:
{ "label": "Top Title", "value": "The Last Voyage", "meta": "2,481 plays today" }This app uses a single shared admin password plus per-dashboard passwords, intended as simple access control for an internal/trusted display — not a hardened multi-tenant auth system. The admin password also works as a master password for logging into any individual dashboard directly.
Dashboard records (including passwords, in plaintext) are stored server-side — in a Vercel Blob store in production, or a local JSON file in development — rather than in a database with proper credential hashing.