Skip to content

Conversation

ameer2468
Copy link
Collaborator

@ameer2468 ameer2468 commented Oct 9, 2025

Summary by CodeRabbit

  • New Features

    • Add, remove, and move videos into folders with explicit support for space vs. organization ("All spaces"); "Add videos" button on folder pages.
  • Improvements

    • Space/org-aware video picker and dialogs (reset on close, refresh lists).
    • Folder listings and counts respect space vs. organization context.
    • Video cards: themed folder visuals, richer metadata, keyboard accessibility, taller grid items.
    • Shared/space scoping for organization-wide video management and dialogs.
  • Behavior Changes

    • Dashboard and folder views now distinguish organization-wide vs. space-scoped content and revalidate paths after changes.

Copy link
Contributor

coderabbitai bot commented Oct 9, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds space/org-aware folder and video management: new server actions for add/remove/get/move folder videos, branches between organization-scoped sharedVideos and space-scoped spaceVideos, propagates spaceId through UI, updates lib/folder APIs to accept a root variant, adds folderId to sharedVideos schema and migration, and removes legacy user-only getUserVideos.

Changes

Cohort / File(s) Summary
Folder actions
apps/web/actions/folders/add-videos.ts, apps/web/actions/folders/remove-videos.ts, apps/web/actions/folders/get-folder-videos.ts, apps/web/actions/folders/moveVideoToFolder.ts
New/updated server actions: auth/validation, scope branching between sharedVideos (org) and spaceVideos (space), updated imports/types to use Space.SpaceIdOrOrganisationId, revalidation and standardized responses.
Space-scoped actions
apps/web/actions/spaces/add-videos.ts, apps/web/actions/spaces/get-space-videos.ts, apps/web/actions/spaces/get-user-videos.ts, apps/web/actions/spaces/remove-videos.ts
Reworked flows to detect "all spaces" vs per-space using isAllSpacesEntry; use sharedVideos for org scope, spaceVideos for space scope; adds getUserVideos(spaceId) with rich projection.
Organization actions
apps/web/actions/organizations/add-videos.ts, apps/web/actions/organizations/get-organization-videos.ts
Adjusted add logic to update existing shared entries and insert only new ones; tightened get query to require folderId IS NULL.
Removed legacy video action
apps/web/actions/videos/get-user-videos.ts
Deleted legacy getUserVideos(limit?) implementation (superseded by space-aware version).
UI — Shared dialogs & components
apps/web/app/(org)/dashboard/spaces/[spaceId]/components/...
Wire dialogs to space-aware getters (use getUserVideos(spaceId)), change AddVideosDialogBase API to parameterless getters, add folderName/folderColor to VideoData, UI tweaks to VideoCard and grid sizing, pass spaceId through SharedCaps and dialogs.
UI — Folder pages & button
apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx, .../folder/[folderId]/page.tsx, apps/web/app/(org)/dashboard/folder/[id]/page.tsx
New client AddVideosButton component wired to add/remove folder videos with space-aware actions; pages now pass spaceId/root variant to fetchers and to the button.
Breadcrumbs & links
apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx, .../ClientMyCapsLink.tsx
Components accept/forward spaceId; drag-and-drop and move operations now pass spaceId; navigation links use spaceId when present.
Misc UI cleanup
apps/web/app/(org)/dashboard/caps/page.tsx, apps/web/app/(org)/dashboard/folder/[id]/components/FolderVideosSection.tsx
Removed unused extraction of customDomain/domainVerified; removed unused useStore import.
Data layer — folder lib
apps/web/lib/folder.ts
getVideosByFolderId and getChildFolders signatures updated to accept root variant (`user
Database schema & migrations
packages/database/schema.ts, packages/database/migrations/meta/_journal.json
Added nullable folderId to sharedVideos with indexes; added migration journal entry.
Types
apps/web/app/(org)/dashboard/caps/components/Folder.tsx
FolderDataType.spaceId updated to Space.SpaceIdOrOrganisationId; parentId changed to `Folder.FolderId

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as Client UI
  participant Server as addVideosToFolder
  participant DB as Database

  rect rgba(230,245,255,0.6)
  note over UI,Server: Add videos to folder (space/org aware)
  User->>UI: Trigger add videos (folderId, videoIds, spaceId)
  UI->>Server: addVideosToFolder(folderId, videoIds, spaceId)
  Server->>Server: authenticate & validate inputs
  Server->>DB: fetch folder, resolve scope (org vs space)
  alt org scope
    Server->>DB: update/insert into sharedVideos set folderId
  else space scope
    Server->>DB: update/insert into spaceVideos set folderId
  end
  Server-->>UI: { success, addedCount }
  UI->>User: refresh view / revalidate paths
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant UI as Client UI
  participant Server as lib/folder.getVideosByFolderId
  participant DB as Database

  User->>UI: Request folder videos (folderId, root)
  UI->>Server: getVideosByFolderId(folderId, root)
  alt root.variant == "user"
    Server->>DB: query videos where videos.folderId = folderId
  else root.variant == "space"
    Server->>DB: join spaceVideos where spaceVideos.folderId = folderId
  else root.variant == "org"
    Server->>DB: join sharedVideos where sharedVideos.folderId = folderId
  end
  DB-->>Server: rows
  Server-->>UI: data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Suggested reviewers

  • Brendonovich

Poem

In my burrow I hop with cheer,
I tuck each cap and shift them near.
Shared roots, space leaves, folders snug,
I plant a carrot, index dug.
Hooray — videos find their den, hooray again! 🥕🐇

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 references the addition of a videos button in folders, which corresponds to part of this set of changes, but the use of “& more” is vague and fails to convey the extensive API and UI updates included in the pull request.

📜 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 96d18f2 and 95e8bd1.

📒 Files selected for processing (1)
  • apps/web/actions/spaces/remove-videos.ts (3 hunks)

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/database/migrations/meta/_journal.json (1)

2-74: Restore the original migration journal version

The pipeline is failing (Validate Migrations) because this commit changes the journal version value. The migration journal version is immutable—only append new entries while keeping the existing version untouched. Please revert the version change (and rerun drizzle-kit so the journal update is generated correctly).

apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx (2)

37-55: Restore organisation folder filtering

When allSpacesEntry is true we pass the organisation id into fetchFolders, but the predicate still does eq(folders.spaceId, spaceId). Organisation-level folders have spaceId set to NULL, so this condition filters every row out and the org dashboard loses all folders. Please branch the where clause: in the org path filter by folders.organizationId = spaceId plus isNull(folders.spaceId), and keep the existing folders.spaceId = spaceId check for the space path. Example:

-		.where(and(eq(folders.spaceId, spaceId), isNull(folders.parentId)));
+		.where(
+			allSpacesEntry
+				? and(
+						eq(folders.organizationId, spaceId),
+						isNull(folders.spaceId),
+						isNull(folders.parentId),
+				  )
+				: and(eq(folders.spaceId, spaceId), isNull(folders.parentId)),
+		);

266-274: Use sharedVideos.folderId in org total count

The organisation total-count query still checks isNull(videos.folderId), but in this refactor folder membership lives in sharedVideos.folderId. Once a video is placed into an org folder, it keeps videos.folderId = NULL, so the count keeps including it. Pagination will claim more pages than exist and users land on empty pages. Swap the predicate to isNull(sharedVideos.folderId) to align with the select query.

-					.where(
-						and(
-							eq(sharedVideos.organizationId, orgId),
-							isNull(videos.folderId),
-						),
-					),
+					.where(
+						and(
+							eq(sharedVideos.organizationId, orgId),
+							isNull(sharedVideos.folderId),
+						),
+					),
apps/web/lib/folder.ts (1)

298-301: Fix child-folder video counts for space/org roots

After rerouting space/org folder membership into spaceVideos / sharedVideos, the child-folder counter still reads from videos.folderId. For space or org variants this always returns zero, so nested folders show empty counts even when populated. Please branch the subquery to count from spaceVideos (filtered by root.spaceId) or sharedVideos (filtered by the active organisation) instead of videos.

-				videoCount: sql<number>`(
-        	SELECT COUNT(*) FROM videos WHERE videos.folderId = folders.id
-	      )`,
+				videoCount:
+					root.variant === "space"
+						? sql<number>`(
+								SELECT COUNT(*)
+								FROM ${spaceVideos}
+								WHERE ${spaceVideos}.folderId = ${folders}.id
+									AND ${spaceVideos}.spaceId = ${root.spaceId}
+						  )`
+						: root.variant === "org"
+							? sql<number>`(
+									SELECT COUNT(*)
+									FROM ${sharedVideos}
+									WHERE ${sharedVideos}.folderId = ${folders}.id
+										AND ${sharedVideos}.organizationId = ${user.activeOrganizationId}
+							  )`
+							: sql<number>`(
+									SELECT COUNT(*) FROM ${videos} WHERE ${videos}.folderId = ${folders}.id
+							  )`,
🧹 Nitpick comments (1)
apps/web/actions/folders/remove-videos.ts (1)

64-88: Inconsistent query operator at line 83.

Lines 64-75 use eq for all comparisons in sharedVideos, but line 83 uses a sql template for the spaceId comparison in spaceVideos. For consistency and clarity, prefer eq unless there's a specific reason for the template.

Apply this diff to use eq consistently:

-						sql`${spaceVideos.spaceId} = ${folder.spaceId}`,
+						eq(spaceVideos.spaceId, folder.spaceId),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b9eb28 and 1d2f118.

📒 Files selected for processing (26)
  • apps/web/actions/folders/add-videos.ts (1 hunks)
  • apps/web/actions/folders/get-folder-videos.ts (1 hunks)
  • apps/web/actions/folders/moveVideoToFolder.ts (3 hunks)
  • apps/web/actions/folders/remove-videos.ts (1 hunks)
  • apps/web/actions/organizations/add-videos.ts (1 hunks)
  • apps/web/actions/organizations/get-organization-videos.ts (2 hunks)
  • apps/web/actions/spaces/add-videos.ts (3 hunks)
  • apps/web/actions/spaces/get-space-videos.ts (2 hunks)
  • apps/web/actions/spaces/get-user-videos.ts (1 hunks)
  • apps/web/actions/spaces/remove-videos.ts (2 hunks)
  • apps/web/actions/videos/get-user-videos.ts (0 hunks)
  • apps/web/app/(org)/dashboard/caps/page.tsx (0 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/FolderVideosSection.tsx (0 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx (1 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx (6 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.tsx (2 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx (8 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx (3 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx (3 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VirtualizedVideoGrid.tsx (1 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx (1 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx (2 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx (7 hunks)
  • apps/web/lib/folder.ts (2 hunks)
  • packages/database/migrations/meta/_journal.json (1 hunks)
  • packages/database/schema.ts (3 hunks)
💤 Files with no reviewable changes (3)
  • apps/web/app/(org)/dashboard/caps/page.tsx
  • apps/web/actions/videos/get-user-videos.ts
  • apps/web/app/(org)/dashboard/folder/[id]/components/FolderVideosSection.tsx
🧰 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/spaces/get-user-videos.ts
  • apps/web/actions/organizations/get-organization-videos.ts
  • apps/web/actions/spaces/remove-videos.ts
  • apps/web/actions/folders/add-videos.ts
  • apps/web/actions/spaces/add-videos.ts
  • apps/web/actions/folders/get-folder-videos.ts
  • apps/web/actions/organizations/add-videos.ts
  • apps/web/actions/spaces/get-space-videos.ts
  • apps/web/actions/folders/remove-videos.ts
  • apps/web/actions/folders/moveVideoToFolder.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/spaces/get-user-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VirtualizedVideoGrid.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/organizations/get-organization-videos.ts
  • apps/web/actions/spaces/remove-videos.ts
  • apps/web/actions/folders/add-videos.ts
  • apps/web/actions/spaces/add-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx
  • apps/web/actions/folders/get-folder-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
  • apps/web/actions/organizations/add-videos.ts
  • apps/web/actions/spaces/get-space-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx
  • apps/web/actions/folders/remove-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.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/spaces/get-user-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VirtualizedVideoGrid.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/organizations/get-organization-videos.ts
  • apps/web/actions/spaces/remove-videos.ts
  • apps/web/actions/folders/add-videos.ts
  • apps/web/actions/spaces/add-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx
  • apps/web/actions/folders/get-folder-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
  • apps/web/actions/organizations/add-videos.ts
  • apps/web/actions/spaces/get-space-videos.ts
  • packages/database/schema.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx
  • apps/web/actions/folders/remove-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.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/spaces/get-user-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VirtualizedVideoGrid.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/organizations/get-organization-videos.ts
  • apps/web/actions/spaces/remove-videos.ts
  • apps/web/actions/folders/add-videos.ts
  • apps/web/actions/spaces/add-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx
  • apps/web/actions/folders/get-folder-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
  • apps/web/actions/organizations/add-videos.ts
  • apps/web/actions/spaces/get-space-videos.ts
  • packages/database/schema.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx
  • apps/web/actions/folders/remove-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.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/spaces/get-user-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VirtualizedVideoGrid.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/organizations/get-organization-videos.ts
  • apps/web/actions/spaces/remove-videos.ts
  • apps/web/actions/folders/add-videos.ts
  • apps/web/actions/spaces/add-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx
  • apps/web/actions/folders/get-folder-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
  • apps/web/actions/organizations/add-videos.ts
  • apps/web/actions/spaces/get-space-videos.ts
  • packages/database/schema.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx
  • apps/web/actions/folders/remove-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.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/spaces/get-user-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VirtualizedVideoGrid.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/organizations/get-organization-videos.ts
  • apps/web/actions/spaces/remove-videos.ts
  • apps/web/actions/folders/add-videos.ts
  • apps/web/actions/spaces/add-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx
  • apps/web/actions/folders/get-folder-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx
  • apps/web/lib/folder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
  • apps/web/actions/organizations/add-videos.ts
  • apps/web/actions/spaces/get-space-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx
  • apps/web/actions/folders/remove-videos.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.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/spaces/[spaceId]/SharedCaps.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VirtualizedVideoGrid.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.tsx
🧬 Code graph analysis (20)
apps/web/actions/spaces/get-user-videos.ts (2)
packages/database/schema.ts (8)
  • videos (277-332)
  • comments (364-385)
  • users (58-110)
  • folders (249-275)
  • videoUploads (710-716)
  • sharedVideos (334-362)
  • spaces (580-606)
  • spaceVideos (631-651)
packages/database/index.ts (1)
  • db (29-34)
apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx (1)
packages/web-domain/src/Space.ts (2)
  • SpaceIdOrOrganisationId (7-7)
  • SpaceIdOrOrganisationId (8-8)
apps/web/app/(org)/dashboard/folder/[id]/page.tsx (1)
apps/web/lib/folder.ts (1)
  • getVideosByFolderId (157-276)
apps/web/actions/organizations/get-organization-videos.ts (1)
packages/database/schema.ts (1)
  • sharedVideos (334-362)
apps/web/actions/spaces/remove-videos.ts (2)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (4)
  • sharedVideos (334-362)
  • folders (249-275)
  • videos (277-332)
  • spaceVideos (631-651)
apps/web/actions/folders/add-videos.ts (3)
packages/web-domain/src/Folder.ts (1)
  • Folder (37-45)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (4)
  • folders (249-275)
  • videos (277-332)
  • sharedVideos (334-362)
  • spaceVideos (631-651)
apps/web/actions/spaces/add-videos.ts (3)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (2)
  • sharedVideos (334-362)
  • spaceVideos (631-651)
packages/database/helpers.ts (1)
  • nanoId (6-9)
apps/web/app/(org)/dashboard/spaces/[spaceId]/page.tsx (2)
packages/web-domain/src/Space.ts (2)
  • SpaceIdOrOrganisationId (7-7)
  • SpaceIdOrOrganisationId (8-8)
packages/database/schema.ts (2)
  • sharedVideos (334-362)
  • spaceVideos (631-651)
apps/web/actions/folders/get-folder-videos.ts (3)
packages/web-domain/src/Folder.ts (1)
  • Folder (37-45)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (2)
  • sharedVideos (334-362)
  • spaceVideos (631-651)
apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx (2)
apps/web/actions/spaces/get-user-videos.ts (1)
  • getUserVideos (18-140)
apps/web/actions/organizations/get-organization-videos.ts (1)
  • getOrganizationVideoIds (9-49)
apps/web/lib/folder.ts (2)
packages/web-domain/src/Space.ts (2)
  • SpaceIdOrOrganisationId (7-7)
  • SpaceIdOrOrganisationId (8-8)
packages/database/schema.ts (3)
  • spaceVideos (631-651)
  • videos (277-332)
  • sharedVideos (334-362)
apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx (2)
apps/web/app/(org)/dashboard/Contexts.tsx (1)
  • useTheme (49-49)
apps/web/components/VideoThumbnail.tsx (1)
  • ImageLoadingStatus (8-8)
apps/web/actions/organizations/add-videos.ts (3)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (1)
  • sharedVideos (334-362)
packages/database/helpers.ts (1)
  • nanoId (6-9)
apps/web/actions/spaces/get-space-videos.ts (2)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (2)
  • sharedVideos (334-362)
  • spaceVideos (631-651)
packages/database/schema.ts (1)
packages/web-domain/src/Folder.ts (3)
  • Folder (37-45)
  • FolderId (11-11)
  • FolderId (12-12)
apps/web/actions/folders/remove-videos.ts (3)
packages/web-domain/src/Folder.ts (1)
  • Folder (37-45)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (4)
  • folders (249-275)
  • videos (277-332)
  • sharedVideos (334-362)
  • spaceVideos (631-651)
apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx (5)
packages/web-domain/src/Folder.ts (1)
  • Folder (37-45)
apps/web/actions/folders/add-videos.ts (1)
  • addVideosToFolder (15-97)
apps/web/actions/folders/remove-videos.ts (1)
  • removeVideosFromFolder (15-112)
apps/web/actions/spaces/get-user-videos.ts (1)
  • getUserVideos (18-140)
apps/web/actions/folders/get-folder-videos.ts (1)
  • getFolderVideoIds (9-50)
apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx (3)
packages/web-domain/src/Space.ts (2)
  • SpaceIdOrOrganisationId (7-7)
  • SpaceIdOrOrganisationId (8-8)
apps/web/lib/folder.ts (1)
  • getVideosByFolderId (157-276)
apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx (1)
  • AddVideosButton (15-52)
apps/web/actions/folders/moveVideoToFolder.ts (2)
packages/web-domain/src/Folder.ts (1)
  • Folder (37-45)
packages/database/schema.ts (3)
  • spaceVideos (631-651)
  • sharedVideos (334-362)
  • videos (277-332)
apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.tsx (2)
apps/web/actions/spaces/get-user-videos.ts (1)
  • getUserVideos (18-140)
apps/web/actions/spaces/get-space-videos.ts (1)
  • getSpaceVideoIds (9-56)
🪛 GitHub Actions: Validate Migrations
packages/database/migrations/meta/_journal.json

[error] 2-2: Migration journal version changed. Migration journal version cannot be changed (was: $BASE_VERSION, now: $CURRENT_VERSION).

⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (16)
apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx (2)

49-49: LGTM!

The new spaceId prop correctly types as Space.SpaceIdOrOrganisationId, enabling the component to distinguish between organization-wide and space-specific contexts.

Also applies to: 61-61


185-185: LGTM!

Passing spaceId instead of spaceData.id to child dialogs is correct. This enables the dialogs to branch between sharedVideos (org-wide) and spaceVideos (space-specific) queries based on the isAllSpacesEntry pattern used in backend actions.

Also applies to: 197-197, 252-252, 264-264

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

9-50: LGTM!

The action correctly implements organization-wide vs space-specific branching using the isAllSpacesEntry pattern. Authentication, validation, and error handling are appropriate.

apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.tsx (2)

7-7: LGTM!

The import path update correctly points to the space-scoped getUserVideos action that supports the organization-wide vs space-specific branching pattern.


35-36: LGTM!

Wrapping getUserVideos and getSpaceVideoIds with arrow functions that bind spaceId is the correct approach. This maintains the component's public API while enabling the base dialog to fetch context-appropriate data.

apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx (3)

23-26: LGTM!

Correctly updates spaceId type to Space.SpaceIdOrOrganisationId, aligning with the union type pattern used throughout the codebase for space/org context.


47-52: LGTM!

The getVideosByFolderId call correctly passes a root variant object that branches between space-specific and organization-wide contexts based on spaceOrOrg.variant.


59-63: LGTM!

The AddVideosButton integration correctly passes folderId, spaceId, and derives folderName from the breadcrumb, with an appropriate fallback.

apps/web/actions/folders/remove-videos.ts (2)

34-41: LGTM!

Folder existence verification is appropriate. Retrieving spaceId enables the subsequent branching logic.


44-53: LGTM!

Video ownership validation correctly restricts removal operations to user-owned videos only.

apps/web/actions/spaces/get-space-videos.ts (1)

21-42: LGTM!

The branching logic correctly queries sharedVideos for organization-wide contexts and spaceVideos for space-specific contexts. The isNull(folderId) condition appropriately filters for root-level videos.

apps/web/actions/organizations/add-videos.ts (2)

90-113: LGTM!

The refactored logic correctly handles both existing and new videos:

  • Existing shared videos are moved to the organization root by clearing folderId
  • New videos are inserted only when newVideoIds is non-empty

This avoids unnecessary database operations.


118-124: LGTM!

The success message correctly reports the total count with proper singular/plural forms and verb agreement.

apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx (3)

55-64: LGTM!

Adding role="button", tabIndex={0}, and keyboard handling for Enter/Space properly implements accessibility for the clickable card.


36-52: LGTM!

The Rive integration correctly computes the artboard based on theme and folder color, providing dynamic visual feedback. The key at line 168 ensures the component re-renders when theme or color changes.


165-193: LGTM!

The conditional rendering logic correctly displays:

  • Folder icon with name when folderName exists
  • Home icon with "Root" when in entity without folder
  • Vinyl icon with "Caps" otherwise

This provides clear visual context for video location.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/app/(org)/dashboard/caps/components/Folder.tsx (1)

115-119: Ensure spaceId is never undefined
The fallback activeOrganization?.organization.id can yield undefined, which doesn’t match the function’s spaceId: … | null type. Update the call to:

await moveVideoToFolder({
  videoId: data.id,
  folderId: id,
  spaceId: spaceId ?? activeOrganization?.organization.id ?? null,
});

Or add a guard so activeOrganization is always defined before this call.

apps/web/actions/folders/moveVideoToFolder.ts (1)

30-35: Fetch originalFolderId from the appropriate table based on spaceId

  • At apps/web/actions/folders/moveVideoToFolder.ts (lines 30–35), replace the single videos query with context-aware lookups:
    • If spaceId && !isAllSpacesEntry, select spaceVideos.folderId
    • Else if spaceId && isAllSpacesEntry, select sharedVideos.folderId
    • Otherwise, select videos.folderId
  • This ensures originalFolderId isn’t null for space- or org-scoped videos so that revalidatePath correctly covers the original 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 1d2f118 and 9c0ca54.

📒 Files selected for processing (6)
  • apps/web/actions/folders/moveVideoToFolder.ts (3 hunks)
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx (2 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx (4 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/ClientMyCapsLink.tsx (4 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx (4 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx (2 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/folder/[id]/components/ClientMyCapsLink.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/folder/[id]/components/ClientMyCapsLink.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/folder/[id]/components/ClientMyCapsLink.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/folder/[id]/components/ClientMyCapsLink.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/folder/[id]/components/ClientMyCapsLink.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/folder/[id]/components/ClientMyCapsLink.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
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/moveVideoToFolder.ts
🧬 Code graph analysis (5)
apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx (1)
apps/web/actions/folders/moveVideoToFolder.ts (1)
  • moveVideoToFolder (14-125)
apps/web/actions/folders/moveVideoToFolder.ts (2)
packages/web-domain/src/Folder.ts (1)
  • Folder (37-45)
packages/database/schema.ts (3)
  • spaceVideos (631-651)
  • sharedVideos (334-362)
  • videos (277-332)
apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx (6)
packages/web-domain/src/Space.ts (2)
  • SpaceIdOrOrganisationId (7-7)
  • SpaceIdOrOrganisationId (8-8)
packages/web-domain/src/Folder.ts (3)
  • Folder (37-45)
  • FolderId (11-11)
  • FolderId (12-12)
apps/web/lib/folder.ts (1)
  • getVideosByFolderId (157-276)
apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/AddVideosButton.tsx (1)
  • AddVideosButton (15-52)
apps/web/app/(org)/dashboard/folder/[id]/components/ClientMyCapsLink.tsx (1)
  • ClientMyCapsLink (15-148)
apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx (1)
  • BreadcrumbItem (22-129)
apps/web/app/(org)/dashboard/caps/components/Folder.tsx (2)
packages/web-domain/src/Space.ts (2)
  • SpaceIdOrOrganisationId (7-7)
  • SpaceIdOrOrganisationId (8-8)
packages/web-domain/src/Folder.ts (3)
  • Folder (37-45)
  • FolderId (11-11)
  • FolderId (12-12)
apps/web/app/(org)/dashboard/folder/[id]/page.tsx (4)
packages/web-domain/src/Folder.ts (3)
  • Folder (37-45)
  • FolderId (11-11)
  • FolderId (12-12)
apps/web/lib/folder.ts (3)
  • getChildFolders (278-314)
  • getFolderBreadcrumb (37-62)
  • getVideosByFolderId (157-276)
apps/web/app/(org)/dashboard/folder/[id]/components/ClientMyCapsLink.tsx (1)
  • ClientMyCapsLink (15-148)
apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx (1)
  • BreadcrumbItem (22-129)
⏰ 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: Analyze (rust)
🔇 Additional comments (6)
apps/web/app/(org)/dashboard/caps/components/Folder.tsx (1)

19-26: No missing FolderDataType consumers found
All inspected imports and object constructions include both spaceId and parentId; the change to required fields is fully accounted for.

apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx (1)

3-3: LGTM!

The space context propagation is correctly implemented. The spaceId prop is properly typed as Space.SpaceIdOrOrganisationId and consistently passed through to the moveVideoToFolder action.

Also applies to: 18-18, 27-27, 64-68

apps/web/app/(org)/dashboard/spaces/[spaceId]/folder/[folderId]/page.tsx (1)

23-26: LGTM!

The space-aware folder page implementation is correct:

  • Proper async params handling with Promise wrapper
  • Correct root variant branching based on spaceOrOrg for data fetching
  • Space context consistently propagated to all child components
  • AddVideosButton properly wired with folder metadata

Also applies to: 47-53, 59-63, 67-67, 72-72

apps/web/app/(org)/dashboard/folder/[id]/components/ClientMyCapsLink.tsx (1)

4-4: LGTM!

The space context integration is correctly implemented:

  • Proper type imports and signature updates
  • Space-aware routing logic
  • Correct propagation of spaceId to moveVideoToFolder

Also applies to: 15-19, 97-97, 116-116

apps/web/app/(org)/dashboard/folder/[id]/page.tsx (1)

46-46: Space context propagation looks correct (pending verification).

The spaceId is correctly passed to ClientMyCapsLink, BreadcrumbItem, and FolderCard components. However, confirm that spaceId is actually available in this route's params before finalizing these changes (see previous comment).

Also applies to: 53-53, 75-75

apps/web/actions/folders/moveVideoToFolder.ts (1)

5-11: LGTM on the three-way update branching logic.

The conditional logic correctly routes folder updates based on space context:

  • Space-scoped videos → spaceVideos table
  • Organization-wide (all spaces) → sharedVideos table
  • User-only videos → videos table

The isAllSpacesEntry helper clearly distinguishes between per-space and organization-wide contexts. Type imports and signature updates are correct.

Also applies to: 21-21, 37-37, 56-84

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/web/actions/folders/moveVideoToFolder.ts (2)

30-38: Track original folder per scope before revalidation

We still read originalFolderId from videos.folderId, but in both space and “All spaces” flows the folder lives in spaceVideos/sharedVideos. That keeps originalFolderId at null, so the old folder page (and its parents) never revalidate, leaving stale listings. Please pull the original folder from the scoped table and revalidate the matching space route as well.

-  const [currentVideo] = await db()
-    .select({ folderId: videos.folderId, id: videos.id })
-    .from(videos)
-    .where(eq(videos.id, videoId));
-
-  const originalFolderId = currentVideo?.folderId;
-
-  const isAllSpacesEntry = spaceId === user.activeOrganizationId;
+  const [currentVideo] = await db()
+    .select({ folderId: videos.folderId, id: videos.id })
+    .from(videos)
+    .where(eq(videos.id, videoId));
+
+  const isAllSpacesEntry = spaceId === user.activeOrganizationId;
+
+  let originalFolderId: Folder.FolderId | null = currentVideo?.folderId ?? null;
+
+  if (spaceId && !isAllSpacesEntry) {
+    const [spaceVideo] = await db()
+      .select({ folderId: spaceVideos.folderId })
+      .from(spaceVideos)
+      .where(
+        and(eq(spaceVideos.videoId, videoId), eq(spaceVideos.spaceId, spaceId)),
+      );
+    originalFolderId = spaceVideo?.folderId ?? null;
+  } else if (spaceId && isAllSpacesEntry) {
+    const [sharedVideo] = await db()
+      .select({ folderId: sharedVideos.folderId })
+      .from(sharedVideos)
+      .where(
+        and(
+          eq(sharedVideos.videoId, videoId),
+          eq(sharedVideos.organizationId, user.activeOrganizationId),
+        ),
+      );
+    originalFolderId = sharedVideo?.folderId ?? null;
+  }
@@
-  if (originalFolderId) {
-    revalidatePath(`/dashboard/folder/${originalFolderId}`);
-  }
+  if (originalFolderId) {
+    revalidatePath(`/dashboard/folder/${originalFolderId}`);
+    if (spaceId) {
+      revalidatePath(`/dashboard/spaces/${spaceId}/folder/${originalFolderId}`);
+    }
+  }

Also applies to: 98-123


89-91: Avoid revalidating /folder/null when moving to root

When folderId is null (moving a video out to the root), this builds /dashboard/spaces/${spaceId}/folder/null, so the real root view stays stale. Only revalidate the folder page when folderId exists, and fall back to the space root otherwise.

-  if (spaceId) {
-    revalidatePath(`/dashboard/spaces/${spaceId}/folder/${folderId}`);
-  }
+  if (spaceId && folderId) {
+    revalidatePath(`/dashboard/spaces/${spaceId}/folder/${folderId}`);
+  } else if (spaceId) {
+    revalidatePath(`/dashboard/spaces/${spaceId}`);
+  }
apps/web/app/(org)/dashboard/caps/components/Folder.tsx (1)

262-262: Align spaceId fallback in desktop drag handler
In apps/web/app/(org)/dashboard/caps/components/Folder.tsx line 262, replace

await moveVideoToFolder({ videoId: capData.id, folderId: id, spaceId });

with

await moveVideoToFolder({
  videoId: capData.id,
  folderId: id,
  spaceId: spaceId ?? activeOrganization?.organization.id,
});
♻️ Duplicate comments (1)
apps/web/app/(org)/dashboard/folder/[id]/page.tsx (1)

21-25: Drop spaceId from the plain folder route params

/dashboard/folder/[id] never receives a spaceId, so the new required field is always undefined at runtime. That value now flows into BreadcrumbItem (which builds space-aware links) and produces spaces/undefined/... URLs. This was flagged earlier and still needs to be removed—please revert the param/type change and stop forwarding spaceId here.

Also applies to: 52-55

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c0ca54 and 2e4203d.

📒 Files selected for processing (5)
  • apps/web/actions/folders/moveVideoToFolder.ts (3 hunks)
  • apps/web/actions/spaces/remove-videos.ts (2 hunks)
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx (2 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/ClientMyCapsLink.tsx (4 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(org)/dashboard/folder/[id]/components/ClientMyCapsLink.tsx
🧰 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/spaces/remove-videos.ts
  • apps/web/actions/folders/moveVideoToFolder.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/spaces/remove-videos.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/caps/components/Folder.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/spaces/remove-videos.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/caps/components/Folder.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/spaces/remove-videos.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/caps/components/Folder.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/spaces/remove-videos.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/caps/components/Folder.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/spaces/remove-videos.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/caps/components/Folder.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/folder/[id]/page.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx
🧬 Code graph analysis (4)
apps/web/actions/spaces/remove-videos.ts (2)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (4)
  • sharedVideos (334-362)
  • folders (249-275)
  • videos (277-332)
  • spaceVideos (631-651)
apps/web/app/(org)/dashboard/folder/[id]/page.tsx (2)
packages/web-domain/src/Folder.ts (3)
  • Folder (37-45)
  • FolderId (11-11)
  • FolderId (12-12)
apps/web/lib/folder.ts (2)
  • getChildFolders (278-314)
  • getVideosByFolderId (157-276)
apps/web/actions/folders/moveVideoToFolder.ts (3)
packages/web-domain/src/Folder.ts (1)
  • Folder (37-45)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (3)
  • spaceVideos (631-651)
  • sharedVideos (334-362)
  • videos (277-332)
apps/web/app/(org)/dashboard/caps/components/Folder.tsx (2)
packages/web-domain/src/Space.ts (2)
  • SpaceIdOrOrganisationId (7-7)
  • SpaceIdOrOrganisationId (8-8)
packages/web-domain/src/Folder.ts (3)
  • Folder (37-45)
  • FolderId (11-11)
  • FolderId (12-12)
⏰ 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). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (1)
apps/web/app/(org)/dashboard/caps/components/Folder.tsx (1)

24-25: Type definitions correctly align with domain model.

The changes to spaceId and parentId types are appropriate:

  • spaceId is now optional and nullable with a more specific branded type (Space.SpaceIdOrOrganisationId), matching the domain's Schema.OptionFromNullOr pattern
  • parentId uses the branded Folder.FolderId type and is required but nullable, providing clearer semantics than optional

These changes resolve the previous review comment about type inconsistency.

Comment on lines 91 to 112
const folderRows = await db()
.select({ id: folders.id })
.from(folders)
.where(
and(
inArray(videos.id, validVideoIds),
inArray(videos.folderId, folderIds),
isNull(folders.spaceId),
eq(folders.organizationId, user.activeOrganizationId),
),
);

const folderIds = folderRows.map((f) => f.id);

if (folderIds.length > 0) {
await db()
.update(videos)
.set({ folderId: null })
.where(
and(
inArray(videos.id, validVideoIds),
inArray(videos.folderId, folderIds),
),
);
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 | 🔴 Critical

Fix folder scope for space removals

This branch is supposed to clear folder assignments for videos in folders belonging to the target space, but the query still filters folders.spaceId IS NULL. That only returns org-level folders, so space folders never get cleared and removed videos stay linked to the space’s folders. Replace the filter with eq(folders.spaceId, spaceId) to target the correct folders before updating the videos table.

🤖 Prompt for AI Agents
In apps/web/actions/spaces/remove-videos.ts around lines 91 to 112, the folder
query incorrectly filters for folders.spaceId IS NULL (org-level folders)
instead of the target space’s folders; change the where clause to filter
eq(folders.spaceId, spaceId) so folderRows contains folders for the given space,
then proceed to map ids and update videos.folderId to null for videos whose
folderId is in that list and whose id is in validVideoIds.

Comment on lines +33 to +35
getVideosByFolderId(params.id, {
variant: "user",
}),
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 | 🔴 Critical

Fetch organization-scoped videos instead of user variant

This page lives under the org dashboard, so the folder contents now live in sharedVideos. Leaving the call on the "user" variant means any org/“All spaces” move just updated sharedVideos but the UI keeps querying videos.folderId, so the folder appears empty/mis-synced. Please switch to the org variant and pass the active organization id.

-      getVideosByFolderId(params.id, {
-        variant: "user",
-      }),
+      getVideosByFolderId(params.id, {
+        variant: "org",
+        organizationId: user.activeOrganizationId,
+      }),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getVideosByFolderId(params.id, {
variant: "user",
}),
getVideosByFolderId(params.id, {
variant: "org",
organizationId: user.activeOrganizationId,
}),
🤖 Prompt for AI Agents
In apps/web/app/(org)/dashboard/folder/[id]/page.tsx around lines 33 to 35, the
call to getVideosByFolderId is using variant: "user" which queries user-scoped
videos; update it to variant: "org" and pass the active organization id (e.g.,
organization.id or activeOrgId from context/props) so the function queries
sharedVideos for the org scope and returns the folder contents for the current
organization.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/actions/folders/moveVideoToFolder.ts (1)

89-91: Guard revalidation path for organization entries

Wrap the revalidatePath call so that when isAllSpacesEntry is true you revalidate /dashboard/spaces/${spaceId} (as in add/remove-videos) instead of /dashboard/spaces/${spaceId}/folder/${folderId}; for actual spaces, keep the current folder-specific path.

🧹 Nitpick comments (1)
apps/web/actions/folders/moveVideoToFolder.ts (1)

37-37: Document the isAllSpacesEntry business logic.

The flag isAllSpacesEntry checks if spaceId === user.activeOrganizationId, treating the organization ID as a special "all spaces" scope. While this aligns with the SpaceIdOrOrganisationId union type and the PR's org/space-aware design, the implicit overloading of the organization ID within a space-focused parameter could be confusing.

Consider adding a brief inline explanation of why an organization ID in the spaceId parameter indicates "all spaces" or organization-wide context, or refactor to use a more explicit parameter (e.g., scope: 'space' | 'organization').

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c0ca54 and 031e0ea.

📒 Files selected for processing (6)
  • apps/web/actions/folders/moveVideoToFolder.ts (3 hunks)
  • apps/web/actions/spaces/remove-videos.ts (2 hunks)
  • apps/web/app/(org)/dashboard/caps/components/Folder.tsx (2 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx (4 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/ClientMyCapsLink.tsx (4 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/app/(org)/dashboard/folder/[id]/components/BreadcrumbItem.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/components/ClientMyCapsLink.tsx
  • apps/web/actions/spaces/remove-videos.ts
🧰 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/caps/components/Folder.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/Folder.tsx
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/caps/components/Folder.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/caps/components/Folder.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/caps/components/Folder.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.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/caps/components/Folder.tsx
  • apps/web/actions/folders/moveVideoToFolder.ts
  • apps/web/app/(org)/dashboard/folder/[id]/page.tsx
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/moveVideoToFolder.ts
🧬 Code graph analysis (3)
apps/web/app/(org)/dashboard/caps/components/Folder.tsx (2)
packages/web-domain/src/Space.ts (2)
  • SpaceIdOrOrganisationId (7-7)
  • SpaceIdOrOrganisationId (8-8)
packages/web-domain/src/Folder.ts (3)
  • Folder (37-45)
  • FolderId (11-11)
  • FolderId (12-12)
apps/web/actions/folders/moveVideoToFolder.ts (2)
packages/web-domain/src/Folder.ts (1)
  • Folder (37-45)
packages/database/schema.ts (3)
  • spaceVideos (631-651)
  • sharedVideos (334-362)
  • videos (277-332)
apps/web/app/(org)/dashboard/folder/[id]/page.tsx (1)
apps/web/lib/folder.ts (1)
  • getVideosByFolderId (157-276)
⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (4)
apps/web/app/(org)/dashboard/folder/[id]/page.tsx (2)

29-31: LGTM! Correct variant usage for user-scoped folder.

The call to getVideosByFolderId now correctly passes the { variant: "user" } option, matching the updated API signature and properly indicating user-scoped folder access.


1-89: AI summary inconsistent with actual code.

The AI-generated summary claims this file:

  • "accepts and uses spaceId of type Space.SpaceIdOrOrganisationId"
  • "renders AddVideosButton and passes spaceId to it"
  • "Breadcrumb and client link components are updated to receive and propagate spaceId"

However, the actual code shows:

  • Line 21: params only contains id: Folder.FolderId (no spaceId)
  • No AddVideosButton import or usage anywhere in the file
  • No spaceId props passed to ClientMyCapsLink, BreadcrumbItem, or FolderCard

The only actual change is lines 29-31 where getVideosByFolderId now receives the { variant: "user" } option.

apps/web/app/(org)/dashboard/caps/components/Folder.tsx (1)

24-25: Type alignment looks good; verify parentId requirement.

The spaceId type change to Space.SpaceIdOrOrganisationId | null (optional) correctly addresses the previous review concern and aligns with its usage throughout the component.

However, note that parentId is now typed as required (Folder.FolderId | null) while the domain schema defines it as optional (Schema.OptionFromNullOr(FolderId)). This means the component type requires parentId to always be provided (though it can be null), whereas the domain allows it to be omitted entirely. Verify this is intentional—if folders data can sometimes lack a parentId property, the component type should be parentId?: Folder.FolderId | null to match.

apps/web/actions/folders/moveVideoToFolder.ts (1)

62-64: LGTM: Conditional table updates correctly implement space/org-aware logic.

The three-branch conditional correctly routes updates to the appropriate table:

  • spaceVideos for space-specific contexts
  • sharedVideos for organization-wide contexts
  • videos for user-scoped contexts

The where clauses properly scope updates using both videoId and the relevant ID (spaceId or organizationId), preventing unintended cross-scope modifications.

Also applies to: 72-75, 79-83

Comment on lines +56 to 84
if (spaceId && !isAllSpacesEntry) {
await db()
.update(spaceVideos)
.set({
folderId: folderId === null ? null : folderId,
})
.where(eq(spaceVideos.videoId, videoId));
.where(
and(eq(spaceVideos.videoId, videoId), eq(spaceVideos.spaceId, spaceId)),
);
} else if (spaceId && isAllSpacesEntry) {
await db()
.update(sharedVideos)
.set({
folderId: folderId === null ? null : folderId,
})
.where(
and(
eq(sharedVideos.videoId, videoId),
eq(sharedVideos.organizationId, user.activeOrganizationId),
),
);
} else {
await db()
.update(videos)
.set({
folderId: folderId === null ? null : folderId,
})
.where(eq(videos.id, videoId));
}
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 | 🔴 Critical

🧩 Analysis chain

Verify spaceId belongs to the user's organization.

The function branches between updating spaceVideos, sharedVideos, or videos based on the spaceId parameter, but it does not verify that the provided spaceId actually belongs to the user's active organization. If a caller passes a spaceId from a different organization, the function could update records in an unintended scope.

Add a verification step after line 25 to ensure the spaceId is valid:


🏁 Script executed:

#!/bin/bash
# Check if there's existing validation for spaceId in related actions

rg -nP --type=ts -C3 'spaceId.*activeOrganizationId' apps/web/actions/

Length of output: 1055


Add validation for spaceId ownership
Before branching (after the isAllSpacesEntry assignment), if spaceId is non-null and not equal to user.activeOrganizationId, query the spaces table for { id: spaceId, organizationId: user.activeOrganizationId } and throw NotFoundError if no match.

🤖 Prompt for AI Agents
In apps/web/actions/folders/moveVideoToFolder.ts around lines 56 to 84, after
the isAllSpacesEntry assignment add validation that if spaceId is non-null and
spaceId !== user.activeOrganizationId you must query the spaces table for a row
matching { id: spaceId, organizationId: user.activeOrganizationId } and throw
NotFoundError if no match is found; place this check before the existing
branching that updates spaceVideos/sharedVideos/videos so
unauthorized/mismatched spaceIds are rejected early.

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/actions/spaces/add-videos.ts (1)

39-116: Consider extracting shared logic to reduce duplication.

The organization-wide (lines 39-76) and space-specific (lines 77-116) branches contain nearly identical logic: querying existing entries, nulling folderId, and inserting new entries. This duplication increases maintenance burden.

Consider refactoring to a shared helper function:

async function addVideosToContext(
  table: typeof sharedVideos | typeof spaceVideos,
  contextId: string,
  contextIdField: 'organizationId' | 'spaceId',
  validVideoIds: Video.VideoId[],
  userId: User.UserId
) {
  const existingVideos = await db()
    .select({ videoId: table.videoId })
    .from(table)
    .where(
      and(
        eq(table[contextIdField], contextId),
        inArray(table.videoId, validVideoIds)
      )
    );

  const existingVideoIds = existingVideos.map(v => v.videoId);
  const newVideoIds = validVideoIds.filter(id => !existingVideoIds.includes(id));

  if (existingVideoIds.length > 0) {
    await db()
      .update(table)
      .set({ folderId: null })
      .where(
        and(
          eq(table[contextIdField], contextId),
          inArray(table.videoId, existingVideoIds)
        )
      );
  }

  if (newVideoIds.length > 0) {
    const entries = newVideoIds.map(videoId => ({
      id: nanoId(),
      videoId,
      [contextIdField]: contextId,
      ...(table === sharedVideos 
        ? { sharedByUserId: userId } 
        : { addedById: userId })
    }));
    await db().insert(table).values(entries);
  }
}

Then use it in both branches:

if (isAllSpacesEntry) {
  await addVideosToContext(
    sharedVideos,
    spaceId,
    'organizationId',
    validVideoIds,
    user.id
  );
} else {
  await addVideosToContext(
    spaceVideos,
    spaceId,
    'spaceId',
    validVideoIds,
    user.id
  );
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 031e0ea and 96d18f2.

📒 Files selected for processing (2)
  • apps/web/actions/spaces/add-videos.ts (3 hunks)
  • apps/web/actions/spaces/remove-videos.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
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/spaces/add-videos.ts
  • apps/web/actions/spaces/remove-videos.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/spaces/add-videos.ts
  • apps/web/actions/spaces/remove-videos.ts
**/*.{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/spaces/add-videos.ts
  • apps/web/actions/spaces/remove-videos.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/actions/spaces/add-videos.ts
  • apps/web/actions/spaces/remove-videos.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/actions/spaces/add-videos.ts
  • apps/web/actions/spaces/remove-videos.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/actions/spaces/add-videos.ts
  • apps/web/actions/spaces/remove-videos.ts
🧬 Code graph analysis (2)
apps/web/actions/spaces/add-videos.ts (3)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (2)
  • sharedVideos (334-362)
  • spaceVideos (631-651)
packages/database/helpers.ts (1)
  • nanoId (6-9)
apps/web/actions/spaces/remove-videos.ts (2)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (2)
  • sharedVideos (334-362)
  • spaceVideos (631-651)
⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (6)
apps/web/actions/spaces/add-videos.ts (3)

125-126: LGTM!

Proper error logging and graceful error handling with informative messages.


55-65: Verify intended behavior of clearing folderId when adding existing videos
The code nulls folderId for existing videos, moving them to the root. Confirm if this is the desired behavior or if existing videos should be skipped or preserved in their current folders.
Also applies to apps/web/actions/organizations/add-videos.ts (lines 94–104).


26-26: Ensure organization-wide entry detection is unambiguous.

Relying on user.activeOrganizationId === spaceId to detect the “all spaces” context risks misclassification if a space ID ever equals an organization ID; confirm that these IDs are generated in disjoint namespaces or introduce an explicit mode flag instead.

apps/web/actions/spaces/remove-videos.ts (3)

42-63: LGTM! Clean deletion approach.

The logic correctly branches between organization-level and space-level removals. By deleting rows from sharedVideos or spaceVideos, you remove both the space/org membership and any folder associations in a single operation, which is cleaner than the previous approach of nulling folderId separately.


72-79: LGTM! Proper error handling pattern.

The change to use a generic catch with an instanceof Error check follows TypeScript best practices for handling unknown error types.


15-40: Verify space membership before removal.
The function ensures the user owns the videos but does not check they belong to the target space when deleting from spaceVideos. Confirm whether space-membership should be enforced.

@ameer2468 ameer2468 merged commit 10a7f5a into main Oct 9, 2025
14 of 16 checks passed
@ameer2468 ameer2468 deleted the add-videos-button-in-folders branch October 9, 2025 12:44
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