Skip to content

Commit 7b9e226

Browse files
feat: filter in dimension modal
Signed-off-by: Henry Gressmann <[email protected]>
1 parent 3c1609f commit 7b9e226

File tree

10 files changed

+120
-79
lines changed

10 files changed

+120
-79
lines changed

Cargo.lock

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

web/bun.lockb

368 Bytes
Binary file not shown.

web/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
"@radix-ui/react-dialog": "^1.1.1",
1919
"@radix-ui/react-tabs": "^1.1.0",
2020
"@scaleway/use-query-params": "^5.0.5",
21-
"@tanstack/react-query": "^5.54.1",
21+
"@tanstack/react-query": "^5.55.0",
2222
"@uidotdev/usehooks": "^2.4.1",
2323
"date-fns": "^3.6.0",
2424
"fets": "^0.8.3",
25+
"fuzzysort": "^3.0.2",
2526
"lightningcss": "^1.26.0",
26-
"lucide-react": "^0.438.0",
27+
"lucide-react": "^0.439.0",
2728
"react": "^18.3.1",
2829
"react-dom": "^18.3.1",
2930
"react-simple-maps": "^3.0.0",
@@ -35,7 +36,7 @@
3536
"@types/react": "^18.3.5",
3637
"@types/react-dom": "^18.3.0",
3738
"@types/react-simple-maps": "^3.0.6",
38-
"astro": "^4.15.3",
39+
"astro": "^4.15.4",
3940
"bun-types": "^1.1.26",
4041
"rollup-plugin-license": "^3.5.2",
4142
"typescript": "^5.5.4"

web/src/api/ranges.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { DateRange } from ".";
2-
import type { GraphRange } from "../components/graph";
32

43
import {
54
addHours,
@@ -14,6 +13,7 @@ import {
1413
subDays,
1514
subMonths,
1615
} from "date-fns";
16+
import type { GraphRange } from "../components/graph/graph";
1717

1818
export const rangeNames = {
1919
today: "Today",

web/src/components/dimensions/dimensions.module.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@
116116
margin-bottom: 0.2rem;
117117
}
118118

119+
.external {
120+
display: flex;
121+
align-items: center;
122+
}
123+
119124
.dimensionEmpty {
120125
flex: 1;
121126
display: flex;

web/src/components/dimensions/index.tsx

Lines changed: 5 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as Tabs from "@radix-ui/react-tabs";
2-
import { FullscreenIcon, LinkIcon, SquareArrowOutUpRightIcon, ZoomIn } from "lucide-react";
2+
import { LinkIcon, SquareArrowOutUpRightIcon, ZoomIn } from "lucide-react";
33
import styles from "./dimensions.module.css";
44

55
import {
@@ -14,10 +14,9 @@ import {
1414
useDimension,
1515
} from "../../api";
1616

17-
import { cls } from "../../utils";
18-
import { Dialog } from "../dialog";
1917
import { BrowserIcon, MobileDeviceIcon, OSIcon, ReferrerIcon } from "../icons";
2018
import { countryCodeToFlag, formatFullUrl, formatHost, getHref, tryParseUrl } from "./utils";
19+
import { DetailsModal } from "./modal";
2120

2221
export const cardStyles = styles.card;
2322

@@ -120,67 +119,6 @@ export const DimensionTable = ({
120119
);
121120
};
122121

123-
const DetailsModal = ({
124-
project,
125-
dimension,
126-
metric,
127-
range,
128-
}: { project: ProjectResponse; dimension: Dimension; metric: Metric; range: DateRange }) => {
129-
const { data, biggest, order, isLoading } = useDimension({ project, dimension, metric, range });
130-
131-
return (
132-
<Dialog
133-
title={`${dimensionNames[dimension]} - ${metricNames[metric]}`}
134-
description={`Detailed breakdown of ${dimensionNames[dimension]} by ${metricNames[metric]}`}
135-
hideTitle
136-
hideDescription
137-
showClose
138-
className={styles.detailsModal}
139-
trigger={() => (
140-
<button
141-
type="button"
142-
className={cls(styles.showMore, (data?.length ?? 0) === 0 && styles.showMoreHidden)}
143-
onClick={() => console.log("show more")}
144-
>
145-
<ZoomIn size={16} />
146-
Show details
147-
</button>
148-
)}
149-
>
150-
<div className={styles.dimensionTable} style={{ "--count": data?.length } as React.CSSProperties}>
151-
<div className={styles.dimensionHeader}>
152-
<div>{dimensionNames[dimension]}</div>
153-
<div>{metricNames[metric]}</div>
154-
</div>
155-
{data?.map((d) => {
156-
return (
157-
<div
158-
key={d.dimensionValue}
159-
style={{ order: order?.indexOf(d.dimensionValue) }}
160-
className={styles.dimensionRow}
161-
>
162-
<DimensionValueBar value={d.value} biggest={biggest}>
163-
<DimensionLabel dimension={dimension} value={d} />
164-
</DimensionValueBar>
165-
<div>{formatMetricVal(metric, d.value)}</div>
166-
</div>
167-
);
168-
})}
169-
{isLoading && data?.length === 0 && (
170-
<div className={styles.dimensionEmpty}>
171-
<div>Loading...</div>
172-
</div>
173-
)}
174-
{!isLoading && data?.length === 0 && (
175-
<div className={styles.dimensionEmpty}>
176-
<div>No data available</div>
177-
</div>
178-
)}
179-
</div>
180-
</Dialog>
181-
);
182-
};
183-
184122
const dimensionLabels: Record<Dimension, (value: DimensionTableRow) => React.ReactNode> = {
185123
platform: (value) => (
186124
<>
@@ -250,7 +188,7 @@ const dimensionLabels: Record<Dimension, (value: DimensionTableRow) => React.Rea
250188
{value.dimensionValue && isValidFqdn(value.dimensionValue) && (
251189
<>
252190
&nbsp;
253-
<a href={`https://${value.dimensionValue}`} target="_blank" rel="noreferrer">
191+
<a href={`https://${value.dimensionValue}`} target="_blank" rel="noreferrer" className={styles.external}>
254192
<SquareArrowOutUpRightIcon size={16} />
255193
</a>
256194
</>
@@ -270,10 +208,10 @@ const isValidFqdn = (fqdn: string) => {
270208
}
271209
};
272210

273-
const DimensionLabel = ({ dimension, value }: { dimension: Dimension; value: DimensionTableRow }) =>
211+
export const DimensionLabel = ({ dimension, value }: { dimension: Dimension; value: DimensionTableRow }) =>
274212
dimensionLabels[dimension](value);
275213

276-
const DimensionValueBar = ({
214+
export const DimensionValueBar = ({
277215
value,
278216
biggest,
279217
children,
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { ZoomInIcon } from "lucide-react";
2+
import fuzzysort from "fuzzysort";
3+
import styles from "./dimensions.module.css";
4+
5+
import { cls } from "../../utils";
6+
import { Dialog } from "../dialog";
7+
import { DimensionLabel, DimensionValueBar } from ".";
8+
import {
9+
dimensionNames,
10+
formatMetricVal,
11+
metricNames,
12+
useDimension,
13+
type DateRange,
14+
type Dimension,
15+
type DimensionTableRow,
16+
type Metric,
17+
type ProjectResponse,
18+
} from "../../api";
19+
import { useDeferredValue, useEffect, useMemo, useState } from "react";
20+
21+
export const DetailsModal = ({
22+
project,
23+
dimension,
24+
metric,
25+
range,
26+
}: { project: ProjectResponse; dimension: Dimension; metric: Metric; range: DateRange }) => {
27+
const { data, biggest, order, isLoading } = useDimension({ project, dimension, metric, range });
28+
29+
const [query, setQuery] = useState("");
30+
const deferredQuery = useDeferredValue(query);
31+
32+
const results = useMemo(() => {
33+
if (!deferredQuery || !data) return data;
34+
return fuzzysort.go(deferredQuery, data, { keys: ["displayName", "dimensionValue", "value"] }).map((r) => r.obj);
35+
}, [deferredQuery, data]);
36+
37+
return (
38+
<Dialog
39+
title={`${dimensionNames[dimension]} - ${metricNames[metric]}`}
40+
description={`Detailed breakdown of ${dimensionNames[dimension]} by ${metricNames[metric]}`}
41+
hideTitle
42+
hideDescription
43+
showClose
44+
className={styles.detailsModal}
45+
trigger={() => (
46+
<button type="button" className={cls(styles.showMore, (data?.length ?? 0) === 0 && styles.showMoreHidden)}>
47+
<ZoomInIcon size={16} />
48+
Show details
49+
</button>
50+
)}
51+
>
52+
<div className={styles.dimensionTable} style={{ "--count": data?.length } as React.CSSProperties}>
53+
<div className={styles.dimensionHeader}>
54+
<div>{dimensionNames[dimension]}</div>
55+
<div>{metricNames[metric]}</div>
56+
</div>
57+
<input
58+
type="search"
59+
placeholder="Search..."
60+
value={query}
61+
onChange={(e) => setQuery(e.target.value)}
62+
className={styles.search}
63+
/>
64+
{results?.map((d) => {
65+
const row = d as DimensionTableRow;
66+
return (
67+
<div
68+
key={d.dimensionValue}
69+
style={{ order: order?.indexOf(d.dimensionValue) }}
70+
className={styles.dimensionRow}
71+
>
72+
<DimensionValueBar value={d.value} biggest={biggest}>
73+
<DimensionLabel dimension={dimension} value={row} />
74+
</DimensionValueBar>
75+
<div>{formatMetricVal(metric, d.value)}</div>
76+
</div>
77+
);
78+
})}
79+
{isLoading && data?.length === 0 && (
80+
<div className={styles.dimensionEmpty}>
81+
<div>Loading...</div>
82+
</div>
83+
)}
84+
{!isLoading && data?.length === 0 && (
85+
<div className={styles.dimensionEmpty}>
86+
<div>No data available</div>
87+
</div>
88+
)}
89+
</div>
90+
</Dialog>
91+
);
92+
};

web/src/components/project.module.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
gap: 1rem;
1212
width: 100%;
1313
padding-bottom: 0.5rem;
14+
z-index: 2;
1415

1516
a {
1617
cursor: pointer;

web/src/components/userInfo.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ export const LoginButton = () => {
3333
</li>
3434
<li>
3535
{/* biome-ignore lint/a11y/useValidAnchor: */}
36-
<button
37-
type="button"
36+
<a
37+
href="#"
3838
onClick={() => {
3939
api["/api/dashboard/auth/logout"].post().then(() => {
40-
// window.location.href = "/";
40+
window.location.href = "/";
4141
});
4242
}}
4343
>
4444
<LogOutIcon size="16" />
4545
Logout
46-
</button>
46+
</a>
4747
</li>
4848
</ul>
4949
</details>

web/src/global.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,7 @@ article {
6767
html body[data-scroll-locked] {
6868
margin-right: 0 !important;
6969
}
70+
71+
[type="search"] {
72+
--pico-border-radius: 0.7rem;
73+
}

0 commit comments

Comments
 (0)