import { formatDistanceToNow } from "date-fns"; import { HiOutlineArrowLeft, HiOutlineArrowRight, HiOutlinePencil, HiOutlinePlus, HiOutlineTag, HiOutlineUserMinus, HiOutlineUserPlus, } from "react-icons/hi2"; import type { GetCardByIdOutput } from "@kan/api/types"; import Avatar from "~/components/Avatar"; import Comment from "./Comment"; import { authClient } from "@kan/auth/client"; type ActivityType = NonNullable["activities"][number]["type"]; const ACTIVITY_TYPE_MAP = { "card.created": "created the card", "card.updated.title": "updated the title", "card.updated.description": "updated the description", "card.updated.list": "moved the card to another list", "card.updated.label.added": "added a label to the card", "card.updated.label.removed": "removed a label from the card", "card.updated.member.added": "added a member to the card", "card.updated.member.removed": "removed a member from the card", } as const; const getActivityText = ({ type, toTitle, fromList, toList, memberName, isSelf, label, }: { type: ActivityType; toTitle: string | null; fromList: string | null; toList: string | null; memberName: string | null; isSelf: boolean; label: string | null; }) => { if (!(type in ACTIVITY_TYPE_MAP)) return null; const baseText = ACTIVITY_TYPE_MAP[type as keyof typeof ACTIVITY_TYPE_MAP]; const TextHighlight = ({ children }: { children: React.ReactNode }) => ( {children} ); if (type === "card.updated.title" && toTitle) { return ( <> updated the title to {toTitle} ); } if (type === "card.updated.list" && fromList && toList) { return ( <> moved the card from {fromList} to {toList} ); } if (type === "card.updated.member.added" && memberName) { if (isSelf) return <>self-assigned the card; return ( <> assigned {memberName} to the card ); } if (type === "card.updated.member.removed" && memberName) { if (isSelf) return <>unassigned themselves from the card; return ( <> unassigned {memberName} from the card ); } if (type === "card.updated.label.added" && label) { return ( <> added label {label} ); } if (type === "card.updated.label.removed" && label) { return ( <> removed label {label} ); } return baseText; }; const ACTIVITY_ICON_MAP: Partial> = { "card.created": , "card.updated.title": , "card.updated.description": , "card.updated.label.added": , "card.updated.label.removed": , "card.updated.member.added": , "card.updated.member.removed": , } as const; const getActivityIcon = ( type: ActivityType, fromIndex?: number | null, toIndex?: number | null, ): React.ReactNode | null => { if (type === "card.updated.list" && fromIndex != null && toIndex != null) { return fromIndex > toIndex ? ( ) : ( ); } return ACTIVITY_ICON_MAP[type] ?? null; }; const ActivityList = ({ activities, cardPublicId, isLoading, isAdmin, }: { activities: NonNullable["activities"]; cardPublicId: string; isLoading: boolean; isAdmin?: boolean; }) => { const { data } = authClient.useSession(); return (
{activities.map((activity, index) => { const activityText = getActivityText({ type: activity.type, toTitle: activity.toTitle, fromList: activity.fromList?.name ?? null, toList: activity.toList?.name ?? null, memberName: activity.member?.user?.name ?? null, isSelf: activity.member?.user?.id === data?.user?.id, label: activity.label?.name ?? null, }); if (activity.type === "card.updated.comment.added") return ( ); if (!activityText) return null; return (
{index !== activities.length - 1 && activities[index + 1]?.type !== "card.updated.comment.added" && (
)}

{`${activity.user?.name} `} {activityText} ยท {formatDistanceToNow(new Date(activity.createdAt), { addSuffix: true, })}

); })}
); }; export default ActivityList;