feat: assign members on new card
This commit is contained in:
@@ -14,6 +14,7 @@ import { useModal } from "~/app/providers/modal";
|
||||
interface FormData {
|
||||
title: string;
|
||||
listPublicId: string;
|
||||
memberPublicIds: string[];
|
||||
}
|
||||
|
||||
interface NewCardFormProps {
|
||||
@@ -34,9 +35,12 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
|
||||
defaultValues: {
|
||||
title: "",
|
||||
listPublicId,
|
||||
memberPublicIds: [],
|
||||
},
|
||||
});
|
||||
|
||||
const memberPublicIds = watch("memberPublicIds") || [];
|
||||
|
||||
const refetchBoard = () =>
|
||||
utils.board.byId.refetch({ id: boardData.publicId });
|
||||
|
||||
@@ -64,10 +68,18 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
|
||||
selected: list.publicId === watch("listPublicId"),
|
||||
})) ?? [];
|
||||
|
||||
const formattedMembers =
|
||||
boardData?.workspace?.members?.map((member) => ({
|
||||
key: member.publicId,
|
||||
value: member.user.name,
|
||||
selected: memberPublicIds.includes(member.publicId),
|
||||
})) ?? [];
|
||||
|
||||
const onSubmit = (data: FormData) => {
|
||||
createCard.mutate({
|
||||
title: data.title,
|
||||
listPublicId: data.listPublicId,
|
||||
memberPublicIds: data.memberPublicIds,
|
||||
});
|
||||
reset();
|
||||
};
|
||||
@@ -76,6 +88,17 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
|
||||
setValue("listPublicId", listPublicId);
|
||||
};
|
||||
|
||||
const handleSelectMembers = (memberPublicId: string): void => {
|
||||
const currentIndex = memberPublicIds.indexOf(memberPublicId);
|
||||
if (currentIndex === -1) {
|
||||
setValue("memberPublicIds", [...memberPublicIds, memberPublicId]);
|
||||
} else {
|
||||
const newMemberPublicIds = [...memberPublicIds];
|
||||
newMemberPublicIds.splice(currentIndex, 1);
|
||||
setValue("memberPublicIds", newMemberPublicIds);
|
||||
}
|
||||
};
|
||||
|
||||
const selectedList = formattedLists.find((item) => item.selected);
|
||||
|
||||
return (
|
||||
@@ -105,16 +128,59 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
|
||||
className="block w-full rounded-md border-0 bg-dark-300 bg-white/5 py-1.5 text-dark-1000 shadow-sm ring-1 ring-inset ring-dark-700 focus:ring-2 focus:ring-inset focus:ring-dark-700 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 w-fit">
|
||||
<CheckboxDropdown
|
||||
items={formattedLists}
|
||||
handleSelect={(list: { key: string }) => handleSelectList(list.key)}
|
||||
>
|
||||
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-dark-600 bg-dark-400 px-2 py-1.5 text-left text-xs text-dark-1000 hover:bg-dark-500">
|
||||
{selectedList?.value}
|
||||
</div>
|
||||
</CheckboxDropdown>
|
||||
<div className="mt-2 flex space-x-1">
|
||||
<div className="w-fit">
|
||||
<CheckboxDropdown
|
||||
items={formattedLists}
|
||||
handleSelect={(list: { key: string }) =>
|
||||
handleSelectList(list.key)
|
||||
}
|
||||
>
|
||||
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-dark-600 bg-dark-400 px-2 py-1 text-left text-xs text-dark-1000 hover:bg-dark-500">
|
||||
{selectedList?.value}
|
||||
</div>
|
||||
</CheckboxDropdown>
|
||||
</div>
|
||||
<div className="w-fit">
|
||||
<CheckboxDropdown
|
||||
items={formattedMembers}
|
||||
handleSelect={(list: { key: string }) =>
|
||||
handleSelectMembers(list.key)
|
||||
}
|
||||
>
|
||||
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-dark-600 bg-dark-400 px-2 py-1 text-left text-xs text-dark-1000 hover:bg-dark-500">
|
||||
{!memberPublicIds.length ? (
|
||||
"Members"
|
||||
) : (
|
||||
<>
|
||||
{memberPublicIds.map((memberPublicId) => {
|
||||
const member = formattedMembers.find(
|
||||
(member) => member.key === memberPublicId,
|
||||
);
|
||||
|
||||
return (
|
||||
<span
|
||||
key={member?.key}
|
||||
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-gray-400 ring-1 ring-dark-500"
|
||||
>
|
||||
<span className="text-[8px] font-medium leading-none text-white">
|
||||
{member?.value
|
||||
.split(" ")
|
||||
.map((namePart) =>
|
||||
namePart.charAt(0).toUpperCase(),
|
||||
)
|
||||
.join("")}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</CheckboxDropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex items-center justify-end">
|
||||
<span className="mr-2 text-xs text-dark-900">Create more</span>
|
||||
<Switch
|
||||
|
||||
@@ -20,6 +20,9 @@ interface BoardData {
|
||||
name: string;
|
||||
publicId: string;
|
||||
lists: List[];
|
||||
workspace: {
|
||||
members: Members[];
|
||||
};
|
||||
}
|
||||
|
||||
interface List {
|
||||
@@ -30,6 +33,13 @@ interface List {
|
||||
cards: Card[];
|
||||
}
|
||||
|
||||
interface Members {
|
||||
publicId: string;
|
||||
user: {
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Card {
|
||||
publicId: string;
|
||||
title: string;
|
||||
@@ -52,6 +62,9 @@ const initialBoardData: BoardData = {
|
||||
name: "",
|
||||
publicId: "",
|
||||
lists: [],
|
||||
workspace: {
|
||||
members: [],
|
||||
},
|
||||
};
|
||||
|
||||
const BoardContext = createContext<BoardContextProps | undefined>(undefined);
|
||||
|
||||
@@ -43,6 +43,25 @@ export const boardRouter = createTRPCRouter({
|
||||
name: true,
|
||||
},
|
||||
with: {
|
||||
workspace: {
|
||||
columns: {
|
||||
publicId: true,
|
||||
},
|
||||
with: {
|
||||
members: {
|
||||
columns: {
|
||||
publicId: true,
|
||||
},
|
||||
with: {
|
||||
user: {
|
||||
columns: {
|
||||
name: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
lists: {
|
||||
orderBy: [asc(lists.index)],
|
||||
where: isNull(cards.deletedAt),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { and, desc, eq, isNull, sql } from "drizzle-orm";
|
||||
import { and, desc, eq, isNull, inArray, sql } from "drizzle-orm";
|
||||
|
||||
import { cards, cardsToLabels, cardToWorkspaceMembers, labels, lists, workspaceMembers } from "~/server/db/schema";
|
||||
import { generateUID } from "~/utils/generateUID";
|
||||
@@ -15,6 +15,7 @@ export const cardRouter = createTRPCRouter({
|
||||
z.object({
|
||||
title: z.string().min(1),
|
||||
listPublicId: z.string().min(12),
|
||||
memberPublicIds: z.array(z.string().min(12))
|
||||
}),
|
||||
)
|
||||
.mutation(({ ctx, input }) => {
|
||||
@@ -40,13 +41,27 @@ export const cardRouter = createTRPCRouter({
|
||||
orderBy: desc(cards.index)
|
||||
});
|
||||
|
||||
return tx.insert(cards).values({
|
||||
const newCard = await tx.insert(cards).values({
|
||||
publicId: generateUID(),
|
||||
title: input.title,
|
||||
createdBy: userId,
|
||||
listId: list.id,
|
||||
index: latestCard ? latestCard.index + 1 : 0
|
||||
});
|
||||
|
||||
if (newCard.insertId && input.memberPublicIds.length) {
|
||||
const members = await tx.query.workspaceMembers.findMany({
|
||||
where: inArray(workspaceMembers.publicId, input.memberPublicIds),
|
||||
});
|
||||
|
||||
if (!members.length) return;
|
||||
|
||||
const membersInsert = members.map((member) => ({ cardId: Number(newCard.insertId), workspaceMemberId: member.id}))
|
||||
|
||||
await tx.insert(cardToWorkspaceMembers).values(membersInsert);
|
||||
}
|
||||
|
||||
return newCard;
|
||||
})
|
||||
}),
|
||||
addOrRemoveLabel: publicProcedure
|
||||
|
||||
Reference in New Issue
Block a user