From ae6a8d69b8474bdd87c21734dbe6c7f7610e946d Mon Sep 17 00:00:00 2001 From: Ahmad <103906421+ahmadk953@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:01:22 -0400 Subject: [PATCH] Added the Ability to Update Board Background Images After Creation --- README.md | 2 +- actions/update-board/index.ts | 34 +++++++- actions/update-board/schema.ts | 8 +- .../(dashboard)/_components/sidebar.tsx | 1 - .../[boardId]/_components/board-options.tsx | 6 +- .../_components/board-update-image.tsx | 78 +++++++++++++++++++ 6 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 app/(platform)/(dashboard)/board/[boardId]/_components/board-update-image.tsx diff --git a/README.md b/README.md index bc165f3..d7c9a79 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Currently, there is no documentation or wiki available but, I do plan to add one This will also be published on the site some time soon but for now, the roadmap will be listed here. - [ ] Finish adding started at date feature -- [ ] Make board background image editable after board creation - _In Progress_ +- [X] Make board background image editable after board creation - [ ] Pagination for Audit Logs page - [ ] Board sorting options (Boards Page) - [ ] Add list and card views to boards page diff --git a/actions/update-board/index.ts b/actions/update-board/index.ts index 96c8b70..d6f2754 100644 --- a/actions/update-board/index.ts +++ b/actions/update-board/index.ts @@ -16,10 +16,36 @@ const handler = async (data: InputType): Promise => { if (!userId || !orgId) return { error: 'Unauthorized' }; - const { title, id } = data; + const { title, id, image } = data; let board; try { + const currentBoard = await db.board.findUnique({ + where: { + id, + orgId, + }, + select: { + imageId: true, + imageThumbUrl: true, + imageFullUrl: true, + imageUserName: true, + imageLinkHTML: true, + imageDownloadUrl: true, + }, + }); + + const currentImageString = `${currentBoard?.imageId}|${currentBoard?.imageThumbUrl}|${currentBoard?.imageFullUrl}|${currentBoard?.imageUserName}|${currentBoard?.imageLinkHTML}|${currentBoard?.imageDownloadUrl}`; + + const [ + imageId, + imageThumbUrl, + imageFullUrl, + imageLinkHTML, + imageUserName, + imageDownloadUrl, + ] = image?.split('|') || currentImageString.split('|'); + board = await db.board.update({ where: { id, @@ -27,6 +53,12 @@ const handler = async (data: InputType): Promise => { }, data: { title, + imageId, + imageThumbUrl, + imageFullUrl, + imageLinkHTML, + imageUserName, + imageDownloadUrl, }, }); diff --git a/actions/update-board/schema.ts b/actions/update-board/schema.ts index ca8d5e9..7af5380 100644 --- a/actions/update-board/schema.ts +++ b/actions/update-board/schema.ts @@ -8,6 +8,12 @@ export const UpdateBoard = z.object({ }) .min(3, { message: 'Title must be at least 3 characters', - }), + }) + .optional(), id: z.string(), + image: z.optional( + z.string({ + invalid_type_error: 'Image must be a string', + }) + ), }); diff --git a/app/(platform)/(dashboard)/_components/sidebar.tsx b/app/(platform)/(dashboard)/_components/sidebar.tsx index b9a0cfa..4c571dc 100644 --- a/app/(platform)/(dashboard)/_components/sidebar.tsx +++ b/app/(platform)/(dashboard)/_components/sidebar.tsx @@ -6,7 +6,6 @@ import { useLocalStorage } from 'usehooks-ts'; import { useOrganizationList, useOrganization } from '@clerk/nextjs'; import { Button } from '@/components/ui/button'; -import { Separator } from '@/components/ui/separator'; import { Accordion } from '@/components/ui/accordion'; import { Skeleton } from '@/components/ui/skeleton'; import { NavItem, Organization } from './nav-item'; diff --git a/app/(platform)/(dashboard)/board/[boardId]/_components/board-options.tsx b/app/(platform)/(dashboard)/board/[boardId]/_components/board-options.tsx index ead62f4..b5cd490 100644 --- a/app/(platform)/(dashboard)/board/[boardId]/_components/board-options.tsx +++ b/app/(platform)/(dashboard)/board/[boardId]/_components/board-options.tsx @@ -13,6 +13,7 @@ import { PopoverTrigger, } from '@/components/ui/popover'; import { copyBoard } from '@/actions/copy-board'; +import { BoardUpdateImage } from './board-update-image'; interface BoardOptionsProps { id: string; @@ -70,15 +71,16 @@ export const BoardOptions = ({ id }: BoardOptionsProps) => { disabled={isLoadingCopy} className='h-auto w-full justify-start rounded-none p-2 px-5 text-sm font-normal text-neutral-600' > - Copy this board + Copy this Board + diff --git a/app/(platform)/(dashboard)/board/[boardId]/_components/board-update-image.tsx b/app/(platform)/(dashboard)/board/[boardId]/_components/board-update-image.tsx new file mode 100644 index 0000000..42a602d --- /dev/null +++ b/app/(platform)/(dashboard)/board/[boardId]/_components/board-update-image.tsx @@ -0,0 +1,78 @@ +import { X } from 'lucide-react'; +import { toast } from 'sonner'; +import { ElementRef, useRef } from 'react'; +import Link from 'next/link'; + +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverClose, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { FormPicker } from '@/components/form/form-picker'; +import { FormSubmit } from '@/components/form/form-submit'; +import { useAction } from '@/hooks/use-action'; +import { updateBoard } from '@/actions/update-board'; + +interface BoardUpdateImageProps { + boardId: string; +} + +export const BoardUpdateImage = ({ boardId }: BoardUpdateImageProps) => { + const closeRef = useRef>(null); + + const { execute, fieldErrors } = useAction(updateBoard, { + onSuccess: (data) => { + toast.success('Board image updated'); + closeRef.current?.click(); + }, + onError: (error) => { + toast.error(error); + }, + }); + + const onSubmit = (formData: FormData) => { + const image = formData.get('image') as string; + + execute({ id: boardId, image }); + }; + + return ( + + + + + + + + +
+
+

+ Images Provided by{' '} + + Unsplash + +

+ +
+ Update +
+
+
+ ); +};