Skip to content

Commit 1a9dac1

Browse files
chore: performance improvements
Signed-off-by: Henry Gressmann <[email protected]>
1 parent 37a08ab commit 1a9dac1

File tree

16 files changed

+415
-354
lines changed

16 files changed

+415
-354
lines changed

Cargo.lock

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

data/licenses-npm.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src/web/routes/event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl EventApi {
5757
let events = events.clone();
5858

5959
// run the event processing in the background
60-
let _ = tokio::task::spawn_blocking(move || {
60+
tokio::task::spawn_blocking(move || {
6161
if let Err(e) = process_event(app, events, event, url, ip, user_agent) {
6262
tracing::error!("Failed to process event: {:?}", e);
6363
}

web/astro.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export default defineConfig({
2626
template: (dependencies) => JSON.stringify(dependencies),
2727
},
2828
},
29-
}),
29+
// biome-ignore lint/suspicious/noExplicitAny: wrong type
30+
}) as any,
3031
],
3132
},
3233
integrations: [react()],

web/bun.lockb

65.8 KB
Binary file not shown.

web/package.json

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"typecheck": "bun run --bun tsc --noEmit"
1010
},
1111
"dependencies": {
12-
"@astrojs/react": "^3.6.3",
12+
"@astrojs/react": "4.0.0-beta.2",
1313
"@explodingcamera/css": "^0.0.4",
1414
"@fontsource-variable/outfit": "^5.1.0",
1515
"@icons-pack/react-simple-icons": "^10.2.0",
@@ -18,37 +18,44 @@
1818
"@radix-ui/react-accordion": "^1.2.1",
1919
"@radix-ui/react-dialog": "^1.1.2",
2020
"@radix-ui/react-tabs": "^1.1.1",
21-
"@tanstack/react-query": "^5.61.3",
22-
"@uidotdev/usehooks": "^2.4.1",
21+
"@tanstack/react-query": "^5.62.0",
22+
"d3-array": "^3.2.4",
23+
"d3-ease": "^3.0.1",
2324
"d3-geo": "^3.1.1",
25+
"d3-scale": "^4.0.2",
2426
"d3-selection": "^3.0.0",
27+
"d3-shape": "^3.2.0",
2528
"d3-zoom": "^3.0.0",
2629
"date-fns": "^4.1.0",
2730
"fets": "^0.8.4",
2831
"fuzzysort": "^3.1.0",
29-
"geojson": "^0.5.0",
3032
"little-date": "^1.0.0",
31-
"lucide-react": "0.461.0",
32-
"react": "^18.3.1",
33-
"react-dom": "^18.3.1",
33+
"lucide-react": "0.462.0",
34+
"react": "19.0.0-rc.1",
35+
"react-dom": "19.0.0-rc.1",
3436
"react-tag-autocomplete": "^7.4.0",
3537
"react-tooltip": "^5.28.0",
3638
"topojson-client": "^3.1.0"
3739
},
3840
"devDependencies": {
3941
"@biomejs/biome": "1.9.4",
40-
"@types/bun": "^1.1.13",
42+
"@million/lint": "^1.0.13",
43+
"@types/bun": "^1.1.14",
44+
"@types/d3-array": "^3.2.1",
45+
"@types/d3-ease": "^3.0.2",
4146
"@types/d3-geo": "^3.1.0",
47+
"@types/d3-scale": "^4.0.8",
4248
"@types/d3-selection": "^3.0.11",
49+
"@types/d3-shape": "^3.1.6",
4350
"@types/d3-zoom": "^3.0.8",
4451
"@types/react": "^18.3.12",
4552
"@types/react-dom": "^18.3.1",
4653
"@types/topojson-client": "^3.1.5",
4754
"@types/topojson-specification": "^1.0.5",
48-
"astro": "^4.16.14",
55+
"astro": "5.0.0-beta.12",
4956
"rollup-plugin-license": "^3.5.3",
5057
"typescript": "^5.7.2"
5158
},
52-
"packageManager": "[email protected].36",
59+
"packageManager": "[email protected].38",
5360
"trustedDependencies": ["@biomejs/biome", "esbuild", "sharp"]
5461
}

