mirror of
https://github.com/ahmadk953/tasko.git
synced 2025-02-07 03:32:51 +00:00
Added the Ability to Update Board Background Images After Creation
This commit is contained in:
parent
4ddb7f99fd
commit
ae6a8d69b8
6 changed files with 123 additions and 6 deletions
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
})
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in a new issue