1- import React , { useMemo , useEffect , useState , useLayoutEffect } from 'react'
1+ import React , { useMemo , useEffect , useState , useLayoutEffect , useCallback } from 'react'
22
33import { mdiPlus } from '@mdi/js'
44import classNames from 'classnames'
5+ import { type Location , useNavigate , useLocation , type NavigateFunction } from 'react-router-dom'
56import { of } from 'rxjs'
67import { catchError , map } from 'rxjs/operators'
78
89import { asError , isErrorLike } from '@sourcegraph/common'
910import type { Settings } from '@sourcegraph/shared/src/schema/settings.schema'
10- import { type SettingsCascadeProps , useExperimentalFeatures } from '@sourcegraph/shared/src/settings/settings'
11+ import { type SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
1112import {
1213 PageHeader ,
1314 LoadingSpinner ,
@@ -16,6 +17,7 @@ import {
1617 Link ,
1718 ProductStatusBadge ,
1819 Icon ,
20+ ButtonLink ,
1921} from '@sourcegraph/wildcard'
2022
2123import type { AuthenticatedUser } from '../../auth'
@@ -32,6 +34,34 @@ import { CodeMonitoringGettingStarted } from './CodeMonitoringGettingStarted'
3234import { CodeMonitoringLogs } from './CodeMonitoringLogs'
3335import { CodeMonitorList } from './CodeMonitorList'
3436
37+ type MonitorsTab = 'list' | 'getting-started' | 'logs'
38+ type Tabs = { tab : MonitorsTab ; title : string ; isActive : boolean } [ ]
39+
40+ function getSelectedTabFromLocation (
41+ locationSearch : string ,
42+ userHasCodeMonitors : boolean | Error | undefined
43+ ) : MonitorsTab {
44+ const urlParameters = new URLSearchParams ( locationSearch )
45+ switch ( urlParameters . get ( 'tab' ) ) {
46+ case 'list' :
47+ return 'list'
48+ case 'getting-started' :
49+ return 'getting-started'
50+ case 'logs' :
51+ return 'logs'
52+ }
53+
54+ return userHasCodeMonitors ? 'list' : 'getting-started'
55+ }
56+
57+ function setSelectedLocationTab ( location : Location , navigate : NavigateFunction , selectedTab : MonitorsTab ) : void {
58+ const urlParameters = new URLSearchParams ( location . search )
59+ urlParameters . set ( 'tab' , selectedTab )
60+ if ( location . search !== urlParameters . toString ( ) ) {
61+ navigate ( { ...location , search : urlParameters . toString ( ) } , { replace : true } )
62+ }
63+ }
64+
3565export interface CodeMonitoringPageProps extends SettingsCascadeProps < Settings > {
3666 authenticatedUser : AuthenticatedUser | null
3767 fetchUserCodeMonitors ?: typeof _fetchUserCodeMonitors
@@ -67,18 +97,20 @@ export const CodeMonitoringPage: React.FunctionComponent<React.PropsWithChildren
6797 )
6898 )
6999
70- const [ currentTab , setCurrentTab ] = useState < 'list' | 'getting-started' | 'logs' | null > ( null )
100+ const navigate = useNavigate ( )
101+ const location = useLocation ( )
71102
72- // Select the appropriate tab after loading:
73- // - If the user has code monitors, show the list tab
74- // - If the user has no code monitors, show the getting started tab
75- useLayoutEffect ( ( ) => {
76- if ( userHasCodeMonitors === true ) {
77- setCurrentTab ( 'list' )
78- } else if ( userHasCodeMonitors === false ) {
79- setCurrentTab ( 'getting-started' )
80- }
81- } , [ userHasCodeMonitors ] )
103+ const [ currentTab , setCurrentTab ] = useState < MonitorsTab > ( ( ) =>
104+ getSelectedTabFromLocation ( location . search , userHasCodeMonitors )
105+ )
106+
107+ const onSelectTab = useCallback (
108+ ( tab : MonitorsTab ) => {
109+ setCurrentTab ( tab )
110+ setSelectedLocationTab ( location , navigate , tab )
111+ } ,
112+ [ navigate , location , setCurrentTab ]
113+ )
82114
83115 // Force tab for testing
84116 useLayoutEffect ( ( ) => {
@@ -105,8 +137,26 @@ export const CodeMonitoringPage: React.FunctionComponent<React.PropsWithChildren
105137
106138 const showList = userHasCodeMonitors !== undefined && ! isErrorLike ( userHasCodeMonitors ) && currentTab === 'list'
107139
108- const showLogsTab =
109- useExperimentalFeatures ( features => features . showCodeMonitoringLogs ) && authenticatedUser && ! isCodyApp
140+ const tabs : Tabs = useMemo (
141+ ( ) => [
142+ {
143+ tab : 'list' ,
144+ title : 'Code monitors' ,
145+ isActive : currentTab === 'list' ,
146+ } ,
147+ {
148+ tab : 'getting-started' ,
149+ title : 'Getting started' ,
150+ isActive : currentTab === 'getting-started' ,
151+ } ,
152+ {
153+ tab : 'logs' ,
154+ title : 'Logs' ,
155+ isActive : currentTab === 'logs' ,
156+ } ,
157+ ] ,
158+ [ currentTab ]
159+ )
110160
111161 return (
112162 < div className = "code-monitoring-page" data-testid = "code-monitoring-page" >
@@ -136,56 +186,24 @@ export const CodeMonitoringPage: React.FunctionComponent<React.PropsWithChildren
136186 < div className = "d-flex flex-column" >
137187 < div className = "code-monitoring-page-tabs mb-4" >
138188 < div className = "nav nav-tabs" >
139- { ! isCodyApp && (
140- < div className = "nav-item" >
141- < Link
189+ { tabs . map ( ( { tab , title , isActive } ) => (
190+ < div className = "nav-item" key = { tab } >
191+ < ButtonLink
142192 to = ""
143- onClick = { event => {
144- event . preventDefault ( )
145- setCurrentTab ( 'list' )
146- } }
147- className = { classNames ( 'nav-link' , currentTab === 'list' && 'active' ) }
148193 role = "button"
149- >
150- < span className = "text-content" data-tab-content = "Code monitors" >
151- Code monitors
152- </ span >
153- </ Link >
154- </ div >
155- ) }
156- < div className = "nav-item" >
157- < Link
158- to = ""
159- onClick = { event => {
160- event . preventDefault ( )
161- setCurrentTab ( 'getting-started' )
162- } }
163- className = { classNames ( 'nav-link' , currentTab === 'getting-started' && 'active' ) }
164- role = "button"
165- >
166- < span className = "text-content" data-tab-content = "Getting started" >
167- Getting started
168- </ span >
169- </ Link >
170- </ div >
171- { showLogsTab && (
172- < div className = "nav-item" >
173- < Link
174- to = ""
175- onClick = { event => {
194+ onSelect = { event => {
176195 event . preventDefault ( )
177- setCurrentTab ( 'logs' )
196+ onSelectTab ( tab )
178197 } }
179- className = { classNames ( 'nav-link flex-row' , currentTab === 'logs' && 'active' ) }
180- role = "button"
198+ className = { classNames ( 'nav-link' , isActive && 'active' ) }
181199 >
182- < span className = "text-content" data-tab-content = "Logs" >
183- Logs
200+ < span >
201+ { title }
202+ { tab === 'logs' && < ProductStatusBadge status = "beta" className = "ml-2" /> }
184203 </ span >
185- < ProductStatusBadge status = "beta" className = "ml-2" />
186- </ Link >
204+ </ ButtonLink >
187205 </ div >
188- ) }
206+ ) ) }
189207 </ div >
190208 </ div >
191209
0 commit comments