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 Avatar from "~/components/Avatar";
|
||||||
import Badge from "~/components/Badge";
|
import Badge from "~/components/Badge";
|
||||||
import CircularProgress from "~/components/CircularProgress";
|
import CircularProgress from "~/components/CircularProgress";
|
||||||
@@ -9,6 +11,8 @@ const Card = ({
|
|||||||
labels,
|
labels,
|
||||||
members,
|
members,
|
||||||
checklists,
|
checklists,
|
||||||
|
description,
|
||||||
|
comments,
|
||||||
}: {
|
}: {
|
||||||
title: string;
|
title: string;
|
||||||
labels: { name: string; colourCode: string | null }[];
|
labels: { name: string; colourCode: string | null }[];
|
||||||
@@ -27,6 +31,8 @@ const Card = ({
|
|||||||
index: number;
|
index: number;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
description: string | null;
|
||||||
|
comments: { publicId: string }[];
|
||||||
}) => {
|
}) => {
|
||||||
const completedItems = checklists.reduce((acc, checklist) => {
|
const completedItems = checklists.reduce((acc, checklist) => {
|
||||||
return acc + checklist.items.filter((item) => item.completed).length;
|
return acc + checklist.items.filter((item) => item.completed).length;
|
||||||
@@ -39,10 +45,17 @@ const Card = ({
|
|||||||
const progress =
|
const progress =
|
||||||
totalItems > 0 ? Math.round((completedItems / totalItems) * 100) : 0;
|
totalItems > 0 ? Math.round((completedItems / totalItems) * 100) : 0;
|
||||||
|
|
||||||
|
const hasDescription =
|
||||||
|
description && description.replace(/<[^>]*>/g, "").trim().length > 0;
|
||||||
|
|
||||||
return (
|
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">
|
<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>
|
<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="mt-2 flex flex-col justify-end">
|
||||||
<div className="space-x-0.5">
|
<div className="space-x-0.5">
|
||||||
{labels.map((label) => (
|
{labels.map((label) => (
|
||||||
@@ -52,37 +65,51 @@ const Card = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-end gap-1">
|
<div className="mt-2 flex items-center justify-between gap-1">
|
||||||
{checklists.length > 0 && (
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-1 rounded-full border-[1px] border-light-300 px-2 py-1 dark:border-dark-600">
|
{hasDescription && (
|
||||||
<CircularProgress
|
<div className="flex items-center gap-1 text-light-700 dark:text-dark-800">
|
||||||
progress={progress || 2}
|
<HiBars3BottomLeft className="h-4 w-4" />
|
||||||
size="sm"
|
</div>
|
||||||
className="flex-shrink-0"
|
)}
|
||||||
/>
|
{comments.length > 0 && (
|
||||||
<span className="text-[10px] text-light-900 dark:text-dark-950">
|
<div className="flex items-center gap-1 text-light-700 dark:text-dark-800">
|
||||||
{completedItems}/{totalItems}
|
<HiChatBubbleLeft className="h-4 w-4" />
|
||||||
</span>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
{members.length > 0 && (
|
<div className="flex items-center justify-end gap-1">
|
||||||
<div className="isolate flex justify-end -space-x-1 overflow-hidden">
|
{checklists.length > 0 && (
|
||||||
{members.map(({ user, email }) => {
|
<div className="flex items-center gap-1 rounded-full border-[1px] border-light-300 px-2 py-1 dark:border-dark-600">
|
||||||
const avatarUrl = user?.image
|
<CircularProgress
|
||||||
? getAvatarUrl(user.image)
|
progress={progress || 2}
|
||||||
: undefined;
|
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 (
|
return (
|
||||||
<Avatar
|
<Avatar
|
||||||
name={user?.name ?? ""}
|
name={user?.name ?? ""}
|
||||||
email={user?.email ?? email}
|
email={user?.email ?? email}
|
||||||
imageUrl={avatarUrl}
|
imageUrl={avatarUrl}
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -546,6 +546,8 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
|
|||||||
labels={card.labels}
|
labels={card.labels}
|
||||||
members={card.members}
|
members={card.members}
|
||||||
checklists={card.checklists ?? []}
|
checklists={card.checklists ?? []}
|
||||||
|
description={card.description ?? null}
|
||||||
|
comments={card.comments ?? []}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ export default function PublicBoardView() {
|
|||||||
members: formatToArray(router.query.members),
|
members: formatToArray(router.query.members),
|
||||||
labels: formatToArray(router.query.labels),
|
labels: formatToArray(router.query.labels),
|
||||||
},
|
},
|
||||||
{ enabled: router.isReady && !!boardSlug, placeholderData: keepPreviousData },
|
{
|
||||||
|
enabled: router.isReady && !!boardSlug,
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const CopyBoardLink = () => {
|
const CopyBoardLink = () => {
|
||||||
@@ -156,7 +159,7 @@ export default function PublicBoardView() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="min-w-[2rem]" />
|
<div className="min-w-[2rem]" />
|
||||||
{data.lists.map((list) => (
|
{data?.lists.map((list) => (
|
||||||
<div
|
<div
|
||||||
key={list.publicId}
|
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"
|
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}
|
labels={card.labels}
|
||||||
checklists={card.checklists ?? []}
|
checklists={card.checklists ?? []}
|
||||||
members={[]}
|
members={[]}
|
||||||
|
description={card.description}
|
||||||
|
comments={card.comments ?? []}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
cardToWorkspaceMembers,
|
cardToWorkspaceMembers,
|
||||||
checklistItems,
|
checklistItems,
|
||||||
checklists,
|
checklists,
|
||||||
|
comments,
|
||||||
labels,
|
labels,
|
||||||
lists,
|
lists,
|
||||||
workspaceMembers,
|
workspaceMembers,
|
||||||
@@ -215,6 +216,13 @@ export const getByPublicId = async (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
comments: {
|
||||||
|
columns: {
|
||||||
|
publicId: true,
|
||||||
|
},
|
||||||
|
where: isNull(comments.deletedAt),
|
||||||
|
limit: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
where: and(
|
where: and(
|
||||||
cardIds.length > 0 ? inArray(cards.publicId, cardIds) : undefined,
|
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: {
|
checklists: {
|
||||||
columns: {
|
columns: {
|
||||||
publicId: true,
|
publicId: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user