Skip to content

refactor(portal-framework-core, portal-framework-ui, portal-framework-ui-core, portal-plugin-core, portal-plugin-dashboard): modularize UI components and enhance navigation/item rendering logic#469

Merged
pcfreak30 merged 1 commit intodevelopfrom
libs/portal-plugin-dashboard
Aug 29, 2025

Conversation

@pcfreak30
Copy link
Copy Markdown
Member

@pcfreak30 pcfreak30 commented Aug 29, 2025

  • Tailwind CSS Integration:

    • Add prettier-plugin-tailwindcss for consistent class ordering in .prettierrc.json
    • Introduce tailwind-scrollbar plugin for custom scrollbar styling
    • Define scrollbar CSS variables in shared/assets/tailwind.css
    • Create tailwindSafelist and tailwindBlocklist in libs/portal-framework-ui-core/config/classlist.ts to optimize Tailwind's build output
    • Update tailwind.config.ts in both portal-framework-ui-core and portal-plugin-dashboard to utilize the new safelist/blocklist and add a tablet screen size
  • Widget Area Refactor:

    • Replace inline style generation with Tailwind CSS utility classes in WidgetArea.tsx
    • Add helper functions in grid.ts to generate Tailwind classes for grid layouts, column/row spans, and min/max dimensions
    • Use cn utility for dynamic class composition in widget rendering
  • Navigation System Improvements:

    • Add linkable and forceShowInNavigation properties to NavigationItem type
    • Refactor MainNavigation.tsx to support non-linkable parent items and index routes visibility
    • Implement isRouteActive and isChildRouteActive helper functions for precise active state determination
    • Create reusable NavItemContent, LinkableNavItem, and NonLinkableNavItem components
    • Update CollapseMenuButton to handle icons for submenus and reset open state for mobile
  • Authentication UI Refactor:

    • Wrap OtpForm.tsx content with new AuthPage component for consistent layout
    • Simplify OTP form header content and remove footer link in otp.tsx
  • Email Verification Enhancements:

    • Create dedicated components (VerificationStatus, VerificationLoading, etc.) for different verification states in account.verify.tsx
    • Add MissingParametersError component for handling incomplete verification links
    • Improve error handling to distinguish between general errors and "already verified" (409 Conflict) responses
  • UI Component Adjustments:

    • Add scrollbar styling class to BaseTableContent.tsx for horizontal overflow
    • Set explicit width/height classes for DataTable action columns in DataTable.tsx
    • Update DefaultPagination.tsx to handle zero page count gracefully
    • Add loader icon to resend button in EmailVerificationBanner.tsx
    • Adjust button sizing and whitespace handling in account widgets (2fa.tsx, delete.tsx, password.tsx)
    • Add success/error notification support to SchemaForm.tsx
    • Modify sidebar/desktop layout dimensions and spacing in DesktopSidebar.tsx and GeneralLayout.tsx
    • Fix mobile menu closing behavior in MobileMenu.tsx
    • Adjust sidebar toggle positioning in SidebarToggle.tsx
  • Theme Management Refactor:

    • Implement persist middleware in uiStore.ts to save selected theme
    • Update useTheme and withTheme hooks to prioritize persisted theme state
  • Miscellaneous:

    • Add portal-framework-ui-core as a dependency to portal-framework-core
    • Update route definitions in portal-plugin-dashboard to include icons and navigation properties

Summary by CodeRabbit

  • New Features

    • OTP screen now uses a full login-style page with a reset-password link.
    • Navigation supports active/child route highlighting and optional non-link items.
    • Theme selection persists across sessions.
  • Improvements

    • Mobile menu closes after selecting a navigation item.
    • Data tables gain horizontal scrolling and refined action column sizing.
    • Email verification flow consolidated with clearer states and a loading spinner.
    • Grid-based widget layout is more responsive and consistent.
  • UI/Style

    • Sidebar widths and toggles refined; general layout spacing adjusted.
    • Buttons get responsive heights/wrapping; scrollbar styling enhanced.
  • Bug Fixes

    • Pagination now shows “No pages” when there are zero pages.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Aug 29, 2025

⚠️ No Changeset found

