Skip to content

Conversation

@DakshithaS
Copy link
Contributor

@DakshithaS DakshithaS commented Dec 4, 2025

Purpose

Refactor management portal codebase to standardize portal property names from isActive to isEnabled, improve code consistency with single quotes, enhance error handling with centralized API error parsing, and update type definitions for better maintainability.

Goals

  • Standardize portal activation property naming across all components and API interfaces
  • Improve code formatting consistency by converting double quotes to single quotes
  • Implement centralized error handling for API operations
  • Remove deprecated type definitions and consolidate interfaces
  • Enhance type safety and developer experience

Approach

  • Updated all portal-related interfaces and types to use isEnabled instead of isActive
  • Converted all string literals from double quotes to single quotes across the codebase
  • Created new apiErrorUtils.ts utility for consistent API error parsing
  • Enhanced API hooks (devportals.ts, apiPublish.ts) with improved error handling using parseApiError
  • Updated React components (PortalCard.tsx, PortalList.tsx, PortalManagement.tsx, etc.) to use new property names
  • Removed deprecated types/portal.ts file and moved interfaces to appropriate hook files
  • Improved form validation and URL validation in portal creation/editing
  • Updated mapper functions to use consistent default values

User stories

  • As a developer, I want consistent property naming so that portal activation state is clear and predictable
  • As a developer, I want centralized error handling so that API failures are handled consistently across the application
  • As a developer, I want improved type safety so that I can catch type errors during development

Documentation

N/A - Internal refactoring with no user-facing changes

Automation tests

  • Unit tests

    Code coverage information

  • Integration tests

    Details about the test cases and coverage

Security checks

Samples

N/A - Internal refactoring

Related PRs

N/A

Test environment

  • JDK versions: N/A
  • Operating systems: macOS
  • Databases: N/A
  • Browser/versions: N/A

Summary by CodeRabbit

  • New Features

    • Portal visibility (Public/Private) option and explicit enabled state shown across the UI
    • Clear published APIs action to reset and refresh publish listings
  • Improvements

    • Stronger form validation and centralized error messages; API key masking respected in edit flows
    • Updated card/list layouts, logo block, truncated URL display, refined empty-state text
    • Publishing status now shows PUBLISHED / FAILED / UNKNOWN / PENDING
  • Bug Fixes

    • Prevented saving masked/placeholder API keys as real keys

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Walkthrough

Backend: UpdateDevPortal now guards APIKey assignment to avoid persisting masked values. Frontend: portal types moved into feature modules; ApiPublish and DevPortal contexts/hooks reworked (array-shaped publications, new methods), parseApiError added, many UI components updated to use new payloads/fields and isEnabled semantics.

Changes

Cohort / File(s) Summary
Backend API key guard
platform-api/src/internal/service/devportal_service.go
UpdateDevPortal only assigns APIKey when provided and strings.Trim(*req.APIKey, "*") != "", preventing masked/placeholder API keys from being persisted.
Types relocated & expanded
portals/management-portal/src/types/portal.ts (deleted), portals/management-portal/src/hooks/devportals.ts
Centralized types/portal.ts removed; portal type definitions moved into feature hooks and expanded. New/changed exports include Portal (interface), PortalUIModel, CreatePortalPayload, UpdatePortalPayload (Partial), PortalListResponse, DevPortalResponse, and related aliases.
Publish & devportal hooks
portals/management-portal/src/hooks/apiPublish.ts, portals/management-portal/src/hooks/devportals.ts
Error handling switched to parseApiError. Publish/unpublish response shapes retyped to { success, message, timestamp }. ApiPublicationWithPortal.isActiveisEnabled. Publications list typing exported; API calls and return shapes adjusted.
Context refactor (ApiPublish & DevPortal)
portals/management-portal/src/context/ApiPublishContext.tsx, portals/management-portal/src/context/DevPortalContext.tsx
ApiPublishContext: publishedApis now an array, error removed, added getPublication(devPortalId) and clearPublishedApis(). DevPortalContext: removed error, method signatures use uuid, added lastFetchedOrgRef to avoid redundant fetches; provider/hook surfaces updated.
API error utility
portals/management-portal/src/utils/apiErrorUtils.ts
Added parseApiError(response, operation) to extract JSON/text error details and return a user-friendly string.
Portal forms, list, card
portals/management-portal/src/pages/portals/PortalForm.tsx, .../PortalList.tsx, .../PortalCard.tsx
PortalForm now uses CreatePortalPayload/UpdatePortalPayload, adds visibility, URL validation, and API key masking handling. PortalList props changed (error removed, onCreateNew added). PortalCard uses enabled (isEnabled) and new PortalCardProps.
Portal management & overview pages
portals/management-portal/src/pages/PortalManagement.tsx, portals/management-portal/src/pages/overview/widgets/APIPortalWidget.tsx
PortalManagement adapted to new payload types, routing helpers, and masked API key handling. APIPortalWidget formatting simplified and explicit isActive filter removed.
API publish UI & mapper
portals/management-portal/src/pages/apis/ApiPublish.tsx, .../DevPortalDeployCard.tsx, .../DevPortalPickTable.tsx, .../mapper.ts
ApiPublish now calls clearPublishedApis() before refresh; published lookups use flat publishedApis. DevPortalDeployCard layout reworked and relativeTime prop removed. DevPortalPickTable props expanded (selection, publishedIds, actions); visible portals now require isEnabled. Mapper now exports buildPublishPayload as default and sets label/policy defaults.
Time util & small UI adjustments
portals/management-portal/src/pages/overview/utils.ts, portals/management-portal/src/pages/portals/ThemeContainer.tsx, .../ThemeSettingsPanel.tsx, .../PortalPreviews.tsx, .../PromoBanner.tsx
relativeTime(d?: Date) now accepts optional Date. ThemeContainer uses local props and centralized messages; several files received quote/style/formatting updates only.
Constants & messages
portals/management-portal/src/constants/portal.ts
Added API_KEY_MASK and many new PORTAL_CONSTANTS.MESSAGES keys; adjusted type expression formatting.
Formatting-only / minor edits
portals/management-portal/src/components/ErrorBoundary.tsx, portals/management-portal/src/utils/portalUtils.ts, portals/management-portal/src/context/NotificationContext.tsx
Cosmetic formatting and quote-style changes; no behavioral changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–75 minutes

Areas to focus during review:

  • ApiPublishContext.tsx & DevPortalContext.tsx: correctness of array-shaped publishedApis, clearPublishedApis(), getPublication(), and org-change gating (lastFetchedOrgRef).
  • Removal of src/types/portal.ts: search for leftover imports/usages and confirm all consumers use relocated types.
  • Backend/frontend contract: PublishResponse/UnpublishResponse shape changes and UpdateDevPortal APIKey guard—verify end-to-end compatibility.
  • PortalForm / PortalManagement: API key masking/trim logic, URL validation, and create vs edit flows.
  • DevPortalPickTable / DevPortalDeployCard: callers accommodating removed relativeTime prop and new enabled-filtering.

Possibly related PRs

Suggested reviewers

  • thivindu
  • Induwara04

Poem

🐇 I hopped through code with a tiny hop,
Masked keys kept secret — no placeholder to stop.
Contexts now tidy, errors parsed with care,
Portals enabled, types moved here and there.
A rabbit's small dance — the repo feels lighter.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main refactoring objective—standardizing portal property names and improving code consistency—which aligns with the extensive changes across multiple files.
Description check ✅ Passed The description follows the template structure with all required sections (Purpose, Goals, Approach, User stories, Documentation, Automation tests, Security checks, Samples, Related PRs, Test environment) completed or marked as N/A. However, specific test coverage details, security check verification answers, and FindSecurityBugs confirmation are incomplete.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

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

@DakshithaS DakshithaS changed the title Refactor/portal property standardization Standardize portal property names and improve code consistency Dec 4, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (2)
portals/management-portal/src/pages/portals/PortalForm.tsx (1)

129-138: Error message doesn't account for edit mode.

The fallback error message is "Failed to create Developer Portal." regardless of whether the form is in create or edit mode. This could confuse users editing a portal.

       const errorMessage =
         error instanceof Error
           ? error.message
-          : 'Failed to create Developer Portal.';
+          : isEdit
+            ? 'Failed to update Developer Portal.'
+            : 'Failed to create Developer Portal.';
       showNotification(errorMessage, 'error');
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (1)

34-47: Unused prop: relativeTime is defined but never used.

