Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/layouts/helpers.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Outlet } from 'react-router-dom'

import { apiQueryClient } from '@oxide/api'
import { Pagination } from '@oxide/pagination'
import { SkipLinkTarget } from '@oxide/ui'
import { classed } from '@oxide/util'
Expand Down Expand Up @@ -27,3 +28,7 @@ export function ContentPane({ children }: { children?: React.ReactNode }) {
</div>
)
}

export async function prefetchSessionMe() {
await apiQueryClient.prefetchQuery('sessionMe', {})
}
2 changes: 1 addition & 1 deletion app/pages/OrgAccessPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ OrgAccessPage.loader = async ({ params }: LoaderFunctionArgs) => {
apiQueryClient.prefetchQuery('policyView', {}),
apiQueryClient.prefetchQuery('organizationPolicyView', requireOrgParams(params)),
// used to resolve user names
apiQueryClient.prefetchQuery('userList'),
apiQueryClient.prefetchQuery('userList', {}),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The {} is now required as a result of removing the ? from the params arg. This ensures the call matches the one it's prefetching for — if they don't match, then the call in the component will not reuse this data.

])
}

Expand Down
2 changes: 1 addition & 1 deletion app/pages/project/access/ProjectAccessPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ ProjectAccessPage.loader = async ({ params }: LoaderFunctionArgs) => {
apiQueryClient.prefetchQuery('organizationPolicyView', { orgName }),
apiQueryClient.prefetchQuery('projectPolicyView', { orgName, projectName }),
// used to resolve user names
apiQueryClient.prefetchQuery('userList'),
apiQueryClient.prefetchQuery('userList', {}),
])
}

Expand Down
240 changes: 122 additions & 118 deletions app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import RootLayout from './layouts/RootLayout'
import SettingsLayout from './layouts/SettingsLayout'
import SiloLayout from './layouts/SiloLayout'
import SystemLayout from './layouts/SystemLayout'
import { prefetchSessionMe } from './layouts/helpers'
import DeviceAuthSuccessPage from './pages/DeviceAuthSuccessPage'
import DeviceAuthVerifyPage from './pages/DeviceAuthVerifyPage'
import LoginPage from './pages/LoginPage'
Expand Down Expand Up @@ -54,152 +55,155 @@ export const routes = createRoutesFromElements(
<Route path="success" element={<DeviceAuthSuccessPage />} />
</Route>

<Route path="settings" handle={{ crumb: 'settings' }} element={<SettingsLayout />}>
<Route index element={<Navigate to="profile" replace />} />
<Route path="profile" element={<ProfilePage />} handle={{ crumb: 'Profile' }} />
<Route
path="appearance"
element={<AppearancePage />}
handle={{ crumb: 'Appearance' }}
/>
<Route
path="ssh-keys"
element={<SSHKeysPage />}
loader={SSHKeysPage.loader}
handle={{ crumb: 'SSH Keys' }}
/>
<Route path="hotkeys" element={<HotkeysPage />} handle={{ crumb: 'Hotkeys' }} />
</Route>
{/* This wraps all routes that are supposed to be authenticated */}
<Route loader={prefetchSessionMe}>
<Route path="settings" handle={{ crumb: 'settings' }} element={<SettingsLayout />}>
<Route index element={<Navigate to="profile" replace />} />
<Route path="profile" element={<ProfilePage />} handle={{ crumb: 'Profile' }} />
<Route
path="appearance"
element={<AppearancePage />}
handle={{ crumb: 'Appearance' }}
/>
<Route
path="ssh-keys"
element={<SSHKeysPage />}
loader={SSHKeysPage.loader}
handle={{ crumb: 'SSH Keys' }}
/>
<Route path="hotkeys" element={<HotkeysPage />} handle={{ crumb: 'Hotkeys' }} />
</Route>

<Route index element={<Navigate to="/orgs" replace />} />
<Route index element={<Navigate to="/orgs" replace />} />

<Route path="system" element={<SystemLayout />}>
<Route index element={null} />
<Route path="issues" element={null} />
<Route path="utilization" element={null} />
<Route path="inventory" element={null} />
<Route path="health" element={null} />
<Route path="update" element={null} />
<Route path="networking" element={null} />
<Route path="settings" element={null} />
</Route>
<Route path="system" element={<SystemLayout />}>
<Route index element={null} />
<Route path="issues" element={null} />
<Route path="utilization" element={null} />
<Route path="inventory" element={null} />
<Route path="health" element={null} />
<Route path="update" element={null} />
<Route path="networking" element={null} />
<Route path="settings" element={null} />
</Route>

{/* These are done here instead of nested so we don't flash a layout on 404s */}
<Route path="/orgs/:orgName" element={<Navigate to="projects" replace />} />
<Route
path="/orgs/:orgName/projects/:projectName"
element={<Navigate to="instances" replace />}
/>
{/* These are done here instead of nested so we don't flash a layout on 404s */}
<Route path="/orgs/:orgName" element={<Navigate to="projects" replace />} />
<Route
path="/orgs/:orgName/projects/:projectName"
element={<Navigate to="instances" replace />}
/>

<Route path="orgs" errorElement={<RouterDataErrorBoundary />}>
<Route element={<SiloLayout />}>
<Route index element={<OrgsPage />} loader={OrgsPage.loader} />
<Route
path="new"
element={<OrgsPage modal="createOrg" />}
loader={OrgsPage.loader}
/>
<Route path="edit">
<Route path="orgs" errorElement={<RouterDataErrorBoundary />}>
<Route element={<SiloLayout />}>
<Route index element={<OrgsPage />} loader={OrgsPage.loader} />
<Route
path=":orgName"
element={<OrgsPage modal="editOrg" />}
path="new"
element={<OrgsPage modal="createOrg" />}
loader={OrgsPage.loader}
/>
<Route path="edit">
<Route
path=":orgName"
element={<OrgsPage modal="editOrg" />}
loader={OrgsPage.loader}
/>
</Route>
</Route>
</Route>

<Route path=":orgName" handle={{ crumb: orgCrumb }}>
<Route element={<OrgLayout />}>
<Route
path="access"
element={<OrgAccessPage />}
loader={OrgAccessPage.loader}
handle={{ crumb: 'Access & IAM' }}
/>
</Route>
<Route path="projects" handle={{ crumb: 'Projects' }}>
{/* ORG */}
<Route path=":orgName" handle={{ crumb: orgCrumb }}>
<Route element={<OrgLayout />}>
<Route index element={<ProjectsPage />} loader={ProjectsPage.loader} />
<Route
path="new"
element={<ProjectsPage modal="createProject" />}
loader={ProjectsPage.loader}
path="access"
element={<OrgAccessPage />}
loader={OrgAccessPage.loader}
handle={{ crumb: 'Access & IAM' }}
/>
<Route path="edit">
</Route>
<Route path="projects" handle={{ crumb: 'Projects' }}>
{/* ORG */}
<Route element={<OrgLayout />}>
<Route index element={<ProjectsPage />} loader={ProjectsPage.loader} />
<Route
path=":projectName"
element={<ProjectsPage modal="editProject" />}
path="new"
element={<ProjectsPage modal="createProject" />}
loader={ProjectsPage.loader}
/>
</Route>
</Route>

{/* PROJECT */}
<Route
path=":projectName"
element={<ProjectLayout />}
handle={{ crumb: projectCrumb }}
>
<Route path="instances" handle={{ crumb: 'Instances' }}>
<Route index element={<InstancesPage />} loader={InstancesPage.loader} />
<Route path="new" element={<FormPage Form={InstanceCreateForm} />} />
<Route path=":instanceName" handle={{ crumb: instanceCrumb }}>
<Route index element={<InstancePage />} loader={InstancePage.loader} />
<Route path="edit">
<Route
path="serial-console"
element={<SerialConsolePage />}
handle={{ crumb: 'serial-console' }}
path=":projectName"
element={<ProjectsPage modal="editProject" />}
loader={ProjectsPage.loader}
/>
</Route>
</Route>
<Route path="vpcs" handle={{ crumb: 'VPCs' }}>
<Route index element={<VpcsPage />} loader={VpcsPage.loader} />
<Route
path="new"
element={<VpcsPage modal="createVpc" />}
loader={VpcsPage.loader}
/>
<Route path="edit">

{/* PROJECT */}
<Route
path=":projectName"
element={<ProjectLayout />}
handle={{ crumb: projectCrumb }}
>
<Route path="instances" handle={{ crumb: 'Instances' }}>
<Route index element={<InstancesPage />} loader={InstancesPage.loader} />
<Route path="new" element={<FormPage Form={InstanceCreateForm} />} />
<Route path=":instanceName" handle={{ crumb: instanceCrumb }}>
<Route index element={<InstancePage />} loader={InstancePage.loader} />
<Route
path="serial-console"
element={<SerialConsolePage />}
handle={{ crumb: 'serial-console' }}
/>
</Route>
</Route>
<Route path="vpcs" handle={{ crumb: 'VPCs' }}>
<Route index element={<VpcsPage />} loader={VpcsPage.loader} />
<Route
path=":vpcName"
element={<VpcsPage modal="editVpc" />}
path="new"
element={<VpcsPage modal="createVpc" />}
loader={VpcsPage.loader}
/>
<Route path="edit">
<Route
path=":vpcName"
element={<VpcsPage modal="editVpc" />}
loader={VpcsPage.loader}
/>
</Route>
<Route
path=":vpcName"
element={<VpcPage />}
loader={VpcPage.loader}
handle={{ crumb: vpcCrumb }}
/>
</Route>
<Route path="disks" handle={{ crumb: 'Disks' }}>
<Route index element={<DisksPage />} loader={DisksPage.loader} />
<Route
path="new"
element={<DisksPage modal="createDisk" />}
loader={DisksPage.loader}
/>
</Route>
<Route
path=":vpcName"
element={<VpcPage />}
loader={VpcPage.loader}
handle={{ crumb: vpcCrumb }}
path="snapshots"
element={<SnapshotsPage />}
loader={SnapshotsPage.loader}
handle={{ crumb: 'Snapshots' }}
/>
</Route>
<Route path="disks" handle={{ crumb: 'Disks' }}>
<Route index element={<DisksPage />} loader={DisksPage.loader} />
<Route
path="new"
element={<DisksPage modal="createDisk" />}
loader={DisksPage.loader}
path="images"
element={<ImagesPage />}
loader={ImagesPage.loader}
handle={{ crumb: 'Images' }}
/>
<Route
path="access"
element={<ProjectAccessPage />}
loader={ProjectAccessPage.loader}
handle={{ crumb: 'Access & IAM' }}
/>
</Route>
<Route
path="snapshots"
element={<SnapshotsPage />}
loader={SnapshotsPage.loader}
handle={{ crumb: 'Snapshots' }}
/>
<Route
path="images"
element={<ImagesPage />}
loader={ImagesPage.loader}
handle={{ crumb: 'Images' }}
/>
<Route
path="access"
element={<ProjectAccessPage />}
loader={ProjectAccessPage.loader}
handle={{ crumb: 'Access & IAM' }}
/>
</Route>
</Route>
</Route>
Expand Down
4 changes: 2 additions & 2 deletions libs/api/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const wrapQueryClient = <A extends ApiClient>(api: A, queryClient: QueryC
) => queryClient.refetchQueries(params ? [method, params] : [method], filters),
fetchQuery: <M extends string & keyof A>(
method: M,
params?: Params<A[M]>,
params: Params<A[M]>,
options: FetchQueryOptions<Result<A[M]>, ErrorResult> = {}
) =>
queryClient.fetchQuery({
Expand All @@ -120,7 +120,7 @@ export const wrapQueryClient = <A extends ApiClient>(api: A, queryClient: QueryC
}),
prefetchQuery: <M extends string & keyof A>(
method: M,
params?: Params<A[M]>,
params: Params<A[M]>,
options: FetchQueryOptions<Result<A[M]>, ErrorResult> = {}
) =>
queryClient.prefetchQuery({
Expand Down