added delete board delete column fuctionality and so on

This commit is contained in:
Juthatip McDevitt 2024-03-27 14:42:00 -05:00
parent 69b5b0376a
commit 1619a7ff7b
8 changed files with 163 additions and 15 deletions

View file

@ -35,4 +35,22 @@ export async function addEmailtoBoard(boardId: string, email: string){
usersAccesses[email] = ['room:write']; usersAccesses[email] = ['room:write'];
await liveblocksClient.updateRoom(boardId, {usersAccesses}); await liveblocksClient.updateRoom(boardId, {usersAccesses});
return true; return true;
}
export async function updateBoard(boardId:string, updateData:any){
const result = await liveblocksClient.updateRoom(boardId, updateData);
return true;
}
export async function removeEmailFromBoard(boardId:string, email:string){
const room = await liveblocksClient.getRoom(boardId);
const usersAccesses:any = room.usersAccesses;
usersAccesses[email] = null
await liveblocksClient.updateRoom(boardId, {usersAccesses});
return true;
}
export async function deleteBoard(boardId:string){
await liveblocksClient.deleteRoom(boardId)
return true
} }

View file

@ -28,7 +28,7 @@ const BoardPage = async (props: PageProps) => {
return ( return (
<div> <div>
<Board name={boardInfo.metadata.boardName} id={boardId}/> <Board name={boardInfo.metadata.boardName.toString()} id={boardId}/>
</div> </div>
) )
} }

View file

@ -1,4 +1,6 @@
"use server" "use server"
import BoardDelete from "@/components/BoardDelete";
import EmailAccessList from "@/components/EmailAccessList";
import NewUserAccess from "@/components/forms/NewUserAccess"; import NewUserAccess from "@/components/forms/NewUserAccess";
import { liveblocksClient } from "@/lib/liveblocksClient"; import { liveblocksClient } from "@/lib/liveblocksClient";
import { getUserEmail } from "@/lib/userClient"; import { getUserEmail } from "@/lib/userClient";
@ -23,14 +25,13 @@ const page = async ({params}: PageProps) => {
return ( return (
<div> <div>
<Link href={`/boards/${boardId}`} className="flex items-center gap-1 text-[#164863] font-semibold hover:text-[#427D9D] hover:duration-300"><ImBackward2/>Go back</Link> <div className="flex justify-between">
<h1 className="text-2xl">Access to {boardInfo.metadata.boardName}:</h1> <Link href={`/boards/${boardId}`} className="flex items-center gap-1 text-[#164863] font-semibold hover:text-[#427D9D] hover:duration-300"><ImBackward2/>Go back</Link>
<BoardDelete boardId={boardId}/>
</div>
<h1 className="text-2xl mb-2">Access to {boardInfo.metadata.boardName}:</h1>
<div className="mb-6"> <div className="mb-6">
{Object.keys(boardInfo.usersAccesses).map(email => ( <EmailAccessList boardId={boardId} usersAccesses={boardInfo.usersAccesses}/>
<div>
{email}
</div>
))}
</div> </div>
<NewUserAccess boardId = {boardId}/> <NewUserAccess boardId = {boardId}/>
</div> </div>

View file

@ -5,9 +5,29 @@ import { ClientSideSuspense } from '@liveblocks/react';
import Columns from './Columns'; import Columns from './Columns';
import Link from 'next/link'; import Link from 'next/link';
import { IoMdSettings } from "react-icons/io"; import { IoMdSettings } from "react-icons/io";
import { FormEvent, useState } from 'react';
import { updateBoard } from '@/app/actions/boardActions';
import { useRouter } from 'next/navigation';
const Board = ({id, name}: {id:string, name:string}) => {
const [renameBoard, setRenameBoard] = useState(false);
const router = useRouter();
async function handleRenameSubmit(ev:FormEvent){
ev.preventDefault();
const input = (ev.target as HTMLFormElement).querySelector('input');
if(input){
const newBoardName = input?.value;
await updateBoard(id, {metadata: {boardName: newBoardName}});
input.value = '';
setRenameBoard(false);
router.refresh();
}
}
const Board = ({id, name}: {id: string, name: string}) => {
return ( return (
<RoomProvider id={id} initialPresence={{}} initialStorage={ <RoomProvider id={id} initialPresence={{}} initialStorage={
@ -19,7 +39,14 @@ const Board = ({id, name}: {id: string, name: string}) => {
<> <>
<div className='flex justify-between gap-2 items-center mb-4'> <div className='flex justify-between gap-2 items-center mb-4'>
<div> <div>
<h1 className='text-2xl'>Board: {name}</h1> {!renameBoard && (
<h1 onClick={() => setRenameBoard(true)} className='text-2xl'>Board: {name}</h1>
)}
{renameBoard && (
<form onSubmit={handleRenameSubmit}>
<input type="text" defaultValue={name} className='border p-2'/>
</form>
)}
</div> </div>
<Link href={`/boards/${id}/settings`} className='flex items-center gap-1 bg-[#427D9D] p-2 rounded-full'> <Link href={`/boards/${id}/settings`} className='flex items-center gap-1 bg-[#427D9D] p-2 rounded-full'>
<IoMdSettings className='text-white text-[18px]'/> <IoMdSettings className='text-white text-[18px]'/>

View file

@ -0,0 +1,23 @@
"use client"
import { deleteBoard } from "@/app/actions/boardActions"
import { useRouter } from "next/navigation";
const BoardDelete = ({boardId}:{boardId:string}) => {
const router = useRouter();
async function handleDeleteBoard(){
await deleteBoard(boardId);
router.push('/');
}
return (
<div>
<button onClick={() => handleDeleteBoard()} className="bg-[tomato] p-2 text-white rounded-md font-semibold hover:opacity-90">Delete board</button>
</div>
)
}
export default BoardDelete

View file

@ -2,6 +2,8 @@ import { Card, useMutation, useStorage } from '@/app/liveblocks.config';
import { shallow } from '@liveblocks/client'; import { shallow } from '@liveblocks/client';
import { ReactSortable } from 'react-sortablejs'; import { ReactSortable } from 'react-sortablejs';
import NewCardForm from './forms/NewCardForm'; import NewCardForm from './forms/NewCardForm';
import { FormEvent, useState } from 'react';
import { BsThreeDots } from "react-icons/bs";
type ColumnProps = { type ColumnProps = {
@ -35,13 +37,48 @@ const Column = ({id, name}: ColumnProps) => {
}) })
}) })
},[]) }, []);
const updateColumn = useMutation(({storage}, id, newName) => {
const columns = storage.get('columns');
columns.find(c => c.toObject().id === id)?.set('name', newName)
}, []);
const [renameColumn, setRenameColumn] = useState(false);
function handleRenameColumn(ev: FormEvent){
ev.preventDefault();
const input = (ev.target as HTMLFormElement).querySelector('input')
if(input){
const newColumnName = input.value;
updateColumn(id, newColumnName)
setRenameColumn(false);
}
}
return ( return (
<div className='w-50 shadow-md rounded-md p-2 bg-gray-100'> <div className='w-50 shadow-md rounded-md p-2 bg-gray-100'>
<h3>{name}</h3> {!renameColumn && (
{columnCards && ( <div className='flex justify-between'>
<h3>{name}</h3>
<button onClick={() => setRenameColumn(true)}><BsThreeDots className='text-[#164863]'/></button>
</div>
)}
{renameColumn && (
<div className='mb-8'>
Edit name:
<form onSubmit={handleRenameColumn} className='mb-2'>
<input type="text" defaultValue={name} className='border p-2 w-full'/>
<button type="submit" className='w-full mt-2'>Save</button>
</form>
<button className='w-full p-2 bg-[tomato] text-white rounded-md font-semibold text-sm'> Delete column</button>
<button onClick={() => setRenameColumn(false)} className='w-full mt-4 text-xs uppercase font-bold text-gray-500 tracking-wider'>Cancel</button>
</div>
)}
{!renameColumn && columnCards && (
<> <>
<ReactSortable list={columnCards} setList={items => setTasksOrderForColumn(items, id)} group="cards" ghostClass='opacity-40' className='min-h-12'> <ReactSortable list={columnCards} setList={items => setTasksOrderForColumn(items, id)} group="cards" ghostClass='opacity-40' className='min-h-12'>
{columnCards.map(card => ( {columnCards.map(card => (
@ -52,7 +89,10 @@ const Column = ({id, name}: ColumnProps) => {
</ReactSortable> </ReactSortable>
</> </>
)} )}
<NewCardForm columnId={id}/> {!renameColumn && (
<NewCardForm columnId={id}/>
)}
</div> </div>
) )

View file

@ -0,0 +1,33 @@
"use client"
import { deleteBoard, removeEmailFromBoard } from "@/app/actions/boardActions";
import { RoomAccesses } from "@liveblocks/node";
import { useRouter } from "next/navigation";
import { IoTrashOutline } from "react-icons/io5";
const EmailAccessList = ({boardId, usersAccesses} : {boardId:string ,usersAccesses:RoomAccesses}) => {
const router = useRouter();
async function handleDelete(emailToDelete: string){
await removeEmailFromBoard(boardId, emailToDelete)
router.refresh();
}
return (
<div className="max-w-xs">
{Object.keys(usersAccesses).map(email => (
<div key={email} className="flex gap-2 my-2 items-center max-w-xs justify-between border pl-2">
{email}
<button onClick={() => handleDelete(email)} className="p-2 bg-gray-100">
<IoTrashOutline className="text-[tomato] text-lg"/>
</button>
</div>
))}
</div>
)
}
export default EmailAccessList

View file

@ -2,4 +2,10 @@ import { Liveblocks } from "@liveblocks/node";
export const liveblocksClient = new Liveblocks({ export const liveblocksClient = new Liveblocks({
secret: process.env.LIVEBLOCKS_SECRET_KEY || '', secret: process.env.LIVEBLOCKS_SECRET_KEY || '',
}); });
export function getLiveblocksClient(){
return new Liveblocks({
secret: process.env.LIVEBLOCKS_SECRET_KEY || '',
});
}