Skip to content

feat(ui): redesign dashboard, model market and sharing views (PR 4/6) - #155

Merged
argszero merged 1 commit into
mainfrom
feat/ui-dashboard-market-share
Sep 11, 2026
Merged

argszero merged 1 commit into
mainfrom
feat/ui-dashboard-market-share

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Adapts the dashboard, model market and sharing views to the new prototype — PR 4 of 6 of the full console UI redesign requested in rant 2026-09-11T16:23:43 ("按新原型完整适配前端 UI(全站视觉重设计 + 8 视图结构对齐)", section 六 "建议实施顺序").

Pure front-end change: no backend API contract is altered, no build tooling is introduced (still plain HTML/CSS/JS), and the existing behaviour conventions are preserved (zero-mock in signed-in state — never fall back to D. static data; inline expansion over modals; theme remembered in localStorage; data-i18n coverage).

The prototype is the single design source and is already committed in this repo as a baseline at docs/prototype/aitokenpool-console.html (landed in PR #152).

Related Issue

Refers to the host rant of 2026-09-11T16:23:43.625143+08:00 (project aitokenpool). No GitHub issue exists for this work — the repo currently has no open issues.

Changes

Dashboard (rant §3)

  • page-head gains the .crumb breadcrumb + a right-aligned "call a model in the market" action (data-goto).
  • New 14-day consumption-vs-earnings two-colour bar chart (.trend, consumption = accent / sharing earnings = ok, .legend included, bars normalised to that day's max, 2% minimum height).
  • "My sharings" moved into its own full-width card with a card-sub description and a "manage sharing" ghost button.
  • "Month point changes" gains its card-sub and keeps the existing mini-list/sparkline.

Model market (rant §4)

  • Toolbar: new availability filter (all / available only) and the count pushed right via .grow.
  • Provider + model columns merged into one .provider-cell (status dot + small-caps provider + .model-name).
  • New capabilities column rendering only fields the backend actually returns: a Vision tag when vision is set, a tag-accent peak-pricing tag when peak rates exist.
  • Availability pill is now three-state and carries the real key count: >=2 → "available · N keys" (pill-ok), ==1 → "tight" (pill-warn), 0 → "no key" (pill-muted), with the "use" button disabled when there is no key.
  • Existing dynamic provider options and the recent-models chips are untouched.

Sharing (rant §5)

  • "List a new key" promoted into page-head as a primary action; the form is now a sibling card instead of a grid cell.
  • New availability time-window column fed by the real available_days / available_start / available_end fields.
  • Used / quota now renders a progress bar alongside the numbers (.bar-track / .bar-fill, alt variant at 100%).

Shared

Correctness fix worth calling out

The dashboard chart reuses /api/transactions/trend rather than the 7-day series from /api/dashboard, so the dashboard and the transactions page cannot disagree on the same numbers. That endpoint aggregates with GROUP BY, so days with no transactions are returned as absent rows, not as zero rows. Rendering that raw would shift the bars left and mislabel the axis, so dashTrendDays() now densifies the window to a fixed DASH_TREND_DAYS (14) with 0-height columns for empty days, and the request start is anchored to UTC midnight so it lines up with the backend's UTC day buckets (a now - 13d start truncated the first bucket once the clock passed midnight).

i18n

Every new label ships with zh + en entries: dash.crumb, dash.gotoMarket, dash.trend*, dash.month.sub, dash.sharings.sub, dash.manageSharing, mk.crumb, mk.avail.*, mk.col.providerModel, mk.col.caps, mk.cap.*, share.crumb, share.col.avail. Entries orphaned by the column changes (mk.col.provider, mk.col.model, mk.avail, mk.busy, share.col.time) were removed from both languages. ZH/EN dictionaries remain in exact parity and every data-i18n attribute and T() call resolves.

No config or data-structure change is involved, so no example file needed updating.

Tests

  • cargo test passes — 148 passed; 0 failed (14.62s)
  • cargo fmt --check passes
  • cargo clippy --all-targets -- -D warnings passes
  • node --check passes on all four JS files (api.js, data.js, i18n.js, app.js)
  • No new Rust unit tests: this PR is front-end only and touches no Rust code. I18n coverage was instead verified by script (ZH/EN key sets compared for exact parity; every data-i18n value and every T() key in app.js checked to resolve). Visual verification was done by loading the real ui/ in headless Chrome against stubbed API responses and rendering all three views in both light and dark themes, confirming real data (14 trend columns, 4 stat cards, 4 marketplace rows with 4 availability pills, 3 sharing rows with the availability column) rather than empty fallback states.

Checklist

  • Branch naming follows the convention (feat/)
  • Commit message uses Conventional Commits (feat(ui): …)
  • Single responsibility — scoped to the three views named for this batch; the deliberately deferred cleanups (e.g. the now-stale .search-box reference in ui/README.md) were left out to keep the diff reviewable

…component layer

Adapt the dashboard, model-market and sharing views to the new prototype
(rant 2026-09-11T16:23:43, PR 4 of 6). Pure front-end; no API contract change.

Dashboard: add the page-head breadcrumb + "call a model" action, move the
sharings mini-list into its own card with a "manage sharing" ghost button, and
add the new 14-day consumption-vs-earnings two-colour bar chart fed by the real
/api/transactions/trend endpoint (same income/expense semantics as the
transactions view, which was reused instead of the 7-day /api/dashboard series
so both pages agree).

The backend aggregates with GROUP BY, so days without transactions come back as
missing rows, not zero rows. A fixed 14-column chart would then shift bars left
and mislabel the axis, so the renderer now densifies the window to DASH_TREND_DAYS
(empty days render as 0-height columns) and the request start is anchored to UTC
midnight to line up with the backend's UTC day buckets.

Marketplace: toolbar gain an availability filter (all / available only) and a
count aligned right; the provider and model columns merge into one .provider-cell
(status dot + provider + model name), a capability column renders only real
backend fields (vision tag; peak-pricing tag for models with peak rates), and the
availability column becomes a three-state pill carrying the real key count
(>=2 available / ==1 tight / 0 no key, with "use" disabled when no key). New
mk.avail.* / mk.col.caps / mk.cap.* keys; the now-unused mk.col.provider,
mk.col.model, mk.avail and mk.busy entries were dropped from both languages.

Sharing: move "list a new key" into the page-head as a primary action with the
form as a sibling card, add the availability time-window column (real
available_days / available_start / available_end), and render used/quota as a
progress bar next to the numbers. share.col.time is dropped in favour of
share.col.avail in both languages.

Also define .mt16/.mb16, which the new layout uses but the stylesheet never
declared.

i18n stays complete: ZH/EN dictionaries are in exact parity and every data-i18n
attribute plus every T() call in app.js resolves.
@argszero
argszero merged commit 7e094e0 into main Sep 11, 2026
1 check passed
@argszero
argszero deleted the feat/ui-dashboard-market-share branch September 11, 2026 09:56
argszero added a commit that referenced this pull request Sep 11, 2026
发布 v0.7.21(rant 2026-09-11T21:03:06)。

- Cargo.toml / Cargo.lock: 0.7.20 → 0.7.21
- ui/index.html: cache-bust ?v=20260911-2 → ?v=20260911-3(5 处资源引用)
- CHANGELOG.md: 新增 v0.7.21 条目,概括 6 片全站 UI 重设计
  (#152 OKLCH 设计 token 层 / #153 登录页左右分栏 + 侧边栏 / #154 共享组件层 /
  #155 仪表盘·市场·共享 / #156 钱包·交易 / #157 设置·管理·运营)

本次为纯前端改动,后端契约未变;部署结构不变(数据库仍留 NAS,不启用 WAL)。

Gates: cargo test 148 passed / cargo fmt --check clean / cargo clippy clean /
node --check x4 / i18n ZH-EN 762=762 exact parity.

Co-authored-by: argszero <argszero@argszerodeMac-mini.local>
argszero added a commit that referenced this pull request Sep 13, 2026
…e's x axis is time (#218)

`GET /api/dashboard`'s `series` was a sparse day-bucket list: `GROUP BY
date(time)` emits a row only for days that actually have transactions, and
`ui/js/app.js::renderMonthChanges` feeds that list straight into
`sparkline()`, which places point i at an INDEX-based x
(`pad + i*(w - 2*pad)/(len - 1)`) rather than at its date. Days without
transactions were therefore collapsed instead of being shown as zero:

  * a week with a single active day degenerated to a lone `M` command, i.e.
    no line at all, while the number above it still rendered;
  * a week with two active days was drawn as one straight line across the
    full 7-day width, claiming a trend the data does not support.

Every sibling time series in this tree is zero-filled to its own window and
says why: `routes/ops.rs::runtime` fills hours 0..23 ('若不补零前端柱状图会
整体左移'), `app.js::dashTrendDays` and `app.js::txTrendDays` fill their day
buckets, and the guest branch of this very function always hands over 7
points. This was the only consumer that did not, so treat it as the omission
it is: `f525242` (#79) wired the sparse series up and the siblings were fixed
afterwards (#155/#156 and the ops PR6).

Fix: build the window with a recursive CTE (today plus the previous 6 days,
UTC) and a LEFT JOIN whose ON clause carries the user filter. The filter has
to stay in the JOIN condition: in WHERE it would drop exactly the empty days
this change exists to add. Only `src/routes/wallet.rs` changes; the window
semantics (a 7-day day-key window anchored on date('now', '-6 days'), UTC)
and the `month` / `net` windows of C2049 are untouched.

Evidence, all driven through the real router:
- `dashboard_series_is_a_zero_filled_seven_day_window`: 7 rows, ascending,
  the date set compared against chrono (`Utc::now()`) as an independent
  authority rather than derived from the CTE.
- `dashboard_series_zero_fills_inside_the_user_and_the_window`: per-day
  values equal a plain per-day SUM for the same user; another user's row
  inside the window and this user's row outside it never appear; a positive
  control re-checks a zero-filled day after a later transaction lands on it.
- `wallet_summary_and_dashboard`'s `!series.is_empty()` (trivially true once
  the window is filled) is rewritten into the shape assertion it meant.
- A/B, each mutation applied alone and reverted with the file's md5 restored:
  the pre-change sparse query reddens 3 tests (including both new ones); an
  off-by-one window (`-5 days`) reddens 2, a different set; carrying the user
  filter in WHERE reddens those same 3; comparing a datetime column against a
  date key reddens exactly 1 (the value test). Unmutated tree: 222 passed,
  0 failed.
- jsdom end-to-end (real `ui/index.html` plus the four real scripts, only
  `fetch` stubbed), fed the exact series the patched handler returns: 7
  points / 6 line segments / 7 tooltips, against 1 point / 0 segments for the
  pre-change shape. The 14-day bar chart on the same page keeps rendering 14
  columns in every leg.

Display / API shape only: no ledger, balance or settlement change. No `ui/`
or i18n key touched, so the i18n gate counts are unchanged.
`cargo fmt --check` and `cargo clippy --all-targets -- -D warnings` are clean;
`cargo test` goes from 220 to 222.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant