Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@TWilson023
Copy link
Collaborator

@TWilson023 TWilson023 commented Oct 7, 2025

Summary by CodeRabbit

  • New Features

    • Optional folder descriptions in create/edit flows and folder displays.
    • New Edit Folder panel to manage folder details, access, and members.
    • Side panel on the Links page showing folder info and controls.
  • Improvements

    • Retain previous folder data during refreshes for smoother UX.
    • Streamlined page header/layout with side-panel toggle.
    • Refined Folder actions menu and updated keyboard shortcuts.
    • Character-count display for descriptions and minor toolbar/drawer z-index tweaks.
  • Revert/Removal

    • Old Folder Permissions panel removed.

@vercel
Copy link
Contributor

vercel bot commented Oct 7, 2025

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

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Oct 7, 2025 7:59pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 7, 2025

Warning

Rate limit exceeded

@steven-tey has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 37 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between f54b0e9 and 43d9a0e.

📒 Files selected for processing (1)
  • apps/web/tests/folders/index.test.ts (1 hunks)

Walkthrough

Adds an optional folder description across DB schema, API, types, validation, and UI. Introduces folder info/edit panels, replaces the old permissions panel, adds a side-panel page layout and header refactor, a new useFolderUsers SWR hook, and wires folders UI to include description fields.

Changes

Cohort / File(s) Summary of changes
Database Schema
packages/prisma/schema/folder.prisma
Add optional description: String? (@db.Text) to Folder model.
API: Folders
apps/web/app/api/folders/route.ts, apps/web/app/api/folders/[folderId]/route.ts
Parse description on create (POST) and update (PATCH); include in DB payloads.
Folder Data Access & Types
apps/web/lib/folder/get-folder-or-throw.ts, apps/web/lib/folder/get-folders.ts, apps/web/lib/types.ts, apps/web/lib/folder/constants.ts
Select and return description; add description to exported FolderSummary and unsortedLinks.
Validation Schemas
apps/web/lib/zod/schemas/folders.ts
Add nullable description to schemas; export FOLDER_MAX_DESCRIPTION_LENGTH = 500.
SWR Hooks
apps/web/lib/swr/use-folder-users.ts
New useFolderUsers hook to fetch folder users with capability gating; uses SWR with keepPreviousData and revalidateOnFocus:false.
Folders UI: Add/Edit Forms
apps/web/ui/folders/add-folder-form.tsx, apps/web/ui/folders/edit-folder-form.tsx
Add description input to add/edit flows; enforce max length and show live counters; new EditFolderForm component that PATCHes folder with description.
Folders UI: Panels & Actions
apps/web/ui/folders/edit-folder-panel.tsx, apps/web/ui/folders/folder-info-panel.tsx, apps/web/ui/folders/folder-actions.tsx
Add EditFolderPanel & hook, FolderInfoPanel and controls; refactor FolderActions to open edit panel, adjust action set, add className? prop and update keyboard shortcuts.
Removed: Permissions Panel
apps/web/ui/folders/folder-permissions-panel.tsx
Delete old FolderPermissionsPanel, related components, and useFolderPermissionsPanel export.
Layout: Page Content & Side Panel
apps/web/ui/layout/page-content/index.tsx, .../page-content-header.tsx, .../page-content-with-side-panel.tsx, .../toggle-side-panel-button.tsx
Simplify PageContent to delegate to PageContentHeader; add PageContentHeader type/component; add PageContentWithSidePanel + context and ToggleSidePanelButton.
Pages: Links
apps/web/app/app.dub.co/(dashboard)/[slug]/links/page.tsx, .../links/page-client.tsx, .../links/folders/page-client.tsx
Use PageContentWithSidePanel; add FolderDropdown and FolderInfoPanel; update useFolders call to keepPreviousData and remove loading return.
Misc UI & Small Tweaks
apps/web/ui/links/links-toolbar.tsx, apps/web/ui/shared/max-characters-counter.tsx, packages/ui/src/sheet.tsx
Minor style/z-index adjustments (toolbar z-10, tabular-nums on counter, Drawer overlay/content z-index changes).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as Client UI (Create/Edit Folder)
  participant API as API /api/folders
  participant DB as Prisma DB

  rect rgb(245,248,255)
  note right of UI: Create folder with optional description
  UI->>API: POST /api/folders { name, description?, accessLevel, ... }
  API->>DB: prisma.folder.create({ data: { name, description, ... } })
  DB-->>API: Folder { id, name, description, ... }
  API-->>UI: 200 OK { folder }
  end

  rect rgb(245,255,245)
  note right of UI: Update folder name/description
  UI->>API: PATCH /api/folders/{id}?workspaceId=... { name?, description? }
  API->>DB: prisma.folder.update({ where:{id}, data:{ name, description } })
  DB-->>API: Updated folder
  API-->>UI: 200 OK
  UI-->>SWR: mutate(/api/folders*)
  end
