feat: assign labels on new card

This commit is contained in:
Henry
2024-03-24 14:14:29 +00:00
parent f524d49d7f
commit 87a05c6208
4 changed files with 115 additions and 4 deletions

View File

@@ -14,7 +14,9 @@ import { useModal } from "~/app/providers/modal";
interface FormData {
title: string;
listPublicId: string;
labelPublicIds: string[];
memberPublicIds: string[];
isCreateAnotherEnabled: boolean;
}
interface NewCardFormProps {
@@ -29,17 +31,20 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
const utils = api.useUtils();
const { boardData } = useBoard();
const { closeModal } = useModal();
const [isCreateAnotherEnabled, setIsCreateAnotherEnabled] = useState(false);
const { register, handleSubmit, reset, setValue, watch } = useForm<FormData>({
defaultValues: {
title: "",
listPublicId,
labelPublicIds: [],
memberPublicIds: [],
isCreateAnotherEnabled: false,
},
});
const labelPublicIds = watch("labelPublicIds") || [];
const memberPublicIds = watch("memberPublicIds") || [];
const isCreateAnotherEnabled = watch("isCreateAnotherEnabled");
const refetchBoard = () =>
utils.board.byId.refetch({ id: boardData.publicId });
@@ -49,6 +54,13 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
try {
await refetchBoard();
if (!isCreateAnotherEnabled) closeModal();
reset({
title: "",
listPublicId: watch("listPublicId"),
labelPublicIds: [],
memberPublicIds: [],
isCreateAnotherEnabled: watch("isCreateAnotherEnabled"),
});
} catch (e) {
console.log(e);
}
@@ -61,6 +73,13 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
if (titleElement) titleElement.focus();
}, []);
const formattedLabels =
boardData?.labels.map((label) => ({
key: label.publicId,
value: label.name,
selected: labelPublicIds.includes(label.publicId),
})) ?? [];
const formattedLists =
boardData?.lists.map((list) => ({
key: list.publicId,
@@ -79,9 +98,13 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
createCard.mutate({
title: data.title,
listPublicId: data.listPublicId,
labelsPublicIds: data.labelPublicIds,
memberPublicIds: data.memberPublicIds,
});
reset();
};
const handleToggleCreateAnother = (): void => {
setValue("isCreateAnotherEnabled", !isCreateAnotherEnabled);
};
const handleSelectList = (listPublicId: string): void => {
@@ -99,6 +122,17 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
}
};
const handleSelectLabels = (labelPublicId: string): void => {
const currentIndex = labelPublicIds.indexOf(labelPublicId);
if (currentIndex === -1) {
setValue("labelPublicIds", [...labelPublicIds, labelPublicId]);
} else {
const newLabelPublicIds = [...labelPublicIds];
newLabelPublicIds.splice(currentIndex, 1);
setValue("labelPublicIds", newLabelPublicIds);
}
};
const selectedList = formattedLists.find((item) => item.selected);
return (
@@ -152,7 +186,7 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
{!memberPublicIds.length ? (
"Members"
) : (
<>
<div className="flex -space-x-1 overflow-hidden">
{memberPublicIds.map((memberPublicId) => {
const member = formattedMembers.find(
(member) => member.key === memberPublicId,
@@ -174,6 +208,55 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
</span>
);
})}
</div>
)}
</div>
</CheckboxDropdown>
</div>
<div className="w-fit">
<CheckboxDropdown
items={formattedLabels}
handleSelect={(list: { key: string }) =>
handleSelectLabels(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">
{!labelPublicIds.length ? (
"Labels"
) : (
<>
<div
className={
labelPublicIds.length > 1
? "flex -space-x-[2px] overflow-hidden"
: "flex items-center"
}
>
{labelPublicIds.map((labelPublicId) => {
const label = boardData?.labels.find(
(label) => label.publicId === labelPublicId,
);
return (
<>
<svg
fill={label?.colourCode}
className="h-2 w-2"
viewBox="0 0 6 6"
aria-hidden="true"
>
<circle cx={3} cy={3} r={3} />
</svg>
{labelPublicIds.length === 1 && (
<div className="ml-1">{label?.name}</div>
)}
</>
);
})}
</div>
{labelPublicIds.length > 1 && (
<div className="ml-1">{`${labelPublicIds.length} labels`}</div>
)}
</>
)}
</div>
@@ -185,7 +268,7 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
<span className="mr-2 text-xs text-dark-900">Create more</span>
<Switch
checked={isCreateAnotherEnabled}
onChange={setIsCreateAnotherEnabled}
onChange={handleToggleCreateAnother}
className={classNames(
isCreateAnotherEnabled ? "bg-indigo-600" : "bg-dark-800",
"relative inline-flex h-4 w-6 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none",

View File

@@ -19,12 +19,19 @@ interface BoardContextProps {
interface BoardData {
name: string;
publicId: string;
labels: Label[];
lists: List[];
workspace: {
members: Members[];
};
}
interface Label {
publicId: string;
name: string;
colourCode: string;
}
interface List {
publicId: string;
name: string;
@@ -62,6 +69,7 @@ const initialBoardData: BoardData = {
name: "",
publicId: "",
lists: [],
labels: [],
workspace: {
members: [],
},

View File

@@ -62,6 +62,13 @@ export const boardRouter = createTRPCRouter({
}
}
},
labels: {
columns: {
publicId: true,
colourCode: true,
name: true,
}
},
lists: {
orderBy: [asc(lists.index)],
where: isNull(cards.deletedAt),

View File

@@ -15,6 +15,7 @@ export const cardRouter = createTRPCRouter({
z.object({
title: z.string().min(1),
listPublicId: z.string().min(12),
labelsPublicIds: z.array(z.string().min(12)),
memberPublicIds: z.array(z.string().min(12))
}),
)
@@ -49,6 +50,18 @@ export const cardRouter = createTRPCRouter({
index: latestCard ? latestCard.index + 1 : 0
});
if (newCard.insertId && input.labelsPublicIds.length) {
const labels = await tx.query.labels.findMany({
where: inArray(cards.publicId, input.labelsPublicIds),
});
if (!labels.length) return;
const labelsInsert = labels.map((label) => ({ cardId: Number(newCard.insertId), labelId: label.id }))
await tx.insert(cardsToLabels).values(labelsInsert);
}
if (newCard.insertId && input.memberPublicIds.length) {
const members = await tx.query.workspaceMembers.findMany({
where: inArray(workspaceMembers.publicId, input.memberPublicIds),