|
5 | 5 | * |
6 | 6 | * Copyright Oxide Computer Company |
7 | 7 | */ |
8 | | -import { useMemo, type ReactElement } from 'react' |
9 | | -import { useLocation, useNavigate, type LoaderFunctionArgs } from 'react-router' |
10 | 8 |
|
11 | | -import { apiq, queryClient, usePrefetchedQuery } from '@oxide/api' |
12 | 9 | import { |
13 | | - Access16Icon, |
14 | | - Folder16Icon, |
15 | | - Images16Icon, |
16 | | - Instances16Icon, |
17 | | - IpGlobal16Icon, |
18 | | - Networking16Icon, |
19 | | - Snapshots16Icon, |
20 | | - Storage16Icon, |
21 | | -} from '@oxide/design-system/icons/react' |
| 10 | + ProjectLayoutBase, |
| 11 | + projectLayoutHandle, |
| 12 | + projectLayoutLoader, |
| 13 | +} from './ProjectLayoutBase.tsx' |
22 | 14 |
|
23 | | -import { TopBar } from '~/components/TopBar' |
24 | | -import { getProjectSelector, useProjectSelector } from '~/hooks/use-params' |
25 | | -import { useQuickActions } from '~/hooks/use-quick-actions' |
26 | | -import { Divider } from '~/ui/lib/Divider' |
27 | | -import { pb } from '~/util/path-builder' |
28 | | -import type * as PP from '~/util/path-params' |
| 15 | +export const clientLoader = projectLayoutLoader |
29 | 16 |
|
30 | | -import { DocsLinkItem, NavLinkItem, Sidebar } from '../components/Sidebar' |
31 | | -import { ContentPane, PageContainer } from './helpers' |
| 17 | +export const handle = projectLayoutHandle |
32 | 18 |
|
33 | | -type ProjectLayoutProps = { |
34 | | - /** Sometimes we need a different layout for the content pane. Like |
35 | | - * `<ContentPane />`, the element passed here should contain an `<Outlet />`. |
36 | | - */ |
37 | | - overrideContentPane?: ReactElement |
38 | | -} |
39 | | - |
40 | | -const projectView = ({ project }: PP.Project) => apiq('projectView', { path: { project } }) |
41 | | - |
42 | | -ProjectLayout.loader = async ({ params }: LoaderFunctionArgs) => { |
43 | | - const { project } = getProjectSelector(params) |
44 | | - await queryClient.prefetchQuery(projectView({ project })) |
45 | | - return null |
46 | | -} |
47 | | - |
48 | | -export function ProjectLayout({ overrideContentPane }: ProjectLayoutProps) { |
49 | | - const navigate = useNavigate() |
50 | | - // project will always be there, instance may not |
51 | | - const projectSelector = useProjectSelector() |
52 | | - const { data: project } = usePrefetchedQuery(projectView(projectSelector)) |
53 | | - |
54 | | - const { pathname } = useLocation() |
55 | | - useQuickActions( |
56 | | - useMemo( |
57 | | - () => |
58 | | - [ |
59 | | - { value: 'Instances', path: pb.instances(projectSelector) }, |
60 | | - { value: 'Disks', path: pb.disks(projectSelector) }, |
61 | | - { value: 'Snapshots', path: pb.snapshots(projectSelector) }, |
62 | | - { value: 'Images', path: pb.projectImages(projectSelector) }, |
63 | | - { value: 'VPCs', path: pb.vpcs(projectSelector) }, |
64 | | - { value: 'Floating IPs', path: pb.floatingIps(projectSelector) }, |
65 | | - { value: 'Access', path: pb.projectAccess(projectSelector) }, |
66 | | - ] |
67 | | - // filter out the entry for the path we're currently on |
68 | | - .filter((i) => i.path !== pathname) |
69 | | - .map((i) => ({ |
70 | | - navGroup: `Project '${project.name}'`, |
71 | | - value: i.value, |
72 | | - onSelect: () => navigate(i.path), |
73 | | - })), |
74 | | - [pathname, navigate, project.name, projectSelector] |
75 | | - ) |
76 | | - ) |
77 | | - |
78 | | - return ( |
79 | | - <PageContainer> |
80 | | - <TopBar systemOrSilo="silo" /> |
81 | | - <Sidebar> |
82 | | - <Sidebar.Nav> |
83 | | - <NavLinkItem to={pb.projects()} end> |
84 | | - <Folder16Icon /> |
85 | | - Projects |
86 | | - </NavLinkItem> |
87 | | - <DocsLinkItem /> |
88 | | - </Sidebar.Nav> |
89 | | - <Divider /> |
90 | | - <Sidebar.Nav heading={project.name}> |
91 | | - <NavLinkItem to={pb.instances(projectSelector)}> |
92 | | - <Instances16Icon /> Instances |
93 | | - </NavLinkItem> |
94 | | - <NavLinkItem to={pb.disks(projectSelector)}> |
95 | | - <Storage16Icon /> Disks |
96 | | - </NavLinkItem> |
97 | | - <NavLinkItem to={pb.snapshots(projectSelector)}> |
98 | | - <Snapshots16Icon /> Snapshots |
99 | | - </NavLinkItem> |
100 | | - <NavLinkItem to={pb.projectImages(projectSelector)}> |
101 | | - <Images16Icon title="images" /> Images |
102 | | - </NavLinkItem> |
103 | | - <NavLinkItem to={pb.vpcs(projectSelector)}> |
104 | | - <Networking16Icon /> VPCs |
105 | | - </NavLinkItem> |
106 | | - <NavLinkItem to={pb.floatingIps(projectSelector)}> |
107 | | - <IpGlobal16Icon /> Floating IPs |
108 | | - </NavLinkItem> |
109 | | - <NavLinkItem to={pb.projectAccess(projectSelector)}> |
110 | | - <Access16Icon title="Access" /> Access |
111 | | - </NavLinkItem> |
112 | | - </Sidebar.Nav> |
113 | | - </Sidebar> |
114 | | - {overrideContentPane || <ContentPane />} |
115 | | - </PageContainer> |
116 | | - ) |
| 19 | +export default function ProjectLayout() { |
| 20 | + return <ProjectLayoutBase /> |
117 | 21 | } |
0 commit comments