added comment functionality
This commit is contained in:
parent
86b9427bd6
commit
389771d33f
7 changed files with 1211 additions and 18 deletions
1096
task_management_app/package-lock.json
generated
1096
task_management_app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -13,6 +13,7 @@
|
||||||
"@liveblocks/client": "^1.11.0",
|
"@liveblocks/client": "^1.11.0",
|
||||||
"@liveblocks/node": "^1.11.0",
|
"@liveblocks/node": "^1.11.0",
|
||||||
"@liveblocks/react": "^1.11.0",
|
"@liveblocks/react": "^1.11.0",
|
||||||
|
"@liveblocks/react-comments": "^1.11.0",
|
||||||
"@liveblocks/yjs": "^1.11.0",
|
"@liveblocks/yjs": "^1.11.0",
|
||||||
"@tailwindcss/forms": "^0.5.7",
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
"@tiptap/extension-collaboration": "^2.2.4",
|
"@tiptap/extension-collaboration": "^2.2.4",
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
"@tiptap/react": "^2.2.4",
|
"@tiptap/react": "^2.2.4",
|
||||||
"@tiptap/starter-kit": "^2.2.4",
|
"@tiptap/starter-kit": "^2.2.4",
|
||||||
"mongodb": "^6.5.0",
|
"mongodb": "^6.5.0",
|
||||||
|
"mongoose": "^8.2.4",
|
||||||
"next": "14.1.4",
|
"next": "14.1.4",
|
||||||
"next-auth": "^4.24.7",
|
"next-auth": "^4.24.7",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|
24
task_management_app/src/app/api/users/route.ts
Normal file
24
task_management_app/src/app/api/users/route.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { User, UserType } from "@/models/User";
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
|
export async function GET(req: NextRequest){
|
||||||
|
const url = new URL(req.url);
|
||||||
|
const connectionString = process.env.MONGODB_URI
|
||||||
|
if(!connectionString){
|
||||||
|
return new Response('No db connection string', {status:500});
|
||||||
|
}
|
||||||
|
if(!url.searchParams.has('ids')){
|
||||||
|
return Response.json([])
|
||||||
|
|
||||||
|
}
|
||||||
|
const emails = url.searchParams.getAll('ids');
|
||||||
|
await mongoose.connect(connectionString)
|
||||||
|
const users = await User.find({email: emails});
|
||||||
|
|
||||||
|
return Response.json(users.map((u:UserType) => ({
|
||||||
|
id:u.email,
|
||||||
|
name:u.name,
|
||||||
|
image:u.image
|
||||||
|
})));
|
||||||
|
}
|
|
@ -3,9 +3,8 @@
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
|
||||||
body{}
|
|
||||||
button[type="button"], button[type="submit"], button.primary{
|
button[type="button"], button[type="submit"], button.primary{
|
||||||
@apply bg-[#164863] text-white p-2 text-sm font-semibold rounded-md
|
@apply bg-[#164863] text-white p-2 text-sm font-semibold rounded-md flex
|
||||||
}
|
}
|
||||||
button[type="submit"], button.primary{
|
button[type="submit"], button.primary{
|
||||||
@apply bg-[#427D9D] rounded-md
|
@apply bg-[#427D9D] rounded-md
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import { LiveList, LiveObject, createClient } from "@liveblocks/client";
|
import { LiveList, LiveObject, ThreadData, createClient } from "@liveblocks/client";
|
||||||
import { createRoomContext } from "@liveblocks/react";
|
import { createRoomContext } from "@liveblocks/react";
|
||||||
|
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
authEndpoint: "/api/liveblocks-auth",
|
authEndpoint: "/api/liveblocks-auth",
|
||||||
throttle: 100,
|
throttle: 100,
|
||||||
|
resolveUsers: async ({userIds}) => {
|
||||||
|
const params = new URLSearchParams(userIds.map(id => ['ids', id]));
|
||||||
|
const response = await fetch(`/api/users?` + params.toString());
|
||||||
|
return await response.json();
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type Presence = {
|
type Presence = {
|
||||||
|
@ -28,6 +34,23 @@ type Storage = {
|
||||||
cards: LiveList<LiveObject<Card>>;
|
cards: LiveList<LiveObject<Card>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UserMeta = {
|
||||||
|
id: string;
|
||||||
|
info: {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
image: string;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type RoomEvent = {};
|
||||||
|
|
||||||
|
type ThreadMetadata = {
|
||||||
|
cardId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
RoomProvider,
|
RoomProvider,
|
||||||
|
@ -37,9 +60,28 @@ export const {
|
||||||
useRoom,
|
useRoom,
|
||||||
useSelf,
|
useSelf,
|
||||||
useOthers,
|
useOthers,
|
||||||
|
useThreads,
|
||||||
|
|
||||||
/* ...all the other hooks you’re using... */
|
/* ...all the other hooks you’re using... */
|
||||||
} = createRoomContext<
|
} = createRoomContext<
|
||||||
Presence,
|
Presence,
|
||||||
Storage
|
Storage,
|
||||||
/* UserMeta, RoomEvent, ThreadMetadata */
|
UserMeta,
|
||||||
>(client);
|
RoomEvent,
|
||||||
|
ThreadMetadata
|
||||||
|
|
||||||
|
>(client, {
|
||||||
|
/*resolveUsers: async ({userIds}) => {
|
||||||
|
const params = new URLSearchParams(userIds.map(id => ['ids', id]));
|
||||||
|
const response = await fetch(`/api/users?` + params.toString());
|
||||||
|
return await response.json();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading to 1.10 instead of putting this in createRoomContext --> move to createClient
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
});
|
|
@ -2,12 +2,15 @@
|
||||||
import { useParams, useRouter } from "next/navigation"
|
import { useParams, useRouter } from "next/navigation"
|
||||||
import { FormEvent, useContext, useEffect, useState } from "react";
|
import { FormEvent, useContext, useEffect, useState } from "react";
|
||||||
import { BoardContext, BoardContextProps } from "../BoardContext";
|
import { BoardContext, BoardContextProps } from "../BoardContext";
|
||||||
import { Card, useMutation, useStorage } from "@/app/liveblocks.config";
|
import { Card, useMutation, useStorage, useThreads } from "@/app/liveblocks.config";
|
||||||
import { shallow } from "@liveblocks/client";
|
import { shallow } from "@liveblocks/client";
|
||||||
import { BsThreeDots } from "react-icons/bs";
|
import { BsThreeDots } from "react-icons/bs";
|
||||||
import DeleteConfirmation from "../DeleteConfirmation";
|
import DeleteConfirmation from "../DeleteConfirmation";
|
||||||
import { IoDocumentTextOutline } from "react-icons/io5";
|
import { IoDocumentTextOutline } from "react-icons/io5";
|
||||||
|
import { FaRegCommentDots } from "react-icons/fa6";
|
||||||
import CardDesc from "../CardDesc";
|
import CardDesc from "../CardDesc";
|
||||||
|
import { Composer, Thread } from "@liveblocks/react-comments";
|
||||||
|
import "@liveblocks/react-comments/styles.css";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +18,14 @@ import CardDesc from "../CardDesc";
|
||||||
const CardModal = () => {
|
const CardModal = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const {threads} = useThreads({
|
||||||
|
query:{
|
||||||
|
metadata:{
|
||||||
|
cardId:params.cardId?.toString(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const {setOpenCard} = useContext<BoardContextProps>(BoardContext);
|
const {setOpenCard} = useContext<BoardContextProps>(BoardContext);
|
||||||
const [editCard, setEditCard] = useState(false);
|
const [editCard, setEditCard] = useState(false);
|
||||||
|
|
||||||
|
@ -69,7 +80,7 @@ const CardModal = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onClick={handleBackdrop} className='fixed inset-0 bg-black/80'>
|
<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'>
|
<div onClick={ev => ev.stopPropagation()} className='bg-white p-4 mt-8 max-w-sm mx-auto'>
|
||||||
{!editCard && (
|
{!editCard && (
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<p className="mb-6 uppercase text-sm font-semibold">{card?.name}</p>
|
<p className="mb-6 uppercase text-sm font-semibold">{card?.name}</p>
|
||||||
|
@ -80,7 +91,7 @@ const CardModal = () => {
|
||||||
<div>
|
<div>
|
||||||
<form onSubmit={handleCardNamechange} className="flex flex-col">
|
<form onSubmit={handleCardNamechange} className="flex flex-col">
|
||||||
<input type="text" defaultValue={card?.name} className="mb-4 border p-2 rounded-md"/>
|
<input type="text" defaultValue={card?.name} className="mb-4 border p-2 rounded-md"/>
|
||||||
<button type="submit" className="uppercase">save</button>
|
<button type="submit" className="uppercase w-full flex text-center justify-center itemc">save</button>
|
||||||
</form>
|
</form>
|
||||||
<div>
|
<div>
|
||||||
<DeleteConfirmation onDelete={() => handleDelete()}/>
|
<DeleteConfirmation onDelete={() => handleDelete()}/>
|
||||||
|
@ -91,6 +102,20 @@ const CardModal = () => {
|
||||||
<div>
|
<div>
|
||||||
<h3 className="flex items-center"><IoDocumentTextOutline/> Description</h3>
|
<h3 className="flex items-center"><IoDocumentTextOutline/> Description</h3>
|
||||||
<CardDesc/>
|
<CardDesc/>
|
||||||
|
<h3 className="flex items-center mt-4"><FaRegCommentDots/> Comments</h3>
|
||||||
|
<div className="mt-4">
|
||||||
|
{threads && threads.map(thread => (
|
||||||
|
<div key={thread.id}>
|
||||||
|
<Thread thread={thread} id={thread.id}/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{threads?.length === 0 && (
|
||||||
|
<div>
|
||||||
|
<Composer metadata={{cardId:params.cardId.toString()}}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
21
task_management_app/src/models/User.ts
Normal file
21
task_management_app/src/models/User.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import {Schema, models, model} from 'mongoose'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type ModelType = {
|
||||||
|
User: any;
|
||||||
|
};
|
||||||
|
export type UserType = {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
image: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userSchema = new Schema({
|
||||||
|
name: String,
|
||||||
|
email: String,
|
||||||
|
image: String,
|
||||||
|
emailVerified: Date,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const User = (models as ModelType)?.User || model('User', userSchema);
|
Loading…
Add table
Reference in a new issue