@@ -9,7 +9,16 @@ import { AuthMechChart, AuthMechDataPoint } from './auth-mech-chart'
99import { DailySignupsChart , DailySignupsDataPoint } from './daily-signups-chart'
1010import { DailyLoginFailsChart , DailyLoginFailsDataPoint } from './daily-login-fails-chart'
1111import { DateRangePicker , DateRange } from '@/components/ui/date-range-picker'
12- import { startOfDay , endOfDay , format } from 'date-fns'
12+ import { startOfDay , endOfDay , format , subDays } from 'date-fns'
13+ import {
14+ Select ,
15+ SelectContent ,
16+ SelectItem ,
17+ SelectTrigger ,
18+ SelectValue ,
19+ } from "@/components/ui/select"
20+ import { Separator } from "@/components/ui/separator"
21+ import { Card , CardContent } from "@/components/ui/card"
1322
1423interface ConversionData {
1524 new_signups : number
@@ -74,14 +83,56 @@ export default function Auth0Dashboard() {
7483 const [ authMechData , setAuthMechData ] = useState < AuthMechDataPoint [ ] > ( [ ] )
7584 const [ dailySignupsData , setDailySignupsData ] = useState < DailySignupsDataPoint [ ] > ( [ ] )
7685 const [ dailyLoginFailsData , setDailyLoginFailsData ] = useState < DailyLoginFailsDataPoint [ ] > ( [ ] )
86+ const [ selectedApp , setSelectedApp ] = useState < string > ( 'all' )
87+ const [ selectedConnection , setSelectedConnection ] = useState < string > ( 'all' )
88+ const [ applications , setApplications ] = useState < Array < { client_id : string , client_name : string } > > ( [ ] )
89+ const [ connections , setConnections ] = useState < Array < { connection_id : string , connection_name : string } > > ( [ ] )
7790
7891 useEffect ( ( ) => {
92+ async function fetchInitialData ( ) {
93+ if ( ! token ) return
94+ try {
95+ const [ appsResult , connsResult ] = await Promise . all ( [
96+ pipe < { data : Array < { client_id : string , client_name : string } > } > (
97+ token ,
98+ 'auth0_applications'
99+ ) ,
100+ pipe < { data : Array < { id : string , connection_name : string } > } > (
101+ token ,
102+ 'auth0_connections'
103+ )
104+ ] )
105+ setApplications ( appsResult . data ?? [ ] )
106+ setConnections ( connsResult . data ?? [ ] )
107+ } catch ( error ) {
108+ console . error ( 'Failed to fetch initial data:' , error )
109+ }
110+ }
111+ fetchInitialData ( )
112+ } , [ token ] )
113+
114+ useEffect ( ( ) => {
115+ let mounted = true
116+
79117 async function fetchMetrics ( ) {
80118 if ( ! token ) return
81119
82120 const fromDate = format ( dateRange . from , "yyyy-MM-dd HH:mm:ss" )
83121 const toDate = format ( dateRange . to , "yyyy-MM-dd HH:mm:ss" )
84- const thirtyDaysAgo = format ( new Date ( Date . now ( ) - 30 * 24 * 60 * 60 * 1000 ) , "yyyy-MM-dd HH:mm:ss" )
122+ const thirtyDaysAgo = format ( subDays ( new Date ( ) , 30 ) , "yyyy-MM-dd HH:mm:ss" )
123+
124+ const params = {
125+ date_from : fromDate ,
126+ date_to : toDate ,
127+ ...( selectedApp !== 'all' && { client_id : selectedApp } ) ,
128+ ...( selectedConnection !== 'all' && { connection_id : selectedConnection } )
129+ }
130+
131+ const thirtyDayParams = {
132+ date_from : thirtyDaysAgo ,
133+ ...( selectedApp !== 'all' && { client_id : selectedApp } ) ,
134+ ...( selectedConnection !== 'all' && { connection_id : selectedConnection } )
135+ }
85136
86137 try {
87138 const [
@@ -93,39 +144,30 @@ export default function Auth0Dashboard() {
93144 monthlyActiveUsersResult ,
94145 conversionRateResult ,
95146 dauResult ,
147+ dauComparisonResult ,
96148 authMechResult ,
97149 dailySignupsResult ,
98150 dailyLoginFailsResult
99151 ] = await Promise . all ( [
100- pipe < UsersResult > ( token , 'auth0_users_total' ) ,
152+ pipe < UsersResult > ( token , 'auth0_users_total' , selectedApp !== 'all' || selectedConnection !== 'all' ? {
153+ ...( selectedApp !== 'all' && { client_id : selectedApp } ) ,
154+ ...( selectedConnection !== 'all' && { connection_id : selectedConnection } )
155+ } : undefined ) ,
101156 pipe ( token , 'auth0_applications' ) ,
102157 pipe ( token , 'auth0_apis' ) ,
103158 pipe ( token , 'auth0_connections' ) ,
104- pipe ( token , 'auth0_signups' , {
105- date_from : thirtyDaysAgo
106- } ) ,
107- pipe ( token , 'auth0_mau' , {
108- date_from : thirtyDaysAgo
109- } ) ,
110- pipe < ConversionRateResult > ( token , 'auth0_conversion_rate' , {
111- date_from : thirtyDaysAgo
112- } ) ,
113- pipe < { data : DauDataPoint [ ] } > ( token , 'auth0_dau_ts' , {
114- date_from : fromDate ,
115- date_to : toDate
116- } ) ,
117- pipe < { data : AuthMechDataPoint [ ] } > ( token , 'auth0_mech_usage' , {
118- date_from : fromDate ,
119- date_to : toDate
120- } ) ,
121- pipe < { data : DailySignupsDataPoint [ ] } > ( token , 'auth0_daily_signups' , {
122- date_from : fromDate ,
123- date_to : toDate
124- } ) ,
125- pipe < { data : DailyLoginFailsDataPoint [ ] } > ( token , 'auth0_daily_login_fails' , {
126- date_from : fromDate ,
127- date_to : toDate
128- } )
159+ pipe ( token , 'auth0_signups' , thirtyDayParams ) ,
160+ pipe ( token , 'auth0_mau' , thirtyDayParams ) ,
161+ pipe < ConversionRateResult > ( token , 'auth0_conversion_rate' , thirtyDayParams ) ,
162+ pipe < { data : DauDataPoint [ ] } > ( token , 'auth0_dau_ts' , params ) ,
163+ dateRange . compareMode ? pipe < { data : DauDataPoint [ ] } > ( token , 'auth0_dau_ts' , {
164+ ...params ,
165+ date_from : comparisonFromDate ,
166+ date_to : comparisonToDate
167+ } ) : Promise . resolve ( { data : [ ] } ) ,
168+ pipe < { data : AuthMechDataPoint [ ] } > ( token , 'auth0_mech_usage' , params ) ,
169+ pipe < { data : DailySignupsDataPoint [ ] } > ( token , 'auth0_daily_signups' , params ) ,
170+ pipe < { data : DailyLoginFailsDataPoint [ ] } > ( token , 'auth0_daily_login_fails' , params )
129171 ] )
130172
131173 setSummaryMetrics ( {
@@ -148,30 +190,70 @@ export default function Auth0Dashboard() {
148190 }
149191
150192 fetchMetrics ( )
151- } , [ token , dateRange ] )
193+ return ( ) => {
194+ mounted = false
195+ }
196+ } , [ token , dateRange . from , dateRange . to , dateRange . compareMode , selectedApp , selectedConnection ] )
152197
153198 return (
154199 < div className = "space-y-8" >
155- { /* Top row - Total metrics */ }
156- < div className = "grid gap-4 md:grid-cols-2 lg:grid-cols-4" >
157- < MetricCard
158- title = "Total Users"
159- value = { summaryMetrics . total_users }
160- />
161- < MetricCard
162- title = "Applications"
163- value = { summaryMetrics . total_applications }
164- />
165- < MetricCard
166- title = "APIs"
167- value = { summaryMetrics . total_apis }
168- />
169- < MetricCard
170- title = "Connections"
171- value = { summaryMetrics . total_connections }
172- />
200+ < div className = "flex justify-between items-center gap-4" >
201+ < div className = "flex gap-2" >
202+ < Select value = { selectedApp } onValueChange = { setSelectedApp } >
203+ < SelectTrigger className = "w-[200px]" >
204+ < SelectValue placeholder = "Select application" />
205+ </ SelectTrigger >
206+ < SelectContent >
207+ < SelectItem value = "all" > All Applications</ SelectItem >
208+ { applications . map ( ( app ) => (
209+ < SelectItem key = { app . client_id } value = { app . client_id } >
210+ { app . client_name }
211+ </ SelectItem >
212+ ) ) }
213+ </ SelectContent >
214+ </ Select >
215+
216+ < Select value = { selectedConnection } onValueChange = { setSelectedConnection } >
217+ < SelectTrigger className = "w-[200px]" >
218+ < SelectValue placeholder = "Select connection" />
219+ </ SelectTrigger >
220+ < SelectContent >
221+ < SelectItem value = "all" > All Connections</ SelectItem >
222+ { connections . map ( ( conn ) => (
223+ < SelectItem key = { conn . connection_id } value = { conn . connection_id } >
224+ { conn . connection_name }
225+ </ SelectItem >
226+ ) ) }
227+ </ SelectContent >
228+ </ Select >
229+ </ div >
173230 </ div >
174231
232+ { /* Top row - Total metrics */ }
233+ < Card >
234+ < CardContent className = "pt-6" >
235+ < div className = "grid gap-4 md:grid-cols-2 lg:grid-cols-4" >
236+ < div className = "flex flex-col" >
237+ < span className = "text-sm font-medium" > Total Users</ span >
238+ < span className = "text-2xl font-bold" > { summaryMetrics . total_users } </ span >
239+ </ div >
240+ < div className = "flex flex-col" >
241+ < span className = "text-sm font-medium" > Applications</ span >
242+ < span className = "text-2xl font-bold" > { summaryMetrics . total_applications } </ span >
243+ </ div >
244+ < div className = "flex flex-col" >
245+ < span className = "text-sm font-medium" > APIs</ span >
246+ < span className = "text-2xl font-bold" > { summaryMetrics . total_apis } </ span >
247+ </ div >
248+ < div className = "flex flex-col" >
249+ < span className = "text-sm font-medium" > Connections</ span >
250+ < span className = "text-2xl font-bold" > { summaryMetrics . total_connections } </ span >
251+ </ div >
252+ </ div >
253+ </ CardContent >
254+ </ Card >
255+
256+
175257 { /* Second row - Monthly metrics */ }
176258 < div className = "grid gap-4 md:grid-cols-2 lg:grid-cols-3" >
177259 < MetricCard
@@ -191,14 +273,20 @@ export default function Auth0Dashboard() {
191273 />
192274 </ div >
193275
194- { /* Date Range Picker */ }
276+ < Separator className = "my-6" />
277+
195278 < div className = "flex justify-end" >
196279 < DateRangePicker
197280 initialDateRange = { dateRange }
198281 onChange = { ( newRange ) => setDateRange ( newRange ) }
199282 />
200283 </ div >
201284
285+ < DauChart
286+ data = { dauData }
287+ comparisonData = { dateRange . compareMode ? dauComparisonData : undefined }
288+ />
289+
202290 { /* Charts Grid */ }
203291 < div className = "grid gap-4 grid-cols-1" >
204292 < DauChart data = { dauData } />
0 commit comments