-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Folder updates #2942
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
Folder updates #2942
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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 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. 📒 Files selected for processing (1)
WalkthroughAdds 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
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. Comment |
There was a problem hiding this 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: falseandkeepPreviousData: trueis 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
maxLengthand 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 || falsecan 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
📒 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-10ensures 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
descriptionfield 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
PageContentHeadercomponent correctly handles optional props, performs proper type narrowing fortitleInfo(Lines 26-38), and uses responsive classes appropriately. The component successfully abstracts header rendering logic from the mainPageContentcomponent.apps/web/app/api/folders/[folderId]/route.ts (1)
45-45: LGTM! Description field added to PATCH handler.The
descriptionfield is correctly parsed viaupdateFolderSchemaand 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
descriptionfield 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-numsensures 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
descriptionfield correctly aligns with the Prisma schema definition.
35-43: LGTM! Description validation schema added.The
FOLDER_MAX_DESCRIPTION_LENGTHconstant (500 characters) and thedescriptionfield increateFolderSchemacorrectly enforce the maximum length constraint while allowing the field to be optional vianullish(). 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 thePageContentWithSidePanel,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
descriptionfield 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
descriptionis properly parsed from the request body viacreateFolderSchema(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
FolderSummarytype now includesdescription, aligning with the Prisma schema changes and enabling downstream consumers to accessfolder.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: trueprevents the previous folder list from disappearing during refetch, providing a smoother user experience. The switch fromloadingtoisValidatingis also appropriate for the search box loading indicator.
61-65: LGTM! Correct loading check.Using
!foldersto determine when to show placeholders is correct, asfoldersisundefinedduring the initial load. This aligns with the removal of theloadingflag from the hook return value.apps/web/ui/folders/add-folder-form.tsx (2)
30-30: LGTM! Description field properly integrated.The
descriptionstate 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
sideprop (180° for left-side panels)- Horizontal flip of the chevron based on
isOpenstate- Proper SVG transform properties (
vector-effect:non-scaling-stroke,transform-box:fill-box,transform-origin:center) for consistent rendering across transformsapps/web/ui/layout/page-content/page-content-with-side-panel.tsx (3)
19-25: LGTM! Context for side panel state management.The
PageContentWithSidePanelContextprovides 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
PageContentHeaderwith 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
classNameimproves styling flexibility.
There was a problem hiding this 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
📒 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
descriptionfield is always included in the request body (even whenundefined), whileaccessLeveluses 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: undefinedexplicitly, document this requirement or verify that both fields follow the intended API contract.
There was a problem hiding this 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
folderIdproperty 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
📒 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
FolderSummarytype 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
classNameprop 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
Summary by CodeRabbit
New Features
Improvements
Revert/Removal