added delete board delete column fuctionality and so on
This commit is contained in:
parent
69b5b0376a
commit
1619a7ff7b
8 changed files with 163 additions and 15 deletions
|
@ -36,3 +36,21 @@ export async function addEmailtoBoard(boardId: string, email: string){
|
|||
await liveblocksClient.updateRoom(boardId, {usersAccesses});
|
||||
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
|
||||
}
|
|
@ -28,7 +28,7 @@ const BoardPage = async (props: PageProps) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Board name={boardInfo.metadata.boardName} id={boardId}/>
|
||||
<Board name={boardInfo.metadata.boardName.toString()} id={boardId}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"use server"
|
||||
import BoardDelete from "@/components/BoardDelete";
|
||||
import EmailAccessList from "@/components/EmailAccessList";
|
||||
import NewUserAccess from "@/components/forms/NewUserAccess";
|
||||
import { liveblocksClient } from "@/lib/liveblocksClient";
|
||||
import { getUserEmail } from "@/lib/userClient";
|
||||
|
@ -23,14 +25,13 @@ const page = async ({params}: PageProps) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between">
|
||||
<Link href={`/boards/${boardId}`} className="flex items-center gap-1 text-[#164863] font-semibold hover:text-[#427D9D] hover:duration-300"><ImBackward2/>Go back</Link>
|
||||
<h1 className="text-2xl">Access to {boardInfo.metadata.boardName}:</h1>
|
||||
<div className="mb-6">
|
||||
{Object.keys(boardInfo.usersAccesses).map(email => (
|
||||
<div>
|
||||
{email}
|
||||
<BoardDelete boardId={boardId}/>
|
||||
</div>
|
||||
))}
|
||||
<h1 className="text-2xl mb-2">Access to {boardInfo.metadata.boardName}:</h1>
|
||||
<div className="mb-6">
|
||||
<EmailAccessList boardId={boardId} usersAccesses={boardInfo.usersAccesses}/>
|
||||
</div>
|
||||
<NewUserAccess boardId = {boardId}/>
|
||||
</div>
|
||||
|
|
|
@ -5,9 +5,29 @@ import { ClientSideSuspense } from '@liveblocks/react';
|
|||
import Columns from './Columns';
|
||||
import Link from 'next/link';
|
||||
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 (
|
||||
<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>
|
||||
<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>
|
||||
<Link href={`/boards/${id}/settings`} className='flex items-center gap-1 bg-[#427D9D] p-2 rounded-full'>
|
||||
<IoMdSettings className='text-white text-[18px]'/>
|
||||
|
|
23
task_management_app/src/components/BoardDelete.tsx
Normal file
23
task_management_app/src/components/BoardDelete.tsx
Normal 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
|
|
@ -2,6 +2,8 @@ import { Card, useMutation, useStorage } from '@/app/liveblocks.config';
|
|||
import { shallow } from '@liveblocks/client';
|
||||
import { ReactSortable } from 'react-sortablejs';
|
||||
import NewCardForm from './forms/NewCardForm';
|
||||
import { FormEvent, useState } from 'react';
|
||||
import { BsThreeDots } from "react-icons/bs";
|
||||
|
||||
|
||||
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 (
|
||||
<div className='w-50 shadow-md rounded-md p-2 bg-gray-100'>
|
||||
{!renameColumn && (
|
||||
<div className='flex justify-between'>
|
||||
<h3>{name}</h3>
|
||||
{columnCards && (
|
||||
<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'>
|
||||
{columnCards.map(card => (
|
||||
|
@ -52,7 +89,10 @@ const Column = ({id, name}: ColumnProps) => {
|
|||
</ReactSortable>
|
||||
</>
|
||||
)}
|
||||
{!renameColumn && (
|
||||
<NewCardForm columnId={id}/>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
)
|
||||
|
|
33
task_management_app/src/components/EmailAccessList.tsx
Normal file
33
task_management_app/src/components/EmailAccessList.tsx
Normal 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
|
|
@ -3,3 +3,9 @@ import { Liveblocks } from "@liveblocks/node";
|
|||
export const liveblocksClient = new Liveblocks({
|
||||
secret: process.env.LIVEBLOCKS_SECRET_KEY || '',
|
||||
});
|
||||
|
||||
export function getLiveblocksClient(){
|
||||
return new Liveblocks({
|
||||
secret: process.env.LIVEBLOCKS_SECRET_KEY || '',
|
||||
});
|
||||
}
|
Loading…
Add table
Reference in a new issue