Skip to content

feat: add REST dashboard web shell - #3

Merged
ioncache merged 3 commits into
mainfrom
shotlab-web-dashboard-rest-shell
Jun 29, 2026
Merged

ioncache merged 3 commits into
mainfrom
shotlab-web-dashboard-rest-shell

Conversation

@ioncache

@ioncache ioncache commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Overview

Add the first REST-only ShotLab web dashboard shell so the machine state and shot history can be explored in the browser before socket work and brew controls.

Details

  • scaffold @shotlab/web with Vite, React, TypeScript, MUI, and @mui/x-charts
  • add direct browser-to-machine REST loading for machine, settings, last profile, and history
  • render live info cards, disabled safe-action placeholders, a shared shot chart surface, and a history browser
  • normalize observed history payloads into chart series for pressure, flow, gravimetric flow, and weight
  • move weight onto a secondary chart axis so the primary axis stays readable for the flow-range metrics
  • update @shotlab/meticulous-client history typing to match the verified payload shape

Related Tickets and/or Pull Requests

  • Relates to the roadmap step for the first web dashboard shell

Checklist

  • Tests added or updated
  • README and TSDoc updated if the public API changed
  • Breaking changes called out (if any)
  • Roadmap item checked off if this PR completes one

Summary by CodeRabbit

  • New Features
    • Introduced a new Web Dashboard experience with live status cards, interactive charts, selected shot details, and history browsing.
    • Added web app metadata (manifest) and a service worker for improved offline/launch behavior.
    • Added shared theme and a React/MUI app entry setup.
  • Bug Fixes
    • Improved progressive loading and clearer error/warning display when dashboard data can’t be retrieved.
    • Added stronger handling for missing/partial machine, profile, and history data to avoid blank/incorrect UI.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ioncache, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bad07aeb-73f5-49f4-b2a5-3604ec875344

📥 Commits

Reviewing files that changed from the base of the PR and between f15b05d and 4a8458a.

📒 Files selected for processing (1)
  • packages/meticulous-client/src/index.ts
📝 Walkthrough

Walkthrough

This PR adds the apps/web ShotLab dashboard workspace, including app scaffolding, config and client wrappers, dashboard data normalization and chart helpers, the React UI shell, and a PWA manifest/service worker. It also updates the client package’s history types and method return contracts.

Changes

Meticulous Client History Types

Layer / File(s) Summary
Structured history model
packages/meticulous-client/src/index.ts, packages/meticulous-client/src/index.test.ts
Replaces the loose history response alias with structured history interfaces, updates history method return types, and adjusts tests for null current history and data: [] last-history payloads.

Web Dashboard Workspace

Layer / File(s) Summary
Workspace scaffold and PWA shell
apps/web/package.json, apps/web/tsconfig.json, apps/web/vite.config.ts, apps/web/index.html, apps/web/public/manifest.webmanifest, apps/web/public/sw.js
Adds the web workspace package metadata, TypeScript and Vite setup, HTML entrypoint, web manifest, and service worker.
App config and dashboard client
apps/web/src/config.ts, apps/web/src/lib/create-dashboard-client.ts, apps/web/src/lib/create-dashboard-client.test.ts
Validates METICULOUS_BASE_URL and wraps the Meticulous client fetch path to use cache: 'no-store'.
Dashboard types, selectors, and snapshot loader
apps/web/src/lib/dashboard-types.ts, apps/web/src/lib/dashboard-selectors.ts, apps/web/src/lib/dashboard-selectors.test.ts, apps/web/src/lib/load-dashboard-snapshot.ts, apps/web/src/lib/load-dashboard-snapshot.test.ts
Defines dashboard view models, normalizes live and history payloads, composes snapshots, and loads the four dashboard inputs concurrently.
Shot chart utilities
apps/web/src/lib/shot-chart.ts, apps/web/src/lib/shot-chart.test.ts
Adds chart-series generation, point selection, and formatted point-detail helpers for shot data.
MUI dashboard App and entrypoint
apps/web/src/app.tsx, apps/web/src/app.test.tsx, apps/web/src/theme.ts, apps/web/src/main.tsx
Implements the React dashboard shell, selection/loading state, chart and table rendering, theme setup, app bootstrap, and staged render tests.
Implementation plan document
docs/plans/2026-06-28-web-dashboard-rest-shell.md
Adds the staged implementation plan, task breakdown, non-goals, and checklist.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ioncache/shotlab#1: Changes createDashboardClient by depending on the client fetch option shape and request behavior.
  • ioncache/shotlab#2: Expands the Meticulous client REST methods that the web dashboard loads through loadDashboardSnapshot and App.

