Skip to content

[Portal] Migrate Hooks and Advanced settings UI to Radix#5794

Open
jimmycwc wants to merge 24 commits into
authgear:mainfrom
jimmycwc:ui-Radix-hooks
Open

[Portal] Migrate Hooks and Advanced settings UI to Radix#5794
jimmycwc wants to merge 24 commits into
authgear:mainfrom
jimmycwc:ui-Radix-hooks

Conversation

@jimmycwc

Copy link
Copy Markdown
Contributor

Summary

Migrates the Portal Hooks page and several Advanced settings screens from the legacy FluentUI layout to Radix UI, and introduces a v2 unsaved-changes flow.

UI migration

  • Migrate the Hooks page to Radix UI (accordion layout, Edit Script UI restyle)
  • Migrate Advanced settings screens and the Custom Email Provider screen to Radix UI
  • Migrate shared discard/confirmation dialogs to a Radix ConfirmationDialog
  • Replace the embedded authgear logo image with vector paths; restore sendgrid_logo
  • Remove the unused contentWidthAnchor class across configuration screens
  • Add Toggle Storybook stories

Save / form UX

  • Add a v2 SaveFunctionBar for unsaved form changes, with appear/disappear animation and robust, rAF-throttled alignment
  • Extend v2 TextField, FormField, and SecondaryButton
  • Fix missing SaveFunctionBar i18n keys

Hooks behavior

  • Add a name field to non-blocking hook handlers
  • Validate the hook endpoint URL on save with an inline error, persisting blocking hook endpoint errors until a successful save
  • Fix the flashing unsaved indicator on existing Deno hooks

Housekeeping

  • Revert non-hooks Advanced pages and non-hooks GraphQL generated files to the main versions to keep the diff scoped
  • Restore the projectQuota util required by AppsScreen

Test plan

  • Hooks page renders correctly with the Radix accordion and Edit Script UI
  • Adding/editing/removing blocking and non-blocking hooks works; the name field persists
  • Invalid hook endpoint URLs show an inline error on save and block saving
  • SaveFunctionBar appears on unsaved changes, animates correctly, and is properly aligned
  • Migrated Advanced and Custom Email Provider screens behave as before
  • Discard/confirmation dialogs work across migrated screens
  • npm run typecheck and make -C portal lint pass

Made with Cursor

jimmycwc and others added 24 commits June 1, 2026 15:03
Add password type and plain suffix support on TextField, optional
labelSize on FormField, and match disabled SecondaryButton styling to
the design system.

Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce a floating save bar aligned to content width, with discard
confirmation and Storybook coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the enable toggle with provider cards, adopt v2 form controls and
SaveFunctionBar, update typography and spacing to match design, and swap
provider logos to SVG assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use v2 components for Admin API Configuration screen and make the keys table horizontally scrollable on narrow viewports.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use ConfirmationDialog for SaveFunctionBar, FormContainer reset, and
navigation blocker flows. Raise dialog overlay z-index so Monaco editor
UI does not paint above the modal mask.

Co-authored-by: Cursor <cursoragent@cursor.com>
Document default, checked, with-text, and disabled Toggle variants.

Co-authored-by: Cursor <cursoragent@cursor.com>
Refactor Account Deletion, Account Anonymization, Cookie Lifetime,
Endpoint Direct Access, SMTP, SMS Provider, Admin API, Edit Config,
and SAML Certificate to v2 layout patterns with SaveFunctionBar.
Align content width to Admin API grid span 9 and add page descriptions.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use inline path elements instead of a base64 PNG for sharper rendering.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Replace Fluent UI components with Radix UI equivalents (Text, Select,
  TextField, ConfirmationDialog, IconButton, Tooltip)
- Introduce tab-based navigation (Blocking Events, Non-blocking Events,
  Hook Settings, Webhook Signature) using Radix Themes Tabs
- Apply two-column section layout (left: label, right: content) inside
  each tab panel, matching other advanced settings pages
