Conversation
* Dialog created * small changes
…3654) * Dialog created * small changes * half functional * small spacing changes and chevron * Rabbit Changes * remove useffect
…3663) * Dialog created * small changes * half functional * small spacing changes and chevron * Rabbit Changes * remove useffect * almost * re ordered badge list collapse * undo rabbit * [autofix.ci] apply automated fixes * fix scroll * button size and margin --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* Dialog created * small changes * half functional * small spacing changes and chevron * Rabbit Changes * remove useffect * almost * re ordered badge list collapse * undo rabbit * [autofix.ci] apply automated fixes * fix scroll * changes before merge * functioning again * style change * more tweaks * Fix all the fucking things * remove close button * fmt --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: James Perkins <jamesperkins@hey.com>
* Dialog created * small changes * half functional * small spacing changes and chevron * Rabbit Changes * remove useffect * almost * re ordered badge list collapse * undo rabbit * [autofix.ci] apply automated fixes * fix scroll * changes before merge * functioning again * style change * more tweaks * Fix all the fucking things * remove close button * fmt * dialog and confirm added --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: James Perkins <jamesperkins@hey.com>
* Dialog created * small changes * half functional * small spacing changes and chevron * Rabbit Changes * remove useffect * almost * re ordered badge list collapse * undo rabbit * [autofix.ci] apply automated fixes * fix scroll * changes before merge * chore: deprecate v1 endpoints (#3680) * fix: openapi * chore: deprecate v1 endpoints * fix: vault credentials and chproxy config (#3681) * fix: openapi * fix: vault credentials and chproxy config * fix: rename flag accessor too * fix: linter issues * fix: some more v2 api changes (#3677) * remove namespaceID * actually use limit and cursor * filter out delted overrides * fix error messages list endpoints * fix more error messages * ensure identity create handles like permission/role create * fix regex for roles * fix regex for roles * fix list keys cursor * fix: uppercase common files (#3683) * name files uppercase * name files uppercase * [autofix.ci] apply automated fixes * name files uppercase * name files uppercase --------- Co-authored-by: Andreas Thomas <dev@chronark.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * fix: conflicting casing (#3689) * fix the openapi spec again (#3692) * dont trace chproxy endpoints (#3691) * fix: log verifications to the owning workspace (#3693) * functioning again * style change * more tweaks * fix: validate s3 config (#3694) * Fix all the fucking things * remove close button * fmt * fix: speakeasy ignore directive is ignored if it's a string (#3699) * fix: upsert permissions with slug or name colission (#3696) * fix: upsert permissions with slug or name colission * chore: also remove index * fix permission test and remove unnnecessary test --------- Co-authored-by: Flo <53355483+Flo4604@users.noreply.github.com> * docs: migration (#3678) * fix: openapi * docs: migration from v1 to v2 * fix: remove binaries * [autofix.ci] apply automated fixes * docs: add james' feedback --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * fix: api list keys zod errors (#3702) * fix api zod errors * [autofix.ci] apply automated fixes * make array handling uniform * make array handling uniform * fix rabbi comment * fix: permission array for roles --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * ci: don't build for windows and freebsd (#3700) * docs: errors (#3703) * chore: move sdks to unkeyed/sdks (#3701) * fix: omitting array vs null (#3704) * fix omitting array vs null * [autofix.ci] apply automated fixes * fix flakey test * fix flakey test --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * fix: panics not being catched (#3706) * fix panics not being catched * feat: add counter to track panics Signed-off-by: Ian Meyer <k@imeyer.io> --------- Signed-off-by: Ian Meyer <k@imeyer.io> Co-authored-by: Ian Meyer <k@imeyer.io> * docs: use `CodeGroup` in hono/nextjs TS libraries (#3708) * Update hono.mdx * Update nextjs.mdx * ci: remove outdated steps and flows (#3709) * docs: update sdks (#3712) * docs: update sdks * Update nextjs.mdx * [autofix.ci] apply automated fixes * fix: rabbit feedback * Update nextjs.mdx * fix: root key is required --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * dialog and confirm added * feat(deployment beta): projects UI for Unkey Deploy (#3662) * projects and branches * wip * wip * spec differ wip * fix some docker, add some trpc, integrate diff viewer * change version to deployments, add feature flag, update go schema * update versions page * fix null condition * delete old router, fix null assertion * fmt * fmt * fmt again * apply auth and feature flagging access to projects, remove versions * yolo * stable yolo * stable yolo * style: fmt * fix: hardcode time, so it doesn't fail on the first of a month * [autofix.ci] apply automated fixes --------- Co-authored-by: chronark <dev@chronark.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * chore: add missingd delete endpoint for v2 (#3711) * only log key at the end (#3716) * fix: allow wildcard and colon in permissions query (#3717) * remove regex for permissions * allow for asterix and colon in permissions * fix: update identity by identity key instead of externalId and fix wrong body for permission and role (#3713) * docs and remove externalId from keyResponse * fix updateIdentity to take in an identity parameter instead of an externalId * fix get role/permission * Update go/apps/api/openapi/spec/common/Permission.yaml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update openapi-generated.yaml * fix comment --------- Co-authored-by: Andreas Thomas <dev@chronark.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: openapi references (#3723) * feat: add paginated tRPC endpoint for projects (#3697) * feat: add new endpoint for deploy projects * chore: replace file path * [autofix.ci] apply automated fixes * feat: add missing endpoint * fix: trpc path * fix: add feature flag * chore: remove optin --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * fix: region not showing and wrong rl id (#3722) * fix: region not showing and wrong rl id * pass region down * perf: bad get key performance (#3724) * perf: make getKey 2 seperate queries so mysql chooses correct idx * fix query * fix query name * docs: verify identities endpoints (#3727) * chore: docs (#3728) * chore: fixup migration guide * adjust more * adjust more * adjust more * rabbit comments * Update index.mdx * working updates * re factor for clarity * only update if diff than existing * [autofix.ci] apply automated fixes * re name create-root-key to root-key folder --------- Signed-off-by: Ian Meyer <k@imeyer.io> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Andreas Thomas <dev@chronark.com> Co-authored-by: Flo <53355483+Flo4604@users.noreply.github.com> Co-authored-by: Oğuzhan Olguncu <21091016+ogzhanolguncu@users.noreply.github.com> Co-authored-by: James Perkins <jamesperkins@hey.com> Co-authored-by: Ian Meyer <k@imeyer.io> Co-authored-by: JA Castro <51177379+ubinatus@users.noreply.github.com> Co-authored-by: Meg Stepp <mcstepp@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Thank you for following the naming conventions for pull request titles! 🙏 |
📝 WalkthroughWalkthroughRefactors root-key UI and permission system: adds typed permission catalog, reducer, hooks, and many client components (dialog, sheet, list, badges, toggles, search); centralizes constants/messages; replaces add/remove RBAC mutations with updateRootKeyPermissions; updates table/navigation to dialog flows; enhances UI primitives and removes legacy per-key pages/components. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Table as RootKeys Table
participant Dialog as RootKeyDialog
participant Hook as useRootKeyDialog
participant TRPC as TRPC Router
participant DB as Database
User->>Table: click "Edit root key..."
Table->>Dialog: open(existingKey) via onEditKey
Dialog->>Hook: init (name, permissions, fetch APIs)
User->>Dialog: modify name/permissions
User->>Dialog: click Save
Dialog->>Hook: handleCreateKey / handleUpdate
alt edit mode
Hook->>TRPC: rootKey.update { name?, permissions? }
TRPC->>DB: update key & permissions
DB-->>TRPC: OK
TRPC-->>Hook: success
Hook-->>Dialog: close
else create mode
Hook->>TRPC: rootKey.create { name, permissions }
TRPC->>DB: insert key + permissions
DB-->>TRPC: OK (key, secret)
TRPC-->>Hook: success
Hook-->>Dialog: show RootKeySuccess
end
sequenceDiagram
participant User
participant Sheet as PermissionSheet
participant Hook as usePermissionSheet
participant Util as permissions utils
User->>Sheet: type search
Sheet->>Hook: handleSearchChange(value)
Hook->>Sheet: set isProcessing, searchValue
Sheet->>Util: filterPermissionList(searchValue)
Util-->>Sheet: filtered lists
User->>Sheet: toggle root/category/permission
Sheet->>Hook: handleApiPermissionChange / handleWorkspacePermissionChange
Hook->>Caller: onChange(mergedPermissions)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
There was a problem hiding this comment.
Actionable comments posted: 42
🔭 Outside diff range comments (2)
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (1)
32-41: Prevent row selection when clicking the actions placeholderWhile the dynamic import is loading, the placeholder button doesn’t stop event propagation. Clicking it will bubble to the row and trigger row selection or other row handlers, which is inconsistent with the real component behavior.
Apply this diff to stop propagation on the placeholder trigger:
loading: () => ( <button type="button" + onClick={(e) => e.stopPropagation()} className={cn( "group-data-[state=open]:bg-gray-6 group-hover:bg-gray-6 group size-5 p-0 rounded m-0 items-center flex justify-center", "border border-gray-6 group-hover:border-gray-8 ring-2 ring-transparent focus-visible:ring-gray-7 focus-visible:border-gray-7", )} >apps/dashboard/app/(app)/settings/root-keys/components/table/components/actions/root-keys-table-action.popover.constants.tsx (1)
21-31: Disable the “Edit” action when no handler is providedIf onEditKey is undefined, the item currently does nothing. Disable it to avoid a dead control, or provide a fallback navigation.
Apply this diff:
{ id: "edit-root-key", label: "Edit root key...", icon: <PenWriting3 size="md-regular" />, onClick: () => { onEditKey?.(rootKey); }, + disabled: !onEditKey, divider: true, },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (28)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/README.md(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/highlighted-text.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-input.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permissions.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/utils/permissions.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/table/components/actions/root-keys-table-action.popover.constants.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx(7 hunks)apps/dashboard/app/(app)/settings/root-keys/navigation.tsx(2 hunks)apps/dashboard/components/ui/sheet.tsx(3 hunks)apps/dashboard/lib/trpc/routers/index.ts(2 hunks)apps/dashboard/lib/trpc/routers/key/updateRootKeyPermissions.ts(1 hunks)internal/icons/src/icons/minus.tsx(1 hunks)internal/icons/src/index.ts(1 hunks)internal/ui/src/components/form/checkbox.tsx(11 hunks)internal/ui/src/index.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Use Biome for formatting and linting in TypeScript/JavaScript projects
Prefer named exports over default exports in TypeScript/JavaScript, except for Next.js pages
Files:
internal/icons/src/index.tsinternal/icons/src/icons/minus.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsxinternal/ui/src/index.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/lib/trpc/routers/key/updateRootKeyPermissions.tsapps/dashboard/app/(app)/settings/root-keys/navigation.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permissions.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/lib/trpc/routers/index.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/utils/permissions.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-input.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/highlighted-text.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/components/actions/root-keys-table-action.popover.constants.tsxinternal/ui/src/components/form/checkbox.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsxapps/dashboard/components/ui/sheet.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Follow strict TypeScript configuration
Use Zod for runtime validation in TypeScript projects
Files:
internal/icons/src/index.tsinternal/icons/src/icons/minus.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsxinternal/ui/src/index.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/lib/trpc/routers/key/updateRootKeyPermissions.tsapps/dashboard/app/(app)/settings/root-keys/navigation.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permissions.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/lib/trpc/routers/index.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/utils/permissions.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-input.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/highlighted-text.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/components/actions/root-keys-table-action.popover.constants.tsxinternal/ui/src/components/form/checkbox.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsxapps/dashboard/components/ui/sheet.tsx
**/*.{env,js,ts,go}
📄 CodeRabbit Inference Engine (CLAUDE.md)
All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Files:
internal/icons/src/index.tsinternal/ui/src/index.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/lib/trpc/routers/key/updateRootKeyPermissions.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permissions.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/lib/trpc/routers/index.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/utils/permissions.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.ts
🧠 Learnings (1)
📚 Learning: 2024-12-03T14:17:08.016Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/app/(app)/logs/logs-page.tsx:77-83
Timestamp: 2024-12-03T14:17:08.016Z
Learning: The `<LogsTable />` component already implements virtualization to handle large datasets efficiently.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
🧬 Code Graph Analysis (5)
internal/icons/src/icons/minus.tsx (1)
internal/icons/src/props.ts (2)
IconProps(30-35)sizeMap(7-28)
apps/dashboard/app/(app)/settings/root-keys/components/table/components/actions/root-keys-table-action.popover.constants.tsx (3)
apps/dashboard/lib/trpc/routers/settings/root-keys/query.ts (1)
RootKey(35-35)apps/dashboard/components/logs/table-action.popover.tsx (2)
TableActionPopover(29-148)MenuItem(12-22)internal/icons/src/icons/pen-writing-3.tsx (1)
PenWriting3(15-45)
internal/ui/src/components/form/checkbox.tsx (2)
internal/icons/src/icons/check.tsx (1)
Check(15-38)internal/icons/src/icons/minus.tsx (1)
Minus(15-38)
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (5)
apps/dashboard/lib/trpc/routers/settings/root-keys/query.ts (1)
RootKey(35-35)apps/dashboard/app/(app)/settings/root-keys/components/table/utils/get-row-class.ts (1)
getRowClassName(26-38)apps/dashboard/app/(app)/settings/root-keys/components/table/components/skeletons.tsx (6)
RootKeyColumnSkeleton(4-13)KeyColumnSkeleton(20-25)CreatedAtColumnSkeleton(15-19)PermissionsColumnSkeleton(27-34)LastUpdatedColumnSkeleton(36-41)ActionColumnSkeleton(43-53)packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/components/virtual-table/index.tsx (1)
VirtualTable(38-404)
apps/dashboard/components/ui/sheet.tsx (1)
internal/icons/src/icons/xmark.tsx (1)
XMark(15-51)
🪛 LanguageTool
apps/dashboard/app/(app)/settings/root-keys/components/root-key/README.md
[grammar] ~1-~1: Use correct spacing
Context: # Root Key Components This directory contains the refactored r...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~3-~3: Use correct spacing
Context: ...key management components for the Unkey dashboard. ## Architecture Overview The components ha...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~5-~5: Use correct spacing
Context: ...r the Unkey dashboard. ## Architecture Overview The components have been refactored to f...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~7-~7: Use correct spacing
Context: ... to follow a clean architecture pattern with: - Custom Hooks: Business logic extracted...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~12-~12: Use correct spacing
Context: ...own into focused, single-responsibility pieces ## Directory Structure ``` root-key/ ├── c...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~14-~14: Use correct spacing
Context: ...gle-responsibility pieces ## Directory Structure root-key/ ├── components/ # UI components │ ├── expandable-category.tsx │ ├── highlighted-text.tsx │ ├── permission-badge-list.tsx │ ├── permission-list.tsx │ ├── permission-sheet.tsx │ ├── permission-toggle.tsx │ ├── search-input.tsx │ └── search-permissions.tsx ├── hooks/ # Custom hooks for business logic │ ├── use-permissions.ts │ ├── use-permission-sheet.ts │ ├── use-root-key-dialog.ts │ └── use-root-key-success.ts ├── utils/ # Utility functions │ └── permissions.ts ├── constants.ts # Shared constants and messages ├── create-rootkey-button.tsx ├── root-key-dialog.tsx ├── root-key-success.tsx └── README.md ## Key Improvements ### 1. Custom Hooks - ...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~41-~41: Use correct spacing
Context: ...y-success.tsx └── README.md ``` ## Key Improvements ### 1. Custom Hooks - usePermissions: ...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~43-~43: There might be a mistake here.
Context: ...``` ## Key Improvements ### 1. Custom Hooks - usePermissions: Manages permission state and logic - *...
(QB_NEW_EN_OTHER)
[grammar] ~47-~47: Use correct spacing
Context: ...ss**: Handles success dialog state and navigation ### 2. Utility Functions - **permissions.ts...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~49-~49: There might be a mistake here.
Context: ...og state and navigation ### 2. Utility Functions - permissions.ts: Centralized permission management util...
(QB_NEW_EN_OTHER)
[grammar] ~51-~51: Use correct spacing
Context: ...ts`**: Shared constants and user-facing messages ### 3. Component Refactoring - **Smaller, fo...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~54-~54: There might be a mistake here.
Context: ...mponents**: Each component has a single responsibility - Better separation of concerns: UI logi...
(QB_NEW_EN_OTHER)
[grammar] ~55-~55: Use modal and auxiliary verbs correctly
Context: ...- Better separation of concerns: UI logic separated from business logic - **Impro...
(QB_NEW_EN_OTHER_ERROR_IDS_24)
[grammar] ~55-~55: There might be a mistake here.
Context: ...rns**: UI logic separated from business logic - Improved reusability: Components are m...
(QB_NEW_EN_OTHER)
[grammar] ~56-~56: There might be a mistake here.
Context: ...lity**: Components are more modular and reusable - Enhanced type safety: Better TypeScrip...
(QB_NEW_EN_OTHER)
[grammar] ~57-~57: There might be a mistake here.
Context: ... type safety**: Better TypeScript types throughout ### 4. Code Organization - **Reduced duplica...
(QB_NEW_EN_OTHER)
[grammar] ~62-~62: Use correct spacing
Context: ...ier to understand and modify individual pieces ## Usage Examples ### Creating a Root Key ...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~64-~64: Use correct spacing
Context: ... and modify individual pieces ## Usage Examples ### Creating a Root Key ```tsx import { Crea...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~66-~66: Use correct spacing
Context: ... ## Usage Examples ### Creating a Root Key tsx import { CreateRootKeyButton } from "./create-rootkey-button"; <CreateRootKeyButton /> ### Using Permission Management ```tsx impor...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~73-~73: Use correct spacing
Context: ...tKeyButton /> ### Using Permission Managementtsx import { usePermissions } from "./hooks/use-permissions"; const { state, handlePermissionToggle } = usePermissions({ type: "workspace", selected: permissions, onPermissionChange: setPermissions, }); ### Using Constantstsx import { ROOT_KEY...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~84-~84: Use correct spacing
Context: ...nge: setPermissions, }); ### Using Constantstsx import { ROOT_KEY_MESSAGES } from "./constants"; {ROOT_KEY_MESSAGES.UI.CREATE_ROOT_KEY} ``` ## Benefits 1. Maintainability: Easier...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~94-~94: There might be a mistake here.
Context: ...be reused across different parts of the app 3. Testability: Business logic in hooks i...
(QB_NEW_EN_OTHER)
[grammar] ~95-~95: There might be a mistake here.
Context: ...ess logic in hooks is easier to test in isolation 4. Type Safety: Better TypeScript support...
(QB_NEW_EN_OTHER)
[grammar] ~98-~98: Use correct spacing
Context: ...ncy**: Standardized patterns across all components ## Migration Notes The refactoring maintai...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
[grammar] ~100-~100: Use correct spacing
Context: ...rns across all components ## Migration Notes The refactoring maintains backward compa...
(QB_NEW_EN_OTHER_ERROR_IDS_5)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Test Go API Local / Test
- GitHub Check: Test API / API Test Local
- GitHub Check: Test Packages / Test
- GitHub Check: Build / Build
- GitHub Check: autofix
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (37)
internal/ui/src/index.ts (1)
22-22: LGTM! Export is properly positioned and consistent.The export for the search-icon component is correctly added after the main llm-search export, maintaining logical grouping and making the SearchIcon component available for use in the root-key UI components.
internal/icons/src/icons/minus.tsx (1)
15-38: LGTM! Well-implemented icon component following established patterns.The Minus icon component correctly follows the established icon patterns in the codebase:
- Properly imports and uses the
IconPropsandsizeMapfrom props- Uses the default "xl-thin" size consistent with other icons
- Correctly implements the SVG with proper dimensions, stroke properties, and accessibility features
- Path data "M3.25 9H14.75" creates a proper horizontal line centered in the 18x18 viewBox
This icon will be used for the indeterminate state in checkboxes as part of the root key permission UI enhancements.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/highlighted-text.tsx (1)
15-17: Robust regex implementation with proper escaping.The implementation correctly escapes special regex characters and creates a case-insensitive global regex with capturing groups. This will safely handle user input containing regex metacharacters.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/README.md (1)
1-102: Comprehensive documentation with clear architecture overview.The README provides excellent documentation of the refactored root key components, clearly outlining the architectural improvements, directory structure, and usage examples. The separation of concerns into hooks, utilities, constants, and focused components demonstrates good software engineering practices.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx (2)
30-36: Verify CheckedState handling logic.The logic maps indeterminate state to
falseand toggles boolean states, but this differs from the existing permission toggle implementation in the codebase which uses TRPC mutations and optimistic updates.This component appears to be a simpler version compared to the existing
apps/dashboard/app/(app)/settings/root-keys/[keyId]/permissions/permission_toggle.tsxwhich includes server-side mutations. Verify that this new component is intended for local state management only and that server-side persistence is handled elsewhere in the permission management flow.
26-50: Clean UI implementation with good accessibility.The component provides a well-structured UI with:
- Proper checkbox interaction using Radix CheckedState
- Clear visual hierarchy with category → label flow
- InfoTooltip for additional context
- Hover states and responsive styling
The implementation follows the design system patterns established in the codebase.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (2)
1-6: LGTM! Well-structured constants module.The constants are properly typed with
as constfor literal types and provide sensible defaults. The environment variable follows the required naming convention.
8-56: LGTM! Comprehensive message catalog for the Root Key UI.The message structure is well-organized with clear categorization (SUCCESS, ERROR, WARNING, UI, PLACEHOLDERS, DESCRIPTIONS). The
as constassertion ensures type safety for all string literals.apps/dashboard/app/(app)/settings/root-keys/components/root-key/utils/permissions.ts (2)
1-201: LGTM! Well-structured permission management utilities.The implementation provides a comprehensive and type-safe permission management system with proper state management through a reducer pattern. The utility functions are well-organized and the use of
CheckedStatefor tri-state checkboxes is appropriate for hierarchical permission structures.
97-98: Optimize permission length comparison.Consider checking for an exact match first before computing the full comparison, as it's more efficient.
// Compute root checked state let rootChecked: CheckedState = false; if (selectedPermissions.length === 0) { rootChecked = false; - } else if (selectedPermissions.length === allPermissionNames.length) { + } else if (selectedPermissions.length === allPermissionNames.length && + allPermissionNames.every(p => selectedPermissions.includes(p))) { rootChecked = true; } else { rootChecked = "indeterminate"; }Likely an incorrect or invalid review comment.
internal/icons/src/index.ts (1)
71-71: LGTM! Correctly added Minus icon export.The new export is properly placed in alphabetical order between "magnifier" and "moon-stars".
apps/dashboard/app/(app)/settings/root-keys/navigation.tsx (1)
7-7: LGTM: switch to dedicated CreateRootKeyButtonThe import looks correct and aligns with the modular create flow introduced by the PR.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permissions.ts (1)
26-35: AllusePermissionscalls withtype="api”supply anapiprop
Verified thatPermissionContentList(inpermission-sheet.tsx) always passesapiwhentype="api", and there are no other directusePermissionsusages. The empty‐object fallback only applies if someone misuses the hook in the future. Adding a development‐only guard is optional but not required to protect current callers.apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-input.tsx (3)
20-33: LGTM! Well-structured component with clear props and responsibilitiesThe component has a clear separation of concerns with appropriate prop naming and types. The TypeScript types are properly defined, and the component follows React best practices.
34-41: LGTM! Clear conditional rendering logicThe loading state display logic is appropriately implemented with proper conditional rendering based on
searchMode.
43-59: LGTM! Well-implemented input with appropriate accessibility attributesThe input implementation properly uses forwarded refs, includes data-testid for testing, and has appropriate disabled state handling.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (2)
133-159: LGTM! Well-organized permission display structureThe ScrollArea implementation with PermissionBadgeList components provides a clean UI for displaying selected permissions with proper separation between workspace and API permissions.
161-167: RootKeySuccess already guards against undefined propsThe
RootKeySuccesscomponent immediately returnsnullwhen eitherkeyValueorkeyIdis falsy (see root-key-success.tsx:42–44), so it won’t render if those props are undefined. No further changes required.apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsx (3)
73-82: LGTM! Clean conditional rendering with proper null checksThe component properly calculates workspace vs API display text and safely handles optional api properties.
116-129: LGTM! Well-structured permission rendering with search highlightingThe implementation properly highlights search terms and maps permissions with appropriate descriptions.
42-54: Potential memory leak with effect dependenciesThe effect updates state based on
searchValueandfilteredPermissionList, but the state setters are not memoized. This could cause unnecessary re-renders.Consider using
useMemofor the expanded state logic:- // State to track expanded categories and root - const [expandedCategories, setExpandedCategories] = useState<Set<string>>(new Set()); - const [isRootExpanded, setIsRootExpanded] = useState(false); - - // Auto-expand when search is active - useEffect(() => { - if (searchValue && searchValue.trim() !== "") { - setIsRootExpanded(true); - setExpandedCategories(new Set(Object.keys(filteredPermissionList))); - } else { - setIsRootExpanded(false); - setExpandedCategories(new Set()); - } - }, [searchValue, filteredPermissionList]); + const { isRootExpanded, expandedCategories } = useMemo(() => { + const hasSearch = searchValue && searchValue.trim() !== ""; + return { + isRootExpanded: hasSearch, + expandedCategories: hasSearch ? new Set(Object.keys(filteredPermissionList)) : new Set() + }; + }, [searchValue, filteredPermissionList]); + + const [localExpandedCategories, setLocalExpandedCategories] = useState(expandedCategories); + + useEffect(() => { + setLocalExpandedCategories(expandedCategories); + }, [expandedCategories]);Likely an incorrect or invalid review comment.
internal/ui/src/components/form/checkbox.tsx (5)
4-4: LGTM! Proper icon imports for tri-state checkbox supportThe addition of the
Minusicon import alongsideCheckenables proper visualization of the indeterminate state.
11-11: LGTM! Group class added for state-based icon visibilityThe addition of the
groupclass to the root element enables the use of group-based data-state selectors for showing/hiding icons.
18-18: LGTM! Comprehensive indeterminate state stylingAll checkbox variants (primary, outline, ghost) and color schemes (danger, warning, success) now properly support the indeterminate state with consistent styling.
Also applies to: 25-25, 32-32, 64-64, 75-75, 86-86, 98-98, 109-109, 120-120, 132-132, 143-143
238-238: LGTM! Type definition properly supports tri-state checkboxThe
checkedprop type now correctly supportsboolean | "indeterminate", enabling tri-state checkbox functionality.
320-321: LGTM! Clean implementation of state-based icon renderingThe icons are properly shown/hidden based on the checkbox state using group-based data-state selectors.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (1)
25-172: Well-structured success dialog implementationThe component is well-organized with clear separation of concerns through the useRootKeySuccess hook. The UI provides comprehensive feedback about the created key with appropriate warnings about key visibility.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.ts (1)
27-28: Remove console.error from production codeConsole statements should be removed from production code.
if (!pendingAction) { - console.error(ROOT_KEY_MESSAGES.ERROR.NO_PENDING_ACTION); + // Log to error monitoring service instead return; }⛔ Skipped due to learnings
Learnt from: ogzhanolguncu PR: unkeyed/unkey#3196 File: apps/dashboard/lib/audit.ts:105-108 Timestamp: 2025-04-25T14:16:27.152Z Learning: The dashboard app in the Unkey codebase uses direct console.* methods (console.info, console.error, etc.) for logging rather than a structured logger utility.apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (1)
30-141: Clean implementation of permission sheet with good separation of concernsThe component effectively manages permissions with search functionality and pagination. The use of the custom hook
usePermissionSheetprovides good separation of business logic.apps/dashboard/components/ui/sheet.tsx (2)
30-34: Overlay variant implementation looks goodThe introduction of overlay variants provides flexibility for different UI flows while maintaining backward compatibility with the default overlay behavior.
80-86: Good implementation of conditional close buttonThe implementation properly handles the conditional rendering of the close button with the
disableCloseprop and allows for custom close icons.apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (2)
177-177: LGTM: Stable selection checks via selectedRootKeyIdSwitching to id-based selection checks is correct and prevents unnecessary re-renders due to object identity changes.
Also applies to: 224-224, 235-235, 261-262
156-167: No changes needed for existingKey mappingThe TRPC schema guarantees that
editingKey.permissionsis always a defined array (viaz.array(PermissionResponse)), and the underlying database enforces unique key-permission pairs, preventing duplicates. Casting eachp.nametoUnkeyPermissionaligns with the expected prop type inRootKeyDialogProps.• RootKeyResponse defines
permissions: z.array(PermissionResponse)→ neverundefined
• DB constraints onkeysPermissionsprevent duplicate entries → no dedup needed
• The existing cast toUnkeyPermissioncorrectly matches the dialog’s prop typeLikely an incorrect or invalid review comment.
apps/dashboard/app/(app)/settings/root-keys/components/table/components/actions/root-keys-table-action.popover.constants.tsx (1)
12-15: LGTM: Decoupled edit action via onEditKeyAccepting an external onEditKey handler cleanly integrates with the dialog-based edit flow introduced in this PR.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.ts (3)
96-108: LGTM: hasNoResults is correctly memoizedThe search-dependent aggregate check across workspace and API permissions is concise and memoized.
110-118: Add unit tests for permission merging and search callbacksThe merging logic in
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.tsis non-trivial and easy to regress. A small test suite would lock in expected behavior by covering:
handleApiPermissionChangemerging logichandleWorkspacePermissionChangemerging logic- Search/filter behavior (
searchValue→hasNoResults)Would you like me to open an issue and follow up with a PR that adds Jest + React Testing Library tests?
3-3: Import path verified; consider centralizing permissions utilities
- The file
apps/dashboard/app/(app)/settings/root-keys/[keyId]/permissions/permissions.tsexists and matches the import in
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.ts- Similar deep-relative imports are used in:
•use-permissions.ts
•permission-badge-list.tsx
•new/client.tsx- While these imports are correct today, their brittleness can lead to breakage if the route structure changes. We recommend moving
apiPermissionsandworkspacePermissionsinto a stable shared module (for example,apps/dashboard/lib/permissions.tsor a centralized barrel) to simplify imports and reduce refactor risk.
...ashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx
Outdated
Show resolved
Hide resolved
...ashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/highlighted-text.tsx
Show resolved
Hide resolved
...hboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
Outdated
Show resolved
Hide resolved
...hboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.ts
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
apps/dashboard/lib/trpc/routers/index.ts (1)
154-158: Avoid duplicated surface area: deprecate single-action RBAC endpoints in favor of rootKey.update.permissionsWith rootKey.update.permissions in place, rbac.addPermissionToRootKey and rbac.removePermissionFromRootKey overlap and risk fragmentation. Recommend deprecating the single-action endpoints (keep them for backward compatibility) and steering callers to the new canonical route. This was raised previously and still applies.
You can start deprecation by:
- Adding deprecation notes next to the new canonical export (inline comment above, see prior suggestion).
- Adding JSDoc deprecation on the RBAC aliases so editors/TS surface warnings.
Outside this hunk, add deprecation JSDoc above the RBAC exports:
/** @deprecated Use rootKey.update.permissions instead (sync authoritative permissions list). */ addPermissionToRootKey: addPermissionToRootKey, /** @deprecated Use rootKey.update.permissions instead (sync authoritative permissions list). */ removePermissionFromRootKey: removePermissionFromRootKey,Optionally, plan a follow-up to either:
- Remove the RBAC add/remove routes after a deprecation window, or
- Move them under a distinct namespace (e.g., rbac.legacy.*) to make intent explicit.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
apps/dashboard/lib/trpc/routers/index.ts(2 hunks)internal/icons/src/index.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Use Biome for formatting and linting in TypeScript/JavaScript projects
Prefer named exports over default exports in TypeScript/JavaScript, except for Next.js pages
Files:
internal/icons/src/index.tsapps/dashboard/lib/trpc/routers/index.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Follow strict TypeScript configuration
Use Zod for runtime validation in TypeScript projects
Files:
internal/icons/src/index.tsapps/dashboard/lib/trpc/routers/index.ts
**/*.{env,js,ts,go}
📄 CodeRabbit Inference Engine (CLAUDE.md)
All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Files:
internal/icons/src/index.tsapps/dashboard/lib/trpc/routers/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Test Go API Local / Test
- GitHub Check: Build / Build
- GitHub Check: Test API / API Test Local
- GitHub Check: Test Packages / Test
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: autofix
🔇 Additional comments (3)
internal/icons/src/index.ts (1)
75-75: No changes needed—barrel export is correctThe
minus.tsxfile declares a named export (export const Minus…), soexport * from "./icons/minus";correctly re-exportsMinus.apps/dashboard/lib/trpc/routers/index.ts (2)
66-66: LGTM on import; keeps related handlers co-locatedImporting updateRootKeyPermissions right next to updateRootKeyName maintains locality and consistency. No issues spotted with naming or path.
154-158: Route addition is consistent with existing update router shapeAdding rootKey.update.permissions mirrors the noun-based keys under the other update routers (e.g., name, metadata). Good alignment.
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (11)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (2)
38-38: Responsive padding for mobile (pre-existing suggestion)The fixed px-[120px] overflows on small viewports. Switch to responsive padding.
- <div className="bg-grayA-2 py-10 flex flex-col items-center justify-center w-full px-[120px]"> + <div className="bg-grayA-2 py-10 flex flex-col items-center justify-center w-full px-6 sm:px-12 md:px-[120px]">
48-51: Mark decorative icons as aria-hidden and non-focusableBoth icons are decorative. Hide them from a11y tree and prevent focus.
- <Key2 size="2xl-thin" aria-hidden="true" /> + <Key2 size="2xl-thin" aria-hidden="true" focusable="false" /> <div className="flex items-center justify-center border border-grayA-3 rounded-full bg-success-9 text-white size-[22px] absolute right-[-10px] top-[-10px]"> - <Check size="sm-bold" /> + <Check size="sm-bold" aria-hidden="true" focusable="false" /> </div>apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (4)
86-93: Decouple submit loading from background fetchingUse isMutating for the button spinner. Keep background apisLoading out of the primary action’s loading state.
- disabled={!hasChanges || isMutating} + disabled={!hasChanges || isMutating} onClick={handleCreateKey} - loading={isBusy} + loading={isMutating}
146-158: Centralize “Selected from”, “Workspace”, and “from” labelsReplace literals with constants for consistency/i18n.
- title="Selected from" - name="Workspace" + title={ROOT_KEY_MESSAGES.UI.PERMISSION_SELECTED_FROM} + name={ROOT_KEY_MESSAGES.UI.WORKSPACE} @@ - title="from" + title={ROOT_KEY_MESSAGES.UI.PERMISSION_FROM}Add keys in constants.ts (UI section):
- PERMISSION_SELECTED_FROM: "Selected from"
- PERMISSION_FROM: "from"
- WORKSPACE: "Workspace"
98-101: Prefer semantic color tokens to fix light-mode contrasttext-gray-9 may fail contrast in light mode (see PR comment). Use design-system semantic tokens if available.
- <div className="text-gray-9 text-xs"> + <div className="text-secondary-foreground text-xs">Please verify that text-secondary-foreground (or your equivalent) exists in Tailwind config. If not, map one in tailwind.config and apply here.
118-120: Use semantic tokens for labels (light-mode issue)Similar to above, prefer semantic/muted foreground tokens over raw grays.
- <Label className="text-[13px] font-regular text-gray-10"> + <Label className="text-[13px] font-regular text-muted-foreground">Confirm the token name; adjust to your design system (e.g., text-secondary-foreground or text-muted-foreground).
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx (2)
8-15: Rename generic Props to a descriptive typeImproves readability and avoids conflicts.
-type Props = { +type SearchPermissionsProps = { isProcessing: boolean; search: string | undefined; inputRef: RefObject<HTMLInputElement>; onChange: (e: ChangeEvent<HTMLInputElement>) => void; }; -const SearchPermissions = ({ isProcessing, search, inputRef, onChange }: Props) => { +const SearchPermissions = ({ isProcessing, search, inputRef, onChange }: SearchPermissionsProps) => {
30-46: Wire isLoading to actual search stateCurrently hardcoded false; reflect real searching when processing a non-empty query.
- return ( + const isSearching = isProcessing && (search?.trim().length ?? 0) > 0; + return ( @@ - <SearchInput + <SearchInput value={search ?? ""} placeholder={ROOT_KEY_MESSAGES.UI.SEARCH_PERMISSIONS} isProcessing={isProcessing} - isLoading={false} + isLoading={isSearching} loadingText="Searching..." clearingText="Clearing..." searchMode={SEARCH_MODES.MANUAL} onChange={onChange} inputRef={inputRef} />Also applies to: 37-41
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (3)
93-94: Prefer human-friendly secondary textShow the category/description instead of internal ids.
- renderSecondaryText={(permission) => permission.id} + renderSecondaryText={(permission) => permission.description}
98-105: Avoid duplicate className prop in intersection typeclassName is already in React.ComponentProps.
type CollapsibleListProps = { info: PermissionInfo; title: string; expandCount: number; removePermission: (permission: UnkeyPermission) => void; name: string; -} & React.ComponentProps<typeof CollapsibleTrigger>; +} & React.ComponentProps<typeof CollapsibleTrigger>;Remove className from the explicit part if it was declared there earlier (keep relying on ComponentProps).
154-168: Handle wildcard "*" permissionsWhen "" is selected, the list becomes empty because there’s no exact match. Treat "" as “all permissions”.
const findPermission = ( allPermissions: PermissionInfo, selectedPermissions: UnkeyPermission[], ): PermissionInfo => { if (!selectedPermissions || !Array.isArray(selectedPermissions)) { return []; } + // Treat wildcard as "all permissions" + if (selectedPermissions.includes("*" as UnkeyPermission)) { + return allPermissions; + } return selectedPermissions .map((permission) => { return allPermissions.find((p) => p.permission === permission); }) .filter((item): item is { permission: UnkeyPermission; category: string; action: string } => Boolean(item), ); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
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.
📒 Files selected for processing (5)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Use Biome for formatting and linting in TypeScript/JavaScript projects
Prefer named exports over default exports in TypeScript/JavaScript, except for Next.js pages
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Follow strict TypeScript configuration
Use Zod for runtime validation in TypeScript projects
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
**/*.{env,js,ts,go}
📄 CodeRabbit Inference Engine (CLAUDE.md)
All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
📚 Learning: 2024-12-03T14:07:45.173Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/components/ui/group-button.tsx:21-31
Timestamp: 2024-12-03T14:07:45.173Z
Learning: In the `ButtonGroup` component (`apps/dashboard/components/ui/group-button.tsx`), avoid suggesting the use of `role="group"` in ARIA attributes.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2024-10-23T16:19:42.049Z
Learnt from: p6l-richard
PR: unkeyed/unkey#2085
File: apps/www/components/glossary/search.tsx:41-57
Timestamp: 2024-10-23T16:19:42.049Z
Learning: For the `FilterableCommand` component in `apps/www/components/glossary/search.tsx`, adding error handling and loading states to the results list is not necessary.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx
📚 Learning: 2024-10-04T20:44:38.489Z
Learnt from: chronark
PR: unkeyed/unkey#2180
File: apps/dashboard/lib/constants/workspace-navigations.tsx:56-118
Timestamp: 2024-10-04T20:44:38.489Z
Learning: When typing the `workspace` parameter in functions like `createWorkspaceNavigation`, prefer importing the `Workspace` type from the database module and picking the necessary keys (e.g., `features`) instead of redefining the interface.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2025-04-08T09:34:24.576Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test Go API Local / Test
- GitHub Check: Test API / API Test Local
- GitHub Check: Test Packages / Test
- GitHub Check: Build / Build
- GitHub Check: Analyze (javascript-typescript)
...hboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (4)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (1)
38-38: Responsive padding: avoid fixed 120px on small screensFixed px-[120px] causes overflow on mobile. Use responsive padding to preserve spacing on larger screens without breaking small viewports.
- <div className="bg-grayA-2 py-10 flex flex-col items-center justify-center w-full px-[120px]"> + <div className="bg-grayA-2 py-10 flex flex-col items-center justify-center w-full px-6 sm:px-12 md:px-[120px]">apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
1-7: Avoid hard-coding API_URL; support environment configuration with UNKEY-prefixed varsMake API_URL configurable for different environments and follow the repository env var convention. Keep a sensible default as fallback.
export const ROOT_KEY_CONSTANTS = { DEFAULT_LIMIT: 10, UNNAMED_KEY: "Unnamed", - API_URL: "https://api.unkey.com", + API_URL: + process.env.NEXT_PUBLIC_UNKEY_DASHBOARD_API_URL ?? + process.env.NEXT_PUBLIC_UNKEY_API_URL ?? + "https://api.unkey.com", EXPAND_COUNT: 3, WORKSPACE: "workspace", } as const;
- Ensure NEXT_PUBLIC_UNKEY_DASHBOARD_API_URL is added to .env.example and deployment configs.
- Confirm no SSR usage requires a server-only env (if so, mirror with UNKEY_DASHBOARD_API_URL on the server side).
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (2)
92-94: Show friendly secondary text (category/description) instead of repeating the idSecondary text currently mirrors the id; use the friendlier category we already pass as description.
<SelectedItemsList className="pt-2 overflow-hidden" items={info.map((permission) => ({ id: permission.permission.toString(), name: permission.action, - description: permission.category, + description: permission.category, }))} @@ - renderSecondaryText={(permission) => permission.id} + renderSecondaryText={(permission) => permission.description}Also applies to: 83-87
154-168: Wildcard permission (“”) not handled — selected “” renders nothingUnkeyPermission includes "" but findPermission maps only exact matches. When "" is selected, the list becomes empty. Treat "*" as “all permissions” (or render a single “All permissions” badge if that’s the desired UX).
const findPermission = ( allPermissions: PermissionInfo, selectedPermissions: UnkeyPermission[], ): PermissionInfo => { if (!selectedPermissions || !Array.isArray(selectedPermissions)) { return []; } + // Treat wildcard as "all permissions" + if (selectedPermissions.includes("*" as UnkeyPermission)) { + return allPermissions; + } return selectedPermissions .map((permission) => { return allPermissions.find((p) => p.permission === permission); }) .filter((item): item is { permission: UnkeyPermission; category: string; action: string } => Boolean(item), ); };If you prefer rendering a single “All permissions” badge instead of expanding all, I can provide an alternate implementation.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
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.
📒 Files selected for processing (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Use Biome for formatting and linting in TypeScript/JavaScript projects
Prefer named exports over default exports in TypeScript/JavaScript, except for Next.js pages
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Follow strict TypeScript configuration
Use Zod for runtime validation in TypeScript projects
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx
**/*.{env,js,ts,go}
📄 CodeRabbit Inference Engine (CLAUDE.md)
All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: For repo unkeyed/unkey and PR review workflows: When imeyer comments "issue" on a thread, automatically create a thorough GitHub issue (sections: Summary, Impact, Where, Repro/Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and the specific comment, and assign the issue to imeyer.
📚 Learning: 2025-08-04T07:44:39.438Z
Learnt from: CR
PR: unkeyed/unkey#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-04T07:44:39.438Z
Learning: Applies to **/*.{env,js,ts,go} : All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
📚 Learning: 2025-07-21T18:05:58.236Z
Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{env,sh,yaml,yml,json,conf,ini} : All environment variables MUST follow the format UNKEY_<SERVICE_NAME>_VARNAME.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
📚 Learning: 2024-12-03T14:07:45.173Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/components/ui/group-button.tsx:21-31
Timestamp: 2024-12-03T14:07:45.173Z
Learning: In the `ButtonGroup` component (`apps/dashboard/components/ui/group-button.tsx`), avoid suggesting the use of `role="group"` in ARIA attributes.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx
📚 Learning: 2024-10-04T20:44:38.489Z
Learnt from: chronark
PR: unkeyed/unkey#2180
File: apps/dashboard/lib/constants/workspace-navigations.tsx:56-118
Timestamp: 2024-10-04T20:44:38.489Z
Learning: When typing the `workspace` parameter in functions like `createWorkspaceNavigation`, prefer importing the `Workspace` type from the database module and picking the necessary keys (e.g., `features`) instead of redefining the interface.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2025-04-08T09:34:24.576Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
🧬 Code Graph Analysis (2)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (7)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_CONSTANTS(1-7)apps/dashboard/app/(app)/settings/root-keys/components/root-key/permissions.ts (2)
workspacePermissions(10-153)apiPermissions(155-206)apps/dashboard/components/selected-item-list.tsx (1)
SelectedItemsList(35-126)internal/icons/src/icons/key-2.tsx (1)
Key2(15-73)internal/icons/src/icons/caret-right.tsx (1)
CaretRight(15-37)internal/ui/src/components/badge.tsx (1)
Badge(47-47)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (7)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.ts (1)
useRootKeySuccess(7-29)internal/icons/src/icons/key-2.tsx (1)
Key2(15-73)internal/icons/src/icons/check.tsx (1)
Check(15-38)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-59)apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/secret-key.tsx (1)
SecretKey(12-54)internal/icons/src/icons/circle-info.tsx (1)
CircleInfo(15-56)apps/dashboard/components/confirmation-popover.tsx (1)
ConfirmPopover(51-116)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build / Build
- GitHub Check: Test Packages / Test
- GitHub Check: Test API / API Test Local
- GitHub Check: Test Go API Local / Test
- GitHub Check: Analyze (javascript-typescript)
...hboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 6
🔭 Outside diff range comments (1)
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (1)
32-41: Add aria-label to fallback actions button for accessibilityThe fallback button rendered while actions are loading lacks an accessible name.
Apply this diff:
- loading: () => ( - <button - type="button" + loading: () => ( + <button + type="button" + aria-label="Open root key actions" className={cn( "group-data-[state=open]:bg-gray-6 group-hover:bg-gray-6 group size-5 p-0 rounded m-0 items-center flex justify-center", "border border-gray-6 group-hover:border-gray-8 ring-2 ring-transparent focus-visible:ring-gray-7 focus-visible:border-gray-7", )} > <Dots className="group-hover:text-gray-12 text-gray-11" size="sm-regular" /> </button> ),
♻️ Duplicate comments (5)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx (2)
7-14: Rename Props to SearchPermissionsProps and export the typeA more descriptive name avoids generic collisions and improves readability. Also consider exporting the type for reuse.
-type Props = { +export type SearchPermissionsProps = { isProcessing: boolean; search: string | undefined; inputRef: RefObject<HTMLInputElement>; onChange: (e: ChangeEvent<HTMLInputElement>) => void; }; -const SearchPermissions = ({ isProcessing, search, inputRef, onChange }: Props) => { +const SearchPermissions = ({ isProcessing, search, inputRef, onChange }: SearchPermissionsProps) => {
13-16: Wire isLoading to actual search state (avoid always showing “Clearing…”)Currently isLoading={false}, so when isProcessing is true the component always renders “Clearing…”. Tie isLoading to a meaningful “isSearching” condition.
-const SearchPermissions = ({ isProcessing, search, inputRef, onChange }: Props) => { - return ( +const SearchPermissions = ({ isProcessing, search, inputRef, onChange }: Props) => { + const isSearching = isProcessing && Boolean(search?.trim()); + return ( @@ - isProcessing={isProcessing} - isLoading={false} + isProcessing={isProcessing} + isLoading={isSearching}Also applies to: 21-25
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (2)
76-79: Avoid magic numbers in class calc — extract named class constants (Tailwind-safe)Extract these calc classes into named constants for clarity and reuse, without introducing dynamic class generation that could confuse Tailwind JIT.
- <div - className={`flex flex-col ${ - hasNextPage ? "max-h-[calc(100%-80px)]" : "max-h-[calc(100%-40px)]" - }`} - > + <div + className={`flex flex-col ${hasNextPage ? MAX_H_WITH_PAGINATION : MAX_H_NO_PAGINATION}`} + >Add near the top of the file (module scope):
// Keep these as literal class strings so Tailwind can statically detect them. const MAX_H_WITH_PAGINATION = "max-h-[calc(100%-80px)]"; const MAX_H_NO_PAGINATION = "max-h-[calc(100%-40px)]";
119-135: Fix typo in Button classes, add accessible name/type, and guard missing loadMore handlerTypo merges mx-auto and rounded-lg; also show the button only when a handler exists, add aria-label and type="button".
- <div className="sticky bottom-0 bg-background border-t border-gray-4 mt-auto"> - {hasNextPage ? ( + <div className="sticky bottom-0 bg-background border-t border-gray-4 mt-auto"> + {hasNextPage && loadMore ? ( <div className="w-full py-4"> <div className="flex flex-row justify-center items-center"> <Button - className="mx-autorounded-lg" + type="button" + aria-label={ROOT_KEY_MESSAGES.UI.LOAD_MORE} + className="mx-auto rounded-lg" size="sm" - onClick={loadMore} - disabled={!hasNextPage} + onClick={loadMore} + disabled={!hasNextPage} loading={isFetchingNextPage} > {ROOT_KEY_MESSAGES.UI.LOAD_MORE} </Button> </div> </div> ) : undefined} </div>apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (1)
100-101: Empty-state copy fix looks good (role → root key)The copy was corrected as previously requested. Thanks for addressing it.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
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.
📒 Files selected for processing (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx(7 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Use Biome for formatting and linting in TypeScript/JavaScript projects
Prefer named exports over default exports in TypeScript/JavaScript, except for Next.js pages
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Follow strict TypeScript configuration
Use Zod for runtime validation in TypeScript projects
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
🧠 Learnings (4)
📓 Common learnings
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
📚 Learning: 2024-12-03T14:07:45.173Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/components/ui/group-button.tsx:21-31
Timestamp: 2024-12-03T14:07:45.173Z
Learning: In the `ButtonGroup` component (`apps/dashboard/components/ui/group-button.tsx`), avoid suggesting the use of `role="group"` in ARIA attributes.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx
📚 Learning: 2024-10-23T16:19:42.049Z
Learnt from: p6l-richard
PR: unkeyed/unkey#2085
File: apps/www/components/glossary/search.tsx:41-57
Timestamp: 2024-10-23T16:19:42.049Z
Learning: For the `FilterableCommand` component in `apps/www/components/glossary/search.tsx`, adding error handling and loading states to the results list is not necessary.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx
📚 Learning: 2024-12-03T14:17:08.016Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/app/(app)/logs/logs-page.tsx:77-83
Timestamp: 2024-12-03T14:17:08.016Z
Learning: The `<LogsTable />` component already implements virtualization to handle large datasets efficiently.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
🧬 Code Graph Analysis (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx (2)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-input.tsx (2)
SearchInput(25-63)SEARCH_MODES(5-9)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-59)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (5)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.ts (1)
usePermissionSheet(14-117)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-59)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsx (1)
PermissionContentList(20-142)internal/db/src/schema/apis.ts (1)
apis(8-27)
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (8)
apps/dashboard/lib/trpc/routers/settings/root-keys/query.ts (1)
RootKey(35-35)apps/dashboard/app/(app)/settings/root-keys/components/table/utils/get-row-class.ts (1)
getRowClassName(26-38)apps/dashboard/components/virtual-table/types.ts (1)
Column(13-24)apps/dashboard/app/(app)/settings/root-keys/components/table/components/skeletons.tsx (6)
RootKeyColumnSkeleton(4-13)KeyColumnSkeleton(20-25)CreatedAtColumnSkeleton(15-19)PermissionsColumnSkeleton(27-34)LastUpdatedColumnSkeleton(36-41)ActionColumnSkeleton(43-53)packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/table/components/actions/root-keys-table-action.popover.constants.tsx (1)
RootKeysTableActions(12-15)apps/dashboard/components/virtual-table/index.tsx (1)
VirtualTable(38-404)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (1)
RootKeyDialog(39-169)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test Packages / Test
- GitHub Check: Build / Build
- GitHub Check: Test API / API Test Local
- GitHub Check: Test Go API Local / Test
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx (1)
3-3: Good: type-only React importsNice use of type-only imports for ChangeEvent and RefObject. This plays well with isolatedModules/tsconfig strictness.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (2)
119-119: Good: switched to a sticky footer for the Load More controlUsing a sticky footer avoids content overlap issues that absolute positioning caused.
61-64: Light mode overlay/contrast: please verifyUsing overlay="transparent" with bg-gray-1/dark:bg-black can surface underlying content and reduce contrast in light mode (noted in PR comments). Validate in light theme and consider a subtle overlay variant if contrast is poor.
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (4)
273-274: Ensure action popover clicks don’t trigger row click (stop propagation)With
onRowClickactive, clicking the actions trigger can bubble and open the editor unintentionally. Make sure the actions trigger callse.stopPropagation().If not already handled in
root-keys-table-action.popover.constants.tsx, add:// Inside the trigger element for the popover <button type="button" onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} aria-label="Open root key actions" > … </button>I can open a quick PR to the actions component if you want.
179-179: Selection state derivation looks consistent and efficientSwitching to
selectedRootKeyIdfor per-cell selection checks is a good micro-optimization that avoids object identity pitfalls.Also applies to: 226-227, 237-238, 263-264
182-189: Verify light-mode contrast on icon containerGiven the PR comment about light mode issues, please double-check contrast for the icon container (bg/border alpha tokens with text-gray-12). The combination of
border-grayA-3 bg-grayA-3andtext-gray-12might be borderline in light mode.If needed, consider:
- Using non-alpha tokens for borders in light mode (e.g.,
border-gray-5,bg-gray-2).- Applying theme-aware classes via data-theme or a CSS variable from the design system.
7-13: Imports for permissions typing and dialog composition look correctBringing in
UnkeyPermissionand theRootKeyDialogaligns with the new edit flow and keeps types explicit.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
Outdated
Show resolved
Hide resolved
|
And, @perkinsjr or @MichaelUnkey can one of you skim over the coderabbit stuff. It sometimes find critical bugs |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
internal/icons/src/icons/check.tsx (1)
20-26: Avoid double-passingfocusable; destructure to make intent clear
{...props}already forwardsfocusable. Passing it again is redundant and risks confusion about which value wins. Destructure and forward explicitly.Apply this refactor:
-export const Check: React.FC<IconProps> = ({ size = "xl-thin", ...props }) => { +export const Check: React.FC<IconProps> = ({ size = "xl-thin", focusable, ...rest }) => { const { size: pixelSize, strokeWidth } = sizeMap[size]; return ( <svg - {...props} + {...rest} height={pixelSize} width={pixelSize} viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg" - focusable={props.focusable} + focusable={focusable} >apps/dashboard/app/(app)/settings/root-keys/components/controls/components/root-keys-filters/index.tsx (3)
78-79: A11y: Update aria-label to reflect Root Keys context (currently says “Filter logs”).This looks copied from Logs; it can mislead screen readers in Settings > Root Keys.
Apply this diff:
- aria-label="Filter logs" + aria-label="Filter root keys"
83-83: Icon sizing: align with 28px spec noted in design feedback.Review feedback called out 28px icons elsewhere. Consider bumping the icon size for consistency.
- <BarsFilter className="text-accent-9 size-4" /> + <BarsFilter className="text-accent-9 size-7" />
75-77: Minor spacing polish: add a gap between icon/label/badge to improve hover/visual balance.Helps with the “hover state feels poor / needs padding” feedback without altering layout.
- className={cn( - "group-data-[state=open]:bg-gray-4 px-2 rounded-lg", - filters.length > 0 ? "bg-gray-4" : "", - )} + className={cn( + "group-data-[state=open]:bg-gray-4 px-2 rounded-lg gap-2", + filters.length > 0 ? "bg-gray-4" : "", + )}
♻️ Duplicate comments (21)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.ts (1)
16-27: Route dialog close attempts through the confirmation flow (expose handleDialogOpenChange).Avoid binding onOpenChange directly to onClose in consumers. Provide a handler that funnels close attempts through handleCloseAttempt so the confirm UI is honored.
Apply this diff to add a safe onOpenChange handler:
const handleConfirmClose = () => { setIsConfirmOpen(false); onClose(); }; + const handleDialogOpenChange = (open: boolean) => { + if (!open) { + handleCloseAttempt(); + } + }; + return { isConfirmOpen, setIsConfirmOpen, dividerRef, handleCloseAttempt, handleConfirmClose, + handleDialogOpenChange, };apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
4-7: Align API_URL with env var convention and fallbacks.Follow repo guideline UNKEY_<SERVICE_NAME>_VARNAME and support dashboard-specific/public env. Keep a sane default.
- API_URL: "https://api.unkey.com", + API_URL: + process.env.NEXT_PUBLIC_UNKEY_DASHBOARD_API_URL ?? + process.env.NEXT_PUBLIC_UNKEY_API_URL ?? + "https://api.unkey.com",Please also add NEXT_PUBLIC_UNKEY_DASHBOARD_API_URL to .env.example and deployment configs.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts (2)
139-169: Add error handling around async mutations; await create for proper failure handling.Wrap the edit and create flows in try/catch to avoid leaving the UI in an inconsistent state, and await the create mutation so failures are catchable.
- const handleCreateKey = useCallback(async () => { + const handleCreateKey = useCallback(async () => { if (editMode && existingKey) { - // Update existing key name if changed - const nameChanged = name !== existingKey.name && name !== "" && name !== null; - - if (nameChanged) { - await updateName.mutateAsync({ - keyId: existingKey.id, - name: name && name.length > 0 ? name : null, - }); - } - if ( - selectedPermissions.length > 0 && - !arePermissionArraysEqual(selectedPermissions, existingKey.permissions) - ) { - // Update permissions using the new bulk update route - await updatePermissions.mutateAsync({ - keyId: existingKey.id, - permissions: selectedPermissions, - }); - } - - onOpenChange(false); + try { + // name/permissions handled in the other diff; retain those calls here + // ... + onOpenChange(false); + } catch (_err) { + // Errors are surfaced via mutation onError toasts; do not close the dialog + return; + } } else { - // Create new key - key.mutate({ - name: name && name.length > 0 ? name : undefined, - permissions: selectedPermissions, - }); + try { + // Create new key + await key.mutateAsync({ + name: name.trim().length > 0 ? name.trim() : undefined, + permissions: selectedPermissions, + }); + // Success UI handled by RootKeySuccess/Toast; keep dialog open for success flow + } catch (_err) { + toast.error(ROOT_KEY_MESSAGES.ERROR.ACTION_FAILED, { + description: ROOT_KEY_MESSAGES.ERROR.ACTION_FAILED_DESCRIPTION, + }); + } } }, [
187-189: Fix misleading comment on effect trigger.The effect only depends on existingKey.
- // Reset form when dialog opens/closes or when existingKey changes + // Reset form when existingKey changesapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx (1)
37-41: Centralize subtitle string in ROOT_KEY_MESSAGES.Move the literal into constants for consistency and future i18n.
- subTitle="Define a new root key and assign permissions" + subTitle={ROOT_KEY_MESSAGES.UI.NEW_ROOT_KEY_SUBTITLE}Add UI.NEW_ROOT_KEY_SUBTITLE to constants as suggested in the constants.ts comment.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsx (2)
90-93: Map CheckedState to boolean before invoking root toggle.Pass the intended state instead of ignoring the argument to avoid tri-state inconsistencies.
- checked={state.rootChecked} - setChecked={handleRootToggle} + checked={state.rootChecked} + setChecked={(next) => handleRootToggle(next === true)}Note: This requires updating usePermissions.handleRootToggle to accept a desired boolean (see follow-up below).
108-112: Don’t discard the CheckedState for category toggles; forward intent.Forward the desired state to the handler so category select-all behaves predictably.
- checked={state.categoryChecked[category]} - setChecked={() => handleCategoryToggle(category)} + checked={state.categoryChecked[category]} + setChecked={(next) => handleCategoryToggle(category, next === true)}Follow-up: Update usePermissions.handleCategoryToggle to accept (category: string, desired: boolean).
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx (2)
9-16: Remove unused prop from interface.The component interface definition is clean and well-structured.
46-47: Mark ChevronRight icon as fully decorative.The icon has
text-grayA-8but should also include focusable attribute for better accessibility.- <ChevronRight size="sm-regular" className="text-grayA-8" /> + <ChevronRight size="sm-regular" className="text-grayA-8" aria-hidden="true" focusable="false" />apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx (1)
43-46: Render description conditionally to avoid unnecessary DOM elements.When description is undefined or empty, the paragraph element still takes up space in the DOM.
<div className="flex flex-col text-left min-w-48 w-full"> <p className="text-sm w-full">{category}</p> - <p className="text-xs text-gray-10 w-full truncate">{description}</p> + {description && ( + <p className="text-xs text-gray-10 w-full truncate">{description}</p> + )} </div>apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (1)
39-40: Consider responsive padding for mobile viewports.The fixed horizontal padding of 120px may cause layout issues on mobile devices.
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (2)
69-73: Reconsider single-click editing behavior.Opening the edit dialog on row click might conflict with row selection UX. Users might expect single-click to select and double-click or action menu to edit.
314-319: Consider clearing both editing states together.When the dialog closes, you're clearing
editingKeybut not synchronizing with theeditDialogOpenstate. While this works, it could be cleaner.apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (2)
76-78: Consider extracting magic numbers as constants.The height calculations use magic numbers that affect layout. Extracting them improves maintainability.
119-135: Fix className typo and improve load more button layout.There's a typo in the button className where two classes are merged together.
<Button - className="mx-autorounded-lg" + className="mx-auto rounded-lg" size="sm"apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (3)
98-101: Use semantic color tokens to fix light-mode contrastHardcoded gray class can fail in light mode. Prefer semantic tokens from your design system.
- <div className="text-gray-9 text-xs"> + <div className="text-secondary-foreground text-xs">If
text-secondary-foregroundisn’t available, use the appropriate semantic muted/secondary token defined in your Tailwind config and update accordingly.
118-120: Use semantic color tokens for label text to align with design and light mode- <Label className="text-[13px] font-regular text-gray-10"> + <Label className="text-[13px] font-regular text-secondary-foreground">Confirm the correct semantic token (e.g., text-muted-foreground) based on the dashboard’s tokens.
146-151: Centralize “Selected from”, “from”, and “Workspace” stringsThese literals bypass your ROOT_KEY_MESSAGES and hinder i18n/consistency.
<PermissionBadgeList selectedPermissions={selectedPermissions} apiId={ROOT_KEY_CONSTANTS.WORKSPACE} - title="Selected from" - name="Workspace" + title={ROOT_KEY_MESSAGES.UI.PERMISSION_SELECTED_FROM} + name={ROOT_KEY_MESSAGES.UI.WORKSPACE} expandCount={ROOT_KEY_CONSTANTS.EXPAND_COUNT} removePermission={removePermission} /> @@ <PermissionBadgeList key={api.id} selectedPermissions={selectedPermissions} apiId={api.id} - title="from" + title={ROOT_KEY_MESSAGES.UI.PERMISSION_FROM} name={api.name} expandCount={ROOT_KEY_CONSTANTS.EXPAND_COUNT} removePermission={removePermission} />Additionally, add the missing keys to constants:
// apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts export const ROOT_KEY_MESSAGES = { // ... UI: { // ... PERMISSION_SELECTED_FROM: "Selected from", PERMISSION_FROM: "from", WORKSPACE: "Workspace", }, // ... } as const;Also applies to: 152-161
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (3)
73-79: Outdated comment: no event is being stopped nowThe handler no longer receives an event; update the comment to avoid confusion.
- // Stop propagation to prevent triggering parent collapsible when removing permissions + // Remove permission by id emitted from SelectedItemsList const handleRemovePermissionClick = (id: string) => {
90-94: A11y and UX: mark decorative icon hidden and show friendlier secondary text
- Hide the decorative icon from assistive tech.
- Show the category/description as secondary text instead of repeating the id.
- renderIcon={() => <Key2 size="sm-regular" className="text-grayA-11" />} + renderIcon={() => ( + <Key2 size="sm-regular" className="text-grayA-11" aria-hidden="true" focusable="false" /> + )} @@ - renderSecondaryText={(permission) => permission.id} + renderSecondaryText={(permission) => permission.description}
140-147: Use semantic tokens for light-mode compatibilityStatic grays risk contrast issues. Prefer semantic text and badge colors.
- <span className="text-sm flex-1 text-grayA-10 text-left flex items-center"> + <span className="text-sm flex-1 text-secondary-foreground text-left flex items-center"> @@ - <Badge + <Badge variant="primary" size="sm" - className="text-[11px] font-normal text-grayA-11 rounded-full px-2 ml-1 py-1 h-[18px] min-w-[22px] border-[1px] border-grayA-3 " + className="text-[11px] font-normal text-muted-foreground rounded-full px-2 ml-1 py-1 h-[18px] min-w-[22px] border-[1px] border-border " >Confirm semantic token names in your Tailwind config; adjust to your design system (e.g., text-muted-foreground, border).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
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.
📒 Files selected for processing (15)
apps/dashboard/app/(app)/settings/root-keys/components/controls/components/root-keys-filters/index.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx(7 hunks)internal/icons/src/icons/check.tsx(1 hunks)internal/icons/src/props.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Use Biome for formatting and linting in TypeScript/JavaScript projects
Prefer named exports over default exports in TypeScript/JavaScript, except for Next.js pages
Files:
internal/icons/src/props.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/controls/components/root-keys-filters/index.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.tsinternal/icons/src/icons/check.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Follow strict TypeScript configuration
Use Zod for runtime validation in TypeScript projects
Files:
internal/icons/src/props.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/controls/components/root-keys-filters/index.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.tsinternal/icons/src/icons/check.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
**/*.{env,js,ts,go}
📄 CodeRabbit Inference Engine (CLAUDE.md)
All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Files:
internal/icons/src/props.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
🧠 Learnings (12)
📓 Common learnings
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: For repo unkeyed/unkey and PR review workflows: When imeyer comments "issue" on a thread, automatically create a thorough GitHub issue (sections: Summary, Impact, Where, Repro/Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and the specific comment, and assign the issue to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/runbook-freshness-check.yaml:157-173
Timestamp: 2025-08-08T14:59:52.283Z
Learning: Repo unkeyed/unkey: When a CI/workflow fix is deferred, imeyer prefers a thorough GitHub issue be opened with sections (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References) and assigned to imeyer, including backlinks to the originating PR and comment.
📚 Learning: 2024-10-04T20:44:38.489Z
Learnt from: chronark
PR: unkeyed/unkey#2180
File: apps/dashboard/lib/constants/workspace-navigations.tsx:56-118
Timestamp: 2024-10-04T20:44:38.489Z
Learning: When typing the `workspace` parameter in functions like `createWorkspaceNavigation`, prefer importing the `Workspace` type from the database module and picking the necessary keys (e.g., `features`) instead of redefining the interface.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2025-04-08T09:34:24.576Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2025-08-13T19:55:19.828Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3779
File: apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx:39-41
Timestamp: 2025-08-13T19:55:19.828Z
Learning: min-w-48 is not a default Tailwind CSS utility - it requires custom configuration or should be replaced with min-w-[12rem] using arbitrary value syntax.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx
📚 Learning: 2025-08-13T19:55:19.828Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3779
File: apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx:39-41
Timestamp: 2025-08-13T19:55:19.828Z
Learning: The Tailwind CSS truncate utility includes white-space: nowrap behavior along with overflow: hidden and text-overflow: ellipsis, making additional nowrap classes redundant.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx
📚 Learning: 2025-08-13T19:55:19.828Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3779
File: apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx:39-41
Timestamp: 2025-08-13T19:55:19.828Z
Learning: The Tailwind CSS truncate utility already includes white-space: nowrap behavior along with overflow: hidden and text-overflow: ellipsis, making additional nowrap classes redundant.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx
📚 Learning: 2025-08-13T19:55:19.828Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3779
File: apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx:39-41
Timestamp: 2025-08-13T19:55:19.828Z
Learning: In Tailwind CSS v3.4+, min-w-48 is a valid utility class equivalent to min-width: 12rem.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx
📚 Learning: 2025-07-11T12:57:39.875Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3528
File: apps/dashboard/app/(app)/settings/team/role-switcher.tsx:60-76
Timestamp: 2025-07-11T12:57:39.875Z
Learning: In the Select component from unkey/ui, when fixing chevron overflow/positioning issues, applying `w-fit` directly to the `SelectTrigger` doesn't resolve the problem. A wrapper div with `w-fit` class around the entire Select component is required to properly contain the chevron within the trigger area.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx
📚 Learning: 2024-12-03T14:07:45.173Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/components/ui/group-button.tsx:21-31
Timestamp: 2024-12-03T14:07:45.173Z
Learning: In the `ButtonGroup` component (`apps/dashboard/components/ui/group-button.tsx`), avoid suggesting the use of `role="group"` in ARIA attributes.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsxapps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
📚 Learning: 2025-08-04T07:44:39.438Z
Learnt from: CR
PR: unkeyed/unkey#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-04T07:44:39.438Z
Learning: Applies to **/*.{env,js,ts,go} : All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
📚 Learning: 2025-07-21T18:05:58.236Z
Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{env,sh,yaml,yml,json,conf,ini} : All environment variables MUST follow the format UNKEY_<SERVICE_NAME>_VARNAME.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
📚 Learning: 2024-12-03T14:17:08.016Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/app/(app)/logs/logs-page.tsx:77-83
Timestamp: 2024-12-03T14:17:08.016Z
Learning: The `<LogsTable />` component already implements virtualization to handle large datasets efficiently.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx
🧬 Code Graph Analysis (10)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (5)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts (1)
useRootKeyDialog(53-232)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (2)
ROOT_KEY_MESSAGES(9-61)ROOT_KEY_CONSTANTS(1-7)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (1)
PermissionSheet(30-141)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (1)
RootKeySuccess(14-127)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts (3)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/lib/trpc/server.ts (1)
trpc(7-14)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (2)
ROOT_KEY_CONSTANTS(1-7)ROOT_KEY_MESSAGES(9-61)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx (1)
internal/icons/src/icons/caret-right.tsx (1)
CaretRight(15-37)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (4)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_CONSTANTS(1-7)apps/dashboard/app/(app)/settings/root-keys/components/root-key/permissions.ts (2)
workspacePermissions(10-153)apiPermissions(155-206)apps/dashboard/components/selected-item-list.tsx (1)
SelectedItemsList(35-126)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx (1)
internal/icons/src/icons/chevron-right.tsx (1)
ChevronRight(17-40)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permission-sheet.ts (1)
usePermissionSheet(14-117)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-61)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsx (1)
PermissionContentList(20-144)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (4)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-success.ts (1)
useRootKeySuccess(7-28)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-61)apps/dashboard/app/(app)/apis/[apiId]/_components/create-key/components/secret-key.tsx (1)
SecretKey(12-54)apps/dashboard/components/confirmation-popover.tsx (1)
ConfirmPopover(51-116)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsx (4)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-permissions.ts (1)
usePermissions(18-135)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-61)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/highlighted-text.tsx (1)
HighlightedText(11-34)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-61)internal/icons/src/icons/plus.tsx (1)
Plus(16-52)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (1)
RootKeyDialog(39-169)
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (8)
packages/rbac/src/permissions.ts (2)
UnkeyPermission(83-83)unkeyPermissionValidation(87-119)apps/dashboard/lib/trpc/routers/settings/root-keys/query.ts (1)
RootKey(35-35)apps/dashboard/app/(app)/settings/root-keys/components/table/utils/get-row-class.ts (1)
getRowClassName(26-38)internal/ui/src/components/buttons/button.tsx (1)
buttonVariants(439-439)apps/dashboard/components/virtual-table/types.ts (1)
Column(13-24)apps/dashboard/app/(app)/settings/root-keys/components/table/components/actions/root-keys-table-action.popover.constants.tsx (1)
RootKeysTableActions(12-15)apps/dashboard/components/virtual-table/index.tsx (1)
VirtualTable(38-404)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (1)
RootKeyDialog(39-169)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test API / API Test Local
- GitHub Check: Test Go API Local / Test
- GitHub Check: Test Packages / Test
- GitHub Check: Build / Build
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (17)
internal/icons/src/props.ts (1)
35-36: AddingfocusabletoIconProps: LGTMExposes focus control for SVGs where needed. No breaking changes.
apps/dashboard/app/(app)/settings/root-keys/components/controls/components/root-keys-filters/index.tsx (1)
3-3: Fix residualcnimports and ensure a single source of truthWe’ve confirmed that:
cnis correctly exported in apps/dashboard/lib/utils.ts (usesclsx+twMerge).- The
@/lib/utilsalias resolves via apps/dashboard/tsconfig.json ("@/*": ["./*"]).However, there are still many import sites across apps/dashboard (and even in apps/engineering) pulling
cnfrom@unkey/ui/src/lib/utils. To fully migrate:• Replace all
import { cn } from "@unkey/ui/src/lib/utils"
with
import { cn } from "@/lib/utils"
(search for"@unkey/ui/src/lib/utils").• Verify no other utilities are imported from the old path.
• Confirm your root-level TS config (or workspace config) also defines
"@/*"if you have consumers outside apps/dashboard.• Once complete, remove any lingering references to
@unkey/ui/src/lib/utils.This will centralize your
cnimplementation and avoid class‐merging inconsistencies.⛔ Skipped due to learnings
Learnt from: ogzhanolguncu PR: unkeyed/unkey#3292 File: apps/dashboard/lib/vault.ts:80-97 Timestamp: 2025-06-02T11:08:56.397Z Learning: The vault.ts file in apps/dashboard/lib/vault.ts is a duplicate of the vault package from the `api` directory and should be kept consistent with that original implementation.apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-list.tsx (1)
76-83: Good structure: collapsible root with clear header and count.The auto-expand on search and separation of concerns via usePermissions reads well. Nice.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx (2)
18-21: Component implementation looks good.The forwardRef implementation and use of
useIdfor accessibility is appropriate.
38-39: Good accessibility practices with proper event handling.The stopPropagation on the checkbox click and the tri-state to boolean conversion in onCheckedChange are correctly implemented.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx (1)
39-40: Good implementation of the collapsible trigger.The className merging with
cnutility and proper forwarding of ref and props is well implemented. The rotation animation for the caret icon is clean.apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (3)
25-31: LGTM! Dialog close handling properly routes through confirmation.The dialog's
onOpenChangecorrectly callshandleCloseAttemptwhen closing, ensuring the confirmation popover is shown before actual closure.
49-51: Good accessibility implementation for decorative icons.The decorative icons properly use
aria-hidden="true"andfocusable={false}attributes.
34-34: Consider responsive design for the dialog width.The
max-w-[760px]works well for desktop but might need adjustment for smaller screens.Consider adding responsive breakpoints:
- className="drop-shadow-2xl border-grayA-4 overflow-hidden !rounded-2xl p-0 gap-0 w-full max-w-[760px] max-h-[90vh] overflow-y-auto" + className="drop-shadow-2xl border-grayA-4 overflow-hidden !rounded-2xl p-0 gap-0 w-full max-w-full sm:max-w-[760px] max-h-[90vh] overflow-y-auto"Likely an incorrect or invalid review comment.
apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx (4)
15-19: Good addition of runtime type guard for permissions.The
isUnkeyPermissiontype guard using Zod validation is a robust approach to ensure type safety at runtime. This prevents potential runtime errors from invalid permission names.
60-63: Clean implementation of edit key handler.The
handleEditKeycallback is well-structured and properly memoized.
167-181: Excellent implementation of safe permission validation.The
existingKeymemo properly validates permissions using the type guard function, filtering out any invalid permission names. This is a robust defensive programming practice.
111-121: Fix nested interactive elements accessibility issue.The anchor wrapping a button creates invalid HTML and accessibility problems. This violates WCAG guidelines for interactive elements.
Use the Button's
asChildpattern if supported:<Empty.Actions className="mt-4 justify-start"> - <a - href="https://www.unkey.com/docs/security/root-keys" - target="_blank" - rel="noopener noreferrer" - className={buttonVariants({ variant: "outline" })} - > - <span className="flex items-center gap-2"> - <BookBookmark /> - Learn about Root Keys - </span> - </a> + <Button asChild variant="outline"> + <a + href="https://www.unkey.com/docs/security/root-keys" + target="_blank" + rel="noopener noreferrer" + > + <BookBookmark /> + Learn about Root Keys + </a> + </Button> </Empty.Actions>Or use just the anchor with button styling:
- <a - href="https://www.unkey.com/docs/security/root-keys" - target="_blank" - rel="noopener noreferrer" - className={buttonVariants({ variant: "outline" })} - > + <a + href="https://www.unkey.com/docs/security/root-keys" + target="_blank" + rel="noopener noreferrer" + className={buttonVariants({ variant: "outline" })} + >(This option is already correct in your code - the
buttonVariantsapproach doesn't create nested interactive elements)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (2)
49-53: Hook integration looks correct.The
usePermissionSheethook is properly initialized with all required props including theonChangehandler.
82-85: Good UX for empty search results.The "No results found" message provides clear feedback when search yields no matches.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (1)
14-24: Dynamic import fallback is solidGood use of a role="alert" fallback and SSR disabled for the DialogContainer load.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (1)
31-41: LGTM: efficient flattening with memoizationNice use of useMemo to flatten permission catalogs once per apiId; avoids recompute on unrelated renders.
...ashboard/app/(app)/settings/root-keys/components/root-key/components/expandable-category.tsx
Show resolved
Hide resolved
...hboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
Outdated
Show resolved
Hide resolved
...hboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
Show resolved
Hide resolved
.../dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts
Outdated
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts
Outdated
Show resolved
Hide resolved
|
➕ Pushed commit: pr suggested changes |
There was a problem hiding this comment.
Actionable comments posted: 8
♻️ Duplicate comments (13)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx (1)
33-34: Verify Plus icon renders at 28px as per designDesign feedback calls for a 28px icon. Ensure the token you pass yields 28px (e.g., size="lg-regular" or "xl-regular" depending on your icon sizeMap).
- <Plus /> + <Plus size="lg-regular" />If “lg-regular” isn’t 28px in your icon system, switch to the correct token that maps to 28px.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx (1)
7-14: Rename Props to a descriptive type to avoid collisionsUse a specific type name for clarity and consistency.
-type Props = { +type SearchPermissionsProps = { isProcessing: boolean; search: string | undefined; inputRef: RefObject<HTMLInputElement>; onChange: (e: ChangeEvent<HTMLInputElement>) => void; }; -const SearchPermissions = ({ isProcessing, search, inputRef, onChange }: Props) => { +const SearchPermissions = ({ + isProcessing, + search, + inputRef, + onChange, +}: SearchPermissionsProps) => {apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx (3)
26-26: Limit transitions to colors (avoid transition-all)Only colors change here. Narrow the transition scope to avoid unnecessary reflow.
- "hover:cursor-pointer flex flex-row items-center justify-start gap-4 transition-all pl-3 h-full mb-1 ml-2 w-full hover:bg-grayA-3 rounded-lg", + "hover:cursor-pointer flex flex-row items-center justify-start gap-4 transition-colors pl-3 h-full mb-1 ml-2 w-full hover:bg-grayA-3 rounded-lg",
45-46: Mark ChevronRight icon as decorativePrevent non-informative icons from being read by screen readers.
- <ChevronRight size="sm-regular" className="text-grayA-8" /> + <ChevronRight size="sm-regular" className="text-grayA-8" aria-hidden="true" focusable="false" />
46-47: Remove redundant JSX bracesMinor cleanup.
- {<span className="text-sm w-full">{label}</span>} + <span className="text-sm w-full">{label}</span>apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
4-4: Use env vars for API_URL in root-key constantsReplace the hard-coded URL with the repo-standard env var lookup and a safe fallback:
--- a/apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts +++ b/apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts @@ lines 1–10 DEFAULT_LIMIT: 10, UNNAMED_KEY: "Unnamed", - API_URL: "https://api.unkey.com", + API_URL: + process.env.NEXT_PUBLIC_UNKEY_DASHBOARD_API_URL ?? + process.env.NEXT_PUBLIC_UNKEY_API_URL ?? + "https://api.unkey.dev", EXPAND_COUNT: 3, WORKSPACE: "workspace",• Add
NEXT_PUBLIC_UNKEY_DASHBOARD_API_URLto.env.exampleand your deployment config.
• Runrg -n '\bAPI_URL\b'to confirm no other code still relies on the old hard-coded value.apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts (1)
180-181: Fix misleading comment about effect dependenciesThe effect only depends on existingKey.
- // Reset form when dialog opens/closes or when existingKey changes + // Reset form when existingKey changesapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (3)
146-149: Centralize “Selected from”, “Workspace”, and “from” strings to constantsUse message constants for consistency/i18n. Add keys and replace literals.
<PermissionBadgeList selectedPermissions={selectedPermissions} apiId={ROOT_KEY_CONSTANTS.WORKSPACE} - title="Selected from" - name="Workspace" + title={ROOT_KEY_MESSAGES.UI.PERMISSION_SELECTED_FROM} + name={ROOT_KEY_MESSAGES.UI.WORKSPACE} expandCount={ROOT_KEY_CONSTANTS.EXPAND_COUNT} removePermission={removePermission} /> @@ <PermissionBadgeList key={api.id} selectedPermissions={selectedPermissions} apiId={api.id} - title="from" + title={ROOT_KEY_MESSAGES.UI.PERMISSION_FROM} name={api.name}Add to constants (outside this diff):
// constants.ts export const ROOT_KEY_MESSAGES = { // ... UI: { // ... PERMISSION_SELECTED_FROM: "Selected from", PERMISSION_FROM: "from", WORKSPACE: "Workspace", }, };Also applies to: 157-158
98-101: Prefer semantic color tokens over raw grays for light-mode contrastReplace
text-gray-9with a semantic token (e.g.,text-secondary-foregroundor your system’s muted foreground) to avoid light-mode contrast issues.- <div className="text-gray-9 text-xs"> + <div className="text-secondary-foreground text-xs">
118-120: Use semantic colors for label textAlign with design tokens for consistent theming.
- <Label className="text-[13px] font-regular text-gray-10"> + <Label className="text-[13px] font-regular text-muted-foreground">apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (3)
73-79: Outdated comment and narrow the id typingThe handler no longer receives an event; update the comment. Also, type the id as UnkeyPermission to preserve intent.
- // Stop propagation to prevent triggering parent collapsible when removing permissions - const handleRemovePermissionClick = (id: string) => { + // Remove permission by id emitted from SelectedItemsList + const handleRemovePermissionClick = (id: UnkeyPermission) => { const permission = info.find((p) => p.permission === id); if (permission) { removePermission(permission.permission); } };
154-168: Handle wildcard “*” and dedupe selections in findPermissionUnkeyPermission includes "*" which should represent all permissions. Also dedupe to prevent duplicate badges.
const findPermission = ( allPermissions: PermissionInfo, selectedPermissions: UnkeyPermission[], ): PermissionInfo => { if (!selectedPermissions || !Array.isArray(selectedPermissions)) { return []; } - return selectedPermissions + // Treat "*" as all permissions + if (selectedPermissions.includes("*")) { + return allPermissions; + } + // Deduplicate selected permissions + const seen = new Set<UnkeyPermission>(); + return selectedPermissions .map((permission) => { - return allPermissions.find((p) => p.permission === permission); + if (seen.has(permission)) return undefined; + seen.add(permission); + return allPermissions.find((p) => p.permission === permission); }) .filter((item): item is { permission: UnkeyPermission; category: string; action: string } => Boolean(item), ); };
90-94: A11y and UX: hide decorative icon; show friendly secondary text
- Mark the icon decorative for assistive tech.
- Show category/description as secondary text instead of the id.
- renderIcon={() => <Key2 size="sm-regular" className="text-grayA-11" />} + renderIcon={() => ( + <Key2 size="sm-regular" className="text-grayA-11" aria-hidden="true" focusable="false" /> + )} @@ - renderSecondaryText={(permission) => permission.id} + renderSecondaryText={(permission) => permission.description}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
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.
📒 Files selected for processing (7)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Use Biome for formatting and linting in TypeScript/JavaScript projects
Prefer named exports over default exports in TypeScript/JavaScript, except for Next.js pages
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Follow strict TypeScript configuration
Use Zod for runtime validation in TypeScript projects
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx
**/*.{env,js,ts,go}
📄 CodeRabbit Inference Engine (CLAUDE.md)
All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.tsapps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: For repo unkeyed/unkey and PR review workflows: When imeyer comments "issue" on a thread, automatically create a thorough GitHub issue (sections: Summary, Impact, Where, Repro/Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and the specific comment, and assign the issue to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/runbook-freshness-check.yaml:157-173
Timestamp: 2025-08-08T14:59:52.283Z
Learning: Repo unkeyed/unkey: When a CI/workflow fix is deferred, imeyer prefers a thorough GitHub issue be opened with sections (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References) and assigned to imeyer, including backlinks to the originating PR and comment.
📚 Learning: 2024-12-03T14:07:45.173Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/components/ui/group-button.tsx:21-31
Timestamp: 2024-12-03T14:07:45.173Z
Learning: In the `ButtonGroup` component (`apps/dashboard/components/ui/group-button.tsx`), avoid suggesting the use of `role="group"` in ARIA attributes.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx
📚 Learning: 2024-10-04T20:44:38.489Z
Learnt from: chronark
PR: unkeyed/unkey#2180
File: apps/dashboard/lib/constants/workspace-navigations.tsx:56-118
Timestamp: 2024-10-04T20:44:38.489Z
Learning: When typing the `workspace` parameter in functions like `createWorkspaceNavigation`, prefer importing the `Workspace` type from the database module and picking the necessary keys (e.g., `features`) instead of redefining the interface.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2025-04-08T09:34:24.576Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2025-04-22T17:33:28.162Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3173
File: apps/docs/security/delete-protection.mdx:32-36
Timestamp: 2025-04-22T17:33:28.162Z
Learning: In the Unkey dashboard UI for delete protection, the button/link to initiate the process is labeled "Disable Delete Protection" while the confirmation button is labeled "Disable API Delete Protection". The documentation should maintain these different labels to match the actual UI.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx
📚 Learning: 2025-04-22T17:34:04.438Z
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3173
File: apps/docs/security/delete-protection.mdx:21-24
Timestamp: 2025-04-22T17:34:04.438Z
Learning: In the Unkey dashboard UI for enabling delete protection, the button/link to initiate the process is labeled "Enable Delete Protection" while the confirmation button is labeled "Enable API Delete Protection". The documentation should maintain these different labels to match the actual UI.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx
📚 Learning: 2024-10-23T16:19:42.049Z
Learnt from: p6l-richard
PR: unkeyed/unkey#2085
File: apps/www/components/glossary/search.tsx:41-57
Timestamp: 2024-10-23T16:19:42.049Z
Learning: For the `FilterableCommand` component in `apps/www/components/glossary/search.tsx`, adding error handling and loading states to the results list is not necessary.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx
📚 Learning: 2025-02-06T17:41:47.228Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2876
File: apps/dashboard/components/logs/datetime/constants.ts:96-96
Timestamp: 2025-02-06T17:41:47.228Z
Learning: In the Unkey codebase, avoid hardcoding IDs or indices that depend on array positions, as the arrays may be modified in the future. Instead, use methods like `find` with unique identifiers or properties to locate specific items.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2025-08-04T07:44:39.438Z
Learnt from: CR
PR: unkeyed/unkey#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-04T07:44:39.438Z
Learning: Applies to **/*.{env,js,ts,go} : All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
📚 Learning: 2025-07-21T18:05:58.236Z
Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{env,sh,yaml,yml,json,conf,ini} : All environment variables MUST follow the format UNKEY_<SERVICE_NAME>_VARNAME.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts
🧬 Code Graph Analysis (6)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts (3)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/lib/trpc/server.ts (1)
trpc(7-14)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (2)
ROOT_KEY_CONSTANTS(1-7)ROOT_KEY_MESSAGES(9-62)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-62)internal/icons/src/icons/plus.tsx (1)
Plus(16-52)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (1)
RootKeyDialog(39-169)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (5)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts (1)
useRootKeyDialog(46-225)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (2)
ROOT_KEY_MESSAGES(9-62)ROOT_KEY_CONSTANTS(1-7)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (1)
PermissionSheet(30-141)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (1)
RootKeySuccess(14-127)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx (1)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-input.tsx (2)
SearchInput(25-63)SEARCH_MODES(5-9)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (6)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_CONSTANTS(1-7)apps/dashboard/app/(app)/settings/root-keys/components/root-key/permissions.ts (2)
workspacePermissions(10-153)apiPermissions(155-206)apps/dashboard/components/selected-item-list.tsx (1)
SelectedItemsList(35-126)internal/icons/src/icons/key-2.tsx (1)
Key2(15-73)internal/icons/src/icons/caret-right.tsx (1)
CaretRight(15-37)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx (3)
internal/ui/src/components/form/checkbox.tsx (1)
Checkbox(330-330)internal/icons/src/icons/chevron-right.tsx (1)
ChevronRight(17-40)internal/ui/src/components/info-tooltip.tsx (1)
InfoTooltip(63-63)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test Packages / Test
- GitHub Check: Test Go API Local / Test
- GitHub Check: Test API / API Test Local
- GitHub Check: Build / Build
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx (1)
15-35: Event composition, ref wiring, and button type are correctly implementedYou’ve addressed the prior concerns: composed onClick, wired triggerRef, and set type="button". Looks good.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/search-permissions.tsx (1)
22-32: Good loading-state wiring for SearchInputPassing isLoading based on trimmed, non-empty search while processing is correct and prevents “Clearing…” from showing during searches. Nice.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
44-46: Centralized subtitle looks good and is in useGood job moving “Define a new root key and assign permissions” into messages and consuming it in the dialog.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts (2)
7-34: Set-based permission equality is correct and efficientO(n) membership check via Set avoids sorting allocations and is suitable for this use-case.
121-129: Permission change gating logic is clearer nowThe simplified “canUpdate” condition improves readability and matches intent.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (1)
14-24: Dynamic import fallback is solidGood use of dynamic import with a graceful, accessible fallback and SSR disabled.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (1)
30-41: Flattening permissions via useMemo is correct and efficientDeriving a stable flattened list from apiId keeps memoization effective. Good use of workspace vs per-API catalogs.
...hboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
Show resolved
Hide resolved
.../dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-toggle.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (8)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx (1)
20-35: Add dialog a11y attributes to the triggerExpose dialog state via ARIA so assistive tech understands the control opens a dialog.
<Button {...props} title={ROOT_KEY_MESSAGES.UI.NEW_ROOT_KEY} onClick={(e) => { onClick?.(e); setIsOpen(true); }} ref={triggerRef} variant="primary" type="button" size="sm" + aria-haspopup="dialog" + aria-expanded={isOpen} className={cn("px-3 rounded-md", className)} >apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (2)
86-93: Decouple submit state from background fetch; remove redundant disable conditionPrimary action shouldn’t spin/disable due to background API fetching. Also,
!selectedPermissions.lengthduplicates the create-mode logic already covered byhasChanges.<Button variant="primary" size="xlg" className="w-full rounded-lg" - disabled={!hasChanges || isMutating || !selectedPermissions.length} + disabled={!hasChanges || isMutating} onClick={handleCreateKey} - loading={isBusy} + loading={isMutating} + aria-busy={isMutating} >
131-137: Permission sheet trigger: align colors with semantic tokensAvoid hard-coded grays. Use design-system tokens for text, borders, bg, and focus rings.
<Button type="button" variant="outline" size="lg" - className="rounded-lg font-light text-grayA-8 text-[13px] border border-gray-5 hover:border-gray-8 bg-gray-2 dark:bg-black focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-5 focus-visible:ring-offset-0" + className="rounded-lg font-light text-[13px] text-muted-foreground border border-border hover:border-foreground/40 bg-muted dark:bg-black focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0" disabled={isBusy} >Run the Tailwind token check (see earlier script) and adjust tokens to match your config if names differ.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (5)
90-94: Prefer description for secondary text (id is already present internally)Show the human-friendly category/description instead of the internal id.
- renderSecondaryText={(permission) => permission.id} + renderSecondaryText={(permission) => permission.description}
90-91: A11y: mark icon as non-focusableThe icon is decorative; add focusable={false} so it can’t receive focus.
- renderIcon={() => <Key2 size="sm-regular" className="text-grayA-11" aria-hidden="true" />} + renderIcon={() => ( + <Key2 size="sm-regular" className="text-grayA-11" aria-hidden="true" focusable={false} /> + )}
73-79: Update outdated comment (no event to stop propagation anymore)The handler no longer receives an event; fix the comment to reflect actual behavior.
- // Stop propagation to prevent triggering parent collapsible when removing permissions + // Remove permission by id emitted from SelectedItemsList const handleRemovePermissionClick = (id: string) => {
154-168: Handle wildcard "*" and dedupe selections to avoid empty/duplicate badgesUnkeyPermission includes "*". Treat it as “all permissions” and dedupe to avoid duplicate items.
const findPermission = ( allPermissions: PermissionInfo, selectedPermissions: UnkeyPermission[], ): PermissionInfo => { if (!selectedPermissions || !Array.isArray(selectedPermissions)) { return []; } - return selectedPermissions + // Treat "*" as all permissions + if (selectedPermissions.includes("*")) { + return allPermissions; + } + // Deduplicate to avoid duplicate badges + const seen = new Set<UnkeyPermission>(); + return selectedPermissions .map((permission) => { - return allPermissions.find((p) => p.permission === permission); + if (seen.has(permission)) return undefined; + seen.add(permission); + return allPermissions.find((p) => p.permission === permission); }) .filter((item): item is { permission: UnkeyPermission; category: string; action: string } => Boolean(item), ); };
140-147: Use semantic tokens for text/border colors for theme consistencyReplace hard-coded grays with semantic tokens.
- <span className="text-[13px] flex-1 text-grayA-10 text-left flex items-center"> + <span className="text-[13px] flex-1 text-muted-foreground text-left flex items-center"> {title} - <span className="font-normal text-grayA-12 ml-1 font-mono">{category}</span> + <span className="font-normal text-foreground ml-1 font-mono">{category}</span> <Badge variant="primary" size="sm" - className="text-[11px] font-normal text-gray-11 rounded-full px-2 ml-1 py-1 h-[18px] min-w-[22px] border-[1px] border-grayA-3 " + className="text-[11px] font-normal text-foreground rounded-full px-2 ml-1 py-1 h-[18px] min-w-[22px] border-[1px] border-border " > {count} </Badge>Verify token availability in your Tailwind config if needed (see earlier script).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
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.
📒 Files selected for processing (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx(1 hunks)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Use Biome for formatting and linting in TypeScript/JavaScript projects
Prefer named exports over default exports in TypeScript/JavaScript, except for Next.js pages
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Follow strict TypeScript configuration
Use Zod for runtime validation in TypeScript projects
Files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx
🧠 Learnings (4)
📓 Common learnings
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
PR: unkeyed/unkey#3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
📚 Learning: 2024-10-04T20:44:38.489Z
Learnt from: chronark
PR: unkeyed/unkey#2180
File: apps/dashboard/lib/constants/workspace-navigations.tsx:56-118
Timestamp: 2024-10-04T20:44:38.489Z
Learning: When typing the `workspace` parameter in functions like `createWorkspaceNavigation`, prefer importing the `Workspace` type from the database module and picking the necessary keys (e.g., `features`) instead of redefining the interface.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2025-04-08T09:34:24.576Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx
📚 Learning: 2024-12-03T14:07:45.173Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2143
File: apps/dashboard/components/ui/group-button.tsx:21-31
Timestamp: 2024-12-03T14:07:45.173Z
Learning: In the `ButtonGroup` component (`apps/dashboard/components/ui/group-button.tsx`), avoid suggesting the use of `role="group"` in ARIA attributes.
Applied to files:
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsxapps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx
🧬 Code Graph Analysis (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (5)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/hooks/use-root-key-dialog.ts (1)
useRootKeyDialog(46-225)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (2)
ROOT_KEY_MESSAGES(9-62)ROOT_KEY_CONSTANTS(1-7)apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-sheet.tsx (1)
PermissionSheet(30-141)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-success.tsx (1)
RootKeySuccess(14-127)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/components/permission-badge-list.tsx (6)
packages/rbac/src/permissions.ts (1)
UnkeyPermission(83-83)apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_CONSTANTS(1-7)apps/dashboard/app/(app)/settings/root-keys/components/root-key/permissions.ts (2)
workspacePermissions(10-153)apiPermissions(155-206)apps/dashboard/components/selected-item-list.tsx (1)
SelectedItemsList(35-126)internal/icons/src/icons/key-2.tsx (1)
Key2(15-73)internal/icons/src/icons/caret-right.tsx (1)
CaretRight(15-37)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx (3)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/constants.ts (1)
ROOT_KEY_MESSAGES(9-62)internal/icons/src/icons/plus.tsx (1)
Plus(16-52)apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (1)
RootKeyDialog(39-169)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test API / API Test Local
- GitHub Check: Test Go API Local / Test
- GitHub Check: Build / Build
- GitHub Check: Test Packages / Test
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx (1)
23-31: Good fix: composed onClick, forwarded ref, and explicit type="button"You avoided clobbering consumer onClick, wired triggerRef, and set type="button". This addresses the earlier concerns cleanly.
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx (1)
98-102: Use a semantic text color token for better theming
Replace the raw gray utility with a semantic token to guarantee proper light-/dark-mode contrast.- <div className="text-gray-9 text-xs"> + <div className="text-muted-foreground text-xs">If
text-muted-foregroundisn’t defined in your Tailwind config, pick the closest semantic alternative (for example,text-secondary-foreground). You can search your configs for available tokens with:#!/bin/bash fd -HI 'tailwind.config.*' -x sh -c ' echo "== {} =="; rg -n "muted-foreground\\|secondary-foreground" -C3 {} '
apps/dashboard/app/(app)/settings/root-keys/components/root-key/create-rootkey-button.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx
Show resolved
Hide resolved
apps/dashboard/app/(app)/settings/root-keys/components/root-key/root-key-dialog.tsx
Show resolved
Hide resolved
Graphite Automations"Post a GIF when PR approved" took an action on this PR • (08/20/25)1 gif was posted to this PR based on Andreas Thomas's automation. |






What does this PR do?
This adds the new modal version of the creation and editing of a root key.
Fixes # (issue)
The issues are already closed but closes out Root key redesign project
Type of change
How should this be tested?
Checklist
Required
pnpm buildpnpm fmtconsole.logsgit pull origin mainAppreciated
Summary by CodeRabbit
New Features
Backend/API
Style
Documentation
Summary by CodeRabbit
New Features
Improvements
Documentation
Removed / Breaking Changes