Poem

🐇 I hopped through code, both neat and bright,
With charts and cards and REST in sight.
A root sprang up, the shots lined clear,
The dashboard wagged its rabbit ear.
No proxy needed, all is live—
ShotLab now helps the roastery thrive!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: a new REST-based web dashboard shell.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch shotlab-web-dashboard-rest-shell

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ioncache ioncache changed the title feat(0): add REST dashboard web shell feat: add REST dashboard web shell Jun 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
packages/meticulous-client/src/index.ts (1)

73-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid widening getCurrentHistory() back to JsonObject.

Line 73 effectively drops the new history typing if HistoryEntry already extends JsonObject. If /history/current is structured, return Promise<HistoryEntry>; otherwise model the alternate payload with its own interface instead of a catch-all supertype.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/meticulous-client/src/index.ts` at line 73, The getCurrentHistory()
contract is being widened back to a catch-all JsonObject, which undoes the new
history typing. Update the method in index.ts to return Promise<HistoryEntry> if
/history/current is always structured, or introduce a separate explicit
interface for any alternate payload instead of using a union with JsonObject.
Keep the change aligned with the existing HistoryEntry type so callers get the
intended specific history shape.
apps/web/src/app.tsx (1)

88-194: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Abort stale machine reads in the cleanup path.

Cleanup only flips isCancelled, so unmounted or reconfigured instances still leave four browser requests running against the machine until they finish. For a direct browser-to-machine dashboard, canceling those reads is a better default than only ignoring their responses.

Proposed change
     let isCancelled = false;
-    const client = createDashboardClient(config.meticulousBaseUrl);
+    const controller = new AbortController();
+    const client = createDashboardClient(
+      config.meticulousBaseUrl,
+      (input, init) => fetch(input, { ...init, signal: controller.signal }),
+    );
@@
     return () => {
       isCancelled = true;
+      controller.abort();
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/app.tsx` around lines 88 - 194, The cleanup in the `useEffect`
inside `App` only sets `isCancelled`, so the `getMachine`, `getSettings`,
`getHistory`, and `getLastProfile` requests keep running after unmount or base
URL changes. Add request cancellation to the `createDashboardClient` flow and
abort those in-flight reads from the effect cleanup, while still keeping the
existing `isCancelled` guards around the state updates.
apps/web/src/lib/create-dashboard-client.ts (1)

1-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Import the client through its package entrypoint.

Reaching into packages/meticulous-client/src/index couples the app to the monorepo layout and bypasses the package boundary this workspace is supposed to consume. Import @shotlab/meticulous-client here instead, and keep apps/web/src/app.tsx on the same boundary.

Proposed change
 import {
   createMeticulousClient,
   type MeticulousClientOptions,
-} from '../../../../packages/meticulous-client/src/index';
+} from '`@shotlab/meticulous-client`';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/lib/create-dashboard-client.ts` around lines 1 - 4, The import
in create-dashboard-client is reaching into the package source path instead of
using the published package boundary. Update the createMeticulousClient and
MeticulousClientOptions import to come from `@shotlab/meticulous-client`, and make
the same boundary-safe import change in apps/web/src/app.tsx so both call sites
consume the package entrypoint consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/app.test.tsx`:
- Line 74: The test is resolving mocks.deferreds.history and then exiting before
the final rerender settles, which can leave pending updates behind. Update the
relevant test in app.test.tsx to wait for the post-resolution UI state using the
empty-history assertion after mocks.deferreds.history.resolve, so the test only
finishes once the final render has completed.

