Skip to content

[Customer Portal] Show full token name in generate/regenerate modals and add duplicate name validation - #642

Merged
shayanmalinda merged 2 commits into
wso2-open-operations:mainfrom
shayanmalinda:main
Apr 30, 2026
Merged

shayanmalinda merged 2 commits into
wso2-open-operations:mainfrom
shayanmalinda:main

Conversation

@shayanmalinda

@shayanmalinda shayanmalinda commented Apr 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Prevent creating tokens with case-insensitive duplicate names.
    • Require integration user selection for service tokens before generation.
  • Bug Fixes

    • Display and copy actions now use the server-returned token name after create/regenerate.
    • Name validation errors recompute on input and disable generation while present.

…name validation

- Add name field to RegistryTokenCreationResponse to capture full token
  name from API response
- Display full token name (e.g. robot$token-...) in both Generate and
  Regenerate modals instead of the short display name
- Add duplicate token name validation in GenerateTokenModal with
  real-time checking on keystroke
- Disable Generate Token button when validation errors are present

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 30, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 31e67227-29ad-4239-82f2-9ae1c4376db2

📥 Commits

Reviewing files that changed from the base of the PR and between bf83d85 and 178cac6.

📒 Files selected for processing (1)
  • apps/customer-portal/webapp/src/features/settings/components/GenerateTokenModal.tsx

📝 Walkthrough

Walkthrough

The changes add case-insensitive duplicate-name validation to token creation, pass existing token names into the generate modal, and store/display a returned full token name from create/regenerate API responses. A response type was extended to optionally include the token name.

Changes

Cohort / File(s) Summary
Token Modal Components
apps/customer-portal/webapp/src/features/settings/components/GenerateTokenModal.tsx, apps/customer-portal/webapp/src/features/settings/components/RegenerateTokenModal.tsx
Introduce fullTokenName state set from API response data.name and use it for post-create/regenerate display and copy. GenerateTokenModal now accepts existingTokenNames prop, validates robotName case-insensitively against it, recomputes name errors on input change, and disables generate when validation errors (and for service tokens, when no integration user selected).
Settings Registry Component
apps/customer-portal/webapp/src/features/settings/components/SettingsRegistryTokens.tsx
Builds an array of existing token names (prefers displayName over name) and passes it to GenerateTokenModal via new existingTokenNames prop.
Type Definitions
apps/customer-portal/webapp/src/features/settings/types/registryTokens.ts, apps/customer-portal/webapp/src/features/settings/types/settings.ts
RegistryTokenCreationResponse now optionally includes name?: string; GenerateTokenModalProps gains optional existingTokenNames?: string[].

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Settings as SettingsRegistryTokens
  participant Modal as GenerateTokenModal
  participant API as Token API

  User->>Settings: open Tokens page
  Settings->>Modal: provide existingTokenNames (prefers displayName)
  User->>Modal: enter robotName
  Modal->>Modal: validate robotName (case-insensitive vs existingTokenNames)
  alt valid
    User->>Modal: click Generate
    Modal->>API: create token request
    API-->>Modal: response { secret, name? }
    Modal->>Modal: set fullTokenName = response.name
    Modal-->>User: show token secret and fullTokenName (copy available)
  else invalid
    Modal-->>User: show validation error (duplicate/already exists)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Type/Improvement, Type/UX, App/Customer Portal, Area/Frontend

Poem

🐰 I hop through code with joy and cheer,
Duplicate names? I sniff them near.
Full names returned, displayed with pride,
Tokens copied, errors tried—denied.
Hop, validate, then celebrate! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided; all required sections from the template are missing or empty. Add a comprehensive description following the template, including Purpose, Goals, Approach, User stories, Release note, Documentation, and other required sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: displaying full token names in modals and adding duplicate name validation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@shayanmalinda shayanmalinda changed the title Show full token name in generate/regenerate modals and add duplicate name validation [Customer Portal] Show full token name in generate/regenerate modals and add duplicate name validation Apr 30, 2026

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/customer-portal/webapp/src/features/settings/components/RegenerateTokenModal.tsx (1)

66-74: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Block modal dismissal while regeneration is pending.

Dialog onClose and the title-bar close button can still dismiss the modal during regenerateMutation.isPending. That can rotate the secret without the user seeing/copying the new value.

Suggested fix
+  const isModalBusy = regenerateMutation.isPending;
+
   function handleClose() {
+    if (isModalBusy) return;
     setSecret(null);
     setFullTokenName(null);
     setShowSecret(false);
     setCopiedSecret(false);
     setCopiedName(false);
     regenerateMutation.reset();
     onClose();
   }

