Skip to content

Commit 45cb171

Browse files
committed
refactor utilization chart component for improved data handling and styling
1 parent 0da2ef0 commit 45cb171

File tree

1 file changed

+119
-64
lines changed
  • src/routes/team/[team]/[env]/app/[app]/utilization

1 file changed

+119
-64
lines changed

src/routes/team/[team]/[env]/app/[app]/utilization/+page.svelte

+119-64
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
<script lang="ts">
22
import { page } from '$app/state';
3+
import EChart from '$lib/chart/EChart.svelte';
4+
import GraphErrors from '$lib/GraphErrors.svelte';
5+
import type { EChartsOption } from 'echarts';
6+
37
import { UtilizationResourceType } from '$houdini';
48
import Card from '$lib/Card.svelte';
59
import { euroValueFormatter } from '$lib/chart/cost_transformer';
6-
import EChart from '$lib/chart/EChart.svelte';
710
import SummaryCard from '$lib/components/SummaryCard.svelte';
8-
import GraphErrors from '$lib/GraphErrors.svelte';
911
import CpuIcon from '$lib/icons/CpuIcon.svelte';
1012
import MemoryIcon from '$lib/icons/MemoryIcon.svelte';
1113
import { cpuUtilization, memoryUtilization, yearlyOverageCost } from '$lib/utils/resources';
14+
import { Heading } from '@nais/ds-svelte-community';
1215
import { WalletIcon } from '@nais/ds-svelte-community/icons';
13-
import type { EChartsOption } from 'echarts';
1416
import prettyBytes from 'pretty-bytes';
15-
1617
import type { PageProps } from './$houdini';
1718
1819
let { data }: PageProps = $props();
@@ -29,7 +30,22 @@
2930
limit?: number,
3031
color: string = '#000000'
3132
): EChartsOption {
32-
const dates = data?.map((d) => d.timestamp) || [];
33+
const safeData = data ?? [];
34+
const dates = safeData.map((d) =>
35+
d.timestamp.toLocaleString('en-GB', {
36+
year: 'numeric',
37+
month: 'short',
38+
day: 'numeric',
39+
hour: '2-digit',
40+
minute: '2-digit'
41+
})
42+
);
43+
44+
const usageValues = safeData.map((d) => d.value);
45+
const referenceValues = Array(safeData.length).fill(null);
46+
const limitValues = limit !== undefined ? Array(safeData.length).fill(limit) : referenceValues;
47+
const requestValues = Array(safeData.length).fill(request);
48+
3349
return {
3450
tooltip: {
3551
trigger: 'axis',
@@ -42,39 +58,76 @@
4258
xAxis: {
4359
type: 'category',
4460
boundaryGap: false,
45-
data: dates.map((date) => {
46-
return date.toLocaleDateString('en-GB', {
47-
year: 'numeric',
48-
month: 'short',
49-
day: 'numeric',
50-
hour: '2-digit',
51-
minute: '2-digit'
52-
});
53-
})
61+
data: dates
5462
},
5563
series: [
5664
{
5765
name: 'Usage',
5866
type: 'line',
59-
data: data?.map((d) => d.value) || [],
60-
color
61-
},
62-
{
63-
name: 'Limit',
64-
type: 'line',
65-
data: data?.map(() => limit) || [],
67+
data: usageValues,
6668
showSymbol: false,
67-
color: '#C30000'
69+
color,
70+
areaStyle: {
71+
opacity: 0.2
72+
}
6873
},
74+
...(limit !== undefined
75+
? [
76+
{
77+
name: 'Limit',
78+
type: 'line',
79+
data: limitValues,
80+
showSymbol: false,
81+
color: limitColor,
82+
markLine: {
83+
symbol: ['none', 'none'],
84+
data: [
85+
{
86+
yAxis: limit,
87+
label: {
88+
formatter: 'Limit',
89+
position: 'end',
90+
color: limitColor,
91+
padding: [2, 5],
92+
borderRadius: 3
93+
}
94+
}
95+
],
96+
lineStyle: {
97+
type: 'solid',
98+
color: limitColor
99+
}
100+
}
101+
}
102+
]
103+
: []),
69104
{
70105
name: 'Requested',
71106
type: 'line',
72-
data: data?.map(() => request) || [],
107+
data: requestValues,
73108
showSymbol: false,
74-
color: '#00C300'
109+
color: requestColor,
110+
markLine: {
111+
symbol: ['none', 'none'],
112+
data: [
113+
{
114+
yAxis: request,
115+
label: {
116+
formatter: 'Requested',
117+
position: 'end',
118+
color: requestColor,
119+
padding: [2, 5],
120+
borderRadius: 3
121+
}
122+
}
123+
],
124+
lineStyle: {
125+
type: 'solid',
126+
color: requestColor
127+
}
128+
}
75129
}
76130
],
77-
78131
yAxis: {
79132
type: 'value',
80133
name: 'Usage of requested resources',
@@ -85,13 +138,17 @@
85138
}
86139
} as EChartsOption;
87140
}
141+
142+
const limitColor = '#DE2E2E';
143+
const usageMemColor = '#8269A2';
144+
const usageCPUColor = '#FF9100';
145+
const requestColor = '#3386E0';
88146
</script>
89147

90148
<GraphErrors errors={$ResourceUtilizationForApp.errors} />
91149

92150
{#if $ResourceUtilizationForApp.data}
93151
{@const utilization = $ResourceUtilizationForApp.data.team.environment.application.utilization}
94-
95152
<div class="grid">
96153
<Card columns={3} borderColor="#83bff6">
97154
<SummaryCard
@@ -171,55 +228,53 @@
171228
)}
172229
</SummaryCard>
173230
</Card>
174-
<Card columns={12} borderColor="var(--a-gray-100)">
175-
<span class="graphHeader">
176-
<h3 style="margin-bottom: 0">Memory usage</h3>
177-
<span class="intervalPicker">
178-
{#each ['1h', '6h', '1d', '7d', '30d'] as interval (interval)}
179-
<a
180-
class:active={(page.url.searchParams.get('interval') || '7d') == interval}
181-
href="?interval={interval}">{interval}</a
182-
>
183-
{/each}
184-
</span>
185-
</span>
186-
<EChart
187-
options={options(
188-
utilization.memory_series.map((d) => {
189-
return { timestamp: d.timestamp, value: d.value / 1024 / 1024 / 1024 };
190-
}),
191-
utilization.requested_memory / 1024 / 1024 / 1024,
192-
utilization.limit_memory ? utilization.limit_memory / 1024 / 1024 / 1024 : undefined,
193-
'rgb(145, 220, 117)'
194-
)}
195-
style="height: 400px"
196-
/>
197-
198-
<span class="graphHeader">
199-
<h3 style="margin-bottom: 0">CPU usage</h3>
200-
</span>
201-
<EChart
202-
options={options(
203-
utilization.cpu_series,
204-
utilization.requested_cpu,
205-
utilization.limit_cpu ? utilization.limit_cpu : undefined,
206-
'rgb(131, 191, 246)'
207-
)}
208-
style="height: 400px"
209-
/>
210-
</Card>
211231
</div>
232+
<span class="graphHeader">
233+
<Heading level="2" size="medium">Memory usage</Heading>
234+
<span class="intervalPicker">
235+
{#each ['1h', '6h', '1d', '7d', '30d'] as interval (interval)}
236+
<a
237+
class:active={(page.url.searchParams.get('interval') || '7d') == interval}
238+
href="?interval={interval}">{interval}</a
239+
>
240+
{/each}
241+
</span>
242+
</span>
243+
<EChart
244+
options={options(
245+
utilization.memory_series.map((d) => {
246+
return { timestamp: d.timestamp, value: d.value / 1024 / 1024 / 1024 };
247+
}),
248+
utilization.requested_memory / 1024 / 1024 / 1024,
249+
utilization.limit_memory ? utilization.limit_memory / 1024 / 1024 / 1024 : undefined,
250+
usageMemColor
251+
)}
252+
style="height: 400px"
253+
/>
254+
255+
<span class="graphHeader">
256+
<Heading level="2" size="medium">CPU usage</Heading>
257+
</span>
258+
<EChart
259+
options={options(
260+
utilization.cpu_series,
261+
utilization.requested_cpu,
262+
utilization.limit_cpu ? utilization.limit_cpu : undefined,
263+
usageCPUColor
264+
)}
265+
style="height: 400px"
266+
/>
212267
{/if}
213268

214269
<style>
215270
.grid {
216271
margin-top: 1rem;
272+
margin-bottom: var(--a-spacing-6);
217273
display: grid;
218274
grid-template-columns: repeat(12, 1fr);
219275
column-gap: 1rem;
220276
row-gap: 1rem;
221277
}
222-
223278
.graphHeader {
224279
display: flex;
225280
justify-content: space-between;

0 commit comments

Comments
 (0)