Some checks failed
Build Frontend / build (push) Failing after 7s
Introduces a modular dashboard system with draggable, configurable widgets including revenue, low stock, recent customers, and pending chats. Adds a dashboard editor for layout customization, widget visibility, and settings. Refactors dashboard content to use the new widget system and improves UI consistency and interactivity.
115 lines
3.4 KiB
TypeScript
115 lines
3.4 KiB
TypeScript
"use client"
|
|
|
|
import * as React from "react"
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipProvider,
|
|
TooltipTrigger,
|
|
} from "@/components/ui/tooltip"
|
|
import { format, formatDistanceToNow, isToday, isYesterday, differenceInMinutes } from "date-fns"
|
|
|
|
interface RelativeTimeProps {
|
|
date: Date | string | null | undefined
|
|
className?: string
|
|
showTooltip?: boolean
|
|
updateInterval?: number // ms, for auto-updating recent times
|
|
}
|
|
|
|
/**
|
|
* RelativeTime - Displays time as "2 hours ago" with full date on hover
|
|
* Auto-updates for times less than 1 hour old
|
|
*/
|
|
export function RelativeTime({
|
|
date,
|
|
className = "",
|
|
showTooltip = true,
|
|
updateInterval = 60000 // Update every minute
|
|
}: RelativeTimeProps) {
|
|
const [, forceUpdate] = React.useReducer(x => x + 1, 0)
|
|
|
|
const parsedDate = React.useMemo(() => {
|
|
if (!date) return null
|
|
return typeof date === "string" ? new Date(date) : date
|
|
}, [date])
|
|
|
|
// Auto-update for recent times
|
|
React.useEffect(() => {
|
|
if (!parsedDate) return
|
|
|
|
const minutesAgo = differenceInMinutes(new Date(), parsedDate)
|
|
|
|
// Only auto-update if within the last hour
|
|
if (minutesAgo < 60) {
|
|
const interval = setInterval(forceUpdate, updateInterval)
|
|
return () => clearInterval(interval)
|
|
}
|
|
}, [parsedDate, updateInterval])
|
|
|
|
if (!parsedDate || isNaN(parsedDate.getTime())) {
|
|
return <span className={className}>-</span>
|
|
}
|
|
|
|
const formatRelative = (d: Date): string => {
|
|
const now = new Date()
|
|
const minutesAgo = differenceInMinutes(now, d)
|
|
|
|
// Just now (< 1 minute)
|
|
if (minutesAgo < 1) return "Just now"
|
|
|
|
// Minutes ago (< 60 minutes)
|
|
if (minutesAgo < 60) return `${minutesAgo}m ago`
|
|
|
|
// Hours ago (< 24 hours and today)
|
|
if (isToday(d)) {
|
|
const hoursAgo = Math.floor(minutesAgo / 60)
|
|
return `${hoursAgo}h ago`
|
|
}
|
|
|
|
// Yesterday
|
|
if (isYesterday(d)) return "Yesterday"
|
|
|
|
// Use formatDistanceToNow for older dates
|
|
return formatDistanceToNow(d, { addSuffix: true })
|
|
}
|
|
|
|
const fullDate = format(parsedDate, "dd MMM yyyy, HH:mm")
|
|
const relativeText = formatRelative(parsedDate)
|
|
|
|
if (!showTooltip) {
|
|
return <span className={className}>{relativeText}</span>
|
|
}
|
|
|
|
return (
|
|
<TooltipProvider>
|
|
<Tooltip delayDuration={300}>
|
|
<TooltipTrigger asChild>
|
|
<span className={`cursor-default ${className}`}>{relativeText}</span>
|
|
</TooltipTrigger>
|
|
<TooltipContent side="top" className="text-xs">
|
|
{fullDate}
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Utility function to get relative time string without component
|
|
*/
|
|
export function getRelativeTimeString(date: Date | string | null | undefined): string {
|
|
if (!date) return "-"
|
|
const d = typeof date === "string" ? new Date(date) : date
|
|
if (isNaN(d.getTime())) return "-"
|
|
|
|
const now = new Date()
|
|
const minutesAgo = differenceInMinutes(now, d)
|
|
|
|
if (minutesAgo < 1) return "Just now"
|
|
if (minutesAgo < 60) return `${minutesAgo}m ago`
|
|
if (isToday(d)) return `${Math.floor(minutesAgo / 60)}h ago`
|
|
if (isYesterday(d)) return "Yesterday"
|
|
|
|
return formatDistanceToNow(d, { addSuffix: true })
|
|
}
|