From d2a1bbede93889fbd45d8d8614a73ade3a4614e3 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 5 Dec 2025 21:04:30 +0000 Subject: [PATCH] feat: display date updates in card activity --- .../views/card/components/ActivityList.tsx | 66 ++++++++++++++----- .../views/card/components/MemberSelector.tsx | 2 +- packages/db/src/repository/card.repo.ts | 2 + 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/apps/web/src/views/card/components/ActivityList.tsx b/apps/web/src/views/card/components/ActivityList.tsx index 8fe8d67b..8c1397b8 100644 --- a/apps/web/src/views/card/components/ActivityList.tsx +++ b/apps/web/src/views/card/components/ActivityList.tsx @@ -1,11 +1,12 @@ +import type { Locale as DateFnsLocale } from "date-fns"; import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; -import { formatDistanceToNow } from "date-fns"; -import { de, enGB, es, fr, it, nl } from "date-fns/locale"; +import { format, formatDistanceToNow, isSameYear } from "date-fns"; import { HiOutlineArrowLeft, HiOutlineArrowRight, HiOutlineCheckCircle, + HiOutlineClock, HiOutlinePencil, HiOutlinePlus, HiOutlineTag, @@ -24,15 +25,6 @@ import Comment from "./Comment"; type ActivityType = NonNullable["activities"][number]["type"]; -const dateLocaleMap = { - en: enGB, - fr: fr, - de: de, - es: es, - it: it, - nl: nl, -} as const; - const truncate = (value: string | null, maxLength = 50) => { if (!value) return value; return value.length > maxLength ? `${value.slice(0, maxLength - 1)}…` : value; @@ -47,6 +39,8 @@ const getActivityText = ({ isSelf, label, fromTitle, + toDueDate, + dateLocale, }: { type: ActivityType; toTitle: string | null; @@ -56,6 +50,9 @@ const getActivityText = ({ isSelf: boolean; label: string | null; fromTitle?: string | null; + fromDueDate?: Date | null; + toDueDate?: Date | null; + dateLocale: DateFnsLocale; }) => { const ACTIVITY_TYPE_MAP = { "card.created": t`created the card`, @@ -74,6 +71,9 @@ const getActivityText = ({ "card.updated.checklist.item.completed": t`completed a checklist item`, "card.updated.checklist.item.uncompleted": t`marked a checklist item as incomplete`, "card.updated.checklist.item.deleted": t`deleted a checklist item`, + "card.updated.dueDate.added": t`set the due date`, + "card.updated.dueDate.updated": t`updated the due date`, + "card.updated.dueDate.removed": t`removed the due date`, } as const; if (!(type in ACTIVITY_TYPE_MAP)) return null; @@ -209,6 +209,38 @@ const getActivityText = ({ ); } + if (type === "card.updated.dueDate.added" && toDueDate) { + const showYear = !isSameYear(toDueDate, new Date()); + const formattedDate = format( + toDueDate, + showYear ? "do MMM yyyy" : "do MMM", + { locale: dateLocale }, + ); + return ( + + changed the due date to {formattedDate} + + ); + } + + if (type === "card.updated.dueDate.updated" && toDueDate) { + const showYear = !isSameYear(toDueDate, new Date()); + const formattedDate = format( + toDueDate, + showYear ? "do MMM yyyy" : "do MMM", + { locale: dateLocale }, + ); + return ( + + changed the due date to {formattedDate} + + ); + } + + if (type === "card.updated.dueDate.removed") { + return removed the due date; + } + return baseText; }; @@ -229,6 +261,9 @@ const ACTIVITY_ICON_MAP: Partial> = "card.updated.checklist.item.completed": , "card.updated.checklist.item.uncompleted": , "card.updated.checklist.item.deleted": , + "card.updated.dueDate.added": , + "card.updated.dueDate.updated": , + "card.updated.dueDate.removed": , } as const; const getActivityIcon = ( @@ -260,9 +295,7 @@ const ActivityList = ({ isViewOnly?: boolean; }) => { const { data } = authClient.useSession(); - const { locale } = useLocalisation(); - - const currentDateLocale = dateLocaleMap[locale] || enGB; + const { dateLocale } = useLocalisation(); return (
@@ -276,6 +309,9 @@ const ActivityList = ({ isSelf: activity.member?.user?.id === data?.user.id, label: activity.label?.name ?? null, fromTitle: activity.fromTitle ?? null, + fromDueDate: activity.fromDueDate ?? null, + toDueDate: activity.toDueDate ?? null, + dateLocale: dateLocale, }); if (activity.type === "card.updated.comment.added") @@ -330,7 +366,7 @@ const ActivityList = ({ {formatDistanceToNow(new Date(activity.createdAt), { addSuffix: true, - locale: currentDateLocale, + locale: dateLocale, })}

diff --git a/apps/web/src/views/card/components/MemberSelector.tsx b/apps/web/src/views/card/components/MemberSelector.tsx index eb445a26..abc60282 100644 --- a/apps/web/src/views/card/components/MemberSelector.tsx +++ b/apps/web/src/views/card/components/MemberSelector.tsx @@ -111,7 +111,7 @@ export default function MemberSelector({ createNewItemLabel={t`Invite member`} asChild > -
+
{selectedMembers.length ? (
{selectedMembers.map(({ value, imageUrl }) => ( diff --git a/packages/db/src/repository/card.repo.ts b/packages/db/src/repository/card.repo.ts index 378206e3..60756290 100644 --- a/packages/db/src/repository/card.repo.ts +++ b/packages/db/src/repository/card.repo.ts @@ -539,6 +539,8 @@ export const getWithListAndMembersByPublicId = async ( toTitle: true, fromDescription: true, toDescription: true, + fromDueDate: true, + toDueDate: true, }, with: { fromList: {