add popup functionality
This commit is contained in:
parent
1619a7ff7b
commit
ddcb7ea71d
14 changed files with 208 additions and 30 deletions
10
task_management_app/src/app/api/boards/route.ts
Normal file
10
task_management_app/src/app/api/boards/route.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { liveblocksClient } from "@/lib/liveblocksClient";
|
||||
import { Liveblocks } from "@liveblocks/node";
|
||||
import { NextRequest } from "next/server";
|
||||
|
||||
export async function PUT(req: NextRequest){
|
||||
const {id, update} = await req.json();
|
||||
const liveblocks = new Liveblocks({secret: process.env.LIVEBLOCKS_SECRET_KEY as string})
|
||||
await liveblocks.updateRoom(id, update);
|
||||
return Response.json(true)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export default function Default(){
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import CardModal from '@/components/views/CardModal'
|
||||
import React from 'react'
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
<CardModal/>
|
||||
)
|
||||
}
|
||||
|
||||
export default page
|
|
@ -0,0 +1,3 @@
|
|||
export default function Default(){
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export default function Default(){
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export default function Default(){
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react'
|
||||
import BoardPage from '../../page';
|
||||
|
||||
|
||||
|
||||
|
||||
type PageProps ={
|
||||
params:{
|
||||
boardId: string;
|
||||
cardId: string;
|
||||
}
|
||||
}
|
||||
|
||||
const page = ({params}: PageProps) => {
|
||||
return (
|
||||
<BoardPage params={params}/>
|
||||
)
|
||||
}
|
||||
|
||||
export default page
|
||||
|
||||
|
||||
|
||||
|
3
task_management_app/src/app/boards/[boardId]/defalt.ts
Normal file
3
task_management_app/src/app/boards/[boardId]/defalt.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function Default(){
|
||||
return null;
|
||||
}
|
19
task_management_app/src/app/boards/[boardId]/layout.tsx
Normal file
19
task_management_app/src/app/boards/[boardId]/layout.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import React from 'react'
|
||||
|
||||
|
||||
type PageProps = {
|
||||
children: React.ReactNode,
|
||||
modal: React.ReactNode,
|
||||
}
|
||||
|
||||
|
||||
const BoardLayout = ({children, modal}: PageProps) => {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
{modal}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BoardLayout
|
|
@ -8,6 +8,7 @@ import { IoMdSettings } from "react-icons/io";
|
|||
import { FormEvent, useState } from 'react';
|
||||
import { updateBoard } from '@/app/actions/boardActions';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { BoardContextProvider } from './BoardContext';
|
||||
|
||||
|
||||
const Board = ({id, name}: {id:string, name:string}) => {
|
||||
|
@ -30,33 +31,35 @@ const Board = ({id, name}: {id:string, name:string}) => {
|
|||
|
||||
|
||||
return (
|
||||
<RoomProvider id={id} initialPresence={{}} initialStorage={
|
||||
{
|
||||
columns: new LiveList(),
|
||||
cards: new LiveList(),
|
||||
}}>
|
||||
<ClientSideSuspense fallback={(<div>Loading...</div>)}>{() => (
|
||||
<>
|
||||
<div className='flex justify-between gap-2 items-center mb-4'>
|
||||
<div>
|
||||
{!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>
|
||||
)}
|
||||
<BoardContextProvider>
|
||||
<RoomProvider id={id} initialPresence={{}} initialStorage={
|
||||
{
|
||||
columns: new LiveList(),
|
||||
cards: new LiveList(),
|
||||
}}>
|
||||
<ClientSideSuspense fallback={(<div>Loading...</div>)}>{() => (
|
||||
<>
|
||||
<div className='flex justify-between gap-2 items-center mb-4'>
|
||||
<div>
|
||||
{!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]'/>
|
||||
</Link>
|
||||
</div>
|
||||
<Link href={`/boards/${id}/settings`} className='flex items-center gap-1 bg-[#427D9D] p-2 rounded-full'>
|
||||
<IoMdSettings className='text-white text-[18px]'/>
|
||||
</Link>
|
||||
</div>
|
||||
<Columns/>
|
||||
</>
|
||||
)}
|
||||
</ClientSideSuspense>
|
||||
</RoomProvider>
|
||||
<Columns/>
|
||||
</>
|
||||
)}
|
||||
</ClientSideSuspense>
|
||||
</RoomProvider>
|
||||
</BoardContextProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
28
task_management_app/src/components/BoardContext.tsx
Normal file
28
task_management_app/src/components/BoardContext.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import React, { Dispatch, createContext, useState } from "react";
|
||||
|
||||
export type OpenCardId = string | null;
|
||||
|
||||
export type BoardContextProps = {
|
||||
openCard?: OpenCardId;
|
||||
setOpenCard?: Dispatch<React.SetStateAction<OpenCardId>>
|
||||
}
|
||||
|
||||
type ProviderProps = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const BoardContext = createContext<BoardContextProps>({});
|
||||
|
||||
|
||||
export function BoardContextProvider({children}:ProviderProps){
|
||||
const [openCard, setOpenCard] = useState<OpenCardId>(null);
|
||||
return(
|
||||
<BoardContext.Provider value={{openCard, setOpenCard,}}>
|
||||
{children}
|
||||
</BoardContext.Provider>
|
||||
)
|
||||
}
|
30
task_management_app/src/components/Card.tsx
Normal file
30
task_management_app/src/components/Card.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
"use client"
|
||||
import Link from "next/link"
|
||||
import { useParams, useRouter } from "next/navigation"
|
||||
import { useContext, useEffect } from "react";
|
||||
import { BoardContext } from "./BoardContext";
|
||||
|
||||
|
||||
const Card = ({id, name}: {id:string, name:string}) => {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const {openCard} = useContext(BoardContext);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if(params.cardId && !openCard){
|
||||
const {boardId, cardId} = params;
|
||||
router.push(`/boards/${boardId}`);
|
||||
router.push(`/boards/${boardId}/cards/${cardId}`);
|
||||
}
|
||||
}, [params.cardId])
|
||||
|
||||
|
||||
return (
|
||||
<Link href={`/boards/${params.boardId}/cards/${id}`} className='border block my-2 p-2 rounded-sm bg-white'>
|
||||
<span>{name}</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default Card
|
|
@ -4,6 +4,7 @@ import { ReactSortable } from 'react-sortablejs';
|
|||
import NewCardForm from './forms/NewCardForm';
|
||||
import { FormEvent, useState } from 'react';
|
||||
import { BsThreeDots } from "react-icons/bs";
|
||||
import {default as ColumnCard} from './Card';
|
||||
|
||||
|
||||
type ColumnProps = {
|
||||
|
@ -44,6 +45,12 @@ const Column = ({id, name}: ColumnProps) => {
|
|||
columns.find(c => c.toObject().id === id)?.set('name', newName)
|
||||
}, []);
|
||||
|
||||
const deleteColumn = useMutation(({storage}, id) => {
|
||||
const columns = storage.get('columns');
|
||||
const columnIndex = columns.findIndex(c => c.toObject().id === id);
|
||||
columns.delete(columnIndex);
|
||||
}, [])
|
||||
|
||||
const [renameColumn, setRenameColumn] = useState(false);
|
||||
function handleRenameColumn(ev: FormEvent){
|
||||
ev.preventDefault();
|
||||
|
@ -72,7 +79,7 @@ const Column = ({id, name}: ColumnProps) => {
|
|||
<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={() => deleteColumn(id)} 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>
|
||||
|
||||
|
@ -82,9 +89,7 @@ const Column = ({id, name}: ColumnProps) => {
|
|||
<>
|
||||
<ReactSortable list={columnCards} setList={items => setTasksOrderForColumn(items, id)} group="cards" ghostClass='opacity-40' className='min-h-12'>
|
||||
{columnCards.map(card => (
|
||||
<div key={card.id} className='border my-2 p-2 rounded-sm bg-white'>
|
||||
<span>{card.name}</span>
|
||||
</div>
|
||||
<ColumnCard key={card.id} id={card.id} name={card.name} />
|
||||
))}
|
||||
</ReactSortable>
|
||||
</>
|
||||
|
|
34
task_management_app/src/components/views/CardModal.tsx
Normal file
34
task_management_app/src/components/views/CardModal.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
"use client"
|
||||
|
||||
import { useParams, useRouter } from "next/navigation"
|
||||
import { useContext, useEffect } from "react";
|
||||
import { BoardContext, BoardContextProps } from "../BoardContext";
|
||||
|
||||
|
||||
|
||||
|
||||
const CardModal = () => {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const {openCard, setOpenCard} = useContext<BoardContextProps>(BoardContext)
|
||||
|
||||
useEffect(() => {
|
||||
if(params.cardId && setOpenCard){
|
||||
setOpenCard(params.cardId.toString());
|
||||
}
|
||||
}, [params])
|
||||
|
||||
|
||||
|
||||
function handleBackdrop(){
|
||||
router.back();
|
||||
}
|
||||
|
||||
return (
|
||||
<div onClick={handleBackdrop} className='fixed inset-0 bg-black/80'>
|
||||
<div onClick={ev => ev.stopPropagation()} className='bg-white p-4 mt-8 max-w-xs mx-auto'>test</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CardModal
|
Loading…
Add table
Reference in a new issue