diff --git a/src/app/boards/[...boardId]/page.tsx b/src/app/boards/[...boardId]/page.tsx
index bea1c4b4..0740ba22 100644
--- a/src/app/boards/[...boardId]/page.tsx
+++ b/src/app/boards/[...boardId]/page.tsx
@@ -255,23 +255,25 @@ export default function BoardPage() {
{label.name}
))}
- {card.members?.map(({ member }) => (
-
-
- {member.user.name
- .split(" ")
- .map((namePart) =>
- namePart
- .charAt(0)
- .toUpperCase(),
- )
- .join("")}
+
+ {card.members?.map(({ member }) => (
+
+
+ {member.user.name
+ .split(" ")
+ .map((namePart) =>
+ namePart
+ .charAt(0)
+ .toUpperCase(),
+ )
+ .join("")}
+
-
- ))}
+ ))}
+
) : null}
diff --git a/src/app/cards/[cardId]/components/MemberSelector.tsx b/src/app/cards/[cardId]/components/MemberSelector.tsx
new file mode 100644
index 00000000..7d1088d7
--- /dev/null
+++ b/src/app/cards/[cardId]/components/MemberSelector.tsx
@@ -0,0 +1,141 @@
+"use client";
+
+import { Fragment } from "react";
+import { api } from "~/trpc/react";
+import { Menu, Transition } from "@headlessui/react";
+import { HiMiniPlus } from "react-icons/hi2";
+import { useFormik } from "formik";
+
+interface MemberSelectorProps {
+ cardPublicId: string;
+ members: {
+ publicId: string;
+ user: {
+ id: string;
+ name: string | null;
+ };
+ selected: boolean;
+ }[];
+}
+
+export default function LabelSelector({
+ cardPublicId,
+ members,
+}: MemberSelectorProps) {
+ const utils = api.useUtils();
+
+ const refetchCard = () => utils.card.byId.refetch({ id: cardPublicId });
+
+ const addOrRemoveMember = api.card.addOrRemoveMember.useMutation({
+ onSuccess: async () => {
+ await refetchCard();
+ },
+ });
+
+ const formik = useFormik({
+ initialValues: {
+ ...Object.fromEntries(
+ members?.map((member) => [member.publicId, member.selected]) ?? [],
+ ),
+ },
+ onSubmit: (values) => {
+ console.log({ values });
+ },
+ enableReinitialize: true,
+ });
+
+ const selectedMembers = members.filter((member) => member.selected);
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/src/app/cards/[cardId]/page.tsx b/src/app/cards/[cardId]/page.tsx
index 0897f257..02d14ed9 100644
--- a/src/app/cards/[cardId]/page.tsx
+++ b/src/app/cards/[cardId]/page.tsx
@@ -10,6 +10,7 @@ import Dropdown from "./components/Dropdown";
import { DeleteCardConfirmation } from "./components/DeleteCardConfirmation";
import LabelSelector from "./components/LabelSelector";
import ListSelector from "./components/ListSelector";
+import MemberSelector from "./components/MemberSelector";
import { NewLabelForm } from "./components/NewLabelForm";
import Modal from "~/app/components/modal";
@@ -35,8 +36,10 @@ export default function CardPage() {
const boardId = data?.list?.board?.publicId;
const labels = data?.list?.board?.labels;
+ const workspaceMembers = data?.list?.board?.workspace?.members;
const board = data?.list?.board;
const selectedLabels = data?.labels;
+ const selectedMembers = data?.members;
const formattedLabels =
labels?.map((label) => {
@@ -57,6 +60,18 @@ export default function CardPage() {
selected: list.publicId === data?.list.publicId,
})) ?? [];
+ const formattedMembers =
+ workspaceMembers?.map((member) => {
+ const isSelected = selectedMembers?.some(
+ (assignedMember) => assignedMember.member.publicId === member.publicId,
+ );
+
+ return {
+ ...member,
+ selected: isSelected ?? false,
+ };
+ }) ?? [];
+
const updateCard = api.card.update.useMutation();
const formik = useFormik({
@@ -126,10 +141,14 @@ export default function CardPage() {
List
-
diff --git a/src/server/api/routers/card.ts b/src/server/api/routers/card.ts
index 8bca36d1..0022d905 100644
--- a/src/server/api/routers/card.ts
+++ b/src/server/api/routers/card.ts
@@ -1,7 +1,7 @@
import { z } from "zod";
import { and, desc, eq, isNull, sql } from "drizzle-orm";
-import { cards, cardsToLabels, labels, lists } from "~/server/db/schema";
+import { cards, cardsToLabels, cardToWorkspaceMembers, labels, lists, workspaceMembers } from "~/server/db/schema";
import { generateUID } from "~/utils/generateUID";
import {
@@ -86,6 +86,45 @@ export const cardRouter = createTRPCRouter({
});
})
}),
+ addOrRemoveMember: publicProcedure
+ .input(
+ z.object({
+ cardPublicId: z.string().min(12),
+ workspaceMemberPublicId: z.string().min(12),
+ }),
+ )
+ .mutation(({ ctx, input }) => {
+ const userId = ctx.session?.user.id;
+
+ if (!userId) return;
+
+ return ctx.db.transaction(async (tx) => {
+ const card = await tx.query.cards.findFirst({
+ where: and(eq(cards.publicId, input.cardPublicId), isNull(cards.deletedAt)),
+ });
+
+ const member = await tx.query.workspaceMembers.findFirst({
+ where: eq(workspaceMembers.publicId, input.workspaceMemberPublicId),
+ });
+
+ if (!card || !member) return;
+
+ console.log({ member })
+
+ const memberExists = await tx.query.cardToWorkspaceMembers.findFirst({
+ where: and(eq(cardToWorkspaceMembers.cardId, card.id), eq(cardToWorkspaceMembers.workspaceMemberId, member.id)),
+ });
+
+ if (memberExists) {
+ return tx.delete(cardToWorkspaceMembers).where(and(eq(cardToWorkspaceMembers.cardId, card.id), eq(cardToWorkspaceMembers.workspaceMemberId, member.id)),);
+ }
+
+ return tx.insert(cardToWorkspaceMembers).values({
+ cardId: card.id,
+ workspaceMemberId: member.id,
+ });
+ })
+ }),
byId: publicProcedure
.input(z.object({ id: z.string().min(12) }))
.query(({ ctx, input }) =>
@@ -103,7 +142,7 @@ export const cardRouter = createTRPCRouter({
name: true,
colourCode: true,
}
- }
+ },
}
},
list: {
@@ -130,9 +169,50 @@ export const cardRouter = createTRPCRouter({
name: true,
},
where: isNull(lists.deletedAt)
+ },
+ workspace: {
+ columns: {
+ publicId: true,
+ },
+ with: {
+ members: {
+ columns: {
+ publicId: true,
+ },
+ with: {
+ user: {
+ columns: {
+ id: true,
+ name: true,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ members: {
+ columns: {
+ workspaceMemberId: false,
+ cardId: false,
+ },
+ with: {
+ member: {
+ columns: {
+ publicId: true,
+ },
+ with: {
+ user: {
+ columns: {
+ id: true,
+ name: true,
+ }
}
}
- }
+ },
}
},
},
diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts
index bb3b0e17..03100bd4 100644
--- a/src/server/db/schema.ts
+++ b/src/server/db/schema.ts
@@ -334,8 +334,9 @@ export const workspaces = mySqlTable(
}
);
-export const workspaceRelations = relations(workspaces, ({ one }) => ({
+export const workspaceRelations = relations(workspaces, ({ one, many }) => ({
user: one(users, { fields: [workspaces.createdBy], references: [users.id] }),
+ members: many(workspaceMembers)
}));
export const workspaceMembers = mySqlTable(