7
7
import { User , TeamMemberInfo } from "@gitpod/gitpod-protocol" ;
8
8
import { useContext , useEffect , useState } from "react" ;
9
9
import { Link , useHistory } from "react-router-dom" ;
10
- import { useLocation } from "react-router" ;
10
+ import { useLocation , useRouteMatch } from "react-router" ;
11
11
import { Location } from "history" ;
12
12
import gitpodIcon from './icons/gitpod.svg' ;
13
13
import CaretDown from "./icons/CaretDown.svg" ;
@@ -39,6 +39,14 @@ export default function Menu() {
39
39
const history = useHistory ( ) ;
40
40
const location = useLocation ( ) ;
41
41
42
+ const match = useRouteMatch < { team : string , resource : string } > ( "/:team/:resource" ) ;
43
+ const projectName = ( ( ) => {
44
+ const resource = match ?. params ?. resource ;
45
+ if ( resource !== "projects" && resource !== "members" ) {
46
+ return resource ;
47
+ }
48
+ } ) ( ) ;
49
+
42
50
const userFullName = user ?. fullName || user ?. name || '...' ;
43
51
const showTeamsUI = user ?. rolesOrPermissions ?. includes ( 'teams-and-projects' ) || window . location . hostname . endsWith ( 'gitpod-dev.com' ) || window . location . hostname . endsWith ( 'gitpod-io-dev.com' ) ;
44
52
const team = getCurrentTeam ( location , teams ) ;
@@ -58,32 +66,48 @@ export default function Menu() {
58
66
} ) ( ) ;
59
67
} , [ teams ] ) ;
60
68
61
- const leftMenu = ( ! ! team
62
- ? [
69
+ const leftMenu : Entry [ ] = ( ( ) => {
70
+ if ( ! team ) {
71
+ return [
72
+ {
73
+ title : 'Workspaces' ,
74
+ link : '/workspaces' ,
75
+ alternatives : [ '/' ]
76
+ } ,
77
+ {
78
+ title : 'Settings' ,
79
+ link : '/settings' ,
80
+ alternatives : settingsMenu . flatMap ( e => e . link )
81
+ }
82
+ ] ;
83
+ }
84
+ return projectName ? [
63
85
{
64
- title : 'Projects ' ,
65
- link : `/${ team . slug } /projects ` ,
86
+ title : 'Overview ' ,
87
+ link : `/${ team . slug } /${ projectName } ` ,
66
88
alternatives : [ `/${ team . slug } ` ]
67
89
} ,
68
90
{
69
- title : 'Members' ,
70
- link : `/${ team . slug } /members`
91
+ title : 'Prebuilds' ,
92
+ link : `/${ team . slug } /${ projectName } /prebuilds`
93
+ } ,
94
+ {
95
+ title : 'Settings' ,
96
+ link : `/${ team . slug } /${ projectName } /settings`
71
97
}
72
- ]
73
- : [
98
+ ] : [
74
99
{
75
- title : 'Workspaces ' ,
76
- link : '/workspaces' ,
77
- alternatives : [ '/' ]
100
+ title : 'Projects ' ,
101
+ link : `/ ${ team . slug } /projects` ,
102
+ alternatives : [ `/ ${ team . slug } ` ]
78
103
} ,
79
104
{
80
- title : 'Settings' ,
81
- link : '/settings' ,
82
- alternatives : settingsMenu . flatMap ( e => e . link )
105
+ title : 'Members' ,
106
+ link : `/${ team . slug } /members`
83
107
}
84
108
]
85
- ) ;
86
- const rightMenu = [
109
+ } ) ( ) ;
110
+ const rightMenu : Entry [ ] = [
87
111
...( user ?. rolesOrPermissions ?. includes ( 'admin' ) ? [ {
88
112
title : 'Admin' ,
89
113
link : '/admin' ,
@@ -99,6 +123,61 @@ export default function Menu() {
99
123
}
100
124
] ;
101
125
126
+ const renderTeamMenu = ( ) => {
127
+ return (
128
+ < div className = "flex p-1 pl-3 " >
129
+ < div className = "flex h-full rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 px-2 py-1" >
130
+ < Link to = { team ? `/${ team . slug } /projects` : "/workspaces" } >
131
+
132
+ < span className = "text-base text-gray-600 dark:text-gray-400 font-semibold" > { team ?. name || userFullName } </ span >
133
+ </ Link >
134
+ </ div >
135
+ < div className = "flex h-full rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 py-1" >
136
+ < ContextMenu classes = "w-64 left-0" menuEntries = { [
137
+ {
138
+ title : userFullName ,
139
+ customContent : < div className = "w-full text-gray-400 flex flex-col" >
140
+ < span className = "text-gray-800 dark:text-gray-100 text-base font-semibold" > { userFullName } </ span >
141
+ < span className = "" > Personal Account</ span >
142
+ </ div > ,
143
+ separator : true ,
144
+ onClick : ( ) => history . push ( "/" ) ,
145
+ } ,
146
+ ...( teams || [ ] ) . map ( t => ( {
147
+ title : t . name ,
148
+ customContent : < div className = "w-full text-gray-400 flex flex-col" >
149
+ < span className = "text-gray-800 dark:text-gray-300 text-base font-semibold" > { t . name } </ span >
150
+ < span className = "" > { ! ! teamMembers [ t . id ]
151
+ ? `${ teamMembers [ t . id ] . length } member${ teamMembers [ t . id ] . length === 1 ? '' : 's' } `
152
+ : '...'
153
+ } </ span >
154
+ </ div > ,
155
+ separator : true ,
156
+ onClick : ( ) => history . push ( `/${ t . slug } ` ) ,
157
+ } ) ) . sort ( ( a , b ) => a . title . toLowerCase ( ) > b . title . toLowerCase ( ) ? 1 : - 1 ) ,
158
+ {
159
+ title : 'Create a new team' ,
160
+ customContent : < div className = "w-full text-gray-400 flex items-center" >
161
+ < span className = "flex-1 font-semibold" > New Team</ span >
162
+ < svg xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 14 14" className = "w-3.5" > < path fill = "currentColor" fill-rule = "evenodd" d = "M7 0a1 1 0 011 1v5h5a1 1 0 110 2H8v5a1 1 0 11-2 0V8H1a1 1 0 010-2h5V1a1 1 0 011-1z" clip-rule = "evenodd" /> </ svg >
163
+ </ div > ,
164
+ onClick : ( ) => history . push ( "/new-team" ) ,
165
+ }
166
+ ] } >
167
+ < div className = "flex h-full p-2 mt-0.5" >
168
+ < img className = "filter-grayscale m-auto" src = { CaretDown } />
169
+ </ div >
170
+ </ ContextMenu >
171
+ </ div >
172
+ { projectName && (
173
+ < div className = "flex h-full ml-2 py-1" >
174
+ < span className = "text-base text-gray-600 dark:text-gray-400 font-semibold" > { projectName } </ span >
175
+ </ div >
176
+ ) }
177
+ </ div >
178
+ )
179
+ }
180
+
102
181
return < >
103
182
< header className = "lg:px-28 px-10 flex flex-col pt-4 space-y-4" >
104
183
< div className = "flex" >
@@ -108,42 +187,7 @@ export default function Menu() {
108
187
</ Link >
109
188
< div className = "ml-2 text-base" >
110
189
{ showTeamsUI
111
- ? < ContextMenu classes = "w-64 left-0" menuEntries = { [
112
- {
113
- title : userFullName ,
114
- customContent : < div className = "w-full text-gray-400 flex flex-col" >
115
- < span className = "text-gray-800 dark:text-gray-100 text-base font-semibold" > { userFullName } </ span >
116
- < span className = "" > Personal Account</ span >
117
- </ div > ,
118
- separator : true ,
119
- onClick : ( ) => history . push ( "/" ) ,
120
- } ,
121
- ...( teams || [ ] ) . map ( t => ( {
122
- title : t . name ,
123
- customContent : < div className = "w-full text-gray-400 flex flex-col" >
124
- < span className = "text-gray-800 dark:text-gray-300 text-base font-semibold" > { t . name } </ span >
125
- < span className = "" > { ! ! teamMembers [ t . id ]
126
- ? `${ teamMembers [ t . id ] . length } member${ teamMembers [ t . id ] . length === 1 ? '' : 's' } `
127
- : '...'
128
- } </ span >
129
- </ div > ,
130
- separator : true ,
131
- onClick : ( ) => history . push ( `/${ t . slug } ` ) ,
132
- } ) ) . sort ( ( a , b ) => a . title . toLowerCase ( ) > b . title . toLowerCase ( ) ? 1 : - 1 ) ,
133
- {
134
- title : 'Create a new team' ,
135
- customContent : < div className = "w-full text-gray-400 flex items-center" >
136
- < span className = "flex-1 font-semibold" > New Team</ span >
137
- < svg xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 14 14" className = "w-3.5" > < path fill = "currentColor" fill-rule = "evenodd" d = "M7 0a1 1 0 011 1v5h5a1 1 0 110 2H8v5a1 1 0 11-2 0V8H1a1 1 0 010-2h5V1a1 1 0 011-1z" clip-rule = "evenodd" /> </ svg >
138
- </ div > ,
139
- onClick : ( ) => history . push ( "/new-team" ) ,
140
- }
141
- ] } >
142
- < div className = "flex p-1.5 pl-3 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" >
143
- < span className = "text-base text-gray-600 dark:text-gray-400 font-semibold" > { team ?. name || userFullName } </ span >
144
- < img className = "m-2 filter-grayscale" src = { CaretDown } />
145
- </ div >
146
- </ ContextMenu >
190
+ ? renderTeamMenu ( )
147
191
: < nav className = "flex-1" >
148
192
< ul className = "flex flex-1 items-center justify-between text-base text-gray-700 space-x-2" >
149
193
< li className = "flex-1" > </ li >
0 commit comments