Skip to content

Conversation

@Enejivk
Copy link
Contributor

@Enejivk Enejivk commented Oct 4, 2025

Summary

Implements comprehensive video folder management functionality with proper validation and space-aware operations.

Changes

  • ✅ Added moveVideosToFolder function with proper validation
  • ✅ Added space-aware folder operations with isNull checks
  • ✅ Implemented proper ownership and permission checks
  • ✅ Added validation for space context and folder access

Features

  • Move videos between folders within organization context
  • Support for space-specific folder operations with proper scoping
  • Validation of video ownership and folder permissions
  • Proper error handling for edge cases and invalid moves
  • Space isolation (prevents moving to space folders from org context)

Summary by CodeRabbit

  • New Features
    • Move selected videos to a folder from the selection bar via a new “Move to Folder” action.
    • Folder selection dialog with hierarchical tree, expand/collapse, video counts, and option to choose root or specific folder.
    • Supports organization- and space-scoped moves with validation and clear success/error toasts.
    • Updates UI counts and selections after moves; preserves existing delete flow with confirmation.
    • Improved loading and error states when fetching folders and executing moves.

- Add validation to ensure all requested videos exist in the space before updating
- Prevent silent partial updates when some videos are missing from space_videos
- Throw descriptive error when videos are not found in the specified space
- Improves data integrity and prevents misleading success responses
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 4, 2025

Walkthrough

Adds folder retrieval and video move-to-folder capabilities. Introduces server actions for fetching all folders and moving videos, expands folder library with root-aware tree building and move logic, updates SelectedCapsBar to open a folder selection dialog, and implements a new FolderSelectionDialog component to browse folders and trigger moves.

Changes

Cohort / File(s) Summary
Server Actions
apps/web/actions/folders/getAllFolders.ts, apps/web/actions/folders/move-videos-to-folder.ts
New Effect-based server actions: getAllFoldersAction authenticates user, resolves root (user/space/org), returns folder tree; moveVideosToFolderAction validates auth/root, executes moveVideosToFolder, and revalidates relevant dashboard paths; structured success/error returns.
Folder Library
apps/web/lib/folder.ts
Adds getAllFolders(root) to build hierarchical folder tree; adds moveVideosToFolder(videoIds, targetFolderId, root?) with ownership/visibility checks and per-folder videoCount deltas; updates queries (child folders, videos) to be root-aware; imports inArray/isNull; minor formatting for existing helpers.
Dashboard Components
apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx, apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
SelectedCapsBar supports moving selected items via new moveSelectedCaps prop and Move-to-Folder button; introduces FolderSelectionDialog component to fetch/display folder tree, select destination (including root), execute move mutation, update caches/UI, and handle errors.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Bar as SelectedCapsBar
  participant Dialog as FolderSelectionDialog
  participant SA1 as getAllFoldersAction (server)
  participant Lib1 as folder.getAllFolders
  participant DB as Database

  User->>Bar: Click "Move to Folder"
  Bar->>Dialog: open=true
  Dialog->>SA1: fetch folders (root: org/space)
  SA1->>Lib1: getAllFolders(root)
  Lib1->>DB: query folders (root-aware)
  DB-->>Lib1: rows
  Lib1-->>SA1: folder tree
  SA1-->>Dialog: { success, folders }
  Dialog->>User: Show tree, counts

  User->>Dialog: Select target, confirm
  participant SA2 as moveVideosToFolderAction (server)
  participant Lib2 as folder.moveVideosToFolder
  Dialog->>SA2: { videoIds, targetFolderId, spaceId? }
  SA2->>Lib2: moveVideosToFolder(...)
  Lib2->>DB: validate + update folders/videos
  DB-->>Lib2: ok
  Lib2-->>SA2: { movedCount, deltas, originalFolderIds, targetFolderId }
  SA2-->>Dialog: success payload
  SA2->>SA2: revalidate dashboard paths
  Dialog->>Bar: onConfirm(folderId)
  Dialog->>Dialog: update local caches / close
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

A rabbit stacks clips like carrots in rows,
Hop to the folders where each video goes.
Click, pick, and move with a tidy delight—
Tree leaves expand in the dashboard light.
Thump-thump! Deltas tallied, caches renew—
Burrow now neat, with a fresh folder view. 🥕📂

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly highlights the primary feature introduced by this PR—moving videos into folders—and is both clear and specific without unnecessary detail or noise.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b84ad4b and e360651.

📒 Files selected for processing (3)
  • apps/web/actions/folders/move-videos-to-folder.ts (1 hunks)
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx (1 hunks)
  • apps/web/lib/folder.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/actions/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All Groq/OpenAI calls must be implemented in Next.js Server Actions under apps/web/actions; do not place AI calls elsewhere

Files:

  • apps/web/actions/folders/move-videos-to-folder.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/actions/folders/move-videos-to-folder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/web/actions/folders/move-videos-to-folder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/actions/folders/move-videos-to-folder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/actions/folders/move-videos-to-folder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/actions/folders/move-videos-to-folder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
🧬 Code graph analysis (3)
apps/web/actions/folders/move-videos-to-folder.ts (5)
packages/web-domain/src/Folder.ts (1)
  • Folder (19-27)
apps/web/lib/folder.ts (1)
  • moveVideosToFolder (383-534)
packages/web-domain/src/Policy.ts (1)
  • Policy (7-10)
packages/web-domain/src/Authentication.ts (1)
  • CurrentUser (7-10)
apps/web/lib/server.ts (1)
  • runPromise (59-71)
apps/web/lib/folder.ts (6)
packages/database/index.ts (1)
  • db (29-34)
packages/web-backend/src/Database.ts (1)
  • Database (9-19)
packages/database/schema.ts (9)
  • folders (219-243)
  • spaceVideos (577-597)
  • spaces (532-552)
  • organizations (149-169)
  • sharedVideos (295-315)
  • videos (245-293)
  • comments (317-337)
  • users (47-96)
  • videoUploads (656-662)
packages/web-domain/src/Folder.ts (3)
  • Folder (19-27)
  • FolderId (8-8)
  • FolderId (9-9)
packages/web-domain/src/Video.ts (3)
  • Video (14-59)
  • VideoId (10-10)
  • VideoId (11-11)
packages/web-domain/src/Authentication.ts (1)
  • CurrentUser (7-10)
apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx (5)
apps/web/app/(org)/dashboard/Contexts.tsx (1)
  • useDashboardContext (44-44)
apps/web/lib/EffectRuntime.ts (2)
  • useEffectQuery (23-23)
  • useEffectMutation (24-24)
apps/web/actions/folders/getAllFolders.ts (1)
  • getAllFoldersAction (9-35)
packages/database/schema.ts (1)
  • folders (219-243)
apps/web/actions/folders/move-videos-to-folder.ts (1)
  • moveVideosToFolderAction (17-98)
⏰ 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). (2)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
🔇 Additional comments (2)
apps/web/actions/folders/move-videos-to-folder.ts (1)

17-97: LGTM

Space-aware policy gating, ID coercion, and cache revalidation are all wired correctly. Good work.

apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx (1)

44-332: LGTM

Query/mutation wiring, selection state, and cache delta handling all look solid.


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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70a1a9e and ea8994c.

📒 Files selected for processing (5)
  • apps/web/actions/folders/getAllFolders.ts (1 hunks)
  • apps/web/actions/folders/moveVideosToFolder.ts (1 hunks)
  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx (1 hunks)
  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx (1 hunks)
  • apps/web/lib/folder.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/actions/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All Groq/OpenAI calls must be implemented in Next.js Server Actions under apps/web/actions; do not place AI calls elsewhere

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/actions/folders/getAllFolders.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/actions/folders/getAllFolders.ts
  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx
  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/actions/folders/getAllFolders.ts
  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx
  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/actions/folders/getAllFolders.ts
  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx
  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/actions/folders/getAllFolders.ts
  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx
  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/actions/folders/getAllFolders.ts
  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx
  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx
  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
🧬 Code graph analysis (5)
apps/web/actions/folders/moveVideosToFolder.ts (5)
packages/web-domain/src/Folder.ts (1)
  • Folder (19-27)
apps/web/lib/folder.ts (1)
  • moveVideosToFolder (383-500)
packages/web-domain/src/Policy.ts (1)
  • Policy (7-10)
packages/web-domain/src/Authentication.ts (1)
  • CurrentUser (7-10)
apps/web/lib/server.ts (1)
  • runPromise (59-71)
apps/web/lib/folder.ts (6)
packages/database/index.ts (1)
  • db (29-34)
packages/web-backend/src/Database.ts (1)
  • Database (9-19)
packages/database/schema.ts (4)
  • folders (219-243)
  • spaceVideos (577-597)
  • videos (245-293)
  • users (47-96)
packages/web-domain/src/Folder.ts (3)
  • Folder (19-27)
  • FolderId (8-8)
  • FolderId (9-9)
packages/web-domain/src/Video.ts (3)
  • Video (14-59)
  • VideoId (10-10)
  • VideoId (11-11)
packages/web-domain/src/Authentication.ts (1)
  • CurrentUser (7-10)
apps/web/actions/folders/getAllFolders.ts (4)
packages/database/schema.ts (1)
  • folders (219-243)
apps/web/lib/server.ts (1)
  • runPromise (59-71)
apps/web/lib/folder.ts (1)
  • getAllFolders (309-381)
packages/web-domain/src/Authentication.ts (1)
  • CurrentUser (7-10)
apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx (3)
packages/web-domain/src/Video.ts (3)
  • Video (14-59)
  • VideoId (10-10)
  • VideoId (11-11)
apps/web/app/(org)/dashboard/_components/ConfirmationDialog.tsx (1)
  • ConfirmationDialog (25-72)
apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx (1)
  • FolderSelectionDialog (45-311)
apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx (4)
apps/web/app/(org)/dashboard/Contexts.tsx (1)
  • useDashboardContext (44-44)
apps/web/lib/EffectRuntime.ts (2)
  • useEffectQuery (23-23)
  • useEffectMutation (24-24)
apps/web/actions/folders/getAllFolders.ts (1)
  • getAllFoldersAction (9-35)
apps/web/actions/folders/moveVideosToFolder.ts (1)
  • moveVideosToFolderAction (17-97)
⏰ 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). (2)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)

Comment on lines 320 to 499
// Get all folders in one query
const allFolders = yield* db.execute((db) =>
db
.select({
id: folders.id,
name: folders.name,
color: folders.color,
parentId: folders.parentId,
organizationId: folders.organizationId,
videoCount: sql<number>`(
SELECT COUNT(*) FROM videos WHERE videos.folderId = folders.id
)`,
})
.from(folders)
.where(
and(
eq(folders.organizationId, user.activeOrganizationId),
root.variant === "space"
? eq(folders.spaceId, root.spaceId)
: isNull(folders.spaceId)
)
)
);

// Define the folder with children type
type FolderWithChildren = {
id: string;
name: string;
color: "normal" | "blue" | "red" | "yellow";
parentId: string | null;
organizationId: string;
videoCount: number;
children: FolderWithChildren[];
};

// Build hierarchy client-side
const folderMap = new Map<string, FolderWithChildren>();
const rootFolders: FolderWithChildren[] = [];

// First pass: create folder objects with children array
allFolders.forEach((folder) => {
folderMap.set(folder.id, { ...folder, children: [] });
});

// Second pass: build parent-child relationships
allFolders.forEach((folder) => {
const folderWithChildren = folderMap.get(folder.id);

if (folder.parentId) {
const parent = folderMap.get(folder.parentId);
if (parent && folderWithChildren) {
parent.children.push(folderWithChildren);
}
} else {
if (folderWithChildren) {
rootFolders.push(folderWithChildren);
}
}
});

return rootFolders;
});

export const moveVideosToFolder = Effect.fn(function* (
videoIds: Video.VideoId[],
targetFolderId: Folder.FolderId | null,
root?:
| { variant: "space"; spaceId: string }
| { variant: "org"; organizationId: string }
) {
if (videoIds.length === 0) throw new Error("No videos to move");

const db = yield* Database;
const user = yield* CurrentUser;

if (!user.activeOrganizationId) throw new Error("No active organization");

// Validate that all videos exist and belong to the user
const existingVideos = yield* db.execute((db) =>
db
.select({
id: videos.id,
folderId: videos.folderId,
ownerId: videos.ownerId,
})
.from(videos)
.where(and(inArray(videos.id, videoIds), eq(videos.ownerId, user.id)))
);

if (existingVideos.length !== videoIds.length) {
throw new Error(
"Some videos not found or you don't have permission to move them"
);
}

// If target folder is specified, validate it exists and user has access
if (targetFolderId) {
const targetFolder = yield* getFolderById(targetFolderId);

if (targetFolder.organizationId !== user.activeOrganizationId) {
throw new Error("Target folder not found or you don't have access to it");
}

// Validate space context if provided
if (root?.variant === "space" && targetFolder.spaceId !== root.spaceId) {
throw new Error("Target folder does not belong to the specified space");
}

// Block moves into space folders when not operating in that space
if (root?.variant !== "space" && targetFolder.spaceId !== null) {
throw new Error(
"Target folder is scoped to a space and cannot be used here"
);
}
}

// Determine original folder ids and perform the move based on context
let originalFolderIds: (string | null)[] = [];

if (root?.variant === "space") {
// Collect originals from space_videos
const spaceRows = yield* db.execute((db) =>
db
.select({
folderId: spaceVideos.folderId,
videoId: spaceVideos.videoId,
})
.from(spaceVideos)
.where(
and(
eq(spaceVideos.spaceId, root.spaceId),
inArray(spaceVideos.videoId, videoIds)
)
)
);
const spaceVideoIds = new Set(spaceRows.map((row) => row.videoId));
const missingVideoIds = videoIds.filter((id) => !spaceVideoIds.has(id));
if (missingVideoIds.length > 0) {
throw new Error(
"Some videos are not in the specified space or you don't have permission to move them"
);
}
originalFolderIds = [
...new Set(spaceRows.map((r) => r.folderId).filter(Boolean)),
];

// Update per-space folder placement
yield* db.execute((db) =>
db
.update(spaceVideos)
.set({ folderId: targetFolderId })
.where(
and(
eq(spaceVideos.spaceId, root.spaceId),
inArray(spaceVideos.videoId, videoIds)
)
)
);
} else {
// ORG/global placement via videos.folderId
originalFolderIds = [
...new Set(existingVideos.map((v) => v.folderId).filter(Boolean)),
];

yield* db.execute((db) =>
db
.update(videos)
.set({
folderId: targetFolderId,
updatedAt: new Date(),
})
.where(inArray(videos.id, videoIds))
);
}

return {
movedCount: videoIds.length,
originalFolderIds,
targetFolderId,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove inline comments introduced in this module.

The recent changes added several // … annotations (e.g. “Get all folders in one query”, “Build hierarchy…”, “Validate that all videos exist…”) in this file. Our TS guideline forbids inline/block comments, so please delete these comment lines across getAllFolders and moveVideosToFolder. As per coding guidelines

- Remove inline comments per coding guidelines
- Add scope isolation with isNull(folders.spaceId) filters
- Implement permission checks with SpacesPolicy validation
- Replace broad cache invalidation with targeted setQueryData updates
- Return detailed result data for efficient cache mutations
- Ensure space context validation to prevent cross-scope moves
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: 1

🧹 Nitpick comments (1)
apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx (1)

81-151: LGTM - cache updates are now properly targeted!

The mutation correctly uses setQueryData and setQueriesData for targeted cache updates instead of broad invalidations, following the coding guidelines. The cache update logic properly decrements video counts from original folders and increments the target folder count.

Minor: Replace any type with explicit Video type.

Line 131 uses any[] for oldVideos, which violates the "avoid any" guideline. Consider defining and using an explicit Video type from your domain types.

Apply this diff:

+import type { Video } from "@cap/web-domain";
+
     queryClient.setQueriesData(
       { queryKey: ["videos"] },
-      (oldVideos: any[] | undefined) => {
+      (oldVideos: Array<{ id: string; folderId: string | null }> | undefined) => {

As per coding guidelines

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea8994c and c2d5606.

📒 Files selected for processing (2)
  • apps/web/actions/folders/moveVideosToFolder.ts (1 hunks)
  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
  • apps/web/actions/folders/moveVideosToFolder.ts
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
  • apps/web/actions/folders/moveVideosToFolder.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
  • apps/web/actions/folders/moveVideosToFolder.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
  • apps/web/actions/folders/moveVideosToFolder.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
  • apps/web/actions/folders/moveVideosToFolder.ts
apps/web/actions/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All Groq/OpenAI calls must be implemented in Next.js Server Actions under apps/web/actions; do not place AI calls elsewhere

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
🧠 Learnings (2)
📚 Learning: 2025-09-22T14:17:47.407Z
Learnt from: CR
PR: CapSoftware/Cap#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-22T14:17:47.407Z
Learning: Applies to **/*.{ts,tsx,js,jsx,rs} : Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Applied to files:

  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
📚 Learning: 2025-09-22T14:17:47.407Z
Learnt from: CR
PR: CapSoftware/Cap#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-22T14:17:47.407Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations

Applied to files:

  • apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx
🧬 Code graph analysis (2)
apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx (5)
apps/web/app/(org)/dashboard/Contexts.tsx (1)
  • useDashboardContext (44-44)
apps/web/lib/EffectRuntime.ts (2)
  • useEffectQuery (23-23)
  • useEffectMutation (24-24)
apps/web/actions/folders/getAllFolders.ts (1)
  • getAllFoldersAction (9-35)
packages/database/schema.ts (1)
  • folders (219-243)
apps/web/actions/folders/moveVideosToFolder.ts (1)
  • moveVideosToFolderAction (17-97)
apps/web/actions/folders/moveVideosToFolder.ts (4)
apps/web/lib/folder.ts (1)
  • moveVideosToFolder (383-500)
packages/web-domain/src/Policy.ts (1)
  • Policy (7-10)
packages/web-domain/src/Authentication.ts (1)
  • CurrentUser (7-10)
apps/web/lib/server.ts (1)
  • runPromise (59-71)
⏰ 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). (2)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
🔇 Additional comments (7)
apps/web/actions/folders/moveVideosToFolder.ts (3)

1-15: LGTM!

The imports are clean, all used, and the interface is properly typed with optional space context support.


17-54: LGTM!

The function logic is well-structured:

  • Proper authentication and authorization checks
  • Correct use of branded types for domain IDs
  • Conditional policy enforcement (SpacesPolicy for space context)
  • Proper Effect composition with CurrentUser service injection

56-96: LGTM!

The cache revalidation strategy is comprehensive and context-aware, properly invalidating affected paths in both space and organization contexts. Error handling includes logging and returns a structured error response.

apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx (4)

44-79: LGTM!

The component properly uses useEffectQuery from EffectRuntime and implements proper enabled guards. The non-null assertion on line 66 is protected by the enabled flag on line 76.


153-232: LGTM!

The folder expansion toggle and recursive rendering logic is clean and correct. Proper state management with Set for expanded folders and depth-based indentation for the tree structure.


234-245: LGTM!

The confirmation and cancellation handlers are straightforward and properly clean up component state.


247-336: LGTM!

The dialog JSX structure is well-organized with proper loading states, empty states, and user feedback. Good UX with disabled buttons during mutation.

Comment on lines 1 to 42
"use client";

import {
Button,
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@cap/ui";
import {
faFolderOpen,
faChevronRight,
faChevronDown,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useEffectMutation, useEffectQuery } from "@/lib/EffectRuntime";
import { useState } from "react";
import { getAllFoldersAction } from "../../../../actions/folders/getAllFolders";
import { moveVideosToFolderAction } from "../../../../actions/folders/moveVideosToFolder";
import { useDashboardContext } from "../Contexts";
import { toast } from "sonner";
import { Effect } from "effect";
import { useQueryClient } from "@tanstack/react-query";

type FolderWithChildren = {
id: string;
name: string;
color: "normal" | "blue" | "red" | "yellow";
parentId: string | null;
organizationId: string;
videoCount: number;
children: FolderWithChildren[];
};

interface FolderSelectionDialogProps {
open: boolean;
onClose: () => void;
onConfirm: (folderId: string | null) => void;
selectedCount: number;
videoIds: string[];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Rename file to follow kebab-case convention.

The coding guidelines require kebab-case for filenames. This file should be renamed from FolderSelectionDialog.tsx to folder-selection-dialog.tsx. As per coding guidelines

🤖 Prompt for AI Agents
In apps/web/app/(org)/dashboard/_components/FolderSelectionDialog.tsx lines
1-42: the filename violates the repo's kebab-case convention; rename the file to
folder-selection-dialog.tsx and update any imports referencing
FolderSelectionDialog.tsx across the codebase (including index files and
component consumers) to use the new kebab-case path, ensure git moves the file
(git mv) to preserve history, and run the build/tests to verify no import paths
remain unchanged.

- Rename FolderSelectionDialog.tsx to folder-selection-dialog.tsx per coding guidelines
- Update import path in SelectedCapsBar.tsx to use new kebab-case filename
- Preserve git history using git mv command
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2d5606 and 0ab0856.

📒 Files selected for processing (2)
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx (1 hunks)
  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)

- Replace over-deduction logic with precise per-folder video count deltas
- Add videoCountDeltas to moveVideosToFolder return value with exact decrements/increments
- Implement recursive applyFolderDeltas helper to update entire folder tree
- Fix multi-source folder moves by calculating actual videos moved per folder
- Ensure nested folders at all depths get updated correctly
- Prevent cache staleness in grandchildren and deeper folder descendants
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: 1

🧹 Nitpick comments (2)
apps/web/lib/folder.ts (1)

345-353: Consider extracting FolderWithChildren type to avoid duplication.

The FolderWithChildren type is defined locally here and also in folder-selection-dialog.tsx (lines 26-34). Consider extracting this type to a shared location (e.g., packages/web-domain/src/Folder.ts or apps/web/types/folder.ts) to maintain a single source of truth and ensure consistency if the structure evolves.

Example location for the shared type:

// packages/web-domain/src/Folder.ts
export type FolderWithChildren = {
  id: string;
  name: string;
  color: "normal" | "blue" | "red" | "yellow";
  parentId: string | null;
  organizationId: string;
  videoCount: number;
  children: FolderWithChildren[];
};

Then import and use in both files:

+import type { FolderWithChildren } from "@cap/web-domain";
-type FolderWithChildren = { ... };
apps/web/actions/folders/moveVideosToFolder.ts (1)

91-97: Consider structured logging for production.

The error handling correctly catches and returns structured error responses. For production observability, consider using a structured logging service instead of console.error to enable better debugging and alerting.

If you'd like, I can help generate a structured logging wrapper that integrates with your observability stack.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab0856 and b84ad4b.

📒 Files selected for processing (3)
  • apps/web/actions/folders/moveVideosToFolder.ts (1 hunks)
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx (1 hunks)
  • apps/web/lib/folder.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/actions/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All Groq/OpenAI calls must be implemented in Next.js Server Actions under apps/web/actions; do not place AI calls elsewhere

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/actions/folders/moveVideosToFolder.ts
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx
🧬 Code graph analysis (3)
apps/web/actions/folders/moveVideosToFolder.ts (5)
packages/web-domain/src/Folder.ts (1)
  • Folder (19-27)
apps/web/lib/folder.ts (1)
  • moveVideosToFolder (383-527)
packages/web-domain/src/Policy.ts (1)
  • Policy (7-10)
packages/web-domain/src/Authentication.ts (1)
  • CurrentUser (7-10)
apps/web/lib/server.ts (1)
  • runPromise (59-71)
apps/web/lib/folder.ts (6)
packages/database/index.ts (1)
  • db (29-34)
packages/web-backend/src/Database.ts (1)
  • Database (9-19)
packages/database/schema.ts (9)
  • folders (219-243)
  • spaceVideos (577-597)
  • spaces (532-552)
  • organizations (149-169)
  • sharedVideos (295-315)
  • videos (245-293)
  • comments (317-337)
  • users (47-96)
  • videoUploads (656-662)
packages/web-domain/src/Folder.ts (3)
  • Folder (19-27)
  • FolderId (8-8)
  • FolderId (9-9)
packages/web-domain/src/Video.ts (3)
  • Video (14-59)
  • VideoId (10-10)
  • VideoId (11-11)
packages/web-domain/src/Authentication.ts (1)
  • CurrentUser (7-10)
apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx (4)
apps/web/app/(org)/dashboard/Contexts.tsx (1)
  • useDashboardContext (44-44)
apps/web/lib/EffectRuntime.ts (2)
  • useEffectQuery (23-23)
  • useEffectMutation (24-24)
apps/web/actions/folders/getAllFolders.ts (1)
  • getAllFoldersAction (9-35)
apps/web/actions/folders/moveVideosToFolder.ts (1)
  • moveVideosToFolderAction (17-98)
⏰ 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). (2)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
🔇 Additional comments (10)
apps/web/app/(org)/dashboard/_components/folder-selection-dialog.tsx (3)

58-77: Correct usage of Effect-based query pattern.

The query setup properly:

  • Uses useEffectQuery with Effect.tryPromise as per coding guidelines
  • Constructs space-aware or org-aware root context from useDashboardContext
  • Enables the query conditionally when dialog is open and organization is available
  • Handles errors with clear error messages

104-122: Recursive delta application correctly fixes multi-folder cache updates.

The applyFolderDeltas helper now properly:

  • Walks the entire folder tree recursively, not just two levels
  • Applies per-folder deltas from result.videoCountDeltas rather than uniform deductions
  • Handles missing deltas with ?? 0 fallback
  • Updates all descendant folders through recursive children processing

This addresses the previous review's concern about over-deduction and stale grandchild counts.


160-227: Clean recursive folder rendering with proper state management.

The renderFolder function correctly:

  • Handles nested folder hierarchies with depth tracking
  • Manages expansion and selection state independently
  • Styles selected folders with visual indicators
  • Recursively renders children when expanded

The implementation provides a good user experience for navigating folder hierarchies.

apps/web/lib/folder.ts (3)

309-381: Efficient hierarchical folder construction with proper filtering.

The getAllFolders implementation correctly:

  • Fetches all folders in a single query with organization and space filtering
  • Uses isNull(folders.spaceId) for org-level folder isolation
  • Builds the hierarchy client-side with a two-pass algorithm (map construction, then parent-child linking)
  • Includes videoCount subquery for each folder

The client-side hierarchy building is efficient and maintains proper parent-child relationships.


415-434: Robust validation ensures space isolation and access control.

The target folder validation correctly enforces:

  • Organization membership (lines 419-421)
  • Space context consistency when operating in space mode (lines 423-426)
  • Prevention of cross-context moves by blocking moves into space folders from org context (lines 428-433)

This validation is critical for maintaining space isolation and preventing unauthorized folder access.


440-519: Dual-mode move operation with accurate per-folder delta calculation.

The implementation correctly handles both contexts:

  • Space mode (lines 440-489): Updates spaceVideos.folderId after validating videos are in the space
  • Org mode (lines 490-514): Updates videos.folderId for organization-level placement
  • Delta calculation (lines 464-476, 492-503): Accurately computes per-folder decrements by counting videos per source folder
  • Target increment (line 518): Uses += to handle cases where target might also be a source

The per-folder delta approach ensures cache updates reflect the actual video distribution across folders, even when moving videos from multiple source folders.

apps/web/actions/folders/moveVideosToFolder.ts (4)

1-9: LGTM!

Imports are clean, properly organized, and all symbols are used. The "use server" directive is correctly placed for Next.js Server Actions.


22-29: LGTM!

User validation follows secure-by-default patterns with proper early-exit on authorization failure. The check for activeOrganizationId ensures org-scoped operations proceed only with valid context.


40-52: LGTM!

The Effect composition correctly applies SpacesPolicy.isMember for space-scoped operations while relying on internal authorization checks in moveVideosToFolder for org-level moves. Service provisioning with Effect.provideService(CurrentUser, user) follows Effect best practices. Based on learnings


56-79: LGTM!

The revalidation strategy is comprehensive and precise, invalidating both source and target folder paths in the appropriate context (space vs. org). The granular approach aligns with the coding guideline to prefer targeted cache updates. As per coding guidelines

@@ -0,0 +1,98 @@
"use server";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Rename file to follow kebab-case convention.

The filename moveVideosToFolder.ts violates the repository's kebab-case convention for TypeScript modules. As per coding guidelines

Rename the file:

git mv apps/web/actions/folders/moveVideosToFolder.ts apps/web/actions/folders/move-videos-to-folder.ts

Update the import in any files that reference this action:

-import { moveVideosToFolderAction } from '@/actions/folders/moveVideosToFolder';
+import { moveVideosToFolderAction } from '@/actions/folders/move-videos-to-folder';
🤖 Prompt for AI Agents
In apps/web/actions/folders/moveVideosToFolder.ts around line 1, the filename
violates the repo's kebab-case convention; rename the file to
apps/web/actions/folders/move-videos-to-folder.ts (e.g. git mv ...) and update
all imports/usages across the codebase to the new path (search for
"moveVideosToFolder" and replace import paths, update any relative paths, and
run TypeScript build/tests to ensure no remaining references).

@Enejivk Enejivk closed this Oct 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.

1 participant