Skip to content

Commit f1e93bd

Browse files
authored
Merge pull request #121 from cptKNJO/ui
UI
2 parents 1509994 + 9a32380 commit f1e93bd

File tree

12 files changed

+394
-46
lines changed

12 files changed

+394
-46
lines changed

urbackupserver/www2/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@fluentui/react-charting": "^5.23.11",
17+
"@fluentui/react-charts-preview": "^0.1.6",
1718
"@fluentui/react-components": "^9.57.0",
1819
"@fluentui/react-experiments": "^8.14.152",
1920
"@fluentui/react-icons": "^2.0.239",

urbackupserver/www2/pnpm-lock.yaml

+51
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

urbackupserver/www2/src/App.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
teamsDarkTheme,
1414
Spinner,
1515
Toaster,
16+
mergeClasses,
1617
} from "@fluentui/react-components";
1718
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
1819
import { useStackStyles } from "./components/StackStyles";
@@ -225,7 +226,9 @@ const App: React.FunctionComponent = () => {
225226
<NavSidebar />
226227
</div>
227228
)}
228-
<div className={styles.itemGrow} style={{ padding: "10pt" }}>
229+
<div
230+
className={mergeClasses(styles.itemGrow, styles.content)}
231+
>
229232
<Suspense fallback={<Spinner />}>
230233
<RouterProvider router={router} />
231234
</Suspense>

urbackupserver/www2/src/components/StackStyles.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,9 @@ export const useStackStyles = makeStyles({
4545
width: "auto",
4646
flex: 1,
4747
},
48+
content: {
49+
padding: "10pt",
50+
maxWidth: "1200px",
51+
marginInline: "auto",
52+
},
4853
});

urbackupserver/www2/src/css/global.css

+32
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,13 @@
6767
justify-content: var(--cluster-horizontal-alignment, flex-start);
6868
align-items: var(--cluster-vertical-alignment, center);
6969
}
70+
71+
.cluster[data-justify-content="space-between"] {
72+
--cluster-horizontal-alignment: space-between;
73+
}
7074
}
7175

76+
/* Table */
7277
.table-wrapper {
7378
--flow-space: 1.5em;
7479
}
@@ -84,3 +89,30 @@
8489
align-items: center;
8590
width: 100%;
8691
}
92+
93+
/* Chart */
94+
.donut-chart {
95+
transform: translateY(-20px);
96+
}
97+
98+
.donut-chart__legend {
99+
transform: translateY(-30px);
100+
}
101+
102+
.donut-chart__legend-rect {
103+
border-radius: var(--borderRadiusSmall);
104+
}
105+
106+
/*
107+
TODO: Review after @fluentui/react-charts-preview is updated,
108+
if there's another way to target the following styles without FUI classes.
109+
*/
110+
.donut-chart__legend .fui-legend__resizableArea {
111+
justify-content: center;
112+
max-height: 200px;
113+
overflow: auto;
114+
}
115+
116+
.donut-chart__legend .fui-legend__resizableArea > * {
117+
margin: 0 !important;
118+
}

