feat: display date icon and label on cards

This commit is contained in:
Henry
2025-12-04 14:09:04 +00:00
parent 7bc83e70a9
commit 3d5f4f63a2
6 changed files with 54 additions and 5 deletions

View File

@@ -110,17 +110,18 @@ const DateSelector = ({ selectedDate, onDateSelect }: DateSelectorProps) => {
className={twMerge(
"flex aspect-square items-center justify-center rounded-lg focus:z-10",
day.isSelected
? "bg-dark-1000 text-dark-50 hover:bg-dark-1000 dark:bg-dark-1000 dark:text-dark-50 dark:hover:bg-dark-1000"
? "bg-dark-1000 hover:bg-dark-1000 dark:bg-dark-1000 dark:hover:bg-dark-1000"
: "bg-light-100 hover:bg-light-200 dark:bg-dark-100 dark:hover:bg-dark-200",
)}
>
<time
dateTime={day.date}
className={twMerge(
"mx-auto flex size-7 items-center justify-center rounded-full",
"mx-auto flex size-7 items-center justify-center rounded-full text-light-900 dark:text-dark-900",
day.isCurrentMonth
? "text-light-900 dark:text-dark-900"
: "text-light-700 dark:text-dark-600",
day.isSelected && "text-light-1000 dark:text-dark-50",
)}
>
{day.date.split("-").pop()?.replace(/^0/, "")}

View File

@@ -1,4 +1,6 @@
import type { Locale as DateFnsLocale } from "date-fns";
import { useLingui } from "@lingui/react";
import { de, enGB, es, fr, it, nl, pl, ru } from "date-fns/locale";
import type { Locale } from "~/locales";
import { useLinguiContext } from "~/providers/lingui";
@@ -8,6 +10,19 @@ export function useLocalisation() {
const { i18n } = useLingui();
const { locale, setLocale, availableLocales } = useLinguiContext();
const dateLocaleMap: Partial<Record<Locale, DateFnsLocale>> = {
en: enGB,
fr,
de,
es,
it,
nl,
ru,
pl,
};
const currentDateLocale = dateLocaleMap[locale] ?? enGB;
const handleSetLocale = async (newLocale: Locale) => {
await activateLocale(newLocale);
setLocale(newLocale);
@@ -15,9 +30,9 @@ export function useLocalisation() {
return {
locale,
dateLocale: currentDateLocale,
setLocale: handleSetLocale,
availableLocales,
formatDate: i18n.date,
formatNumber: i18n.number,
};
}

View File

@@ -1,10 +1,17 @@
import { format, isBefore, isSameYear, startOfDay } from "date-fns";
import { HiOutlinePaperClip } from "react-icons/hi";
import { HiBars3BottomLeft, HiChatBubbleLeft } from "react-icons/hi2";
import {
HiBars3BottomLeft,
HiChatBubbleLeft,
HiOutlineClock,
} from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
import Avatar from "~/components/Avatar";
import Badge from "~/components/Badge";
import CircularProgress from "~/components/CircularProgress";
import LabelIcon from "~/components/LabelIcon";
import { useLocalisation } from "~/hooks/useLocalisation";
import { getAvatarUrl } from "~/utils/helpers";
const Card = ({
@@ -15,6 +22,7 @@ const Card = ({
description,
comments,
attachments,
dueDate,
}: {
title: string;
labels: { name: string; colourCode: string | null }[];
@@ -36,7 +44,11 @@ const Card = ({
description: string | null;
comments: { publicId: string }[];
attachments?: { publicId: string }[];
dueDate?: Date | null;
}) => {
const { dateLocale } = useLocalisation();
const showYear = dueDate ? !isSameYear(dueDate, new Date()) : false;
const isOverdue = dueDate ? isBefore(dueDate, startOfDay(new Date())) : false;
const completedItems = checklists.reduce((acc, checklist) => {
return acc + checklist.items.filter((item) => item.completed).length;
}, 0);
@@ -51,6 +63,7 @@ const Card = ({
const hasDescription =
description && description.replace(/<[^>]*>/g, "").trim().length > 0;
const hasAttachments = attachments && attachments.length > 0;
const hasDueDate = !!dueDate;
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">
@@ -60,6 +73,7 @@ const Card = ({
checklists.length > 0 ||
hasDescription ||
comments.length > 0 ||
hasDueDate ||
hasAttachments ? (
<div className="mt-2 flex flex-col justify-end">
<div className="space-x-0.5">
@@ -77,6 +91,23 @@ const Card = ({
<HiBars3BottomLeft className="h-4 w-4" />
</div>
)}
{hasDueDate && dueDate && (
<div
className={twMerge(
"flex items-center gap-1",
isOverdue
? "text-red-600 dark:text-red-400"
: "text-light-700 dark:text-dark-800",
)}
>
<HiOutlineClock className="h-4 w-4" />
<span className="text-[11px]">
{format(dueDate, showYear ? "do MMM yyyy" : "do MMM", {
locale: dateLocale,
})}
</span>
</div>
)}
{comments.length > 0 && (
<div className="flex items-center gap-1 text-light-700 dark:text-dark-800">
<HiChatBubbleLeft className="h-4 w-4" />

View File

@@ -567,6 +567,7 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
}
comments={card.comments ?? []}
attachments={card.attachments}
dueDate={card.dueDate ?? null}
/>
</Link>
)}

View File

@@ -121,7 +121,7 @@ export function DueDateSelector({
<>
<div className="fixed inset-0 z-10" onClick={handleBackdropClick} />
<div
className="absolute -left-4 top-full z-20 mt-2 rounded-md border border-light-200 bg-light-50 shadow-lg dark:border-dark-200 dark:bg-dark-100"
className="absolute -left-8 top-full z-20 mt-2 rounded-md border border-light-200 bg-light-50 shadow-lg dark:border-dark-200 dark:bg-dark-100"
onClick={(e) => {
e.stopPropagation();
}}

View File

@@ -199,6 +199,7 @@ export default function PublicBoardView() {
description={card.description}
comments={card.comments ?? []}
attachments={card.attachments}
dueDate={card.dueDate ?? null}
/>
</Link>
);