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:
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user