feat: create label from new card modal
This commit is contained in:
@@ -135,12 +135,12 @@ export default function CheckboxDropdown({
|
|||||||
>
|
>
|
||||||
<Menu.Items
|
<Menu.Items
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"absolute z-50 mt-2 w-56 origin-top-left rounded-md border-[1px] border-light-200 bg-light-50 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-500 dark:bg-dark-200",
|
"mt-2s absolute z-50 w-56 origin-top-left rounded-md border-[1px] border-light-200 bg-light-50 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-dark-500 dark:bg-dark-200",
|
||||||
position === "left" ? "left-0" : "right-0",
|
position === "left" ? "left-0" : "right-0",
|
||||||
menuSpacingClass[menuSpacing],
|
menuSpacingClass[menuSpacing],
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="p-1">
|
<div className="max-h-[350px] overflow-y-auto p-1">
|
||||||
{!selectedGroup ? (
|
{!selectedGroup ? (
|
||||||
<>
|
<>
|
||||||
{items && renderMenuItems(items, null)}
|
{items && renderMenuItems(items, null)}
|
||||||
|
|||||||
@@ -4,20 +4,17 @@ import { usePopup } from "~/providers/popup";
|
|||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
export function DeleteLabelConfirmation({
|
export function DeleteLabelConfirmation({
|
||||||
cardPublicId,
|
|
||||||
labelPublicId,
|
labelPublicId,
|
||||||
|
refetch,
|
||||||
}: {
|
}: {
|
||||||
cardPublicId: string;
|
|
||||||
labelPublicId: string;
|
labelPublicId: string;
|
||||||
|
refetch: () => void;
|
||||||
}) {
|
}) {
|
||||||
const utils = api.useUtils();
|
|
||||||
const { closeModal } = useModal();
|
const { closeModal } = useModal();
|
||||||
const { showPopup } = usePopup();
|
const { showPopup } = usePopup();
|
||||||
|
|
||||||
const refetchCard = () => utils.card.byId.refetch({ cardPublicId });
|
|
||||||
|
|
||||||
const deleteLabelMutation = api.label.delete.useMutation({
|
const deleteLabelMutation = api.label.delete.useMutation({
|
||||||
onSuccess: () => refetchCard(),
|
onSuccess: () => refetch(),
|
||||||
onError: () =>
|
onError: () =>
|
||||||
showPopup({
|
showPopup({
|
||||||
header: "Error deleting label",
|
header: "Error deleting label",
|
||||||
@@ -23,13 +23,14 @@ interface Colour {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function LabelForm({
|
export function LabelForm({
|
||||||
cardPublicId,
|
boardPublicId,
|
||||||
|
refetch,
|
||||||
isEdit,
|
isEdit,
|
||||||
}: {
|
}: {
|
||||||
cardPublicId: string;
|
boardPublicId: string;
|
||||||
|
refetch: () => void;
|
||||||
isEdit?: boolean;
|
isEdit?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const utils = api.useUtils();
|
|
||||||
const { closeModal, entityId, openModal } = useModal();
|
const { closeModal, entityId, openModal } = useModal();
|
||||||
|
|
||||||
const label = api.label.byPublicId.useQuery(
|
const label = api.label.byPublicId.useQuery(
|
||||||
@@ -52,17 +53,15 @@ export function LabelForm({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const refetchCard = () => utils.card.byId.refetch({ cardPublicId });
|
|
||||||
|
|
||||||
const isCreateAnotherEnabled = watch("isCreateAnotherEnabled");
|
const isCreateAnotherEnabled = watch("isCreateAnotherEnabled");
|
||||||
|
|
||||||
const createLabel = api.label.create.useMutation({
|
const createLabel = api.label.create.useMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: () => {
|
||||||
const currentColourIndex = colours.findIndex(
|
const currentColourIndex = colours.findIndex(
|
||||||
(c) => c.code === watch("colour").code,
|
(c) => c.code === watch("colour").code,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
await refetchCard();
|
refetch();
|
||||||
if (!isCreateAnotherEnabled) closeModal();
|
if (!isCreateAnotherEnabled) closeModal();
|
||||||
reset({
|
reset({
|
||||||
name: "",
|
name: "",
|
||||||
@@ -76,8 +75,8 @@ export function LabelForm({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const updateLabel = api.label.update.useMutation({
|
const updateLabel = api.label.update.useMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: () => {
|
||||||
await refetchCard();
|
refetch();
|
||||||
closeModal();
|
closeModal();
|
||||||
reset({
|
reset({
|
||||||
name: "",
|
name: "",
|
||||||
@@ -98,8 +97,8 @@ export function LabelForm({
|
|||||||
} else {
|
} else {
|
||||||
createLabel.mutate({
|
createLabel.mutate({
|
||||||
name: values.name,
|
name: values.name,
|
||||||
cardPublicId,
|
|
||||||
colourCode: values.colour.code,
|
colourCode: values.colour.code,
|
||||||
|
boardPublicId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -43,7 +43,7 @@ export function NewCardForm({
|
|||||||
queryParams,
|
queryParams,
|
||||||
}: NewCardFormProps) {
|
}: NewCardFormProps) {
|
||||||
const { showPopup } = usePopup();
|
const { showPopup } = usePopup();
|
||||||
const { closeModal } = useModal();
|
const { closeModal, openModal } = useModal();
|
||||||
|
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
|
|
||||||
@@ -299,6 +299,11 @@ export function NewCardForm({
|
|||||||
<CheckboxDropdown
|
<CheckboxDropdown
|
||||||
items={formattedLabels}
|
items={formattedLabels}
|
||||||
handleSelect={(_groupKey, item) => handleSelectLabels(item.key)}
|
handleSelect={(_groupKey, item) => handleSelectLabels(item.key)}
|
||||||
|
handleEdit={(labelPublicId) =>
|
||||||
|
openModal("EDIT_LABEL", labelPublicId)
|
||||||
|
}
|
||||||
|
handleCreate={() => openModal("NEW_LABEL")}
|
||||||
|
createNewItemLabel="Create new label"
|
||||||
>
|
>
|
||||||
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-600 bg-light-200 px-2 py-1 text-left text-xs text-light-800 hover:bg-light-300 dark:border-dark-600 dark:bg-dark-400 dark:text-dark-1000 dark:hover:bg-dark-500">
|
<div className="flex h-full w-full items-center rounded-[5px] border-[1px] border-light-600 bg-light-200 px-2 py-1 text-left text-xs text-light-800 hover:bg-light-300 dark:border-dark-600 dark:bg-dark-400 dark:text-dark-1000 dark:hover:bg-dark-500">
|
||||||
{!labelPublicIds.length ? (
|
{!labelPublicIds.length ? (
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { HiOutlinePlusSmall, HiOutlineSquare3Stack3D } from "react-icons/hi2";
|
|||||||
import type { UpdateBoardInput } from "@kan/api/types";
|
import type { UpdateBoardInput } from "@kan/api/types";
|
||||||
|
|
||||||
import Button from "~/components/Button";
|
import Button from "~/components/Button";
|
||||||
|
import { DeleteLabelConfirmation } from "~/components/DeleteLabelConfirmation";
|
||||||
|
import { LabelForm } from "~/components/LabelForm";
|
||||||
import Modal from "~/components/modal";
|
import Modal from "~/components/modal";
|
||||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
@@ -40,7 +42,7 @@ export default function BoardPage() {
|
|||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
const { showPopup } = usePopup();
|
const { showPopup } = usePopup();
|
||||||
const { workspace } = useWorkspace();
|
const { workspace } = useWorkspace();
|
||||||
const { openModal, modalContentType } = useModal();
|
const { openModal, modalContentType, entityId } = useModal();
|
||||||
const [selectedPublicListId, setSelectedPublicListId] =
|
const [selectedPublicListId, setSelectedPublicListId] =
|
||||||
useState<PublicListId>("");
|
useState<PublicListId>("");
|
||||||
const [isInitialLoading, setIsInitialLoading] = useState(true);
|
const [isInitialLoading, setIsInitialLoading] = useState(true);
|
||||||
@@ -78,6 +80,10 @@ export default function BoardPage() {
|
|||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const refetchBoard = async () => {
|
||||||
|
if (boardId) await utils.board.byId.refetch({ boardPublicId: boardId });
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (boardId) {
|
if (boardId) {
|
||||||
setIsInitialLoading(false);
|
setIsInitialLoading(false);
|
||||||
@@ -409,6 +415,22 @@ export default function BoardPage() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{modalContentType === "NEW_WORKSPACE" && <NewWorkspaceForm />}
|
{modalContentType === "NEW_WORKSPACE" && <NewWorkspaceForm />}
|
||||||
|
{modalContentType === "NEW_LABEL" && (
|
||||||
|
<LabelForm boardPublicId={boardId ?? ""} refetch={refetchBoard} />
|
||||||
|
)}
|
||||||
|
{modalContentType === "EDIT_LABEL" && (
|
||||||
|
<LabelForm
|
||||||
|
boardPublicId={boardId ?? ""}
|
||||||
|
refetch={refetchBoard}
|
||||||
|
isEdit
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{modalContentType === "DELETE_LABEL" && (
|
||||||
|
<DeleteLabelConfirmation
|
||||||
|
refetch={refetchBoard}
|
||||||
|
labelPublicId={entityId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{modalContentType === "UPDATE_BOARD_SLUG" && (
|
{modalContentType === "UPDATE_BOARD_SLUG" && (
|
||||||
<UpdateBoardSlugForm
|
<UpdateBoardSlugForm
|
||||||
boardPublicId={boardId ?? ""}
|
boardPublicId={boardId ?? ""}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useForm } from "react-hook-form";
|
|||||||
import { IoChevronForwardSharp } from "react-icons/io5";
|
import { IoChevronForwardSharp } from "react-icons/io5";
|
||||||
|
|
||||||
import Avatar from "~/components/Avatar";
|
import Avatar from "~/components/Avatar";
|
||||||
|
import { LabelForm } from "~/components/LabelForm";
|
||||||
import LabelIcon from "~/components/LabelIcon";
|
import LabelIcon from "~/components/LabelIcon";
|
||||||
import Modal from "~/components/modal";
|
import Modal from "~/components/modal";
|
||||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||||
@@ -15,12 +16,11 @@ import { useWorkspace } from "~/providers/workspace";
|
|||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { formatMemberDisplayName } from "~/utils/helpers";
|
import { formatMemberDisplayName } from "~/utils/helpers";
|
||||||
import { getPublicUrl } from "~/utils/supabase/getPublicUrl";
|
import { getPublicUrl } from "~/utils/supabase/getPublicUrl";
|
||||||
|
import { DeleteLabelConfirmation } from "../../components/DeleteLabelConfirmation";
|
||||||
import ActivityList from "./components/ActivityList";
|
import ActivityList from "./components/ActivityList";
|
||||||
import { DeleteCardConfirmation } from "./components/DeleteCardConfirmation";
|
import { DeleteCardConfirmation } from "./components/DeleteCardConfirmation";
|
||||||
import { DeleteCommentConfirmation } from "./components/DeleteCommentConfirmation";
|
import { DeleteCommentConfirmation } from "./components/DeleteCommentConfirmation";
|
||||||
import { DeleteLabelConfirmation } from "./components/DeleteLabelConfirmation";
|
|
||||||
import Dropdown from "./components/Dropdown";
|
import Dropdown from "./components/Dropdown";
|
||||||
import { LabelForm } from "./components/LabelForm";
|
|
||||||
import LabelSelector from "./components/LabelSelector";
|
import LabelSelector from "./components/LabelSelector";
|
||||||
import ListSelector from "./components/ListSelector";
|
import ListSelector from "./components/ListSelector";
|
||||||
import MemberSelector from "./components/MemberSelector";
|
import MemberSelector from "./components/MemberSelector";
|
||||||
@@ -47,6 +47,10 @@ export default function CardPage() {
|
|||||||
cardPublicId: cardId ?? "",
|
cardPublicId: cardId ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const refetchCard = async () => {
|
||||||
|
if (cardId) await utils.card.byId.refetch({ cardPublicId: cardId });
|
||||||
|
};
|
||||||
|
|
||||||
const board = card?.list?.board;
|
const board = card?.list?.board;
|
||||||
const boardId = board?.publicId;
|
const boardId = board?.publicId;
|
||||||
const labels = board?.labels;
|
const labels = board?.labels;
|
||||||
@@ -246,14 +250,18 @@ export default function CardPage() {
|
|||||||
|
|
||||||
<Modal>
|
<Modal>
|
||||||
{modalContentType === "NEW_LABEL" && (
|
{modalContentType === "NEW_LABEL" && (
|
||||||
<LabelForm cardPublicId={cardId} />
|
<LabelForm boardPublicId={boardId ?? ""} refetch={refetchCard} />
|
||||||
)}
|
)}
|
||||||
{modalContentType === "EDIT_LABEL" && (
|
{modalContentType === "EDIT_LABEL" && (
|
||||||
<LabelForm cardPublicId={cardId} isEdit />
|
<LabelForm
|
||||||
|
boardPublicId={boardId ?? ""}
|
||||||
|
refetch={refetchCard}
|
||||||
|
isEdit
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{modalContentType === "DELETE_LABEL" && (
|
{modalContentType === "DELETE_LABEL" && (
|
||||||
<DeleteLabelConfirmation
|
<DeleteLabelConfirmation
|
||||||
cardPublicId={cardId}
|
refetch={refetchCard}
|
||||||
labelPublicId={entityId}
|
labelPublicId={entityId}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import * as boardRepo from "@kan/db/repository/board.repo";
|
||||||
import * as cardRepo from "@kan/db/repository/card.repo";
|
import * as cardRepo from "@kan/db/repository/card.repo";
|
||||||
import * as labelRepo from "@kan/db/repository/label.repo";
|
import * as labelRepo from "@kan/db/repository/label.repo";
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ export const labelRouter = createTRPCRouter({
|
|||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string().min(1).max(36),
|
name: z.string().min(1).max(36),
|
||||||
cardPublicId: z.string().min(12),
|
boardPublicId: z.string().min(12),
|
||||||
colourCode: z.string().length(7),
|
colourCode: z.string().length(7),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -59,14 +60,14 @@ export const labelRouter = createTRPCRouter({
|
|||||||
code: "UNAUTHORIZED",
|
code: "UNAUTHORIZED",
|
||||||
});
|
});
|
||||||
|
|
||||||
const card = await cardRepo.getCardWithListByPublicId(
|
const board = await boardRepo.getIdByPublicId(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
input.cardPublicId,
|
input.boardPublicId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!card?.list)
|
if (!board)
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
message: `Card with public ID ${input.cardPublicId} not found`,
|
message: `Board with public ID ${input.boardPublicId} not found`,
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@ export const labelRouter = createTRPCRouter({
|
|||||||
name: input.name,
|
name: input.name,
|
||||||
colourCode: input.colourCode,
|
colourCode: input.colourCode,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
boardId: card.list.boardId,
|
boardId: board.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ export const getAllByWorkspaceId = async (
|
|||||||
return data ?? [];
|
return data ?? [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getIdByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
boardPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("board")
|
||||||
|
.select("id")
|
||||||
|
.eq("publicId", boardPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
export const getByPublicId = async (
|
export const getByPublicId = async (
|
||||||
db: SupabaseClient<Database>,
|
db: SupabaseClient<Database>,
|
||||||
boardPublicId: string,
|
boardPublicId: string,
|
||||||
|
|||||||
Reference in New Issue
Block a user