Skip to content

[WEB-3998] feat: settings page revamp #6959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 54 commits into from
May 30, 2025
Merged

Conversation

sangeethailango
Copy link
Member

@sangeethailango sangeethailango commented Apr 23, 2025

Description

  • New Features

    • Introduced a comprehensive settings interface for workspace, profile, and project management, including new layouts, sidebars, tabs, and mobile navigation.
    • Added dedicated pages for workspace billing, exports, imports, integrations, webhooks, and member management with enhanced permission checks.
    • Implemented new profile settings sections: activity, notifications, preferences (theme, language, timezone), and security.
    • Added project settings pages for automations, estimates, features, labels, members, and states, each with granular access control.
  • Improvements

    • Updated navigation and URL structures for settings, projects, and account management for consistency and clarity.
    • Enhanced empty states, loaders, and user feedback with improved visuals and localized messaging.
    • Reorganized and renamed settings sections (e.g., "Appearance" to "Preferences", "API Tokens" to "Personal Access Tokens") for better usability.
    • Improved error handling and user notifications across forms and modals.
  • Bug Fixes

    • Fixed workspace and project redirection logic to ensure users are routed appropriately after certain actions.
    • Updated links and navigation targets to reflect the new settings URL structure.
  • Style

    • Refined visual styling for tables, icons, headers, and layout components to ensure a consistent and modern look.
  • Chores

    • Added and refactored constants, types, and utility functions to support the new settings structure and permission logic.
  • Documentation

    • Updated translation keys and labels for improved internationalization and clarity in the user interface.

Summary by CodeRabbit

  • New Features

    • Added detailed workspace, project, and profile settings sidebars with dynamic icons and permission-aware navigation.
    • Introduced mobile navigation components with responsive hamburger menus and workspace settings tabs.
    • Launched new settings pages covering billing, exports, imports, integrations, members, automations, estimates, features, labels, states, notifications, security, API tokens, and webhooks.
    • Added user preference management including theme switching, start of week, language, and timezone settings.
    • Implemented export functionality with forms and previous exports listing featuring status indicators and download options.
    • Provided workspace and project settings layouts integrated with authentication and authorization wrappers.
    • Enhanced profile settings with activity tracking, preferences, security, and notification management.
  • Improvements

    • Updated routing and URL structures for projects and workspace settings for improved consistency.
    • Replaced error throwing with user-facing toast notifications for better error handling.
    • Refined UI components using reusable headings, loaders, empty states, and buttons for consistent styling.
    • Improved styling of tables, avatars, buttons, and layout spacing for enhanced visual clarity.
    • Centralized and expanded translation keys to support new and updated UI elements.
    • Updated navigation links and call-to-actions to dynamically reflect the current workspace context.
  • Bug Fixes

    • Fixed redirection after workspace deletion or leaving workspace to use workspace-specific URLs.
    • Cleared stale references to deleted workspaces in user profiles to maintain data integrity.
  • Refactor

    • Modularized settings components into logical groups with shared layouts, sidebars, headers, and tabs.
    • Replaced manual UI constructions with reusable components like SettingsHeading, DetailedEmptyState, and SettingsSidebarNavItem.
    • Simplified exporter guide by delegating rendering to dedicated export form and previous exports components.
    • Streamlined profile and workspace sidebars with permission filtering and icon mapping.
    • Consolidated settings helper functions for pathname-to-label mappings.
  • Style

    • Adjusted padding, margins, and text colors across tables, buttons, headers, and icons.
    • Updated avatar and placeholder sizing for consistent appearance.
    • Enhanced responsive layouts for mobile and desktop views.
  • Chores

    • Extended user settings and workspace stores with observable states for sidebar collapse and scroll detection.
    • Added new type definitions for workspace metadata including last workspace name and logo.
    • Added exports for new constants, components, and helper modules supporting settings features.

Copy link
Contributor

coderabbitai bot commented Apr 23, 2025

Walkthrough

This update introduces a comprehensive reorganization and enhancement of workspace, profile, and project settings across backend and frontend. It adds new settings categories, modularizes preferences (theme, start-of-week, language/timezone), restructures navigation and layout for settings pages, updates API and UI for user preferences, and adjusts routing, permissions, and redirects throughout the application.

Changes

File(s) Change Summary
apiserver/plane/app/serializers/user.py, apiserver/plane/app/views/workspace/base.py Enhanced user workspace serializer to include workspace name and logo; workspace deletion now clears last workspace references from user profiles.
packages/constants/src/index.ts, packages/constants/src/settings.ts, packages/constants/src/profile.ts, packages/constants/src/workspace.ts, packages/types/src/users.d.ts Added and reorganized settings and profile constants, categories, and options; moved API tokens from workspace to profile; updated exports accordingly.
packages/i18n/src/locales/en/translations.json Renamed "Appearance" to "Preferences", added "Language & Time" keys, and renamed "API Tokens" to "Personal Access Tokens" with updated UI text.
web/ce/components/preferences/config.ts, web/ce/components/preferences/theme-switcher.tsx, web/core/components/preferences/list.tsx, web/core/components/preferences/section.tsx Introduced modular preference components for theme switching and start-of-week selection with dynamic rendering and layout components.
web/core/components/settings/* (multiple new files) Added a modular, category-based settings navigation system with responsive sidebar, header, mobile navigation, tabs, and helper utilities for path-label mapping.
web/core/components/settings/content-wrapper.tsx, web/core/store/user/settings.store.ts Added scroll state and sidebar collapse state observables to user settings; content wrapper tracks scroll and adapts layout accordingly.
web/core/components/settings/header.tsx, web/core/components/settings/tabs.tsx Added dynamic settings header and tab navigation components responsive to scroll state and user context.
web/core/store/workspace/index.ts Added method to determine workspace redirection URL based on user settings and existing workspaces.
web/core/components/profile/preferences/language-timezone.tsx, web/core/components/profile/start-of-week-preference.tsx, web/core/components/profile/form.tsx Refactored language, timezone, and start-of-week preferences into dedicated components and moved them under the preferences section; removed language/timezone from profile form.
web/core/components/profile/sidebar.tsx, web/core/components/workspace/sidebar/dropdown.tsx, web/core/components/home/widgets/empty-states/no-projects.tsx, web/core/components/profile/index.ts Updated profile and workspace navigation URLs to use workspace-specific settings/account paths instead of generic profile routes.
web/core/layouts/auth-layout/workspace-wrapper.tsx, web/core/lib/wrappers/authentication-wrapper.tsx Adjusted workspace not found UI and authentication redirection logic to use new workspace/account URLs.
web/app/(all)/[workspaceSlug]/(settings)/... (many new files) Introduced new settings pages and layouts for workspace, account, and project settings with permission checks, modular navigation, improved empty states, and updated routing structure.
web/next.config.js Added permanent redirects to support the new settings URL structure, including project settings path reordering and API tokens path update.
web/core/components/exporter/* (several new files) Refactored export functionality into modular components: export form, previous exports list, and export guide wrapper.
web/core/components/ui/loader/settings/api-token.tsx Localized API token loader heading and adjusted padding for consistency.
web/core/components/estimates/empty-screen.tsx Refactored empty state to use centralized DetailedEmptyState component for cleaner UI.
web/core/components/project/card.tsx, web/core/components/project/settings/member-columns.tsx, web/core/components/workspace/sidebar/projects-list-item.tsx Updated project settings links to new URL structure and adjusted avatar/table styling for compactness.
web/core/components/workspace/settings/members-list-item.tsx, web/core/components/workspace/settings/member-columns.tsx Updated member list item styling; changed redirection logic after leaving workspace to dynamic workspace URL.
web/core/components/profile/activity/profile-activity-list.tsx, web/core/components/profile/notification/email-notification-form.tsx Minor UI and layout adjustments for icon padding and container spacing.
web/core/components/profile/profile-setting-content-header.tsx, web/core/components/workspace/settings/workspace-details.tsx, web/core/components/labels/project-setting-label-list.tsx Minor padding, overflow, and styling refinements.
web/core/components/auth-screens/not-authorized-view.tsx, web/core/layouts/default-layout/index.tsx Added optional className prop for layout components to allow styling customization.
web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/... (multiple files) Updated navigation paths for project settings pages to reflect new URL structure with /settings/projects prefix.
Deleted files: web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/(with-sidebar)/* and related Removed old project and workspace settings layouts, sidebars, and headers replaced by new modular components and layouts.
web/core/components/settings/heading.tsx Added reusable SettingsHeading component for consistent section headers with optional buttons and appended content.
web/core/components/settings/layout.tsx Added SettingsContentLayout component to track scroll state and provide scrollable container for settings pages.
web/core/components/profile/preferences/language-timezone.tsx Added new component for managing language and timezone preferences with async update and toast notifications.
web/core/components/profile/sidebar.tsx Added profile sidebar component with permission-based filtering and dynamic icon rendering.
web/core/components/workspace/sidebar/sidebar.tsx Added workspace settings sidebar with permission filtering, active link detection, and icons.
web/core/components/settings/mobile/nav.tsx Added mobile navigation component for settings with outside click detection and sidebar toggle.
web/core/components/settings/project/sidebar/* Added project settings sidebar components with grouped projects and nested navigation items.
web/core/components/settings/sidebar/* Added generic settings sidebar and navigation item components with expandable children and permission checks.
web/core/components/settings/tabs.tsx Added tab navigation component for Account, Workspace, and Projects settings.
web/core/components/settings/header.tsx Added settings page header component with breadcrumb, back button, and tabs, responsive to scroll state.
web/core/components/settings/content-wrapper.tsx Added content wrapper component for consistent padding and max width in settings pages.
web/core/components/profile/preferences/list.tsx Added preferences list component rendering preference components dynamically.
web/core/components/preferences/theme-switcher.tsx Added theme switcher component with next-themes integration and remote update.
web/core/components/profile/start-of-week-preference.tsx Refactored start-of-week preference to use PreferencesSection component.
web/core/components/profile/form.tsx Removed language and timezone fields; added info banner linking to preferences.
web/core/components/profile/preferences/section.tsx Added reusable layout for preference sections.
web/core/components/exporter/column.tsx, web/core/components/exporter/export-form.tsx, web/core/components/exporter/guide.tsx, web/core/components/exporter/prev-exports.tsx Modularized export UI components for form, previous exports, and guide wrapper.
web/core/components/workspace/delete-workspace-form.tsx, web/core/components/workspace/settings/members-list-item.tsx Updated redirection after workspace leave/delete to dynamic workspace URLs; refetched user settings.
web/core/components/workspace/sidebar/dropdown.tsx Updated user dropdown settings link to workspace-specific URL.
web/core/components/home/widgets/empty-states/no-projects.tsx Updated personalize account CTA link to workspace-specific account settings URL.
web/core/layouts/auth-layout/workspace-wrapper.tsx, web/core/lib/wrappers/authentication-wrapper.tsx Updated workspace not found UI and authentication redirection to use new workspace/account paths.
web/next.config.js Added permanent redirects for new settings URL structure, including project settings path reordering and API tokens path update.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant Store

    User->>Frontend: Navigate to /[workspaceSlug]/settings/account
    Frontend->>Store: Fetch user and workspace settings (includes workspace name/logo)
    Store->>Backend: Request user settings data
    Backend-->>Store: Respond with user settings including workspace metadata
    Store-->>Frontend: Provide user and workspace data
    Frontend->>Frontend: Render settings layout, header, sidebar, preferences
    User->>Frontend: Update a preference (e.g., theme)
    Frontend->>Store: Update preference state
    Store->>Backend: Persist updated preference
    Backend-->>Store: Confirm update
    Store-->>Frontend: Update UI with new preference
Loading

Suggested labels

🌐frontend, ✨feature, pages, ready_to_merge

Suggested reviewers

  • sriramveeraghanta
  • prateekshourya29
  • vamsikrishnamathala

Poem

In the warren of settings, a bunny hopped through,
Tidying preferences, making old paths new.
With sidebars and headers, and themes to delight,
Now every workspace feels just right.
Hop, hop, hooray! The settings are neat—
For every user, a personalized treat!

((\
( -.-)
o_(")(")

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

@sangeethailango sangeethailango changed the title chore: return workspace name and logo in profile settings api feat: settings page revamp Apr 23, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (6)
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/labels/page.tsx (1)

48-55: Consider adding error boundary and loading states.

While the current implementation handles authorization well, consider adding loading states for when currentProjectDetails is being fetched and error boundaries for potential drag-and-drop failures.

  return (
    <SettingsContentWrapper>
      <PageHead title={pageTitle} />
+     {!currentProjectDetails ? (
+       <div className="flex items-center justify-center h-32">
+         <div className="text-custom-text-300">Loading...</div>
+       </div>
+     ) : (
        <div ref={scrollableContainerRef} className="h-full w-full gap-10">
          <ProjectSettingsLabelList />
        </div>
+     )}
    </SettingsContentWrapper>
  );
web/app/[workspaceSlug]/(settings)/settings/projects/page.tsx (1)

20-20: Consider adding alt text localization.

For better internationalization support, consider making the alt text translatable.

-      <Image src={resolvedPath} alt="No projects yet" width={384} height={250} />
+      <Image src={resolvedPath} alt={t("projects.empty_state.alt_text")} width={384} height={250} />

Note: This would require adding the useTranslation hook import and usage.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/page.tsx (1)

26-28: Consider removing redundant opacity styling for unauthorized users.

The opacity reduction on line 33 may be unnecessary since users without admin permissions already see the NotAuthorizedView and never reach the features list rendering.

  if (workspaceUserInfo && !canPerformProjectAdminActions) {
    return <NotAuthorizedView section="settings" isProjectView />;
  }
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/estimates/page.tsx (1)

24-24: Maintain consistency with other settings pages.

The features page returns null for missing parameters, but this returns an empty fragment. Consider using null for consistency.

-  if (!workspaceSlug || !projectId) return <></>;
+  if (!workspaceSlug || !projectId) return null;
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/page.tsx (1)

67-67: Improve accessibility by providing context for opacity change.

The opacity reduction to indicate non-admin status lacks accessibility context. Screen readers won't understand why the content appears faded.

Consider adding an accessible indicator:

-      <div className={`w-full ${isAdmin ? "" : "opacity-60"}`}>
+      <div className={`w-full ${isAdmin ? "" : "opacity-60"}`} aria-label={!isAdmin ? "Read-only view - Admin permissions required to edit" : undefined}>
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/automations/page.tsx (1)

53-53: Remove redundant opacity styling since unauthorized users see a different view.

The opacity styling is redundant because users without admin permissions see the NotAuthorizedView component instead of this content.

Simplify the styling since unauthorized users are already redirected:

-      <section className={`w-full ${canPerformProjectAdminActions ? "" : "opacity-60"}`}>
+      <section className="w-full">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba126fd and f713538.

📒 Files selected for processing (21)
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/intake/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/automations/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/estimates/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/labels/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/sidebar.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/layout.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/page.tsx (1 hunks)
  • web/core/components/issues/issue-layouts/empty-states/archived-issues.tsx (1 hunks)
  • web/core/components/project/card.tsx (2 hunks)
  • web/core/components/settings/project/sidebar/nav-item-children.tsx (1 hunks)
  • web/core/components/settings/project/sidebar/root.tsx (1 hunks)
  • web/core/components/settings/tabs.tsx (1 hunks)
  • web/core/components/workspace/sidebar/projects-list-item.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • web/core/components/workspace/sidebar/projects-list-item.tsx
🚧 Files skipped from review as they are similar to previous changes (10)
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/intake/page.tsx
  • web/core/components/issues/issue-layouts/empty-states/archived-issues.tsx
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx
  • web/core/components/project/card.tsx
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/page.tsx
  • web/core/components/settings/tabs.tsx
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/page.tsx
  • web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/page.tsx
  • web/core/components/settings/project/sidebar/nav-item-children.tsx
  • web/core/components/settings/project/sidebar/root.tsx
🧰 Additional context used
🧬 Code Graph Analysis (2)
web/app/[workspaceSlug]/(settings)/settings/projects/layout.tsx (5)
web/core/store/router.store.ts (2)
  • projectId (85-87)
  • workspaceSlug (69-71)
web/core/store/project/project.store.ts (1)
  • joinedProjectIds (216-227)
web/core/components/settings/mobile/nav.tsx (1)
  • SettingsMobileNav (13-46)
web/core/components/settings/project/sidebar/root.tsx (1)
  • ProjectSettingsSidebar (15-49)
web/core/components/settings/helper.ts (1)
  • getProjectActivePath (50-56)
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/automations/page.tsx (6)
packages/i18n/src/hooks/use-translation.ts (1)
  • useTranslation (23-35)
packages/types/src/project/projects.d.ts (1)
  • IProject (39-55)
web/core/services/project/project.service.ts (1)
  • updateProject (77-83)
web/core/components/settings/content-wrapper.tsx (1)
  • SettingsContentWrapper (11-59)
web/core/components/automation/auto-archive-automation.tsx (1)
  • AutoArchiveAutomation (25-125)
web/core/components/automation/auto-close-automation.tsx (1)
  • AutoCloseAutomation (24-191)
🔇 Additional comments (12)
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/labels/page.tsx (2)

16-29: Excellent permission handling and component structure!

The component correctly implements permission checks and derives the page title dynamically. The permission logic appropriately restricts access to users with ADMIN or MEMBER roles at the project level.


31-42: Auto-scroll implementation looks good with proper cleanup.

The drag-and-drop auto-scroll is correctly implemented using the combine utility, which ensures proper cleanup when the component unmounts. The effect properly checks for element existence before setting up the auto-scroll.

web/app/[workspaceSlug]/(settings)/settings/projects/page.tsx (1)

14-17: Theme-aware image handling is well implemented.

The conditional image path based on theme is a good UX practice. The resolved theme check properly handles both dark and light modes.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (1)

15-29: Good permission logic and internationalization usage.

The component properly implements permission checks and uses internationalization for the "States" label. The permission logic correctly restricts access to ADMIN or MEMBER roles at the project level.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (2)

19-24: Excellent granular permission handling!

The permission logic is well-structured, combining both project-level and workspace-level permissions. This provides appropriate access control where workspace admins can manage project members even if they're not project members themselves.


33-36: Clean component composition with proper semantic structure.

The use of semantic <section> element and clean separation of ProjectSettingsMemberDefaults and ProjectMemberList components follows good React composition patterns.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/page.tsx (1)

14-42: LGTM: Well-structured component with proper permission handling.

The component follows good practices with:

  • Proper MobX observer pattern
  • Null checks for required parameters
  • Permission-based authorization
  • Dynamic page title generation
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/estimates/page.tsx (1)

33-33: Good implementation of disabled state.

The use of both pointer-events-none and opacity-60 properly disables interaction while providing visual feedback when users lack admin permissions.

web/app/[workspaceSlug]/(settings)/settings/projects/layout.tsx (1)

18-44: Well-designed layout component with proper authorization.

The layout effectively handles:

  • Auto-redirect for missing project IDs
  • Mobile navigation integration
  • Proper authorization wrapping
  • Conditional sidebar rendering
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/sidebar.tsx (2)

30-43: Excellent loading state implementation.

The skeleton loader with proper placeholder items provides good UX while waiting for user permissions to load.


50-68: Good permission-based navigation rendering.

The sidebar properly:

  • Checks permissions for each link before rendering
  • Uses internationalization for labels
  • Implements proper active state highlighting
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/automations/page.tsx (1)

32-32: 🛠️ Refactor suggestion

Add validation before type conversion to prevent runtime errors.

The function converts workspaceSlug and projectId to strings without validating they exist, which could cause runtime errors if the params are undefined.

Add proper validation:

-  const handleChange = async (formData: Partial<IProject>) => {
-    if (!workspaceSlug || !projectId || !projectDetails) return;
+  const handleChange = async (formData: Partial<IProject>) => {
+    if (!workspaceSlug || !projectId || !projectDetails) {
+      console.warn("Missing required parameters for project update");
+      return;
+    }

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🔭 Outside diff range comments (1)
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (1)

13-42: 🛠️ Refactor suggestion

Add loading state handling for better UX

The component doesn't handle loading states when currentProjectDetails or user permissions are being fetched, which could result in flickering or incomplete renders.

Consider adding loading state handling:

 const MembersSettingsPage = observer(() => {
   // store
   const { currentProjectDetails } = useProject();
   const { workspaceUserInfo, allowPermissions } = useUserPermissions();
+  
+  // Show loading state while data is being fetched
+  if (!currentProjectDetails || workspaceUserInfo === undefined) {
+    return <div className="flex justify-center items-center h-64">Loading...</div>;
+  }
   
   // derived values
   const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Members` : undefined;
♻️ Duplicate comments (5)
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/labels/page.tsx (1)

32-42: Move the DND logic to the ProjectSettingsLabelsList component.

The auto-scroll setup for drag-and-drop functionality should be moved to the ProjectSettingsLabelsList component for better separation of concerns and modularity.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (1)

42-44: Type safety issue persists from previous review.

The URL parameters still require proper type validation as identified in the previous review. The use of .toString() indicates these parameters might not be strings, which could cause runtime issues.

This is the same type safety concern raised in the previous review about validating workspaceSlug and projectId before using them.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (1)

31-31: SettingsContentWrapper sizing is consistent with past decisions

This component correctly uses size="lg" for a member list page, which aligns with the established pattern for table/list pages as identified in previous reviews.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/automations/page.tsx (2)

29-29: Fix incomplete permission check - missing workspace and project context.

The permission check is missing the required workspace and project context parameters, making it less precise than expected for project-level permissions.

Based on standard permission check patterns, the allowPermissions call should include workspace and project context:

-  const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
+  const canPerformProjectAdminActions = allowPermissions(
+    [EUserPermissions.ADMIN],
+    EUserPermissionsLevel.PROJECT,
+    workspaceSlug?.toString(),
+    projectId?.toString()
+  );

34-40: Enhance error handling with proper logging and user feedback.

The error handling catches all errors but doesn't log them for debugging purposes, and provides a generic error message that doesn't help users understand what went wrong.

Improve error handling:

-    await updateProject(workspaceSlug.toString(), projectId.toString(), formData).catch(() => {
+    await updateProject(workspaceSlug.toString(), projectId.toString(), formData).catch((error) => {
+      console.error("Failed to update project automation settings:", error);
       setToast({
         type: TOAST_TYPE.ERROR,
         title: "Error!",
-        message: "Something went wrong. Please try again.",
+        message: error?.message || "Failed to update automation settings. Please try again.",
       });
     });
🧹 Nitpick comments (6)
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (1)

23-25: Remove duplicate comment.

There's a duplicate "// derived values" comment on both lines 23 and 25.

  const { t } = useTranslation();

  // derived values
  const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - States` : undefined;
- // derived values
  const canPerformProjectMemberActions = allowPermissions(
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/estimates/page.tsx (3)

10-11: Move SettingsContentWrapper import to the components section.

The SettingsContentWrapper is a component but is imported after the "// hooks" comment, which could cause confusion.

// components
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { NotAuthorizedView } from "@/components/auth-screens";
import { PageHead } from "@/components/core";
import { EstimateRoot } from "@/components/estimates";
+import { SettingsContentWrapper } from "@/components/settings";
// hooks
-import { SettingsContentWrapper } from "@/components/settings";
import { useProject, useUserPermissions } from "@/hooks/store";

14-15: Add TypeScript types for better type safety.

Consider adding type annotations for the component and params to improve type safety and catch potential runtime errors.

-const EstimatesSettingsPage = observer(() => {
-  const { workspaceSlug, projectId } = useParams();
+const EstimatesSettingsPage: React.FC = observer(() => {
+  const { workspaceSlug, projectId } = useParams<{ workspaceSlug: string; projectId: string }>();

24-24: Return null instead of empty fragment.

For cleaner code, return null instead of an empty fragment when params are missing.

-  if (!workspaceSlug || !projectId) return <></>;
+  if (!workspaceSlug || !projectId) return null;
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/page.tsx (2)

10-12: Fix import organization - SettingsContentWrapper is not a hook.

The SettingsContentWrapper import is placed under the "// hooks" comment, but it's a component, not a hook.

-// hooks
-import { SettingsContentWrapper } from "@/components/settings";
-import { useProject, useUserPermissions } from "@/hooks/store";
+import { SettingsContentWrapper } from "@/components/settings";
+// hooks  
+import { useProject, useUserPermissions } from "@/hooks/store";

33-33: Remove redundant opacity styling.

The conditional opacity styling is unnecessary since non-admin users are already redirected by the permission check above.

-      <section className={`w-full ${canPerformProjectAdminActions ? "" : "opacity-60"}`}>
+      <section className="w-full">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58454be and 426ec3d.

📒 Files selected for processing (16)
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/billing/page.tsx (2 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/exports/page.tsx (3 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/imports/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/integrations/page.tsx (3 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/layout.tsx (2 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/members/page.tsx (3 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/webhooks/page.tsx (3 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx (3 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/automations/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/estimates/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/labels/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (1 hunks)
  • web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (1 hunks)
  • web/core/components/auth-screens/not-authorized-view.tsx (1 hunks)
  • web/core/layouts/default-layout/index.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/billing/page.tsx
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/members/page.tsx
  • web/app/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/imports/page.tsx
  • web/app/[workspaceSlug]/(settings)/settings/(workspace)/layout.tsx
🔇 Additional comments (21)
web/core/components/auth-screens/not-authorized-view.tsx (3)

15-15: LGTM: Well-defined optional styling prop.

The addition of the optional className prop follows TypeScript best practices and maintains backwards compatibility.


19-19: LGTM: Clean destructuring implementation.

The destructuring is properly updated to include the new className parameter while maintaining a clean function signature.


26-26: LGTM: Proper prop forwarding to layout component.

The className prop is correctly forwarded to the DefaultLayout component, enabling external styling customization while following React patterns for prop delegation.

web/app/[workspaceSlug]/(settings)/settings/(workspace)/exports/page.tsx (3)

11-11: LGTM! Proper import added for layout wrapper.

The import for SettingsContentWrapper is correctly added to support the new layout structure.


33-33: Good improvement to unauthorized view styling.

Adding explicit className="h-auto" provides better height control for the unauthorized state, which is consistent with the layout improvements across the settings pages.


37-49: Excellent refactoring to standardize layout structure.

The introduction of SettingsContentWrapper with size="lg" creates a consistent layout pattern across settings pages. The removal of overflow-y-auto from the inner div (line 40) is appropriate since the wrapper component likely handles scroll behavior centrally, improving the overall user experience and maintainability.

web/core/layouts/default-layout/index.tsx (3)

2-2: LGTM: Clean utility import.

The import of the cn utility from @plane/utils is correctly added to support class name merging functionality.


7-7: LGTM: Well-typed optional prop.

The optional className prop is properly typed as string and follows React conventions for allowing external styling.


10-14: LGTM: Clean implementation with proper class merging.

The component correctly:

  • Destructures the new className parameter
  • Uses the cn utility to merge base classes with the optional className prop
  • Preserves the existing gradient conditional logic
  • Maintains proper TypeScript typing

This enhancement successfully adds external styling capability while maintaining backward compatibility.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/labels/page.tsx (2)

16-30: Well-structured component setup with proper permission handling.

The component correctly uses MobX observer pattern, sets up store hooks appropriately, and implements proper permission checking for project-level access control.


48-55: Clean render structure with appropriate component composition.

The render logic is well-organized with proper use of the settings wrapper, page head component, and scrollable container setup. The component composition follows React best practices.

web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (2)

15-33: Well-structured permission handling and authorization logic.

The component correctly:

  • Uses MobX observer pattern for reactive updates
  • Implements proper permission checks using allowPermissions
  • Handles unauthorized access with NotAuthorizedView
  • Follows the permission-based rendering pattern consistent with other settings pages

35-47: Clean render logic with proper layout structure.

The render logic is well-organized with:

  • Proper use of SettingsContentWrapper for consistent layout
  • Internationalized text using the translation function
  • Clean header structure with border styling
  • Conditional rendering based on parameter availability
web/app/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (1)

19-24: Permission logic is well-structured and secure

The permission logic correctly implements the business rule where either project members/admins OR workspace admins can manage project members. The separation of concerns between project-level and workspace-level permissions is clear and maintainable.

web/app/[workspaceSlug]/(settings)/settings/(workspace)/integrations/page.tsx (3)

7-7: Great improvement! Consistent unauthorized handling implemented.

The addition of NotAuthorizedView component addresses the previous feedback about having consistent unauthorized components across settings pages. The early return pattern with the dedicated component is much cleaner than the previous inline implementation.

Also applies to: 35-35


10-10: Excellent! Layout consistency achieved with SettingsContentWrapper.

The implementation properly addresses the previous feedback by wrapping the main content in SettingsContentWrapper. This ensures consistent styling and layout across all workspace settings pages. The size="lg" prop usage is appropriate for this content type.

Also applies to: 38-38, 52-52


31-33: Hook usage is actually correct - clarifying previous feedback.

Regarding the previous comment about "calling a hook conditionally" - the useSWR hook is actually being called correctly here. The hook itself is always invoked, but with conditional parameters (key and fetcher). This is a valid SWR pattern where:

  • When isAdmin is false, the key becomes null and SWR won't fetch
  • When isAdmin is true, SWR will fetch the data

This pattern is recommended by SWR documentation for conditional fetching and doesn't violate the Rules of Hooks.

web/app/[workspaceSlug]/(settings)/settings/(workspace)/webhooks/page.tsx (4)

15-15: LGTM: Clean architectural improvement.

The import of SettingsContentWrapper aligns well with the settings page revamp objective, promoting consistent layout patterns across settings pages.


58-58: LGTM: Consistent wrapper implementation.

The SettingsContentWrapper provides a clean abstraction for settings page layouts, replacing the previous React fragment. This promotes consistency across the settings section.

Also applies to: 104-104


90-98: Excellent UX enhancement to the empty state.

The improvements to DetailedEmptyState significantly enhance user experience:

  • Custom styling with className="!px-0 py-5"
  • Appropriate sizing with size="sm"
  • Actionable primary button that directly enables webhook creation

This makes the empty state more engaging and functional.


60-60:

✅ Verification successful

Verify scroll behavior after removing overflow-y-auto.

The removal of overflow-y-auto class suggests that scroll handling is now managed by SettingsContentWrapper. Ensure the parent wrapper properly handles content overflow to prevent layout issues.


🏁 Script executed:

#!/bin/bash
# Description: Check SettingsContentWrapper implementation for overflow handling
# Expected: Find the component definition and verify it handles scrolling appropriately

# Search for SettingsContentWrapper component definition
ast-grep --pattern 'const SettingsContentWrapper = $_'
ast-grep --pattern 'function SettingsContentWrapper($_) { $$$ }'

# Check for overflow handling in the component
rg -A 10 -B 5 'overflow' --type tsx --type ts | rg -A 10 -B 5 'SettingsContentWrapper'

Length of output: 4328


Scroll behavior is covered by SettingsContentWrapper – no further action required

SettingsContentWrapper’s root <div> already includes overflow-y-scroll (see web/core/components/settings/content-wrapper.tsx:50), so removing overflow-y-auto from the child won’t break scrolling.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 23

♻️ Duplicate comments (1)
web/core/components/core/modals/workspace-image-upload-modal.tsx (1)

74-80: Good toast notification addition, but address previous feedback on error logging.

The toast notification is a great improvement for user experience. However, the previous review comment about adding more descriptive error logging hasn't been fully addressed.

The console.log still doesn't indicate where the error originated in the frontend. Consider updating it to:

-      console.log("error", error);
+      console.log("Error uploading workspace image:", error);

This would help with debugging and address the previous reviewer's concern about identifying the error source in the frontend.

🧹 Nitpick comments (40)
web/app/(all)/[workspaceSlug]/(settings)/settings/account/page.tsx (1)

24-29: Add error handling for better user experience.

The component lacks error handling for cases where user data fails to load. Consider adding error boundary or error state handling to improve user experience.

Consider adding error handling:

+ const { data: currentUser, userProfile, error } = useUser();
+
+ if (error) {
+   return (
+     <div className="grid h-full w-full place-items-center px-4 sm:px-0">
+       <div className="text-center">
+         <p className="text-red-500">{t("error.loading_profile")}</p>
+       </div>
+     </div>
+   );
+ }
web/app/(all)/[workspaceSlug]/(settings)/settings/account/activity/page.tsx (3)

17-17: Add TypeScript types for better type safety.

The component lacks TypeScript type definitions. Consider adding proper types for improved type safety and developer experience.

Example implementation:

-const ProfileActivityPage = observer(() => {
+const ProfileActivityPage: React.FC = observer(() => {

Additionally, consider defining interfaces for the state and callback functions.


73-75: Enhance accessibility for the load more button.

Add an aria-label to improve accessibility for screen reader users.

-          <Button variant="accent-primary" size="sm" onClick={handleLoadMore}>
+          <Button variant="accent-primary" size="sm" onClick={handleLoadMore} aria-label={t("load_more_activities")}>
             {t("load_more")}
           </Button>

15-15: Consider making PER_PAGE configurable.

The hardcoded value of 100 items per page might not be optimal for all use cases. Consider making this configurable through environment variables or user preferences.

web/app/(all)/[workspaceSlug]/(settings)/settings/account/preferences/page.tsx (1)

21-33: Remove unnecessary fragment wrapper

The fragment wrapper <>...</> is redundant here since it contains only a single child element.

Apply this diff to simplify the JSX:

-        <>
-          <div className="flex flex-col gap-4 w-full">
-            <div>
-              <ProfileSettingContentHeader title={t("preferences")} />
-              <PreferencesList />
-            </div>
-            <div>
-              <ProfileSettingContentHeader title={t("language_and_time")} />
-              <LanguageTimezone />
-            </div>
-          </div>
-        </>
+        <div className="flex flex-col gap-4 w-full">
+          <div>
+            <ProfileSettingContentHeader title={t("preferences")} />
+            <PreferencesList />
+          </div>
+          <div>
+            <ProfileSettingContentHeader title={t("language_and_time")} />
+            <LanguageTimezone />
+          </div>
+        </div>
web/app/(all)/[workspaceSlug]/(settings)/settings/account/notifications/page.tsx (2)

13-13: Consider moving service instantiation inside the component or using a singleton pattern.

The UserService instance is created outside the component, which could lead to potential issues with module-level side effects or memory leaks in server-side rendering contexts.

Consider one of these approaches:

-const userService = new UserService();
-
 export default function ProfileNotificationPage() {
+  const userService = new UserService();
   const { t } = useTranslation();

Or create a singleton service:

-const userService = new UserService();
+const getUserService = () => UserService.getInstance();
 
 export default function ProfileNotificationPage() {
   const { t } = useTranslation();
   // fetching user email notification settings
-  const { data, isLoading } = useSWR("CURRENT_USER_EMAIL_NOTIFICATION_SETTINGS", () =>
-    userService.currentUserEmailNotificationSettings()
+  const { data, isLoading } = useSWR("CURRENT_USER_EMAIL_NOTIFICATION_SETTINGS", () =>
+    getUserService().currentUserEmailNotificationSettings()
   );

22-24: Optimize loading condition logic.

The condition !data || isLoading might be redundant since SWR typically handles the data/loading states appropriately. When isLoading is true, data is usually undefined anyway.

-  if (!data || isLoading) {
+  if (isLoading) {
     return <EmailSettingsLoader />;
   }

However, if you want to be explicit about both conditions for clarity, the current approach is also acceptable.

web/app/(all)/[workspaceSlug]/(settings)/settings/account/security/page.tsx (3)

101-101: Use strict equality for enum comparison.

-    getPasswordStrength(password) != E_PASSWORD_STRENGTH.STRENGTH_VALID ||
+    getPasswordStrength(password) !== E_PASSWORD_STRENGTH.STRENGTH_VALID ||

113-113: Simplify password match error display logic for better UX.

The current logic for showing password mismatch errors is complex and might show errors while the user is still typing. Consider showing the error only after the field loses focus or when the user has typed enough characters.

-  const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;
+  const renderPasswordMatchError = !isRetryPasswordInputFocused && confirmPassword.length > 0;

This ensures the error only shows after the user has finished typing in the confirm password field.

Also applies to: 233-235


109-109: Use strict equality for consistency.

-    getPasswordStrength(password) != E_PASSWORD_STRENGTH.STRENGTH_VALID && (
+    getPasswordStrength(password) !== E_PASSWORD_STRENGTH.STRENGTH_VALID && (
web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx (2)

40-44: Simplify the SWR fetcher function.

The condition check is duplicated in both the SWR key and fetcher function. The fetcher should assume it's only called when the key is valid.

 const { data: tokens } = useSWR(
   workspaceSlug && canPerformWorkspaceAdminActions ? API_TOKENS_LIST(workspaceSlug.toString()) : null,
-  () =>
-    workspaceSlug && canPerformWorkspaceAdminActions ? apiTokenService.getApiTokens(workspaceSlug.toString()) : null
+  () => apiTokenService.getApiTokens(workspaceSlug.toString())
 );

91-95: Consider removing the duplicate "Add Token" button.

The empty state includes a primary button that duplicates the functionality of the "Add Token" button in the header. Having two buttons with identical actions on the same view may confuse users.

Consider removing the primaryButton from the DetailedEmptyState since users can use the header button:

 <DetailedEmptyState
   title={t("workspace_settings.empty_state.api_tokens.title")}
   description={t("workspace_settings.empty_state.api_tokens.description")}
   assetPath={resolvedPath}
   className="w-full !px-0 justify-center mx-auto"
   size="sm"
-  primaryButton={{
-    text: t("workspace_settings.settings.api_tokens.add_token"),
-    onClick: () => setIsCreateTokenModalOpen(true),
-  }}
 />
web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/mobile-header-tabs.tsx (1)

20-20: Consider extracting z-index to a constant.

The hardcoded z-10 might conflict with other UI elements. Consider defining z-index values in a central constants file for better maintainability.

web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx (1)

59-62: Consider documenting the special case for /settings route.

The isActive function has special handling for the exact /settings route vs nested routes. Consider adding a comment explaining why this distinction is necessary.

 isActive={(data: { href: string }) =>
+  // Exact match for /settings root, regex match for nested routes
   data.href === "/settings"
     ? pathname === `/${workspaceSlug}${data.href}/`
     : new RegExp(`^/${workspaceSlug}${data.href}/`).test(pathname)
 }
web/app/(all)/[workspaceSlug]/(settings)/settings/account/layout.tsx (1)

15-33: Consider aligning component name with file path

The component is named ProfileSettingsLayout but is located in the account folder. Consider renaming either the component or the folder to maintain consistency and avoid confusion.

If this is meant to be for account settings:

-const ProfileSettingsLayout = observer((props: Props) => {
+const AccountSettingsLayout = observer((props: Props) => {

And update the export:

-export default ProfileSettingsLayout;
+export default AccountSettingsLayout;
web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (1)

33-33: Remove unnecessary template literal

The template literal is not needed here since there's no string interpolation.

-      <section className={`w-full`}>
+      <section className="w-full">
web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/exports/page.tsx (2)

18-18: Consider removing unused variable or adding null check earlier.

The workspaceUserInfo variable is only used to check existence on line 32. Consider either:

  1. Removing it if not needed elsewhere
  2. Moving the null check earlier in the component for consistency with other permission checks
-  const { workspaceUserInfo, allowPermissions } = useUserPermissions();
+  const { allowPermissions } = useUserPermissions();

Also applies to: 32-33


40-42: Remove redundant opacity styling.

The opacity styling is unnecessary since unauthorized users will see the NotAuthorizedView component instead of this content.

-      <div
-        className={cn("w-full", {
-          "opacity-60": !canPerformWorkspaceMemberActions,
-        })}
-      >
+      <div className="w-full">
web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/imports/page.tsx (2)

14-14: Remove incomplete comment.

The comment // router appears to be incomplete or leftover from previous code.

-  // router

17-17: Add workspaceUserInfo check for consistency.

For consistency with the ExportsPage and to prevent potential issues, consider checking if workspaceUserInfo exists before evaluating permissions.

-  const { allowPermissions } = useUserPermissions();
+  const { workspaceUserInfo, allowPermissions } = useUserPermissions();
-  if (!isAdmin) return <NotAuthorizedView section="settings" className="h-auto" />;
+  if (workspaceUserInfo && !isAdmin) return <NotAuthorizedView section="settings" className="h-auto" />;

Also applies to: 22-22

web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/estimates/page.tsx (2)

24-24: Return null instead of empty fragment.

For better React practices and consistency, return null instead of an empty fragment when conditions aren't met.

-  if (!workspaceSlug || !projectId) return <></>;
+  if (!workspaceSlug || !projectId) return null;

33-33: Use cn() helper for consistency.

For consistency with other settings pages (like ExportsPage), use the cn() helper function for conditional class names.

First, add the import:

 import { NotAuthorizedView } from "@/components/auth-screens";
 import { PageHead } from "@/components/core";
 import { EstimateRoot } from "@/components/estimates";
+// helpers
+import { cn } from "@/helpers/common.helper";
 // hooks
 import { SettingsContentWrapper } from "@/components/settings";

Then update the className:

-      <div className={`w-full ${canPerformProjectAdminActions ? "" : "pointer-events-none opacity-60"}`}>
+      <div className={cn("w-full", {
+        "pointer-events-none opacity-60": !canPerformProjectAdminActions,
+      })}>
web/app/(all)/[workspaceSlug]/(settings)/settings/projects/page.tsx (1)

9-36: Consider renaming the component to better reflect its purpose.

The component is named ProjectSettingsPage but it actually renders an empty state for when there are no projects. A more descriptive name like ProjectsEmptyState or NoProjectsPage would better communicate its purpose.

web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/webhooks/[webhookId]/page.tsx (2)

29-32: Address or remove the TODO comment.

There's commented-out code with a TODO that appears to handle clearing a secret key based on an isCreated parameter. This unfinished functionality should either be implemented or removed to maintain code cleanliness.

Would you like me to help implement this functionality or open an issue to track this TODO?


38-43: Add type safety for URL parameters.

Consider adding type guards or validation for workspaceSlug and webhookId to ensure they are strings before using them.

  useSWR(
-    workspaceSlug && webhookId && isAdmin ? `WEBHOOK_DETAILS_${workspaceSlug}_${webhookId}` : null,
+    workspaceSlug && webhookId && isAdmin ? `WEBHOOK_DETAILS_${workspaceSlug.toString()}_${webhookId.toString()}` : null,
    workspaceSlug && webhookId && isAdmin
      ? () => fetchWebhookById(workspaceSlug.toString(), webhookId.toString())
      : null
  );
web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/integrations/page.tsx (1)

31-33: Simplify the SWR fetcher function

The conditional logic in the fetcher function is redundant since SWR won't call the fetcher when the key is falsy.

-  const { data: appIntegrations } = useSWR(workspaceSlug && isAdmin ? APP_INTEGRATIONS : null, () =>
-    workspaceSlug && isAdmin ? integrationService.getAppIntegrationsList() : null
-  );
+  const { data: appIntegrations } = useSWR(
+    workspaceSlug && isAdmin ? APP_INTEGRATIONS : null,
+    () => integrationService.getAppIntegrationsList()
+  );
web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/layout.tsx (2)

31-35: Consider simplifying the type annotation for isAuthorized.

The variable isAuthorized is typed as boolean | string, but the expression always returns a boolean value. The type annotation could be misleading.

-  const isAuthorized: boolean | string =
+  const isAuthorized: boolean =
     pathname &&
     workspaceSlug &&
     userWorkspaceRole &&
     WORKSPACE_SETTINGS_ACCESS[accessKey]?.includes(userWorkspaceRole as EUserWorkspaceRoles);

49-49: Remove redundant CSS classes.

The combination of sm:hidden and hidden is redundant since hidden already hides the element on all screen sizes.

-            <div className="flex-shrink-0 overflow-y-hidden sm:hidden hidden md:block lg:block">
+            <div className="flex-shrink-0 overflow-y-hidden hidden md:block">
web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/page.tsx (1)

26-39: Remove dead code for non-admin opacity styling.

The opacity styling on line 33 for non-admin users is unreachable code since users without admin permissions are redirected to NotAuthorizedView on lines 26-28.

   return (
     <SettingsContentWrapper>
       <PageHead title={pageTitle} />
-      <section className={`w-full ${canPerformProjectAdminActions ? "" : "opacity-60"}`}>
+      <section className="w-full">
         <ProjectFeaturesList
           workspaceSlug={workspaceSlug.toString()}
           projectId={projectId.toString()}
           isAdmin={canPerformProjectAdminActions}
         />
       </section>
     </SettingsContentWrapper>
   );
web/app/(all)/[workspaceSlug]/(settings)/settings/projects/layout.tsx (1)

26-31: Verify that using the first project in the array is the intended behavior.

The redirect logic uses joinedProjectIds[0] which assumes the array is consistently ordered. Consider if this should use a more deterministic approach like the most recently accessed project or a default project setting.

Consider implementing a more user-friendly approach:

  • Store the last accessed project in user preferences
  • Allow users to set a default project
  • Sort projects by last accessed time

This would provide a better user experience than always redirecting to the first project in the array.

web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (1)

24-25: Remove duplicate comment.

There's a duplicate "// derived values" comment that should be removed for clarity.

   // derived values
   const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - States` : undefined;
-  // derived values
   const canPerformProjectMemberActions = allowPermissions(
web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/automations/page.tsx (3)

43-44: Remove duplicate comment.

There's a duplicate "// derived values" comment that should be removed.

-  // derived values
   const pageTitle = projectDetails?.name ? `${projectDetails?.name} - Automations` : undefined;

34-40: Enhance error handling with specific error messages.

The current error handling only shows a generic message. Consider providing more context about what went wrong.

-    await updateProject(workspaceSlug.toString(), projectId.toString(), formData).catch(() => {
+    await updateProject(workspaceSlug.toString(), projectId.toString(), formData).catch((error) => {
       setToast({
         type: TOAST_TYPE.ERROR,
         title: "Error!",
-        message: "Something went wrong. Please try again.",
+        message: error?.message || "Failed to update project automation settings. Please try again.",
       });
     });

53-53: Remove redundant opacity styling.

The opacity styling on line 53 appears redundant since users without admin permissions are already redirected to NotAuthorizedView on line 47.

-      <section className={`w-full ${canPerformProjectAdminActions ? "" : "opacity-60"}`}>
+      <section className="w-full">
web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/page.tsx (2)

31-36: Address TODO comment about API call necessity.

The TODO comment suggests this API call might be redundant. If currentProjectDetails is already available from the store, consider removing this fetch.

Would you like me to help verify if this API call is necessary by checking how currentProjectDetails is populated in the store?


87-87: Simplify redundant null fallback.

Using nullish coalescing with null as the fallback value is redundant.

-              handleDelete={() => setSelectedProject(currentProjectDetails.id ?? null)}
+              handleDelete={() => setSelectedProject(currentProjectDetails.id)}
web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/members/page.tsx (3)

88-88: Fix unnecessary template literal.

The template literal is redundant when using the nullish coalescing operator.

-          message: `${err.error ?? t("something_went_wrong_please_try_again")}`,
+          message: err.error ?? t("something_went_wrong_please_try_again"),

111-114: Remove redundant opacity styling.

The opacity styling based on canPerformWorkspaceMemberActions is unnecessary since unauthorized users are already redirected at line 98-100.

-      <section
-        className={cn("w-full h-full", {
-          "opacity-60": !canPerformWorkspaceMemberActions,
-        })}
-      >
+      <section className="w-full h-full">

128-128: Consider removing autoFocus from search input.

The autoFocus attribute might be disruptive to users who navigate using keyboard or screen readers, as it automatically moves focus away from the expected tab order.

-              autoFocus
web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/page.tsx (1)

14-26: Consider adding loading state for better UX.

While the current implementation handles undefined workspace gracefully, you might want to add a loading state if workspace data is fetched asynchronously.

Example implementation:

 const WorkspaceSettingsPage = observer(() => {
   // store hooks
-  const { currentWorkspace } = useWorkspace();
+  const { currentWorkspace, isLoading } = useWorkspace();
   const { t } = useTranslation();
   // derived values
   const pageTitle = currentWorkspace?.name
     ? t("workspace_settings.page_label", { workspace: currentWorkspace.name })
     : undefined;

+  if (isLoading) {
+    return (
+      <SettingsContentWrapper>
+        <PageHead title={t("workspace_settings.loading")} />
+        <div>Loading workspace settings...</div>
+      </SettingsContentWrapper>
+    );
+  }
+
   return (
     <SettingsContentWrapper>
       <PageHead title={pageTitle} />
       <WorkspaceDetails />
     </SettingsContentWrapper>
   );
 });

Note: This assumes the useWorkspace hook provides an isLoading state. Adjust based on your actual store implementation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 426ec3d and aa2a8e1.

📒 Files selected for processing (36)
  • packages/i18n/src/locales/en/translations.json (6 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/intake/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/layout.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/billing/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/exports/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/imports/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/integrations/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/layout.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/members/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/mobile-header-tabs.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/webhooks/[webhookId]/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/webhooks/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/activity/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/layout.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/notifications/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/preferences/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/security/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/sidebar.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/automations/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/estimates/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/labels/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/layout.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/page.tsx (1 hunks)
  • web/core/components/core/modals/workspace-image-upload-modal.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/page.tsx
🔇 Additional comments (33)
web/core/components/core/modals/workspace-image-upload-modal.tsx (2)

1-1: Flag inconsistent AI summary.

The AI-generated summary describes changes to an EstimateEmptyScreen component, but this file contains a WorkspaceImageUploadModal component. The summary appears to be for a different file entirely.

Likely an incorrect or invalid review comment.


11-11: Good addition of toast imports for user notifications.

Adding the toast imports enables better user experience by showing error messages directly to users instead of just logging to console.

web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/page.tsx (1)

71-71:

✅ Verification successful

URL restructuring looks good, but verify route consistency.

The path change from /projects/{projectId}/settings/features to /settings/projects/{projectId}/features aligns well with the settings page revamp objective and centralizes settings navigation.

Please verify that:

  1. The new route pattern is consistently implemented across all settings-related navigation
  2. The target route /settings/projects/{projectId}/features actually exists and is properly defined

🏁 Script executed:

#!/bin/bash
# Description: Verify the new settings route structure is consistently used across the codebase
# Expected: All settings navigation should use the new `/settings/projects/` pattern

echo "=== Checking for old route patterns that may need updating ==="
rg -n "/${workspaceSlug}/projects/.*?/settings" --type tsx --type ts --type js --type jsx

echo -e "\n=== Checking for new route pattern usage ==="
rg -n "/${workspaceSlug}/settings/projects" --type tsx --type ts --type js --type jsx

echo -e "\n=== Checking if the target route definition exists ==="
fd -e tsx -e ts | xargs rg -l "settings/projects/.*?/features"

Length of output: 1211


I’d like to double-check that there are no lingering old URLs and that the actual route file is in place. Please run:


🏁 Script executed:

#!/bin/bash
echo "=== Checking for any remaining old `/projects/{id}/settings` usages ==="
rg -n "/projects/.*/settings" -g "*.tsx"

echo -e "\n=== Locating the new Next.js route definition for features ==="
find web/app -type f -path "*/settings/projects/*/features/page.tsx"

Length of output: 597


Routing update verified – no legacy routes remain

I’ve confirmed that:

  • There are no remaining imports or links using the old /projects/{projectId}/settings pattern.
  • The new feature route is defined at
    web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/page.tsx

Everything is consistent with the settings-centric URL structure.

web/app/(all)/[workspaceSlug]/(settings)/settings/account/page.tsx (3)

1-11: Clean imports and setup structure.

The component imports are well-organized and follow good practices with proper separation of concerns. The use of "use client" directive is appropriate for this interactive settings component.


12-30: Well-structured component with MobX observer pattern.

The component follows React best practices with proper use of MobX observer for reactive state management. The conditional rendering and component composition are clean and maintainable.


26-26:

✅ Verification successful

Verify translation keys exist.

Ensure that the translation keys "profile.label" and "general_settings" are properly defined in the i18n configuration.


🏁 Script executed:

#!/bin/bash
# Description: Check if translation keys exist in the i18n files
# Expected: Find both translation keys in locale files

rg -r '$1' '"(profile\.label|general_settings)"' --type json

Length of output: 1709


🏁 Script executed:

#!/bin/bash
# Search for nested "profile" entries in translation files to verify existence of "profile.label"
rg -n '"profile"' --type json packages/i18n/src/locales -A2

