diff --git a/apps/studio/components/interfaces/Account/AuditLogs.tsx b/apps/studio/components/interfaces/Account/AuditLogs.tsx index eeef64ab8c9ae..9b774a17b9c81 100644 --- a/apps/studio/components/interfaces/Account/AuditLogs.tsx +++ b/apps/studio/components/interfaces/Account/AuditLogs.tsx @@ -6,16 +6,15 @@ import { LogDetailsPanel } from 'components/interfaces/AuditLogs' import Table from 'components/to-be-cleaned/Table' import AlertError from 'components/ui/AlertError' import { ButtonTooltip } from 'components/ui/ButtonTooltip' -import { DatePicker } from 'components/ui/DatePicker' import { FilterPopover } from 'components/ui/FilterPopover' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import type { AuditLog } from 'data/organizations/organization-audit-logs-query' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useProfileAuditLogsQuery } from 'data/profile/profile-audit-logs-query' import { useProjectsQuery } from 'data/projects/projects-query' -import { Alert, Button } from 'ui' +import { Button } from 'ui' import { TimestampInfo } from 'ui-patterns' -import { formatSelectedDateRange } from '../Organization/AuditLogs/AuditLogs.utils' +import { LogsDatePicker } from '../Settings/Logs/Logs.DatePickers' const AuditLogs = () => { const currentTime = dayjs().utc().set('millisecond', 0) @@ -33,12 +32,16 @@ const AuditLogs = () => { const { data: projects } = useProjectsQuery() const { data: organizations } = useOrganizationsQuery() const { data, error, isLoading, isSuccess, isError, isRefetching, refetch } = - useProfileAuditLogsQuery({ - iso_timestamp_start: dateRange.from, - iso_timestamp_end: dateRange.to, - }) + useProfileAuditLogsQuery( + { + iso_timestamp_start: dateRange.from, + iso_timestamp_end: dateRange.to, + }, + { + retry: false, + } + ) - const retentionPeriod = data?.retention_period ?? 0 const logs = data?.result ?? [] const sortedLogs = logs ?.sort((a, b) => @@ -54,9 +57,6 @@ const AuditLogs = () => { } }) - const minDate = dayjs().subtract(retentionPeriod, 'days') - const maxDate = dayjs() - // This feature depends on the subscription tier of the user. Free user can view logs up to 1 day // in the past. The API limits the logs to maximum of 1 day and 5 minutes so when the page is // viewed for more than 5 minutes, the call parameters needs to be updated. This also works with @@ -88,35 +88,38 @@ const AuditLogs = () => { activeOptions={filters.projects} onSaveFilters={(values) => setFilters({ ...filters, projects: values })} /> - { - if (value.from !== null && value.to !== null) { - const { from, to } = formatSelectedDateRange(value) - setDateRange({ from, to }) - } - }} - renderFooter={() => { - return ( - - You have a log retention period of{' '} - - {retentionPeriod} day - {retentionPeriod > 1 ? 's' : ''} - - . You may only view logs from{' '} - {dayjs().subtract(retentionPeriod, 'days').format('DD MMM YYYY')} as the - earliest date. - - ) - }} + setDateRange(value)} + helpers={[ + { + text: 'Last 1 hour', + calcFrom: () => dayjs().subtract(1, 'hour').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + { + text: 'Last 3 hours', + calcFrom: () => dayjs().subtract(3, 'hour').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + + { + text: 'Last 6 hours', + calcFrom: () => dayjs().subtract(6, 'hour').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + { + text: 'Last 12 hours', + calcFrom: () => dayjs().subtract(12, 'hour').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + { + text: 'Last 24 hours', + calcFrom: () => dayjs().subtract(1, 'day').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + ]} /> {isSuccess && ( <> diff --git a/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx b/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx index 0895737379e51..f14eb4f2030af 100644 --- a/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx +++ b/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx @@ -31,8 +31,7 @@ import { Button, WarningIcon, } from 'ui' -import { Admonition } from 'ui-patterns' -import { formatSelectedDateRange } from './AuditLogs.utils' +import { LogsDatePicker } from 'components/interfaces/Settings/Logs/Logs.DatePickers' // [Joshen considerations] // - Maybe fix the height of the table to the remaining height of the viewport, so that the search input is always visible @@ -69,14 +68,7 @@ const AuditLogs = () => { }, { enabled: canReadAuditLogs, - retry(_failureCount, error) { - if (error.message.endsWith('upgrade to Team or Enterprise Plan to access audit logs.')) { - return false - } - return true - }, - retryOnMount: false, - refetchOnWindowFocus: false, + retry: false, } ) @@ -139,6 +131,77 @@ const AuditLogs = () => { <>
+
+
+

Filter by

+ setFilters({ ...filters, users: values })} + /> + p.organization_id === currentOrganization?.id) ?? [] + } + labelKey="name" + valueKey="ref" + activeOptions={filters.projects} + onSaveFilters={(values) => setFilters({ ...filters, projects: values })} + /> + setDateRange(value)} + helpers={[ + { + text: 'Last 1 hour', + calcFrom: () => dayjs().subtract(1, 'hour').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + { + text: 'Last 3 hours', + calcFrom: () => dayjs().subtract(3, 'hour').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + + { + text: 'Last 6 hours', + calcFrom: () => dayjs().subtract(6, 'hour').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + { + text: 'Last 12 hours', + calcFrom: () => dayjs().subtract(12, 'hour').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + { + text: 'Last 24 hours', + calcFrom: () => dayjs().subtract(1, 'day').toISOString(), + calcTo: () => dayjs().toISOString(), + }, + ]} + /> + {isSuccess && ( + <> +
+

Viewing {sortedLogs.length} logs in total

+ + )} +
+ +
+ {isLoading && (
@@ -176,6 +239,15 @@ const AuditLogs = () => {
+ ) : error.message.includes('range exceeded') ? ( + + + Date range too large + + The selected date range exceeds the maximum allowed period. Please select a + smaller time range. + + ) : ( ) @@ -183,79 +255,6 @@ const AuditLogs = () => { {isSuccess && ( <> -
-
-

Filter by

- setFilters({ ...filters, users: values })} - /> - p.organization_id === currentOrganization?.id) ?? [] - } - labelKey="name" - valueKey="ref" - activeOptions={filters.projects} - onSaveFilters={(values) => setFilters({ ...filters, projects: values })} - /> - { - if (value.from !== null && value.to !== null) { - const { from, to } = formatSelectedDateRange(value) - setDateRange({ from, to }) - } - }} - renderFooter={() => { - return ( - -
- Your organization has a log retention period of{' '} - - {retentionPeriod} day - {retentionPeriod > 1 ? 's' : ''} - - . You may only view logs from{' '} - {dayjs().subtract(retentionPeriod, 'days').format('DD MMM YYYY')} as the - earliest date. -
-
- ) - }} - /> - {isSuccess && ( - <> -
-

Viewing {sortedLogs.length} logs in total

- - )} -
- -
{logs.length === 0 ? (

diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx b/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx index b4a7010cce6a2..258a65f1dfb52 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx @@ -1,7 +1,7 @@ /* eslint-disable react-hooks/exhaustive-deps */ import dayjs from 'dayjs' -import { ChevronLeft, ChevronRight, Clock, XIcon } from 'lucide-react' -import { PropsWithChildren, useEffect, useState } from 'react' +import { ChevronLeft, ChevronRight, Clock, HistoryIcon, XIcon } from 'lucide-react' +import { PropsWithChildren, useEffect, useRef, useState } from 'react' import DatePicker from 'react-datepicker' import { Label } from '@ui/components/shadcn/ui/label' @@ -20,6 +20,7 @@ import { } from 'ui' import { LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD } from './Logs.constants' import type { DatetimeHelper } from './Logs.types' +import { ButtonTooltip } from 'components/ui/ButtonTooltip' export type DatePickerValue = { to: string @@ -34,6 +35,7 @@ interface Props { onSubmit: (value: DatePickerValue) => void buttonTriggerProps?: ButtonProps popoverContentProps?: typeof PopoverContent_Shadcn_ + hideWarnings?: boolean } export const LogsDatePicker = ({ @@ -42,9 +44,12 @@ export const LogsDatePicker = ({ value, buttonTriggerProps, popoverContentProps, + hideWarnings, }: PropsWithChildren) => { const [open, setOpen] = useState(false) + const todayButtonRef = useRef(null) + // Reset the state when the popover closes useEffect(() => { if (!open) { @@ -315,8 +320,13 @@ export const LogsDatePicker = ({ />

- + >
+ +
+ } onChange={(dates) => { handleDatePickerChange(dates) }} @@ -371,7 +388,7 @@ export const LogsDatePicker = ({ )} />
- {isLargeRange && ( + {isLargeRange && !hideWarnings && (
Large ranges may result in memory errors for
big projects.
@@ -393,6 +410,10 @@ export const LogsDatePicker = ({