diff --git a/apps/studio/components/interfaces/Account/AuditLogs.tsx b/apps/studio/components/interfaces/Account/AuditLogs.tsx
index f5f49c5fc0c..63a96069040 100644
--- a/apps/studio/components/interfaces/Account/AuditLogs.tsx
+++ b/apps/studio/components/interfaces/Account/AuditLogs.tsx
@@ -15,6 +15,7 @@ import { useProfileAuditLogsQuery } from 'data/profile/profile-audit-logs-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { Alert, Button } from 'ui'
import { TimestampInfo } from 'ui-patterns'
+import { formatSelectedDateRange } from '../Organization/AuditLogs/AuditLogs.utils'
const AuditLogs = () => {
const currentTime = dayjs().utc().set('millisecond', 0)
@@ -70,6 +71,9 @@ const AuditLogs = () => {
}
})
+ const minDate = dayjs().subtract(retentionPeriod, 'days')
+ const maxDate = dayjs()
+
return (
<>
@@ -91,47 +95,12 @@ const AuditLogs = () => {
triggerButtonTitle=""
from={dateRange.from}
to={dateRange.to}
- minDate={dayjs().subtract(retentionPeriod, 'days').toDate()}
- maxDate={dayjs().toDate()}
+ minDate={minDate.toDate()}
+ maxDate={maxDate.toDate()}
onChange={(value) => {
if (value.from !== null && value.to !== null) {
- const current = dayjs()
- const from = dayjs(value.from)
- .hour(current.hour())
- .minute(current.minute())
- .second(current.second())
- const to = dayjs(value.to)
- .hour(current.hour())
- .minute(current.minute())
- .second(current.second())
-
- if (from.date() === to.date()) {
- // [Joshen] If a single date is selected, we either set the "from" to start from 00:00
- // or "to" to end at 23:59 depending on which date was selected
- if (from.date() === current.date()) {
- setDateRange({
- from: from
- .set('hour', 0)
- .set('minute', 0)
- .set('second', 0)
- .utc()
- .toISOString(),
- to: to.utc().toISOString(),
- })
- } else {
- setDateRange({
- from: from.utc().toISOString(),
- to: to
- .set('hour', 23)
- .set('minute', 59)
- .set('second', 59)
- .utc()
- .toISOString(),
- })
- }
- } else {
- setDateRange({ from: from.utc().toISOString(), to: to.utc().toISOString() })
- }
+ const { from, to } = formatSelectedDateRange(value)
+ setDateRange({ from, to })
}
}}
renderFooter={() => {
diff --git a/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx b/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx
index 5abde526b91..0895737379e 100644
--- a/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx
+++ b/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx
@@ -32,6 +32,7 @@ import {
WarningIcon,
} from 'ui'
import { Admonition } from 'ui-patterns'
+import { formatSelectedDateRange } from './AuditLogs.utils'
// [Joshen considerations]
// - Maybe fix the height of the table to the remaining height of the viewport, so that the search input is always visible
@@ -79,23 +80,6 @@ const AuditLogs = () => {
}
)
- // 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
- // higher tiers (7 days of logs).The user will see a loading shimmer.
- useEffect(() => {
- const duration = dayjs(dateRange.from).diff(dayjs(dateRange.to))
- const interval = setInterval(() => {
- const currentTime = dayjs().utc().set('millisecond', 0)
- setDateRange({
- from: currentTime.add(duration).toISOString(),
- to: currentTime.toISOString(),
- })
- }, 5 * 60000)
-
- return () => clearInterval(interval)
- }, [dateRange.from, dateRange.to])
-
const activeMembers = (members ?? []).filter((x) => !x.invited_at)
const roles = [...(rolesData?.org_scoped_roles ?? []), ...(rolesData?.project_scoped_roles ?? [])]
@@ -123,6 +107,25 @@ const AuditLogs = () => {
})
const currentOrganization = organizations?.find((o) => o.slug === slug)
+ 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
+ // higher tiers (7 days of logs).The user will see a loading shimmer.
+ useEffect(() => {
+ const duration = dayjs(dateRange.from).diff(dayjs(dateRange.to))
+ const interval = setInterval(() => {
+ const currentTime = dayjs().utc().set('millisecond', 0)
+ setDateRange({
+ from: currentTime.add(duration).toISOString(),
+ to: currentTime.toISOString(),
+ })
+ }, 5 * 60000)
+
+ return () => clearInterval(interval)
+ }, [dateRange.from, dateRange.to])
if (!canReadAuditLogs) {
return (
@@ -208,23 +211,11 @@ const AuditLogs = () => {
triggerButtonTitle=""
from={dateRange.from}
to={dateRange.to}
- minDate={dayjs().subtract(retentionPeriod, 'days').toDate()}
- maxDate={dayjs().toDate()}
+ minDate={minDate.toDate()}
+ maxDate={maxDate.toDate()}
onChange={(value) => {
if (value.from !== null && value.to !== null) {
- const current = dayjs().utc()
- const from = dayjs(value.from)
- .utc()
- .hour(current.hour())
- .minute(current.minute())
- .second(current.second())
- .toISOString()
- const to = dayjs(value.to)
- .utc()
- .hour(current.hour())
- .minute(current.minute())
- .second(current.second())
- .toISOString()
+ const { from, to } = formatSelectedDateRange(value)
setDateRange({ from, to })
}
}}
diff --git a/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.utils.ts b/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.utils.ts
new file mode 100644
index 00000000000..c5429c8e6c3
--- /dev/null
+++ b/apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.utils.ts
@@ -0,0 +1,34 @@
+import { DatePickerToFrom } from 'components/interfaces/Settings/Logs/Logs.types'
+import dayjs from 'dayjs'
+
+// [Joshen] Mainly to handle if a single date is selected - currently just for Audit Logs as
+// i'm on the fence if this logic should be within the DatePicker component itself
+// e.g for Logs.DatePicker which uses this component, the component itself has its own time selection UI
+// JFYI currentDate is just a parameter so that I can run tests for this
+
+export const formatSelectedDateRange = (value: DatePickerToFrom) => {
+ const current = dayjs()
+ const from = dayjs(value.from)
+ .hour(current.hour())
+ .minute(current.minute())
+ .second(current.second())
+ const to = dayjs(value.to).hour(current.hour()).minute(current.minute()).second(current.second())
+
+ if (from.date() === to.date()) {
+ // [Joshen] If a single date is selected, we either set the "from" to start from 00:00
+ // or "to" to end at 23:59 depending on which date was selected
+ if (from.date() === current.date()) {
+ return {
+ from: from.set('hour', 0).set('minute', 0).set('second', 0).utc().toISOString(),
+ to: to.utc().toISOString(),
+ }
+ } else {
+ return {
+ from: from.utc().toISOString(),
+ to: to.set('hour', 23).set('minute', 59).set('second', 59).utc().toISOString(),
+ }
+ }
+ } else {
+ return { from: from.utc().toISOString(), to: to.utc().toISOString() }
+ }
+}
diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx b/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx
index f48b236428b..2910f150828 100644
--- a/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx
+++ b/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx
@@ -1,7 +1,8 @@
-import { DatePicker } from 'components/ui/DatePicker'
import dayjs from 'dayjs'
-import { useEffect, useState } from 'react'
+import { Clock } from 'lucide-react'
+import { ComponentProps, PropsWithChildren, useEffect, useState } from 'react'
+import { DatePicker } from 'components/ui/DatePicker'
import {
Alert,
Button,
@@ -13,15 +14,14 @@ import {
} from 'ui'
import { LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD, getDefaultHelper } from './Logs.constants'
import type { DatetimeHelper } from './Logs.types'
-import { Clock } from 'lucide-react'
interface Props {
to: string
from: string
- onChange: React.ComponentProps
['onChange']
+ onChange: ComponentProps['onChange']
helpers: DatetimeHelper[]
}
-const DatePickers: React.FC = ({ to, from, onChange, helpers }) => {
+const DatePickers = ({ to, from, onChange, helpers }: PropsWithChildren) => {
const defaultHelper = getDefaultHelper(helpers)
const [helperValue, setHelperValue] = useState(to || from ? '' : defaultHelper.text)
diff --git a/apps/studio/data/organizations/organization-audit-logs-query.ts b/apps/studio/data/organizations/organization-audit-logs-query.ts
index 4480816c4a1..47f3cf7f372 100644
--- a/apps/studio/data/organizations/organization-audit-logs-query.ts
+++ b/apps/studio/data/organizations/organization-audit-logs-query.ts
@@ -1,5 +1,4 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
-import dayjs from 'dayjs'
import { get, handleError } from 'data/fetchers'
import type { ResponseError } from 'types'
@@ -68,11 +67,12 @@ export const useOrganizationAuditLogsQuery =
}: UseQueryOptions = {}
) => {
const { slug, iso_timestamp_start, iso_timestamp_end } = vars
- const date_start = dayjs(iso_timestamp_start).utc().format('YYYY-MM-DD')
- const date_end = dayjs(iso_timestamp_end).utc().format('YYYY-MM-DD')
return useQuery(
- organizationKeys.auditLogs(slug, { date_start, date_end }),
+ organizationKeys.auditLogs(slug, {
+ date_start: iso_timestamp_start,
+ date_end: iso_timestamp_end,
+ }),
({ signal }) => getOrganizationAuditLogs(vars, signal),
{
enabled: enabled && typeof slug !== 'undefined',