Length of output: 9165


Translation keys confirmed in i18n files
Both "profile.label" and "general_settings" are defined across all locale JSONs under packages/i18n/src/locales. No further action is needed.

web/app/(all)/[workspaceSlug]/(settings)/settings/account/notifications/page.tsx (3)

1-12: Well-organized imports with good separation of concerns.

The imports are logically grouped and the "use client" directive is correctly placed for Next.js app router client components. The import structure follows good practices with clear separation between external libraries, internal components, and services.


15-36: Clean component structure with good separation of concerns.

The component follows React best practices with proper hook usage, conditional rendering, and clean JSX structure. The use of internationalization and dedicated UI components shows good architectural planning.


28-32:

✅ Verification successful

Verify translation keys exist in the i18n configuration.

The component uses several translation keys that should be verified to exist in the internationalization configuration.


🏁 Script executed:

#!/bin/bash
# Description: Check if the translation keys used in this component exist in the i18n files
# Expected: Find corresponding entries for the translation keys

echo "Searching for translation keys used in the notifications page..."

# Search for translation key definitions
rg -A 2 -B 2 '"profile\.label"' 
rg -A 2 -B 2 '"notifications"'
rg -A 2 -B 2 '"email_notifications"'
rg -A 2 -B 2 '"stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified"'

