💄 show product updates list

This commit is contained in:
Fred KISSIE
2025-11-06 22:42:49 +01:00
parent 7a2dd31019
commit 18757d7eb3
3 changed files with 87 additions and 2 deletions

View File

@@ -1282,6 +1282,8 @@
"productUpdateMoreInfo": "{noOfUpdates} more updates",
"productUpdateInfo": "{noOfUpdates} updates",
"productUpdateWhatsNew": "What's New",
"productUpdateTitle": "Product Updates",
"dismissAll": "Dismiss all",
"pangolinUpdateAvailable": "New version available",
"pangolinUpdateAvailableInfo": "Version {version} is ready to install",
"pangolinUpdateAvailableReleaseNotes": "View release notes",

View File

@@ -16,6 +16,9 @@ import { useTranslations } from "next-intl";
import { Transition } from "@headlessui/react";
import * as React from "react";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Button } from "./ui/button";
import { Badge } from "./ui/badge";
import { timeAgoFormatter } from "@app/lib/timeAgoFormatter";
export default function ProductUpdates({
isCollapsed
@@ -158,8 +161,40 @@ function ProductUpdatesListPopup({
</div>
</button>
</PopoverTrigger>
<PopoverContent side="right" align="end">
Hello
<PopoverContent
side="right"
align="end"
className="p-0 flex flex-col w-80"
>
<div className="p-3 flex justify-between border-b items-center">
<span className="text-sm inline-flex gap-2 items-center font-medium">
{t("productUpdateTitle")}
<Badge variant="secondary">{updates.length}</Badge>
</span>
<Button variant="ghost">{t("dismissAll")}</Button>
</div>
<ol className="p-3 flex flex-col gap-1 max-h-112 overflow-y-auto">
{updates.map((update) => (
<li
key={update.id}
className="border rounded-md flex flex-col p-4 gap-2"
>
<h4 className="text-sm font-medium inline-flex items-start gap-1">
<span>{update.title}</span>
<Badge variant="secondary">New</Badge>
</h4>
<small className="text-muted-foreground">
{update.contents}
</small>
<time
dateTime={update.publishedAt.toLocaleString()}
className="text-xs text-muted-foreground"
>
{timeAgoFormatter(update.publishedAt)}
</time>
</li>
))}
</ol>
</PopoverContent>
</Transition>
</Popover>

View File

@@ -0,0 +1,48 @@
export function timeAgoFormatter(
dateInput: string | Date,
short: boolean = false
): string {
const date = new Date(dateInput);
const now = new Date();
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
const secondsInMinute = 60;
const secondsInHour = 60 * secondsInMinute;
const secondsInDay = 24 * secondsInHour;
const secondsInWeek = 7 * secondsInDay;
const secondsInMonth = 30 * secondsInDay;
const secondsInYear = 365 * secondsInDay;
let value: number;
let unit: Intl.RelativeTimeFormatUnit;
if (diffInSeconds < secondsInMinute) {
value = diffInSeconds;
unit = "second";
} else if (diffInSeconds < secondsInHour) {
value = Math.floor(diffInSeconds / secondsInMinute);
unit = "minute";
} else if (diffInSeconds < secondsInDay) {
value = Math.floor(diffInSeconds / secondsInHour);
unit = "hour";
} else if (diffInSeconds < secondsInWeek) {
value = Math.floor(diffInSeconds / secondsInDay);
unit = "day";
} else if (diffInSeconds < secondsInMonth) {
value = Math.floor(diffInSeconds / secondsInWeek);
unit = "week";
} else if (diffInSeconds < secondsInYear) {
value = Math.floor(diffInSeconds / secondsInMonth);
unit = "month";
} else {
value = Math.floor(diffInSeconds / secondsInYear);
unit = "year";
}
const rtf = new Intl.RelativeTimeFormat("en", {
numeric: "auto",
style: short ? "narrow" : "long"
});
const formatedValue = rtf.format(-value, unit);
return formatedValue === "now" ? "Just now" : formatedValue;
}