import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { HiOutlineBarsArrowDown, HiOutlineBarsArrowUp, HiXMark, } from "react-icons/hi2"; import type { NewCardInput } from "@kan/api/types"; import { generateUID } from "@kan/shared/utils"; import Avatar from "~/components/Avatar"; import Button from "~/components/Button"; import CheckboxDropdown from "~/components/CheckboxDropdown"; import Editor from "~/components/Editor"; import Input from "~/components/Input"; import LabelIcon from "~/components/LabelIcon"; import Toggle from "~/components/Toggle"; import { useModal } from "~/providers/modal"; import { usePopup } from "~/providers/popup"; import { api } from "~/utils/api"; import { formatMemberDisplayName, getAvatarUrl } from "~/utils/helpers"; type NewCardFormInput = NewCardInput & { isCreateAnotherEnabled: boolean; }; interface QueryParams { boardPublicId: string; members: string[]; labels: string[]; } interface NewCardFormProps { boardPublicId: string; listPublicId: string; queryParams: QueryParams; } export function NewCardForm({ boardPublicId, listPublicId, queryParams, }: NewCardFormProps) { const { showPopup } = usePopup(); const { closeModal, openModal } = useModal(); const utils = api.useUtils(); const { register, handleSubmit, reset, setValue, watch } = useForm({ defaultValues: { title: "", description: "", listPublicId, labelPublicIds: [], memberPublicIds: [], isCreateAnotherEnabled: false, position: "start", }, }); const labelPublicIds = watch("labelPublicIds") || []; const memberPublicIds = watch("memberPublicIds") || []; const isCreateAnotherEnabled = watch("isCreateAnotherEnabled"); const position = watch("position"); const title = watch("title"); const { data: boardData } = api.board.byId.useQuery(queryParams, { enabled: !!boardPublicId, }); const createCard = api.card.create.useMutation({ onMutate: async (args) => { await utils.board.byId.cancel(); const currentState = utils.board.byId.getData(queryParams); utils.board.byId.setData(queryParams, (oldBoard) => { if (!oldBoard) return oldBoard; const updatedLists = oldBoard.lists.map((list) => { if (list.publicId === listPublicId) { const newCard = { publicId: `PLACEHOLDER_${generateUID()}`, title: args.title, listId: 2, description: "", labels: oldBoard.labels.filter((label) => args.labelPublicIds.includes(label.publicId), ), members: oldBoard.workspace.members .filter((member) => args.memberPublicIds.includes(member.publicId), ) .map((member) => ({ ...member, deletedAt: null, })) ?? [], _filteredLabels: labelPublicIds.map((id) => ({ publicId: id })), _filteredMembers: memberPublicIds.map((id) => ({ publicId: id })), index: position === "start" ? 0 : list.cards.length, }; const updatedCards = position === "start" ? [newCard, ...list.cards] : [...list.cards, newCard]; return { ...list, cards: updatedCards }; } return list; }); return { ...oldBoard, lists: updatedLists }; }); return { previousState: currentState }; }, onError: (error, _newList, context) => { utils.board.byId.setData(queryParams, context?.previousState); showPopup({ header: t`Unable to create card`, message: error.data?.zodError?.fieldErrors.title?.[0] ? `${error.data.zodError.fieldErrors.title[0].replace("String", "Title")}` : t`Please try again later, or contact customer support.`, icon: "error", }); }, onSuccess: async () => { const isCreateAnotherEnabled = watch("isCreateAnotherEnabled"); if (!isCreateAnotherEnabled) closeModal(); await utils.board.byId.invalidate(queryParams); reset({ title: "", description: "", listPublicId: watch("listPublicId"), labelPublicIds: [], memberPublicIds: [], isCreateAnotherEnabled, position, }); }, }); useEffect(() => { const titleElement: HTMLElement | null = document.querySelector("#title"); if (titleElement) titleElement.focus(); }, []); const formattedLabels = boardData?.labels.map((label) => ({ key: label.publicId, value: label.name, leftIcon: , selected: labelPublicIds.includes(label.publicId), })) ?? []; const formattedLists = boardData?.lists.map((list) => ({ key: list.publicId, value: list.name, selected: list.publicId === watch("listPublicId"), })) ?? []; const formattedMembers = boardData?.workspace.members.map((member) => ({ key: member.publicId, value: formatMemberDisplayName( member.user?.name ?? null, member.user?.email ?? member.email, ), selected: memberPublicIds.includes(member.publicId), leftIcon: ( ), })) ?? []; const onSubmit = (data: NewCardInput) => { createCard.mutate({ title: data.title, description: data.description, listPublicId: data.listPublicId, labelPublicIds: data.labelPublicIds, memberPublicIds: data.memberPublicIds, position: data.position, }); }; const handleToggleCreateAnother = (): void => { setValue("isCreateAnotherEnabled", !isCreateAnotherEnabled); }; const handleSelectList = (listPublicId: string): void => { 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 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 (

{t`New card`}

{ if (e.key === "Enter") { e.preventDefault(); await handleSubmit(onSubmit)(); } }} />
setValue("description", value)} />
handleSelectList(item.key)} >
{selectedList?.value}
handleSelectMembers(item.key)} >
{!memberPublicIds.length ? ( t`Members` ) : (
{memberPublicIds.map((memberPublicId) => { const member = formattedMembers.find( (member) => member.key === memberPublicId, ); return ( {member?.value .split(" ") .map((namePart) => namePart.charAt(0).toUpperCase(), ) .join("")} ); })}
)}
handleSelectLabels(item.key)} handleEdit={(labelPublicId) => openModal("EDIT_LABEL", labelPublicId) } handleCreate={() => openModal("NEW_LABEL")} createNewItemLabel={t`Create new label`} >
{!labelPublicIds.length ? ( t`Labels` ) : ( <>
1 ? "flex -space-x-[2px] overflow-hidden" : "flex items-center" } > {labelPublicIds.map((labelPublicId) => { const label = boardData?.labels.find( (label) => label.publicId === labelPublicId, ); return ( <> {labelPublicIds.length === 1 && (
{label?.name}
)} ); })}
{labelPublicIds.length > 1 && (
{`${labelPublicIds.length} labels`}
)} )}
); }