add popup functionality

This commit is contained in:
Juthatip McDevitt 2024-03-28 14:34:39 -05:00
parent 1619a7ff7b
commit ddcb7ea71d
14 changed files with 208 additions and 30 deletions

View 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)
}

View file

@ -0,0 +1,3 @@
export default function Default(){
return null;
}

View file

@ -0,0 +1,10 @@
import CardModal from '@/components/views/CardModal'
import React from 'react'
const page = () => {
return (
<CardModal/>
)
}
export default page

View file

@ -0,0 +1,3 @@
export default function Default(){
return null;
}

View file

@ -0,0 +1,3 @@
export default function Default(){
return null;
}

View file

@ -0,0 +1,3 @@
export default function Default(){
return null;
}

View file

@ -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

View file

@ -0,0 +1,3 @@
export default function Default(){
return null;
}

View 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

View file

@ -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>
)
}

View 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>
)
}

View 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

View file

@ -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>
</>

View 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