1
+ import { OrganizationSwitcher , useClerk } from "@clerk/clerk-react" ;
1
2
import {
2
3
faArrowUpRight ,
3
- faCheck ,
4
4
faLink ,
5
5
faServer ,
6
6
faSpinnerThird ,
7
- faTriangleExclamation ,
8
7
Icon ,
9
8
} from "@rivet-gg/icons" ;
10
9
import { useQuery } from "@tanstack/react-query" ;
11
- import { Link , useMatchRoute , useNavigate } from "@tanstack/react-router" ;
10
+ import {
11
+ Link ,
12
+ useMatch ,
13
+ useMatchRoute ,
14
+ useNavigate ,
15
+ } from "@tanstack/react-router" ;
12
16
import {
13
17
type ComponentProps ,
14
18
createContext ,
@@ -22,8 +26,10 @@ import {
22
26
useState ,
23
27
} from "react" ;
24
28
import type { ImperativePanelGroupHandle } from "react-resizable-panels" ;
29
+ import { match } from "ts-pattern" ;
25
30
import {
26
31
Button ,
32
+ type ButtonProps ,
27
33
cn ,
28
34
DocsSheet ,
29
35
type ImperativePanelHandle ,
@@ -148,13 +154,25 @@ const Sidebar = ({
148
154
/>
149
155
</ Link >
150
156
< div className = "flex flex-1 flex-col gap-4 px-2 min-h-0" >
151
- { __APP_TYPE__ === "inspector" ? (
152
- < ConnectionStatus />
153
- ) : null }
154
- { __APP_TYPE__ === "engine" ? < Breadcrumbs /> : null }
155
- < ScrollArea >
156
- < Subnav />
157
- </ ScrollArea >
157
+ { match ( __APP_TYPE__ )
158
+ . with ( "engine" , ( ) => (
159
+ < >
160
+ < ConnectionStatus />
161
+ < ScrollArea >
162
+ < Subnav />
163
+ </ ScrollArea >
164
+ </ >
165
+ ) )
166
+ . with ( "inspector" , ( ) => (
167
+ < >
168
+ < Breadcrumbs />
169
+ < ScrollArea >
170
+ < Subnav />
171
+ </ ScrollArea >
172
+ </ >
173
+ ) )
174
+ . with ( "cloud" , ( ) => < CloudSidebar /> )
175
+ . exhaustive ( ) }
158
176
</ div >
159
177
< div >
160
178
< div className = "border-t p-2 flex flex-col gap-[1px] text-sm" >
@@ -233,7 +251,7 @@ const Footer = () => {
233
251
234
252
export { Root , Main , Header , Footer , VisibleInFull , Sidebar } ;
235
253
236
- const Breadcrumbs = ( ) => {
254
+ const Breadcrumbs = ( ) : ReactNode => {
237
255
const matchRoute = useMatchRoute ( ) ;
238
256
const nsMatch = matchRoute ( {
239
257
to : "/ns/$namespace" ,
@@ -333,26 +351,37 @@ const Subnav = () => {
333
351
334
352
function HeaderLink ( { icon, children, className, ...props } : HeaderLinkProps ) {
335
353
return (
336
- < Button
354
+ < HeaderButton
337
355
asChild
338
356
variant = "ghost"
339
357
{ ...props }
340
- className = { cn (
341
- "text-muted-foreground px-2 aria-current-page:text-foreground relative h-auto py-1 justify-start" ,
342
- className ,
343
- ) }
344
358
startIcon = {
345
359
icon ? (
346
360
< Icon className = { cn ( "size-5 opacity-80" ) } icon = { icon } />
347
361
) : undefined
348
362
}
349
363
>
350
364
< Link to = { props . to } > { children } </ Link >
365
+ </ HeaderButton >
366
+ ) ;
367
+ }
368
+
369
+ function HeaderButton ( { children, className, ...props } : ButtonProps ) {
370
+ return (
371
+ < Button
372
+ variant = "ghost"
373
+ { ...props }
374
+ className = { cn (
375
+ "text-muted-foreground px-2 aria-current-page:text-foreground relative h-auto py-1 justify-start" ,
376
+ className ,
377
+ ) }
378
+ >
379
+ { children }
351
380
</ Button >
352
381
) ;
353
382
}
354
383
355
- function ConnectionStatus ( ) {
384
+ function ConnectionStatus ( ) : ReactNode {
356
385
const { endpoint, ...queries } = useManager ( ) ;
357
386
const { setCredentials } = useInspectorCredentials ( ) ;
358
387
const { isLoading, isError, isSuccess } = useQuery (
@@ -406,4 +435,48 @@ function ConnectionStatus() {
406
435
</ div >
407
436
) ;
408
437
}
438
+
439
+ return null ;
440
+ }
441
+
442
+ function CloudSidebar ( ) : ReactNode {
443
+ const match = useMatch ( {
444
+ from : "/_layout/orgs/$organization/" ,
445
+ shouldThrow : false ,
446
+ } ) ;
447
+
448
+ const clerk = useClerk ( ) ;
449
+ return (
450
+ < >
451
+ < OrganizationSwitcher />
452
+
453
+ < ScrollArea >
454
+ < div className = "flex gap-1.5 flex-col" >
455
+ < HeaderLink
456
+ to = "/orgs/$organization"
457
+ className = "font-normal"
458
+ params = { match ?. params }
459
+ >
460
+ Projects
461
+ </ HeaderLink >
462
+ < HeaderButton
463
+ onClick = { ( ) => {
464
+ clerk . openUserProfile ( {
465
+ __experimental_startPath : "/billing" ,
466
+ } ) ;
467
+ } }
468
+ >
469
+ Billing
470
+ </ HeaderButton >
471
+ < HeaderButton
472
+ onClick = { ( ) => {
473
+ clerk . openUserProfile ( ) ;
474
+ } }
475
+ >
476
+ Settings
477
+ </ HeaderButton >
478
+ </ div >
479
+ </ ScrollArea >
480
+ </ >
481
+ ) ;
409
482
}
0 commit comments