Added the Ability to Update Board Background Images After Creation

This commit is contained in:
Ahmad 2024-04-30 20:01:22 -04:00
parent 4ddb7f99fd
commit ae6a8d69b8
No known key found for this signature in database
GPG key ID: 8FD8A93530D182BF
6 changed files with 123 additions and 6 deletions

View file

@ -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. 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 - [ ] 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 - [ ] Pagination for Audit Logs page
- [ ] Board sorting options (Boards Page) - [ ] Board sorting options (Boards Page)
- [ ] Add list and card views to boards page - [ ] Add list and card views to boards page

View file

@ -16,10 +16,36 @@ const handler = async (data: InputType): Promise<ReturnType> => {
if (!userId || !orgId) return { error: 'Unauthorized' }; if (!userId || !orgId) return { error: 'Unauthorized' };
const { title, id } = data; const { title, id, image } = data;
let board; let board;
try { 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({ board = await db.board.update({
where: { where: {
id, id,
@ -27,6 +53,12 @@ const handler = async (data: InputType): Promise<ReturnType> => {
}, },
data: { data: {
title, title,
imageId,
imageThumbUrl,
imageFullUrl,
imageLinkHTML,
imageUserName,
imageDownloadUrl,
}, },
}); });

View file

@ -8,6 +8,12 @@ export const UpdateBoard = z.object({
}) })
.min(3, { .min(3, {
message: 'Title must be at least 3 characters', message: 'Title must be at least 3 characters',
}), })
.optional(),
id: z.string(), id: z.string(),
image: z.optional(
z.string({
invalid_type_error: 'Image must be a string',
})
),
}); });

View file

@ -6,7 +6,6 @@ import { useLocalStorage } from 'usehooks-ts';
import { useOrganizationList, useOrganization } from '@clerk/nextjs'; import { useOrganizationList, useOrganization } from '@clerk/nextjs';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import { Accordion } from '@/components/ui/accordion'; import { Accordion } from '@/components/ui/accordion';
import { Skeleton } from '@/components/ui/skeleton'; import { Skeleton } from '@/components/ui/skeleton';
import { NavItem, Organization } from './nav-item'; import { NavItem, Organization } from './nav-item';

View file

@ -13,6 +13,7 @@ import {
PopoverTrigger, PopoverTrigger,
} from '@/components/ui/popover'; } from '@/components/ui/popover';
import { copyBoard } from '@/actions/copy-board'; import { copyBoard } from '@/actions/copy-board';
import { BoardUpdateImage } from './board-update-image';
interface BoardOptionsProps { interface BoardOptionsProps {
id: string; id: string;
@ -70,15 +71,16 @@ export const BoardOptions = ({ id }: BoardOptionsProps) => {
disabled={isLoadingCopy} disabled={isLoadingCopy}
className='h-auto w-full justify-start rounded-none p-2 px-5 text-sm font-normal text-neutral-600' 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
</Button> </Button>
<BoardUpdateImage boardId={id} />
<Button <Button
variant='ghost' variant='ghost'
onClick={onDelete} onClick={onDelete}
disabled={isLoadingDelete} disabled={isLoadingDelete}
className='h-auto w-full justify-start rounded-none p-2 px-5 text-sm font-normal text-destructive hover:text-destructive' className='h-auto w-full justify-start rounded-none p-2 px-5 text-sm font-normal text-destructive hover:text-destructive'
> >
Delete this board Delete this Board
</Button> </Button>
</PopoverContent> </PopoverContent>
</Popover> </Popover>

View file

@ -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<ElementRef<'button'>>(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 (
<Popover>
<PopoverTrigger asChild>
<Button
variant='ghost'
className='h-auto w-full justify-start p-2 px-5 text-sm font-normal text-neutral-600'
>
Change Background Image
</Button>
</PopoverTrigger>
<PopoverContent className='w-80 pt-3' side='left' align='start'>
<PopoverClose asChild>
<Button
className='absolute right-2 top-2 h-auto w-auto p-2 text-neutral-600'
variant='ghost'
>
<X className='h-4 w-4' />
</Button>
</PopoverClose>
<form action={onSubmit} className='space-y-4'>
<div className='space-y-4'>
<p className='text-center text-xs font-medium italic text-neutral-700'>
Images Provided by{' '}
<Link
className='text-sky-900 underline'
href='https://unsplash.com/'
>
Unsplash
</Link>
</p>
<FormPicker id='image' errors={fieldErrors} />
</div>
<FormSubmit className='w-full'>Update</FormSubmit>
</form>
</PopoverContent>
</Popover>
);
};