Grafana: Realms Grant Permission as Business Forms (chooser + button)#4744
Open
Grafana: Realms Grant Permission as Business Forms (chooser + button)#4744
Conversation
…er + button
The Grant Permission tile on the Realms dashboard was a stat panel
with a Grafana-native action whose entire input UX lived in the
dashboard top bar (matrix_username textbox + permission_level radio
template variables). Two problems:
1. The username was a free-text textbox — operators had to remember
the exact Matrix ID with no help from existing user data.
2. The control surface was scattered: variables in the top bar, a
"Grant" button in the panel body. Easy to forget which top-bar
variable feeds which panel.
Replace with a `volkovlabs-form-panel` (Business Forms — same plugin
the Reindex panel already uses on this dashboard) that bundles
chooser + permission selector + button into one self-contained
control:
- Matrix username: `select` element with `optionsSource: Query`,
sourced from `SELECT matrix_user_id FROM users ORDER BY 1`.
`allowCustomValue: true` so operators can still type a brand-new
Matrix ID for users not yet in the table.
- Permission level: `radio` with Read / Read+Write.
- Button: customCode that pre-validates, browser-confirms with the
resolved values, and POSTs to the existing
`/_grafana-upsert-realm-user-permission?...` endpoint with the
CS-10929 bearer header (same auth path the Reindex button uses).
Side effects:
- Dashboard template variables `matrix_username`, `permission_level`,
`permission_label`, `read_flag`, `write_flag` are now unused
(verified: zero `${var}` references after the conversion). Removed.
- Grant tile grows from h=4 to h=8 to fit the form. Cascaded the y
positions of the panels below it (Realm Permissions table,
Indexing status, Reindex form, Recent Errors, Recent Jobs).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Observability diff (vs staging)No dashboard / folder changes detected against the staging Grafana. (Run: https://github.com/cardstack/boxel/actions/runs/25613855685) |
volkovlabs-form-panel compiles a Custom Button's `customCode` via
`new Function("context", body)` — a regular (non-async) function.
Top-level `await` in the body throws SyntaxError, which the plugin's
wrapper silently catches and turns into a no-op (only a `Code Error`
console log shows the failure). Result: clicking Grant did nothing,
no network request fired.
Fix: wrap the `try { ... await fetch ... } catch (err) {...}` block
in an `(async () => { ... })()` IIFE. The outer Function-built closure
stays sync; the inner IIFE is the async context that legalises await.
Pre-existing buttons on this and other dashboards (Reindex this realm,
Reindex all realms, Add Credit ×4) are broken in exactly the same way.
Those are out of scope for this PR — fixed in a follow-up so the
Grant Permission feature ships working in isolation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
`Code Error SyntaxError: Unexpected identifier 's'` shows up when
clicking the Grant button locally. Cause: the local-dev default
`grafana_secret` is `shhh! it's a secret`, and customCode uses the
bare-quote substitution `'Bearer ${grafana_secret}'`. After
`replaceVariables`, the apostrophe in `it's` closes the JS string
early, leaving `s a secret'` as garbage that crashes the parser. The
plugin's `_e` wrapper catches the SyntaxError and turns it into a
no-op — same silent-failure mode as the await bug just fixed.
Fix: use Grafana's `:doublequote` formatter so the substituted value
arrives pre-quoted as a JSON-style double-quoted string with proper
escaping, which is also a valid JS string literal. Drop the
surrounding single quotes (or split-and-concat at variable
boundaries) so the JS source ends up syntactically clean for any
value the SSM secret might hold in staging/prod.
Same root cause as the rest of the operator-action buttons; this PR
only fixes its own btn_grant. The 6 pre-existing broken buttons are
fixed in #4745.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Grant Permission tile on the Realms dashboard was a stat panel with a Grafana-native action; its entire input UX lived in the top bar as template variables (
matrix_usernametextbox +permission_levelradio). Two problems:This converts the tile to a
volkovlabs-form-panel(the same plugin the Reindex panel already uses on this dashboard), bundling the chooser, permission selector, and button into one self-contained control:selectwithoptionsSource: \"Query\", sourced from `SELECT matrix_user_id FROM users ORDER BY 1`. `allowCustomValue: true` so operators can still type a brand-new Matrix ID for users not yet in the table.Side effects
Depends on
Test plan
http://localhost:3001/d/boxelrealms001(Realms) — Grant Permission renders as a horizontal form: Matrix username dropdown on the left, button on the rightuserstable; typing a value not in the list (with `allowCustomValue`) keeps the typed text🤖 Generated with Claude Code