Skip to content

Commit 1f7cea5

Browse files
committed
StatsHouse UI: feature widget system
1 parent 70fef9a commit 1f7cea5

File tree

127 files changed

+2437
-2297
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+2437
-2297
lines changed

statshouse-ui/package-lock.json

Lines changed: 53 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

statshouse-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@swc/core": "^1.10.7",
2222
"@swc/jest": "^0.2.37",
2323
"@tanstack/react-query": "^5.63.0",
24+
"@tanstack/react-query-devtools": "^5.67.3",
2425
"@testing-library/jest-dom": "^6.6.3",
2526
"@testing-library/react": "^16.1.0",
2627
"@testing-library/user-event": "^14.5.2",

statshouse-ui/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { queryClient } from './common/queryClient';
1212
import { useStatsHouse } from '@/store2';
1313
import View2Page from './view2/ViewPage';
1414
import Core from './view2/Core';
15+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
1516

1617
const FAQ = React.lazy(() => import('./doc/FAQ'));
1718
const Admin = React.lazy(() => import('./admin/Admin'));
@@ -26,6 +27,7 @@ export function App() {
2627
const isAdmin = useStatsHouse((s) => s.user.admin);
2728
return (
2829
<QueryClientProvider client={queryClient}>
30+
{process.env.NODE_ENV !== 'production' && <ReactQueryDevtools client={queryClient} />}
2931
<Routes>
3032
<Route path="/" element={<Core />}>
3133
<Route path="" element={<Navigate to="view" replace={true} />} />

statshouse-ui/src/admin/Admin.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// License, v. 2.0. If a copy of the MPL was not distributed with this
55
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
66

7-
import * as React from 'react';
87
import { FormPage } from './pages/FormPage';
98
import { CreatePage } from './pages/CreatePage';
109
import { Route, Routes } from 'react-router-dom';

statshouse-ui/src/admin/pages/CreatePage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ function useSubmitCreate(name: string) {
9595
resolution: 1,
9696
withPercentiles: false,
9797
visible: true,
98+
disable: false,
9899
tags: [
99100
{ name: '', alias: 'environment', customMapping: [] }, // env
100101
...new Array(maxTagsSize - 1).fill({}).map(() => getDefaultTag()),

statshouse-ui/src/admin/storages/MetricFormValues/reducer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const initialValues: IMetric = {
2222
resolution: 1,
2323
withPercentiles: false,
2424
visible: true,
25+
disable: false,
2526
tags: [getDefaultTag()],
2627
tags_draft: [],
2728
tagsSize: 1,

statshouse-ui/src/api/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,6 @@ export class ExtendedError extends Error {
156156
this.status = status;
157157
}
158158
toString() {
159-
return `${this.status}: ${this.message.substring(0, 255)}`;
159+
return `${this.status}: ${this.message ? this.message.substring(0, 255) : 'unknown error'}`;
160160
}
161161
}

statshouse-ui/src/api/badges.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66

77
import { apiFetch, ExtendedError } from './api';
88
import type { ApiQueryGet } from './query';
9-
import type { PlotParams, QueryParams } from '@/url2';
9+
import type { PlotParams, TimeRange, VariableKey, VariableParams } from '@/url2';
1010
import { useLiveModeStore } from '@/store2/liveModeStore';
1111
import { useMemo } from 'react';
12-
import { getLoadPlotUrlParams } from '@/store2/plotDataStore/loadPlotData';
1312
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
13+
import { useStatsHouse } from '@/store2';
14+
import { getLoadPlotUrlParamsLight } from '@/store2/plotDataStore/loadPlotData2';
1415

1516
export const ApiBadgesEndpoint = '/api/badges';
1617

@@ -35,35 +36,36 @@ export async function apiBadgesFetch(params: ApiQueryGet, keyRequest?: unknown)
3536

3637
export function useApiBadges<T = ApiBadges>(
3738
plot: PlotParams,
38-
params: QueryParams,
39+
timeRange: TimeRange,
40+
timeShifts: number[],
41+
variables: Partial<Record<VariableKey, VariableParams>>,
3942
select?: (response?: ApiBadges) => T,
40-
enabled: boolean = true
43+
enabled: boolean = false,
44+
priority: number = 2
4145
): UseQueryResult<T, ExtendedError> {
42-
const interval = useLiveModeStore(({ interval, status }) => (status ? interval : undefined));
43-
44-
const priority = useMemo(() => {
45-
if (plot?.id === params.tabNum) {
46-
return 1;
47-
}
48-
return enabled ? 2 : 3;
49-
}, [enabled, params.tabNum, plot?.id]);
46+
const interval = useLiveModeStore(({ interval, status }) => (status ? interval * 2 : undefined));
5047

5148
const keyParams = useMemo(() => {
5249
if (!plot?.id) {
5350
return null;
5451
}
55-
return getLoadPlotUrlParams(plot?.id, params);
56-
}, [params, plot?.id]);
52+
return getLoadPlotUrlParamsLight(plot, timeRange, timeShifts, variables, interval);
53+
}, [interval, plot, timeRange, timeShifts, variables]);
54+
55+
const plotHeals = useStatsHouse((s) => {
56+
const status = s.plotHeals[plot.id];
57+
return !(!!status && !status.status && status.lastTimestamp + status.timeout * 1000 > Date.now());
58+
});
5759

5860
const fetchParams = useMemo(() => {
5961
if (!plot?.id) {
6062
return null;
6163
}
62-
return getLoadPlotUrlParams(plot?.id, params, interval, false, priority);
63-
}, [interval, params, plot?.id, priority]);
64+
return getLoadPlotUrlParamsLight(plot, timeRange, timeShifts, variables, interval, false, priority);
65+
}, [interval, plot, priority, timeRange, timeShifts, variables]);
6466

6567
return useQuery({
66-
enabled,
68+
enabled: plotHeals && enabled,
6769
select,
6870
queryKey: [ApiBadgesEndpoint, keyParams],
6971
queryFn: async ({ signal }) => {
@@ -78,5 +80,6 @@ export function useApiBadges<T = ApiBadges>(
7880
return response;
7981
},
8082
placeholderData: (previousData) => previousData,
83+
refetchInterval: interval ? interval * 1000 : undefined, //live mode badge x2 interval
8184
});
8285
}

statshouse-ui/src/api/metric.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { GET_PARAMS, type MetricMetaKind, type MetricMetaTagRawKind } from './en
88
import { apiFetch, type ApiFetchResponse, ExtendedError } from './api';
99
import { type UndefinedInitialDataOptions, useQuery, type UseQueryResult } from '@tanstack/react-query';
1010
import { queryClient } from '@/common/queryClient';
11+
import { promQLMetric } from '@/url2';
12+
import { debug } from '@/common/debug';
13+
import { MetricMeta, tagsArrToObject } from '@/store2/metricsMetaStore';
14+
import { useMemo } from 'react';
1115

1216
export const ApiMetricEndpoint = '/api/metric';
1317
/**
@@ -115,14 +119,43 @@ export async function apiMetric<T = ApiMetric>(metricName: string): Promise<ApiF
115119
}
116120
return result;
117121
}
122+
export async function loadMetricMeta(metricName: string) {
123+
if (!metricName || metricName === promQLMetric) {
124+
return null;
125+
}
126+
127+
const { response, error } = await apiMetric(metricName);
128+
129+
if (response && !error) {
130+
debug.log('loading meta for', response.data.metric.name);
131+
const metricMeta: MetricMeta = {
132+
...response.data.metric,
133+
...tagsArrToObject(response.data.metric.tags),
134+
};
135+
return metricMeta;
136+
}
137+
return null;
138+
}
118139

119140
export function useApiMetric<T = ApiMetric>(
120141
metricName: string,
121142
select?: (response?: ApiMetric) => T,
122143
enabled: boolean = true
123144
): UseQueryResult<T, ExtendedError> {
145+
const options = useMemo(() => getMetricOptions(metricName, enabled), [enabled, metricName]);
124146
return useQuery({
125-
...getMetricOptions(metricName, enabled),
147+
...options,
126148
select,
127149
});
128150
}
151+
152+
export function getMetricMeta(metricName: string): MetricMeta | null {
153+
const meta = queryClient.getQueryData<ApiMetric>([ApiMetricEndpoint, metricName])?.data.metric;
154+
if (meta) {
155+
return {
156+
...meta,
157+
...tagsArrToObject(meta.tags),
158+
};
159+
}
160+
return null;
161+
}

0 commit comments

Comments
 (0)