diff --git a/apps/web/src/utils/helpers.ts b/apps/web/src/utils/helpers.ts
index 9db6af73..b57b36d3 100644
--- a/apps/web/src/utils/helpers.ts
+++ b/apps/web/src/utils/helpers.ts
@@ -45,10 +45,12 @@ export const formatMemberDisplayName = (
return localPart.replace(/[_-]/g, ".");
};
-export const getAvatarUrl = (imageOrKey: string) => {
+export const getAvatarUrl = (imageOrKey: string | null) => {
+ if (!imageOrKey) return "";
+
if (imageOrKey.startsWith("http://") || imageOrKey.startsWith("https://")) {
return imageOrKey;
}
-
+
return `${env("NEXT_PUBLIC_STORAGE_URL")}/${env("NEXT_PUBLIC_AVATAR_BUCKET_NAME")}/${imageOrKey}`;
};
diff --git a/apps/web/src/views/board/components/Card.tsx b/apps/web/src/views/board/components/Card.tsx
index 2d5d48b1..08a1eaff 100644
--- a/apps/web/src/views/board/components/Card.tsx
+++ b/apps/web/src/views/board/components/Card.tsx
@@ -1,5 +1,6 @@
import Avatar from "~/components/Avatar";
import Badge from "~/components/Badge";
+import CircularProgress from "~/components/CircularProgress";
import LabelIcon from "~/components/LabelIcon";
import { getAvatarUrl } from "~/utils/helpers";
@@ -7,6 +8,7 @@ const Card = ({
title,
labels,
members,
+ checklists,
}: {
title: string;
labels: { name: string; colourCode: string | null }[];
@@ -15,7 +17,28 @@ const Card = ({
email: string;
user: { name: string | null; email: string; image: string | null } | null;
}[];
+ checklists: {
+ publicId: string;
+ name: string;
+ items: {
+ publicId: string;
+ title: string;
+ completed: boolean;
+ index: number;
+ }[];
+ }[];
}) => {
+ const completedItems = checklists.reduce((acc, checklist) => {
+ return acc + checklist.items.filter((item) => item.completed).length;
+ }, 0);
+
+ const totalItems = checklists.reduce((acc, checklist) => {
+ return acc + checklist.items.length;
+ }, 0);
+
+ const progress =
+ totalItems > 0 ? Math.round((completedItems / totalItems) * 100) : 0;
+
return (
{title}
@@ -29,21 +52,37 @@ const Card = ({
/>
))}
-
- {members.map(({ user, email }) => {
- const avatarUrl = user?.image
- ? getAvatarUrl(user.image)
- : undefined;
-
- return (
-
+ {checklists.length > 0 && (
+
+
- );
- })}
+
+ {completedItems}/{totalItems}
+
+
+ )}
+ {members.length > 0 && (
+
+ {members.map(({ user, email }) => {
+ const avatarUrl = user?.image
+ ? getAvatarUrl(user.image)
+ : undefined;
+
+ return (
+
+ );
+ })}
+
+ )}
) : null}
diff --git a/apps/web/src/views/board/index.tsx b/apps/web/src/views/board/index.tsx
index 26d3fd87..0e4ce5a2 100644
--- a/apps/web/src/views/board/index.tsx
+++ b/apps/web/src/views/board/index.tsx
@@ -238,15 +238,15 @@ export default function BoardPage() {
const renderModalContent = () => {
return (
<>
-
-
-
-
-
-
-
-
-
)}
diff --git a/apps/web/src/views/card/components/NewChecklistForm.tsx b/apps/web/src/views/card/components/NewChecklistForm.tsx
index e76ed4b5..4d15865d 100644
--- a/apps/web/src/views/card/components/NewChecklistForm.tsx
+++ b/apps/web/src/views/card/components/NewChecklistForm.tsx
@@ -22,7 +22,7 @@ export function NewChecklistForm({ cardPublicId }: { cardPublicId: string }) {
const utils = api.useUtils();
- const { register, handleSubmit, reset, setValue, watch } =
+ const { register, handleSubmit, reset, watch } =
useForm({
defaultValues: {
name: "Checklist",
@@ -32,34 +32,45 @@ export function NewChecklistForm({ cardPublicId }: { cardPublicId: string }) {
const createChecklist = api.checklist.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 newList = {
- // publicId: generateUID(),
- // name: args.name,
- // boardId: 1,
- // boardPublicId,
- // cards: [],
- // index: oldBoard.lists.length,
- // };
- // const updatedLists = [...oldBoard.lists, newList];
- // return { ...oldBoard, lists: updatedLists };
- // });
- // return { previousState: currentState };
+ await utils.card.byId.cancel({ cardPublicId: args.cardPublicId });
+ const previous = utils.card.byId.getData({
+ cardPublicId: args.cardPublicId,
+ });
+ utils.card.byId.setData({ cardPublicId: args.cardPublicId }, (old) => {
+ if (!old) return old as any;
+ const placeholderChecklist = {
+ publicId: `PLACEHOLDER_${generateUID()}`,
+ name: args.name,
+ index: old.checklists.length,
+ items: [] as {
+ publicId: string;
+ title: string;
+ completed: boolean;
+ index: number;
+ }[],
+ };
+ return {
+ ...old,
+ checklists: [...old.checklists, placeholderChecklist],
+ } as typeof old;
+ });
+ return { previous };
},
- onError: (_error, _newList, context) => {
- // utils.board.byId.setData(queryParams, context?.previousState);
+ onError: (_error, vars, ctx) => {
+ if (ctx?.previous)
+ utils.card.byId.setData(
+ { cardPublicId: vars.cardPublicId },
+ ctx.previous,
+ );
showPopup({
header: t`Unable to create checklist`,
message: t`Please try again later, or contact customer support.`,
icon: "error",
});
},
- // onSettled: async () => {
- // await utils.board.byId.invalidate(queryParams);
- // },
+ onSettled: async (_data, _error, vars) => {
+ await utils.card.byId.invalidate({ cardPublicId: vars.cardPublicId });
+ },
});
useEffect(() => {
diff --git a/packages/db/src/repository/board.repo.ts b/packages/db/src/repository/board.repo.ts
index 6e4a5057..42219ec8 100644
--- a/packages/db/src/repository/board.repo.ts
+++ b/packages/db/src/repository/board.repo.ts
@@ -7,6 +7,8 @@ import {
cards,
cardsToLabels,
cardToWorkspaceMembers,
+ checklistItems,
+ checklists,
labels,
lists,
workspaceMembers,
@@ -164,6 +166,27 @@ export const getByPublicId = async (
},
},
},
+ checklists: {
+ columns: {
+ publicId: true,
+ name: true,
+ index: true,
+ },
+ where: isNull(checklists.deletedAt),
+ orderBy: asc(checklists.index),
+ with: {
+ items: {
+ columns: {
+ publicId: true,
+ title: true,
+ completed: true,
+ index: true,
+ },
+ where: isNull(checklistItems.deletedAt),
+ orderBy: asc(checklistItems.index),
+ },
+ },
+ },
},
where: and(
cardIds.length > 0 ? inArray(cards.publicId, cardIds) : undefined,