mirror of
https://github.com/supabase/supabase.git
synced 2026-10-16 22:59:11 +03:00
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:
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
|
||||
|
||||
+4
-4
@@ -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>
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
+1
-1
@@ -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} />
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+1
-1
@@ -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>
|
||||
)
|
||||
|
||||
+1
-1
@@ -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} />
|
||||
|
||||
+1
-1
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user