echo "Checking i18n files structure..."
fd -e json -e ts -e js | grep -E "(i18n|locale|translation)" | head -10

Length of output: 56669


🏁 Script executed:

#!/bin/bash
# Verify that 'profile.label' is defined in the default locale translations (English)
echo "Searching for 'profile.label' in packages/i18n/src/locales/en/translations.json..."
rg -n -C2 '"profile"' packages/i18n/src/locales/en/translations.json

Length of output: 497


Translation keys verified in i18n configuration

All translation keys used in notifications/page.tsx are present in the default English locale:

  • profile.label (packages/i18n/src/locales/en/translations.json line 1494–1496)
  • notifications (packages/i18n/src/locales/en/translations.json)
  • email_notifications (packages/i18n/src/locales/en/translations.json)
  • stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified (packages/i18n/src/locales/en/translations.json)

No missing keys—no action required.

web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/page.tsx (1)

64-64:

✅ Verification successful

Verify all navigation paths have been updated to the new settings structure.

The URL path change from /${workspaceSlug}/projects/${projectId}/settings/features to /${workspaceSlug}/settings/projects/${projectId}/features aligns with the settings page revamp objectives. This creates a more logical hierarchy where all settings are grouped under a single /settings/ path.

Run the following script to ensure all old settings paths have been updated:


🏁 Script executed:

#!/bin/bash
# Description: Check for any remaining references to the old settings URL pattern

# Search for old URL pattern in TypeScript/JavaScript files
echo "Searching for old settings URL pattern..."
rg -t ts -t tsx -t js -t jsx '/projects/[^/]+/settings' --glob '!node_modules' -A 2 -B 2

# Also check for string literals or template literals with the old pattern
rg -t ts -t tsx -t js -t jsx 'projects.*settings.*features' --glob '!node_modules' -A 2 -B 2

Length of output: 334


🏁 Script executed:

#!/bin/bash
# Verifying no old settings paths remain—using file globs instead of unsupported type flags

echo "Searching for any references to the old nested settings path..."
rg -n "/projects/[^/]+/settings/features" -g "*.{ts,tsx,js,jsx}" --glob "!node_modules" -A2 -B2 || echo "No matches for old features URL."

echo
echo "Searching for any other references to the old settings path (excluding the new `/settings/projects`)…"
rg -n "/projects/[^/]+/settings" -g "*.{ts,tsx,js,jsx}" --glob "!node_modules" -A2 -B2 \
  | grep -v "/settings/projects" \
  || echo "No leftover old settings paths found."

Length of output: 832


No leftover old settings URLs—change approved.

I searched the codebase for both the old /projects/{workspaceSlug}/settings/features pattern and any remaining /projects/{workspaceSlug}/settings occurrences (excluding the new /settings/projects) and found none. The updated route
/${workspaceSlug}/settings/projects/${projectId}/features
is applied consistently.

web/app/(all)/[workspaceSlug]/(settings)/settings/account/sidebar.tsx (1)

58-58: 🛠️ Refactor suggestion

Add null check for avatar_url to prevent potential issues.

The current condition doesn't check for null, which could cause the avatar to display incorrectly if avatar_url is null.

