Allow defining specific view criteria for server performance graphs

Adds permissions for specific data series on the performance page

Affects issues:
- Close #3738
This commit is contained in:
Aurora Lahtela 2024-11-30 13:26:07 +02:00
parent 3d1ba3d396
commit 22e0b8ed82
8 changed files with 108 additions and 56 deletions

View File

@ -95,6 +95,14 @@ public enum WebPermission implements Supplier<String>, Lang {
PAGE_SERVER_GEOLOCATIONS_PING_PER_COUNTRY("See Ping Per Country table"), PAGE_SERVER_GEOLOCATIONS_PING_PER_COUNTRY("See Ping Per Country table"),
PAGE_SERVER_PERFORMANCE("See Performance tab"), PAGE_SERVER_PERFORMANCE("See Performance tab"),
PAGE_SERVER_PERFORMANCE_GRAPHS("See Performance graphs"), PAGE_SERVER_PERFORMANCE_GRAPHS("See Performance graphs"),
PAGE_SERVER_PERFORMANCE_GRAPHS_PLAYERS_ONLINE("See Players Online data in Performance graphs"),
PAGE_SERVER_PERFORMANCE_GRAPHS_TPS("See TPS data in Performance graphs"),
PAGE_SERVER_PERFORMANCE_GRAPHS_CPU("See CPU usage in Performance graphs"),
PAGE_SERVER_PERFORMANCE_GRAPHS_RAM("See Memory usage in Performance graphs"),
PAGE_SERVER_PERFORMANCE_GRAPHS_ENTITIES("See Entity count data in Performance graphs"),
PAGE_SERVER_PERFORMANCE_GRAPHS_CHUNKS("See Chunk count data in Performance graphs"),
PAGE_SERVER_PERFORMANCE_GRAPHS_DISK("See Disk Space usage Performance graphs"),
PAGE_SERVER_PERFORMANCE_GRAPHS_PING("See Ping data in Performance graphs"),
PAGE_SERVER_PERFORMANCE_OVERVIEW("See Performance numbers"), PAGE_SERVER_PERFORMANCE_OVERVIEW("See Performance numbers"),
PAGE_SERVER_PLUGIN_HISTORY("See Plugin History"), PAGE_SERVER_PLUGIN_HISTORY("See Plugin History"),
PAGE_SERVER_PLUGINS("See Plugins -tabs of servers"), PAGE_SERVER_PLUGINS("See Plugins -tabs of servers"),

View File

@ -218,10 +218,29 @@ public class GraphsJSONResolver extends JSONResolver {
private List<WebPermission> getRequiredPermission(DataID dataID) { private List<WebPermission> getRequiredPermission(DataID dataID) {
switch (dataID) { switch (dataID) {
case GRAPH_PERFORMANCE: case GRAPH_PERFORMANCE:
return List.of(WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS); return List.of(WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_PLAYERS_ONLINE,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_TPS,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_CPU,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_RAM,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_CHUNKS,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_DISK
);
case GRAPH_PING: case GRAPH_PING:
return List.of(WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_PING,
WebPermission.PAGE_NETWORK_PERFORMANCE
);
case GRAPH_OPTIMIZED_PERFORMANCE: case GRAPH_OPTIMIZED_PERFORMANCE:
return List.of(WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS, WebPermission.PAGE_NETWORK_PERFORMANCE); return List.of(WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_PLAYERS_ONLINE,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_TPS,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_CPU,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_RAM,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_CHUNKS,
WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS_DISK,
WebPermission.PAGE_NETWORK_PERFORMANCE
);
case GRAPH_ONLINE: case GRAPH_ONLINE:
return List.of(WebPermission.PAGE_SERVER_OVERVIEW_PLAYERS_ONLINE_GRAPH, WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_ONLINE); return List.of(WebPermission.PAGE_SERVER_OVERVIEW_PLAYERS_ONLINE_GRAPH, WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_ONLINE);
case GRAPH_UNIQUE_NEW: case GRAPH_UNIQUE_NEW:

View File

@ -67,7 +67,7 @@ const PingGraphTab = ({identifier}) => {
const PerformanceGraphsCard = () => { const PerformanceGraphsCard = () => {
const {t} = useTranslation(); const {t} = useTranslation();
const {authRequired, hasPermission} = useAuth(); const {authRequired, hasPermission, hasChildPermission} = useAuth();
const {identifier} = useParams(); const {identifier} = useParams();
const {data, loadingError} = useDataRequest(fetchOptimizedPerformance, [identifier]); const {data, loadingError} = useDataRequest(fetchOptimizedPerformance, [identifier]);
@ -115,33 +115,40 @@ const PerformanceGraphsCard = () => {
} }
}, [pluginHistory, setPluginHistorySeries, t]); }, [pluginHistory, setPluginHistorySeries, t]);
const tabs = [
{
name: t('html.label.all'), icon: faGears, color: 'blue-grey', href: 'all',
element: <AllGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>,
permission: 'page.server.performance.graphs'
}, {
name: t('html.label.tps'), icon: faTachometerAlt, color: 'red', href: 'tps',
element: <TpsGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>,
permission: 'page.server.performance.graphs.tps'
}, {
name: t('html.label.cpuRam'), icon: faMicrochip, color: 'light-green', href: 'cpu-ram',
element: <CpuRamGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>,
permission: ['page.server.performance.graphs.cpu', 'page.server.performance.graphs.ram']
}, {
name: t('html.label.world'), icon: faMap, color: 'purple', href: 'world-load',
element: <WorldGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>,
permission: ['page.server.performance.graphs.entities', 'page.server.performance.graphs.chunks']
}, {
name: t('html.label.ping'), icon: faSignal, color: 'amber', href: 'ping',
element: <PingGraphTab identifier={identifier}/>,
permission: 'page.server.performance.graphs.ping'
}, {
name: t('html.label.diskSpace'), icon: faHdd, color: 'green', href: 'disk',
element: <DiskGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>,
permission: 'page.server.performance.graphs.disk'
},
].filter(tab => hasChildPermission(tab.permission));
return <Card id={"performance-graphs"}> return <Card id={"performance-graphs"}>
<CardTabs tabs={[ <CardTabs tabs={tabs}/>
{
name: t('html.label.all'), icon: faGears, color: 'blue-grey', href: 'all',
element: <AllGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>
}, {
name: t('html.label.tps'), icon: faTachometerAlt, color: 'red', href: 'tps',
element: <TpsGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>
}, {
name: t('html.label.cpuRam'), icon: faMicrochip, color: 'light-green', href: 'cpu-ram',
element: <CpuRamGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>
}, {
name: t('html.label.world'), icon: faMap, color: 'purple', href: 'world-load',
element: <WorldGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>
}, {
name: t('html.label.ping'), icon: faSignal, color: 'amber', href: 'ping',
element: <PingGraphTab identifier={identifier}/>
}, {
name: t('html.label.diskSpace'), icon: faHdd, color: 'green', href: 'disk',
element: <DiskGraphTab data={data} dataSeries={parsedData} pluginHistorySeries={pluginHistorySeries}
loadingError={loadingError || pluginHistoryLoadingError}/>
},
]}/>
</Card> </Card>
} }

View File

@ -8,6 +8,7 @@ import {useTheme} from "../../../hooks/themeHook";
import {withReducedSaturation} from "../../../util/colors"; import {withReducedSaturation} from "../../../util/colors";
import Accessibility from "highcharts/modules/accessibility"; import Accessibility from "highcharts/modules/accessibility";
import {useMetadata} from "../../../hooks/metadataHook"; import {useMetadata} from "../../../hooks/metadataHook";
import {useAuth} from "../../../hooks/authenticationHook.jsx";
const yAxis = [ const yAxis = [
{ {
@ -66,6 +67,7 @@ const AllPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
const {t} = useTranslation(); const {t} = useTranslation();
const {graphTheming, nightModeEnabled} = useTheme(); const {graphTheming, nightModeEnabled} = useTheme();
const {timeZoneOffsetMinutes} = useMetadata(); const {timeZoneOffsetMinutes} = useMetadata();
const {hasPermission} = useAuth();
const onResize = useCallback(() => { const onResize = useCallback(() => {
let chartElement = document.getElementById(id); let chartElement = document.getElementById(id);
@ -74,14 +76,14 @@ const AllPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
if (chart?.yAxis?.length) { if (chart?.yAxis?.length) {
const newWidth = window.innerWidth const newWidth = window.innerWidth
chart.yAxis[0].update({labels: {enabled: newWidth >= 900}}); chart.yAxis[0].update({labels: {enabled: newWidth >= 900 && hasPermission('page.server.performance.graphs.players.online')}});
chart.yAxis[1].update({labels: {enabled: newWidth >= 900}}); chart.yAxis[1].update({labels: {enabled: newWidth >= 900 && hasPermission('page.server.performance.graphs.tps')}});
chart.yAxis[2].update({labels: {enabled: newWidth >= 1000}}); chart.yAxis[2].update({labels: {enabled: newWidth >= 1000 && hasPermission('page.server.performance.graphs.cpu')}});
chart.yAxis[3].update({labels: {enabled: newWidth >= 1000}}); chart.yAxis[3].update({labels: {enabled: newWidth >= 1000 && hasPermission('page.server.performance.graphs.ram')}});
chart.yAxis[4].update({labels: {enabled: newWidth >= 1400}}); chart.yAxis[4].update({labels: {enabled: newWidth >= 1400 && hasPermission('page.server.performance.graphs.entities')}});
chart.yAxis[5].update({labels: {enabled: newWidth >= 1400}}); chart.yAxis[5].update({labels: {enabled: newWidth >= 1400 && hasPermission('page.server.performance.graphs.chunks')}});
} }
}, [id]) }, [id, hasPermission])
useEffect(() => { useEffect(() => {
window.addEventListener("resize", onResize); window.addEventListener("resize", onResize);
@ -107,14 +109,15 @@ const AllPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
const spline = 'spline' const spline = 'spline'
const series = { const series = {
playersOnline: { playersOnline: hasPermission('page.server.performance.graphs.players.online') ? {
name: t('html.label.playersOnline'), name: t('html.label.playersOnline'),
type: 'areaspline', type: 'areaspline',
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.playersOnline, data: dataSeries.playersOnline,
color: data.colors.playersOnline, color: data.colors.playersOnline,
yAxis: 0 yAxis: 0
}, tps: { } : {},
tps: hasPermission('page.server.performance.graphs.tps') ? {
name: t('html.label.tps'), name: t('html.label.tps'),
type: spline, type: spline,
color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high, color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high,
@ -122,35 +125,39 @@ const AllPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
tooltip: tooltip.twoDecimals, tooltip: tooltip.twoDecimals,
data: dataSeries.tps, data: dataSeries.tps,
yAxis: 1 yAxis: 1
}, cpu: { } : {},
cpu: hasPermission('page.server.performance.graphs.cpu') ? {
name: t('html.label.cpu'), name: t('html.label.cpu'),
type: spline, type: spline,
tooltip: tooltip.twoDecimals, tooltip: tooltip.twoDecimals,
data: dataSeries.cpu, data: dataSeries.cpu,
color: nightModeEnabled ? withReducedSaturation(data.colors.cpu) : data.colors.cpu, color: nightModeEnabled ? withReducedSaturation(data.colors.cpu) : data.colors.cpu,
yAxis: 2 yAxis: 2
}, ram: { } : {},
ram: hasPermission('page.server.performance.graphs.ram') ? {
name: t('html.label.ram'), name: t('html.label.ram'),
type: spline, type: spline,
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.ram, data: dataSeries.ram,
color: nightModeEnabled ? withReducedSaturation(data.colors.ram) : data.colors.ram, color: nightModeEnabled ? withReducedSaturation(data.colors.ram) : data.colors.ram,
yAxis: 3 yAxis: 3
}, entities: { } : {},
entities: hasPermission('page.server.performance.graphs.entities') ? {
name: t('html.label.loadedEntities'), name: t('html.label.loadedEntities'),
type: spline, type: spline,
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.entities, data: dataSeries.entities,
color: nightModeEnabled ? withReducedSaturation(data.colors.entities) : data.colors.entities, color: nightModeEnabled ? withReducedSaturation(data.colors.entities) : data.colors.entities,
yAxis: 4 yAxis: 4
}, chunks: { } : {},
chunks: hasPermission('page.server.performance.graphs.chunks') ? {
name: t('html.label.loadedChunks'), name: t('html.label.loadedChunks'),
type: spline, type: spline,
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.chunks, data: dataSeries.chunks,
color: nightModeEnabled ? withReducedSaturation(data.colors.chunks) : data.colors.chunks, color: nightModeEnabled ? withReducedSaturation(data.colors.chunks) : data.colors.chunks,
yAxis: 5 yAxis: 5
} } : {}
}; };
NoDataDisplay(Highcharts); NoDataDisplay(Highcharts);

View File

@ -8,38 +8,42 @@ import {useTheme} from "../../../hooks/themeHook";
import {withReducedSaturation} from "../../../util/colors"; import {withReducedSaturation} from "../../../util/colors";
import Accessibility from "highcharts/modules/accessibility"; import Accessibility from "highcharts/modules/accessibility";
import {useMetadata} from "../../../hooks/metadataHook"; import {useMetadata} from "../../../hooks/metadataHook";
import {useAuth} from "../../../hooks/authenticationHook.jsx";
const CpuRamPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => { const CpuRamPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
const {t} = useTranslation(); const {t} = useTranslation();
const {graphTheming, nightModeEnabled} = useTheme(); const {graphTheming, nightModeEnabled} = useTheme();
const {timeZoneOffsetMinutes} = useMetadata(); const {timeZoneOffsetMinutes} = useMetadata();
const {hasPermission} = useAuth();
useEffect(() => { useEffect(() => {
const spline = 'spline' const spline = 'spline'
const series = { const series = {
playersOnline: { playersOnline: hasPermission('page.server.performance.graphs.players.online') ? {
name: t('html.label.playersOnline'), name: t('html.label.playersOnline'),
type: 'areaspline', type: 'areaspline',
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.playersOnline, data: dataSeries.playersOnline,
color: data.colors.playersOnline, color: data.colors.playersOnline,
yAxis: 0 yAxis: 0
}, cpu: { } : {},
cpu: hasPermission('page.server.performance.graphs.cpu') ? {
name: t('html.label.cpu'), name: t('html.label.cpu'),
type: spline, type: spline,
tooltip: tooltip.twoDecimals, tooltip: tooltip.twoDecimals,
data: dataSeries.cpu, data: dataSeries.cpu,
color: nightModeEnabled ? withReducedSaturation(data.colors.cpu) : data.colors.cpu, color: nightModeEnabled ? withReducedSaturation(data.colors.cpu) : data.colors.cpu,
yAxis: 1 yAxis: 1
}, ram: { } : {},
ram: hasPermission('page.server.performance.graphs.ram') ? {
name: t('html.label.ram'), name: t('html.label.ram'),
type: spline, type: spline,
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.ram, data: dataSeries.ram,
color: nightModeEnabled ? withReducedSaturation(data.colors.ram) : data.colors.ram, color: nightModeEnabled ? withReducedSaturation(data.colors.ram) : data.colors.ram,
yAxis: 2 yAxis: 2
} } : {}
}; };
NoDataDisplay(Highcharts); NoDataDisplay(Highcharts);

View File

@ -8,11 +8,13 @@ import {useTheme} from "../../../hooks/themeHook";
import {withReducedSaturation} from "../../../util/colors"; import {withReducedSaturation} from "../../../util/colors";
import Accessibility from "highcharts/modules/accessibility"; import Accessibility from "highcharts/modules/accessibility";
import {useMetadata} from "../../../hooks/metadataHook"; import {useMetadata} from "../../../hooks/metadataHook";
import {useAuth} from "../../../hooks/authenticationHook.jsx";
const TpsPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => { const TpsPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
const {t} = useTranslation(); const {t} = useTranslation();
const {graphTheming, nightModeEnabled} = useTheme(); const {graphTheming, nightModeEnabled} = useTheme();
const {timeZoneOffsetMinutes} = useMetadata(); const {timeZoneOffsetMinutes} = useMetadata();
const {hasPermission} = useAuth();
useEffect(() => { useEffect(() => {
const zones = { const zones = {
@ -30,14 +32,15 @@ const TpsPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
const spline = 'spline' const spline = 'spline'
const series = { const series = {
playersOnline: { playersOnline: hasPermission('page.server.performance.graphs.players.online') ? {
name: t('html.label.playersOnline'), name: t('html.label.playersOnline'),
type: 'areaspline', type: 'areaspline',
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.playersOnline, data: dataSeries.playersOnline,
color: data.colors.playersOnline, color: data.colors.playersOnline,
yAxis: 0 yAxis: 0
}, tps: { } : {},
tps: hasPermission('page.server.performance.graphs.tps') ? {
name: t('html.label.tps'), name: t('html.label.tps'),
type: spline, type: spline,
color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high, color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high,
@ -45,7 +48,7 @@ const TpsPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
tooltip: tooltip.twoDecimals, tooltip: tooltip.twoDecimals,
data: dataSeries.tps, data: dataSeries.tps,
yAxis: 1 yAxis: 1
} } : {}
}; };
NoDataDisplay(Highcharts); NoDataDisplay(Highcharts);

View File

@ -8,38 +8,42 @@ import {useTheme} from "../../../hooks/themeHook";
import {withReducedSaturation} from "../../../util/colors"; import {withReducedSaturation} from "../../../util/colors";
import Accessibility from "highcharts/modules/accessibility"; import Accessibility from "highcharts/modules/accessibility";
import {useMetadata} from "../../../hooks/metadataHook"; import {useMetadata} from "../../../hooks/metadataHook";
import {useAuth} from "../../../hooks/authenticationHook.jsx";
const WorldPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => { const WorldPerformanceGraph = ({id, data, dataSeries, pluginHistorySeries}) => {
const {t} = useTranslation(); const {t} = useTranslation();
const {graphTheming, nightModeEnabled} = useTheme(); const {graphTheming, nightModeEnabled} = useTheme();
const {timeZoneOffsetMinutes} = useMetadata(); const {timeZoneOffsetMinutes} = useMetadata();
const {hasPermission} = useAuth();
useEffect(() => { useEffect(() => {
const spline = 'spline' const spline = 'spline'
const series = { const series = {
playersOnline: { playersOnline: hasPermission('page.server.performance.graphs.players.online') ? {
name: t('html.label.playersOnline'), name: t('html.label.playersOnline'),
type: 'areaspline', type: 'areaspline',
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.playersOnline, data: dataSeries.playersOnline,
color: data.colors.playersOnline, color: data.colors.playersOnline,
yAxis: 0 yAxis: 0
}, entities: { } : {},
entities: hasPermission('page.server.performance.graphs.entities') ? {
name: t('html.label.loadedEntities'), name: t('html.label.loadedEntities'),
type: spline, type: spline,
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.entities, data: dataSeries.entities,
color: nightModeEnabled ? withReducedSaturation(data.colors.entities) : data.colors.entities, color: nightModeEnabled ? withReducedSaturation(data.colors.entities) : data.colors.entities,
yAxis: 1 yAxis: 1
}, chunks: { } : {},
chunks: hasPermission('page.server.performance.graphs.chunks') ? {
name: t('html.label.loadedChunks'), name: t('html.label.loadedChunks'),
type: spline, type: spline,
tooltip: tooltip.zeroDecimals, tooltip: tooltip.zeroDecimals,
data: dataSeries.chunks, data: dataSeries.chunks,
color: nightModeEnabled ? withReducedSaturation(data.colors.chunks) : data.colors.chunks, color: nightModeEnabled ? withReducedSaturation(data.colors.chunks) : data.colors.chunks,
yAxis: 2 yAxis: 2
} } : {}
}; };
NoDataDisplay(Highcharts); NoDataDisplay(Highcharts);

View File

@ -12,10 +12,10 @@ import ExtendableRow from "../../components/layout/extension/ExtendableRow";
import {useAuth} from "../../hooks/authenticationHook"; import {useAuth} from "../../hooks/authenticationHook";
const ServerPerformance = () => { const ServerPerformance = () => {
const {hasPermission} = useAuth(); const {hasPermission, hasChildPermission} = useAuth();
const {identifier} = useParams(); const {identifier} = useParams();
const seeGraphs = hasPermission('page.server.performance.graphs'); const seeGraphs = hasChildPermission('page.server.performance.graphs');
const seeOverview = hasPermission('page.server.performance.overview'); const seeOverview = hasPermission('page.server.performance.overview');
const {data, loadingError} = useDataRequest(fetchPerformanceOverview, [identifier], seeOverview); const {data, loadingError} = useDataRequest(fetchPerformanceOverview, [identifier], seeOverview);