The relativeTime function is declared in Props and destructured but is never called in the component. Either remove it from the props or utilize it (e.g., for displaying relative timestamps for publication.publishedAt).

 type Props = {
   portal: ApiPublicationWithPortal;
   apiId: string;
   publishingIds: Set<string>;
-  relativeTime: (d?: Date) => string;
   onPublish: (portal: ApiPublicationWithPortal) => void;
 };

 const DevPortalDeployCard: React.FC<Props> = ({
   portal,
   apiId,
   publishingIds,
-  onPublish,
+  onPublish,
 }) => {

Or if it should be used, apply it to display relative time for publications.

🧹 Nitpick comments (13)
platform-api/src/internal/service/devportal_service.go (1)

344-346: API key masking guard works but clarifying “clear” semantics and edge cases would help

The new condition correctly prevents a masked placeholder (all *) from overwriting the stored API key. A couple of behavioral nuances are worth calling out:

  • This also prevents clearing the API key via an empty string: if the frontend ever tries to “remove” the key by sending "", the update will be ignored and the existing key retained. If the product should support explicitly clearing the key (similar to how some fields are intentionally clearable via empty strings), this path will need a separate flag or different condition. Based on learnings, description updates are explicitly allowed to clear via empty strings, so the asymmetry here should be deliberate.
  • Inputs like "******** " or " " (trailing/only spaces) will pass the current check and overwrite the real key with a non-usable value. If the UI might ever add whitespace, you may want to normalize with TrimSpace before the Trim('*') check, or centralize this logic in a small helper so frontend/backend stay consistent on what counts as a “masked” value.

If the intent is “only update when a real, non-empty, non-placeholder key is provided and never allow clearing”, then this is fine as-is; otherwise, consider tightening the predicate or adding explicit “clear key” semantics.

portals/management-portal/src/pages/portals/PortalCard.tsx (1)

69-70: Empty testId props should be meaningful or removed.

Passing empty strings to testId provides no testing value. Either assign meaningful identifiers for test automation or remove them:

-    <Card testId={''} style={{ maxWidth: 450 }}>
-      <CardActionArea onClick={onClick} testId={''}>
+    <Card testId="portal-card" style={{ maxWidth: 450 }}>
+      <CardActionArea onClick={onClick} testId="portal-card-action">
portals/management-portal/src/pages/portals/PortalList.tsx (2)

20-28: Unused error prop in interface.

The error prop is defined in PortalListProps (line 23) but is never destructured or used in the component. Consider removing it if error handling is managed elsewhere, or implement the error display if it's intended functionality.

 interface PortalListProps {
   portals: Portal[];
   loading: boolean;
-  error?: string | null;
   onPortalClick: (portalId: string) => void;
   onPortalActivate: (portalId: string) => void;
   onPortalEdit: (portalId: string) => void;
   onCreateNew: () => void;
 }

192-203: Redundant dialog close calls.

confirmationDialog.onConfirm() (line 196) already closes the dialog via line 92, but closeConfirmationDialog() (line 197) is called immediately after. While this is harmless, it's redundant. Consider either removing the second close call or restructuring so the dialog's onConfirm only runs the action without closing.

       <ConfirmationDialog
         open={confirmationDialog.open}
         onClose={closeConfirmationDialog}
-        onConfirm={() => {
-          confirmationDialog.onConfirm();
-          closeConfirmationDialog();
-        }}
+        onConfirm={confirmationDialog.onConfirm}
         title={confirmationDialog.title}
         message={confirmationDialog.message}
         confirmText={confirmationDialog.confirmText}
         severity={confirmationDialog.severity}
       />
portals/management-portal/src/hooks/apiPublish.ts (1)

68-76: Weak typing on list property.

Using any[] for the list property defeats the purpose of TypeScript's type safety. Consider using a more specific type.

 export type PublicationsListResponse = {
   count: number;
-  list: any[];
+  list: ApiPublicationWithPortal[];
   pagination?: {
     total: number;
     offset: number;
     limit: number;
   } | null;
 };
portals/management-portal/src/pages/apis/ApiPublish/DevPortalPickTable.tsx (1)

61-65: Comment-code mismatch: "active" vs "isEnabled".

The comment on line 61 says "active portals" but the code now filters by isEnabled. Update the comment to maintain consistency with the property name standardization.

-  // Only show NOT published AND active portals
+  // Only show NOT published AND enabled portals
   const visiblePortals = React.useMemo(
     () => portals.filter((p) => !publishedSet.has(p.uuid) && p.isEnabled),
     [portals, publishedSet]
   );
portals/management-portal/src/pages/apis/ApiPublish.tsx (1)

256-288: Consider extracting portal-to-publication mapping to a reusable utility.

The inline construction of ApiPublicationWithPortal from Portal is verbose. A mapper function (possibly in ApiPublish/mapper.ts) would improve maintainability and ensure consistency if this transformation is needed elsewhere.

Example extraction:

// In mapper.ts
export const portalToUnpublishedPublication = (portal: Portal): ApiPublicationWithPortal => ({
  uuid: portal.uuid,
  name: portal.name,
  identifier: portal.identifier,
  description: portal.description,
  portalUrl: portal.uiUrl || '',
  apiUrl: portal.apiUrl || '',
  hostname: portal.hostname || '',
  isEnabled: portal.isEnabled || false,
  createdAt: portal.createdAt || '',
  updatedAt: portal.updatedAt || '',
  associatedAt: '',
  isPublished: false,
  publication: {
    status: 'UNPUBLISHED',
    apiVersion: '',
    sandboxEndpoint: '',
    productionEndpoint: '',
    publishedAt: '',
    updatedAt: '',
  },
});
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (3)

53-67: Consider using constants for status values.

The status comparison uses lowercase strings ('published', 'failed') from the API while displaying uppercase ('PUBLISHED', 'FAILED'). Consider using constants from PORTAL_CONSTANTS for consistency and maintainability.


109-116: Remove commented-out code.

The commented-out logoSrc conditional is leftover development code. Remove it for cleaner code.

-              {/* {logoSrc ? ( */}
               <Box
                 component="img"
                 src={BijiraDPLogo}
                 alt="Bijira Dev Portal Logo"
                 sx={{ width: 90, height: 90, objectFit: 'contain' }}
               />
-              {/* ) : null} */}

264-264: Use single quotes for consistency.

The button text uses double quotes, inconsistent with the PR's single-quote standardization.

-          {isPublishingThis ? "Adding…" : "Add API"}
+          {isPublishingThis ? 'Adding…' : 'Add API'}
portals/management-portal/src/hooks/devportals.ts (1)

1-1: File header comment mismatch.

The comment says useDevPortalsApi.ts but the file is devportals.ts. Update for accuracy.

-// 📁 useDevPortalsApi.ts
+// 📁 devportals.ts
portals/management-portal/src/context/ApiPublishContext.tsx (2)

101-106: Consider logging refresh failures.

While silently ignoring refresh errors after a successful publish is acceptable for UX, consider logging the error for debugging purposes.

         try {
           await refreshPublishedApis(apiId);
         } catch {
-          /* ignore refresh error */
+          // Publish succeeded but refresh failed - user may need to manually refresh
+          console.warn('Failed to refresh publications after publish');
         }

198-203: Use single quotes for consistency.

The error message uses double quotes, inconsistent with the PR's single-quote standardization.

 export const useApiPublishing = () => {
   const ctx = useContext(ApiPublishContext);
   if (!ctx) {
-    throw new Error("useApiPublishing must be used within an ApiPublishProvider");
+    throw new Error('useApiPublishing must be used within an ApiPublishProvider');
   }
   return ctx;
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b225fd1 and 8504e8b.

📒 Files selected for processing (25)
  • platform-api/src/internal/service/devportal_service.go (1 hunks)
  • portals/management-portal/src/components/ErrorBoundary.tsx (2 hunks)
  • portals/management-portal/src/constants/portal.ts (2 hunks)
  • portals/management-portal/src/context/ApiPublishContext.tsx (2 hunks)
  • portals/management-portal/src/context/DevPortalContext.tsx (2 hunks)
  • portals/management-portal/src/context/NotificationContext.tsx (4 hunks)
  • portals/management-portal/src/hooks/apiPublish.ts (3 hunks)
  • portals/management-portal/src/hooks/devportals.ts (1 hunks)
  • portals/management-portal/src/pages/PortalManagement.tsx (6 hunks)
  • portals/management-portal/src/pages/apis/ApiPublish.tsx (10 hunks)
  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (4 hunks)
  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalPickTable.tsx (13 hunks)
  • portals/management-portal/src/pages/apis/ApiPublish/mapper.ts (3 hunks)
  • portals/management-portal/src/pages/overview/utils.ts (1 hunks)
  • portals/management-portal/src/pages/overview/widgets/APIPortalWidget.tsx (8 hunks)
  • portals/management-portal/src/pages/portals/PortalCard.tsx (9 hunks)
  • portals/management-portal/src/pages/portals/PortalForm.tsx (7 hunks)
  • portals/management-portal/src/pages/portals/PortalList.tsx (6 hunks)
  • portals/management-portal/src/pages/portals/PortalPreviews.tsx (6 hunks)
  • portals/management-portal/src/pages/portals/PromoBanner.tsx (4 hunks)
  • portals/management-portal/src/pages/portals/ThemeContainer.tsx (4 hunks)
  • portals/management-portal/src/pages/portals/ThemeSettingsPanel.tsx (4 hunks)
  • portals/management-portal/src/types/portal.ts (0 hunks)
  • portals/management-portal/src/utils/apiErrorUtils.ts (1 hunks)
  • portals/management-portal/src/utils/portalUtils.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • portals/management-portal/src/types/portal.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-11-05T05:26:25.128Z
Learnt from: thivindu
Repo: wso2/api-platform PR: 101
File: platform-api/src/internal/handler/project.go:169-169
Timestamp: 2025-11-05T05:26:25.128Z
Learning: In platform-api/src/internal/handler/project.go UpdateProject handler, the UpdateProjectRequest allows empty description field by design. This is intentional to provide a means to clear/empty the description of a project. The frontend is responsible for populating the existing description value when the user is not modifying it, implementing a full-replacement update pattern rather than partial/PATCH-style updates.

Applied to files:

  • platform-api/src/internal/service/devportal_service.go
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/components/ErrorBoundary.tsx
  • portals/management-portal/src/utils/portalUtils.ts
  • portals/management-portal/src/pages/portals/PortalPreviews.tsx
  • portals/management-portal/src/pages/portals/PortalForm.tsx
  • portals/management-portal/src/pages/portals/ThemeSettingsPanel.tsx
  • portals/management-portal/src/pages/portals/PromoBanner.tsx
  • portals/management-portal/src/pages/portals/PortalCard.tsx
  • portals/management-portal/src/context/DevPortalContext.tsx
  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalPickTable.tsx
  • portals/management-portal/src/pages/portals/PortalList.tsx
  • portals/management-portal/src/pages/overview/widgets/APIPortalWidget.tsx
  • portals/management-portal/src/pages/portals/ThemeContainer.tsx
  • portals/management-portal/src/pages/PortalManagement.tsx
📚 Learning: 2025-11-06T07:04:01.101Z
Learnt from: DakshithaS
Repo: wso2/api-platform PR: 105
File: platform-api/src/internal/model/api_publication.go:63-79
Timestamp: 2025-11-06T07:04:01.101Z
Learning: In the API Platform codebase (platform-api/src/internal/model/api_publication.go), the PublicationStateMachine intentionally disallows Published → Publishing transitions. The design requires APIs to be explicitly unpublished before they can be republished, enforcing the lifecycle: Published → Unpublishing → Unpublished → Publishing → Published. This prevents accidental overwrites and ensures proper cleanup.

Applied to files:

  • portals/management-portal/src/hooks/apiPublish.ts
  • portals/management-portal/src/pages/apis/ApiPublish.tsx
🧬 Code graph analysis (10)
portals/management-portal/src/utils/apiErrorUtils.ts (1)
platform-api/src/internal/dto/openapi_spec.go (1)
  • Response (105-108)
portals/management-portal/src/pages/apis/ApiPublish/mapper.ts (1)
portals/management-portal/src/hooks/apiPublish.ts (1)
  • ApiPublishPayload (46-66)
portals/management-portal/src/pages/portals/PortalForm.tsx (4)
portals/management-portal/src/hooks/devportals.ts (2)
  • CreatePortalPayload (47-56)
  • UpdatePortalPayload (59-59)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-53)
portals/management-portal/src/context/NotificationContext.tsx (1)
  • useNotifications (68-76)
portals/management-portal/src/utils/portalUtils.ts (1)
  • slugify (110-117)
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (1)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-53)
portals/management-portal/src/pages/portals/PortalCard.tsx (1)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-53)
portals/management-portal/src/context/DevPortalContext.tsx (2)
portals/management-portal/src/hooks/devportals.ts (4)
  • Portal (10-27)
  • CreatePortalPayload (47-56)
  • UpdatePortalPayload (59-59)
  • useDevPortalsApi (82-213)
portals/management-portal/src/context/NotificationContext.tsx (1)
  • useNotifications (68-76)
portals/management-portal/src/hooks/apiPublish.ts (1)
portals/management-portal/src/utils/apiErrorUtils.ts (1)
  • parseApiError (5-34)
portals/management-portal/src/pages/overview/widgets/APIPortalWidget.tsx (1)
portals/management-portal/src/context/DevPortalContext.tsx (1)
  • useDevPortals (225-231)
portals/management-portal/src/hooks/devportals.ts (1)
portals/management-portal/src/utils/apiErrorUtils.ts (1)
  • parseApiError (5-34)
portals/management-portal/src/pages/PortalManagement.tsx (4)
portals/management-portal/src/context/DevPortalContext.tsx (1)
  • useDevPortals (225-231)
portals/management-portal/src/utils/portalUtils.ts (4)
  • getPortalMode (67-87)
  • getPortalIdFromPath (92-105)
  • navigateToPortalTheme (43-50)
  • navigateToPortalEdit (55-62)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-53)
portals/management-portal/src/hooks/devportals.ts (2)
  • CreatePortalPayload (47-56)
  • UpdatePortalPayload (59-59)
🔇 Additional comments (36)
portals/management-portal/src/pages/portals/PromoBanner.tsx (1)

1-67: Quote standardization applied cleanly—LGTM.

All changes are cosmetic quote conversions (double → single) consistent with the PR's formatting standardization goals. No functional, behavioral, or type-safety modifications; component structure and logic remain intact.

portals/management-portal/src/utils/portalUtils.ts (1)

21-27: ✓ Formatting refactoring is clean and consistent.

The multi-line parameter formatting across navigation functions and the conditional refactoring in getPortalIdFromPath improve readability and align with the PR's standardization goals. All type annotations are maintained, and the utility patterns remain effective—particularly the reuse of getPortalListPath to avoid duplication.

Also applies to: 32-38, 43-50, 55-62, 98-103

portals/management-portal/src/pages/portals/ThemeSettingsPanel.tsx (1)

1-59: LGTM! Formatting standardization applied consistently.

All changes in this file are cosmetic, converting double-quoted strings to single quotes across import statements, string literals, and MUI selectors. This aligns with the PR's objective of improving formatting consistency across the codebase.

portals/management-portal/src/pages/portals/ThemeContainer.tsx (2)

10-14: LGTM! Type localization improves modularity.

Moving ThemeContainerProps from the external types module to a local declaration aligns well with the PR objectives to decentralize types. The interface signature remains unchanged, preserving the component's public API while improving code organization.


31-32: LGTM! Formatting consistency improved.

The multi-line error message formatting enhances readability while preserving the existing error handling logic.

Also applies to: 43-44

portals/management-portal/src/constants/portal.ts (1)

56-59: LGTM! Type helper formatting improvements.

The parentheses around the indexed access types improve readability and follow TypeScript conventions for complex type expressions.

portals/management-portal/src/context/NotificationContext.tsx (1)

19-65: LGTM! Formatting standardization with no behavioral changes.

The component logic remains sound with proper useCallback memoization and context error handling.

portals/management-portal/src/components/ErrorBoundary.tsx (1)

1-73: LGTM! Formatting-only changes.

Error boundary implementation remains correct with proper lifecycle methods for error capture and state reset.

portals/management-portal/src/pages/portals/PortalPreviews.tsx (1)

1-119: LGTM! Formatting standardization applied consistently.

Quote style and spacing adjustments maintain the existing preview component behavior.

portals/management-portal/src/pages/portals/PortalCard.tsx (2)

23-38: Well-structured interface with clear prop naming.

The PortalCardProps interface properly documents the component's API, and the enabled naming aligns with the PR's standardization goals.


