mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
Fix single date selection in organization audit logs (#34689)
* Fix single date selection in organization audit logs * Update apps/studio/components/interfaces/Account/AuditLogs.tsx Co-authored-by: Alaister Young <alaister@users.noreply.github.com> --------- Co-authored-by: Alaister Young <alaister@users.noreply.github.com>
This commit is contained in:
1 parent
06091157d9
commit
74723eccb6
5 files changed
+74
-80
No files matched your search
@@ -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 (
|
||||
<>
|
||||
<div className="space-y-4 flex flex-col pb-8">
|
||||
@@ -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={() => {
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
@@ -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<typeof DatePicker>['onChange']
|
||||
onChange: ComponentProps<typeof DatePicker>['onChange']
|
||||
helpers: DatetimeHelper[]
|
||||
}
|
||||
const DatePickers: React.FC<Props> = ({ to, from, onChange, helpers }) => {
|
||||
const DatePickers = ({ to, from, onChange, helpers }: PropsWithChildren<Props>) => {
|
||||
const defaultHelper = getDefaultHelper(helpers)
|
||||
const [helperValue, setHelperValue] = useState<string>(to || from ? '' : defaultHelper.text)
|
||||
|
||||
|
||||
@@ -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 = <TData = OrganizationAuditLogsData>
|
||||
}: UseQueryOptions<OrganizationAuditLogsData, OrganizationAuditLogsError, TData> = {}
|
||||
) => {
|
||||
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<OrganizationAuditLogsData, OrganizationAuditLogsError, TData>(
|
||||
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',
|
||||
|
||||
Reference in new issue
Block a user