Latest commit: 32726c8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 29, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between a2113a2 and 32726c8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (37)
  • .prettierrc.json (1 hunks)
  • apps/portal-app-shell/tailwind.config.ts (1 hunks)
  • libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (4 hunks)
  • libs/portal-framework-auth/src/ui/forms/otp.tsx (1 hunks)
  • libs/portal-framework-core/package.json (1 hunks)
  • libs/portal-framework-core/src/components/WidgetArea.tsx (2 hunks)
  • libs/portal-framework-core/src/types/navigation.ts (1 hunks)
  • libs/portal-framework-core/src/util/grid.ts (2 hunks)
  • libs/portal-framework-ui-core/config/classlist.ts (1 hunks)
  • libs/portal-framework-ui-core/config/tailwind.config.ts (3 hunks)
  • libs/portal-framework-ui-core/package.json (2 hunks)
  • libs/portal-framework-ui-core/src/components/ui/table.tsx (7 hunks)
  • libs/portal-framework-ui/src/components/MainNavigation.tsx (8 hunks)
  • libs/portal-framework-ui/src/components/data-table/BaseTableContent.tsx (2 hunks)
  • libs/portal-framework-ui/src/components/data-table/DataTable.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/data-table/DefaultPagination.tsx (2 hunks)
  • libs/portal-framework-ui/src/components/form/SchemaForm.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/layout/DesktopSidebar.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/layout/GeneralLayout.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/layout/MobileMenu.tsx (2 hunks)
  • libs/portal-framework-ui/src/components/layout/SidebarToggle.tsx (1 hunks)
  • libs/portal-framework-ui/src/hooks/useTheme.tsx (2 hunks)
  • libs/portal-framework-ui/src/store/uiStore.ts (2 hunks)
  • libs/portal-plugin-core/src/features/navigation.ts (2 hunks)
  • libs/portal-plugin-dashboard/src/routes.tsx (5 hunks)
  • libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx (2 hunks)
  • libs/portal-plugin-dashboard/src/ui/forms/createApiKey.tsx (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/forms/editProfile.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/hooks/useEmailVerification.ts (4 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/account.api-keys.tsx (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/widgets/account/2fa.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/widgets/account/delete.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/widgets/account/password.tsx (1 hunks)
  • libs/portal-plugin-dashboard/tailwind.config.ts (1 hunks)
  • package.json (1 hunks)
  • shared/assets/tailwind.css (1 hunks)
 __________________________________________________________________________________________________________________________________________
< Test early. Test often. Test automatically. Tests that run with every build are much more effective than test plans that sit on a shelf. >
 ------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

Walkthrough

Configuration adds Tailwind/Prettier plugins and centralizes Tailwind safelist/blocklist. Grid utilities are refactored to class-based helpers; WidgetArea updated accordingly. Navigation gains active-state logic and onItemClick support; MobileMenu becomes controlled. OTP and verification UIs are reworked. Tables/layout receive scroll/spacing tweaks. Store persists theme; theme hook honors persisted selection.

Changes

Cohort / File(s) Summary
Prettier & root tooling
package.json, .prettierrc.json
Add prettier-plugin-tailwindcss; set trailingComma to "all".
Tailwind classlists & configs
libs/portal-framework-ui-core/config/classlist.ts, libs/portal-framework-ui-core/config/tailwind.config.ts, apps/portal-app-shell/tailwind.config.ts, libs/portal-plugin-dashboard/tailwind.config.ts
Introduce safelist/blocklist generators and export; register safelist in app/dashboard configs; add tailwind-scrollbar plugin and tablet breakpoint.
UI core packaging
libs/portal-framework-ui-core/package.json, libs/portal-framework-core/package.json
Export classlist module; add tailwind-scrollbar devDependency; add dependency on @lumeweb/portal-framework-ui-core in framework-core.
Grid system refactor
libs/portal-framework-core/src/util/grid.ts, libs/portal-framework-core/src/components/WidgetArea.tsx
Replace inline styles with class-map helpers; add min/max and span class utilities; remove getWidgetStyles; update WidgetArea to use new responsive classes and constraints.
Navigation types & plugin
libs/portal-framework-core/src/types/navigation.ts, libs/portal-plugin-core/src/features/navigation.ts
Extend NavigationItem fields (forceShowInNavigation, index, linkable, show, parentId, path); data-driven property inclusion; carry index and force-show handling.
App navigation components
libs/portal-framework-ui/src/components/MainNavigation.tsx, .../layout/MobileMenu.tsx, .../layout/DesktopSidebar.tsx, .../layout/GeneralLayout.tsx, .../layout/SidebarToggle.tsx
Add route-aware active checks; split linkable/non-linkable items; support onItemClick and collapse reset; make MobileMenu controlled and close on item click; width/spacing tweaks; adjust toggle offset.
Auth OTP flow
libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx, libs/portal-framework-auth/src/ui/forms/otp.tsx
Wrap OTP form in AuthPage with reset link; update header text to authenticator code; remove back-link/resend button; adjust classes.
Email verification flow
libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx, .../src/ui/hooks/useEmailVerification.ts, .../src/ui/components/EmailVerificationBanner.tsx
Add status-driven UI components; handle 409 as already verified; support email from query param; update loading indicator with spinner.
Dashboard routes & widgets
libs/portal-plugin-dashboard/src/routes.tsx, .../ui/widgets/account/2fa.tsx, .../ui/widgets/account/delete.tsx, .../ui/widgets/account/password.tsx
Add navigation metadata and type assertion; adjust button heights/whitespace.
Tables & data table
libs/portal-framework-ui-core/src/components/ui/table.tsx, libs/portal-framework-ui/src/components/data-table/BaseTableContent.tsx, .../DataTable.tsx, .../DefaultPagination.tsx
Add scrollable wrappers; remove inline sizing; constrain Actions column classes; handle zero pages text; minor class reorderings.
Forms config passthrough
libs/portal-framework-ui/src/components/form/SchemaForm.tsx, .../components/form/types.ts
Pass errorNotification/successNotification from config to adapter; extend FormConfig type.
Theme & UI store
libs/portal-framework-ui/src/hooks/useTheme.tsx, libs/portal-framework-ui/src/store/uiStore.ts
Honor persisted theme ID in selection/application; persist UI store (theme only) via zustand persist and privatize store constant.
Shared CSS variables
shared/assets/tailwind.css
Add CSS variables for scrollbar colors, radii, and dimensions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant MobileMenu
  participant Sheet
  participant MainNavigation
  Note over MobileMenu,Sheet: Sheet is controlled (open, onOpenChange)
  User->>MobileMenu: Tap menu button
  MobileMenu->>Sheet: open = true
  Sheet->>MainNavigation: Render with onItemClick
  User->>MainNavigation: Select nav item
  MainNavigation-->>MobileMenu: onItemClick()
  MobileMenu->>Sheet: setOpen(false)
  Sheet-->>User: Closes
Loading
sequenceDiagram
  autonumber
  actor User
  participant AccountVerify
  participant EmailHook as useEmailVerification
  participant SDK
  participant Status as VerificationStatus
  User->>AccountVerify: Open verify link (?token,&email)
  AccountVerify->>EmailHook: init (user, params)
  EmailHook->>SDK: requestEmailVerification(email fallback)
  AccountVerify->>SDK: verifyEmail(token)
  alt 200 OK
    AccountVerify->>Status: isVerified=true
  else 409 Conflict
    AccountVerify->>Status: alreadyVerified=true
  else error
    AccountVerify->>Status: isError=true
  end
  User->>Status: Resend / Redirect
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

A nimble hop through grids and rows,
I twirl my whiskers where the sidebar goes.
Safelists bloom, the scrollbars gleam,
Menus fold like a gentle dream.
OTPs hum, and themes persist—
Approving carrots, I can’t resist. 🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch libs/portal-plugin-dashboard

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@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: 27

Caution

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

⚠️ Outside diff range comments (5)
libs/portal-plugin-dashboard/src/ui/widgets/account/2fa.tsx (2)

70-83: Keep 44px touch target on md+ and prevent accidental form submits

h-9 (36px) on md+ may undercut recommended 44px targets; also add type="button" to avoid unintended form submissions inside forms.

-      <Button
-        className="w-full h-11 whitespace-normal md:h-9 md:whitespace-nowrap"
+      <Button
+        type="button"
+        className="w-full h-11 whitespace-normal md:h-11 md:whitespace-nowrap"
         onClick={(e) => {

80-81: Fix wording: “Authorization” → “Authentication”

User-facing label inconsistency.

-        {identity?.otp && "Disable Two-Factor Authorization"}
+        {identity?.otp && "Disable Two-Factor Authentication"}
libs/portal-plugin-dashboard/src/ui/widgets/account/delete.tsx (1)

24-29: Match accessible height on md+ and set explicit button type

Keep ≥44px height on md+ and guard against form-submit defaults.

-      <Button
-        className="w-full h-11 whitespace-normal md:h-9 md:whitespace-nowrap"
+      <Button
+        type="button"
+        className="w-full h-11 whitespace-normal md:h-11 md:whitespace-nowrap"
         onClick={handleDeleteClick}
         variant="destructive">
libs/portal-plugin-core/src/features/navigation.ts (1)

166-185: Child processing is only one level deep; recursively flatten to support nested menus.

Current logic maps only direct children and ignores grandchildren. Use a recursive helper to emit a fully flattened list with correct parentId chain.

Apply:

-  private processRouteForNavigation(route: RouteDefinition, pluginId: NamespacedId): NavigationItem[] {
-    const item = this.createNavigationItem(route, pluginId);
-    if (!item) {
-      return [];
-    }
-
-    const childItems =
-      route.children
-        ?.filter((child) => this.shouldIncludeRouteInNavigation(child))
-        .map((child) => {
-          const childItem = this.createNavigationItem(child, pluginId);
-          if (childItem) {
-            childItem.parentId = item.id; // Set parentId for navigation hierarchy
-          }
-          return childItem;
-        })
-        .filter((item): item is NavigationItem => item !== null) ?? [];
-
-    return [item, ...childItems];
-  }
+  private processRouteForNavigation(
+    route: RouteDefinition,
+    pluginId: NamespacedId,
+    parentId?: NamespacedId
+  ): NavigationItem[] {
+    const item = this.createNavigationItem(route, pluginId);
+    if (!item) return [];
+    if (parentId) item.parentId = parentId;
+
+    const results: NavigationItem[] = [item];
+    for (const child of route.children ?? []) {
+      if (!this.shouldIncludeRouteInNavigation(child)) continue;
+      results.push(
+        ...this.processRouteForNavigation(child, pluginId, item.id),
+      );
+    }
+    return results;
+  }
libs/portal-framework-ui/src/components/MainNavigation.tsx (1)

141-161: Invoke onItemClick when header link is clicked (mobile close).

Header link stops propagation but doesn’t call onItemClick, so mobile Sheet may remain open.

-              {headerHref && item.linkable !== false ? (
+              {headerHref && item.linkable !== false ? (
                 <Link
                   aria-label={label}
-                  onClick={(e) => e.stopPropagation()}
+                  onClick={(e) => {
+                    e.stopPropagation();
+                    onItemClick?.();
+                  }}
🧹 Nitpick comments (26)
libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx (1)

42-49: A11y: hide spinner from SR and announce loading.
Make the icon non-announcable and expose a polite status for "Sending…".

Apply this diff within the loading branch:

-            {isLoading ? (
-              <>
-                <Loader2 className="mr-2 h-4 w-4 animate-spin" />
-                Sending...
-              </>
-            ) : (
+            {isLoading ? (
+              <span role="status" aria-live="polite" className="inline-flex items-center">
+                <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" focusable="false" />
+                Sending…
+              </span>
+            ) : (
               "Resend Verification Email"
             )}

Optional: add aria-busy to the button for completeness:
<Button ... aria-busy={isLoading} />

libs/portal-framework-ui/src/store/uiStore.ts (1)

19-20: Namespace the storage key to avoid collisions across packages/apps.

“ui-store” is generic; collisions are likely in a monorepo. Prefer a scoped key.

Example:

-      name: "ui-store",
+      name: "lumeweb.portal.ui",
libs/portal-framework-ui/src/hooks/useTheme.tsx (2)

87-101: Prevent FOUC: apply theme in an isomorphic layout effect.

Using useEffect applies after paint, causing a flash. Switch to an isomorphic layout effect to apply before paint on the client.

Apply this diff:

 import React, { useEffect } from "react";
+// Apply theme before paint on client; falls back to useEffect on server
+const useIsomorphicLayoutEffect =
+  typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
@@
-    useEffect(() => {
+    useIsomorphicLayoutEffect(() => {
       if (!themes) {
         return;
       }

Also applies to: 1-1


4-4: Unify import path to avoid duplicate module instances.

Mixing alias and relative paths for the same module can bloat bundles or break singletons.

Apply this diff:

-import { getThemeById } from "@/utils/theme";
+import { getThemeById, applyThemeStyles } from "@/utils/theme";
@@
-import { applyThemeStyles } from "../utils/theme";

Also applies to: 9-9

libs/portal-plugin-dashboard/src/ui/widgets/account/password.tsx (1)

27-27: Avoid overflow on md+ by truncating instead of forcing nowrap.

Long i18n strings may overflow when nowrap at md+. Prefer md:truncate to get ellipsis.

-        className="w-full h-11 whitespace-normal md:h-9 md:whitespace-nowrap"
+        className="w-full h-11 whitespace-normal md:h-9 md:truncate"
.prettierrc.json (1)

7-9: Enable sorting inside cn()/clsx() calls and stabilize plugin resolution in a monorepo.

Helps the plugin catch Tailwind classes within your cn helper and avoids plugin lookup pitfalls across packages.

   "singleQuote": false,
   "trailingComma": "all",
-  "plugins": ["prettier-plugin-tailwindcss"]
+  "plugins": ["prettier-plugin-tailwindcss"],
+  "pluginSearchDirs": false,
+  "tailwindFunctions": ["cn", "clsx", "cva"]
libs/portal-framework-core/package.json (1)

33-34: Reassess core → ui-core dependency (layering and transitive peers)

Making “core” depend on a UI package can force React/DOM peers onto “core” consumers and complicate server/bundler setups. If “core” only needs classlist/config, consider:

  • Extracting shared constants to a small “portal-framework-shared” (no React).
  • Or depend on ui-core as a peer and import only side-effect-free subpaths.

Please confirm where @lumeweb/portal-framework-ui-core is imported inside “core”. If it’s only config/classlist, we can safely move to a shared or peer dependency.

libs/portal-framework-ui/src/components/form/SchemaForm.tsx (2)

63-70: Propagate notifications with stronger typing; consider RHF success parity.

  • Type config.errorNotification / config.successNotification to the refine notification payload (e.g., { message: string; description?: string; type?: "success"|"error"|"info" } | false) instead of any for safety and DX.
  • Currently only error notifications are surfaced for the non-"refine" adapter. If intentional parity is desired, also surface successNotification for non-"refine" flows after successful submit.

I can add types in types.ts and wire a non-refine success notification hook-up if you confirm the desired UX.


118-137: Harden non-refine error notifications.

error can be unknown. Guard conversion to a notification payload to avoid runtime surprises and make failures deterministic.

Apply:

-              onError: async (error) => {
+              onError: async (error: unknown) => {
                 if (adapterName !== "refine" && cConfig.errorNotification) {
-                  const notification =
-                    typeof cConfig.errorNotification === "function"
-                      ? cConfig.errorNotification(error)
-                      : cConfig.errorNotification;
+                  const notification =
+                    typeof cConfig.errorNotification === "function"
+                      ? cConfig.errorNotification(error)
+                      : cConfig.errorNotification;
                   openNotification?.(notification);
                 }
               },
libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (1)

28-31: Guard navigation when redirect target is absent.

go({ to: parsed.params?.to, ... }) with to undefined may mis-route depending on refine version. Prefer an explicit fallback.

Apply:

-    if (!isAuthLoading && authData?.authenticated) {
-      go({ to: parsed.params?.to, type: "push" });
-    }
+    if (!isAuthLoading && authData?.authenticated) {
+      const to = parsed.params?.to ?? "/";
+      go({ to, type: "push" });
+    }
libs/portal-plugin-core/src/features/navigation.ts (1)

163-163: Clarify inclusion rule for index routes; consider honoring explicit show === true.

If consumers set navigation.show = false, it still passes this filter and relies on render-time checks. If you want to prune earlier, consider integrating hidden === true here as well.

Apply:

-    return !!route.navigation && (!route.index || route.navigation.forceShowInNavigation);
+    return (
+      !!route.navigation &&
+      (!route.index || route.navigation.forceShowInNavigation) &&
+      route.navigation.hidden !== true
+    );
shared/assets/tailwind.css (1)

68-76: Validate HSL variable compatibility and consider cross-browser fallbacks

  • hsl(var(--background)) etc. require the referenced custom props to be HSL triplets; if your tokens emit hex or rgb, this will resolve invalidly. Please confirm the theme variables resolve to HSL components.
  • Consider adding Firefox fallbacks (scrollbar-color, scrollbar-width) at base scope to keep styling consistent outside WebKit.

Example fallback (outside this block):

* {
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
  scrollbar-width: thin;
}
libs/portal-framework-auth/src/ui/forms/otp.tsx (2)

30-34: Copy consistency: “authenticator code” vs field label “Confirmation Code”

Either align the header to “Confirmation code” or the field label to “Authenticator code” to avoid mixed terminology.

-        <h3 className="font-bold">Enter your authenticator code</h3>
+        <h3 className="font-bold">Enter your confirmation code</h3>

21-27: Enhance OTP input UX and validity hints

If supported by your FormField component, add numeric-friendly attributes and length guards to reduce input errors.

Additional change (outside the selected range’s diff, adjust to your form API):

{
  label: "Confirmation Code",
  name: "otp",
  placeholder: "Enter 6-digit code",
  required: true,
  type: FormFieldType.TEXT,
  inputProps: { inputMode: "numeric", autoComplete: "one-time-code", maxLength: 6, pattern: "\\d{6}" },
}
libs/portal-framework-core/src/types/navigation.ts (2)

40-41: ID typing consistency with NamespacedId

Route IDs are often namespaced; consider typing NavigationItem.id as NamespacedId for symmetry, or document when a plain string is expected.


66-68: show() signature may be too constrained

A zero-arg show(): boolean limits dynamic checks. Consider allowing a context param (e.g., user/route/permissions) for future-proofing.

libs/portal-framework-core/src/util/grid.ts (2)

241-246: Comment and breakpoint mismatch

The implementation uses a “tablet” breakpoint but the comment mentions “sm”.

-// Always default to 1 column on mobile, expand on sm and up
+// Always default to 1 column on mobile, expand on tablet and up

150-155: Gap map looks good; ensure parity with classlist safelist

Your GRID_GAP_CLASSES includes values (e.g., 14, 28, 36, 44, …) not present in classlist’s gaps array. Add these to the safelist patterns or rely solely on gap-.* pattern.

libs/portal-framework-ui-core/config/tailwind.config.ts (2)

6-6: Unify safelist naming and (optionally) wire blocklist

For consistency with other packages using tailwindSafelist, consider importing that named export here as well. If tailwindBlocklist exists in classlist, wiring blocklist reduces CSS size.

-import tailwindClassList from "./classlist";
+import tailwindClassList, { tailwindSafelist, tailwindBlocklist } from "./classlist";
@@
-  safelist: tailwindClassList,
+  safelist: tailwindSafelist ?? tailwindClassList,
+  // Optional:
+  // blocklist: tailwindBlocklist,

Also applies to: 19-19


4-5: Scrollbar plugin config: consider variant/options

If you need rounded scrollbars or to limit plugin impact, pass options (e.g., { nocompatible: true }) and enable variants.

-import tailwindcss_scrollbar from "tailwind-scrollbar";
+import tailwindcss_scrollbar from "tailwind-scrollbar";
@@
-  plugins: [tailwindcss_animate, tailwindcss_scrollbar],
+  plugins: [tailwindcss_animate, tailwindcss_scrollbar({ nocompatible: true })],
+  // variants: { scrollbar: ["rounded"] },

Also applies to: 17-18

libs/portal-framework-ui/src/components/data-table/DataTable.tsx (1)

36-41: Actions column width: avoid size: 0 and prefer explicit min width

  • size: 0 may collapse the column depending on TanStack sizing; better omit it or set a sensible pixel size.
  • Use min-w-12 w-12 instead of max-w-24 w-12 to enforce a floor.
-        meta: {
-          cellClassName: "max-w-24 w-12",
-          headerClassName: "max-w-24 w-12",
-        },
-        size: 0,
+        meta: {
+          cellClassName: "w-12 min-w-12",
+          headerClassName: "w-12 min-w-12",
+        },
+        // omit size to allow CSS width to control, or set an explicit px value:
+        // size: 48,

Please confirm BaseTable consumes meta.cellClassName/headerClassName; otherwise these classes won’t apply.

libs/portal-plugin-dashboard/src/ui/forms/createApiKey.tsx (1)

11-13: Copy nit: add a period

-        description:
-          "Give your API key a descriptive name to help you identify its purpose",
+        description:
+          "Give your API key a descriptive name to help you identify its purpose.",
libs/portal-framework-ui/src/components/layout/MobileMenu.tsx (1)

16-20: Controlled Sheet + close-on-click: LGTM; also close on route change.

Current behavior is solid. Optionally auto-close when navigation occurs via other mechanisms (e.g., browser back).

Add:

+import { useLocation } from "react-router-dom";
...
 export function MobileMenu() {
   const [open, setOpen] = React.useState(false);
+  const { pathname } = useLocation();
+  React.useEffect(() => setOpen(false), [pathname]);

Also applies to: 36-39

libs/portal-framework-ui/src/components/layout/DesktopSidebar.tsx (1)

23-23: Removing overflow-y-auto can cause footer overlap on short viewports.

With justify-between, large menus can push the bottom slogans off-screen. Either keep container scroll or ensure the nav area owns scrolling.

Option A (restore container scroll):

-      <div className="relative h-full flex flex-col px-3 py-4 justify-between">
+      <div className="relative h-full flex flex-col px-3 py-4 overflow-y-auto justify-between">

Option B (preferable: make inner nav region scrollable; ensure min-h-0 so ScrollArea can shrink):

-      <div className="relative h-full flex flex-col px-3 py-4 justify-between">
+      <div className="relative h-full flex flex-col px-3 py-4 justify-between min-h-0">

Test with many nav items to confirm the slogans remain visible and the nav area scrolls.

libs/portal-framework-ui/src/components/MainNavigation.tsx (2)

189-199: Use stable keys for submenu items.

Index keys can cause incorrect re-renders on reorder. Prefer href.

-        {submenus.map(({ active, href, icon: Icon, label }, index) => (
+        {submenus.map(({ active, href, icon: Icon, label }) => (
           <Button
             asChild
             className="w-full justify-start h-10 mb-1"
-            key={index}
+            key={href}

27-39: Normalize paths to avoid trailing-slash edge cases.

startsWith(\${itemPath}/`)fails ifitemPathends with/`. Normalize both sides.

-const isRouteActive = (item: NavigationItemType, currentPathname: string): boolean => {
-  const itemPath = item.path;
+const isRouteActive = (item: NavigationItemType, currentPathname: string): boolean => {
+  const itemPath = item.path;
   if (!itemPath) return false;
-  if (itemPath === currentPathname) return true;
-  if (itemPath !== "/" && currentPathname.startsWith(`${itemPath}/`)) return true;
+  const norm = (p: string) => (p !== "/" ? p.replace(/\/+$/, "") : "/");
+  const a = norm(itemPath);
+  const b = norm(currentPathname);
+  if (a === b) return true;
+  if (a !== "/" && b.startsWith(`${a}/`)) return true;
   return false;
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4950a and a2113a2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (37)
  • .prettierrc.json (1 hunks)
  • apps/portal-app-shell/tailwind.config.ts (1 hunks)
  • libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (4 hunks)
  • libs/portal-framework-auth/src/ui/forms/otp.tsx (1 hunks)
  • libs/portal-framework-core/package.json (1 hunks)
  • libs/portal-framework-core/src/components/WidgetArea.tsx (2 hunks)
  • libs/portal-framework-core/src/types/navigation.ts (1 hunks)
  • libs/portal-framework-core/src/util/grid.ts (2 hunks)
  • libs/portal-framework-ui-core/config/classlist.ts (1 hunks)
  • libs/portal-framework-ui-core/config/tailwind.config.ts (3 hunks)
  • libs/portal-framework-ui-core/package.json (2 hunks)
  • libs/portal-framework-ui-core/src/components/ui/table.tsx (7 hunks)
  • libs/portal-framework-ui/src/components/MainNavigation.tsx (8 hunks)
  • libs/portal-framework-ui/src/components/data-table/BaseTableContent.tsx (2 hunks)
  • libs/portal-framework-ui/src/components/data-table/DataTable.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/data-table/DefaultPagination.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/form/SchemaForm.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/layout/DesktopSidebar.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/layout/GeneralLayout.tsx (1 hunks)
  • libs/portal-framework-ui/src/components/layout/MobileMenu.tsx (2 hunks)
  • libs/portal-framework-ui/src/components/layout/SidebarToggle.tsx (1 hunks)
  • libs/portal-framework-ui/src/hooks/useTheme.tsx (2 hunks)
  • libs/portal-framework-ui/src/store/uiStore.ts (2 hunks)
  • libs/portal-plugin-core/src/features/navigation.ts (2 hunks)
  • libs/portal-plugin-dashboard/src/routes.tsx (5 hunks)
  • libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx (2 hunks)
  • libs/portal-plugin-dashboard/src/ui/forms/createApiKey.tsx (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/forms/editProfile.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/hooks/useEmailVerification.ts (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/account.api-keys.tsx (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx (3 hunks)
  • libs/portal-plugin-dashboard/src/ui/widgets/account/2fa.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/widgets/account/delete.tsx (1 hunks)
  • libs/portal-plugin-dashboard/src/ui/widgets/account/password.tsx (1 hunks)
  • libs/portal-plugin-dashboard/tailwind.config.ts (1 hunks)
  • package.json (1 hunks)
  • shared/assets/tailwind.css (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (18)
libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (2)
libs/portal-framework-auth/src/ui/components/common/AuthPage.tsx (1)
  • AuthPage (18-59)
libs/portal-framework-auth/src/ui/components/common/AuthPageTitle.tsx (1)
  • AuthPageTitle (9-15)
libs/portal-framework-ui/src/components/layout/DesktopSidebar.tsx (1)
libs/portal-framework-ui/src/components/layout/SidebarToggle.tsx (1)
  • SidebarToggle (10-27)
libs/portal-plugin-dashboard/src/ui/routes/account.verify.tsx (2)
libs/portal-sdk/src/account/generated/accountAPI.schemas.ts (1)
  • Error (74-78)
libs/portal-plugin-dashboard/src/ui/hooks/useEmailVerification.ts (1)
  • useEmailVerification (8-64)
libs/portal-plugin-dashboard/src/ui/routes/account.api-keys.tsx (1)
libs/portal-framework-ui/src/components/layout/PageHeader.tsx (1)
  • PageHeader (9-19)
libs/portal-framework-ui-core/config/classlist.ts (1)
go/portal-plugin-abuse-report/build/static/js/view-BD93An_3.js (1)
  • gaps (23925-23925)
apps/portal-app-shell/tailwind.config.ts (1)
libs/portal-framework-ui-core/config/classlist.ts (1)
  • tailwindSafelist (86-86)
libs/portal-framework-ui/src/components/data-table/BaseTableContent.tsx (1)
libs/portal-framework-ui-core/src/components/ui/table.tsx (5)
  • TableHeader (124-124)
  • TableRow (125-125)
  • TableHead (123-123)
  • TableBody (119-119)
  • TableCell (121-121)
libs/portal-plugin-core/src/features/navigation.ts (1)
libs/portal-framework-core/src/types/navigation.ts (1)
  • RouteDefinition (76-104)
libs/portal-framework-ui/src/hooks/useTheme.tsx (2)
libs/portal-framework-ui/src/hooks/usePluginMeta.ts (1)
  • usePluginMeta (6-12)
libs/portal-shared/src/hooks/useTheme.tsx (1)
  • Theme (39-45)
libs/portal-framework-core/src/components/WidgetArea.tsx (1)
libs/portal-framework-core/src/util/grid.ts (8)
  • getGridAutoRows (122-134)
  • getResponsiveGridTemplateColumnsClass (241-246)
  • getGridStyles (162-201)
  • getResponsiveGridColumnSpanClass (224-239)
  • getGridRowSpanClass (157-160)
  • getMinHeightClass (216-218)
  • getMaxWidthClass (212-214)
  • getMaxHeightClass (208-210)
libs/portal-plugin-dashboard/tailwind.config.ts (1)
libs/portal-framework-ui-core/config/classlist.ts (2)
  • tailwindSafelist (86-86)
  • tailwindBlocklist (87-87)
libs/portal-plugin-dashboard/src/routes.tsx (1)
libs/portal-framework-core/src/types/navigation.ts (1)
  • RouteDefinition (76-104)
libs/portal-framework-ui/src/components/layout/MobileMenu.tsx (1)
libs/portal-framework-ui/src/components/MainNavigation.tsx (1)
  • MainNavigation (313-377)
libs/portal-framework-ui/src/components/layout/GeneralLayout.tsx (2)
libs/portal-framework-ui/src/components/layout/UserNav.tsx (1)
  • UserNav (26-94)
libs/portal-framework-ui/src/components/layout/MobileMenu.tsx (1)
  • MobileMenu (15-43)
libs/portal-framework-ui/src/components/MainNavigation.tsx (1)
libs/portal-framework-core/src/types/navigation.ts (1)
  • NavigationItemIconProps (71-74)
libs/portal-plugin-dashboard/src/ui/hooks/useEmailVerification.ts (1)
go/portal-plugin-dashboard/build/static/js/useEmailVerification-Cq2zbvCo.js (1)
  • verifyAgain (19-51)
libs/portal-framework-core/src/types/navigation.ts (1)
libs/portal-framework-core/src/types/plugin.ts (1)
  • NamespacedId (18-18)
libs/portal-framework-core/src/util/grid.ts (2)
go/portal-plugin-abuse-report/build/static/js/view-BD93An_3.js (1)
  • rowHeight (11760-11764)
libs/portal-framework-core/src/types/widget.ts (1)
  • DEFAULT_WIDGET_AREA_DEFINITION (22-28)
🔇 Additional comments (19)
libs/portal-plugin-dashboard/src/ui/components/EmailVerificationBanner.tsx (1)

9-9: LGTM: importing Loader2 for spinner is appropriate.
No issues spotted with the new import.

libs/portal-framework-ui/src/store/uiStore.ts (1)

25-26: Hook wrapper looks good.

Selector-based access via useStore(uiStore, selector) is correct and keeps the store private.

libs/portal-framework-ui/src/hooks/useTheme.tsx (1)

62-65: useTheme fallback looks solid.

Returning undefined when no themes exist and then falling back to default/first theme is consistent with the HOC behavior.

Also applies to: 77-77

package.json (1)

62-62: Manual verification required: The debug check did not confirm that prettier-plugin-tailwindcss was loaded under your pnpm workspaces setup. Please run the verification command in your CI environment (or adjust the grep pattern) and confirm the plugin is discovered and applied.

libs/portal-plugin-dashboard/src/ui/forms/editProfile.tsx (1)

13-17: LGTM: responsive grid spans align with form grid

Fields span full width on small and split on md+, matching formClassName. Ensure these classes are included in the Tailwind safelist (looks covered by the new classlist module).

Also applies to: 19-23

libs/portal-plugin-dashboard/src/ui/hooks/useEmailVerification.ts (1)

61-61: No change needed: using React Query v4, isLoading is correct. Project’s package.json specifies @tanstack/react-query v4.36.1, and in v4 useMutation exposes isLoading rather than isPending.

libs/portal-framework-auth/src/ui/components/login/OtpForm.tsx (1)

38-47: LGTM: AuthPage wrapper improves UX and discoverability of reset flow.

The composition is clean and preserves existing form behavior.

libs/portal-framework-core/src/components/WidgetArea.tsx (1)

24-28: Guard against missing widget area data

If framework.getWidgetArea(id) can return undefined, area.grid.* will throw. Add an early return or a reasonable fallback.

Would you like me to add a null-guard and default layout grid (e.g., 1 column, auto rows)?

libs/portal-framework-ui-core/config/tailwind.config.ts (1)

107-109: Confirm breakpoint semantics

tablet: "1000px" sits between md (768px) and lg (1024px). Verify component logic doesn’t assume sm < tablet < md < lg. If it does, rename to avoid confusion or adjust conditional utilities.

libs/portal-plugin-dashboard/src/ui/forms/createApiKey.tsx (1)

21-25: Nice: success notification wired

Good addition; aligns with SchemaForm notification forwarding.

apps/portal-app-shell/tailwind.config.ts (1)

2-2: LGTM: central safelist source

Using the shared tailwindSafelist keeps configs consistent across packages.

Also applies to: 7-7

libs/portal-plugin-dashboard/tailwind.config.ts (1)

3-3: Import looks good and centralizes classlists.

Tying this config to the shared safelist/blocklist source is the right call for consistency.

libs/portal-framework-ui/src/components/layout/GeneralLayout.tsx (2)

21-23: Header/spacing reordering: LGTM.

No functional change; spacing reads cleaner with margins on content container.

Also applies to: 28-28


19-19: Verified responsive offset mapping. MainWithResponsiveOffset correctly applies lg:ml-40 when collapsed and lg:ml-72 when expanded with the same transition-[margin-left] duration-300 ease-in-out behavior.

libs/portal-plugin-dashboard/src/routes.tsx (3)

55-58: Non-linkable parent: ensure menu renders without an anchor.

navigation.linkable: false implies the parent should toggle only. Verify CollapseMenuButton/NavItem honor this so it doesn’t create a clickable link.

If not already handled, propagate linkable into the rendering logic to switch between link and button semantics (role, aria-expanded).


62-65: Normalize top-level route path conventions
Routes currently mix absolute (/dashboard, /account) and relative (login, register, otp, account/verify, etc.) paths. Pick one convention and apply it consistently:

  • For absolute paths, prefix all path values with /.
  • For relative segments (React Router’s default for top-level routes), drop leading slashes on /dashboard and /account.

Apply the same rule to account/verify, login, register, reset-password, otp, etc.


26-31: Incorrect — index route already omits path.

The route (libs/portal-plugin-dashboard/src/routes.tsx, id: "account_index") has index: true and no path property; the suggested removal is unnecessary.

Likely an incorrect or invalid review comment.

libs/portal-framework-ui/src/components/data-table/BaseTableContent.tsx (1)

73-76: Ensure emptyState renders valid table markup.

emptyState is inserted directly under <TableBody>. If callers pass a non-<TableRow>, the table DOM becomes invalid.

Consider enforcing a default empty row or documenting the contract. Example:

-                : table.getRowModel().rows.length > 0
+                : table.getRowModel().rows.length > 0
                   ? table.getRowModel().rows.map((row) => {
                     ...
-                    })
-                  : emptyState}
+                    })
+                  : (emptyState ?? (
+                      <TableRow>
+                        <TableCell colSpan={table.getAllColumns().length} className="text-center">
+                          No data
+                        </TableCell>
+                      </TableRow>
+                    ))}

Also applies to: 108-110

libs/portal-plugin-dashboard/src/ui/routes/account.api-keys.tsx (1)

45-46: Fix typo in actionButtonsLayout value

-          actionButtonsLayout: "horizonal",
+          actionButtonsLayout: "horizontal",

…-ui-core, portal-plugin-core, portal-plugin-dashboard): modularize UI components and enhance navigation/item rendering logic

- **Tailwind CSS Integration:**
  - Add `prettier-plugin-tailwindcss` for consistent class ordering in `.prettierrc.json`
  - Introduce `tailwind-scrollbar` plugin for custom scrollbar styling
  - Define scrollbar CSS variables in `shared/assets/tailwind.css`
  - Create `tailwindSafelist` and `tailwindBlocklist` in `libs/portal-framework-ui-core/config/classlist.ts` to optimize Tailwind's build output
  - Update `tailwind.config.ts` in both `portal-framework-ui-core` and `portal-plugin-dashboard` to utilize the new safelist/blocklist and add a `tablet` screen size

- **Widget Area Refactor:**
  - Replace inline style generation with Tailwind CSS utility classes in `WidgetArea.tsx`
  - Add helper functions in `grid.ts` to generate Tailwind classes for grid layouts, column/row spans, and min/max dimensions
  - Use `cn` utility for dynamic class composition in widget rendering

- **Navigation System Improvements:**
  - Add `linkable` and `forceShowInNavigation` properties to `NavigationItem` type
  - Refactor `MainNavigation.tsx` to support non-linkable parent items and index routes visibility
  - Implement `isRouteActive` and `isChildRouteActive` helper functions for precise active state determination
  - Create reusable `NavItemContent`, `LinkableNavItem`, and `NonLinkableNavItem` components
  - Update `CollapseMenuButton` to handle icons for submenus and reset open state for mobile

- **Authentication UI Refactor:**
  - Wrap `OtpForm.tsx` content with new `AuthPage` component for consistent layout
  - Simplify OTP form header content and remove footer link in `otp.tsx`

- **Email Verification Enhancements:**
  - Create dedicated components (`VerificationStatus`, `VerificationLoading`, etc.) for different verification states in `account.verify.tsx`
  - Add `MissingParametersError` component for handling incomplete verification links
  - Improve error handling to distinguish between general errors and "already verified" (409 Conflict) responses

- **UI Component Adjustments:**
  - Add scrollbar styling class to `BaseTableContent.tsx` for horizontal overflow
  - Set explicit width/height classes for DataTable action columns in `DataTable.tsx`
  - Update `DefaultPagination.tsx` to handle zero page count gracefully
  - Add loader icon to resend button in `EmailVerificationBanner.tsx`
  - Adjust button sizing and whitespace handling in account widgets (`2fa.tsx`, `delete.tsx`, `password.tsx`)
  - Add success/error notification support to `SchemaForm.tsx`
  - Modify sidebar/desktop layout dimensions and spacing in `DesktopSidebar.tsx` and `GeneralLayout.tsx`
  - Fix mobile menu closing behavior in `MobileMenu.tsx`
  - Adjust sidebar toggle positioning in `SidebarToggle.tsx`

- **Theme Management Refactor:**
  - Implement `persist` middleware in `uiStore.ts` to save selected theme
  - Update `useTheme` and `withTheme` hooks to prioritize persisted theme state

- **Miscellaneous:**
  - Add `portal-framework-ui-core` as a dependency to `portal-framework-core`
  - Update route definitions in `portal-plugin-dashboard` to include icons and navigation properties
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