158-213: URL handling logic correctly gates access based on enabled state.

The conditional rendering for tooltips, link behavior, and icon button states properly prevents interaction when the portal is not enabled.

portals/management-portal/src/pages/overview/utils.ts (1)

16-17: Good defensive improvement with optional parameter.

Returning an empty string for undefined input is a safe fallback. Ensure callers expecting a non-empty time string handle this case appropriately in the UI.

portals/management-portal/src/pages/overview/widgets/APIPortalWidget.tsx (1)

119-123: Good security practice with noopener,noreferrer.

The window.open call correctly uses noopener,noreferrer to prevent the opened page from accessing the opener window, mitigating potential tabnabbing attacks.

portals/management-portal/src/pages/portals/PortalList.tsx (1)

72-102: Activation flow handles async correctly with proper cleanup.

The requestActivate callback properly uses a try/finally block to ensure activating state is cleaned up regardless of success or failure, and the dialog is closed after activation completes.

portals/management-portal/src/hooks/apiPublish.ts (2)

104-110: Consistent error handling with parseApiError.

Good standardization—all API methods now use parseApiError for consistent error message extraction, improving the user experience when API calls fail.


147-161: Verify if devPortalId is required in both URL path and request body.

The devPortalId is included in the URL path (line 152) and duplicated in the request body (line 159). This pattern is unusual—typically path parameters aren't repeated in the body. Confirm whether the API actually requires this duplication, as it may indicate either intentional defensive design or an error where the body should contain different data.

portals/management-portal/src/pages/portals/PortalForm.tsx (2)

85-93: URL validation is thorough and well-implemented.

The validation correctly:

  1. Ensures the URL is well-formed via new URL()
  2. Restricts protocols to http: or https:
  3. Rejects trailing slashes

This prevents common URL format issues.


112-117: Good practice: Stripping masked API key from update payload.

Correctly removes the masked placeholder from the payload before submission, ensuring the backend won't receive or process the mask as an actual key value.

portals/management-portal/src/pages/PortalManagement.tsx (1)

180-193: Critical: API key mask mismatch between components.

The masked apiKey is set to '**********' (10 asterisks) here, but PortalForm.tsx (lines 84 and 114) checks for '*****' (5 asterisks). This mismatch will cause:

  1. The form validation to incorrectly require a new API key in edit mode
  2. The API key to be included in update requests when it shouldn't be

Either update this mask to match PortalForm's expectation:

             initialData={
               selectedPortal
                 ? {
                     name: selectedPortal.name,
                     identifier: selectedPortal.identifier,
                     description: selectedPortal.description,
                     apiUrl: selectedPortal.apiUrl,
                     hostname: selectedPortal.hostname,
-                    apiKey: '**********', // Masked for security
+                    apiKey: '*****', // Masked for security
                     headerKeyName: selectedPortal.headerKeyName,
                     visibility: selectedPortal.visibility,
                   }
                 : undefined
             }

Or define a shared constant (recommended):

// In constants/portal.ts
export const MASKED_API_KEY = '*****';
⛔ Skipped due to learnings
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.
portals/management-portal/src/pages/apis/ApiPublish/DevPortalPickTable.tsx (2)

18-31: LGTM on Props interface expansion.

The Props interface cleanly documents optional props and API compatibility notes. The component correctly computes its own allSelected/someSelected internally based on visible portals, which is the right approach since the parent may not know which portals are filtered out.


81-92: LGTM on select-all logic.

The handleToggleAllVisible correctly scopes selection toggling to only visible (unpublished + enabled) portals, preventing unintended selection of filtered-out items.

portals/management-portal/src/pages/apis/ApiPublish.tsx (3)

104-113: LGTM on clear-before-refresh pattern.

Calling clearPublishedApis() before refreshPublishedApis(effectiveApiId) ensures a clean state, preventing stale data from a previous API from mixing with the newly fetched publications.


432-438: LGTM on provider composition.

The component correctly wraps DevelopContent with both DevPortalProvider and ApiPublishProvider, ensuring the necessary context is available.


123-130: Ensure callback dependencies are memoized with useCallback.

The effect depends on multiple callback functions (fetchApiById, selectApi, refreshDevPortals, refreshPublishedApis, clearPublishedApis). These should be wrapped in useCallback in their respective hooks or contexts to prevent unnecessary effect re-runs and maintain referential stability across renders.

portals/management-portal/src/hooks/devportals.ts (3)

10-27: LGTM on Portal interface standardization.

The Portal interface correctly uses isEnabled instead of isActive, aligning with the PR's property standardization goal. All relevant fields are properly typed.


47-59: LGTM on payload types.

CreatePortalPayload defines all required fields for portal creation, and UpdatePortalPayload correctly uses Partial<CreatePortalPayload> to allow partial updates. The apiKey field in updates enables key rotation.


91-94: LGTM on centralized error handling.

Consistent use of parseApiError across all API operations ensures uniform error messages. Including the operation context (e.g., 'fetch devportal (${uuid})') aids debugging.

portals/management-portal/src/context/ApiPublishContext.tsx (2)

26-43: LGTM on context type definition.

The ApiPublishContextValue type is well-structured with clear function signatures. The shift from a per-API map to a flat ApiPublicationWithPortal[] simplifies the interface while clearPublishedApis enables clean state resets.


150-162: LGTM on organization switch handling.

Using prevOrgRef to track the previous organization prevents unnecessary state clears during transient loading states. The effect correctly clears data only when the organization actually changes.

portals/management-portal/src/context/DevPortalContext.tsx (7)

1-19: LGTM!

Clean import organization. The addition of useRef for the fetch optimization pattern and the integration with useOrganization/useNotifications contexts aligns well with the centralized error handling approach.


25-38: LGTM!

The interface cleanly exposes uuid-based methods with proper payload types. The removal of the error field in favor of centralized notification handling is a good separation of concerns.


48-73: LGTM!

Good pattern: renaming the API methods (e.g., createRequest, updateRequest) avoids naming collisions with the context's public methods. The handleError callback properly extracts error messages and delegates to the notification system.


77-90: LGTM!

The refreshDevPortals implementation correctly handles the loading state and returns an empty array on error, which is a sensible default for list data. The error is surfaced via notification.


135-152: Verify intended behavior: new portals are prepended to the list.

When fetchDevPortalById retrieves a portal not already in state, it prepends to the beginning of the list (line 143). If ordering matters (e.g., sorted by createdAt), this could lead to inconsistent display until the next full refresh.

If insertion order is intentional for visibility, this is fine. Otherwise, consider triggering a full refresh or inserting at the correct position.


191-218: LGTM!

Proper use of useMemo to stabilize the context value. All dependencies are correctly specified, preventing unnecessary re-renders of consumers.


225-231: LGTM!

Standard and correct pattern for context consumption. The descriptive error message aids debugging when the hook is used outside the provider.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
portals/management-portal/src/pages/portals/PortalForm.tsx (1)

85-93: Consider validating URL format before trailing slash check.

The validation logic on line 93 checks !formData.apiUrl.trim().endsWith('/') before calling isValidUrl. If the URL is invalid (e.g., not a URL at all), the trailing slash check will still pass, potentially causing confusion.

Consider reordering the checks:

-    const apiUrlValid = formData.apiUrl.trim() && !formData.apiUrl.trim().endsWith('/') && isValidUrl(formData.apiUrl.trim());
+    const trimmedUrl = formData.apiUrl.trim();
+    const apiUrlValid = trimmedUrl && isValidUrl(trimmedUrl) && !trimmedUrl.endsWith('/');

This ensures URL format is validated first, then checks for trailing slash on known-valid URLs. It also avoids multiple .trim() calls.

portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (1)

34-40: Unused relativeTime prop in type definition.

The relativeTime prop is declared in the Props type but is never destructured or used in the component body. Either remove it from the type or destructure and use it where needed.

 type Props = {
   portal: ApiPublicationWithPortal;
   apiId: string;
   publishingIds: Set<string>;
-  relativeTime: (d?: Date) => string;
   onPublish: (portal: ApiPublicationWithPortal) => void;
 };
portals/management-portal/src/pages/PortalManagement.tsx (1)

108-131: Consider type refinement for handleCreatePortal.