Loading
sequenceDiagram
  autonumber
  participant Page as Links Page
  participant Layout as PageContentWithSidePanel
  participant Panel as FolderInfoPanel / EditFolderPanel
  participant SWR as useFolderUsers
  participant API as API (folders/users)

  Page->>Layout: Render (folderId?)
  alt folderId present
    Layout->>Panel: Mount side panel
    Panel->>SWR: useFolderUsers(enabled=true)
    SWR->>API: GET /api/folders/{id}/users?workspaceId=...
    API-->>SWR: FolderUser[]
    SWR-->>Panel: users, isLoading/isValidating
  else no folderId
    Layout-->>Page: content only
  end

  Note over Layout,Panel: Panel visibility toggled via context ToggleSidePanelButton
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Partner profile updates #2872 — Touches apps/web/ui/shared/max-characters-counter.tsx; likely related styling/change to character counter rendering.

Suggested reviewers

  • devkiran

Poem

A nibble of notes, a flourish of prose,
I tucked descriptions like carrots in rows.
Side panels swing and toggles click quick —
I hop through headers and refactors slick.
Users and edits now snug in the burrow. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “Folder updates” is overly generic and does not convey the primary changes in the pull request, such as the addition of a description field to the folder model and its propagation through the API, database schema, and UI components. It refers broadly to folder-related work but lacks specificity about the core enhancements and refactorings introduced. As a result, teammates scanning the PR history may not understand the main intent without reading the details. Please choose a more descriptive title that highlights the main changes, for example “Add folder description support and update folder UI panels.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

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

❤️ Share

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

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

🧹 Nitpick comments (6)
apps/web/lib/swr/use-folder-users.ts (1)

7-45: LGTM! Well-structured SWR hook for fetching folder users.

The hook correctly gates the fetch based on permissions, workspace ID, and folder ID. The SWR configuration with revalidateOnFocus: false and keepPreviousData: true is appropriate for this use case.

Optional: Extract "unsorted" magic string.

Consider extracting the hardcoded "unsorted" string on Line 29 into a constant for better maintainability.

+const UNSORTED_FOLDER_ID = "unsorted";
+
 export function useFolderUsers(
   {
     folderId,
     enabled = true,
   }: {
     folderId?: string | null;
     enabled?: boolean;
   },
   swrOpts?: SWRConfiguration,
 ) {
   const { id: workspaceId, plan } = useWorkspace();
   const { canManageFolderPermissions } = getPlanCapabilities(plan);

   const {
     data: users,
     isValidating,
     isLoading,
   } = useSWR<FolderUser[]>(
     enabled &&
       workspaceId &&
       canManageFolderPermissions &&
       folderId &&
-      folderId !== "unsorted"
+      folderId !== UNSORTED_FOLDER_ID
       ? `/api/folders/${folderId}/users?workspaceId=${workspaceId}`
       : undefined,
     fetcher,
     {
       revalidateOnFocus: false,
       keepPreviousData: true,
       ...swrOpts,
     },
   );

   return {
     users,
     isValidating,
     isLoading,
   };
 }
apps/web/ui/folders/add-folder-form.tsx (1)

128-149: Description field well-implemented with character counter.

The optional description textarea includes:

  • Clear labeling with "(optional)" indicator
  • Live character counter showing current length vs. max (500)
  • Keyboard shortcut (Ctrl/Cmd+Enter) to advance to next step

