feat: general dashboard [skip-line-limit]#1546
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (14)
📝 WalkthroughWalkthroughThis PR introduces the ChangesEnclave Dashboard Implementation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (2)
packages/enclave-dashboard/.gitignore (1)
1-1: 💤 Low valueConsider adding common build artifacts and local env files.
The
.gitignoreonly covers.env, which is good for preventing secret leaks. However, consider also ignoring common build artifacts and local environment variations:
dist/(Vite build output).env.local(local environment overrides).vercel/(Vercel CLI artifacts)These might already be covered by a root-level
.gitignore, in which case this is fine as-is.📝 Suggested additions
-.env +.env +.env.local +dist/ +.vercel/🤖 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/enclave-dashboard/.gitignore` at line 1, Add common local and build artifacts to the package's .gitignore: include entries for dist/ (Vite build output), .env.local (local overrides), and .vercel/ (Vercel CLI artifacts) alongside the existing .env entry so local builds and platform artifacts aren’t accidentally committed; if these are already covered by a repository-level .gitignore, verify consistency and add any missing entries to packages/enclave-dashboard/.gitignore.packages/enclave-dashboard/index.html (1)
8-13: ⚡ Quick winConsider self-hosting fonts for privacy and reliability.
Loading fonts from Google Fonts CDN has potential privacy implications (Google can track users) and reliability concerns (external dependency). For a production dashboard, consider self-hosting the Geist font files to improve privacy, ensure availability, and reduce external dependencies.
Self-hosting fonts also provides:
- Better privacy compliance (GDPR/CCPA)
- Guaranteed availability (no external dependency)
- Potential performance improvements (same-origin, better caching control)
🤖 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/enclave-dashboard/index.html` around lines 8 - 13, Replace the external Google Fonts links that load "Geist" and "Geist Mono" with locally hosted font files: download the required font weights/variants for Geist and Geist Mono, add them to your static assets, create a local CSS with `@font-face` declarations for each family/weight, and update the HTML to reference that local stylesheet instead of the Google CDN hrefs; ensure the new CSS paths match the served static directory and remove crossorigin attributes for the external links.
🤖 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/enclave-dashboard/src/App.tsx`:
- Around line 293-324: The UI currently treats any non-ready state as a loader
so fetch errors (status === 'error') spin forever; update the rendering logic to
check each data fetch status explicitly and show an error UI instead of the
Loader. For the inspector branch, check the allE3s (or whatever hook/result
holds E3 list) status and if status === 'error' render a StatusNote (or similar)
with the error message before falling back to Loader or the Inspector; keep
inspectorDetail.status === 'loading' for Inspector.loading and
inspectorDetail.error for Inspector.error. Do the same for crispPolls and
todaysDetail (used with crispReady/livePoll): if crispPolls.status === 'error'
or todaysDetail.status === 'error' render a StatusNote with the error text
rather than the Loader. Ensure you reference and use the existing symbols
inspectorReady, inspectorList/allE3s, inspectorDetail, crispReady, crispPolls,
todaysDetail/livePoll and set loading props only when status === 'loading'.
In `@packages/enclave-dashboard/src/History.tsx`:
- Around line 137-140: The "Load earlier polls" button in History.tsx currently
has no behavior and gives a false affordance; either wire it to an actual
handler (e.g., add an onClick that calls a loadEarlierPolls or onLoadEarlier
prop/function to fetch/append earlier items and update state) or mark it
non-interactive (add disabled, aria-disabled="true", and update
styling/className to a disabled variant like 'link-btn--disabled' and ensure
pointer-events are none). Update the button element accordingly and ensure any
new handler is defined/exported (e.g., loadEarlierPolls) and connected to the
component props/state so the control is functional or clearly disabled.
In `@packages/enclave-dashboard/src/Inspector.tsx`:
- Around line 397-417: The SectionCard status props are hardcoded to 'pending'
causing incorrect UI; derive each card's status from e3.currentStage instead.
Update the three SectionCard instances (the ones titled 'FHE computation',
'Threshold decryption', and 'Result on-chain') to compute status based on
e3.currentStage and the corresponding stage identifiers (e.g., compare
e3.currentStage to 'compute', 'decryption', 'publication' or an ordered stage
index) and pass the correct { kind, label } object to the status prop so the
card shows 'pending', 'active', or 'complete' as appropriate.
In `@packages/enclave-dashboard/src/lib/e3.ts`:
- Around line 226-233: The code in fetchE3Details uses getLogsChunked with
DEPLOY_BLOCK as the lower bound causing full-history scans on each refresh;
change the lower bound to use the specific event's request block
(e3.requestBlock) or another tight per-E3 lower bound when calling
getLogsChunked for requestLogs (and the other similar calls between lines
~239-308) so each query starts at the E3's requestBlock instead of DEPLOY_BLOCK;
update the getLogsChunked call sites (referenced by requestLogs and other
log-fetching variables in fetchE3Details) to pass e3.requestBlock (or
Math.max(e3.requestBlock, DEPLOY_BLOCK) if you need a safe floor) as the start
block.
- Around line 385-392: The decoder currently converts each uint64 word to a JS
Number via Number(BigInt('0x'+word)) which can silently lose precision; in the
loop that reads words (using variables lengthWord, len, hex and pushing into
out), add a check after parsing BigInt('0x'+word) to ensure the bigint value <=
BigInt(Number.MAX_SAFE_INTEGER) (or alternatively assert it fits within the
contract's MAX_VOTE_BITS), and if it exceeds the safe threshold return null (or
throw) instead of converting, so you avoid silent precision loss before pushing
into out and before any Math.max/percentage calculations.
In `@packages/enclave-dashboard/src/lib/useE3s.ts`:
- Around line 30-44: The polling `tick` can overlap when a fetch takes longer
than POLL_MS; modify the `tick` in useE3s (and mirror the same change in
useE3Details) to prevent concurrent runs by adding an `inFlight` flag (e.g., let
inFlight = false outside tick) or replace setInterval with an async loop using
awaited setTimeout: at start of `tick` return immediately if `inFlight` is true,
set `inFlight = true` before calling `fetchE3List`, and in a finally block set
`inFlight = false`; ensure cleanup clears interval or signals the loop to stop
on unmount/cancelled so no overlapping fetches occur.
In `@packages/enclave-dashboard/src/PollCard.tsx`:
- Around line 146-147: Replace the non-null assertion on the winner lookup
(const winner = items.find((i) => i.id === poll.result.winner)!) with a safe
lookup that can be undefined (e.g., const winner = items.find((i) => i.id ===
poll.result.winner) || null) and then guard all places that access winner
properties (render, counts, labels) to handle null/undefined (show a
placeholder, skip rendering, or use a fallback label). Do the same for any other
similar lookups around PollCard that use `!` (the other find usages referenced
in the comment) so the component never dereferences an undefined value during
render.
- Around line 207-208: The code dereferences STAGES[currentStageIdx] without
guarding against undefined (used to compute stageId and later passed into
StageBadge), which can crash rendering; update the logic around STAGES and
currentStageIdx to clamp currentStageIdx into the valid range or provide a
fallback stage object, e.g. derive a safeStage = STAGES[currentStageIdx] ?? {
id: 'unknown', label: 'Unknown' } and then use safeStage.id and safeStage.label
(and compute status from a default key when needed) so StageBadge and the status
lookup never receive undefined.
- Around line 281-284: The click handler on the PollCard currently always calls
e.preventDefault() so the link is inert when onNavigate is undefined; update the
onClick in the PollCard component to only call e.preventDefault() and call
onNavigate('inspector') when onNavigate is provided (i.e., if (onNavigate) {
e.preventDefault(); onNavigate('inspector') }) so native navigation proceeds
when onNavigate is not passed; locate the onClick handler referencing onNavigate
in PollCard.tsx and change its behavior accordingly.
In `@packages/enclave-dashboard/src/styles.css`:
- Line 1918: Replace the deprecated CSS rule "word-break: break-word" with a
modern equivalent: remove "word-break: break-word" and add "overflow-wrap:
anywhere;" (or "overflow-wrap: break-word;" if preferred) in the same CSS rule
where "word-break: break-word" appears so the selector's behavior is preserved
and stylelint no longer fails.
- Around line 1050-1052: The new global rule ".poll-card__head .stage-badge {
margin-left: 0; }" is undoing the intended desktop alignment set earlier
(".poll-card__head .stage-badge { margin-left: auto; }"); to fix, remove or
scope this override so it only applies where needed (e.g., wrap the
margin-left:0 inside a mobile media query like `@media` (max-width: ...){
.poll-card__head .stage-badge { margin-left: 0; } } or apply a more specific
selector for the mobile/state variant), ensuring the original .poll-card__head
.stage-badge margin-left: auto remains effective on desktop.
In `@packages/enclave-dashboard/src/Timeline.tsx`:
- Around line 52-55: The code assumes stages[explainerIdx] always exists and
will throw if stages is empty or explainerIdx is out of range; update the logic
around hoverIdx, explainerIdx and explainer to guard against empty or OOB
indexes (e.g., compute explainerIdx = clamp(hoverIdx ?? currentStageIdx, 0,
stages.length - 1) or detect stages.length === 0 and provide a safe fallback
object), and replace direct accesses to explainer.label/explainer.blurb (and the
same occurrences around the block referenced at lines ~97-103) with
checks/optional chaining or the fallback so the UI renders an empty state
instead of crashing. Ensure to keep references to hoverIdx, setHoverIdx,
currentStageIdx and explainer when locating and changing the code.
In `@packages/enclave-dashboard/src/tweaks-panel.tsx`:
- Around line 322-334: The radio group is currently pointer-only; add keyboard
operability by handling key events and proper focus management: attach an
onKeyDown to the container (where trackRef and onPointerDown are used) to listen
for ArrowLeft/ArrowRight (prev/next), Home (first) and End (last) and update the
selected value using the same state setter you use for pointer selection (the
handler that updates value), then move focus to the newly selected button; also
set tabIndex on each rendered radio button (tabIndex=0 for the selected option,
-1 for others) and ensure aria-checked is updated based on value so screen
readers and keyboard users can navigate (use opts, value, idx, n to compute
next/prev indices).
In `@packages/enclave-dashboard/tsconfig.json`:
- Around line 8-9: Update the TypeScript configuration to enable strict mode by
setting the "strict" and "noImplicitAny" compiler options to true in
tsconfig.json; then run the type checker (tsc) and fix resulting type errors
across the codebase (e.g., add explicit types, handle null/undefined, and refine
interfaces) so the project compiles cleanly under strict mode. Ensure you modify
the "strict" and "noImplicitAny" options in tsconfig.json and address failures
reported by tsc by updating functions, components, and types where implicit any
or unsafe null handling occurs.
---
Nitpick comments:
In `@packages/enclave-dashboard/.gitignore`:
- Line 1: Add common local and build artifacts to the package's .gitignore:
include entries for dist/ (Vite build output), .env.local (local overrides), and
.vercel/ (Vercel CLI artifacts) alongside the existing .env entry so local
builds and platform artifacts aren’t accidentally committed; if these are
already covered by a repository-level .gitignore, verify consistency and add any
missing entries to packages/enclave-dashboard/.gitignore.
In `@packages/enclave-dashboard/index.html`:
- Around line 8-13: Replace the external Google Fonts links that load "Geist"
and "Geist Mono" with locally hosted font files: download the required font
weights/variants for Geist and Geist Mono, add them to your static assets,
create a local CSS with `@font-face` declarations for each family/weight, and
update the HTML to reference that local stylesheet instead of the Google CDN
hrefs; ensure the new CSS paths match the served static directory and remove
crossorigin attributes for the external links.
🪄 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
Run ID: 9f108579-afd4-4709-b8b6-f8dc0ecadb69
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (27)
packages/enclave-dashboard/.env.examplepackages/enclave-dashboard/.gitignorepackages/enclave-dashboard/README.mdpackages/enclave-dashboard/index.htmlpackages/enclave-dashboard/package.jsonpackages/enclave-dashboard/src/App.tsxpackages/enclave-dashboard/src/History.tsxpackages/enclave-dashboard/src/Inspector.tsxpackages/enclave-dashboard/src/Loader.tsxpackages/enclave-dashboard/src/PollCard.tsxpackages/enclave-dashboard/src/Pulse.tsxpackages/enclave-dashboard/src/Timeline.tsxpackages/enclave-dashboard/src/data.tspackages/enclave-dashboard/src/lib/adapt.tspackages/enclave-dashboard/src/lib/chain.tspackages/enclave-dashboard/src/lib/e3.tspackages/enclave-dashboard/src/lib/links.tspackages/enclave-dashboard/src/lib/pollMeta.tspackages/enclave-dashboard/src/lib/useE3s.tspackages/enclave-dashboard/src/main.tsxpackages/enclave-dashboard/src/styles.csspackages/enclave-dashboard/src/tweaks-panel.tsxpackages/enclave-dashboard/src/useTweaks.tspackages/enclave-dashboard/tsconfig.jsonpackages/enclave-dashboard/vercel.jsonpackages/enclave-dashboard/vite.config.tspnpm-workspace.yaml
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/enclave-dashboard/src/lib/useE3s.ts (1)
78-104:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReset detail state when switching between E3 selections.
This effect only resets to
loadingwhene3Id === null. When the user changes from one non-null E3 to another, the hook retains the previous{ status: 'ready', data }until the new fetch completes, causing the UI to briefly display the newly selected E3's ID with the old selection's stale data andloading={false}. Add an unconditional state reset before the polling setup for each new non-nulle3Id.🔄 Minimal fix
if (e3Id === null) { setState({ status: 'loading', data: null, error: null }) return } + setState({ status: 'loading', data: null, error: null }) let cancelled = false let inFlight = false🤖 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/enclave-dashboard/src/lib/useE3s.ts` around lines 78 - 104, When e3Id changes to a new non-null value the hook should immediately reset the detail state to loading so the UI doesn't show stale ready data; in useE3s (inside the effect that references e3Id, mounted, tick, and setInterval) add an unconditional setState({ status: 'loading', data: null, error: null }) before creating the tick/polling (i.e., after the early-return for e3Id === null) so every new non-null e3Id starts from loading while fetchE3Details runs; keep the existing inFlight/cancelled/tick logic unchanged.
♻️ Duplicate comments (2)
packages/enclave-dashboard/src/tweaks-panel.tsx (1)
320-329:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep focus aligned with the checked radio.
Arrow keys update
value, but focus stays on the previously focused button. After rerender, the active element can be an unchecked radio, which makes keyboard and screen-reader interaction inconsistent. Move focus to the newly selected button when the selection changes; Home/End can reuse the same helper.♿ Minimal fix
+ const focusOption = (nextIdx: number) => { + onChange(opts[nextIdx].value) + requestAnimationFrame(() => { + trackRef.current?.querySelectorAll<HTMLButtonElement>('button[role="radio"]')[nextIdx]?.focus() + }) + } + const onKeyDown = (e: React.KeyboardEvent) => { const cur = opts.findIndex((o: any) => o.value === value) if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { e.preventDefault() - onChange(opts[Math.min(opts.length - 1, cur + 1)].value) + focusOption(Math.min(opts.length - 1, cur + 1)) } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault() - onChange(opts[Math.max(0, cur - 1)].value) + focusOption(Math.max(0, cur - 1)) + } else if (e.key === 'Home') { + e.preventDefault() + focusOption(0) + } else if (e.key === 'End') { + e.preventDefault() + focusOption(opts.length - 1) } }Also applies to: 333-355
🤖 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/enclave-dashboard/src/tweaks-panel.tsx` around lines 320 - 329, The arrow-key handler (onKeyDown) updates the selected value but doesn't move DOM focus to the newly checked radio, causing mismatch for keyboard/screen-reader users; after calling onChange with the new value (computed via opts and cur), programmatically focus the corresponding radio element (e.g., query/select the radio by value or use a ref array) so the focused element matches the checked one, and extract this focus logic into a small helper that is also reused by the Home/End handlers and the similar keyboard handler later in the file to keep behavior consistent across the component.packages/enclave-dashboard/src/Timeline.tsx (1)
52-55:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winHandle the empty
stagescase before clamping.This still crashes when
stages.length === 0:clamp()returns-1,explainerbecomesundefined, and the explainer render dereferences it below. Add an explicit empty state before computingexplainerIdx.🛡️ Minimal fix
+ if (stages.length === 0) { + return ( + <section className={`timeline timeline--${density}`}> + <div className='timeline__explainer'>No lifecycle stages available.</div> + </section> + ) + } + const [hoverIdx, setHoverIdx] = useState<number | null>(null) const clamp = (i: number) => Math.min(Math.max(i, 0), stages.length - 1) const explainerIdx = clamp(hoverIdx ?? currentStageIdx)Also applies to: 98-103
🤖 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/enclave-dashboard/src/Timeline.tsx` around lines 52 - 55, The code crashes when stages is empty because clamp computes -1 and explainer becomes undefined; before calling clamp and indexing stages, add an explicit empty-state guard (e.g., if stages.length === 0) and return or render a safe fallback, or set explainerIdx only when stages.length > 0; update the logic around hoverIdx, clamp, explainerIdx and explainer so they are only computed/used when stages.length > 0 (also apply same guard to the similar block around lines 98-103).
🤖 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.
Outside diff comments:
In `@packages/enclave-dashboard/src/lib/useE3s.ts`:
- Around line 78-104: When e3Id changes to a new non-null value the hook should
immediately reset the detail state to loading so the UI doesn't show stale ready
data; in useE3s (inside the effect that references e3Id, mounted, tick, and
setInterval) add an unconditional setState({ status: 'loading', data: null,
error: null }) before creating the tick/polling (i.e., after the early-return
for e3Id === null) so every new non-null e3Id starts from loading while
fetchE3Details runs; keep the existing inFlight/cancelled/tick logic unchanged.
---
Duplicate comments:
In `@packages/enclave-dashboard/src/Timeline.tsx`:
- Around line 52-55: The code crashes when stages is empty because clamp
computes -1 and explainer becomes undefined; before calling clamp and indexing
stages, add an explicit empty-state guard (e.g., if stages.length === 0) and
return or render a safe fallback, or set explainerIdx only when stages.length >
0; update the logic around hoverIdx, clamp, explainerIdx and explainer so they
are only computed/used when stages.length > 0 (also apply same guard to the
similar block around lines 98-103).
In `@packages/enclave-dashboard/src/tweaks-panel.tsx`:
- Around line 320-329: The arrow-key handler (onKeyDown) updates the selected
value but doesn't move DOM focus to the newly checked radio, causing mismatch
for keyboard/screen-reader users; after calling onChange with the new value
(computed via opts and cur), programmatically focus the corresponding radio
element (e.g., query/select the radio by value or use a ref array) so the
focused element matches the checked one, and extract this focus logic into a
small helper that is also reused by the Home/End handlers and the similar
keyboard handler later in the file to keep behavior consistent across the
component.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 06f0d2c2-e4c0-456e-add6-92f86169b6be
📒 Files selected for processing (9)
packages/enclave-dashboard/src/App.tsxpackages/enclave-dashboard/src/History.tsxpackages/enclave-dashboard/src/Inspector.tsxpackages/enclave-dashboard/src/PollCard.tsxpackages/enclave-dashboard/src/Timeline.tsxpackages/enclave-dashboard/src/lib/e3.tspackages/enclave-dashboard/src/lib/useE3s.tspackages/enclave-dashboard/src/styles.csspackages/enclave-dashboard/src/tweaks-panel.tsx
💤 Files with no reviewable changes (1)
- packages/enclave-dashboard/src/History.tsx
0xjei
left a comment
There was a problem hiding this comment.
utACK - reviewed in pair-programming
Summary by CodeRabbit
Release Notes