web/src/api/dashboard.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

web/src/api/hooks.ts

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useMemo } from "react";
33
import { toDataPoints } from "../components/graph";
44
import type { Dimension, DimensionFilter, DimensionTableRow, Metric, ProjectResponse } from "./types";
55

6-
import { api } from ".";
6+
import { api } from "./client";
77
import { queryClient, useQuery } from "./query";
88
import type { DateRange } from "./ranges";
99

@@ -96,19 +96,20 @@ export const useDimension = ({
9696
.json(),
9797
});
9898

99-
const biggest = useMemo(() => data?.data?.reduce((acc, d) => Math.max(acc, d.value), 0) ?? 0, [data]);
100-
const order = useMemo(() => data?.data?.sort((a, b) => b.value - a.value).map((d) => d.dimensionValue), [data]);
101-
102-
return { data: data?.data, biggest, order, isLoading, error };
99+
return useMemo(() => {
100+
const biggest = data?.data?.reduce((acc, d) => Math.max(acc, d.value), 0) ?? 0;
101+
const order = data?.data?.sort((a, b) => b.value - a.value).map((d) => d.dimensionValue);
102+
return { data: data?.data, biggest, order, isLoading, error };
103+
}, [data, isLoading, error]);
103104
};
104105

105-
export const useProjectData = ({
106-
project,
106+
export const useProjectGraph = ({
107+
projectId,
107108
metric,
108109
range,
109110
filters = [],
110111
}: {
111-
project?: ProjectResponse;
112+
projectId?: string;
112113
metric: Metric;
113114
range: DateRange;
114115
filters?: DimensionFilter[];
@@ -122,61 +123,65 @@ export const useProjectData = ({
122123
const dataPoints = range.getGraphDataPoints();
123124

124125
const {
125-
data: stats,
126-
isError: isErrorStats,
127-
isLoading: isLoadingStats,
126+
data: graph,
127+
isError,
128+
isLoading,
128129
} = useQuery({
129130
refetchInterval,
130131
staleTime,
131-
queryKey: ["project_stats", project?.id, range.cacheKey(), metric, filters],
132-
133-
enabled: project !== undefined,
132+
enabled: projectId !== undefined,
133+
queryKey: ["project_graph", projectId, range.cacheKey(), metric, filters, dataPoints],
134134
queryFn: () =>
135-
api["/api/dashboard/project/{project_id}/stats"]
136-
.post({ json: { range: range.toAPI(), filters }, params: { project_id: project?.id ?? "" } })
137-
.json(),
135+
api["/api/dashboard/project/{project_id}/graph"]
136+
.post({
137+
json: { range: range.toAPI(), metric, dataPoints, filters },
138+
params: { project_id: projectId ?? "" },
139+
})
140+
.json()
141+
.then(({ data }) => toDataPoints(data, range, metric)),
138142
placeholderData: (prev) => prev,
139143
});
140144

145+
return {
146+
graph,
147+
isLoading,
148+
isError,
149+
};
150+
};
151+
152+
export const useProjectStats = ({
153+
projectId,
154+
metric,
155+
range,
156+
filters = [],
157+
}: {
158+
projectId?: string;
159+
metric: Metric;
160+
range: DateRange;
161+
filters?: DimensionFilter[];
162+
}) => {
141163
const {
142-
data: graph,
143-
isError: isErrorGraph,
144-
isLoading: isLoadingGraph,
164+
data: stats,
165+
isError,
166+
isLoading,
145167
} = useQuery({
146-
refetchInterval,
147-
staleTime,
148-
enabled: project !== undefined,
149-
queryKey: ["project_graph", project?.id, range.cacheKey(), metric, filters, dataPoints],
168+
queryKey: ["project_stats", projectId, range.cacheKey(), metric, filters],
169+
170+
enabled: projectId !== undefined,
150171
queryFn: () =>
151-
api["/api/dashboard/project/{project_id}/graph"]
152-
.post({
153-
json: { range: range.toAPI(), metric, dataPoints, filters },
154-
params: { project_id: project?.id ?? "" },
155-
})
172+
api["/api/dashboard/project/{project_id}/stats"]
173+
.post({ json: { range: range.toAPI(), filters }, params: { project_id: projectId ?? "" } })
156174
.json(),
157175
placeholderData: (prev) => prev,
158176
});
159177

160178
return {
161-
stats: {
162-
error: isErrorStats,
163-
loading: isLoadingStats,
164-
data: stats,
165-
},
166-
graph: {
167-
error: isErrorGraph,
168-
loading: isLoadingGraph,
169-
range,
170-
data: graph?.data ? toDataPoints(graph.data, range, metric) : [],
171-
},
172-
isLoading: isLoadingStats || isLoadingGraph,
173-
isError: isErrorStats || isErrorGraph,
179+
stats,
180+
isLoading,
181+
isError,
174182
};
175183
};
176184

177-
export type ProjectDataGraph = ReturnType<typeof useProjectData>["graph"];
178-
export type ProjectDataStats = ReturnType<typeof useProjectData>["stats"];
179-
180185
export const invalidateProjects = () => queryClient.invalidateQueries({ queryKey: ["projects"] });
181186
export const invalidateEntities = () => queryClient.invalidateQueries({ queryKey: ["entities"] });
182187
export const invalidateUsers = () => queryClient.invalidateQueries({ queryKey: ["users"] });

web/src/components/dimensions/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export const DimensionTable = (props: DimensionProps) => {
164164
</div>
165165
)}
166166
</div>
167-
<DetailsModal {...props} />
167+
<DetailsModal dimension={props.dimension} query={props.query} />
168168
</>
169169
);
170170
};
@@ -174,7 +174,7 @@ const DimensionValueButton = ({
174174
onSelect,
175175
}: {
176176
children: React.ReactNode;
177-
onSelect: () => void;
177+
onSelect?: () => void;
178178
}) => (
179179
<button type="button" className={styles.dimensionItemSelect} onClick={onSelect}>
180180
{children}
@@ -307,8 +307,8 @@ export const DimensionLabel = ({
307307
dimension,
308308
value,
309309
onSelect,
310-
}: { dimension: Dimension; value: DimensionTableRow; onSelect: (value: DimensionTableRow) => void }) =>
311-
dimensionLabels[dimension](value, () => onSelect(value));
310+
}: { dimension: Dimension; value: DimensionTableRow; onSelect?: (value: DimensionTableRow) => void }) =>
311+
dimensionLabels[dimension](value, () => onSelect?.(value));
312312

313313
export const DimensionValueBar = ({
314314
value,

web/src/components/dimensions/modal.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@ import type { ProjectQuery } from "../project";
1212
export const DetailsModal = ({
1313
dimension,
1414
query,
15-
onSelect,
1615
}: {
1716
dimension: Dimension;
1817
query: ProjectQuery;
19-
onSelect: (value: DimensionTableRow, dimension: Dimension) => void;
2018
}) => {
2119
const { data, biggest, order, isLoading } = useDimension({ dimension, ...query });
2220

@@ -64,7 +62,7 @@ export const DetailsModal = ({
6462
className={styles.dimensionRow}
6563
>
6664
<DimensionValueBar value={d.value} biggest={biggest}>
67-
<DimensionLabel dimension={dimension} value={d} onSelect={() => onSelect(d, dimension)} />
65+
<DimensionLabel dimension={dimension} value={d} />
6866
</DimensionValueBar>
6967
<div>{formatMetricVal(d.value, query.metric)}</div>
7068
</div>

0 commit comments

Comments
 (0)