Skip to content

[OPIK-5312] [FE] feat: add loading state to assistant sidebar#5961

Merged
awkoy merged 1 commit intomainfrom
feat/assistant-sidebar-loading-state
Mar 30, 2026
Merged

[OPIK-5312] [FE] feat: add loading state to assistant sidebar#5961
awkoy merged 1 commit intomainfrom
feat/assistant-sidebar-loading-state

Conversation

@awkoy
Copy link
Copy Markdown
Contributor

@awkoy awkoy commented Mar 30, 2026

Details

Show a shimmer + spinner loader while the assistant backend pod starts up (5-30 seconds) instead of a blank empty space. Adds phase field to useAssistantBackend return type for contextual loading messages. Loader reads stored sidebar width from localStorage to match the iframe's eventual width, preventing layout jumps.

Change checklist

  • User facing
  • Documentation update

Issues

  • OPIK-5312

AI-WATERMARK

AI-WATERMARK: yes

  • Tools: Claude Code
  • Model(s): claude-opus-4-6
  • Scope: full implementation
  • Human verification: code review

Testing

  • Verify loader appears during pod startup in staging/production
  • Verify collapsed state shows shimmer + small spinner
  • Verify expanded state shows shimmer + spinner + phase message
  • Verify no width jump when iframe takes over after loading
  • Verify error state renders when pod times out
  • Verify dev mode still works (loader skipped, iframe loads immediately)

Documentation

Nothing

Show a shimmer + spinner while the backend pod starts up (5-30s)
instead of rendering nothing. The loader reads the sidebar's stored
width from localStorage so there's no layout jump when the iframe
takes over.
@awkoy awkoy requested a review from a team as a code owner March 30, 2026 13:38
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

📋 PR Linter Failed

Invalid Title Format. Your PR title must include a ticket/issue number and may optionally include component tags ([FE], [BE], etc.).

  • Internal contributors: Open a JIRA ticket and link to it: [OPIK-xxxx] or [CUST-xxxx] or [DND-xxxx] or [DEV-xxxx] [COMPONENT] Your change
  • External contributors: Open a Github Issue and link to it via its number: [issue-xxxx] [COMPONENT] Your change
  • No ticket: Use [NA] [COMPONENT] Your change (Issues section not required)

Example: [issue-3108] [BE] [FE] Fix authentication bug or [OPIK-1234] Fix bug or [NA] Update README


Incomplete Issues Section. You must reference at least one GitHub issue (#xxxx), Jira ticket (OPIK-xxxx), CUST ticket (CUST-xxxx), DEV ticket (DEV-xxxx), or DND ticket (DND-xxxx) under the ## Issues section.

@awkoy awkoy changed the title [FE] feat: add loading state to assistant sidebar [OPIK-5312] [FE] feat: add loading state to assistant sidebar Mar 30, 2026
Comment on lines +153 to +154
if (computeResult && !computeResult.enabled)
return notReadyResult(null, "disabled");
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.

if (computeResult && !computeResult.enabled) return notReadyResult(null, "disabled"); leaves error as null so toasts are empty — should we return a fallback like Assistant backend is disabled; enable it in settings. instead of null?
notReadyResult("Assistant backend is disabled; enable it in settings.", "disabled")

Finding type: Provide comprehensive error messages | Severity: 🟢 Low


Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents:

Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/plugins/comet/useAssistantBackend.ts around lines 153 to 154, the
branch handling a disabled compute returns notReadyResult(null, "disabled") which yields
an empty error string for the UI. Change that call to return notReadyResult("Assistant
backend is disabled; enable it in settings.", "disabled") so the returned
UseAssistantBackendResult.error contains a descriptive, actionable message for the user.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not needed — when phase === "disabled" the component returns null and the error UI is never rendered, so this message would never be seen.

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.

Got it, that makes sense given the phase === "disabled" behavior—I'll remember this and avoid similar unnecessary error message suggestions after the PR is merged.

Comment on lines +155 to +156
if (computeError)
return notReadyResult((computeError as Error).message, "error");
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.

if (computeError) return notReadyResult((computeError as Error).message, "error"); forwards computeError.message verbatim — should we fallback to computeError.message ?? "Assistant provisioning failed; please try again." when calling notReadyResult?
if (computeError) return notReadyResult((computeError as Error).message ?? "Assistant provisioning failed; please try again.", "error");

Finding type: Provide comprehensive error messages | Severity: 🟢 Low


Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents:

Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/plugins/comet/useAssistantBackend.ts around lines 155 to 156, the
return currently forwards computeError.message directly which can be undefined for
non-Error shapes. Replace that return with a safe-derived message: compute a message
variable using (computeError as Error)?.message ?? (typeof computeError === 'string' ?
computeError : String(computeError)) ?? 'Assistant provisioning failed; please try
again.' and then call return notReadyResult(message, 'error'); This ensures a non-empty,
user-facing fallback message for all error shapes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

React Query always wraps errors in an Error object, so .message is always a string here. No fallback needed.

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.

Got it, since React Query guarantees an Error object, no fallback is needed here—I'll remember this for similar error-handling suggestions after the PR is merged.

Comment on lines +26 to +37
function getStoredSidebarWidth(): number {
try {
const parsed = parseInt(
localStorage.getItem("assistant-sidebar-width") ?? "",
10,
);
if (parsed > 0) return parsed;
} catch {
/* localStorage unavailable */
}
return LOADER_DEFAULT_WIDTH;
}
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.

getStoredSidebarWidth and getStoredSidebarOpen hardcode the localStorage keys, should we hoist them into constants like const SIDEBAR_WIDTH_KEY = "assistant-sidebar-width" and const SIDEBAR_OPEN_KEY = "assistant-sidebar-open"?

Finding type: Avoid hardcoded configuration values | Severity: 🟢 Low


Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents:

Before applying, verify this suggestion against the current code. In
apps/opik-frontend/src/plugins/comet/AssistantSidebar.tsx around lines 26 to 45, the
functions getStoredSidebarWidth and getStoredSidebarOpen use hardcoded localStorage keys
"assistant-sidebar-width" and "assistant-sidebar-open". Hoist these strings into
well-named constants (e.g. const SIDEBAR_WIDTH_KEY = "assistant-sidebar-width" and const
SIDEBAR_OPEN_KEY = "assistant-sidebar-open") declared near the other top-level
constants, then replace the literal occurrences inside getStoredSidebarWidth and
getStoredSidebarOpen with those constants. Also scan the file and replace any other uses
of those same string literals with the new constants to centralize configuration and
avoid duplication.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Each key is used exactly once in its own dedicated helper function. Extracting constants for single-use strings would be over-abstraction.

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.

Got it, that makes sense for single-use keys scoped to dedicated helpers—I'll keep this nuance in mind and save it to memory after the PR is merged.

@awkoy awkoy merged commit d321a74 into main Mar 30, 2026
10 of 11 checks passed
@awkoy awkoy deleted the feat/assistant-sidebar-loading-state branch March 30, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants