Skip to content

Commit 2f38adc

Browse files
committed
use lazy imports in the routes
1 parent f3207fb commit 2f38adc

File tree

11 files changed

+126
-111
lines changed

11 files changed

+126
-111
lines changed

app/components/SystemMetric.tsx

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8-
import React, { Suspense, useMemo, useRef } from 'react'
8+
import { useMemo, useRef } from 'react'
99

1010
import {
1111
synthesizeData,
@@ -16,7 +16,7 @@ import {
1616

1717
import { Spinner } from '~/ui/lib/Spinner'
1818

19-
const TimeSeriesChart = React.lazy(() => import('./TimeSeriesChart'))
19+
import { TimeSeriesChart } from './TimeSeriesChart'
2020

2121
// The difference between system metric and silo metric is
2222
// 1. different endpoints
@@ -99,20 +99,18 @@ export function SiloMetric({
9999
{(inRange.isPending || beforeStart.isPending) && <Spinner />}
100100
</h2>
101101
{/* TODO: proper skeleton for empty chart */}
102-
<Suspense fallback={<div />}>
103-
<div className="mt-3 h-[300px]">
104-
<TimeSeriesChart
105-
data={data}
106-
title={title}
107-
width={480}
108-
height={240}
109-
interpolation="stepAfter"
110-
startTime={startTime}
111-
endTime={endTime}
112-
unit={unit !== 'count' ? unit : undefined}
113-
/>
114-
</div>
115-
</Suspense>
102+
<div className="mt-3 h-[300px]">
103+
<TimeSeriesChart
104+
data={data}
105+
title={title}
106+
width={480}
107+
height={240}
108+
interpolation="stepAfter"
109+
startTime={startTime}
110+
endTime={endTime}
111+
unit={unit !== 'count' ? unit : undefined}
112+
/>
113+
</div>
116114
</div>
117115
)
118116
}
@@ -177,20 +175,18 @@ export function SystemMetric({
177175
{(inRange.isPending || beforeStart.isPending) && <Spinner />}
178176
</h2>
179177
{/* TODO: proper skeleton for empty chart */}
180-
<Suspense fallback={<div />}>
181-
<div className="mt-3 h-[300px]">
182-
<TimeSeriesChart
183-
data={data}
184-
title={title}
185-
width={480}
186-
height={240}
187-
interpolation="stepAfter"
188-
startTime={startTime}
189-
endTime={endTime}
190-
unit={unit !== 'count' ? unit : undefined}
191-
/>
192-
</div>
193-
</Suspense>
178+
<div className="mt-3 h-[300px]">
179+
<TimeSeriesChart
180+
data={data}
181+
title={title}
182+
width={480}
183+
height={240}
184+
interpolation="stepAfter"
185+
startTime={startTime}
186+
endTime={endTime}
187+
unit={unit !== 'count' ? unit : undefined}
188+
/>
189+
</div>
194190
</div>
195191
)
196192
}

app/components/TimeSeriesChart.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,7 @@ const SkeletonMetric = ({
165165
</div>
166166
)
167167

168-
// default export is most convenient for dynamic import
169-
// eslint-disable-next-line import/no-default-export
170-
export default function TimeSeriesChart({
168+
export function TimeSeriesChart({
171169
className,
172170
data: rawData,
173171
title,

app/components/oxql-metrics/OxqlMetric.tsx

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,20 @@
1111
* https://github.com/oxidecomputer/omicron/tree/main/oximeter/oximeter/schema
1212
*/
1313

14-
import {
15-
Children,
16-
lazy,
17-
Suspense,
18-
useEffect,
19-
useMemo,
20-
useState,
21-
type ReactNode,
22-
} from 'react'
14+
import { Children, useEffect, useMemo, useState, type ReactNode } from 'react'
2315
import type { LoaderFunctionArgs } from 'react-router'
2416

2517
import { apiQueryClient, useApiQuery } from '@oxide/api'
2618

2719
import { CopyCodeModal } from '~/components/CopyCode'
2820
import { MoreActionsMenu } from '~/components/MoreActionsMenu'
2921
import { getInstanceSelector } from '~/hooks/use-params'
30-
import { useMetricsContext } from '~/pages/project/instances/instance/tabs/MetricsTab'
22+
import { useMetricsContext } from '~/pages/project/instances/instance/tabs/common'
3123
import { LearnMore } from '~/ui/lib/SettingsGroup'
3224
import { classed } from '~/util/classed'
3325
import { links } from '~/util/links'
3426

27+
import { TimeSeriesChart } from '../TimeSeriesChart'
3528
import { HighlightedOxqlQuery, toOxqlStr } from './HighlightedOxqlQuery'
3629
import {
3730
composeOxqlData,
@@ -42,8 +35,6 @@ import {
4235
type OxqlQuery,
4336
} from './util'
4437

45-
const TimeSeriesChart = lazy(() => import('~/components/TimeSeriesChart'))
46-
4738
export async function loader({ params }: LoaderFunctionArgs) {
4839
const { project, instance } = getInstanceSelector(params)
4940
await apiQueryClient.prefetchQuery('instanceView', {
@@ -124,20 +115,18 @@ export function OxqlMetric({ title, description, ...queryObj }: OxqlMetricProps)
124115
</CopyCodeModal>
125116
</div>
126117
<div className="px-6 py-5 pt-8">
127-
<Suspense fallback={<div className="h-[300px]" />}>
128-
<TimeSeriesChart
129-
title={title}
130-
startTime={startTime}
131-
endTime={endTime}
132-
unit={unitForSet}
133-
data={data}
134-
yAxisTickFormatter={yAxisTickFormatter}
135-
width={480}
136-
height={240}
137-
hasBorder={false}
138-
hasError={!!error}
139-
/>
140-
</Suspense>
118+
<TimeSeriesChart
119+
title={title}
120+
startTime={startTime}
121+
endTime={endTime}
122+
unit={unitForSet}
123+
data={data}
124+
yAxisTickFormatter={yAxisTickFormatter}
125+
width={480}
126+
height={240}
127+
hasBorder={false}
128+
hasError={!!error}
129+
/>
141130
</div>
142131
</div>
143132
)

app/pages/SiloUtilizationPage.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@ import { bytesToGiB, bytesToTiB } from '~/util/units'
2626

2727
const toListboxItem = (x: { name: string; id: string }) => ({ label: x.name, value: x.id })
2828

29-
export async function loader() {
29+
export const handle = { crumb: 'Utilization' }
30+
31+
export async function clientLoader() {
3032
await Promise.all([
3133
apiQueryClient.prefetchQuery('projectList', {}),
3234
apiQueryClient.prefetchQuery('utilizationView', {}),
3335
])
3436
return null
3537
}
3638

37-
Component.displayName = 'SiloUtilizationPage'
38-
export function Component() {
39+
export default function SiloUtilizationPage() {
3940
const { me } = useCurrentUser()
4041

4142
const siloId = me.siloId

app/pages/project/instances/instance/tabs/MetricsTab.tsx

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,19 @@
77
*/
88

99
import { useIsFetching } from '@tanstack/react-query'
10-
import { createContext, useContext, useState, type ReactNode } from 'react'
10+
import { useState } from 'react'
1111

1212
import { useDateTimeRangePicker } from '~/components/form/fields/DateTimeRangePicker'
1313
import { useIntervalPicker } from '~/components/RefetchIntervalPicker'
1414
import { RouteTabs, Tab } from '~/components/RouteTabs'
1515
import { useInstanceSelector } from '~/hooks/use-params'
16-
import { invariant } from '~/util/invariant'
1716
import { pb } from '~/util/path-builder'
1817

19-
type MetricsContextValue = {
20-
startTime: Date
21-
endTime: Date
22-
dateTimeRangePicker: ReactNode
23-
intervalPicker: ReactNode
24-
setIsIntervalPickerEnabled: (enabled: boolean) => void
25-
}
26-
27-
/**
28-
* Using context lets the selected time window persist across route tab navs.
29-
*/
30-
const MetricsContext = createContext<MetricsContextValue | null>(null)
18+
import { MetricsContext } from './common'
3119

32-
// this lets us init with a null value but rule it out in the consumers
33-
export function useMetricsContext() {
34-
const value = useContext(MetricsContext)
35-
invariant(value, 'useMetricsContext can only be called inside a MetricsContext')
36-
return value
37-
}
20+
export const handle = { crumb: 'Metrics' }
3821

39-
export const MetricsTab = () => {
22+
export default function MetricsTab() {
4023
const { project, instance } = useInstanceSelector()
4124
// this ensures the interval picker (which defaults to reloading every 10s) only kicks in
4225
// once some initial data have loaded, to prevent requests from stacking up

app/pages/project/instances/instance/tabs/MetricsTab/CpuMetricsTab.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ import {
1919
import { useInstanceSelector } from '~/hooks/use-params'
2020
import { Listbox } from '~/ui/lib/Listbox'
2121

22-
import { useMetricsContext } from '../MetricsTab'
22+
import { useMetricsContext } from '../common'
2323

24-
Component.displayName = 'CpuMetricsTab'
25-
export function Component() {
24+
export const handle = { crumb: 'CPU' }
25+
26+
export default function CpuMetricsTab() {
2627
const { project, instance } = useInstanceSelector()
2728
const { data: instanceData } = usePrefetchedApiQuery('instanceView', {
2829
path: { instance },

app/pages/project/instances/instance/tabs/MetricsTab/DiskMetricsTab.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import { EmptyMessage } from '~/ui/lib/EmptyMessage'
2424
import { Listbox } from '~/ui/lib/Listbox'
2525
import { TableEmptyBox } from '~/ui/lib/Table'
2626

27-
import { useMetricsContext } from '../MetricsTab'
27+
import { useMetricsContext } from '../common'
2828

29-
export async function loader({ params }: LoaderFunctionArgs) {
29+
export async function clientLoader({ params }: LoaderFunctionArgs) {
3030
const { project, instance } = getInstanceSelector(params)
3131
await apiQueryClient.prefetchQuery('instanceDiskList', {
3232
path: { instance },
@@ -38,8 +38,9 @@ export async function loader({ params }: LoaderFunctionArgs) {
3838
// out here so we don't have to memoize it
3939
const groupByAttachedInstanceId = { cols: ['attached_instance_id'], op: 'sum' } as const
4040

41-
Component.displayName = 'DiskMetricsTab'
42-
export function Component() {
41+
export const handle = { crumb: 'Disk' }
42+
43+
export default function DiskMetricsTab() {
4344
const { project, instance } = useInstanceSelector()
4445
const { data: disks } = usePrefetchedApiQuery('instanceDiskList', {
4546
path: { instance },

app/pages/project/instances/instance/tabs/MetricsTab/NetworkMetricsTab.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ import { Listbox } from '~/ui/lib/Listbox'
2828
import { TableEmptyBox } from '~/ui/lib/Table'
2929
import { ALL_ISH } from '~/util/consts'
3030

31-
import { useMetricsContext } from '../MetricsTab'
31+
import { useMetricsContext } from '../common'
3232

33-
export async function loader({ params }: LoaderFunctionArgs) {
33+
export async function clientLoader({ params }: LoaderFunctionArgs) {
3434
const { project, instance } = getInstanceSelector(params)
3535
await apiQueryClient.prefetchQuery('instanceNetworkInterfaceList', {
3636
query: { project, instance, limit: ALL_ISH },
@@ -40,8 +40,9 @@ export async function loader({ params }: LoaderFunctionArgs) {
4040

4141
const groupByInstanceId = { cols: ['instance_id'], op: 'sum' } as const
4242

43-
Component.displayName = 'NetworkMetricsTab'
44-
export function Component() {
43+
export const handle = { crumb: 'Network' }
44+
45+
export default function NetworkMetricsTab() {
4546
const { project, instance } = useInstanceSelector()
4647
const { data: nics } = usePrefetchedApiQuery('instanceNetworkInterfaceList', {
4748
query: { project, instance, limit: ALL_ISH },

app/pages/project/instances/instance/tabs/common.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8+
import { createContext, ReactNode, useContext } from 'react'
9+
810
import { intersperse } from '~/util/array'
11+
import { invariant } from '~/util/invariant'
912

1013
const white = (s: string) => (
1114
<span key={s} className="text-raise">
@@ -15,3 +18,23 @@ const white = (s: string) => (
1518

1619
export const fancifyStates = (states: string[]) =>
1720
intersperse(states.map(white), <>, </>, <> or </>)
21+
22+
type MetricsContextValue = {
23+
startTime: Date
24+
endTime: Date
25+
dateTimeRangePicker: ReactNode
26+
intervalPicker: ReactNode
27+
setIsIntervalPickerEnabled: (enabled: boolean) => void
28+
}
29+
30+
/**
31+
* Using context lets the selected time window persist across route tab navs.
32+
*/
33+
export const MetricsContext = createContext<MetricsContextValue | null>(null)
34+
35+
// this lets us init with a null value but rule it out in the consumers
36+
export function useMetricsContext() {
37+
const value = useContext(MetricsContext)
38+
invariant(value, 'useMetricsContext can only be called inside a MetricsContext')
39+
return value
40+
}

app/pages/system/UtilizationPage.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,17 @@ const siloUtilList = getListQFn('siloUtilizationList', {
4444
query: { limit: ALL_ISH },
4545
})
4646

47-
export async function loader() {
47+
export async function clientLoader() {
4848
await Promise.all([
4949
queryClient.prefetchQuery(siloList.optionsFn()),
5050
queryClient.prefetchQuery(siloUtilList.optionsFn()),
5151
])
5252
return null
5353
}
5454

55-
Component.displayName = 'SystemUtilizationPage'
56-
export function Component() {
55+
export const handle = { crumb: 'Utilization' }
56+
57+
export default function SystemUtilizationPage() {
5758
const { data: siloUtilizationList } = usePrefetchedQuery(siloUtilList.optionsFn())
5859

5960
const { totalAllocated, totalProvisioned } = totalUtilization(siloUtilizationList.items)

0 commit comments

Comments
 (0)