urbackupserver/www2/src/css/reset.css

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dd {
3232
ul,
3333
ol {
3434
list-style: none;
35+
padding: 0;
3536
}
3637

3738
/* Set core body defaults */
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
import { type IChartProps, Sparkline } from "@fluentui/react-charting";
1+
import { Sparkline, ChartProps } from "@fluentui/react-charts-preview";
22
import { tokens } from "@fluentui/react-components";
33

44
import type { ProcessItem } from "../../api/urbackupserver";
55
import { format_speed_bpms_to_bps } from "../../utils/format";
66

7-
const sparklineStyles = {
8-
valueText: {
9-
fill: tokens.colorNeutralForeground1,
10-
},
11-
};
12-
137
export function ProcessSpeedChart(process: ProcessItem) {
148
if (process.speed_bpms === 0 && process.past_speed_bpms.length === 0) {
159
return "-";
@@ -18,7 +12,7 @@ export function ProcessSpeedChart(process: ProcessItem) {
1812
const legend =
1913
process.speed_bpms > 0 ? format_speed_bpms_to_bps(process.speed_bpms) : "";
2014

21-
const data: IChartProps = {
15+
const data: ChartProps = {
2216
chartTitle: "Speed chart",
2317
lineChartData: [
2418
{
@@ -32,5 +26,5 @@ export function ProcessSpeedChart(process: ProcessItem) {
3226
],
3327
};
3428

35-
return <Sparkline data={data} showLegend styles={sparklineStyles} />;
29+
return <Sparkline data={data} showLegend />;
3630
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { useState } from "react";
2+
import { useSuspenseQuery } from "@tanstack/react-query";
3+
import {
4+
DonutChart,
5+
ChartProps,
6+
getColorFromToken,
7+
DataVizPalette,
8+
} from "@fluentui/react-charts-preview";
9+
import { tokens } from "@fluentui/react-components";
10+
11+
import { format_size } from "../../utils/format";
12+
import { urbackupServer } from "../../App";
13+
14+
const colors = Object.entries(DataVizPalette)
15+
.filter(([k]) => k.includes("color"))
16+
.map(([, v]) => getColorFromToken(v));
17+
18+
export function StorageAllocation() {
19+
const clientsStorageUsageResult = useSuspenseQuery({
20+
queryKey: ["client-storage-usage"],
21+
queryFn: () => urbackupServer.getPiegraphData(),
22+
});
23+
24+
const clientsStorageUsage = clientsStorageUsageResult.data;
25+
26+
const data: ChartProps = {
27+
chartTitle: "Storage Allocation",
28+
chartData: clientsStorageUsage
29+
.sort((a, b) => a.data - b.data)
30+
.map((d, i) => ({
31+
legend: d.label,
32+
data: d.data,
33+
yAxisCalloutData: format_size(d.data),
34+
// Don't use colours from palette if no data, to make legends
35+
// easier to parse.
36+
color: d.data ? colors[i] : tokens.colorNeutralForegroundDisabled,
37+
})),
38+
};
39+
40+
const [showChart, setShowChart] = useState(false);
41+
42+
const isStorageInUse = data.chartData?.some((d) => d.data !== 0);
43+
44+
if (!isStorageInUse) {
45+
return <span>No storage in use by clients</span>;
46+
}
47+
48+
return (
49+
<div
50+
style={{
51+
visibility: showChart ? "initial" : "hidden",
52+
}}
53+
>
54+
<DonutChart
55+
onResize={() => {
56+
/**
57+
* Required to set the chart visible after it resizes itself
58+
* on initial render according to container dimensions. Without the check,
59+
* the chart flashes into the correct size after initial render.
60+
*/
61+
if (!showChart) {
62+
setShowChart(true);
63+
}
64+
}}
65+
data={data}
66+
innerRadius={100}
67+
legendProps={{
68+
enabledWrapLines: true,
69+
allowFocusOnLegends: true,
70+
styles: {
71+
rect: "donut-chart__legend-rect",
72+
},
73+
}}
74+
hideLabels={false}
75+
showLabelsInPercent
76+
styles={{
77+
chart: "donut-chart",
78+
legendContainer: "donut-chart__legend",
79+
}}
80+
/>
81+
</div>
82+
);
83+
}

urbackupserver/www2/src/features/statistics/StorageUsage.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
import { useState } from "react";
2-
import { useSuspenseQuery } from "@tanstack/react-query";
1+
/**
2+
* TODO: Replace with LineChart from `@fluentui/react-charts-preview` once
3+
* the LineChart is fixed in it.
4+
* Review if the old charting library, `@fluentui/react-charting`
5+
* can be uininstalled after replacing the following chart.
6+
*/
37
import {
48
IChartProps,
59
ILineChartStyles,
610
LineChart,
711
} from "@fluentui/react-charting";
812
import { Tab, TabList, tokens } from "@fluentui/react-components";
13+
import { useState } from "react";
14+
import { useSuspenseQuery } from "@tanstack/react-query";
915

1016
import { urbackupServer } from "../../App";
1117
import { SelectStorageUsageClient } from "./SelectStorageUsageClient";

urbackupserver/www2/src/features/statistics/StorageUsageBreakdownTable.tsx

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useState } from "react";
12
import {
23
DataGrid,
34
DataGridHeader,
@@ -11,10 +12,8 @@ import {
1112
DataGridProps,
1213
} from "@fluentui/react-components";
1314

14-
import type { UsageClientStat } from "../../api/urbackupserver";
15+
import type { UsageClientStat, UsageStats } from "../../api/urbackupserver";
1516
import { format_size } from "../../utils/format";
16-
import { useSuspenseQuery } from "@tanstack/react-query";
17-
import { urbackupServer } from "../../App";
1817
import {
1918
filterBySearch,
2019
SearchBox,
@@ -26,7 +25,6 @@ import {
2625
usePagination,
2726
} from "../../components/Pagination";
2827
import { TableWrapper } from "../../components/TableWrapper";
29-
import { useState } from "react";
3028

3129
const compareNum = (a: number, b: number) => {
3230
return a == b ? 0 : a < b ? -1 : 1;
@@ -83,14 +81,11 @@ export const columns: TableColumnDefinition<UsageClientStat>[] = [
8381
}),
8482
];
8583

86-
export function StorageUsageBreakdownTable() {
87-
const storageUsageStatsResult = useSuspenseQuery({
88-
queryKey: ["storage-usage"],
89-
queryFn: () => urbackupServer.getUsageStats(),
90-
});
91-
92-
const data = storageUsageStatsResult.data!.usage;
93-
84+
export function StorageUsageBreakdownTable({
85+
data,
86+
}: {
87+
data: UsageStats["usage"];
88+
}) {
9489
if (data.length === 0) {
9590
return <span>No storage in use</span>;
9691
}

0 commit comments

Comments
 (0)