Skip to content

Commit e187e21

Browse files
authored
FE-1813: Audit Logs Improvements (supabase#37946)
* use logsdatepicker in audit logs, fix error hiding filters * use logsdatepicker in acc audit logs * move filters out of err * add hideWarnings to logs date picker. make "today" button scroll to current month. change icon and add tooltip in timerange reset button so its not confused with a close button * fix filters below loading state
1 parent 2cca945 commit e187e21

File tree

3 files changed

+152
-129
lines changed

3 files changed

+152
-129
lines changed

apps/studio/components/interfaces/Account/AuditLogs.tsx

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@ import { LogDetailsPanel } from 'components/interfaces/AuditLogs'
66
import Table from 'components/to-be-cleaned/Table'
77
import AlertError from 'components/ui/AlertError'
88
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
9-
import { DatePicker } from 'components/ui/DatePicker'
109
import { FilterPopover } from 'components/ui/FilterPopover'
1110
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
1211
import type { AuditLog } from 'data/organizations/organization-audit-logs-query'
1312
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
1413
import { useProfileAuditLogsQuery } from 'data/profile/profile-audit-logs-query'
1514
import { useProjectsQuery } from 'data/projects/projects-query'
16-
import { Alert, Button } from 'ui'
15+
import { Button } from 'ui'
1716
import { TimestampInfo } from 'ui-patterns'
18-
import { formatSelectedDateRange } from '../Organization/AuditLogs/AuditLogs.utils'
17+
import { LogsDatePicker } from '../Settings/Logs/Logs.DatePickers'
1918

2019
const AuditLogs = () => {
2120
const currentTime = dayjs().utc().set('millisecond', 0)
@@ -33,12 +32,16 @@ const AuditLogs = () => {
3332
const { data: projects } = useProjectsQuery()
3433
const { data: organizations } = useOrganizationsQuery()
3534
const { data, error, isLoading, isSuccess, isError, isRefetching, refetch } =
36-
useProfileAuditLogsQuery({
37-
iso_timestamp_start: dateRange.from,
38-
iso_timestamp_end: dateRange.to,
39-
})
35+
useProfileAuditLogsQuery(
36+
{
37+
iso_timestamp_start: dateRange.from,
38+
iso_timestamp_end: dateRange.to,
39+
},
40+
{
41+
retry: false,
42+
}
43+
)
4044

41-
const retentionPeriod = data?.retention_period ?? 0
4245
const logs = data?.result ?? []
4346
const sortedLogs = logs
4447
?.sort((a, b) =>
@@ -54,9 +57,6 @@ const AuditLogs = () => {
5457
}
5558
})
5659

57-
const minDate = dayjs().subtract(retentionPeriod, 'days')
58-
const maxDate = dayjs()
59-
6060
// This feature depends on the subscription tier of the user. Free user can view logs up to 1 day
6161
// in the past. The API limits the logs to maximum of 1 day and 5 minutes so when the page is
6262
// viewed for more than 5 minutes, the call parameters needs to be updated. This also works with
@@ -88,35 +88,38 @@ const AuditLogs = () => {
8888
activeOptions={filters.projects}
8989
onSaveFilters={(values) => setFilters({ ...filters, projects: values })}
9090
/>
91-
<DatePicker
92-
hideTime
93-
hideClear
94-
triggerButtonType="dashed"
95-
triggerButtonTitle=""
96-
from={dateRange.from}
97-
to={dateRange.to}
98-
minDate={minDate.toDate()}
99-
maxDate={maxDate.toDate()}
100-
onChange={(value) => {
101-
if (value.from !== null && value.to !== null) {
102-
const { from, to } = formatSelectedDateRange(value)
103-
setDateRange({ from, to })
104-
}
105-
}}
106-
renderFooter={() => {
107-
return (
108-
<Alert title="" variant="info" className="mx-3 pl-2 pr-2 pt-1 pb-2">
109-
You have a log retention period of{' '}
110-
<span className="text-brand">
111-
{retentionPeriod} day
112-
{retentionPeriod > 1 ? 's' : ''}
113-
</span>
114-
. You may only view logs from{' '}
115-
{dayjs().subtract(retentionPeriod, 'days').format('DD MMM YYYY')} as the
116-
earliest date.
117-
</Alert>
118-
)
119-
}}
91+
<LogsDatePicker
92+
hideWarnings
93+
value={dateRange}
94+
onSubmit={(value) => setDateRange(value)}
95+
helpers={[
96+
{
97+
text: 'Last 1 hour',
98+
calcFrom: () => dayjs().subtract(1, 'hour').toISOString(),
99+
calcTo: () => dayjs().toISOString(),
100+
},
101+
{
102+
text: 'Last 3 hours',
103+
calcFrom: () => dayjs().subtract(3, 'hour').toISOString(),
104+
calcTo: () => dayjs().toISOString(),
105+
},
106+
107+
{
108+
text: 'Last 6 hours',
109+
calcFrom: () => dayjs().subtract(6, 'hour').toISOString(),
110+
calcTo: () => dayjs().toISOString(),
111+
},
112+
{
113+
text: 'Last 12 hours',
114+
calcFrom: () => dayjs().subtract(12, 'hour').toISOString(),
115+
calcTo: () => dayjs().toISOString(),
116+
},
117+
{
118+
text: 'Last 24 hours',
119+
calcFrom: () => dayjs().subtract(1, 'day').toISOString(),
120+
calcTo: () => dayjs().toISOString(),
121+
},
122+
]}
120123
/>
121124
{isSuccess && (
122125
<>

apps/studio/components/interfaces/Organization/AuditLogs/AuditLogs.tsx

Lines changed: 82 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ import {
3131
Button,
3232
WarningIcon,
3333
} from 'ui'
34-
import { Admonition } from 'ui-patterns'
35-
import { formatSelectedDateRange } from './AuditLogs.utils'
34+
import { LogsDatePicker } from 'components/interfaces/Settings/Logs/Logs.DatePickers'
3635

3736
// [Joshen considerations]
3837
// - 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 = () => {
6968
},
7069
{
7170
enabled: canReadAuditLogs,
72-
retry(_failureCount, error) {
73-
if (error.message.endsWith('upgrade to Team or Enterprise Plan to access audit logs.')) {
74-
return false
75-
}
76-
return true
77-
},
78-
retryOnMount: false,
79-
refetchOnWindowFocus: false,
71+
retry: false,
8072
}
8173
)
8274

@@ -139,6 +131,77 @@ const AuditLogs = () => {
139131
<>
140132
<ScaffoldContainerLegacy>
141133
<div className="space-y-4 flex flex-col">
134+
<div className="flex items-center justify-between">
135+
<div className="flex items-center space-x-2">
136+
<p className="text-xs prose">Filter by</p>
137+
<FilterPopover
138+
name="Users"
139+
options={activeMembers}
140+
labelKey="username"
141+
valueKey="gotrue_id"
142+
activeOptions={filters.users}
143+
onSaveFilters={(values) => setFilters({ ...filters, users: values })}
144+
/>
145+
<FilterPopover
146+
name="Projects"
147+
options={
148+
projects?.filter((p) => p.organization_id === currentOrganization?.id) ?? []
149+
}
150+
labelKey="name"
151+
valueKey="ref"
152+
activeOptions={filters.projects}
153+
onSaveFilters={(values) => setFilters({ ...filters, projects: values })}
154+
/>
155+
<LogsDatePicker
156+
hideWarnings
157+
value={dateRange}
158+
onSubmit={(value) => setDateRange(value)}
159+
helpers={[
160+
{
161+
text: 'Last 1 hour',
162+
calcFrom: () => dayjs().subtract(1, 'hour').toISOString(),
163+
calcTo: () => dayjs().toISOString(),
164+
},
165+
{
166+
text: 'Last 3 hours',
167+
calcFrom: () => dayjs().subtract(3, 'hour').toISOString(),
168+
calcTo: () => dayjs().toISOString(),
169+
},
170+
171+
{
172+
text: 'Last 6 hours',
173+
calcFrom: () => dayjs().subtract(6, 'hour').toISOString(),
174+
calcTo: () => dayjs().toISOString(),
175+
},
176+
{
177+
text: 'Last 12 hours',
178+
calcFrom: () => dayjs().subtract(12, 'hour').toISOString(),
179+
calcTo: () => dayjs().toISOString(),
180+
},
181+
{
182+
text: 'Last 24 hours',
183+
calcFrom: () => dayjs().subtract(1, 'day').toISOString(),
184+
calcTo: () => dayjs().toISOString(),
185+
},
186+
]}
187+
/>
188+
{isSuccess && (
189+
<>
190+
<div className="h-[20px] border-r border-strong !ml-4 !mr-2" />
191+
<p className="prose text-xs">Viewing {sortedLogs.length} logs in total</p>
192+
</>
193+
)}
194+
</div>
195+
<Button
196+
type="default"
197+
disabled={isLoading || isRefetching}
198+
icon={<RefreshCw className={isRefetching ? 'animate-spin' : ''} />}
199+
onClick={() => refetch()}
200+
>
201+
{isRefetching ? 'Refreshing' : 'Refresh'}
202+
</Button>
203+
</div>
204+
142205
{isLoading && (
143206
<div className="space-y-2">
144207
<ShimmeringLoader />
@@ -176,86 +239,22 @@ const AuditLogs = () => {
176239
</div>
177240
</div>
178241
</Alert_Shadcn_>
242+
) : error.message.includes('range exceeded') ? (
243+
<Alert_Shadcn_ variant="destructive" title="Date range too large">
244+
<WarningIcon />
245+
<AlertTitle_Shadcn_>Date range too large</AlertTitle_Shadcn_>
246+
<AlertDescription_Shadcn_>
247+
The selected date range exceeds the maximum allowed period. Please select a
248+
smaller time range.
249+
</AlertDescription_Shadcn_>
250+
</Alert_Shadcn_>
179251
) : (
180252
<AlertError error={error} subject="Failed to retrieve audit logs" />
181253
)
182254
) : null}
183255

184256
{isSuccess && (
185257
<>
186-
<div className="flex items-center justify-between">
187-
<div className="flex items-center space-x-2">
188-
<p className="text-xs prose">Filter by</p>
189-
<FilterPopover
190-
name="Users"
191-
options={activeMembers}
192-
labelKey="username"
193-
valueKey="gotrue_id"
194-
activeOptions={filters.users}
195-
onSaveFilters={(values) => setFilters({ ...filters, users: values })}
196-
/>
197-
<FilterPopover
198-
name="Projects"
199-
options={
200-
projects?.filter((p) => p.organization_id === currentOrganization?.id) ?? []
201-
}
202-
labelKey="name"
203-
valueKey="ref"
204-
activeOptions={filters.projects}
205-
onSaveFilters={(values) => setFilters({ ...filters, projects: values })}
206-
/>
207-
<DatePicker
208-
hideTime
209-
hideClear
210-
triggerButtonType="dashed"
211-
triggerButtonTitle=""
212-
from={dateRange.from}
213-
to={dateRange.to}
214-
minDate={minDate.toDate()}
215-
maxDate={maxDate.toDate()}
216-
onChange={(value) => {
217-
if (value.from !== null && value.to !== null) {
218-
const { from, to } = formatSelectedDateRange(value)
219-
setDateRange({ from, to })
220-
}
221-
}}
222-
renderFooter={() => {
223-
return (
224-
<Admonition
225-
showIcon={false}
226-
type="default"
227-
className="w-auto mx-2 px-3 py-2"
228-
>
229-
<div className="text-xs text-foreground-light">
230-
Your organization has a log retention period of{' '}
231-
<span className="text-brand">
232-
{retentionPeriod} day
233-
{retentionPeriod > 1 ? 's' : ''}
234-
</span>
235-
. You may only view logs from{' '}
236-
{dayjs().subtract(retentionPeriod, 'days').format('DD MMM YYYY')} as the
237-
earliest date.
238-
</div>
239-
</Admonition>
240-
)
241-
}}
242-
/>
243-
{isSuccess && (
244-
<>
245-
<div className="h-[20px] border-r border-strong !ml-4 !mr-2" />
246-
<p className="prose text-xs">Viewing {sortedLogs.length} logs in total</p>
247-
</>
248-
)}
249-
</div>
250-
<Button
251-
type="default"
252-
disabled={isLoading || isRefetching}
253-
icon={<RefreshCw className={isRefetching ? 'animate-spin' : ''} />}
254-
onClick={() => refetch()}
255-
>
256-
{isRefetching ? 'Refreshing' : 'Refresh'}
257-
</Button>
258-
</div>
259258
{logs.length === 0 ? (
260259
<div className="bg-surface-100 border rounded p-4 flex items-center justify-between">
261260
<p className="prose text-sm">

0 commit comments

Comments
 (0)