- Style Blocking Hooks table to match Admin API keys table pattern
  (gray-a2 row backgrounds, border-top separators, semibold headers,
  horizontal scroll wrapper with min-width enforcement)
- Switch ScreenContent to list layout for responsive full-width grid
- Update Webhook Signature secret key input to use embedded reveal/copy
  icon buttons in TextField suffix, matching SMS Provider screen style

Co-authored-by: Cursor <cursoragent@cursor.com>
…ration screens

- Deleted the contentWidthAnchor CSS class from Account Anonymization, Account Deletion, Cookie Lifetime, Edit, Endpoint Direct Access, SAMLCertificate, SMS Provider, and SMTP configuration screens.
- Updated corresponding TSX files to remove references to the now-removed class, ensuring cleaner code and improved maintainability.
Hooks Radix migration is tracked separately on ui-Radix-hooks so this
branch can focus on shared Advanced settings Radix infrastructure.

Co-authored-by: Cursor <cursoragent@cursor.com>
Move hooks Radix migration out of ui-Radix into a dedicated branch so it
can be reviewed and merged independently after the shared Radix infra lands.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep only HookConfigurationScreen changes in this branch.
All other portal screens reverted to match main.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…reen

Co-authored-by: Cursor <cursoragent@cursor.com>
Add an optional Name field to each non-blocking event handler accordion.
The name is shown as the accordion title; if empty, falls back to the
URL or hook type as before.

Changes:
- pkg/lib/config/hook.go: add name to NonBlockingHandlersConfig struct and JSON schema
- portal/src/types.ts: add name? to NonBlockingHookHandlerConfig
- HookConfigurationScreen.tsx: add name to NonBlockingEventHandler interface,
  makeDefaultNonBlockingHandler, state sync callbacks, accordion title logic,
  and Name input field in the accordion body
- en.json: add name label and placeholder i18n keys

Co-authored-by: Cursor <cursoragent@cursor.com>
The alignment hook only measured the anchor on a one-shot layout effect keyed
by the ref object, used getBoundingClientRect + setState on every scroll event
with no throttle, and located the scroll container with a fragile overflowY
walk that missed window-level scrolling.

- rAF-coalesce measurements and skip setState when left/width are unchanged, so
  scrolling no longer re-renders the bar every frame.
- Listen for scroll in the capture phase on window, which catches any ancestor
  scroll container (nested or the window/document) without locating it.
- Retry measurement until a late-mounting anchor appears, and (re)observe the
  current anchor element, instead of giving up when it is null.
- Hoist the discard dialog's onOpenChange into a stable useCallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The save bar previously mounted and unmounted instantly. Keep it mounted across
the exit and play a slide + fade via CSS keyframes: saveFunctionBarIn on
appear, saveFunctionBarOut on disappear (then unmount). Respects
prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Show "Invalid format" on the webhook endpoint field after save when the URL is empty or malformed, and block the save client-side to avoid a top-level validation banner.

Co-authored-by: Cursor <cursoragent@cursor.com>
Track invalid blocking webhook endpoints by index so the inline "Invalid
format" error stays visible after a failed save, instead of clearing as
soon as the user edits the field. The error is cleared only on a
successful save or when the form is no longer dirty.

Co-authored-by: Cursor <cursoragent@cursor.com>
AppsScreen imports isProjectQuotaReached from util/projectQuota, but the
file was missing on this branch, breaking the Vite build. Restore it.

Co-authored-by: Cursor <cursoragent@cursor.com>
Wrap the TypeScript editor in a Radix editor card with a "TypeScript"
header, replace the FluentUI title/description/buttons with Radix
equivalents, shorten the inline "Edit Script" action to "Edit", drop the
trailing colon from the "Script" label, and render the unsaved dirty
indicator as a small CSS dot.

Co-authored-by: Cursor <cursoragent@cursor.com>
When a new Deno hook is added, the app templates query refetches with new
paths and `data` is transiently undefined, causing existing Deno hook
resources to be momentarily diffed as new. Fall back to `previousData`
while the refetch is in flight so existing hooks keep their loaded values.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

2 participants