-            {!currentUser?.avatar_url || currentUser?.avatar_url === "" ? (
+            {!currentUser?.avatar_url || currentUser?.avatar_url === "" || currentUser?.avatar_url === null ? (

Likely an incorrect or invalid review comment.

web/app/(all)/[workspaceSlug]/(settings)/settings/account/security/page.tsx (1)

71-98: Well-implemented password change handler with proper security measures.

The implementation correctly handles CSRF token retrieval, conditional old password requirement, and provides comprehensive error handling with localized messages.

web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/intake/page.tsx (1)

45-45:

✅ Verification successful

URL restructuring looks good - verify route consistency.

The URL path update from /${workspaceSlug}/projects/${projectId}/settings/features to /${workspaceSlug}/settings/projects/${projectId}/features aligns with the broader settings page restructuring effort described in the PR.

Please verify that:

  1. The new route structure is properly defined in the Next.js app directory
  2. All other references to the old URL pattern have been consistently updated

🏁 Script executed:

#!/bin/bash
# Description: Verify all references to the old URL pattern have been updated and new routes exist

# Check for any remaining references to the old URL pattern
rg -A 3 -B 3 "projects/.*?/settings/features" --type tsx --type ts

# Check for new route definitions matching the updated pattern
fd "settings" --type d | head -10

# Search for the new URL pattern to confirm consistent usage
rg -A 3 "settings/projects/.*?/features" --type tsx --type ts

Length of output: 651


I’ll re-run the checks using proper file globs for .ts/.tsx and list the route dirs under web/app:


🏁 Script executed:

#!/bin/bash
set -e

echo "=== Old URL references (projects/.../settings/features) ==="
rg -n "projects/.*/settings/features" -g "*.tsx" -g "*.ts" || echo "→ None found"

echo
echo "=== New URL references (settings/projects/.../features) ==="
rg -n "settings/projects/.*/features" -g "*.tsx" -g "*.ts" || echo "→ None found"

echo
echo "=== Feature route directories under web/app ==="
fd "features" -t d web/app | grep "/settings/projects/" || echo "→ None found"

Length of output: 1596


URL restructuring verified across the codebase

All references to the old path have been removed, and the new URL structure is consistently applied:

  • No occurrences of projects/.../settings/features remain.
  • All router.push calls in:
    • modules/(list)/page.tsx
    • views/(list)/page.tsx
    • intake/page.tsx
    • cycles/(list)/page.tsx
    • pages/(list)/page.tsx
      have been updated to /${workspaceSlug}/settings/projects/${projectId}/features.
  • The feature route directory exists at:
    web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/features/
    

Everything looks consistent—ready to merge.

web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx (1)

72-72: Consistent URL restructuring for cycles feature navigation.

The URL path update matches the pattern seen in other files, changing from /${workspaceSlug}/projects/${projectId}/settings/features to /${workspaceSlug}/settings/projects/${projectId}/features. This maintains consistency across the settings page restructuring effort.

The change correctly preserves the existing permission check (hasAdminLevelPermission) and user experience flow, only updating the navigation destination to align with the new URL hierarchy.

packages/i18n/src/locales/en/translations.json (3)

46-47: LGTM! Consistent terminology update.

The renaming from "Appearance" to "Preferences" is consistent with the broader scope of settings that now includes language and timezone options.


60-63: Well-structured user guidance.

The new translation keys provide clear explanations about the relocated settings and helpful navigation hints for users.


1455-1456: Consistent terminology update to industry standard.

The change from "API Tokens" to "Personal Access Tokens" aligns with industry standards and is more descriptive for end users. All related messages have been updated consistently.

Also applies to: 1462-1471, 1477-1478

web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/mobile-header-tabs.tsx (1)

21-37: Well-implemented permission-aware navigation tabs.

The tab rendering logic properly checks permissions and workspace context before rendering each link. The active state styling provides good visual feedback.

web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx (2)

32-35: Good defensive programming with null checks.

The component properly handles undefined types and missing icons, preventing runtime errors.


53-56: Clean permission-based filtering.

The category filtering logic clearly restricts FEATURES and DEVELOPER categories to admin users only, providing appropriate access control.

web/app/(all)/[workspaceSlug]/(settings)/layout.tsx (2)

9-23: Well-structured layout component with proper authentication flow!

The component correctly implements authentication and workspace authorization checks before rendering settings content. The responsive layout with flex containers is appropriate for a settings page.


18-18:

✅ Verification successful

Verify the custom Tailwind class md:py-page-y

The class md:py-page-y appears to be a custom spacing value. Ensure this is defined in your Tailwind configuration.


🏁 Script executed:

#!/bin/bash
# Description: Check if md:py-page-y is defined in Tailwind config

# Search for page-y definition in Tailwind config files
fd -e js -e ts -e json "tailwind.config" --exec grep -l "page-y"

# Also check for any CSS files that might define this custom value
rg -g "*.css" "page-y" -A 2

Length of output: 136


🏁 Script executed:

#!/bin/bash
# Inspect the definition of the custom spacing key `page-y` in the Tailwind config
rg -C3 "page-y" packages/tailwind-config/tailwind.config.js

Length of output: 202


Confirmed custom spacing page-y is defined

The Tailwind config at packages/tailwind-config/tailwind.config.js includes:

spacing: {
  // …
  "page-x": "1.35rem",
  "page-y": "1.35rem",
},

No further action required.

web/app/(all)/[workspaceSlug]/(settings)/settings/account/layout.tsx (1)

1-9: Component implementation follows good patterns

web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/members/page.tsx (1)

19-28: Excellent permission checking implementation!

The permission logic correctly checks for project-level permissions (admin/member) OR workspace admin permissions, following the principle of least privilege. The conditional rendering of NotAuthorizedView provides good UX for unauthorized users.

web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/exports/page.tsx (1)

45-45: ⚠️ Potential issue

Missing translation function call for header text.

The header text displays the translation key instead of the translated text. You need to wrap it with the t() function.

-          <h3 className="text-xl font-medium">{t("workspace_settings.settings.exports.title")}</h3>
+          <h3 className="text-xl font-medium">{t("workspace_settings.settings.exports.title")}</h3>

Wait, I see the t() function is already being used. The issue might be that the same translation key is used for both page title and header. Please verify if this is intentional or if a different key should be used for the header.

web/app/(all)/[workspaceSlug]/(settings)/settings/projects/page.tsx (1)

1-39: Major inconsistency between AI summary and actual implementation.

The AI summary describes this component as a comprehensive project settings management page with features like:

  • Permission checks using useUserPermissions
  • Project data fetching with useSWR
  • Archive/restore and delete modals
  • ProjectDetailsForm for editing

However, the actual implementation is a simple empty state component that only shows when there are no projects. This suggests either:

  1. The wrong file content was provided
  2. The AI summary is incorrect
  3. There's been a significant change in implementation

Please verify that the correct file content is being reviewed.

Likely an incorrect or invalid review comment.

web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/webhooks/page.tsx (1)

47-49: Good implementation of cleanup logic.

The useEffect hook properly cleans up the webhook secret key when the modal is closed, preventing potential security issues from lingering sensitive data.

web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/labels/page.tsx (1)

19-19:

❌ Incorrect review comment

Verify workspaceUserInfo availability

The code uses workspaceUserInfo in the permission check, but it's not destructured from the useUserPermissions hook on line 19. This could lead to a runtime error.

Also applies to: 44-46


🏁 Script executed:

#!/bin/bash
# Description: Check the return value of useUserPermissions hook to verify if workspaceUserInfo is available
# Test: Search for the useUserPermissions hook implementation
ast-grep --pattern 'export $_ function useUserPermissions($$$) {
  $$$
  return {
    $$$
  }
}'

# Also check for the hook definition with different patterns
rg -A 20 'useUserPermissions.*=' --glob '**/hooks/store/**'

Length of output: 684


🏁 Script executed:

#!/bin/bash
# Locate the IUserPermissionStore definition to see if workspaceUserInfo is part of its shape
rg -n "IUserPermissionStore" -A 5 -B 5

Length of output: 5597


🏁 Script executed:

#!/bin/bash
# Search for allowPermissions in the codebase
rg -n "allowPermissions" -A 5 -B 5

Length of output: 66169


Ignore workspaceUserInfo availability concern

The useUserPermissions hook’s return type (IUserPermissionStore) includes workspaceUserInfo, and you already destructure it on line 19:

const { workspaceUserInfo, allowPermissions } = useUserPermissions();

There is no missing import or destructuring—no action required.

Likely an incorrect or invalid review comment.

web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/billing/page.tsx (1)

14-24: Clean permission handling implementation

The permission checking logic is well-structured and follows a consistent pattern with proper workspace-level admin verification.

web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (1)

15-48: Well-structured permission-aware component!

The component follows best practices:

  • Proper permission checks before rendering sensitive content
  • Clean conditional rendering with early returns
  • Proper null checks before converting params to strings
  • Good use of internationalization
web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/members/page.tsx (1)

1-26: LGTM! Well-organized imports.

The imports are properly structured and grouped by type (external packages, types, UI components, helpers, hooks).

web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/page.tsx (2)

1-29: Well-structured workspace settings page component!

The implementation follows React and Next.js best practices with proper use of MobX observer pattern and internationalization support.


17-19:

✅ Verification successful

Verify the translation key exists in i18n configuration.

The translation key workspace_settings.page_label with interpolation parameter workspace should be defined in your translation files.


🏁 Script executed:

#!/bin/bash
# Description: Verify that the translation key 'workspace_settings.page_label' exists in translation files

# Search for the translation key in common translation file locations
echo "Searching for translation key 'workspace_settings.page_label'..."
fd -e json -e ts -e js | xargs rg -l "workspace_settings" | head -20

# Look for the specific key pattern
rg -A 2 -B 2 'workspace_settings.*page_label' --type-add 'trans:*.{json,ts,js}' -t trans

Length of output: 1366


🏁 Script executed:

#!/bin/bash
# Verify that workspace_settings.page_label exists in each locale translations.json
echo "Checking 'workspace_settings.page_label' across all locale files..."
for file in packages/i18n/src/locales/*/translations.json; do
  echo
  echo "File: $file"
  python3 - "$file" << 'EOF'
import json, sys
data = json.load(open(sys.argv[1]))
value = data.get("workspace_settings", {}).get("page_label")
print("→", value if value is not None else "<missing>")
EOF
done

Length of output: 5402


Translation key confirmed in all locales

Verified that workspace_settings.page_label is defined in every translations.json file under packages/i18n/src/locales/*/translations.json, each using the {workspace} placeholder correctly. No further action required.

@gakshita gakshita changed the title feat: settings page revamp [WEB-3998] feat: settings page revamp May 27, 2025
Copy link

makeplane bot commented May 27, 2025

Pull Request Linked with Plane Work Items

Comment Automatically Generated by Plane

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
web/app/(all)/[workspaceSlug]/(settings)/settings/account/activity/page.tsx (2)

36-47: 🛠️ Refactor suggestion

Performance concern: Multiple components rendered simultaneously.

The current implementation creates and renders all ProfileActivityListPage components at once, which can cause performance issues as the number of pages grows.

Consider implementing virtualization or lazy loading:

  • Use a single component that manages pagination internally
  • Implement windowing with libraries like react-window
  • Load pages only when needed rather than all at once
- const activityPages: JSX.Element[] = [];
- for (let i = 0; i < pageCount; i++)
-   activityPages.push(
-     <ProfileActivityListPage
-       key={i}
-       cursor={`${PER_PAGE}:${i}:0`}
-       perPage={PER_PAGE}
-       updateResultsCount={updateResultsCount}
-       updateTotalPages={updateTotalPages}
-       updateEmptyState={updateEmptyState}
-     />
-   );
+ // Render only the current page or implement virtualization
+ <ProfileActivityListPage
+   key={pageCount - 1}
+   cursor={`${PER_PAGE}:${pageCount - 1}:0`}
+   perPage={PER_PAGE}
+   updateResultsCount={updateResultsCount}
+   updateTotalPages={updateTotalPages}
+   updateEmptyState={updateEmptyState}
+ />

39-46: 🛠️ Refactor suggestion

Missing error handling for component failures.

The component doesn't handle potential errors when ProfileActivityListPage components fail to load data.

Implement error boundaries or error states to gracefully handle loading failures:

+ const [hasError, setHasError] = useState(false);
+ 
+ const handleError = () => setHasError(true);
+ 
+ if (hasError) {
+   return (
+     <div className="flex h-full w-full flex-col">
+       <ProfileSettingContentHeader title={t("activity")} />
+       <div className="text-center text-custom-text-400 mt-8">
+         {t("error_loading_activity")}
+       </div>
+     </div>
+   );
+ }

  <ProfileActivityListPage
    key={i}
    cursor={`${PER_PAGE}:${i}:0`}
    perPage={PER_PAGE}
    updateResultsCount={updateResultsCount}
    updateTotalPages={updateTotalPages}
    updateEmptyState={updateEmptyState}
+   onError={handleError}
  />
🧹 Nitpick comments (3)
web/core/components/settings/heading.tsx (2)

3-12: Well-designed Props interface with good flexibility.

The Props type definition is comprehensive and provides good flexibility:

  • title accepts both string and ReactNode for versatility
  • Optional props are properly typed
  • Button configuration is well-structured

Consider adding JSDoc comments to document the expected behavior of each prop.


14-27: Consider accessibility improvements for the heading structure.

The component implementation is solid, but consider these accessibility enhancements:

  1. Add proper heading hierarchy support
  2. Consider ARIA attributes for better screen reader support
  3. The conditional title rendering could benefit from consistent heading levels
export const SettingsHeading = ({ title, description, button, appendToRight, showButton = true }: Props) => (
- <div className="flex items-center justify-between border-b border-custom-border-100 pb-3.5">
+ <div className="flex items-center justify-between border-b border-custom-border-100 pb-3.5" role="banner">
    <div className="flex flex-col items-start gap-1">
-     {typeof title === "string" ? <h3 className="text-xl font-medium">{title}</h3> : title}
+     {typeof title === "string" ? <h3 className="text-xl font-medium" id="settings-heading">{title}</h3> : title}
-     {description && <div className="text-sm text-custom-text-300">{description}</div>}
+     {description && <div className="text-sm text-custom-text-300" aria-describedby="settings-heading">{description}</div>}
    </div>
web/core/components/settings/layout.tsx (1)

20-24: Consider simplifying the scroll condition logic.

The condition container.scrollHeight > container.clientHeight || scrolledRef.current might be clearer with better documentation or simplification.

Consider adding a comment to explain the logic:

  const handleScroll = () => {
    const scrollTop = container.scrollTop;
+   // Only update scroll state if content is scrollable or was previously scrolled
    if (container.scrollHeight > container.clientHeight || scrolledRef.current) {
      const _isScrolled = scrollTop > 0;
      toggleIsScrolled(_isScrolled);
    }
  };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b411d3c and 8bbed0a.

📒 Files selected for processing (25)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/(with-sidebar)/layout.tsx (0 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/(with-sidebar)/sidebar.tsx (0 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/header.tsx (0 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/settings/(with-sidebar)/layout.tsx (0 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/settings/(with-sidebar)/sidebar.tsx (0 hunks)
  • web/app/(all)/[workspaceSlug]/(projects)/settings/header.tsx (0 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/layout.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/exports/page.tsx (2 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/imports/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/layout.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/webhooks/page.tsx (3 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/activity/page.tsx (1 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx (3 hunks)
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx (2 hunks)
  • web/ce/components/workspace/billing/root.tsx (2 hunks)
  • web/core/components/estimates/empty-screen.tsx (2 hunks)
  • web/core/components/labels/project-setting-label-list.tsx (3 hunks)
  • web/core/components/project/member-list.tsx (2 hunks)
  • web/core/components/project/settings/features-list.tsx (2 hunks)
  • web/core/components/settings/content-wrapper.tsx (1 hunks)
  • web/core/components/settings/header.tsx (1 hunks)
  • web/core/components/settings/heading.tsx (1 hunks)
  • web/core/components/settings/index.ts (1 hunks)
  • web/core/components/settings/layout.tsx (1 hunks)
  • web/core/components/settings/sidebar/root.tsx (1 hunks)
💤 Files with no reviewable changes (6)
  • web/app/(all)/[workspaceSlug]/(projects)/settings/(with-sidebar)/layout.tsx
  • web/app/(all)/[workspaceSlug]/(projects)/settings/header.tsx
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/(with-sidebar)/sidebar.tsx
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/(with-sidebar)/layout.tsx
  • web/app/(all)/[workspaceSlug]/(projects)/settings/(with-sidebar)/sidebar.tsx
  • web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/header.tsx
✅ Files skipped from review due to trivial changes (4)
  • web/core/components/project/settings/features-list.tsx
  • web/ce/components/workspace/billing/root.tsx
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/webhooks/page.tsx
  • web/app/(all)/[workspaceSlug]/(settings)/settings/account/api-tokens/page.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
  • web/core/components/settings/index.ts
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/imports/page.tsx
  • web/app/(all)/[workspaceSlug]/(settings)/settings/projects/[projectId]/states/page.tsx
  • web/core/components/labels/project-setting-label-list.tsx
  • web/core/components/settings/content-wrapper.tsx
  • web/core/components/estimates/empty-screen.tsx
  • web/core/components/settings/header.tsx
  • web/app/(all)/[workspaceSlug]/(settings)/layout.tsx
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/exports/page.tsx
  • web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/layout.tsx
  • web/core/components/settings/sidebar/root.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
web/core/components/project/member-list.tsx (2)
web/core/components/settings/heading.tsx (1)
  • SettingsHeading (14-27)
packages/i18n/src/store/index.ts (1)
  • t (211-232)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (9)
web/core/components/project/member-list.tsx (2)

16-16: Good refactoring to use standardized component.

The import of SettingsHeading aligns with the broader effort to standardize header layouts across settings pages.


52-79: Excellent refactoring that maintains functionality while improving consistency.

The replacement of the manual header div with SettingsHeading is well-executed:

  • The title is properly passed using the translation function
  • The search input and add member button are preserved in the appendToRight prop
  • All styling, event handlers, and conditional logic remain intact
  • Admin permission check for the add button is maintained

This change improves UI consistency across the settings pages while preserving all existing functionality.

web/core/components/settings/heading.tsx (1)

29-29: Export pattern is appropriate.

Both named and default exports provide flexibility for different import styles across the codebase.

web/app/(all)/[workspaceSlug]/(settings)/settings/account/activity/page.tsx (2)

17-32: Good state management and callback structure.

The component properly manages pagination state and provides clear callback functions for child components to update parent state. The state structure is logical and the naming is clear.


51-64: Well-implemented empty state handling.

The empty state implementation provides good user experience with:

  • Proper conditional rendering
  • Localized messages
  • Consistent styling with the rest of the page
  • Appropriate use of DetailedEmptyState component
web/core/components/settings/layout.tsx (4)

6-12: Good use of MobX observer and proper hook integration.

The component is properly wrapped with observer for reactivity and correctly uses the useUserSettings hook to manage scroll state.


13-36: Excellent scroll event handling with proper optimization.

The implementation demonstrates good practices:

  • Throttling with 150ms delay to improve performance
  • Proper cleanup of event listeners and throttled functions
  • Guard clause for container existence
  • Scrollability check before updating state

The scroll logic correctly identifies when scrolling occurs and updates the global state accordingly.


38-40: Proper state synchronization between ref and store.

The effect correctly synchronizes the ref with the store state, ensuring consistency between local and global scroll state.


41-46: Clean and functional layout structure.

The layout provides a simple, scrollable container with proper styling classes. The full height and overflow-y-scroll are appropriate for a settings content area.

@sriramveeraghanta sriramveeraghanta merged commit 41c2aef into preview May 30, 2025
5 of 6 checks passed
@sriramveeraghanta sriramveeraghanta deleted the feat-settings-revamp branch May 30, 2025 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants