diff --git a/apps/web/actions/caps/share.ts b/apps/web/actions/caps/share.ts index de025712d1..707e0e4d65 100644 --- a/apps/web/actions/caps/share.ts +++ b/apps/web/actions/caps/share.ts @@ -11,13 +11,13 @@ import { spaceVideos, videos, } from "@cap/database/schema"; -import type { Video } from "@cap/web-domain"; +import type { Organisation, Space, Video } from "@cap/web-domain"; import { and, eq, inArray } from "drizzle-orm"; import { revalidatePath } from "next/cache"; interface ShareCapParams { capId: Video.VideoId; - spaceIds: string[]; + spaceIds: Space.SpaceIdOrOrganisationId[]; public?: boolean; } @@ -53,7 +53,7 @@ export async function shareCap({ .from(organizations) .where( and( - inArray(organizations.id, spaceIds), + inArray(organizations.id, spaceIds as Organisation.OrganisationId[]), inArray(organizations.id, userOrganizationIds), ), ) diff --git a/apps/web/actions/organization/check-domain.ts b/apps/web/actions/organization/check-domain.ts index 93a57282c7..c7041c6439 100644 --- a/apps/web/actions/organization/check-domain.ts +++ b/apps/web/actions/organization/check-domain.ts @@ -3,10 +3,13 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { organizations } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { checkDomainStatus } from "./domain-utils"; -export async function checkOrganizationDomain(organizationId: string) { +export async function checkOrganizationDomain( + organizationId: Organisation.OrganisationId, +) { const user = await getCurrentUser(); if (!user || !organizationId) { diff --git a/apps/web/actions/organization/create-space.ts b/apps/web/actions/organization/create-space.ts index 552344f1a4..d50b193484 100644 --- a/apps/web/actions/organization/create-space.ts +++ b/apps/web/actions/organization/create-space.ts @@ -6,6 +6,7 @@ import { nanoId, nanoIdLength } from "@cap/database/helpers"; import { spaceMembers, spaces, users } from "@cap/database/schema"; import { serverEnv } from "@cap/env"; import { S3Buckets } from "@cap/web-backend"; +import { Space } from "@cap/web-domain"; import { and, eq, inArray } from "drizzle-orm"; import { Effect, Option } from "effect"; import { revalidatePath } from "next/cache"; @@ -62,7 +63,7 @@ export async function createSpace( } // Generate the space ID early so we can use it in the file path - const spaceId = nanoId(); + const spaceId = Space.SpaceId.make(nanoId()); const iconFile = formData.get("icon") as File | null; let iconUrl = null; diff --git a/apps/web/actions/organization/delete-space.ts b/apps/web/actions/organization/delete-space.ts index 066222621b..406474c888 100644 --- a/apps/web/actions/organization/delete-space.ts +++ b/apps/web/actions/organization/delete-space.ts @@ -9,6 +9,7 @@ import { spaceVideos, } from "@cap/database/schema"; import { S3Buckets } from "@cap/web-backend"; +import type { Space } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { Effect, Option } from "effect"; import { revalidatePath } from "next/cache"; @@ -20,7 +21,7 @@ interface DeleteSpaceResponse { } export async function deleteSpace( - spaceId: string, + spaceId: Space.SpaceIdOrOrganisationId, ): Promise { try { const user = await getCurrentUser(); diff --git a/apps/web/actions/organization/get-organization-sso-data.ts b/apps/web/actions/organization/get-organization-sso-data.ts index 2340d86077..00b42981bf 100644 --- a/apps/web/actions/organization/get-organization-sso-data.ts +++ b/apps/web/actions/organization/get-organization-sso-data.ts @@ -2,9 +2,12 @@ import { db } from "@cap/database"; import { organizations } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { eq } from "drizzle-orm"; -export async function getOrganizationSSOData(organizationId: string) { +export async function getOrganizationSSOData( + organizationId: Organisation.OrganisationId, +) { if (!organizationId) { throw new Error("Organization ID is required"); } diff --git a/apps/web/actions/organization/remove-domain.ts b/apps/web/actions/organization/remove-domain.ts index 9085875431..1f4cdd97a3 100644 --- a/apps/web/actions/organization/remove-domain.ts +++ b/apps/web/actions/organization/remove-domain.ts @@ -3,10 +3,13 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { organizations } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; -export async function removeOrganizationDomain(organizationId: string) { +export async function removeOrganizationDomain( + organizationId: Organisation.OrganisationId, +) { const user = await getCurrentUser(); if (!user) { diff --git a/apps/web/actions/organization/remove-icon.ts b/apps/web/actions/organization/remove-icon.ts index 7efa19fb2e..6eba5818ce 100644 --- a/apps/web/actions/organization/remove-icon.ts +++ b/apps/web/actions/organization/remove-icon.ts @@ -3,10 +3,13 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { organizations } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; -export async function removeOrganizationIcon(organizationId: string) { +export async function removeOrganizationIcon( + organizationId: Organisation.OrganisationId, +) { const user = await getCurrentUser(); if (!user) { diff --git a/apps/web/actions/organization/remove-invite.ts b/apps/web/actions/organization/remove-invite.ts index 4e06234f18..a9039cdcab 100644 --- a/apps/web/actions/organization/remove-invite.ts +++ b/apps/web/actions/organization/remove-invite.ts @@ -3,12 +3,13 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { organizationInvites, organizations } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { and, eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; export async function removeOrganizationInvite( inviteId: string, - organizationId: string, + organizationId: Organisation.OrganisationId, ) { const user = await getCurrentUser(); diff --git a/apps/web/actions/organization/remove-member.ts b/apps/web/actions/organization/remove-member.ts index 82e3a097c3..f7cfc6a134 100644 --- a/apps/web/actions/organization/remove-member.ts +++ b/apps/web/actions/organization/remove-member.ts @@ -3,6 +3,7 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { organizationMembers, organizations } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { and, eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; @@ -13,7 +14,7 @@ import { revalidatePath } from "next/cache"; */ export async function removeOrganizationMember( memberId: string, - organizationId: string, + organizationId: Organisation.OrganisationId, ) { const user = await getCurrentUser(); if (!user) throw new Error("Unauthorized"); diff --git a/apps/web/actions/organization/send-invites.ts b/apps/web/actions/organization/send-invites.ts index 3532fde120..183a115b24 100644 --- a/apps/web/actions/organization/send-invites.ts +++ b/apps/web/actions/organization/send-invites.ts @@ -7,12 +7,13 @@ import { OrganizationInvite } from "@cap/database/emails/organization-invite"; import { nanoId } from "@cap/database/helpers"; import { organizationInvites, organizations } from "@cap/database/schema"; import { serverEnv } from "@cap/env"; +import type { Organisation } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; export async function sendOrganizationInvites( invitedEmails: string[], - organizationId: string, + organizationId: Organisation.OrganisationId, ) { const user = await getCurrentUser(); diff --git a/apps/web/actions/organization/update-details.ts b/apps/web/actions/organization/update-details.ts index 0422708057..0156e2654c 100644 --- a/apps/web/actions/organization/update-details.ts +++ b/apps/web/actions/organization/update-details.ts @@ -3,6 +3,7 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { organizations } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; @@ -13,7 +14,7 @@ export async function updateOrganizationDetails({ }: { organizationName?: string | null; allowedEmailDomain?: string | null; - organizationId: string; + organizationId: Organisation.OrganisationId; }) { const user = await getCurrentUser(); diff --git a/apps/web/actions/organization/update-domain.ts b/apps/web/actions/organization/update-domain.ts index dd109e7f13..20c9d2914e 100644 --- a/apps/web/actions/organization/update-domain.ts +++ b/apps/web/actions/organization/update-domain.ts @@ -3,11 +3,15 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { organizations } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { addDomain, checkDomainStatus } from "./domain-utils"; -export async function updateDomain(domain: string, organizationId: string) { +export async function updateDomain( + domain: string, + organizationId: Organisation.OrganisationId, +) { const user = await getCurrentUser(); if (!user) { diff --git a/apps/web/actions/organization/update-space.ts b/apps/web/actions/organization/update-space.ts index ab6237d0bd..0fe9743873 100644 --- a/apps/web/actions/organization/update-space.ts +++ b/apps/web/actions/organization/update-space.ts @@ -5,6 +5,7 @@ import { getCurrentUser } from "@cap/database/auth/session"; import { nanoIdLength } from "@cap/database/helpers"; import { spaceMembers, spaces } from "@cap/database/schema"; import { S3Buckets } from "@cap/web-backend"; +import { Space, type User } from "@cap/web-domain"; import { and, eq } from "drizzle-orm"; import { Effect, Option } from "effect"; import { revalidatePath } from "next/cache"; @@ -16,9 +17,9 @@ export async function updateSpace(formData: FormData) { const user = await getCurrentUser(); if (!user) return { success: false, error: "Unauthorized" }; - const id = formData.get("id") as string; + const id = Space.SpaceId.make(formData.get("id") as string); const name = formData.get("name") as string; - const members = formData.getAll("members[]") as string[]; + const members = formData.getAll("members[]") as User.UserId[]; const iconFile = formData.get("icon") as File | null; const [membership] = await db() diff --git a/apps/web/actions/organization/upload-organization-icon.ts b/apps/web/actions/organization/upload-organization-icon.ts index 485418da7c..3ab5989009 100644 --- a/apps/web/actions/organization/upload-organization-icon.ts +++ b/apps/web/actions/organization/upload-organization-icon.ts @@ -5,6 +5,7 @@ import { getCurrentUser } from "@cap/database/auth/session"; import { organizations } from "@cap/database/schema"; import { serverEnv } from "@cap/env"; import { S3Buckets } from "@cap/web-backend"; +import type { Organisation } from "@cap/web-domain"; import DOMPurify from "dompurify"; import { eq } from "drizzle-orm"; import { Effect, Option } from "effect"; @@ -15,7 +16,7 @@ import { runPromise } from "@/lib/server"; export async function uploadOrganizationIcon( formData: FormData, - organizationId: string, + organizationId: Organisation.OrganisationId, ) { const user = await getCurrentUser(); diff --git a/apps/web/actions/organization/upload-space-icon.ts b/apps/web/actions/organization/upload-space-icon.ts index e56c92b5a6..395d668026 100644 --- a/apps/web/actions/organization/upload-space-icon.ts +++ b/apps/web/actions/organization/upload-space-icon.ts @@ -5,13 +5,17 @@ import { getCurrentUser } from "@cap/database/auth/session"; import { spaces } from "@cap/database/schema"; import { serverEnv } from "@cap/env"; import { S3Buckets } from "@cap/web-backend"; +import type { Space } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { Effect, Option } from "effect"; import { revalidatePath } from "next/cache"; import { sanitizeFile } from "@/lib/sanitizeFile"; import { runPromise } from "@/lib/server"; -export async function uploadSpaceIcon(formData: FormData, spaceId: string) { +export async function uploadSpaceIcon( + formData: FormData, + spaceId: Space.SpaceId, +) { const user = await getCurrentUser(); if (!user) { diff --git a/apps/web/actions/organizations/add-videos.ts b/apps/web/actions/organizations/add-videos.ts index 3571238088..97c24fc84e 100644 --- a/apps/web/actions/organizations/add-videos.ts +++ b/apps/web/actions/organizations/add-videos.ts @@ -9,12 +9,12 @@ import { sharedVideos, videos, } from "@cap/database/schema"; -import type { Video } from "@cap/web-domain"; +import type { Organisation, Video } from "@cap/web-domain"; import { and, eq, inArray } from "drizzle-orm"; import { revalidatePath } from "next/cache"; export async function addVideosToOrganization( - organizationId: string, + organizationId: Organisation.OrganisationId, videoIds: Video.VideoId[], ) { try { diff --git a/apps/web/actions/organizations/get-organization-videos.ts b/apps/web/actions/organizations/get-organization-videos.ts index 1b847fa1d4..41431c3af1 100644 --- a/apps/web/actions/organizations/get-organization-videos.ts +++ b/apps/web/actions/organizations/get-organization-videos.ts @@ -3,9 +3,12 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { sharedVideos } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { eq } from "drizzle-orm"; -export async function getOrganizationVideoIds(organizationId: string) { +export async function getOrganizationVideoIds( + organizationId: Organisation.OrganisationId, +) { try { const user = await getCurrentUser(); diff --git a/apps/web/actions/organizations/remove-videos.ts b/apps/web/actions/organizations/remove-videos.ts index 307384fc39..0275857232 100644 --- a/apps/web/actions/organizations/remove-videos.ts +++ b/apps/web/actions/organizations/remove-videos.ts @@ -9,12 +9,12 @@ import { sharedVideos, videos, } from "@cap/database/schema"; -import type { Video } from "@cap/web-domain"; +import type { Organisation, Video } from "@cap/web-domain"; import { and, eq, inArray } from "drizzle-orm"; import { revalidatePath } from "next/cache"; export async function removeVideosFromOrganization( - organizationId: string, + organizationId: Organisation.OrganisationId, videoIds: Video.VideoId[], ) { try { diff --git a/apps/web/actions/spaces/add-videos.ts b/apps/web/actions/spaces/add-videos.ts index 0e5e77aec5..bbb5656816 100644 --- a/apps/web/actions/spaces/add-videos.ts +++ b/apps/web/actions/spaces/add-videos.ts @@ -4,12 +4,12 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { nanoId } from "@cap/database/helpers"; import { spaces, spaceVideos, videos } from "@cap/database/schema"; -import type { Video } from "@cap/web-domain"; +import type { Space, Video } from "@cap/web-domain"; import { and, eq, inArray } from "drizzle-orm"; import { revalidatePath } from "next/cache"; export async function addVideosToSpace( - spaceId: string, + spaceId: Space.SpaceIdOrOrganisationId, videoIds: Video.VideoId[], ) { try { diff --git a/apps/web/actions/spaces/get-space-videos.ts b/apps/web/actions/spaces/get-space-videos.ts index be72f9c5bb..7ebf0d61fe 100644 --- a/apps/web/actions/spaces/get-space-videos.ts +++ b/apps/web/actions/spaces/get-space-videos.ts @@ -3,9 +3,10 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { spaceVideos } from "@cap/database/schema"; +import type { Space } from "@cap/web-domain"; import { eq } from "drizzle-orm"; -export async function getSpaceVideoIds(spaceId: string) { +export async function getSpaceVideoIds(spaceId: Space.SpaceIdOrOrganisationId) { try { const user = await getCurrentUser(); diff --git a/apps/web/actions/spaces/remove-videos.ts b/apps/web/actions/spaces/remove-videos.ts index 0f252bfc9d..dcf39fb264 100644 --- a/apps/web/actions/spaces/remove-videos.ts +++ b/apps/web/actions/spaces/remove-videos.ts @@ -3,12 +3,12 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { folders, spaceVideos, videos } from "@cap/database/schema"; -import type { Video } from "@cap/web-domain"; +import type { Space, Video } from "@cap/web-domain"; import { and, eq, inArray } from "drizzle-orm"; import { revalidatePath } from "next/cache"; export async function removeVideosFromSpace( - spaceId: string, + spaceId: Space.SpaceIdOrOrganisationId, videoIds: Video.VideoId[], ) { try { diff --git a/apps/web/actions/video/upload.ts b/apps/web/actions/video/upload.ts index 87a3481b65..c2482120dd 100644 --- a/apps/web/actions/video/upload.ts +++ b/apps/web/actions/video/upload.ts @@ -11,7 +11,7 @@ import { s3Buckets, videos, videoUploads } from "@cap/database/schema"; import { buildEnv, NODE_ENV, serverEnv } from "@cap/env"; import { userIsPro } from "@cap/utils"; import { S3Buckets } from "@cap/web-backend"; -import { type Folder, Video } from "@cap/web-domain"; +import { type Folder, type Organisation, Video } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { Effect, Option } from "effect"; import { revalidatePath } from "next/cache"; @@ -175,7 +175,7 @@ export async function createVideoAndGetUploadUrl({ isScreenshot?: boolean; isUpload?: boolean; folderId?: Folder.FolderId; - orgId: string; + orgId: Organisation.OrganisationId; // TODO: Remove this once we are happy with it's stability supportsUploadProgress?: boolean; }) { diff --git a/apps/web/actions/videos/delete-comment.ts b/apps/web/actions/videos/delete-comment.ts index 1cfb069321..8451ecc178 100644 --- a/apps/web/actions/videos/delete-comment.ts +++ b/apps/web/actions/videos/delete-comment.ts @@ -3,6 +3,7 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { comments, notifications } from "@cap/database/schema"; +import type { Comment, Video } from "@cap/web-domain"; import { and, eq, sql } from "drizzle-orm"; import { revalidatePath } from "next/cache"; @@ -11,9 +12,9 @@ export async function deleteComment({ parentId, videoId, }: { - commentId: string; - parentId?: string; - videoId: string; + commentId: Comment.CommentId; + parentId?: Comment.CommentId; + videoId: Video.VideoId; }) { const user = await getCurrentUser(); diff --git a/apps/web/actions/videos/new-comment.ts b/apps/web/actions/videos/new-comment.ts index 85a4466f7b..f12729c3dd 100644 --- a/apps/web/actions/videos/new-comment.ts +++ b/apps/web/actions/videos/new-comment.ts @@ -4,7 +4,7 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { nanoId } from "@cap/database/helpers"; import { comments } from "@cap/database/schema"; -import type { Video } from "@cap/web-domain"; +import { Comment, type Video } from "@cap/web-domain"; import { revalidatePath } from "next/cache"; import { createNotification } from "@/lib/Notification"; @@ -12,7 +12,7 @@ export async function newComment(data: { content: string; videoId: Video.VideoId; type: "text" | "emoji"; - parentCommentId: string; + parentCommentId: Comment.CommentId; timestamp: number; }) { const user = await getCurrentUser(); @@ -35,7 +35,7 @@ export async function newComment(data: { if (!content || !videoId) { throw new Error("Content and videoId are required"); } - const id = nanoId(); + const id = Comment.CommentId.make(nanoId()); const newComment = { id: id, diff --git a/apps/web/app/(org)/dashboard/_components/Navbar/SpacesList.tsx b/apps/web/app/(org)/dashboard/_components/Navbar/SpacesList.tsx index ad77e09ce7..69bd0541d1 100644 --- a/apps/web/app/(org)/dashboard/_components/Navbar/SpacesList.tsx +++ b/apps/web/app/(org)/dashboard/_components/Navbar/SpacesList.tsx @@ -1,6 +1,7 @@ "use client"; import { Avatar, Button } from "@cap/ui"; +import type { Space } from "@cap/web-domain"; import { faLayerGroup, faPlus, @@ -91,7 +92,10 @@ const SpacesList = ({ toggleMobileNav }: { toggleMobileNav?: () => void }) => { setActiveDropTarget(null); }; - const handleDrop = async (e: React.DragEvent, spaceId: string) => { + const handleDrop = async ( + e: React.DragEvent, + spaceId: Space.SpaceIdOrOrganisationId, + ) => { e.preventDefault(); setActiveDropTarget(null); @@ -120,7 +124,8 @@ const SpacesList = ({ toggleMobileNav }: { toggleMobileNav?: () => void }) => { } }; - const activeSpaceParams = (spaceId: string) => params.spaceId === spaceId; + const activeSpaceParams = (spaceId: Space.SpaceIdOrOrganisationId) => + params.spaceId === spaceId; return (
diff --git a/apps/web/app/(org)/dashboard/_components/Navbar/server.ts b/apps/web/app/(org)/dashboard/_components/Navbar/server.ts index 419eb5e8c5..83109fd149 100644 --- a/apps/web/app/(org)/dashboard/_components/Navbar/server.ts +++ b/apps/web/app/(org)/dashboard/_components/Navbar/server.ts @@ -7,12 +7,15 @@ import { organizations, users, } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { and, eq } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { createSpace as createSpaceAction } from "@/actions/organization/create-space"; import { updateSpace as updateSpaceAction } from "@/actions/organization/update-space"; -export async function updateActiveOrganization(organizationId: string) { +export async function updateActiveOrganization( + organizationId: Organisation.OrganisationId, +) { const user = await getCurrentUser(); if (!user) throw new Error("Unauthorized"); diff --git a/apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx b/apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx index ef7dfe3c60..6565de9b7b 100644 --- a/apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx +++ b/apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx @@ -9,7 +9,7 @@ import { DialogTitle, Input, } from "@cap/ui"; -import type { Folder } from "@cap/web-domain"; +import type { Folder, Space } from "@cap/web-domain"; import { faFolderPlus } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { type RiveFile, useRiveFile } from "@rive-app/react-canvas"; @@ -31,7 +31,7 @@ import { interface Props { open: boolean; onOpenChange: (open: boolean) => void; - spaceId?: string; + spaceId?: Space.SpaceIdOrOrganisationId; } const FolderOptions = [ diff --git a/apps/web/app/(org)/dashboard/caps/components/SharingDialog.tsx b/apps/web/app/(org)/dashboard/caps/components/SharingDialog.tsx index 4eb32644f4..b3d7c0ca57 100644 --- a/apps/web/app/(org)/dashboard/caps/components/SharingDialog.tsx +++ b/apps/web/app/(org)/dashboard/caps/components/SharingDialog.tsx @@ -9,7 +9,7 @@ import { Input, Switch, } from "@cap/ui"; -import type { Video } from "@cap/web-domain"; +import { Space, type Video } from "@cap/web-domain"; import { faCopy, faShareNodes } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useMutation } from "@tanstack/react-query"; @@ -69,7 +69,7 @@ export const SharingDialog: React.FC = ({ public: isPublic, }: { capId: Video.VideoId; - spaceIds: string[]; + spaceIds: Space.SpaceIdOrOrganisationId[]; public: boolean; }) => { const result = await shareCap({ capId, spaceIds, public: isPublic }); @@ -358,7 +358,9 @@ export const SharingDialog: React.FC = ({ onClick={() => updateSharing.mutate({ capId, - spaceIds: Array.from(selectedSpaces), + spaceIds: Array.from(selectedSpaces).map((v) => + Space.SpaceId.make(v), + ), public: publicToggle, }) } diff --git a/apps/web/app/(org)/dashboard/caps/components/UploadCapButton.tsx b/apps/web/app/(org)/dashboard/caps/components/UploadCapButton.tsx index 085d0dfb96..638f546520 100644 --- a/apps/web/app/(org)/dashboard/caps/components/UploadCapButton.tsx +++ b/apps/web/app/(org)/dashboard/caps/components/UploadCapButton.tsx @@ -2,7 +2,7 @@ import { Button } from "@cap/ui"; import { userIsPro } from "@cap/utils"; -import type { Folder } from "@cap/web-domain"; +import type { Folder, Organisation } from "@cap/web-domain"; import { faUpload } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { type QueryClient, useQueryClient } from "@tanstack/react-query"; @@ -100,7 +100,7 @@ export const UploadCapButton = ({ async function legacyUploadCap( file: File, folderId: Folder.FolderId | undefined, - orgId: string, + orgId: Organisation.OrganisationId, setUploadStatus: (state: UploadStatus | undefined) => void, queryClient: QueryClient, ) { diff --git a/apps/web/app/(org)/dashboard/settings/account/Settings.tsx b/apps/web/app/(org)/dashboard/settings/account/Settings.tsx index 384c78438b..6c8dc3ade8 100644 --- a/apps/web/app/(org)/dashboard/settings/account/Settings.tsx +++ b/apps/web/app/(org)/dashboard/settings/account/Settings.tsx @@ -9,6 +9,7 @@ import { Input, Select, } from "@cap/ui"; +import { Organisation } from "@cap/web-domain"; import { useMutation } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; @@ -25,9 +26,9 @@ export const Settings = ({ const { organizationData } = useDashboardContext(); const [firstName, setFirstName] = useState(user?.name || ""); const [lastName, setLastName] = useState(user?.lastName || ""); - const [defaultOrgId, setDefaultOrgId] = useState( - user?.defaultOrgId || undefined, - ); + const [defaultOrgId, setDefaultOrgId] = useState< + Organisation.OrganisationId | undefined + >(user?.defaultOrgId || undefined); // Track if form has unsaved changes const hasChanges = @@ -131,7 +132,9 @@ export const Settings = ({ organizationData?.[0]?.organization.id ?? "" } - onValueChange={(value) => setDefaultOrgId(value)} + onValueChange={(value) => + setDefaultOrgId(Organisation.OrganisationId.make(value)) + } options={(organizationData || []).map((org) => ({ value: org.organization.id, label: org.organization.name, diff --git a/apps/web/app/(org)/dashboard/settings/account/server.ts b/apps/web/app/(org)/dashboard/settings/account/server.ts index b0a8bceaee..9f47b8a7a0 100644 --- a/apps/web/app/(org)/dashboard/settings/account/server.ts +++ b/apps/web/app/(org)/dashboard/settings/account/server.ts @@ -7,13 +7,14 @@ import { organizations, users, } from "@cap/database/schema"; +import type { Organisation } from "@cap/web-domain"; import { eq, or } from "drizzle-orm"; import { revalidatePath } from "next/cache"; export async function patchAccountSettings( firstName?: string, lastName?: string, - defaultOrgId?: string, + defaultOrgId?: Organisation.OrganisationId, ) { const currentUser = await getCurrentUser(); if (!currentUser) throw new Error("Unauthorized"); @@ -21,7 +22,7 @@ export async function patchAccountSettings( const updatePayload: Partial<{ name: string; lastName: string; - defaultOrgId: string; + defaultOrgId: Organisation.OrganisationId; }> = {}; if (firstName !== undefined) updatePayload.name = firstName; diff --git a/apps/web/app/(org)/dashboard/settings/organization/components/AccessEmailDomain.tsx b/apps/web/app/(org)/dashboard/settings/organization/components/AccessEmailDomain.tsx index b00c3ecd96..725c3cddf6 100644 --- a/apps/web/app/(org)/dashboard/settings/organization/components/AccessEmailDomain.tsx +++ b/apps/web/app/(org)/dashboard/settings/organization/components/AccessEmailDomain.tsx @@ -1,4 +1,5 @@ import { Button, Input, Label } from "@cap/ui"; +import type { Organisation } from "@cap/web-domain"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { toast } from "sonner"; @@ -18,7 +19,8 @@ export const AccessEmailDomain = () => { setSaveLoading(true); await updateOrganizationDetails({ allowedEmailDomain: emailDomain, - organizationId: activeOrganization?.organization.id as string, + organizationId: activeOrganization?.organization + .id as Organisation.OrganisationId, }); toast.success("Settings updated successfully"); router.refresh(); diff --git a/apps/web/app/(org)/dashboard/settings/organization/components/CustomDomain.tsx b/apps/web/app/(org)/dashboard/settings/organization/components/CustomDomain.tsx index f3737fcd5f..a2d9a784ac 100644 --- a/apps/web/app/(org)/dashboard/settings/organization/components/CustomDomain.tsx +++ b/apps/web/app/(org)/dashboard/settings/organization/components/CustomDomain.tsx @@ -1,4 +1,5 @@ import { Button } from "@cap/ui"; +import { Organisation } from "@cap/web-domain"; import { faCheckCircle, faExclamationCircle, @@ -32,7 +33,9 @@ export function CustomDomain() { const removeDomainMutation = useMutation({ mutationFn: (organizationId: string) => - removeOrganizationDomain(organizationId), + removeOrganizationDomain( + Organisation.OrganisationId.make(organizationId), + ), onSuccess: () => { setIsVerified(false); toast.success("Custom domain removed"); diff --git a/apps/web/app/(org)/dashboard/settings/organization/components/CustomDomainDialog/CustomDomainDialog.tsx b/apps/web/app/(org)/dashboard/settings/organization/components/CustomDomainDialog/CustomDomainDialog.tsx index 0df988ad91..3bb58a5457 100644 --- a/apps/web/app/(org)/dashboard/settings/organization/components/CustomDomainDialog/CustomDomainDialog.tsx +++ b/apps/web/app/(org)/dashboard/settings/organization/components/CustomDomainDialog/CustomDomainDialog.tsx @@ -6,6 +6,7 @@ import { DialogHeader, DialogTitle, } from "@cap/ui"; +import { Organisation } from "@cap/web-domain"; import { faGlobe, faRefresh } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useMutation } from "@tanstack/react-query"; @@ -20,7 +21,6 @@ import { Confetti } from "@/app/(org)/dashboard/_components/Confetti"; import { useDashboardContext } from "../../../../Contexts"; import DomainStep from "./DomainStep"; import { Stepper } from "./Stepper"; - import SubscribeContent from "./SubscribeContent"; import SuccesStep from "./SuccessStep"; import { @@ -151,9 +151,12 @@ const CustomDomainDialog = ({ orgId: string; }) => { if (activeOrganization?.organization.customDomain) { - await removeOrganizationDomain(orgId); + await removeOrganizationDomain(Organisation.OrganisationId.make(orgId)); } - return await updateDomain(domain, orgId); + return await updateDomain( + domain, + Organisation.OrganisationId.make(orgId), + ); }, onSuccess: (data) => { handleNext(); @@ -181,7 +184,12 @@ const CustomDomainDialog = ({ orgId: string; showToasts: boolean; }) => { - return { data: await checkOrganizationDomain(orgId), showToasts }; + return { + data: await checkOrganizationDomain( + Organisation.OrganisationId.make(orgId), + ), + showToasts, + }; }, onSuccess: ({ data, showToasts }) => { setIsVerified(data.verified); diff --git a/apps/web/app/(org)/dashboard/settings/organization/components/InviteDialog.tsx b/apps/web/app/(org)/dashboard/settings/organization/components/InviteDialog.tsx index 6f53c2309b..0f966e472c 100644 --- a/apps/web/app/(org)/dashboard/settings/organization/components/InviteDialog.tsx +++ b/apps/web/app/(org)/dashboard/settings/organization/components/InviteDialog.tsx @@ -9,6 +9,7 @@ import { DialogTitle, Input, } from "@cap/ui"; +import type { Organisation } from "@cap/web-domain"; import { faUserGroup } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useMutation } from "@tanstack/react-query"; @@ -95,7 +96,7 @@ export const InviteDialog = ({ return await sendOrganizationInvites( inviteEmails, - activeOrganization?.organization.id as string, + activeOrganization?.organization.id as Organisation.OrganisationId, ); }, onSuccess: () => { diff --git a/apps/web/app/(org)/dashboard/settings/organization/components/MembersCard.tsx b/apps/web/app/(org)/dashboard/settings/organization/components/MembersCard.tsx index 3359170943..7d9b0f8924 100644 --- a/apps/web/app/(org)/dashboard/settings/organization/components/MembersCard.tsx +++ b/apps/web/app/(org)/dashboard/settings/organization/components/MembersCard.tsx @@ -54,7 +54,7 @@ export const MembersCard = ({ try { await removeOrganizationInvite( inviteId, - activeOrganization?.organization.id as string, + activeOrganization!.organization.id, ); toast.success("Invite deleted successfully"); router.refresh(); @@ -90,7 +90,7 @@ export const MembersCard = ({ try { await removeOrganizationMember( pendingMember.id, - activeOrganization?.organization.id as string, + activeOrganization!.organization.id, ); toast.success("Member removed successfully"); setConfirmOpen(false); @@ -126,7 +126,7 @@ export const MembersCard = ({ title="Remove member" description={ pendingMemberTest - ? `Are you sure you want to remove ${pendingMemberTest.name} + ? `Are you sure you want to remove ${pendingMemberTest.name} from your organization? this action cannot be undone.` : "" } diff --git a/apps/web/app/(org)/dashboard/settings/organization/components/OrgName.tsx b/apps/web/app/(org)/dashboard/settings/organization/components/OrgName.tsx index 952afff150..5917c18212 100644 --- a/apps/web/app/(org)/dashboard/settings/organization/components/OrgName.tsx +++ b/apps/web/app/(org)/dashboard/settings/organization/components/OrgName.tsx @@ -19,7 +19,7 @@ const OrgName = () => { setSaveLoading(true); await updateOrganizationDetails({ organizationName: orgName, - organizationId: activeOrganization?.organization.id as string, + organizationId: activeOrganization!.organization.id, }); toast.success("Settings updated successfully"); router.refresh(); diff --git a/apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx b/apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx index 7201e4572a..cf0b63387f 100644 --- a/apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx +++ b/apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx @@ -2,7 +2,7 @@ import type { VideoMetadata } from "@cap/database/types"; import { Button } from "@cap/ui"; -import type { Video } from "@cap/web-domain"; +import type { Organisation, Space, User, Video } from "@cap/web-domain"; import { faFolderPlus, faInfoCircle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useQuery } from "@tanstack/react-query"; @@ -36,10 +36,10 @@ type SharedVideoData = { }[]; type SpaceData = { - id: string; + id: Space.SpaceIdOrOrganisationId; name: string; - organizationId: string; - createdById: string; + organizationId: Organisation.OrganisationId; + createdById: User.UserId; }; export const SharedCaps = ({ @@ -60,12 +60,12 @@ export const SharedCaps = ({ hideSharedWith?: boolean; spaceMembers?: SpaceMemberData[]; organizationMembers?: OrganizationMemberData[]; - currentUserId?: string; + currentUserId?: User.UserId; folders?: FolderDataType[]; organizationData?: { - id: string; + id: Organisation.OrganisationId; name: string; - ownerId: string; + ownerId: User.UserId; }; }) => { const params = useSearchParams(); diff --git a/apps/web/app/(org)/dashboard/spaces/[spaceId]/actions.ts b/apps/web/app/(org)/dashboard/spaces/[spaceId]/actions.ts index e641b35217..847ef9dc40 100644 --- a/apps/web/app/(org)/dashboard/spaces/[spaceId]/actions.ts +++ b/apps/web/app/(org)/dashboard/spaces/[spaceId]/actions.ts @@ -4,6 +4,7 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { nanoIdLength } from "@cap/database/helpers"; import { spaceMembers } from "@cap/database/schema"; +import { Space, User } from "@cap/web-domain"; import { eq, inArray } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { v4 as uuidv4 } from "uuid"; @@ -12,14 +13,14 @@ import { z } from "zod"; const spaceRole = z.union([z.literal("Admin"), z.literal("member")]); const addSpaceMemberSchema = z.object({ - spaceId: z.string(), - userId: z.string(), + spaceId: z.string().transform((v) => Space.SpaceId.make(v)), + userId: z.string().transform((v) => User.UserId.make(v)), role: spaceRole, }); const addSpaceMembersSchema = z.object({ - spaceId: z.string(), - userIds: z.array(z.string()), + spaceId: z.string().transform((v) => Space.SpaceId.make(v)), + userIds: z.array(z.string().transform((v) => User.UserId.make(v))), role: spaceRole, }); @@ -149,8 +150,10 @@ export async function removeSpaceMember( // Replace all members for a space const setSpaceMembersSchema = z.object({ - spaceId: z.string(), - userIds: z.array(z.string()), + spaceId: z + .string() + .transform((v) => Space.SpaceId.make(v) as Space.SpaceIdOrOrganisationId), + userIds: z.array(z.string().transform((v) => User.UserId.make(v))), role: spaceRole.default("member"), }); @@ -174,7 +177,7 @@ export async function setSpaceMembers( if (userIds.length > 0) { const now = new Date(); const values = userIds.map((userId) => ({ - id: uuidv4().substring(0, nanoIdLength), + id: User.UserId.make(uuidv4().substring(0, nanoIdLength)), spaceId, userId, role, diff --git a/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.tsx b/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.tsx index efb3bc9439..305acfc0b8 100644 --- a/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.tsx +++ b/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialog.tsx @@ -1,5 +1,6 @@ "use client"; +import type { Space } from "@cap/web-domain"; import type React from "react"; import { addVideosToSpace } from "@/actions/spaces/add-videos"; import { getSpaceVideoIds } from "@/actions/spaces/get-space-videos"; @@ -10,7 +11,7 @@ import AddVideosDialogBase from "./AddVideosDialogBase"; interface AddVideosDialogProps { open: boolean; onClose: () => void; - spaceId: string; + spaceId: Space.SpaceIdOrOrganisationId; spaceName: string; onVideosAdded?: () => void; } diff --git a/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx b/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx index cb3d7a0134..3ec721dab6 100644 --- a/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx +++ b/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosDialogBase.tsx @@ -23,16 +23,16 @@ import { toast } from "sonner"; import * as z from "zod"; import VirtualizedVideoGrid from "./VirtualizedVideoGrid"; -interface AddVideosDialogBaseProps { +interface AddVideosDialogBaseProp { open: boolean; onClose: () => void; - entityId: string; + entityId: T; entityName: string; onVideosAdded?: () => void; - addVideos: (entityId: string, videoIds: Video.VideoId[]) => Promise; - removeVideos: (entityId: string, videoIds: Video.VideoId[]) => Promise; + addVideos: (entityId: T, videoIds: Video.VideoId[]) => Promise; + removeVideos: (entityId: T, videoIds: Video.VideoId[]) => Promise; getVideos: (limit?: number) => Promise; - getEntityVideoIds: (entityId: string) => Promise; + getEntityVideoIds: (entityId: T) => Promise; } export interface VideoData { @@ -52,7 +52,7 @@ const formSchema = z.object({ search: z.string(), }); -const AddVideosDialogBase: React.FC = ({ +function AddVideosDialogBase({ open, onClose, entityId, @@ -62,7 +62,7 @@ const AddVideosDialogBase: React.FC = ({ removeVideos, getVideos, getEntityVideoIds, -}) => { +}: AddVideosDialogBaseProp) { const [selectedVideos, setSelectedVideos] = useState([]); const [searchTerm, setSearchTerm] = useState(""); const filterTabs = ["all", "added", "notAdded"]; @@ -334,6 +334,6 @@ const AddVideosDialogBase: React.FC = ({ ); -}; +} export default AddVideosDialogBase; diff --git a/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx b/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx index 3bbf5312ce..bd5ece90c0 100644 --- a/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx +++ b/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/AddVideosToOrganizationDialog.tsx @@ -1,5 +1,6 @@ "use client"; +import type { Organisation } from "@cap/web-domain"; import type React from "react"; import { addVideosToOrganization } from "@/actions/organizations/add-videos"; import { getOrganizationVideoIds } from "@/actions/organizations/get-organization-videos"; @@ -10,7 +11,7 @@ import AddVideosDialogBase from "./AddVideosDialogBase"; interface AddVideosToOrganizationDialogProps { open: boolean; onClose: () => void; - organizationId: string; + organizationId: Organisation.OrganisationId; organizationName: string; onVideosAdded?: () => void; } diff --git a/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/MembersIndicator.tsx b/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/MembersIndicator.tsx index d4bf09afff..f5681a20cc 100644 --- a/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/MembersIndicator.tsx +++ b/apps/web/app/(org)/dashboard/spaces/[spaceId]/components/MembersIndicator.tsx @@ -13,6 +13,7 @@ import { FormControl, FormField, } from "@cap/ui"; +import { type Space, User } from "@cap/web-domain"; import { faPlus, faUserGroup } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -30,7 +31,7 @@ type MembersIndicatorProps = { memberCount: number; members: SpaceMemberData[]; organizationMembers: SpaceMemberData[]; - spaceId: string; + spaceId: Space.SpaceIdOrOrganisationId; canManageMembers: boolean; onAddVideos?: () => void; }; @@ -58,7 +59,7 @@ export const MembersIndicator = ({ }, }); - const handleSaveMembers = async (selectedUserIds: string[]) => { + const handleSaveMembers = async (selectedUserIds: User.UserId[]) => { if (!canManageMembers) return; // Compare selectedUserIds to current members' userIds (order-insensitive) @@ -196,7 +197,11 @@ export const MembersIndicator = ({ {canManageMembers && (