Update TimestampInfo to accept a UTC timestamp by default (#31167)

* add isUTC support in TimestampInfo

* rm isutc

* rename value prop to utcTimestamp

* Fixy

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Jordi EnricandJoshen Lim authored and Joshen Lim committed 2024-12-18 12:33:00 +08:00
1 parent 07629fe685
commit f78a6d09db
10 files changed
+60 -49

No files matched your search

@@ -112,7 +112,7 @@ export const UserLogs = ({ user }: UserLogsProps) => {
className="flex items-center transition font-mono px-2 py-1.5 bg-surface-100 divide-x"
>
<p className="text-xs text-foreground-light min-w-[125px] w-[125px] px-1">
<TimestampInfo value={log.timestamp / 1000} />
<TimestampInfo utcTimestamp={log.timestamp / 1000} />
</p>
<div className="flex items-center text-xs text-foreground-light h-[22px] min-w-[70px] w-[70px] px-2">
<div
@@ -1,11 +1,11 @@
import Panel from 'components/ui/Panel'
import BackupsEmpty from '../BackupsEmpty'
import { useCloneBackupsQuery } from 'data/projects/clone-query'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import Panel from 'components/ui/Panel'
import { useCloneBackupsQuery } from 'data/projects/clone-query'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { Badge, Button } from 'ui'
import { TimestampInfo } from 'ui-patterns'
import BackupsEmpty from '../BackupsEmpty'
interface BackupsListProps {
onSelectRestore: (id: number) => void
@@ -37,7 +37,7 @@ export const BackupsList = ({ onSelectRestore, disabled }: BackupsListProps) =>
return (
<div className="grid grid-cols-4 gap-4 items-center p-4" key={backup.id}>
<div>
<TimestampInfo value={backup.inserted_at} />
<TimestampInfo utcTimestamp={backup.inserted_at} />
</div>
<div>
<Badge>{JSON.stringify(backup.status).replaceAll('"', '')}</Badge>
@@ -16,7 +16,7 @@ const columns: Column<LogData>[] = [
return (
<RowLayout>
<TimestampInfo value={props.row.timestamp!} />
<TimestampInfo utcTimestamp={props.row.timestamp!} />
{props.row.level && <SeverityFormatter value={props.row.level as string} />}
<TextFormatter
className="w-full"
@@ -14,7 +14,7 @@ const columns: Column<LogData>[] = [
}
return (
<RowLayout>
<TimestampInfo value={props.row.timestamp!} />
<TimestampInfo utcTimestamp={props.row.timestamp!} />
<ResponseCodeFormatter row={props} value={props.row.status_code} />
<TextFormatter className="w-20" value={props.row.method as string} />
<TextFormatter className="w-full" value={props.row.path as string} />
@@ -14,7 +14,7 @@ const columns: Column<LogData>[] = [
}
return (
<RowLayout>
<TimestampInfo value={props.row.timestamp!} />
<TimestampInfo utcTimestamp={props.row.timestamp!} />
<SeverityFormatter value={props.row.error_severity as string} />
<TextFormatter className="w-full" value={props.row.event_message} />
</RowLayout>
@@ -5,7 +5,7 @@ import { TimestampInfo } from 'ui-patterns/TimestampInfo'
export const defaultRenderCell = (props: RenderCellProps<LogData, unknown>) => (
<RowLayout>
<TimestampInfo value={props.row.timestamp!} />
<TimestampInfo utcTimestamp={props.row.timestamp!} />
<TextFormatter className="w-full" value={props.row.event_message} />
</RowLayout>
)
@@ -14,7 +14,7 @@ const columns: Column<LogData>[] = [
}
return (
<RowLayout>
<TimestampInfo value={props.row.timestamp!} />
<TimestampInfo utcTimestamp={props.row.timestamp!} />
<ResponseCodeFormatter row={props} value={props.row.status_code} />
<TextFormatter value={props.row.method as string} />
<TextFormatter value={props.row.id} />
@@ -14,7 +14,7 @@ const columns: Column<LogData>[] = [
}
return (
<RowLayout>
<TimestampInfo value={props.row.timestamp!} />
<TimestampInfo utcTimestamp={props.row.timestamp!} />
{props.row.event_type === 'uncaughtException' ? (
<SeverityFormatter value={props.row.event_type} uppercase={false} />
) : (
@@ -150,7 +150,7 @@ const RestoreToNewProject = () => {
<div>
<TimestampInfo
className="font-mono text-xs text-foreground-lighter"
value={clone.inserted_at ?? ''}
utcTimestamp={clone.inserted_at ?? ''}
/>
</div>
</div>
@@ -168,7 +168,7 @@ const RestoreToNewProject = () => {
<div>
<TimestampInfo
className="font-mono text-xs text-foreground-lighter"
value={clone.inserted_at ?? ''}
utcTimestamp={clone.inserted_at ?? ''}
/>
</div>
<div className="flex items-center justify-end text-foreground-lighter group-hover:text-foreground">
+47 -36
View File
@@ -6,6 +6,7 @@ import { cn } from 'ui/src/lib/utils'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import utc from 'dayjs/plugin/utc'
import { Clipboard } from 'lucide-react'
dayjs.extend(relativeTime)
dayjs.extend(utc)
@@ -20,51 +21,56 @@ const isUnixMicro = (unix: string | number): boolean => {
return isNum && digitLength
}
const timestampLabelFormatter = (value: string | number, format: string) => {
const timestamp = isUnixMicro(value) ? unixMicroToIsoTimestamp(value) : value
return dayjs(timestamp).format(format)
type TimestampFormatter = {
utcTimestamp: string | number
format?: string
}
const timestampLocalFormatter = (value: string | number) => {
const timestamp = isUnixMicro(value) ? unixMicroToIsoTimestamp(value) : value
return dayjs(timestamp).format('DD MMM YYYY HH:mm:ss')
export const timestampLocalFormatter = ({ utcTimestamp, format }: TimestampFormatter) => {
const timestamp = isUnixMicro(utcTimestamp) ? unixMicroToIsoTimestamp(utcTimestamp) : utcTimestamp
return dayjs.utc(timestamp).local().format(format)
}
const timestampUtcFormatter = (value: string | number) => {
const timestamp = isUnixMicro(value) ? unixMicroToIsoTimestamp(value) : value
return dayjs(timestamp).utc().format('DD MMM YYYY HH:mm:ss')
const timestampUtcFormatter = ({ utcTimestamp, format }: TimestampFormatter) => {
const timestamp = isUnixMicro(utcTimestamp) ? unixMicroToIsoTimestamp(utcTimestamp) : utcTimestamp
return dayjs.utc(timestamp).format(format)
}
const timestampRelativeFormatter = (value: string | number) => {
const timestamp = isUnixMicro(value) ? unixMicroToIsoTimestamp(value) : value
return dayjs(timestamp).fromNow()
}
const timestampFormatter = (value: string) => {
const timestamp = isUnixMicro(value) ? unixMicroToIsoTimestamp(value) : value
return String(Number(dayjs(timestamp)))
const timestampRelativeFormatter = ({ utcTimestamp }: TimestampFormatter) => {
const timestamp = isUnixMicro(utcTimestamp) ? unixMicroToIsoTimestamp(utcTimestamp) : utcTimestamp
return dayjs.utc(timestamp).fromNow()
}
/**
* TimestampInfo component displays a timestamp with a tooltip showing various time formats.
* @param {string|number} props.utcTimestamp - UTC timestamp value. Can be either:
* - ISO 8601 string (e.g., "2024-01-01T00:00:00Z")
* - Unix microseconds (16-digit number)
* @param {string} [props.format="DD MMM HH:mm:ss"] - Display format for the timestamp (using dayjs format)
* @returns {JSX.Element} Timestamp display with tooltip showing UTC, local, relative, and raw timestamp values
*/
export const TimestampInfo = ({
value,
utcTimestamp,
className,
labelFormatter = 'DD MMM HH:mm:ss',
displayAs = 'local',
format = 'DD MMM HH:mm:ss',
labelFormat = 'DD MMM HH:mm:ss',
}: {
value: string | number
className?: string
labelFormatter?: string
utcTimestamp: string | number
displayAs?: 'local' | 'utc'
format?: string
labelFormat?: string
}) => {
const triggerRef = useRef<HTMLButtonElement>(null)
const local = timestampLocalFormatter({ utcTimestamp, format })
const utc = timestampUtcFormatter({ utcTimestamp, format })
const relative = timestampRelativeFormatter({ utcTimestamp })
const [align, setAlign] = useState<'start' | 'end'>('start')
const local = timestampLocalFormatter(value)
const utc = timestampUtcFormatter(value)
const relative = timestampRelativeFormatter(value)
const triggerRef = useRef<HTMLButtonElement>(null)
const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone
const timestamp = typeof value === 'number' ? String(value) : timestampFormatter(value)
// Calculate alignment based on trigger position
// Needed so that the tooltip isn't hidden behind the header on top rows
// Needed so that the tooltip isn't hidden behind the header on top rows (in logs)
useEffect(() => {
const updateAlignment = () => {
if (triggerRef.current) {
@@ -91,6 +97,7 @@ export const TimestampInfo = ({
<span
onClick={(e) => {
e.stopPropagation()
e.preventDefault()
navigator.clipboard.writeText(value)
setCopied(true)
@@ -99,10 +106,8 @@ export const TimestampInfo = ({
}, 1000)
}}
className={cn(
'relative cursor-default grid grid-cols-2 gap-2 hover:bg-surface-100 px-2 py-1',
{
'bg-surface-100': copied,
}
'relative cursor-default grid grid-cols-2 gap-2 hover:bg-surface-100 px-2 py-1 group',
{ 'bg-surface-100': copied }
)}
>
<span className="text-right truncate">{label}:</span>
@@ -112,7 +117,10 @@ export const TimestampInfo = ({
Copied!
</span>
)}
<span>{value}</span>
<span className="flex items-center gap-x-2">
{value}
<Clipboard size={12} className="opacity-0 group-hover:opacity-100 transition" />
</span>
</div>
</span>
)
@@ -122,16 +130,19 @@ export const TimestampInfo = ({
<Tooltip>
<TooltipTrigger
ref={triggerRef}
asChild
className={`text-xs ${className} border-b border-transparent hover:border-dashed hover:border-foreground-light`}
>
<span>{timestampLabelFormatter(value, labelFormatter)}</span>
<span>
{displayAs === 'local'
? timestampLocalFormatter({ utcTimestamp, format: labelFormat })
: timestampUtcFormatter({ utcTimestamp, format: labelFormat })}
</span>
</TooltipTrigger>
<TooltipContent align={align} side="right" className="font-mono p-0 py-1">
<TooltipRow label="UTC" value={utc} />
<TooltipRow label={`${localTimezone}`} value={local} />
<TooltipRow label="Relative" value={relative} />
<TooltipRow label="Timestamp" value={timestamp} />
<TooltipRow label="Timestamp" value={String(utcTimestamp)} />
</TooltipContent>
</Tooltip>
)