Skip to content

Commit

Permalink
feat(ui): UI update on member management of a given board
Browse files Browse the repository at this point in the history
  • Loading branch information
DW225 committed Sep 7, 2024
1 parent 604f160 commit 0ed2bbb
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 66 deletions.
4 changes: 3 additions & 1 deletion app/board/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ export default async function BoardPage({ params }: BoardPageProps) {
<NavBar />
<BoardProvider>
<div className="container mx-auto w-full max-w-full px-4">
<BoardAccess boardId={boardID}/>
<div className="flex justify-end">
<BoardAccess boardId={boardID} role={role}/>
</div>
<BoardGrid boardID={boardID} />
</div>
</BoardProvider>
Expand Down
39 changes: 0 additions & 39 deletions components/board/AddMemberForm.tsx

This file was deleted.

44 changes: 33 additions & 11 deletions components/board/BoardAccess.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
import { Suspense } from "react";
import AddMemberForm from "./AddMemberForm";
import MemberList from "./MemberList";
import { fetchMembersByBoardID } from "@/lib/db/member"; // Assume this function exists to fetch board members
import { Role } from "@/db/schema";
import { fetchMembersByBoardID } from "@/lib/db/member";
import MD5 from "crypto-js/md5";
import Image from "next/image";
import MemberManage from "./MemberManage";

interface BoardAccessProps {
boardId: string;
role: Role;
}

export default async function BoardAccess({ boardId }: BoardAccessProps) {
export default async function BoardAccess({ boardId, role }: BoardAccessProps) {
const members = await fetchMembersByBoardID(boardId);
const memberCount = members.length;

return (
<div className="p-4 bg-white rounded-lg shadow">
<h2 className="text-xl font-bold mb-4">Manage Board Access</h2>
<AddMemberForm boardId={boardId} />
<Suspense fallback={<div>Loading members...</div>}>
<MemberList boardId={boardId} initialMembers={members} />
</Suspense>
<div className="p-4 flex items-center justify-between">
<div className="avatar-group -space-x-6 rtl:space-x-reverse mr-0.5">
{members.slice(0, 3).map((member) => (
<div className="avatar" key={member.id}>
<div className="w-12">
<Image
src={`https://www.gravatar.com/avatar/${MD5(
member.email
)}?d=mp&s=48`}
alt={`${member.username} avatar`}
width={48}
height={48}
/>
</div>
</div>
))}
{memberCount > 3 && (
<div className="avatar placeholder">
<div className="bg-neutral text-neutral-content w-12">
<span>+{memberCount - 3}</span>
</div>
</div>
)}
</div>
{role === Role.owner && <MemberManage boardId={boardId} />}
</div>
);
}
61 changes: 61 additions & 0 deletions components/board/MemberManage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"use client";

import React, { useState } from "react";
import { addMemberToBoard } from "@/lib/actions/boardActions";

interface MemberManageProps {
boardId: string;
}

export default function MemberManage({ boardId }: MemberManageProps) {
const [email, setEmail] = useState("");
const [isModalOpen, setIsModalOpen] = useState(false);

async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (email.trim()) {
await addMemberToBoard(boardId, email.trim());
setEmail("");
setIsModalOpen(false);
}
}

return (
<>
<button
onClick={() => setIsModalOpen(true)}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Manage Members
</button>
{isModalOpen && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
<div className="bg-white p-4 rounded">
<form onSubmit={handleSubmit} className="mb-4">
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email address"
className="w-full p-2 border rounded"
required
/>
<button
type="submit"
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Add Member
</button>
</form>
<button
onClick={() => setIsModalOpen(false)}
className="px-4 py-2 bg-gray-300 text-black rounded hover:bg-gray-400"
>
Close
</button>
</div>
</div>
)}
</>
);
}
13 changes: 8 additions & 5 deletions lib/db/member.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { NewMember } from "@/db/schema";
import { memberTable } from "@/db/schema";
import { memberTable, userTable } from "@/db/schema";
import { db } from "./client";
import { and, eq } from "drizzle-orm";

Expand All @@ -26,18 +26,21 @@ export const fetchMembersByBoardID = async (boardID: string) => {
id: memberTable.id,
userId: memberTable.userId,
role: memberTable.role,
username: userTable.name,
email: userTable.email,
updateAt: memberTable.updatedAt,
})
.from(memberTable)
.innerJoin(userTable, eq(memberTable.userId, userTable.id))
.where(eq(memberTable.boardId, boardID));
}
};

export const checkMemberRole = async (userID: string, boardID: string) => {
const member = await db
.select()
.from(memberTable)
.where(
.where(
and(eq(memberTable.userId, userID), eq(memberTable.boardId, boardID))
);
return member? member[0].role : null;
}
return member ? member[0].role : null;
};
6 changes: 6 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ const nextConfig = {
port: "",
pathname: "/avatar/**",
},
{
protocol: "https",
hostname: "www.gravatar.com",
port: "",
pathname: "/avatar/**",
},
],
},
};
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@next/env": "^14.2.7",
"@preact/signals-react": "^2.2.0",
"@vercel/speed-insights": "^1.0.12",
"crypto-js": "^4.2.0",
"dotenv": "^16.4.5",
"drizzle-orm": "^0.33.0",
"drizzle-zod": "^0.5.1",
Expand All @@ -38,6 +39,7 @@
"devDependencies": {
"@eslint/js": "^9.9.1",
"@tailwindcss/typography": "^0.5.15",
"@types/crypto-js": "^4.2.2",
"@types/eslint__js": "^8.42.3",
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^22.5.3",
Expand Down
41 changes: 31 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0ed2bbb

Please sign in to comment.