The .toString() call on line 146 is unnecessary for strings but harmless. Client-side validation doesn't prevent exceeding the max length, but the backend schema validation will catch this.

Consider this minor simplification if desired:

-                    {description?.toString().length || 0}/
+                    {description?.length || 0}/
apps/web/ui/folders/edit-folder-form.tsx (1)

72-84: Enforce description length on the client.

Add maxLength and validation to improve UX and match schema limits.

Apply this diff:

-        <textarea
-          className="mt-2 block w-full rounded-md border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
-          {...register("description")}
-        />
+        <textarea
+          className="mt-2 block w-full rounded-md border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
+          maxLength={FOLDER_MAX_DESCRIPTION_LENGTH}
+          {...register("description", {
+            maxLength: FOLDER_MAX_DESCRIPTION_LENGTH,
+          })}
+        />
apps/web/ui/folders/edit-folder-panel.tsx (3)

61-88: Harden workspace access update with try/catch.

Network errors currently fall through. Wrap fetch in try/catch and show a toast on failure.

Apply this diff:

-  const updateWorkspaceAccessLevel = async (accessLevel: string) => {
-    setIsUpdating(true);
-    setWorkspaceAccessLevel(accessLevel);
-
-    const response = await fetch(
-      `/api/folders/${folder.id}?workspaceId=${workspaceId}`,
-      {
-        method: "PATCH",
-        headers: { "Content-Type": "application/json" },
-        body: JSON.stringify({
-          accessLevel: accessLevel === "" ? null : accessLevel,
-        }),
-      },
-    );
-
-    setIsUpdating(false);
-
-    if (!response.ok) {
-      const { error } = await response.json();
-      toast.error(error.message);
-      return;
-    }
-
-    toast.success("Workspace access updated!");
-    await mutate(
-      (key) => typeof key === "string" && key.startsWith(`/api/folders`),
-    );
-  };
+  const updateWorkspaceAccessLevel = async (accessLevel: string) => {
+    setIsUpdating(true);
+    setWorkspaceAccessLevel(accessLevel);
+    try {
+      const response = await fetch(
+        `/api/folders/${folder.id}?workspaceId=${workspaceId}`,
+        {
+          method: "PATCH",
+          headers: { "Content-Type": "application/json" },
+          body: JSON.stringify({
+            accessLevel: accessLevel === "" ? null : accessLevel,
+          }),
+        },
+      );
+      if (!response.ok) {
+        const { error } = await response.json();
+        throw new Error(error?.message || "Failed to update access.");
+      }
+      toast.success("Workspace access updated!");
+      await mutate(
+        (key) => typeof key === "string" && key.startsWith(`/api/folders`),
+      );
+    } catch (e: any) {
+      toast.error(e.message || "Network error. Please try again.");
+    } finally {
+      setIsUpdating(false);
+    }
+  };

268-292: Revert role on error to avoid stale optimistic UI.

You optimistically set the new role but don’t revert on failure; UI can drift until revalidation.

Apply this diff:

-          onChange={(e) => {
+          onChange={async (e) => {
             if (!folder || !workspaceId) {
               return;
             }
 
-            const role = (e.target.value as FolderUserRole) || null;
-
-            executeAsync({
-              workspaceId,
-              folderId: folder.id,
-              userId: user.id,
-              role,
-            });
-
-            setRole(role);
+            const newRole = (e.target.value as FolderUserRole) || null;
+            const prevRole = role;
+            setRole(newRole);
+            try {
+              await executeAsync({
+                workspaceId,
+                folderId: folder.id,
+                userId: user.id,
+                role: newRole,
+              });
+            } catch {
+              setRole(prevRole);
+            }
           }}

196-201: Tiny nit: remove dead || false.

isUsersValidating || isUsersLoading || false can be simplified.

Apply this diff:

-                {isUsersValidating || isUsersLoading || false
+                {isUsersValidating || isUsersLoading
                   ? [...Array(3)].map((_, i) => (
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f969af and 9c861d6.

📒 Files selected for processing (23)
  • apps/web/app/api/folders/[folderId]/route.ts (2 hunks)
  • apps/web/app/api/folders/route.ts (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/links/folders/page-client.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/links/page-client.tsx (4 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/links/page.tsx (1 hunks)
  • apps/web/lib/folder/get-folder-or-throw.ts (2 hunks)
  • apps/web/lib/folder/get-folders.ts (1 hunks)
  • apps/web/lib/swr/use-folder-users.ts (1 hunks)
  • apps/web/lib/types.ts (1 hunks)
  • apps/web/lib/zod/schemas/folders.ts (1 hunks)
  • apps/web/ui/folders/add-folder-form.tsx (4 hunks)
  • apps/web/ui/folders/edit-folder-form.tsx (1 hunks)
  • apps/web/ui/folders/edit-folder-panel.tsx (1 hunks)
  • apps/web/ui/folders/folder-actions.tsx (5 hunks)
  • apps/web/ui/folders/folder-info-panel.tsx (1 hunks)
  • apps/web/ui/folders/folder-permissions-panel.tsx (0 hunks)
  • apps/web/ui/layout/page-content/index.tsx (1 hunks)
  • apps/web/ui/layout/page-content/page-content-header.tsx (1 hunks)
  • apps/web/ui/layout/page-content/page-content-with-side-panel.tsx (1 hunks)
  • apps/web/ui/layout/page-content/toggle-side-panel-button.tsx (1 hunks)
  • apps/web/ui/links/links-toolbar.tsx (1 hunks)
  • apps/web/ui/shared/max-characters-counter.tsx (1 hunks)
  • packages/prisma/schema/folder.prisma (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/ui/folders/folder-permissions-panel.tsx
🧰 Additional context used
🧬 Code graph analysis (13)
apps/web/lib/zod/schemas/folders.ts (1)
packages/prisma/client.ts (1)
  • FolderType (11-11)
apps/web/ui/folders/add-folder-form.tsx (1)
apps/web/lib/zod/schemas/folders.ts (1)
  • FOLDER_MAX_DESCRIPTION_LENGTH (35-35)
apps/web/ui/layout/page-content/page-content-with-side-panel.tsx (2)
apps/web/ui/layout/page-content/page-content-header.tsx (2)
  • PageContentHeaderProps (9-15)
  • PageContentHeader (17-78)
apps/web/ui/layout/page-content/toggle-side-panel-button.tsx (1)
  • ToggleSidePanelButton (4-66)
apps/web/ui/folders/folder-info-panel.tsx (4)
apps/web/lib/swr/use-current-folder-id.ts (1)
  • useCurrentFolderId (6-16)
apps/web/lib/swr/use-folder-users.ts (1)
  • useFolderUsers (7-45)
apps/web/lib/types.ts (1)
  • FolderUser (530-532)
apps/web/ui/folders/folder-actions.tsx (1)
  • FolderActions (27-227)
apps/web/ui/folders/edit-folder-panel.tsx (5)
apps/web/lib/types.ts (2)
  • Folder (524-524)
  • FolderUser (530-532)
apps/web/lib/plan-capabilities.ts (1)
  • getPlanCapabilities (4-20)
apps/web/lib/swr/use-folder-users.ts (1)
  • useFolderUsers (7-45)
apps/web/ui/folders/edit-folder-form.tsx (1)
  • EditFolderForm (14-96)
apps/web/ui/shared/animated-empty-state.tsx (1)
  • AnimatedEmptyState (8-81)
apps/web/app/api/folders/route.ts (1)
apps/web/lib/zod/schemas/folders.ts (1)
  • createFolderSchema (37-45)
apps/web/ui/folders/folder-actions.tsx (2)
apps/web/lib/types.ts (1)
  • FolderSummary (539-542)
apps/web/ui/folders/edit-folder-panel.tsx (1)
  • useEditFolderPanel (321-336)
apps/web/ui/folders/edit-folder-form.tsx (3)
apps/web/lib/types.ts (1)
  • Folder (524-524)
apps/web/lib/zod/schemas/folders.ts (2)
  • updateFolderSchema (58-58)
  • FOLDER_MAX_DESCRIPTION_LENGTH (35-35)
apps/web/ui/shared/max-characters-counter.tsx (1)
  • MaxCharactersCounter (4-22)
apps/web/lib/swr/use-folder-users.ts (2)
apps/web/lib/plan-capabilities.ts (1)
  • getPlanCapabilities (4-20)
apps/web/lib/types.ts (1)
  • FolderUser (530-532)
apps/web/app/app.dub.co/(dashboard)/[slug]/links/page.tsx (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/links/page-client.tsx (1)
  • WorkspaceLinksClient (45-81)
apps/web/app/api/folders/[folderId]/route.ts (1)
apps/web/lib/zod/schemas/folders.ts (1)
  • updateFolderSchema (58-58)
apps/web/ui/layout/page-content/index.tsx (1)
apps/web/ui/layout/page-content/page-content-header.tsx (2)
  • PageContentHeaderProps (9-15)
  • PageContentHeader (17-78)
apps/web/app/app.dub.co/(dashboard)/[slug]/links/page-client.tsx (5)
apps/web/lib/swr/use-current-folder-id.ts (1)
  • useCurrentFolderId (6-16)
apps/web/ui/layout/page-content/page-content-with-side-panel.tsx (1)
  • PageContentWithSidePanel (27-117)
apps/web/ui/folders/folder-dropdown.tsx (1)
  • FolderDropdown (35-289)
apps/web/ui/folders/folder-info-panel.tsx (2)
  • FolderInfoPanel (11-96)
  • FolderInfoPanelControls (137-144)
apps/web/ui/modals/link-builder/index.tsx (1)
  • CreateLinkButton (259-314)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (24)
apps/web/ui/links/links-toolbar.tsx (1)

209-209: LGTM! Z-index adjustment supports the new side-panel layout.

The addition of z-10 ensures the bottom toolbar renders above the new side-panel components introduced in this PR.

packages/prisma/schema/folder.prisma (1)

20-20: LGTM! Folder description field added to schema.

The optional description field correctly extends the Folder model and aligns with the API, type definitions, and UI changes throughout this PR.

apps/web/ui/layout/page-content/page-content-header.tsx (1)

9-78: LGTM! Well-structured header component with proper responsive design.

The PageContentHeader component correctly handles optional props, performs proper type narrowing for titleInfo (Lines 26-38), and uses responsive classes appropriately. The component successfully abstracts header rendering logic from the main PageContent component.

apps/web/app/api/folders/[folderId]/route.ts (1)

45-45: LGTM! Description field added to PATCH handler.

The description field is correctly parsed via updateFolderSchema and included in the update payload (Line 79), aligning with the Prisma schema changes.

apps/web/lib/folder/get-folders.ts (1)

56-56: LGTM! Description field added to folder query projection.

The description field is correctly included in the Prisma select clause, ensuring it's returned with folder data.

apps/web/ui/shared/max-characters-counter.tsx (1)

18-18: LGTM! Nice UI improvement with tabular numbers.

Adding tabular-nums ensures consistent width for the character count display, preventing layout shifts as the count changes. This is especially useful for the folder description character counter.

apps/web/lib/zod/schemas/folders.ts (2)

28-28: LGTM! Description field added to FolderSchema.

The nullable description field correctly aligns with the Prisma schema definition.


35-43: LGTM! Description validation schema added.

The FOLDER_MAX_DESCRIPTION_LENGTH constant (500 characters) and the description field in createFolderSchema correctly enforce the maximum length constraint while allowing the field to be optional via nullish(). This aligns with the Prisma schema and provides proper validation for the API endpoints.

apps/web/app/app.dub.co/(dashboard)/[slug]/links/page.tsx (1)

1-5: LGTM! Clean separation of concerns.

The refactor simplifies this file by delegating layout composition to WorkspaceLinksClient. The client component properly handles the PageContentWithSidePanel, FolderDropdown, and related controls as shown in the relevant code snippet.

apps/web/lib/folder/get-folder-or-throw.ts (1)

20-20: LGTM! Description field properly integrated.

The description field is correctly added to both the Prisma select clause and the returned object, aligning with the schema changes for folder description support.

Also applies to: 52-52

apps/web/app/api/folders/route.ts (1)

51-53: LGTM! Description field correctly integrated into folder creation.

The description is properly parsed from the request body via createFolderSchema (which validates max length) and included in the Prisma folder creation payload. Validation and error handling remain intact.

Also applies to: 90-90

apps/web/lib/types.ts (1)

539-542: LGTM! Type definition updated for folder description.

The FolderSummary type now includes description, aligning with the Prisma schema changes and enabling downstream consumers to access folder.description.

apps/web/app/app.dub.co/(dashboard)/[slug]/links/folders/page-client.tsx (2)

26-31: LGTM! Improved loading UX with data retention.

Adding keepPreviousData: true prevents the previous folder list from disappearing during refetch, providing a smoother user experience. The switch from loading to isValidating is also appropriate for the search box loading indicator.


61-65: LGTM! Correct loading check.

Using !folders to determine when to show placeholders is correct, as folders is undefined during the initial load. This aligns with the removal of the loading flag from the hook return value.

apps/web/ui/folders/add-folder-form.tsx (2)

30-30: LGTM! Description field properly integrated.

The description state and API payload integration are correct. The field is appropriately optional (nullable) and aligns with the schema validation on the backend.

Also applies to: 42-42


103-126: LGTM! Improved name input structure.

The name input has been refactored with explicit labeling and keyboard navigation (Enter to advance), improving accessibility and user experience.

apps/web/ui/layout/page-content/toggle-side-panel-button.tsx (1)

4-66: LGTM! Well-implemented toggle button with proper SVG transforms.

The component correctly handles:

  • Rotation based on side prop (180° for left-side panels)
  • Horizontal flip of the chevron based on isOpen state
  • Proper SVG transform properties (vector-effect:non-scaling-stroke, transform-box:fill-box, transform-origin:center) for consistent rendering across transforms
apps/web/ui/layout/page-content/page-content-with-side-panel.tsx (3)

19-25: LGTM! Context for side panel state management.

The PageContentWithSidePanelContext provides a clean way for nested components to access and control the side panel's open/close state.


51-80: LGTM! Well-structured responsive layout.

The layout implementation:

  • Uses CSS Grid for main content and side panel positioning
  • Integrates PageContentHeader with optional toggle button in controls
  • Properly wraps children with consistent padding and background

83-112: LGTM! Responsive side panel with smooth transitions.

The side panel implementation features:

  • Container query-based responsive behavior (@[960px]/page-content)
  • Smooth width transitions when opening/closing
  • Absolute positioning on small screens (overlay), relative on larger screens (inline)
  • Close button (X) conditionally shown only on smaller screens where the panel overlays content
  • Proper scrolling for panel content
apps/web/app/app.dub.co/(dashboard)/[slug]/links/page-client.tsx (2)

58-79: Side panel integration looks solid.

Conditional side panel, header toggle, and FolderInfoPanel wiring are correct. No issues.


90-91: Minor UI polish accepted.

CreateLinkButton height tweak is fine.

apps/web/ui/folders/folder-actions.tsx (2)

124-171: Edit/Analytics/Copy actions wiring LGTM.

Popover actions and keyboard shortcuts behave as expected; edit panel hook usage is correct.


215-223: className passthrough on trigger button LGTM.

Propagating className improves styling flexibility.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/ui/src/sheet.tsx (1)

19-38: Confirm sheet z-index relative to existing overlays.

Popovers and modals in the codebase use z-50, while the sheet’s overlay and content are set at z-40—verify this ordering matches the intended hierarchy. Consider centralizing z-index values in your Tailwind config or via CSS variables for consistency.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c861d6 and 9a3df65.

📒 Files selected for processing (3)
  • apps/web/ui/folders/add-folder-form.tsx (4 hunks)
  • apps/web/ui/folders/edit-folder-form.tsx (1 hunks)
  • packages/ui/src/sheet.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/ui/folders/edit-folder-form.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/ui/folders/add-folder-form.tsx (1)
apps/web/lib/zod/schemas/folders.ts (1)
  • FOLDER_MAX_DESCRIPTION_LENGTH (35-35)
🔇 Additional comments (1)
apps/web/ui/folders/add-folder-form.tsx (1)

38-45: Verify the inconsistent handling of optional fields in the request payload.

The description field is always included in the request body (even when undefined), while accessLevel uses a spread operator to be conditionally included only when truthy. This inconsistency could lead to confusion or unexpected API behavior.

Consider using the same pattern for both optional fields:

 const response = await fetch(`/api/folders?workspaceId=${workspace.id}`, {
   method: "POST",
   body: JSON.stringify({
     name,
-    description,
+    ...(description && { description }),
     ...(accessLevel && { accessLevel }),
   }),
 });

Alternatively, if the API expects description: undefined explicitly, document this requirement or verify that both fields follow the intended API contract.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/ui/folders/folder-info-panel.tsx (1)

20-23: Use property shorthand.

The folderId property can use object property shorthand syntax.

Apply this diff:

 const { users, isLoading: isLoadingUsers } = useFolderUsers({
-  folderId: folderId,
+  folderId,
   enabled: !!folder,
 });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a3df65 and 4ff2279.

📒 Files selected for processing (4)
  • apps/web/lib/folder/constants.ts (1 hunks)
  • apps/web/ui/folders/add-folder-form.tsx (4 hunks)
  • apps/web/ui/folders/folder-actions.tsx (5 hunks)
  • apps/web/ui/folders/folder-info-panel.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
apps/web/ui/folders/folder-info-panel.tsx (5)
apps/web/lib/swr/use-current-folder-id.ts (1)
  • useCurrentFolderId (6-16)
apps/web/lib/swr/use-folder-users.ts (1)
  • useFolderUsers (7-45)
apps/web/lib/folder/constants.ts (1)
  • FOLDER_WORKSPACE_ACCESS (4-7)
apps/web/lib/types.ts (1)
  • FolderUser (530-532)
apps/web/ui/folders/folder-actions.tsx (1)
  • FolderActions (27-226)
apps/web/ui/folders/add-folder-form.tsx (1)
apps/web/lib/zod/schemas/folders.ts (1)
  • FOLDER_MAX_DESCRIPTION_LENGTH (35-35)
apps/web/ui/folders/folder-actions.tsx (3)
apps/web/lib/types.ts (1)
  • FolderSummary (539-542)
apps/web/ui/folders/edit-folder-panel.tsx (1)
  • useEditFolderPanel (321-336)
apps/web/lib/folder/constants.ts (1)
  • unsortedLinks (44-49)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
apps/web/lib/folder/constants.ts (1)

47-47: LGTM!

The description field addition aligns with the updated FolderSummary type and provides a clear description for the unsorted links folder.

apps/web/ui/folders/add-folder-form.tsx (2)

30-30: LGTM!

The description field is correctly added to the component state and included in the folder creation payload.

Also applies to: 42-42


128-148: LGTM!

The description textarea implementation is well-structured with proper character counting and keyboard shortcuts (Ctrl/Cmd+Enter to proceed).

apps/web/ui/folders/folder-info-panel.tsx (1)

32-34: LGTM!

The conditional rendering logic for Owner/Editor/Viewer sections correctly handles loading states and plan-gating. Sections are shown when loading or when users exist, preventing unnecessary skeleton displays when the feature is unavailable.

Also applies to: 70-95

apps/web/ui/folders/folder-actions.tsx (2)

53-56: LGTM!

The integration of the edit folder panel is well-structured. The Edit action correctly opens the panel and is appropriately gated for unsorted links.

Also applies to: 124-137


30-34: LGTM!

The className prop is properly added to the component signature and correctly applied to the root button, enabling flexible styling from parent components.

Also applies to: 218-218

@steven-tey steven-tey merged commit 8a2bab6 into main Oct 7, 2025
7 of 8 checks passed
@steven-tey steven-tey deleted the folder-updates branch October 7, 2025 20:02
@coderabbitai coderabbitai bot mentioned this pull request Dec 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants