@@ -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