feat(demo): add disabled badges + read-only fieldsets across settings UI#187
feat(demo): add disabled badges + read-only fieldsets across settings UI#187TerrifiedBug merged 3 commits intomainfrom
Conversation
…ngs UI Backend already rejects credential and identity mutations in demo mode (PR #185), but the UI still let users type into fields and click Save, producing toast errors at submit time. This adds frontend signaling so the read-only state is obvious before the user interacts. Adds three small primitives in src/components/demo-disabled.tsx: - DemoDisabledBadge -- inline "Demo" lock badge for card titles - DemoDisabledNotice -- amber info banner above a section - DemoDisabledFieldset -- wraps content in <fieldset disabled> + notice; passthrough outside demo mode Integrates them across the five settings surfaces flagged in the audit plus the per-environment enrollment token panel: - /settings/ai (AI provider + API key) - /settings/backup (S3 storage + manual backups + schedule) - /settings/auth (OIDC + IdP team mappings) - /settings/scim (SCIM provisioning + bearer token) - /environments/[id] (Git Integration tab + Agent Enrollment tab) For agent enrollment specifically: the Generate / Revoke buttons are disabled in demo, the Quick Start install snippets remain visible (now showing a clearly-fake "vf_enroll_demo_example_..." placeholder) so the demo still demonstrates the install flow without minting real tokens. The /api/agent/enroll endpoint already returns 403 in demo, so the placeholder cannot be redeemed. Components are no-ops outside demo mode (isDemoMode() short-circuits), so non-demo deployments are unchanged.
Greptile SummaryThis PR introduces three new UI primitives (
Confidence Score: 4/5Safe to merge after fixing the double-notice in backup-settings; all other surfaces are correct and non-demo deployments are unaffected. One P1 correctness bug (duplicate amber banner in demo mode) keeps this below a clean 5. The rest of the implementation is straightforward and correct. src/app/(dashboard)/settings/_components/backup-settings.tsx — standalone DemoDisabledNotice must be removed in favour of passing its message to DemoDisabledFieldset. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["isDemoMode()\nNEXT_PUBLIC_VF_DEMO_MODE === 'true'"] -->|false| B["Return children unchanged\n(no-op passthrough)"]
A -->|true| C["DemoDisabledFieldset"]
C --> D["Render DemoDisabledNotice\n(amber banner with message)"]
C --> E["Render fieldset disabled\n+ opacity-70"]
E --> F["All native inputs/buttons/selects\ninside are disabled by HTML spec"]
G["Surfaces using DemoDisabledFieldset"] --> H["ai-settings: AI provider form"]
G --> I["auth-settings: OIDC form\n+ IdP Group Mappings form"]
G --> J["backup-settings: All backup cards"]
G --> K["scim-settings: SCIM toggle + token"]
G --> L["git-sync-section: Git config form"]
M["enrollment tab (page.tsx)"] -->|"isDemoMode()"| N["Generate/Revoke buttons\ndisabled=true"]
M -->|"isDemoMode()"| O["Show Quick Start\nwith DEMO_EXAMPLE_ENROLL_TOKEN"]
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/app/(dashboard)/settings/_components/backup-settings.tsx
Line: 221-224
Comment:
**Double amber notice rendered in demo mode**
A standalone `<DemoDisabledNotice>` is placed immediately before `<DemoDisabledFieldset>`, but `DemoDisabledFieldset` unconditionally calls `<DemoDisabledNotice message={message}>` internally. Since no `message` prop is passed to `DemoDisabledFieldset` here, in demo mode two banners appear back-to-back: the explicit custom one, then the default "This area is read-only in the public demo. Self-host VectorFlow to configure it." fallback.
Remove the standalone `<DemoDisabledNotice>` and pass its message to `DemoDisabledFieldset` instead:
```suggestion
<DemoDisabledFieldset message="Backup creation, restore, and S3 storage configuration are disabled in the public demo. The buttons and inputs below are read-only.">
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat(demo): add disabled badges and read..." | Re-trigger Greptile |
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…tion The Greptile suggestion accepted on PR #187 dropped the function's `return (` wrapper and the outer `<div>` while leaving two opening `<DemoDisabledFieldset>` tags, producing a JSX parse error in CI. Restores the return wrapper and consolidates to a single `<DemoDisabledFieldset message=...>` (which already renders the notice internally via its `message` prop, matching the suggestion's intent). Also drops the now-unused DemoDisabledFieldset / DemoDisabledNotice imports flagged by no-unused-vars.
Summary
Pairs with #185 to make the read-only nature of the public demo obvious before a user interacts. Backend rejects credential/identity mutations in demo, but the UI was still letting users type into API key, S3 secret, Git PAT, OIDC client secret, and SCIM token fields and only surfacing the rejection on submit.
New primitives
src/components/demo-disabled.tsx:<DemoDisabledBadge />— inline lock badge for card titles<DemoDisabledNotice />— amber info banner above a section<DemoDisabledFieldset>— wraps content in<fieldset disabled>+ notice; passthrough outside demo modeAll three short-circuit to no-op when `isDemoMode()` is false, so non-demo deployments are unaffected.
Surfaces updated
Test plan