In `@apps/web/src/app.tsx`:
- Around line 61-62: The “Last loaded profile” loading state in the
`getProfileCardValue`/switch handling is tied to both `loading.machine` and
`loading.lastProfile`, which turns off the skeleton too early when
`getMachine()` resolves before `getLastProfile()`. Update the loading condition
to depend on the profile request’s own settle state only, and keep the card in
loading mode until `getLastProfile()` finishes regardless of `loading.machine`.

In `@apps/web/src/lib/dashboard-selectors.ts`:
- Around line 55-56: The pre-heat card in dashboard-selectors is missing support
for the observed heat-on-boot payload key, so machines with that field fall back
to "Unknown". Update the `readBoolean` lookup in the pre-heat selector to
include `heat_on_boot` alongside the existing `preheat` and `pre_heat` keys,
keeping the `value` logic in the same selector so it resolves the actual
pre-heat state when present.

In `@apps/web/src/lib/dashboard-types.ts`:
- Around line 6-13: The `DashboardShotPoint` model currently makes `weight`
non-null, which forces missing shot samples to be fabricated as numbers instead
of being treated as gaps. Update the `DashboardShotPoint` interface to make
`weight` nullable, then adjust the selector/rendering flow in `shot-chart.ts`
and any consumers of `DashboardShotPoint.weight` so they pass through missing
values as null rather than coercing them to 0 or another placeholder.

---

Nitpick comments:
In `@apps/web/src/app.tsx`:
- Around line 88-194: The cleanup in the `useEffect` inside `App` only sets
`isCancelled`, so the `getMachine`, `getSettings`, `getHistory`, and
`getLastProfile` requests keep running after unmount or base URL changes. Add
request cancellation to the `createDashboardClient` flow and abort those
in-flight reads from the effect cleanup, while still keeping the existing
`isCancelled` guards around the state updates.

In `@apps/web/src/lib/create-dashboard-client.ts`:
- Around line 1-4: The import in create-dashboard-client is reaching into the
package source path instead of using the published package boundary. Update the
createMeticulousClient and MeticulousClientOptions import to come from
`@shotlab/meticulous-client`, and make the same boundary-safe import change in
apps/web/src/app.tsx so both call sites consume the package entrypoint
consistently.

In `@packages/meticulous-client/src/index.ts`:
- Line 73: The getCurrentHistory() contract is being widened back to a catch-all
JsonObject, which undoes the new history typing. Update the method in index.ts
to return Promise<HistoryEntry> if /history/current is always structured, or
introduce a separate explicit interface for any alternate payload instead of
using a union with JsonObject. Keep the change aligned with the existing
HistoryEntry type so callers get the intended specific history shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: caa44658-66a6-4ad7-ba4d-eb4ce0e0ac6d

📥 Commits

Reviewing files that changed from the base of the PR and between d649bde and 8254ec2.

