diff --git a/actions/copy-board/index.ts b/actions/copy-board/index.ts new file mode 100644 index 0000000..ebb602e --- /dev/null +++ b/actions/copy-board/index.ts @@ -0,0 +1,69 @@ +'use server'; + +import { auth } from '@clerk/nextjs/server'; + +import { db } from '@/lib/db'; +import { createSafeAction } from '@/lib/create-safe-action'; + +import { InputType, ReturnType } from './types'; +import { CopyBoard } from './schema'; +import { redirect } from 'next/navigation'; + +const handler = async (data: InputType): Promise => { + const { userId, orgId } = auth(); + + if (!userId || !orgId) { + return { + error: 'Unauthorized', + }; + } + + const { id } = data; + let board; + + try { + const boardToCopy = await db.board.findUnique({ + where: { + id, + orgId, + }, + include: { + lists: true, + }, + }); + + if (!boardToCopy) return { error: 'Board not found' }; + + board = await db.board.create({ + data: { + title: `${boardToCopy.title} - Copy`, + orgId, + imageId: boardToCopy.imageId, + imageThumbUrl: boardToCopy.imageThumbUrl, + imageFullUrl: boardToCopy.imageFullUrl, + imageLinkHTML: boardToCopy.imageLinkHTML, + imageUserName: boardToCopy.imageUserName, + imageDownloadUrl: boardToCopy.imageDownloadUrl, + lists: { + createMany: { + data: boardToCopy.lists.map((list) => ({ + title: list.title, + order: list.order, + })), + }, + }, + }, + include: { + lists: true, + }, + }); + } catch (error) { + return { + error: 'Failed to create board', + }; + } + + redirect(`/board/${board.id}`); +}; + +export const copyBoard = createSafeAction(CopyBoard, handler); diff --git a/actions/copy-board/schema.ts b/actions/copy-board/schema.ts new file mode 100644 index 0000000..bba2258 --- /dev/null +++ b/actions/copy-board/schema.ts @@ -0,0 +1,5 @@ +import { z } from 'zod'; + +export const CopyBoard = z.object({ + id: z.string(), +}); diff --git a/actions/copy-board/types.ts b/actions/copy-board/types.ts new file mode 100644 index 0000000..5f9fa9f --- /dev/null +++ b/actions/copy-board/types.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; +import { Board } from '@prisma/client'; + +import { ActionState } from '@/lib/create-safe-action'; +import { CopyBoard } from './schema'; + +export type InputType = z.infer; +export type ReturnType = ActionState; diff --git a/actions/create-board/index.ts b/actions/create-board/index.ts index 5c82ea0..1bf14d7 100644 --- a/actions/create-board/index.ts +++ b/actions/create-board/index.ts @@ -75,6 +75,7 @@ const handler = async (data: InputType): Promise => { imageFullUrl, imageUserName, imageLinkHTML, + imageDownloadUrl, }, }); diff --git a/app/(platform)/(dashboard)/board/[boardId]/_components/board-options.tsx b/app/(platform)/(dashboard)/board/[boardId]/_components/board-options.tsx index d896a81..ead62f4 100644 --- a/app/(platform)/(dashboard)/board/[boardId]/_components/board-options.tsx +++ b/app/(platform)/(dashboard)/board/[boardId]/_components/board-options.tsx @@ -12,20 +12,37 @@ import { PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; +import { copyBoard } from '@/actions/copy-board'; interface BoardOptionsProps { id: string; } export const BoardOptions = ({ id }: BoardOptionsProps) => { - const { execute, isLoading } = useAction(deleteBoard, { - onError: (error) => { - toast.error(error); - }, - }); + const { execute: executeDelete, isLoading: isLoadingDelete } = useAction( + deleteBoard, + { + onError: (error) => { + toast.error(error); + }, + } + ); + + const { execute: executeCopy, isLoading: isLoadingCopy } = useAction( + copyBoard, + { + onError: (error) => { + toast.error(error); + }, + } + ); const onDelete = () => { - execute({ id }); + executeDelete({ id }); + }; + + const onCopy = () => { + executeCopy({ id }); }; return ( @@ -47,10 +64,18 @@ export const BoardOptions = ({ id }: BoardOptionsProps) => { +