The function accepts CreatePortalPayload | UpdatePortalPayload but immediately casts to CreatePortalPayload (line 110). Since this handler is only used for creation (line 169), consider accepting only CreatePortalPayload directly to avoid the cast.

   const handleCreatePortal = React.useCallback(
-    async (formData: CreatePortalPayload | UpdatePortalPayload) => {
-      const fullData = formData as CreatePortalPayload;
+    async (formData: CreatePortalPayload) => {
       setCreatingPortal(true);
       try {
-        const createdPortal = await createDevPortal(fullData);
+        const createdPortal = await createDevPortal(formData);
portals/management-portal/src/context/DevPortalContext.tsx (1)

136-153: Review the update-or-prepend logic in fetchDevPortalById.

The logic at lines 141-144 checks if a portal with portal.uuid exists, but then updates by matching p.uuid === uuid (the parameter). If portal.uuid differs from the uuid parameter (unlikely but possible if API returns different data), this could cause unexpected behavior. Consider using consistent identifiers:

       setDevportals((prev) => {
-        const existing = prev.find((p) => p.uuid === portal.uuid);
+        const existingIndex = prev.findIndex((p) => p.uuid === portal.uuid);
         return existing
-          ? prev.map((p) => (p.uuid === uuid ? { ...p, ...portal } : p))
+        return existingIndex >= 0
+          ? prev.map((p) => (p.uuid === portal.uuid ? { ...p, ...portal } : p))
           : [portal, ...prev];
       });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8504e8b and 5f1bddf.

📒 Files selected for processing (15)
  • portals/management-portal/src/constants/portal.ts (3 hunks)
  • portals/management-portal/src/context/ApiPublishContext.tsx (2 hunks)
  • portals/management-portal/src/context/DevPortalContext.tsx (2 hunks)
  • portals/management-portal/src/hooks/apiPublish.ts (3 hunks)
  • portals/management-portal/src/hooks/devportals.ts (1 hunks)
  • portals/management-portal/src/pages/PortalManagement.tsx (6 hunks)
  • portals/management-portal/src/pages/apis/ApiPublish/ApiPublishModal.tsx (3 hunks)
  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (5 hunks)
  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalPickTable.tsx (13 hunks)
  • portals/management-portal/src/pages/overview/widgets/APIPortalWidget.tsx (8 hunks)
  • portals/management-portal/src/pages/portals/PortalCard.tsx (9 hunks)
  • portals/management-portal/src/pages/portals/PortalForm.tsx (7 hunks)
  • portals/management-portal/src/pages/portals/PortalList.tsx (7 hunks)
  • portals/management-portal/src/pages/portals/ThemeContainer.tsx (4 hunks)
  • portals/management-portal/src/utils/apiErrorUtils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • portals/management-portal/src/pages/portals/ThemeContainer.tsx
  • portals/management-portal/src/pages/overview/widgets/APIPortalWidget.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalPickTable.tsx
  • portals/management-portal/src/pages/portals/PortalCard.tsx
  • portals/management-portal/src/context/DevPortalContext.tsx
  • portals/management-portal/src/pages/PortalManagement.tsx
  • portals/management-portal/src/pages/portals/PortalList.tsx
  • portals/management-portal/src/pages/portals/PortalForm.tsx
📚 Learning: 2025-11-06T07:04:01.101Z
Learnt from: DakshithaS
Repo: wso2/api-platform PR: 105
File: platform-api/src/internal/model/api_publication.go:63-79
Timestamp: 2025-11-06T07:04:01.101Z
Learning: In the API Platform codebase (platform-api/src/internal/model/api_publication.go), the PublicationStateMachine intentionally disallows Published → Publishing transitions. The design requires APIs to be explicitly unpublished before they can be republished, enforcing the lifecycle: Published → Unpublishing → Unpublished → Publishing → Published. This prevents accidental overwrites and ensures proper cleanup.

Applied to files:

  • portals/management-portal/src/hooks/apiPublish.ts
🧬 Code graph analysis (6)
portals/management-portal/src/pages/apis/ApiPublish/ApiPublishModal.tsx (1)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-68)
portals/management-portal/src/pages/portals/PortalCard.tsx (1)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-68)
portals/management-portal/src/hooks/apiPublish.ts (1)
portals/management-portal/src/utils/apiErrorUtils.ts (1)
  • parseApiError (5-34)
portals/management-portal/src/context/ApiPublishContext.tsx (1)
portals/management-portal/src/hooks/apiPublish.ts (5)
  • ApiPublicationWithPortal (30-44)
  • ApiPublishPayload (46-66)
  • PublishResponse (9-13)
  • UnpublishResponse (15-19)
  • useApiPublishApi (91-178)
portals/management-portal/src/hooks/devportals.ts (1)
portals/management-portal/src/utils/apiErrorUtils.ts (1)
  • parseApiError (5-34)
portals/management-portal/src/pages/PortalManagement.tsx (4)
portals/management-portal/src/context/DevPortalContext.tsx (1)
  • useDevPortals (229-235)
portals/management-portal/src/utils/portalUtils.ts (4)
  • getPortalMode (67-87)
  • getPortalIdFromPath (92-105)
  • navigateToPortalTheme (43-50)
  • navigateToPortalEdit (55-62)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-68)
portals/management-portal/src/hooks/devportals.ts (2)
  • CreatePortalPayload (46-55)
  • UpdatePortalPayload (58-58)
🔇 Additional comments (28)
portals/management-portal/src/pages/apis/ApiPublish/ApiPublishModal.tsx (1)

13-13: LGTM! Centralized messaging improves consistency.

The replacement of hard-coded error messages with centralized constants from PORTAL_CONSTANTS.MESSAGES is a good improvement for maintainability and consistency across the codebase.

Also applies to: 115-115, 119-119, 129-129

portals/management-portal/src/utils/apiErrorUtils.ts (1)

5-34: LGTM! Response body handling is correct.

The implementation correctly reads the response body as text once (line 11), then attempts JSON parsing on that text. This avoids the stream-consumed-twice issue and follows best practices. The fallback chain for error message extraction is well-designed.

portals/management-portal/src/pages/portals/PortalCard.tsx (1)

23-38: LGTM! Consistent property renaming.

The renaming from isActive to enabled is applied consistently throughout the component. All conditional logic (URL navigation, tooltips, button states, styling) correctly references the new property. Accessibility features are properly maintained.

Also applies to: 56-56, 160-210, 259-260, 265-308

portals/management-portal/src/constants/portal.ts (1)

10-10: LGTM! Centralized constants improve maintainability.

The additions to PORTAL_CONSTANTS provide good centralization of default values and messages. The API_KEY_MASK constant (10 asterisks) should ensure consistency across the codebase when checking for masked API keys.

Also applies to: 40-57, 71-74

portals/management-portal/src/pages/portals/PortalForm.tsx (2)

84-84: LGTM! API key masking is correctly implemented.

The code now uses PORTAL_CONSTANTS.API_KEY_MASK consistently in both validation (line 84) and payload sanitization (lines 114-116). This prevents the masked placeholder from being sent to the backend during edits, addressing the concern from previous reviews.

Also applies to: 114-116


265-282: LGTM! Visibility field properly integrated.

The visibility field UI is well-implemented with proper accessibility (label), state management, and disabled state handling during submission.

portals/management-portal/src/pages/apis/ApiPublish/DevPortalPickTable.tsx (1)

61-65: LGTM! Filter logic correctly updated for enabled portals.

The filter on line 63 now correctly checks p.isEnabled instead of the deprecated isActive, ensuring only enabled and unpublished portals are shown. The no-data message accurately reflects this filtering logic.

Also applies to: 194-194

portals/management-portal/src/hooks/apiPublish.ts (2)

3-3: LGTM! Consistent error handling with parseApiError.

The integration of parseApiError across all three API methods (fetchPublications, publishApiToDevPortal, unpublishApiFromDevPortal) provides consistent error message extraction and handling. This aligns well with the centralized error utility pattern.

Also applies to: 104-110, 137-140, 163-166


38-38: Property rename consistently applied.

The rename from isActive to isEnabled in the ApiPublicationWithPortal type is consistent with the broader refactoring across the PR.

portals/management-portal/src/pages/portals/PortalList.tsx (2)

20-27: LGTM! Clean interface definition and property usage.

The PortalListProps interface clearly defines the component's public API with well-separated concerns. The use of portal.isEnabled (line 123) is consistent with the property rename across the codebase.

Also applies to: 123-123


71-101: LGTM! Activation flow properly manages async state.

The activation flow correctly:

  • Sets activating state before the async operation (line 80)
  • Cleans up activating state in the finally block (lines 85-89)
  • Closes the confirmation dialog after activation (line 91)
  • Handles the async operation without blocking the UI
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (3)

76-116: LGTM on the logo block restructuring.

The grid-based layout with the logo container is well-structured with proper alignment, borders, and overflow handling. The nested Box components provide good visual hierarchy.


169-186: Good use of centralized constants for accessibility.

The use of PORTAL_CONSTANTS.MESSAGES.OPEN_PORTAL_URL for the tooltip and PORTAL_CONSTANTS.ARIA_LABELS.OPEN_PORTAL_URL for the aria-label aligns well with the PR's goal of standardizing portal constants. The noopener,noreferrer security attributes on window.open are correctly applied.


196-242: Status box styling is now correct.

The previous dead code branches checking !isPublished have been removed as per the past review. The styling logic now correctly handles only the reachable states within the isPublished conditional.

portals/management-portal/src/hooks/devportals.ts (5)

9-26: Good type definition with isEnabled standardization.

The Portal interface properly uses isEnabled instead of isActive, aligning with the PR's objective to standardize portal property names. The interface is comprehensive with all necessary fields.


28-44: Consider aligning optional fields between Portal and PortalUIModel.

Portal.logoSrc and Portal.logoAlt are required (lines 17-18), while PortalUIModel.logoSrc and PortalUIModel.logoAlt are optional (lines 36-37). Ensure this asymmetry is intentional—if API responses can omit logos, Portal should also mark them optional.


81-97: LGTM on standardized error handling.

The use of parseApiError provides consistent error message extraction across all API operations. The fallback to an empty array when data.list is undefined is a safe default.


169-182: LGTM on delete operation.

The delete operation properly uses parseApiError for error handling and doesn't expect a response body, which is appropriate for DELETE requests.


184-202: LGTM on activate operation.

The activate operation is correctly implemented as a POST with proper error handling and no expected response body.

portals/management-portal/src/context/ApiPublishContext.tsx (4)

26-43: Good API surface cleanup.

The context value type is cleaner with publishedApis as a flat array instead of a Record, and the addition of getPublication and clearPublishedApis provides better control over the state.


72-73: Good use of ref for organization tracking.

Using prevOrgRef to track organization changes prevents unnecessary state clears on re-renders when the organization hasn't actually changed.


100-107: Appropriate handling of refresh failure after successful publish.

The nested try-catch correctly handles the case where publish succeeds but the subsequent refresh fails. Logging a warning is reasonable here since the primary operation succeeded.


151-163: LGTM on organization switch handling.

The effect correctly guards against clearing data when the organization hasn't changed by comparing currentOrgId with prevOrgId stored in the ref. This prevents unnecessary state resets on re-renders.

portals/management-portal/src/pages/PortalManagement.tsx (2)

133-150: LGTM on update handler with correct error message.

The handleUpdatePortal now correctly uses PORTAL_CONSTANTS.MESSAGES.UPDATE_FAILED for the fallback error message, addressing the previous review comment about using the wrong constant.


180-193: Good security practice with API key masking.

Using PORTAL_CONSTANTS.API_KEY_MASK for the apiKey field in edit mode prevents exposing the actual API key in the form. This aligns with the PR's goal of standardizing portal constants.

portals/management-portal/src/context/DevPortalContext.tsx (3)

68-74: Good centralized error handling pattern.

The handleError helper provides consistent error message extraction and notification display across all operations, reducing code duplication.


155-170: Good use of isEnabled for activation.

The activation logic correctly updates the portal's isEnabled field to true, aligning with the PR's objective of standardizing the property name from isActive to isEnabled.


184-191: LGTM on organization change handling.

The ref is now set only after refreshDevPortals() completes successfully via .then(), addressing the previous review comment about failed fetches preventing automatic retries. The .catch() handler safely ignores errors that are already captured in state.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
portals/management-portal/src/pages/portals/PortalForm.tsx (2)

130-139: Error message fallback doesn't account for edit mode.

The fallback error message is hardcoded to "Failed to create Developer Portal" but this handler runs for both create and edit operations. When an update fails, users will see an incorrect message.

       const errorMessage =
         error instanceof Error
           ? error.message
-          : 'Failed to create Developer Portal.';
+          : isEdit
+            ? 'Failed to update Developer Portal.'
+            : 'Failed to create Developer Portal.';
       showNotification(errorMessage, 'error');

164-174: Duplicate title displayed in both header and form container.

The title "Edit Portal Details" / "Add Your Portal Details" appears twice—once in the header section (line 165) and again in the form container (line 173). This creates visual redundancy. Consider removing one instance.

       {/* Form Container */}
       <Box sx={{ width: 560, maxWidth: '100%', position: 'relative' }}>
-        {/* Form Title */}
-        <Typography variant="h5" sx={{ mb: 1 }}>
-          {isEdit ? 'Edit Portal Details' : 'Add Your Portal Details'}
-        </Typography>
-
         {/* Form Fields */}
         <Grid container spacing={2}>
🧹 Nitpick comments (4)
portals/management-portal/src/pages/userScenarios/PublishPortalFlow.tsx (1)

110-132: isEnabled-based filtering for portals is correct and consistent

Deriving portals from allPortals.filter(p => p.isEnabled) cleanly scopes the publish flow to enabled portals and aligns with the PR-wide isActiveisEnabled rename. Downstream logic that computes allPublishedToActivePortals and drives the “No Active Developer Portals” state correctly operates on this filtered list.

If you want to polish naming later, you could optionally rename allPublishedToActivePortals to reflect “enabled” portals for internal consistency, but that’s non-blocking.

portals/management-portal/src/pages/portals/PortalForm.tsx (2)

83-101: URL validation is thorough; consider explicit boolean conversion for consistency.

The validation logic correctly handles the masked API key in edit mode and properly validates URL format including protocol and trailing slash. Minor nit: apiKeyValid evaluates to true or a string—explicit !!formData.apiKey.trim() would make the intent clearer, though it works correctly with the final !!() wrapper.

-    const apiKeyValid = isEdit && formData.apiKey === PORTAL_CONSTANTS.API_KEY_MASK ? true : formData.apiKey.trim();
+    const apiKeyValid = isEdit && formData.apiKey === PORTAL_CONSTANTS.API_KEY_MASK ? true : !!formData.apiKey.trim();

186-189: Consider adding meaningful test IDs for form inputs.

Multiple TextInput components have testId="". Meaningful test IDs (e.g., testId="portal-name-input") would improve component testability and enable more robust integration tests.

portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (1)

125-141: Optional: Consider standard approaches for multi-line truncation.

The current implementation uses vendor-prefixed WebKit properties (-webkit-box, WebkitLineClamp, WebkitBoxOrient) for two-line text truncation. While this works in most modern browsers, it relies on vendor-specific CSS.

Consider one of the following alternatives if broader compatibility or future-proofing is desired:

  • Use the standard line-clamp property (check browser support requirements for your project).
  • Use a utility library like react-clamp-lines for more robust cross-browser truncation.
  • Keep the current approach if it meets your browser support matrix.

The current implementation is acceptable for most use cases.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f1bddf and f77168a.

📒 Files selected for processing (4)
  • portals/management-portal/src/pages/apis/ApiPublish/ApiPublishModal.tsx (4 hunks)
  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (4 hunks)
  • portals/management-portal/src/pages/portals/PortalForm.tsx (7 hunks)
  • portals/management-portal/src/pages/userScenarios/PublishPortalFlow.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • portals/management-portal/src/pages/apis/ApiPublish/ApiPublishModal.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/userScenarios/PublishPortalFlow.tsx
  • portals/management-portal/src/pages/portals/PortalForm.tsx
🧬 Code graph analysis (1)
portals/management-portal/src/pages/portals/PortalForm.tsx (3)
portals/management-portal/src/hooks/devportals.ts (2)
  • CreatePortalPayload (46-55)
  • UpdatePortalPayload (58-58)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-68)
portals/management-portal/src/utils/portalUtils.ts (1)
  • slugify (110-117)
🔇 Additional comments (6)
portals/management-portal/src/pages/portals/PortalForm.tsx (5)

20-26: Well-defined props interface with appropriate typing.

The PortalFormProps interface cleanly separates create vs edit concerns with isEdit and properly types initialData as Partial<CreatePortalPayload> to handle partial data in edit mode.


28-58: State initialization and synchronization look correct.

The lazy initializer pattern for useState and the useEffect for syncing initialData changes are properly implemented. Default visibility of 'private' aligns with PORTAL_CONSTANTS.DEFAULT_VISIBILITY_LABEL.


60-77: Auto-generation logic correctly scoped to add mode only.

The conditional auto-generation of identifier from name using slugify is properly gated by !isEdit, ensuring identifiers remain stable during edits.


266-283: Visibility field properly implemented with type-safe value handling.

The SimpleSelect for visibility correctly uses the type assertion for the constrained union type. The field integrates well with the form state management.


302-318: Submit button provides clear feedback for both modes.

The button correctly handles disabled state, loading indicators, and contextual labels for create vs edit operations.

portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (1)

70-70: Verify that apiUrl is a valid fallback for a user-accessible portal URL.

The code falls back to portal.apiUrl when portal.portalUrl is unavailable. Confirm that apiUrl represents a user-facing portal URL and not an API endpoint. If apiUrl is an API endpoint URL, this fallback could result in displaying an incorrect or non-functional link to users.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (2)

146-189: Consider adding URL validation before opening portal.

The IconButton opens the portal URL in a new tab with proper security flags (noopener,noreferrer) and accessibility labels. However, there's no validation that portalUrl is defined or valid before calling window.open().

Apply this diff to add a defensive check:

               <IconButton
                 size="small"
                 sx={{ ml: 0.5 }}
                 onClick={(e: React.MouseEvent) => {
                   e.stopPropagation();
-                  window.open(portalUrl, '_blank', 'noopener,noreferrer');
+                  if (portalUrl) {
+                    window.open(portalUrl, '_blank', 'noopener,noreferrer');
+                  }
                 }}
                 aria-label={PORTAL_CONSTANTS.ARIA_LABELS.OPEN_PORTAL_URL}
+                disabled={!portalUrl}
               >
                 <LaunchOutlinedIcon fontSize="inherit" />
               </IconButton>

This prevents attempting to open an undefined URL and provides visual feedback when the URL is unavailable.


198-244: Consider distinct styling for PENDING and UNKNOWN states.

The styling logic treats UNKNOWN and PENDING states identically to FAILED (red error colors), which may confuse users:

  • PENDING: Indicates publication is in progress or awaiting status—not necessarily an error. Users might expect neutral (gray) or info (blue) styling.
  • UNKNOWN: Indicates an unexpected status value—might warrant warning (yellow/orange) styling to signal something unexpected without implying outright failure.
  • FAILED: Correctly uses error (red) styling.

Apply this diff to provide distinct visual treatment:

             sx={{
               backgroundColor: (t) =>
                 status === 'FAILED'
                   ? t.palette.mode === 'dark'
                     ? 'rgba(239,68,68,0.12)'
                     : '#FDECEC'
                   : success
                     ? t.palette.mode === 'dark'
                       ? 'rgba(16,185,129,0.12)'
                       : '#E8F7EC'
+                    : status === 'PENDING'
+                      ? t.palette.mode === 'dark'
+                        ? 'rgba(59,130,246,0.12)'
+                        : '#EFF6FF'
                       : t.palette.mode === 'dark'
-                        ? 'rgba(239,68,68,0.12)'
-                        : '#FDECEC',
+                        ? 'rgba(251,191,36,0.12)'
+                        : '#FFFBEB',
               border: (t) =>
                 `1px solid ${
                   status === 'FAILED'
                     ? t.palette.error.light
                     : success
                       ? '#D8EEDC'
+                      : status === 'PENDING'
+                        ? t.palette.info.light
                         : t.palette.error.light
+                        : t.palette.warning.light
                 }`,

And update the Chip color logic:

             <Chip
               label={status}
               color={
                 status === 'FAILED'
                   ? 'error'
                   : success
                     ? 'success'
+                    : status === 'PENDING'
+                      ? 'info'
                       : 'error'
+                      : 'warning'
               }
               variant={success ? 'filled' : 'outlined'}
               size="small"
             />

This provides clearer visual feedback:

  • Green: Published (success)
  • Red: Failed (error)
  • Blue: Pending (in progress)
  • Yellow/Orange: Unknown (unexpected)
portals/management-portal/src/pages/portals/PortalForm.tsx (3)

60-77: updateField generic and auto-slug logic are correct, but may override custom identifiers

The generic updateField over keyof CreatePortalPayload is nicely typed, and restricting auto-slug of identifier to add mode (!isEdit) is sensible. One functional nuance: whenever the name changes in add mode, any manual edits to identifier will be overwritten by slugify(name). If you expect users to be able to customize the identifier independently after first generation, consider tracking whether the identifier was user-touched and only auto-update while it’s still in its initial state.


83-101: Validation logic is sound; minor boolean clarity improvement possible

The apiKeyValid handling correctly treats the mask value as “still valid” in edit mode and otherwise enforces a non-empty trimmed key, which matches the API key masking semantics. URL validation via new URL with protocol checks and a trailing-slash guard is also reasonable. For readability, you might want to coerce apiUrlValid to a pure boolean (e.g., const apiUrlValid = !!trimmedUrl && isValidUrl(trimmedUrl) && !trimmedUrl.endsWith('/')) so its type doesn’t become string | boolean, though the final !!(...) already ensures isValid is strictly boolean.


266-283: Visibility select wiring is correct; consider using the payload type for the cast

The visibility field is hooked up correctly with SimpleSelect, and the two options (public / private) align with the initial default. To keep types in sync with CreatePortalPayload, you could cast using the type instead of the literal union:

-              <SimpleSelect
-                value={formData.visibility}
-                onChange={(event) => updateField('visibility', event.target.value as 'public' | 'private')}
+              <SimpleSelect
+                value={formData.visibility}
+                onChange={(event) =>
+                  updateField('visibility', event.target.value as CreatePortalPayload['visibility'])
+                }

This way any future change to the visibility union is picked up automatically here.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f77168a and 92f5f14.

📒 Files selected for processing (2)
  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (4 hunks)
  • portals/management-portal/src/pages/portals/PortalForm.tsx (12 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/portals/PortalForm.tsx
📚 Learning: 2025-12-05T00:18:04.859Z
Learnt from: DakshithaS
Repo: wso2/api-platform PR: 322
File: portals/management-portal/src/pages/apis/ApiPublish/mapper.ts:33-33
Timestamp: 2025-12-05T00:18:04.859Z
Learning: In portals/management-portal/src/pages/apis/ApiPublish/mapper.ts, the labels field is intentionally hardcoded to ['default'] because the dynamic labels feature is not yet implemented, even though FormLike.labels?: string[] exists in the interface.

Applied to files:

  • portals/management-portal/src/pages/portals/PortalForm.tsx
📚 Learning: 2025-11-06T07:04:01.101Z
Learnt from: DakshithaS
Repo: wso2/api-platform PR: 105
File: platform-api/src/internal/model/api_publication.go:63-79
Timestamp: 2025-11-06T07:04:01.101Z
Learning: In the API Platform codebase (platform-api/src/internal/model/api_publication.go), the PublicationStateMachine intentionally disallows Published → Publishing transitions. The design requires APIs to be explicitly unpublished before they can be republished, enforcing the lifecycle: Published → Unpublishing → Unpublished → Publishing → Published. This prevents accidental overwrites and ensures proper cleanup.

Applied to files:

  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx
🧬 Code graph analysis (1)
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (1)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-68)
🔇 Additional comments (7)
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (5)

1-9: LGTM! Imports are consistent and complete.

All imports use single quotes as per the standardization effort, and the new imports (IconButton, Tooltip, LaunchOutlinedIcon, PORTAL_CONSTANTS) align with the refactored UI requirements.


11-32: LGTM! Card layout improvements.

The explicit flexbox properties ensure consistent card height and pin the action button to the bottom, improving visual consistency across portal cards.


34-46: LGTM! Props type is clean.

The removal of the relativeTime prop simplifies the component interface. The remaining props are appropriate for the card's functionality.


47-74: LGTM! Status derivation logic properly handles edge cases.

The updated logic explicitly handles UNKNOWN and PENDING states instead of defaulting to PUBLISHED, which addresses the previous review concern. This improves reliability by distinguishing between:

  • Successful publication ('published'PUBLISHED)
  • Failed publication ('failed'FAILED)
  • Unknown status values (UNKNOWN)
  • Missing status (PENDING)

The fallback to PENDING when publication.status is falsy is reasonable for publications that were initiated but haven't reported status yet.


246-256: LGTM! Publish button logic is correct.

The button text uses single quotes for consistency, and the disabled state properly prevents publishing when apiId is missing or when the operation is already in progress.

portals/management-portal/src/pages/portals/PortalForm.tsx (2)

20-26: Props and initial state wiring look solid

Using Partial<CreatePortalPayload> for initialData and seeding formData from initialFormData plus overrides is clean and type-safe. The added visibility: 'private' default also keeps the payload well-defined without depending on callers.

Also applies to: 28-37, 46-49, 51-53


188-189: Test IDs, field states, button labels, and export all look consistent

Adding stable testId props across inputs, disabling the identifier only in edit mode, and introducing clear button labels (Saving... / Create and continue / Save Changes) make the form easier to test and more understandable for users. The default export of PortalForm is also straightforward and consistent with the rest of the file.

Also applies to: 195-203, 210-218, 224-232, 239-248, 254-262, 285-296, 313-317, 324-324

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (3)

52-69: Enhanced status handling looks good; consider adding debug logging.

The status derivation now correctly handles PUBLISHED, FAILED, UNKNOWN, and PENDING states. This addresses the past review feedback about explicit handling of unexpected statuses.

Optional enhancement: Add debug logging when UNKNOWN or PENDING states are encountered to aid troubleshooting:

  if (isPublished) {
    if (publication?.status === 'published') {
      status = 'PUBLISHED';
      success = true;
    } else if (publication?.status === 'failed') {
      status = 'FAILED';
      success = false;
    } else if (publication?.status) {
      status = 'UNKNOWN';
      success = false;
+     console.warn('[DevPortalDeployCard] Unexpected publication status:', publication.status);
    } else {
      status = 'PENDING';
      success = false;
+     console.warn('[DevPortalDeployCard] Publication status missing for published portal:', portal.uuid);
    }
  }

87-118: Optional: Simplify redundant Box nesting.

The logo block uses two nested Boxes with overlapping properties. Consider consolidating:

- <Box
-   sx={{
-     width: 100,
-     height: 100,
-     borderRadius: 2,
-     bgcolor: 'transparent',
-     display: 'flex',
-     alignItems: 'center',
-     justifyContent: 'center',
-   }}
- >
    <Box
      sx={{
        width: 100,
        height: 100,
        borderRadius: '8px',
        border: '0.5px solid #abb8c2ff',
        bgcolor: '#d9e0e4ff',
        overflow: 'hidden',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      <Box
        component="img"
        src={BijiraDPLogo}
        alt="Bijira Dev Portal Logo"
        sx={{ width: 90, height: 90, objectFit: 'contain' }}
      />
    </Box>
- </Box>

201-255: Status box logic is correct but consider extracting for readability.

The backgroundColor and border styling correctly handles all four states (FAILED, PUBLISHED, PENDING, UNKNOWN) with appropriate theme-aware colors. However, the deeply nested ternary (16 lines) reduces maintainability.

Optional enhancement: Extract styling logic into a helper function:

const getStatusBoxStyles = (status: string, success: boolean, theme: any) => {
  const styles = {
    FAILED: {
      bg: theme.palette.mode === 'dark' ? 'rgba(239,68,68,0.12)' : '#FDECEC',
      border: theme.palette.error.light,
    },
    PUBLISHED: {
      bg: theme.palette.mode === 'dark' ? 'rgba(16,185,129,0.12)' : '#E8F7EC',
      border: '#D8EEDC',
    },
    PENDING: {
      bg: theme.palette.mode === 'dark' ? 'rgba(59,130,246,0.12)' : '#EFF6FF',
      border: theme.palette.info.light,
    },
    UNKNOWN: {
      bg: theme.palette.mode === 'dark' ? 'rgba(251,191,36,0.12)' : '#FFFBEB',
      border: theme.palette.warning.light,
    },
  };
  
  return status === 'FAILED' 
    ? styles.FAILED
    : success 
      ? styles.PUBLISHED
      : status === 'PENDING'
        ? styles.PENDING
        : styles.UNKNOWN;
};

Then use:

const statusStyles = getStatusBoxStyles(status, success, t);
// ...
backgroundColor: statusStyles.bg,
border: `1px solid ${statusStyles.border}`,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92f5f14 and 59aa673.

📒 Files selected for processing (2)
  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (4 hunks)
  • portals/management-portal/src/pages/portals/PortalForm.tsx (12 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-11-06T07:04:01.101Z
Learnt from: DakshithaS
Repo: wso2/api-platform PR: 105
File: platform-api/src/internal/model/api_publication.go:63-79
Timestamp: 2025-11-06T07:04:01.101Z
Learning: In the API Platform codebase (platform-api/src/internal/model/api_publication.go), the PublicationStateMachine intentionally disallows Published → Publishing transitions. The design requires APIs to be explicitly unpublished before they can be republished, enforcing the lifecycle: Published → Unpublishing → Unpublished → Publishing → Published. This prevents accidental overwrites and ensures proper cleanup.

Applied to files:

  • portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/portals/PortalForm.tsx
📚 Learning: 2025-12-05T00:18:04.859Z
Learnt from: DakshithaS
Repo: wso2/api-platform PR: 322
File: portals/management-portal/src/pages/apis/ApiPublish/mapper.ts:33-33
Timestamp: 2025-12-05T00:18:04.859Z
Learning: In portals/management-portal/src/pages/apis/ApiPublish/mapper.ts, the labels field is intentionally hardcoded to ['default'] because the dynamic labels feature is not yet implemented, even though FormLike.labels?: string[] exists in the interface.

Applied to files:

  • portals/management-portal/src/pages/portals/PortalForm.tsx
🧬 Code graph analysis (1)
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (1)
portals/management-portal/src/constants/portal.ts (1)
  • PORTAL_CONSTANTS (2-68)
🔇 Additional comments (7)
portals/management-portal/src/pages/apis/ApiPublish/DevPortalDeployCard.tsx (2)

1-32: LGTM! Clean import organization and consistent card layout.

The Card component's fixed height and flexbox layout ensure visual consistency across multiple portal cards.


146-192: LGTM! Proper URL handling with good UX and security.

The portal URL display includes:

  • Proper overflow handling with ellipsis
  • Accessible Tooltip and IconButton with ARIA labels
  • Secure window.open with noopener,noreferrer
  • Correct disabled state handling
portals/management-portal/src/pages/portals/PortalForm.tsx (5)

17-37: Props and initial payload defaults align with create/edit portal flows

Typing onSubmit as (CreatePortalPayload | UpdatePortalPayload) with initialData?: Partial<CreatePortalPayload> and a CreatePortalPayload-shaped initialFormData (including visibility: 'private') is consistent and gives a sensible default state for both add and edit modes. No changes needed here.


46-55: Form state and identifier auto-generation are clean and type-safe

Using useState<CreatePortalPayload> seeded from initialFormData/initialData plus a generic updateField over keyof CreatePortalPayload keeps the form strongly typed. The identifier auto-generation only when field === 'name' in add mode and identifierTouched is false gives a predictable UX while still allowing manual overrides. This looks solid.

Also applies to: 62-84


90-108: Validation correctly handles API key masking and URL format

apiKeyValid now correctly treats the masked value via PORTAL_CONSTANTS.API_KEY_MASK as valid in edit mode while still requiring a non-empty key otherwise, and the apiUrlValid check enforces a trimmed HTTP(S) URL without a trailing slash. Combined with non-empty name and hostname checks, isValid is a sensible gate for enabling submission.


112-163: Submit handler cleanly separates create vs edit flows and masks API key updates

Branching on isEdit to construct an UpdatePortalPayload (with apiKey dropped when it equals PORTAL_CONSTANTS.API_KEY_MASK) versus passing formData directly for creates keeps the payload semantics clear and avoids persisting the mask. Using mode-specific fallback error messages and tying local submitStatus to effectiveSubmitting also looks correct and addresses earlier concerns about misleading error copy.


191-216: UI additions (test IDs, visibility select, button copy) are coherent and support testing

The added testId props on key inputs, disabling the Identifier field in edit mode, introducing the Visibility SimpleSelect with public/private options, and adjusting button labels/states (Saving…/Creating…, Save Changes vs Create and continue) all align well with the form’s behaviour and should make both UX and automated testing smoother. No issues spotted.

Also applies to: 220-247, 249-275, 279-299, 300-314, 323-332

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.

1 participant