-  return (
-    <Dialog open={open} onClose={handleClose} maxWidth="sm" fullWidth>
+  return (
+    <Dialog
+      open={open}
+      onClose={isModalBusy ? undefined : handleClose}
+      maxWidth="sm"
+      fullWidth
+    >
...
-        <IconButton size="small" onClick={handleClose} aria-label="close">
+        <IconButton
+          size="small"
+          onClick={handleClose}
+          aria-label="close"
+          disabled={isModalBusy}
+        >
Based on learnings: In modal flows (e.g., PR `#418` and PR `#384`), close handlers and title-bar close controls should be guarded while mutations are pending to prevent in-flight-operation interruption.

Also applies to: 95-107

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/features/settings/components/RegenerateTokenModal.tsx`
around lines 66 - 74, The modal can be dismissed while a regeneration is
pending; update the close flow to prevent dismissal during
regenerateMutation.isPending by guarding the handleClose logic and any
title-bar/Dialog onClose handlers: in the handleClose function (and any click
handler wired to the title-bar close control or Dialog onClose), first check
regenerateMutation.isPending and if true simply return (or no-op) so you don't
call setSecret, setFullTokenName, setShowSecret, setCopiedSecret, setCopiedName,
regenerateMutation.reset, or onClose while the mutation is in-flight; also
ensure the title-bar close control is disabled or its handler respects the same
pending check.
apps/customer-portal/webapp/src/features/settings/components/SettingsRegistryTokens.tsx (2)

112-118: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include background refetch in table loading state.

Line 117 only uses isLoading, so refetches can leave stale rows visible without loading feedback.

Suggested fix
   const {
     data: allTokens = [],
     isLoading,
+    isFetching,
     error,
   } = useSearchRegistryTokens(projectId);
-  const isTableLoading = isLoading;
+  const isTableLoading = isLoading || isFetching;
Based on learnings: In `apps/customer-portal/webapp/src/features/settings/components/SettingsRegistryTokens.tsx` (PR `#511`, around lines 125–130), `isTableLoading` should include `isFetching` to avoid stale-table UX during background refetch.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/features/settings/components/SettingsRegistryTokens.tsx`
around lines 112 - 118, The table loading state currently uses only isLoading
from useSearchRegistryTokens, which misses background refetches; update the
calculation of isTableLoading in the SettingsRegistryTokens component to include
isFetching (e.g., const isTableLoading = isLoading || isFetching) so that
background refetches show the loading/disabled table UX; locate the
isTableLoading definition near useSearchRegistryTokens and adjust it to combine
isFetching with isLoading.

517-559: ⚠️ Potential issue | 🟠 Major

Keep MenuItem as direct children of Menu for keyboard navigation.

Wrapping MenuItem with Tooltip > span breaks MUI menu keyboard focus management (arrow-key navigation and roving focus). Remove the outer Tooltip and span wrappers so each MenuItem is a direct child of Menu, preserving disabled={isRestricted}.

Suggested fix
-        <Tooltip
-          title={isRestricted ? restrictedTooltip : ""}
-          disableHoverListener={!isRestricted}
-        >
-          <span>
-            <MenuItem
+        <MenuItem
               disabled={isRestricted}
               onClick={() => {
                 if (isRestricted) return;
                 setRegenerateToken(menuToken);
                 setMenuAnchor(null);
                 setMenuToken(null);
               }}
-            >
-              <ListItemIcon>
-                <RefreshCw size={16} />
-              </ListItemIcon>
-              <ListItemText>{REGISTRY_MENU_REGENERATE}</ListItemText>
-            </MenuItem>
-          </span>
-        </Tooltip>
+            >
+          <ListItemIcon>
+            <RefreshCw size={16} />
+          </ListItemIcon>
+          <ListItemText>{REGISTRY_MENU_REGENERATE}</ListItemText>
+        </MenuItem>
-        <Tooltip
-          title={isRestricted ? restrictedTooltip : ""}
-          disableHoverListener={!isRestricted}
-        >
-          <span>
-            <MenuItem
+        <MenuItem
               disabled={isRestricted}
               onClick={() => {
                 if (isRestricted) return;
                 setDeleteToken(menuToken);
                 setMenuAnchor(null);
                 setMenuToken(null);
               }}
               sx={{ color: "error.main" }}
-            >
-              <ListItemIcon sx={{ color: "error.main" }}>
-                <Trash2 size={16} />
-              </ListItemIcon>
-              <ListItemText>{REGISTRY_MENU_DELETE}</ListItemText>
-            </MenuItem>
-          </span>
-        </Tooltip>
+            >
+          <ListItemIcon sx={{ color: "error.main" }}>
+            <Trash2 size={16} />
+          </ListItemIcon>
+          <ListItemText>{REGISTRY_MENU_DELETE}</ListItemText>
+        </MenuItem>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/features/settings/components/SettingsRegistryTokens.tsx`
around lines 517 - 559, The MenuItem elements must be direct children of the
Menu to preserve MUI keyboard navigation; remove the outer Tooltip and wrapping
<span> around the MenuItem so the MenuItem remains a direct child while keeping
disabled={isRestricted} and the onClick handlers (setRegenerateToken,
setDeleteToken, setMenuAnchor, setMenuToken) intact. If you still need a
tooltip, move the Tooltip down to wrap a non-focusable child (e.g., wrap the
RefreshCw and Trash2 icons or the ListItemIcon) or use the MenuItem title
attribute, but do not wrap the MenuItem itself.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/customer-portal/webapp/src/features/settings/components/GenerateTokenModal.tsx`:
- Line 327: In GenerateTokenModal, the Generate button’s disabled prop only
checks createMutation.isPending and robotNameError, so it still enables when the
service-token assignee is invalid; update the disabled expression in the JSX
(where disabled={createMutation.isPending || !!robotNameError}) to also include
the assignee validation state (e.g., || !!serviceTokenAssigneeError or ||
!isAssigneeValid), ensuring you reference the component's existing assignee
validation variable/state (serviceTokenAssigneeError, isAssigneeValid,
validateAssignee, or equivalent) inside GenerateTokenModal so submissions are
blocked when assignee validation fails.

---

Outside diff comments:
In
`@apps/customer-portal/webapp/src/features/settings/components/RegenerateTokenModal.tsx`:
- Around line 66-74: The modal can be dismissed while a regeneration is pending;
update the close flow to prevent dismissal during regenerateMutation.isPending
by guarding the handleClose logic and any title-bar/Dialog onClose handlers: in
the handleClose function (and any click handler wired to the title-bar close
control or Dialog onClose), first check regenerateMutation.isPending and if true
simply return (or no-op) so you don't call setSecret, setFullTokenName,
setShowSecret, setCopiedSecret, setCopiedName, regenerateMutation.reset, or
onClose while the mutation is in-flight; also ensure the title-bar close control
is disabled or its handler respects the same pending check.

In
`@apps/customer-portal/webapp/src/features/settings/components/SettingsRegistryTokens.tsx`:
- Around line 112-118: The table loading state currently uses only isLoading
from useSearchRegistryTokens, which misses background refetches; update the
calculation of isTableLoading in the SettingsRegistryTokens component to include
isFetching (e.g., const isTableLoading = isLoading || isFetching) so that
background refetches show the loading/disabled table UX; locate the
isTableLoading definition near useSearchRegistryTokens and adjust it to combine
isFetching with isLoading.
- Around line 517-559: The MenuItem elements must be direct children of the Menu
to preserve MUI keyboard navigation; remove the outer Tooltip and wrapping
<span> around the MenuItem so the MenuItem remains a direct child while keeping
disabled={isRestricted} and the onClick handlers (setRegenerateToken,
setDeleteToken, setMenuAnchor, setMenuToken) intact. If you still need a
tooltip, move the Tooltip down to wrap a non-focusable child (e.g., wrap the
RefreshCw and Trash2 icons or the ListItemIcon) or use the MenuItem title
attribute, but do not wrap the MenuItem itself.
🪄 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: 193f6b2f-59b6-4796-bec6-09db15bda964

📥 Commits

Reviewing files that changed from the base of the PR and between 2660c51 and bf83d85.

📒 Files selected for processing (5)
  • apps/customer-portal/webapp/src/features/settings/components/GenerateTokenModal.tsx
  • apps/customer-portal/webapp/src/features/settings/components/RegenerateTokenModal.tsx
  • apps/customer-portal/webapp/src/features/settings/components/SettingsRegistryTokens.tsx
  • apps/customer-portal/webapp/src/features/settings/types/registryTokens.ts
  • apps/customer-portal/webapp/src/features/settings/types/settings.ts

@shayanmalinda
shayanmalinda merged commit f4dcbca into wso2-open-operations:main Apr 30, 2026
1 check was pending
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