mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-28 21:49:40 +02:00
feat: implement relative date formatting for last indexed timestamps in connector views
This commit is contained in:
parent
59dd9554b3
commit
ca7e45405c
3 changed files with 33 additions and 48 deletions
|
|
@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
import { formatRelativeDate } from "@/lib/format-date";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
interface DateRangeSelectorProps {
|
interface DateRangeSelectorProps {
|
||||||
|
|
@ -26,19 +27,10 @@ export const DateRangeSelector: FC<DateRangeSelectorProps> = ({
|
||||||
allowFutureDates = false,
|
allowFutureDates = false,
|
||||||
lastIndexedAt,
|
lastIndexedAt,
|
||||||
}) => {
|
}) => {
|
||||||
// Get the placeholder text for start date based on whether connector was previously indexed
|
const startDatePlaceholder = lastIndexedAt
|
||||||
const getStartDatePlaceholder = () => {
|
? `From ${formatRelativeDate(lastIndexedAt)}`
|
||||||
if (lastIndexedAt) {
|
: "Default (1 year)";
|
||||||
const date = new Date(lastIndexedAt);
|
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
const indexedYear = date.getFullYear();
|
|
||||||
// Show year only if different from current year
|
|
||||||
const formatStr = indexedYear === currentYear ? "MMM d, HH:mm" : "MMM d, yyyy HH:mm";
|
|
||||||
const formattedDate = format(date, formatStr);
|
|
||||||
return `Since (${formattedDate})`;
|
|
||||||
}
|
|
||||||
return "Default (1 year ago)";
|
|
||||||
};
|
|
||||||
const handleLast30Days = () => {
|
const handleLast30Days = () => {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
onStartDateChange(subDays(today, 30));
|
onStartDateChange(subDays(today, 30));
|
||||||
|
|
@ -88,7 +80,7 @@ export const DateRangeSelector: FC<DateRangeSelectorProps> = ({
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
{startDate ? format(startDate, "PPP") : getStartDatePlaceholder()}
|
{startDate ? format(startDate, "PPP") : startDatePlaceholder}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-auto p-0 z-[100]" align="start">
|
<PopoverContent className="w-auto p-0 z-[100]" align="start">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { differenceInDays, differenceInMinutes, format, isToday, isYesterday } from "date-fns";
|
|
||||||
import { ArrowLeft, Plus, Server } from "lucide-react";
|
import { ArrowLeft, Plus, Server } from "lucide-react";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
@ -8,6 +7,7 @@ import { Spinner } from "@/components/ui/spinner";
|
||||||
import { EnumConnectorName } from "@/contracts/enums/connector";
|
import { EnumConnectorName } from "@/contracts/enums/connector";
|
||||||
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
|
import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
|
||||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||||
|
import { formatRelativeDate } from "@/lib/format-date";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useConnectorStatus } from "../hooks/use-connector-status";
|
import { useConnectorStatus } from "../hooks/use-connector-status";
|
||||||
import { getConnectorDisplayName } from "../tabs/all-connectors-tab";
|
import { getConnectorDisplayName } from "../tabs/all-connectors-tab";
|
||||||
|
|
@ -32,38 +32,6 @@ function isIndexableConnector(connectorType: string): boolean {
|
||||||
return !nonIndexableTypes.includes(connectorType);
|
return !nonIndexableTypes.includes(connectorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Format last indexed date with contextual messages
|
|
||||||
*/
|
|
||||||
function formatLastIndexedDate(dateString: string): string {
|
|
||||||
const date = new Date(dateString);
|
|
||||||
const now = new Date();
|
|
||||||
const minutesAgo = differenceInMinutes(now, date);
|
|
||||||
const daysAgo = differenceInDays(now, date);
|
|
||||||
|
|
||||||
if (minutesAgo < 1) {
|
|
||||||
return "Just now";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minutesAgo < 60) {
|
|
||||||
return `${minutesAgo} ${minutesAgo === 1 ? "minute" : "minutes"} ago`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isToday(date)) {
|
|
||||||
return `Today at ${format(date, "h:mm a")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isYesterday(date)) {
|
|
||||||
return `Yesterday at ${format(date, "h:mm a")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (daysAgo < 7) {
|
|
||||||
return `${daysAgo} ${daysAgo === 1 ? "day" : "days"} ago`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return format(date, "MMM d, yyyy");
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
|
export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
|
||||||
connectorType,
|
connectorType,
|
||||||
connectorTitle,
|
connectorTitle,
|
||||||
|
|
@ -215,7 +183,7 @@ export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
|
||||||
<p className="text-[10px] text-muted-foreground mt-1 whitespace-nowrap truncate">
|
<p className="text-[10px] text-muted-foreground mt-1 whitespace-nowrap truncate">
|
||||||
{isIndexableConnector(connector.connector_type)
|
{isIndexableConnector(connector.connector_type)
|
||||||
? connector.last_indexed_at
|
? connector.last_indexed_at
|
||||||
? `Last indexed: ${formatLastIndexedDate(connector.last_indexed_at)}`
|
? `Last indexed: ${formatRelativeDate(connector.last_indexed_at)}`
|
||||||
: "Never indexed"
|
: "Never indexed"
|
||||||
: "Active"}
|
: "Active"}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
25
surfsense_web/lib/format-date.ts
Normal file
25
surfsense_web/lib/format-date.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { differenceInDays, differenceInMinutes, format, isToday, isYesterday } from "date-fns";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a date string as a human-readable relative time
|
||||||
|
* - < 1 min: "Just now"
|
||||||
|
* - < 60 min: "15m ago"
|
||||||
|
* - Today: "Today, 2:30 PM"
|
||||||
|
* - Yesterday: "Yesterday, 2:30 PM"
|
||||||
|
* - < 7 days: "3d ago"
|
||||||
|
* - Older: "Jan 15, 2026"
|
||||||
|
*/
|
||||||
|
export function formatRelativeDate(dateString: string): string {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const now = new Date();
|
||||||
|
const minutesAgo = differenceInMinutes(now, date);
|
||||||
|
const daysAgo = differenceInDays(now, date);
|
||||||
|
|
||||||
|
if (minutesAgo < 1) return "Just now";
|
||||||
|
if (minutesAgo < 60) return `${minutesAgo}m ago`;
|
||||||
|
if (isToday(date)) return `Today, ${format(date, "h:mm a")}`;
|
||||||
|
if (isYesterday(date)) return `Yesterday, ${format(date, "h:mm a")}`;
|
||||||
|
if (daysAgo < 7) return `${daysAgo}d ago`;
|
||||||
|
return format(date, "MMM d, yyyy");
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue