feat: card will indicate icons of comments and description (#222)

* feat: card will indicate icons of comments and description

* fix: comments limit to one

* feat: fetch comments for public boards

---------

Co-authored-by: Henry <henry_ball@hotmail.co.uk>
This commit is contained in:
Ridham Khandar
2025-10-24 01:10:04 +05:30
committed by GitHub
parent 4ffc7e3696
commit bb4784fd28
4 changed files with 82 additions and 33 deletions

View File

@@ -1,3 +1,5 @@
import { HiBars3BottomLeft, HiChatBubbleLeft } from "react-icons/hi2";
import Avatar from "~/components/Avatar";
import Badge from "~/components/Badge";
import CircularProgress from "~/components/CircularProgress";
@@ -9,6 +11,8 @@ const Card = ({
labels,
members,
checklists,
description,
comments,
}: {
title: string;
labels: { name: string; colourCode: string | null }[];
@@ -27,6 +31,8 @@ const Card = ({
index: number;
}[];
}[];
description: string | null;
comments: { publicId: string }[];
}) => {
const completedItems = checklists.reduce((acc, checklist) => {
return acc + checklist.items.filter((item) => item.completed).length;
@@ -39,10 +45,17 @@ const Card = ({
const progress =
totalItems > 0 ? Math.round((completedItems / totalItems) * 100) : 0;
const hasDescription =
description && description.replace(/<[^>]*>/g, "").trim().length > 0;
return (
<div className="flex flex-col rounded-md border border-light-200 bg-light-50 px-3 py-2 text-sm text-neutral-900 dark:border-dark-200 dark:bg-dark-200 dark:text-dark-1000 dark:hover:bg-dark-300">
<span>{title}</span>
{labels.length || members.length || checklists.length > 0 ? (
{labels.length ||
members.length ||
checklists.length > 0 ||
hasDescription ||
comments.length > 0 ? (
<div className="mt-2 flex flex-col justify-end">
<div className="space-x-0.5">
{labels.map((label) => (
@@ -52,37 +65,51 @@ const Card = ({
/>
))}
</div>
<div className="flex items-center justify-end gap-1">
{checklists.length > 0 && (
<div className="flex items-center gap-1 rounded-full border-[1px] border-light-300 px-2 py-1 dark:border-dark-600">
<CircularProgress
progress={progress || 2}
size="sm"
className="flex-shrink-0"
/>
<span className="text-[10px] text-light-900 dark:text-dark-950">
{completedItems}/{totalItems}
</span>
</div>
)}
{members.length > 0 && (
<div className="isolate flex justify-end -space-x-1 overflow-hidden">
{members.map(({ user, email }) => {
const avatarUrl = user?.image
? getAvatarUrl(user.image)
: undefined;
<div className="mt-2 flex items-center justify-between gap-1">
<div className="flex items-center gap-2">
{hasDescription && (
<div className="flex items-center gap-1 text-light-700 dark:text-dark-800">
<HiBars3BottomLeft className="h-4 w-4" />
</div>
)}
{comments.length > 0 && (
<div className="flex items-center gap-1 text-light-700 dark:text-dark-800">
<HiChatBubbleLeft className="h-4 w-4" />
</div>
)}
</div>
<div className="flex items-center justify-end gap-1">
{checklists.length > 0 && (
<div className="flex items-center gap-1 rounded-full border-[1px] border-light-300 px-2 py-1 dark:border-dark-600">
<CircularProgress
progress={progress || 2}
size="sm"
className="flex-shrink-0"
/>
<span className="text-[10px] text-light-900 dark:text-dark-950">
{completedItems}/{totalItems}
</span>
</div>
)}
{members.length > 0 && (
<div className="isolate flex justify-end -space-x-1 overflow-hidden">
{members.map(({ user, email }) => {
const avatarUrl = user?.image
? getAvatarUrl(user.image)
: undefined;
return (
<Avatar
name={user?.name ?? ""}
email={user?.email ?? email}
imageUrl={avatarUrl}
size="sm"
/>
);
})}
</div>
)}
return (
<Avatar
name={user?.name ?? ""}
email={user?.email ?? email}
imageUrl={avatarUrl}
size="sm"
/>
);
})}
</div>
)}
</div>
</div>
</div>
) : null}

View File

@@ -546,6 +546,8 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
labels={card.labels}
members={card.members}
checklists={card.checklists ?? []}
description={card.description ?? null}
comments={card.comments ?? []}
/>
</Link>
)}

View File

@@ -45,7 +45,10 @@ export default function PublicBoardView() {
members: formatToArray(router.query.members),
labels: formatToArray(router.query.labels),
},
{ enabled: router.isReady && !!boardSlug, placeholderData: keepPreviousData },
{
enabled: router.isReady && !!boardSlug,
placeholderData: keepPreviousData,
},
);
const CopyBoardLink = () => {
@@ -156,7 +159,7 @@ export default function PublicBoardView() {
) : (
<div className="flex">
<div className="min-w-[2rem]" />
{data.lists.map((list) => (
{data?.lists.map((list) => (
<div
key={list.publicId}
className="dark-text-dark-1000 mr-5 h-fit min-w-[18rem] max-w-[18rem] rounded-md border border-light-400 bg-light-300 py-2 pl-2 pr-1 text-neutral-900 dark:border-dark-300 dark:bg-dark-100"
@@ -182,6 +185,8 @@ export default function PublicBoardView() {
labels={card.labels}
checklists={card.checklists ?? []}
members={[]}
description={card.description}
comments={card.comments ?? []}
/>
</Link>
))}

View File

@@ -10,6 +10,7 @@ import {
cardToWorkspaceMembers,
checklistItems,
checklists,
comments,
labels,
lists,
workspaceMembers,
@@ -215,6 +216,13 @@ export const getByPublicId = async (
},
},
},
comments: {
columns: {
publicId: true,
},
where: isNull(comments.deletedAt),
limit: 1,
},
},
where: and(
cardIds.length > 0 ? inArray(cards.publicId, cardIds) : undefined,
@@ -335,6 +343,13 @@ export const getBySlug = async (
},
},
},
comments: {
columns: {
publicId: true,
},
where: isNull(comments.deletedAt),
limit: 1,
},
checklists: {
columns: {
publicId: true,