diff --git a/apps/studio/components/interfaces/Auth/Users/UserLogs.tsx b/apps/studio/components/interfaces/Auth/Users/UserLogs.tsx index b42598509c0..2ec1e4f43d8 100644 --- a/apps/studio/components/interfaces/Auth/Users/UserLogs.tsx +++ b/apps/studio/components/interfaces/Auth/Users/UserLogs.tsx @@ -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" >

- +

void @@ -37,7 +37,7 @@ export const BackupsList = ({ onSelectRestore, disabled }: BackupsListProps) => return (
- +
{JSON.stringify(backup.status).replaceAll('"', '')} diff --git a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/AuthColumnRenderer.tsx b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/AuthColumnRenderer.tsx index 32401f8ffd5..6262a2ef899 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/AuthColumnRenderer.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/AuthColumnRenderer.tsx @@ -16,7 +16,7 @@ const columns: Column[] = [ return ( - + {props.row.level && } [] = [ } return ( - + diff --git a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/DatabasePostgresColumnRender.tsx b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/DatabasePostgresColumnRender.tsx index 46e8f5aa4ed..6692a29465d 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/DatabasePostgresColumnRender.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/DatabasePostgresColumnRender.tsx @@ -14,7 +14,7 @@ const columns: Column[] = [ } return ( - + diff --git a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/DefaultPreviewColumnRenderer.tsx b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/DefaultPreviewColumnRenderer.tsx index 5025e2b513e..997361ffbb9 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/DefaultPreviewColumnRenderer.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/DefaultPreviewColumnRenderer.tsx @@ -5,7 +5,7 @@ import { TimestampInfo } from 'ui-patterns/TimestampInfo' export const defaultRenderCell = (props: RenderCellProps) => ( - + ) diff --git a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/FunctionsEdgeColumnRender.tsx b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/FunctionsEdgeColumnRender.tsx index 147732803d3..9d76d5a880f 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/FunctionsEdgeColumnRender.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/FunctionsEdgeColumnRender.tsx @@ -14,7 +14,7 @@ const columns: Column[] = [ } return ( - + diff --git a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/FunctionsLogsColumnRender.tsx b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/FunctionsLogsColumnRender.tsx index ba43620699f..9fcb99c4216 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/FunctionsLogsColumnRender.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogColumnRenderers/FunctionsLogsColumnRender.tsx @@ -14,7 +14,7 @@ const columns: Column[] = [ } return ( - + {props.row.event_type === 'uncaughtException' ? ( ) : ( diff --git a/apps/studio/pages/project/[ref]/database/backups/restore-to-new-project.tsx b/apps/studio/pages/project/[ref]/database/backups/restore-to-new-project.tsx index c9fc727a6f1..a4ffed86e90 100644 --- a/apps/studio/pages/project/[ref]/database/backups/restore-to-new-project.tsx +++ b/apps/studio/pages/project/[ref]/database/backups/restore-to-new-project.tsx @@ -150,7 +150,7 @@ const RestoreToNewProject = () => {
@@ -168,7 +168,7 @@ const RestoreToNewProject = () => {
diff --git a/packages/ui-patterns/TimestampInfo/index.tsx b/packages/ui-patterns/TimestampInfo/index.tsx index b537382b9b1..7b0cf1a82cf 100644 --- a/packages/ui-patterns/TimestampInfo/index.tsx +++ b/packages/ui-patterns/TimestampInfo/index.tsx @@ -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(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(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 = ({ { 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 } )} > {label}: @@ -112,7 +117,10 @@ export const TimestampInfo = ({ Copied! )} - {value} + + {value} + +
) @@ -122,16 +130,19 @@ export const TimestampInfo = ({ - {timestampLabelFormatter(value, labelFormatter)} + + {displayAs === 'local' + ? timestampLocalFormatter({ utcTimestamp, format: labelFormat }) + : timestampUtcFormatter({ utcTimestamp, format: labelFormat })} + - + )