Skip to content

feat: export Roles page as a component and consume it in apps/admin#1349

Open
paanSinghCoder wants to merge 15 commits intomainfrom
refactor/admin-ui-child
Open

feat: export Roles page as a component and consume it in apps/admin#1349
paanSinghCoder wants to merge 15 commits intomainfrom
refactor/admin-ui-child

Conversation

@paanSinghCoder
Copy link
Contributor

@paanSinghCoder paanSinghCoder commented Jan 29, 2026

  • Move roles page to @raystack/frontier/admin
  • Moved roles UI from apps/admin/src/containers/roles.list/ into web/lib/admin/views/roles/ as a router-agnostic page.
  • Exported RolesPage from @raystack/frontier/admin with optional props: selectedRoleId, onSelectRole, onCloseDetail, appName.
  • Added lib/admin shared pieces: utils (helper, connect-timestamp), components (PageTitle, SheetHeader, PageHeader) and CSS modules.
  • Wired admin app via RolesPageWithRouter (uses useParams/useNavigate) and routes roles + roles/:roleId.
  • Removed containers/roles.list from the app; roles are now implemented in the lib.

Note: To test this in local, change frontier version to "@raystack/frontier": "workspace:^", in frontier/web/apps/admin/package.json

Technical Details

Test Plan

  • Manual testing completed
  • Build and type checking passes

@vercel
Copy link

vercel bot commented Jan 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Feb 11, 2026 10:00am

@paanSinghCoder paanSinghCoder changed the title Refactor/admin UI child feat: export Roles page as a component and consume it in apps/admin Jan 29, 2026
@coveralls
Copy link

coveralls commented Jan 29, 2026

Pull Request Test Coverage Report for Build 21900536870

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 38.24%

Totals Coverage Status
Change from base Build 21744973149: 0.0%
Covered Lines: 16107
Relevant Lines: 42121

💛 - Coveralls

Base automatically changed from refactor/admin-ui to main January 29, 2026 08:38
@paanSinghCoder paanSinghCoder changed the base branch from main to refactor/admin-ui January 29, 2026 08:39
@paanSinghCoder paanSinghCoder changed the base branch from refactor/admin-ui to main January 29, 2026 08:39
@paanSinghCoder paanSinghCoder changed the base branch from main to refactor/admin-ui January 29, 2026 08:40
@paanSinghCoder paanSinghCoder changed the base branch from refactor/admin-ui to main January 29, 2026 08:41
@paanSinghCoder paanSinghCoder changed the base branch from main to refactor/admin-ui January 29, 2026 08:41
@paanSinghCoder paanSinghCoder force-pushed the refactor/admin-ui-child branch from e887b1e to ec4b710 Compare January 29, 2026 08:56
@paanSinghCoder paanSinghCoder changed the base branch from refactor/admin-ui to main January 29, 2026 08:57
@rsbh
Copy link
Member

rsbh commented Feb 4, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Centralized roles list/detail page with improved navigation and selection.
    • New admin UI components: page header, page title handler, and sheet header for consistent layout and interactions.
  • Improvements

    • Roles permissions now displayed as a vertical list for clarity.
    • Roles view supports controlled selection and smoother detail toggling.
  • Chores

    • Admin packaging, build targets, and dependency resolution updated; removed obsolete lint config.

Walkthrough

Centralizes roles list/detail routing via a new RolesPage wired to a controlled RolesView, adds reusable header/title/sheet components, introduces admin build/export artifacts and tsup config, adds utilities (timestamp, reduceByKey), UI tweaks (permissions layout), minor ESLint/package.json adjustments, and removes local admin .eslintrc.

Changes

Cohort / File(s) Summary
Admin app metadata
web/apps/admin/.eslintrc.cjs, web/apps/admin/package.json
Deleted local ESLint config; changed @raystack/frontier dependency from ^0.78.0workspace:^.
Routing & page
web/apps/admin/src/routes.tsx, web/apps/admin/src/pages/roles/RolesPage.tsx
Added RolesPage and rewired routes so /roles and /roles/:roleId render RolesPage which maps URL param to RolesView via selectedRoleId/onSelectRole/onCloseDetail.
Admin build targets & packaging
Makefile, web/Makefile, web/lib/package.json, web/lib/tsup.config.ts
Added build-admin make target and changed admin-app target to call it; added ./admin export and included admin/dist/**/* in web/lib/package.json; added tsup admin build (admin/dist) with dts and css modules plugin.
Admin barrel & client directive
web/lib/admin/index.ts
Added "use client" and re-exported RolesView from ./views/roles.
New UI components & styles
web/lib/admin/components/PageHeader.tsx, web/lib/admin/components/PageTitle.tsx, web/lib/admin/components/SheetHeader.tsx, web/lib/admin/components/page-header.module.css
Added PageHeader, PageTitle, SheetHeader components and breadcrumb CSS module with exported types/props.
Views refactor (roles)
web/lib/admin/views/roles/index.tsx, web/lib/admin/views/roles/details.tsx, web/lib/admin/views/roles/header.tsx, web/lib/admin/views/roles/columns.tsx, web/lib/admin/views/roles/roles.module.css
Refactored RoleList → RolesView (controlled selectedRoleId, RolesViewProps, callbacks), RoleDetails now accepts role prop, header imports adjusted, permissions column renders as vertical list, minor CSS formatting.
Utilities
web/lib/admin/utils/connect-timestamp.ts, web/lib/admin/utils/helper.ts, web/apps/admin/src/utils/helper.ts
Added timestampToDate() and TimeStamp alias; added reduceByKey<T>; fixed CSV Blob typing by casting chunks to BlobPart[].
ESLint shared config
web/tools/eslint-config/index.js
Extracted extends into extendsList constant and removed 'turbo' from the extends array.

Sequence Diagram

sequenceDiagram
    participant User
    participant RolesPage
    participant RolesView
    participant RoleDetails

    User->>RolesPage: Navigate to /roles or /roles/:roleId
    RolesPage->>RolesPage: read roleId from URL params
    RolesPage->>RolesView: render with selectedRoleId & onSelectRole/onCloseDetail

    User->>RolesView: click role row
    RolesView->>RolesView: handleRowClick -> determine roleId
    RolesView->>RolesPage: call onSelectRole(roleId)
    RolesPage->>RolesPage: navigate to /roles/:roleId

    RolesPage->>RolesView: selectedRoleId updated
    RolesView->>RoleDetails: pass resolved role prop
    RoleDetails->>RoleDetails: render details

    User->>RoleDetails: click close
    RoleDetails->>RolesView: trigger onCloseDetail
    RolesView->>RolesPage: onCloseDetail()
    RolesPage->>RolesPage: navigate to /roles
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective of the PR: exporting the Roles page as a reusable component and integrating it into the admin app.
Description check ✅ Passed The description covers the key changes and includes a technical note, but lacks the recommended template structure with Summary/Changes/Technical Details/Test Plan sections.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/admin-ui-child

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
web/Makefile (1)

1-1: Add build-admin to .PHONY declaration.

The new build-admin target should be included in the .PHONY declaration for consistency with other targets like dist and test.

Suggested fix
-.PHONY: dist test
+.PHONY: dist test build-admin

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

Copy link

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

🤖 Fix all issues with AI agents
In `@web/lib/admin/components/SheetHeader.tsx`:
- Around line 11-31: Replace the clickable Cross1Icon in SheetHeader with the
IconButton component from `@raystack/apsara` so the close control has proper
button semantics and an accessible name; update the import to include
IconButton, render IconButton (passing the Cross1Icon as its icon prop or
children), move the onClick handler from Cross1Icon to IconButton, add
aria-label (e.g., "Close sheet") and keep data-testid usage (use dataTestId or
default "admin-close-btn") to preserve tests.

In `@web/lib/admin/views/roles/index.tsx`:
- Around line 36-40: handleRowClick inconsistently maps Role.id to "" in the
onSelectRole branch but to undefined in the setInternalRoleId branch; make both
branches pass the same type (prefer leaving id as-is or using undefined
consistently). Update the handleRowClick callback to call onSelectRole(role.id)
(or onSelectRole(role.id ?? undefined) if callback expects string|undefined) and
setInternalRoleId(role.id) so both branches use role.id (string | undefined)
consistently; reference the handleRowClick function, the onSelectRole prop,
setInternalRoleId, and the Role.id field when making the change.

In `@web/tools/eslint-config/index.js`:
- Around line 2-7: The extendsList array currently omits 'turbo' while
package.json still lists eslint-config-turbo and client-demo/.eslintrc.js uses
the turbo/no-undeclared-env-vars rule; either re-add 'turbo' into the
extendsList (add 'turbo' to the extendsList constant so the turbo rules are
applied) or remove the eslint-config-turbo dependency from
web/tools/eslint-config/package.json and update client-demo/.eslintrc.js to stop
referencing turbo rules; locate and edit the extendsList constant and
package.json dependency to keep them consistent with the .eslintrc.js usage.
🧹 Nitpick comments (9)
web/tools/eslint-config/index.js (1)

2-7: Nitpick: Inconsistent quote style.

Line 5 uses double quotes ("eslint:recommended") while the other entries use single quotes. Consider using consistent quoting for readability.

🔧 Suggested fix
 const extendsList = [
   'next',
   'prettier',
-  "eslint:recommended",
+  'eslint:recommended',
   'plugin:test-selectors/recommended'
 ];
web/lib/admin/utils/helper.ts (1)

1-11: Avoid repeated object spreads in reduceByKey.
Current approach re-allocates the accumulator each iteration; mutating the accumulator reduces allocations on large arrays.

♻️ Proposed refactor
 export function reduceByKey<T extends Record<string, unknown>>(
   data: T[],
   key: keyof T
 ): Record<string, T> {
-  return data.reduce(
-    (acc, value) => ({
-      ...acc,
-      [String(value[key])]: value,
-    }),
-    {} as Record<string, T>
-  );
+  return data.reduce((acc, value) => {
+    acc[String(value[key])] = value;
+    return acc;
+  }, {} as Record<string, T>);
 }
web/lib/admin/views/roles/details.tsx (1)

4-12: Consider a null guard to avoid empty detail rendering.
If role is null, the component currently renders empty fields; an early return (or explicit empty state) keeps the UI cleaner and removes optional chaining.

♻️ Suggested guard
 export default function RoleDetails({ role }: { role: Role | null }) {
+  if (!role) return null;
   return (
     <Flex direction="column" gap={9}>
-      <Text size={4}>{role?.name}</Text>
+      <Text size={4}>{role.name}</Text>
       <Flex direction="column" gap={9}>
         <Grid columns={2} gap="small">
           <Text size={1}>Name</Text>
-          <Text size={1}>{role?.name}</Text>
+          <Text size={1}>{role.name}</Text>
         </Grid>
       </Flex>
     </Flex>
   );
 }
web/lib/admin/components/PageTitle.tsx (1)

8-16: Preserve the previous document title on unmount.
The cleanup currently resets to appName/empty, which can overwrite a prior title set elsewhere.

♻️ Suggested adjustment
-import { useEffect } from "react";
+import { useEffect, useRef } from "react";
@@
 export function PageTitle({ title, appName }: PageTitleProps) {
   const fullTitle = title && appName ? `${title} | ${appName}` : title ?? appName ?? "";
+  const prevTitleRef = useRef<string | null>(null);

   useEffect(() => {
+    if (prevTitleRef.current === null) {
+      prevTitleRef.current = document.title;
+    }
     if (fullTitle) document.title = fullTitle;
     return () => {
-      document.title = appName ?? "";
+      if (prevTitleRef.current !== null) {
+        document.title = prevTitleRef.current;
+      }
     };
-  }, [fullTitle, appName]);
+  }, [fullTitle]);
@@
   return null;
 }
web/lib/admin/views/roles/columns.tsx (1)

31-36: Prefer stable keys for permissions list items.
Line 33-34 uses the array index as the key; if permissions reorder, React can reuse DOM incorrectly. Consider keying by permission value (or a composite key if duplicates are possible).

♻️ Suggested tweak
-          {((info.getValue() as string[]) || []).map((p, i) => (
-            <span key={i}>{p}</span>
-          ))}
+          {((info.getValue() as string[]) || []).map(p => (
+            <span key={p}>{p}</span>
+          ))}
web/lib/admin/components/PageHeader.tsx (2)

5-10: Breadcrumb href property is defined but never used.

The PageHeaderTypes defines href?: string for breadcrumb items, but the component renders plain <span> elements (lines 32-34) instead of anchor tags. The CSS also has styles for .breadcrumb a that won't apply.

Either remove the href property from the type if links aren't needed, or render anchors when href is provided:

♻️ Proposed fix to support href
          {breadcrumb.map((item) => (
-            <span key={item.name} className={styles.breadcrumbItem}>
-              {item.name}
-            </span>
+            item.href ? (
+              <a key={item.name} href={item.href}>
+                {item.name}
+              </a>
+            ) : (
+              <span key={item.name} className={styles.breadcrumbItem}>
+                {item.name}
+              </span>
+            )
          ))}

31-35: Consider a more robust key for breadcrumb items.

Using item.name as the key could cause issues if breadcrumb items have duplicate names. Consider using the index as a fallback:

♻️ Proposed fix
-          {breadcrumb.map((item) => (
-            <span key={item.name} className={styles.breadcrumbItem}>
+          {breadcrumb.map((item, index) => (
+            <span key={`${item.name}-${index}`} className={styles.breadcrumbItem}>
web/lib/admin/components/page-header.module.css (2)

9-27: Anchor styles are currently unused.

The .breadcrumb a selectors (lines 9-27) define styles for anchor tags, but the PageHeader component currently renders <span> elements with the .breadcrumbItem class. These styles won't apply until the component is updated to render anchors when href is provided.


11-11: Consider using design tokens instead of hardcoded colors.

Hardcoded color values like #007bff and #666 may not align with the design system. If @raystack/apsara provides CSS variables or tokens, consider using them for consistency.

Also applies to: 18-18, 35-35

@paanSinghCoder paanSinghCoder removed the request for review from rohilsurana February 10, 2026 08:23
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.

3 participants