⛔ Files ignored due to path filters (2)
  • apps/web/public/icon.svg is excluded by !**/*.svg
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (23)
  • apps/web/index.html
  • apps/web/package.json
  • apps/web/public/manifest.webmanifest
  • apps/web/public/sw.js
  • apps/web/src/app.test.tsx
  • apps/web/src/app.tsx
  • apps/web/src/config.ts
  • apps/web/src/lib/create-dashboard-client.test.ts
  • apps/web/src/lib/create-dashboard-client.ts
  • apps/web/src/lib/dashboard-selectors.test.ts
  • apps/web/src/lib/dashboard-selectors.ts
  • apps/web/src/lib/dashboard-types.ts
  • apps/web/src/lib/load-dashboard-snapshot.test.ts
  • apps/web/src/lib/load-dashboard-snapshot.ts
  • apps/web/src/lib/shot-chart.test.ts
  • apps/web/src/lib/shot-chart.ts
  • apps/web/src/main.tsx
  • apps/web/src/theme.ts
  • apps/web/tsconfig.json
  • apps/web/vite.config.ts
  • docs/plans/2026-06-28-web-dashboard-rest-shell.md
  • packages/meticulous-client/src/index.test.ts
  • packages/meticulous-client/src/index.ts

Comment thread apps/web/src/app.test.tsx
Comment thread apps/web/src/app.tsx Outdated
Comment thread apps/web/src/lib/dashboard-selectors.ts Outdated
Comment thread apps/web/src/lib/dashboard-types.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/meticulous-client/src/index.ts`:
- Around line 150-152: `MeticulousClient.getLastHistory()` is typed as returning
`HistoryEntry`, but the factory still uses
`get<HistoryResponse>('history/last')`, so update the `getLastHistory` mapping
in `MeticulousClient` to deserialize `history/last` as `HistoryEntry` instead of
`HistoryResponse`. Keep the change localized to the `getLastHistory` symbol so
the returned object matches the declared interface.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ecca02b-3fe7-4f49-9cde-97b03910fd87

📥 Commits

Reviewing files that changed from the base of the PR and between 8254ec2 and f15b05d.

📒 Files selected for processing (10)
  • apps/web/src/app.test.tsx
  • apps/web/src/app.tsx
  • apps/web/src/lib/create-dashboard-client.ts
  • apps/web/src/lib/dashboard-selectors.test.ts
  • apps/web/src/lib/dashboard-selectors.ts
  • apps/web/src/lib/dashboard-types.ts
  • apps/web/src/lib/load-dashboard-snapshot.ts
  • apps/web/tsconfig.json
  • packages/meticulous-client/src/index.test.ts
  • packages/meticulous-client/src/index.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/lib/dashboard-types.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/web/src/lib/load-dashboard-snapshot.ts
  • apps/web/tsconfig.json
  • apps/web/src/lib/create-dashboard-client.ts
  • apps/web/src/lib/dashboard-selectors.test.ts
  • apps/web/src/app.test.tsx
  • apps/web/src/app.tsx
  • apps/web/src/lib/dashboard-selectors.ts

Comment thread packages/meticulous-client/src/index.ts Outdated
@ioncache
ioncache merged commit 840d685 into main Jun 29, 2026
3 checks passed
ioncache added a commit that referenced this pull request Jun 29, 2026
# Overview

Add the first REST-only ShotLab web dashboard shell so the machine state
and shot history can be explored in the browser before socket work and
brew controls.

## Details

- scaffold `@shotlab/web` with Vite, React, TypeScript, MUI, and
`@mui/x-charts`
- add direct browser-to-machine REST loading for machine, settings, last
profile, and history
- render live info cards, disabled safe-action placeholders, a shared
shot chart surface, and a history browser
- normalize observed history payloads into chart series for pressure,
flow, gravimetric flow, and weight
- move weight onto a secondary chart axis so the primary axis stays
readable for the flow-range metrics
- update `@shotlab/meticulous-client` history typing to match the
verified payload shape

## Related Tickets and/or Pull Requests

- Relates to the roadmap step for the first web dashboard shell

## Checklist

- [x] Tests added or updated
- [ ] README and TSDoc updated if the public API changed
- [ ] Breaking changes called out (if any)
- [ ] Roadmap item checked off if this PR completes one


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Introduced a new Web Dashboard experience with live status cards,
interactive charts, selected shot details, and history browsing.
* Added web app metadata (manifest) and a service worker for improved
offline/launch behavior.
  * Added shared theme and a React/MUI app entry setup.
* **Bug Fixes**
* Improved progressive loading and clearer error/warning display when
dashboard data can’t be retrieved.
* Added stronger handling for missing/partial machine, profile, and
history data to avoid blank/incorrect UI.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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