@letthem/backstage-plugin-cost-insights 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/dist/components/EC2CostPage.esm.js +322 -0
- package/dist/components/EC2CostPage.esm.js.map +1 -0
- package/dist/components/EC2Overview.esm.js +445 -0
- package/dist/components/EC2Overview.esm.js.map +1 -0
- package/dist/components/EC2ResourceTable.esm.js +193 -0
- package/dist/components/EC2ResourceTable.esm.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.esm.js +5 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/plugin.esm.js +19 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Card, CardContent, Typography, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, ButtonGroup, Button } from '@mui/material';
|
|
3
|
+
import Grid2 from '@mui/material/Grid2';
|
|
4
|
+
import { useState, useMemo } from 'react';
|
|
5
|
+
import { ResponsiveContainer, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Line, BarChart, Bar } from 'recharts';
|
|
6
|
+
import { DateTime } from 'luxon';
|
|
7
|
+
|
|
8
|
+
function EC2Overview({ resources, monthlyData = [], startDate, endDate }) {
|
|
9
|
+
const [monthsToShow, setMonthsToShow] = useState(6);
|
|
10
|
+
const instancesCost = resources.filter((r) => r.resourceType === "instance").reduce((sum, r) => sum + r.totalCost, 0);
|
|
11
|
+
const volumeCost = resources.filter((r) => r.resourceType === "snapshot" || r.resourceType === "volume").reduce((sum, r) => sum + r.totalCost, 0);
|
|
12
|
+
const elasticIpCost = resources.filter((r) => r.resourceType === "elastic-ip").reduce((sum, r) => sum + r.totalCost, 0);
|
|
13
|
+
const natGatewayCost = resources.filter((r) => r.resourceType === "nat-gateway").reduce((sum, r) => sum + r.totalCost, 0);
|
|
14
|
+
const dataTransferCost = resources.filter((r) => r.resourceType === "data-transfer").reduce((sum, r) => sum + r.totalCost, 0);
|
|
15
|
+
const vpcCost = resources.filter((r) => r.resourceType === "vpc").reduce((sum, r) => sum + r.totalCost, 0);
|
|
16
|
+
const totalCost = instancesCost + volumeCost + elasticIpCost + natGatewayCost + dataTransferCost + vpcCost;
|
|
17
|
+
const dailyTrendData = useMemo(() => {
|
|
18
|
+
const dailyMap = /* @__PURE__ */ new Map();
|
|
19
|
+
resources.forEach((resource) => {
|
|
20
|
+
resource.dailyCosts.forEach(({ date, cost }) => {
|
|
21
|
+
if (!dailyMap.has(date)) {
|
|
22
|
+
dailyMap.set(date, {
|
|
23
|
+
instances: 0,
|
|
24
|
+
volume: 0,
|
|
25
|
+
elasticIp: 0,
|
|
26
|
+
natGateway: 0,
|
|
27
|
+
dataTransfer: 0,
|
|
28
|
+
vpc: 0
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const entry = dailyMap.get(date);
|
|
32
|
+
if (resource.resourceType === "instance") {
|
|
33
|
+
entry.instances += cost;
|
|
34
|
+
} else if (resource.resourceType === "snapshot" || resource.resourceType === "volume") {
|
|
35
|
+
entry.volume += cost;
|
|
36
|
+
} else if (resource.resourceType === "elastic-ip") {
|
|
37
|
+
entry.elasticIp += cost;
|
|
38
|
+
} else if (resource.resourceType === "nat-gateway") {
|
|
39
|
+
entry.natGateway += cost;
|
|
40
|
+
} else if (resource.resourceType === "data-transfer") {
|
|
41
|
+
entry.dataTransfer += cost;
|
|
42
|
+
} else if (resource.resourceType === "vpc") {
|
|
43
|
+
entry.vpc += cost;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
return Array.from(dailyMap.entries()).map(([date, costs]) => ({
|
|
48
|
+
date,
|
|
49
|
+
Instances: costs.instances,
|
|
50
|
+
Volume: costs.volume,
|
|
51
|
+
"Elastic IP": costs.elasticIp,
|
|
52
|
+
"NAT Gateway": costs.natGateway,
|
|
53
|
+
"Data Transfer": costs.dataTransfer,
|
|
54
|
+
VPC: costs.vpc,
|
|
55
|
+
Total: costs.instances + costs.volume + costs.elasticIp + costs.natGateway + costs.dataTransfer + costs.vpc
|
|
56
|
+
})).sort((a, b) => a.date.localeCompare(b.date));
|
|
57
|
+
}, [resources]);
|
|
58
|
+
const topResources = useMemo(() => {
|
|
59
|
+
return [...resources].sort((a, b) => b.totalCost - a.totalCost).slice(0, 5).map((resource) => ({
|
|
60
|
+
...resource,
|
|
61
|
+
percentage: totalCost > 0 ? resource.totalCost / totalCost * 100 : 0
|
|
62
|
+
}));
|
|
63
|
+
}, [resources, totalCost]);
|
|
64
|
+
const predictedMonthEndCost = useMemo(() => {
|
|
65
|
+
if (dailyTrendData.length === 0) return null;
|
|
66
|
+
const now = DateTime.now();
|
|
67
|
+
const currentMonthStart = now.startOf("month");
|
|
68
|
+
if (!startDate || !endDate) return null;
|
|
69
|
+
const isCurrentMonthStart = startDate.hasSame(currentMonthStart, "day");
|
|
70
|
+
const isCurrentMonth = endDate.hasSame(now, "month") && endDate.hasSame(now, "year");
|
|
71
|
+
if (!isCurrentMonthStart || !isCurrentMonth) return null;
|
|
72
|
+
const daysInMonth = now.daysInMonth || 30;
|
|
73
|
+
const currentDay = now.day;
|
|
74
|
+
if (currentDay <= 0) return null;
|
|
75
|
+
const avgDailyCost = totalCost / currentDay;
|
|
76
|
+
const remainingDays = daysInMonth - currentDay;
|
|
77
|
+
const predictedTotal = totalCost + avgDailyCost * remainingDays;
|
|
78
|
+
return {
|
|
79
|
+
current: totalCost,
|
|
80
|
+
predicted: predictedTotal,
|
|
81
|
+
remainingDays,
|
|
82
|
+
avgDailyCost
|
|
83
|
+
};
|
|
84
|
+
}, [dailyTrendData, totalCost, startDate, endDate]);
|
|
85
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
86
|
+
/* @__PURE__ */ jsxs(Grid2, { container: true, spacing: 3, children: [
|
|
87
|
+
/* @__PURE__ */ jsx(Grid2, { size: { sm: 12, md: 3 }, children: /* @__PURE__ */ jsx(
|
|
88
|
+
Card,
|
|
89
|
+
{
|
|
90
|
+
sx: {
|
|
91
|
+
height: "100%",
|
|
92
|
+
position: "relative",
|
|
93
|
+
bgcolor: "background.paper"
|
|
94
|
+
},
|
|
95
|
+
children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
96
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, color: "text.secondary", mb: 1 }, children: "Total EC2 Cost" }),
|
|
97
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 32, fontWeight: 600 }, children: [
|
|
98
|
+
"$",
|
|
99
|
+
totalCost.toFixed(2)
|
|
100
|
+
] })
|
|
101
|
+
] })
|
|
102
|
+
}
|
|
103
|
+
) }),
|
|
104
|
+
/* @__PURE__ */ jsx(Grid2, { size: { sm: 12, md: 3 }, children: /* @__PURE__ */ jsx(
|
|
105
|
+
Card,
|
|
106
|
+
{
|
|
107
|
+
sx: {
|
|
108
|
+
height: "100%",
|
|
109
|
+
position: "relative",
|
|
110
|
+
bgcolor: "background.paper"
|
|
111
|
+
},
|
|
112
|
+
children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
113
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, color: "text.secondary", mb: 1 }, children: "EC2 Instances" }),
|
|
114
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 32, fontWeight: 600 }, children: [
|
|
115
|
+
"$",
|
|
116
|
+
instancesCost.toFixed(2)
|
|
117
|
+
] })
|
|
118
|
+
] })
|
|
119
|
+
}
|
|
120
|
+
) }),
|
|
121
|
+
/* @__PURE__ */ jsx(Grid2, { size: { sm: 12, md: 3 }, children: /* @__PURE__ */ jsx(
|
|
122
|
+
Card,
|
|
123
|
+
{
|
|
124
|
+
sx: {
|
|
125
|
+
height: "100%",
|
|
126
|
+
position: "relative",
|
|
127
|
+
bgcolor: "background.paper"
|
|
128
|
+
},
|
|
129
|
+
children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
130
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, color: "text.secondary", mb: 1 }, children: "Volumes" }),
|
|
131
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 32, fontWeight: 600 }, children: [
|
|
132
|
+
"$",
|
|
133
|
+
volumeCost.toFixed(2)
|
|
134
|
+
] })
|
|
135
|
+
] })
|
|
136
|
+
}
|
|
137
|
+
) }),
|
|
138
|
+
/* @__PURE__ */ jsx(Grid2, { size: { sm: 12, md: 3 }, children: /* @__PURE__ */ jsx(
|
|
139
|
+
Card,
|
|
140
|
+
{
|
|
141
|
+
sx: {
|
|
142
|
+
height: "100%",
|
|
143
|
+
position: "relative",
|
|
144
|
+
bgcolor: "background.paper"
|
|
145
|
+
},
|
|
146
|
+
children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
147
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, color: "text.secondary", mb: 1 }, children: "Elastic IP" }),
|
|
148
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 32, fontWeight: 600 }, children: [
|
|
149
|
+
"$",
|
|
150
|
+
elasticIpCost.toFixed(2)
|
|
151
|
+
] })
|
|
152
|
+
] })
|
|
153
|
+
}
|
|
154
|
+
) }),
|
|
155
|
+
/* @__PURE__ */ jsx(Grid2, { size: { sm: 12, md: 3 }, children: /* @__PURE__ */ jsx(
|
|
156
|
+
Card,
|
|
157
|
+
{
|
|
158
|
+
sx: {
|
|
159
|
+
height: "100%",
|
|
160
|
+
position: "relative",
|
|
161
|
+
bgcolor: "background.paper"
|
|
162
|
+
},
|
|
163
|
+
children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
164
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, color: "text.secondary", mb: 1 }, children: "NAT Gateway" }),
|
|
165
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 32, fontWeight: 600 }, children: [
|
|
166
|
+
"$",
|
|
167
|
+
natGatewayCost.toFixed(2)
|
|
168
|
+
] })
|
|
169
|
+
] })
|
|
170
|
+
}
|
|
171
|
+
) }),
|
|
172
|
+
/* @__PURE__ */ jsx(Grid2, { size: { sm: 12, md: 3 }, children: /* @__PURE__ */ jsx(
|
|
173
|
+
Card,
|
|
174
|
+
{
|
|
175
|
+
sx: {
|
|
176
|
+
height: "100%",
|
|
177
|
+
position: "relative",
|
|
178
|
+
bgcolor: "background.paper"
|
|
179
|
+
},
|
|
180
|
+
children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
181
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, color: "text.secondary", mb: 1 }, children: "Data Transfer" }),
|
|
182
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 32, fontWeight: 600 }, children: [
|
|
183
|
+
"$",
|
|
184
|
+
dataTransferCost.toFixed(2)
|
|
185
|
+
] })
|
|
186
|
+
] })
|
|
187
|
+
}
|
|
188
|
+
) }),
|
|
189
|
+
/* @__PURE__ */ jsx(Grid2, { size: { sm: 12, md: 3 }, children: /* @__PURE__ */ jsx(
|
|
190
|
+
Card,
|
|
191
|
+
{
|
|
192
|
+
sx: {
|
|
193
|
+
height: "100%",
|
|
194
|
+
position: "relative",
|
|
195
|
+
bgcolor: "background.paper"
|
|
196
|
+
},
|
|
197
|
+
children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
198
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontSize: 14, color: "text.secondary", mb: 1 }, children: "VPC" }),
|
|
199
|
+
/* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 32, fontWeight: 600 }, children: [
|
|
200
|
+
"$",
|
|
201
|
+
vpcCost.toFixed(2)
|
|
202
|
+
] })
|
|
203
|
+
] })
|
|
204
|
+
}
|
|
205
|
+
) })
|
|
206
|
+
] }),
|
|
207
|
+
predictedMonthEndCost && /* @__PURE__ */ jsx(Box, { sx: { mt: 3 }, children: /* @__PURE__ */ jsx(
|
|
208
|
+
Card,
|
|
209
|
+
{
|
|
210
|
+
sx: {
|
|
211
|
+
backgroundColor: "rgba(33, 150, 243, 0.08)",
|
|
212
|
+
borderLeft: "4px solid",
|
|
213
|
+
borderColor: "info.main"
|
|
214
|
+
},
|
|
215
|
+
children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
216
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", children: "Month-End Forecast" }),
|
|
217
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", sx: { mt: 1 }, children: [
|
|
218
|
+
"Current spend:",
|
|
219
|
+
" ",
|
|
220
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
221
|
+
"$",
|
|
222
|
+
predictedMonthEndCost.current.toFixed(2)
|
|
223
|
+
] })
|
|
224
|
+
] }),
|
|
225
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
|
|
226
|
+
"Predicted month-end:",
|
|
227
|
+
" ",
|
|
228
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
229
|
+
"$",
|
|
230
|
+
predictedMonthEndCost.predicted.toFixed(2)
|
|
231
|
+
] })
|
|
232
|
+
] }),
|
|
233
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
|
|
234
|
+
"Average daily cost: $",
|
|
235
|
+
predictedMonthEndCost.avgDailyCost.toFixed(2),
|
|
236
|
+
" (",
|
|
237
|
+
predictedMonthEndCost.remainingDays,
|
|
238
|
+
" days remaining)"
|
|
239
|
+
] })
|
|
240
|
+
] })
|
|
241
|
+
}
|
|
242
|
+
) }),
|
|
243
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", sx: { pt: 6, pb: 2, fontWeight: 600 }, children: "Current Month Daily Trend" }),
|
|
244
|
+
/* @__PURE__ */ jsx(Box, { sx: { mt: 3, mb: 3 }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 300, children: /* @__PURE__ */ jsxs(LineChart, { data: dailyTrendData, children: [
|
|
245
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "#e0e0e0" }),
|
|
246
|
+
/* @__PURE__ */ jsx(
|
|
247
|
+
XAxis,
|
|
248
|
+
{
|
|
249
|
+
dataKey: "date",
|
|
250
|
+
tick: { fontSize: 12 },
|
|
251
|
+
tickFormatter: (value) => {
|
|
252
|
+
const date = new Date(value);
|
|
253
|
+
return `${date.getMonth() + 1}/${date.getDate()}`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
),
|
|
257
|
+
/* @__PURE__ */ jsx(YAxis, { tick: { fontSize: 12 } }),
|
|
258
|
+
/* @__PURE__ */ jsx(
|
|
259
|
+
Tooltip,
|
|
260
|
+
{
|
|
261
|
+
formatter: (value) => value !== void 0 ? `$${value.toFixed(2)}` : "",
|
|
262
|
+
labelFormatter: (label) => `Date: ${label}`
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
/* @__PURE__ */ jsx(Legend, {}),
|
|
266
|
+
/* @__PURE__ */ jsx(
|
|
267
|
+
Line,
|
|
268
|
+
{
|
|
269
|
+
type: "monotone",
|
|
270
|
+
dataKey: "Instances",
|
|
271
|
+
stroke: "#6ac2e5",
|
|
272
|
+
strokeWidth: 2
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
/* @__PURE__ */ jsx(
|
|
276
|
+
Line,
|
|
277
|
+
{
|
|
278
|
+
type: "monotone",
|
|
279
|
+
dataKey: "Volume",
|
|
280
|
+
stroke: "#1fd9a7",
|
|
281
|
+
strokeWidth: 2
|
|
282
|
+
}
|
|
283
|
+
),
|
|
284
|
+
/* @__PURE__ */ jsx(
|
|
285
|
+
Line,
|
|
286
|
+
{
|
|
287
|
+
type: "monotone",
|
|
288
|
+
dataKey: "Elastic IP",
|
|
289
|
+
stroke: "#f4b630",
|
|
290
|
+
strokeWidth: 2
|
|
291
|
+
}
|
|
292
|
+
),
|
|
293
|
+
/* @__PURE__ */ jsx(
|
|
294
|
+
Line,
|
|
295
|
+
{
|
|
296
|
+
type: "monotone",
|
|
297
|
+
dataKey: "NAT Gateway",
|
|
298
|
+
stroke: "#1976d2",
|
|
299
|
+
strokeWidth: 2
|
|
300
|
+
}
|
|
301
|
+
),
|
|
302
|
+
/* @__PURE__ */ jsx(
|
|
303
|
+
Line,
|
|
304
|
+
{
|
|
305
|
+
type: "monotone",
|
|
306
|
+
dataKey: "Data Transfer",
|
|
307
|
+
stroke: "#603fba",
|
|
308
|
+
strokeWidth: 2
|
|
309
|
+
}
|
|
310
|
+
),
|
|
311
|
+
/* @__PURE__ */ jsx(
|
|
312
|
+
Line,
|
|
313
|
+
{
|
|
314
|
+
type: "monotone",
|
|
315
|
+
dataKey: "VPC",
|
|
316
|
+
stroke: "#aa51f7",
|
|
317
|
+
strokeWidth: 2
|
|
318
|
+
}
|
|
319
|
+
),
|
|
320
|
+
/* @__PURE__ */ jsx(
|
|
321
|
+
Line,
|
|
322
|
+
{
|
|
323
|
+
type: "monotone",
|
|
324
|
+
dataKey: "Total",
|
|
325
|
+
stroke: "#e552d1",
|
|
326
|
+
strokeWidth: 3
|
|
327
|
+
}
|
|
328
|
+
)
|
|
329
|
+
] }) }) }),
|
|
330
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", sx: { pt: 6, pb: 2, fontWeight: 600 }, children: "Top Cost Resources This Month" }),
|
|
331
|
+
/* @__PURE__ */ jsx(TableContainer, { component: Paper, children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
332
|
+
/* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
333
|
+
/* @__PURE__ */ jsx(TableCell, { children: "Rank" }),
|
|
334
|
+
/* @__PURE__ */ jsx(TableCell, { children: "Resource ID" }),
|
|
335
|
+
/* @__PURE__ */ jsx(TableCell, { children: "Type" }),
|
|
336
|
+
/* @__PURE__ */ jsx(TableCell, { align: "right", children: "Cost" }),
|
|
337
|
+
/* @__PURE__ */ jsx(TableCell, { align: "right", children: "% of Total" })
|
|
338
|
+
] }) }),
|
|
339
|
+
/* @__PURE__ */ jsx(TableBody, { children: topResources.map((resource, index) => /* @__PURE__ */ jsxs(
|
|
340
|
+
TableRow,
|
|
341
|
+
{
|
|
342
|
+
sx: { "&:hover": { backgroundColor: "action.hover" } },
|
|
343
|
+
children: [
|
|
344
|
+
/* @__PURE__ */ jsx(TableCell, { children: index + 1 }),
|
|
345
|
+
/* @__PURE__ */ jsx(TableCell, { children: resource.resourceId }),
|
|
346
|
+
/* @__PURE__ */ jsx(TableCell, { children: resource.resourceType === "instance" && resource.instanceType ? resource.instanceType : resource.resourceType === "snapshot" || resource.resourceType === "volume" ? resource.volumeType ? `${resource.resourceType} (${resource.volumeType})` : resource.resourceType : resource.resourceType }),
|
|
347
|
+
/* @__PURE__ */ jsx(TableCell, { align: "right", children: /* @__PURE__ */ jsxs("strong", { children: [
|
|
348
|
+
"$",
|
|
349
|
+
resource.totalCost.toFixed(2)
|
|
350
|
+
] }) }),
|
|
351
|
+
/* @__PURE__ */ jsxs(TableCell, { align: "right", children: [
|
|
352
|
+
resource.percentage.toFixed(1),
|
|
353
|
+
"%"
|
|
354
|
+
] })
|
|
355
|
+
]
|
|
356
|
+
},
|
|
357
|
+
resource.resourceId
|
|
358
|
+
)) })
|
|
359
|
+
] }) }),
|
|
360
|
+
monthlyData.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
361
|
+
/* @__PURE__ */ jsxs(Box, { sx: { pt: 6, pb: 2, display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
362
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", sx: { fontWeight: 600 }, children: "Monthly Comparison" }),
|
|
363
|
+
/* @__PURE__ */ jsxs(ButtonGroup, { size: "small", variant: "outlined", children: [
|
|
364
|
+
/* @__PURE__ */ jsx(
|
|
365
|
+
Button,
|
|
366
|
+
{
|
|
367
|
+
variant: monthsToShow === 6 ? "contained" : "outlined",
|
|
368
|
+
onClick: () => setMonthsToShow(6),
|
|
369
|
+
children: "6 Months"
|
|
370
|
+
}
|
|
371
|
+
),
|
|
372
|
+
/* @__PURE__ */ jsx(
|
|
373
|
+
Button,
|
|
374
|
+
{
|
|
375
|
+
variant: monthsToShow === 12 ? "contained" : "outlined",
|
|
376
|
+
onClick: () => setMonthsToShow(12),
|
|
377
|
+
children: "12 Months"
|
|
378
|
+
}
|
|
379
|
+
)
|
|
380
|
+
] })
|
|
381
|
+
] }),
|
|
382
|
+
/* @__PURE__ */ jsx(Box, { sx: { mt: 3, mb: 3 }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 300, children: /* @__PURE__ */ jsxs(BarChart, { data: monthlyData.slice(-monthsToShow), children: [
|
|
383
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "#e0e0e0" }),
|
|
384
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: "month", tick: { fontSize: 12 } }),
|
|
385
|
+
/* @__PURE__ */ jsx(YAxis, { tick: { fontSize: 12 } }),
|
|
386
|
+
/* @__PURE__ */ jsx(
|
|
387
|
+
Tooltip,
|
|
388
|
+
{
|
|
389
|
+
formatter: (value) => value !== void 0 ? `$${value.toFixed(2)}` : ""
|
|
390
|
+
}
|
|
391
|
+
),
|
|
392
|
+
/* @__PURE__ */ jsx(Legend, {}),
|
|
393
|
+
/* @__PURE__ */ jsx(
|
|
394
|
+
Bar,
|
|
395
|
+
{
|
|
396
|
+
dataKey: "instances",
|
|
397
|
+
stackId: "a",
|
|
398
|
+
fill: "#6ac2e5",
|
|
399
|
+
name: "Instances"
|
|
400
|
+
}
|
|
401
|
+
),
|
|
402
|
+
/* @__PURE__ */ jsx(
|
|
403
|
+
Bar,
|
|
404
|
+
{
|
|
405
|
+
dataKey: "volume",
|
|
406
|
+
stackId: "a",
|
|
407
|
+
fill: "#1fd9a7",
|
|
408
|
+
name: "Volume"
|
|
409
|
+
}
|
|
410
|
+
),
|
|
411
|
+
/* @__PURE__ */ jsx(
|
|
412
|
+
Bar,
|
|
413
|
+
{
|
|
414
|
+
dataKey: "elasticIp",
|
|
415
|
+
stackId: "a",
|
|
416
|
+
fill: "#f4b630",
|
|
417
|
+
name: "Elastic IP"
|
|
418
|
+
}
|
|
419
|
+
),
|
|
420
|
+
/* @__PURE__ */ jsx(
|
|
421
|
+
Bar,
|
|
422
|
+
{
|
|
423
|
+
dataKey: "natGateway",
|
|
424
|
+
stackId: "a",
|
|
425
|
+
fill: "#1976d2",
|
|
426
|
+
name: "NAT Gateway"
|
|
427
|
+
}
|
|
428
|
+
),
|
|
429
|
+
/* @__PURE__ */ jsx(
|
|
430
|
+
Bar,
|
|
431
|
+
{
|
|
432
|
+
dataKey: "dataTransfer",
|
|
433
|
+
stackId: "a",
|
|
434
|
+
fill: "#603fba",
|
|
435
|
+
name: "Data Transfer"
|
|
436
|
+
}
|
|
437
|
+
),
|
|
438
|
+
/* @__PURE__ */ jsx(Bar, { dataKey: "vpc", stackId: "a", fill: "#aa51f7", name: "VPC" })
|
|
439
|
+
] }) }) })
|
|
440
|
+
] })
|
|
441
|
+
] });
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export { EC2Overview };
|
|
445
|
+
//# sourceMappingURL=EC2Overview.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EC2Overview.esm.js","sources":["../../src/components/EC2Overview.tsx"],"sourcesContent":["import {\n Box,\n Button,\n ButtonGroup,\n Card,\n CardContent,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material';\nimport Grid2 from '@mui/material/Grid2';\nimport { useMemo, useState } from 'react';\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Legend,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport { DateTime } from 'luxon';\n\ninterface EC2Resource {\n resourceId: string;\n resourceType:\n | 'instance'\n | 'elastic-ip'\n | 'other'\n | 'snapshot'\n | 'volume'\n | 'nat-gateway'\n | 'data-transfer'\n | 'vpc';\n instanceType?: string;\n volumeType?: string;\n totalCost: number;\n usageAmount: number;\n dailyCosts: Array<{ date: string; cost: number }>;\n}\n\ninterface EC2OverviewProps {\n resources: EC2Resource[];\n monthlyData?: Array<{\n month: string;\n instances: number;\n volume: number;\n elasticIp: number;\n natGateway: number;\n dataTransfer: number;\n vpc: number;\n total: number;\n }>;\n startDate?: DateTime | null;\n endDate?: DateTime | null;\n}\n\nexport function EC2Overview({ resources, monthlyData = [], startDate, endDate }: EC2OverviewProps) {\n const [monthsToShow, setMonthsToShow] = useState<6 | 12>(6);\n\n const instancesCost = resources\n .filter(r => r.resourceType === 'instance')\n .reduce((sum, r) => sum + r.totalCost, 0);\n\n const volumeCost = resources\n .filter(r => r.resourceType === 'snapshot' || r.resourceType === 'volume')\n .reduce((sum, r) => sum + r.totalCost, 0);\n\n const elasticIpCost = resources\n .filter(r => r.resourceType === 'elastic-ip')\n .reduce((sum, r) => sum + r.totalCost, 0);\n\n const natGatewayCost = resources\n .filter(r => r.resourceType === 'nat-gateway')\n .reduce((sum, r) => sum + r.totalCost, 0);\n\n const dataTransferCost = resources\n .filter(r => r.resourceType === 'data-transfer')\n .reduce((sum, r) => sum + r.totalCost, 0);\n\n const vpcCost = resources\n .filter(r => r.resourceType === 'vpc')\n .reduce((sum, r) => sum + r.totalCost, 0);\n\n const totalCost =\n instancesCost +\n volumeCost +\n elasticIpCost +\n natGatewayCost +\n dataTransferCost +\n vpcCost;\n\n\n const dailyTrendData = useMemo(() => {\n const dailyMap = new Map<\n string,\n {\n instances: number;\n volume: number;\n elasticIp: number;\n natGateway: number;\n dataTransfer: number;\n vpc: number;\n }\n >();\n\n resources.forEach(resource => {\n resource.dailyCosts.forEach(({ date, cost }) => {\n if (!dailyMap.has(date)) {\n dailyMap.set(date, {\n instances: 0,\n volume: 0,\n elasticIp: 0,\n natGateway: 0,\n dataTransfer: 0,\n vpc: 0,\n });\n }\n\n const entry = dailyMap.get(date) as {\n instances: number;\n volume: number;\n elasticIp: number;\n natGateway: number;\n dataTransfer: number;\n vpc: number;\n };\n if (resource.resourceType === 'instance') {\n entry.instances += cost;\n } else if (\n resource.resourceType === 'snapshot' ||\n resource.resourceType === 'volume'\n ) {\n entry.volume += cost;\n } else if (resource.resourceType === 'elastic-ip') {\n entry.elasticIp += cost;\n } else if (resource.resourceType === 'nat-gateway') {\n entry.natGateway += cost;\n } else if (resource.resourceType === 'data-transfer') {\n entry.dataTransfer += cost;\n } else if (resource.resourceType === 'vpc') {\n entry.vpc += cost;\n }\n });\n });\n\n return Array.from(dailyMap.entries())\n .map(([date, costs]) => ({\n date,\n Instances: costs.instances,\n Volume: costs.volume,\n 'Elastic IP': costs.elasticIp,\n 'NAT Gateway': costs.natGateway,\n 'Data Transfer': costs.dataTransfer,\n VPC: costs.vpc,\n Total:\n costs.instances +\n costs.volume +\n costs.elasticIp +\n costs.natGateway +\n costs.dataTransfer +\n costs.vpc,\n }))\n .sort((a, b) => a.date.localeCompare(b.date));\n }, [resources]);\n\n const topResources = useMemo(() => {\n return [...resources]\n .sort((a, b) => b.totalCost - a.totalCost)\n .slice(0, 5)\n .map(resource => ({\n ...resource,\n percentage: totalCost > 0 ? (resource.totalCost / totalCost) * 100 : 0,\n }));\n }, [resources, totalCost]);\n\n const predictedMonthEndCost = useMemo(() => {\n if (dailyTrendData.length === 0) return null;\n\n // Only show forecast when the ENTIRE current month is selected\n const now = DateTime.now();\n const currentMonthStart = now.startOf('month');\n\n // Check if startDate and endDate represent the current month period\n if (!startDate || !endDate) return null;\n\n const isCurrentMonthStart = startDate.hasSame(currentMonthStart, 'day');\n const isCurrentMonth = endDate.hasSame(now, 'month') && endDate.hasSame(now, 'year');\n\n if (!isCurrentMonthStart || !isCurrentMonth) return null;\n\n const daysInMonth = now.daysInMonth || 30;\n const currentDay = now.day;\n\n // Avoid division by zero\n if (currentDay <= 0) return null;\n\n const avgDailyCost = totalCost / currentDay;\n\n const remainingDays = daysInMonth - currentDay;\n const predictedTotal = totalCost + avgDailyCost * remainingDays;\n\n return {\n current: totalCost,\n predicted: predictedTotal,\n remainingDays,\n avgDailyCost,\n };\n }, [dailyTrendData, totalCost, startDate, endDate]);\n\n\n return (\n <Box>\n <Grid2 container spacing={3}>\n <Grid2 size={{ sm: 12, md: 3 }}>\n <Card\n sx={{\n height: '100%',\n position: 'relative',\n bgcolor: 'background.paper',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: 14, color: 'text.secondary', mb: 1 }}>\n Total EC2 Cost\n </Typography>\n <Typography sx={{ fontSize: 32, fontWeight: 600 }}>\n ${totalCost.toFixed(2)}\n </Typography>\n </CardContent>\n </Card>\n </Grid2>\n\n <Grid2 size={{ sm: 12, md: 3 }}>\n <Card\n sx={{\n height: '100%',\n position: 'relative',\n bgcolor: 'background.paper',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: 14, color: 'text.secondary', mb: 1 }}>\n EC2 Instances\n </Typography>\n <Typography sx={{ fontSize: 32, fontWeight: 600 }}>\n ${instancesCost.toFixed(2)}\n </Typography>\n </CardContent>\n </Card>\n </Grid2>\n\n <Grid2 size={{ sm: 12, md: 3 }}>\n <Card\n sx={{\n height: '100%',\n position: 'relative',\n bgcolor: 'background.paper',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: 14, color: 'text.secondary', mb: 1 }}>\n Volumes\n </Typography>\n <Typography sx={{ fontSize: 32, fontWeight: 600 }}>\n ${volumeCost.toFixed(2)}\n </Typography>\n </CardContent>\n </Card>\n </Grid2>\n\n <Grid2 size={{ sm: 12, md: 3 }}>\n <Card\n sx={{\n height: '100%',\n position: 'relative',\n bgcolor: 'background.paper',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: 14, color: 'text.secondary', mb: 1 }}>\n Elastic IP\n </Typography>\n <Typography sx={{ fontSize: 32, fontWeight: 600 }}>\n ${elasticIpCost.toFixed(2)}\n </Typography>\n </CardContent>\n </Card>\n </Grid2>\n\n <Grid2 size={{ sm: 12, md: 3 }}>\n <Card\n sx={{\n height: '100%',\n position: 'relative',\n bgcolor: 'background.paper',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: 14, color: 'text.secondary', mb: 1 }}>\n NAT Gateway\n </Typography>\n <Typography sx={{ fontSize: 32, fontWeight: 600 }}>\n ${natGatewayCost.toFixed(2)}\n </Typography>\n </CardContent>\n </Card>\n </Grid2>\n\n <Grid2 size={{ sm: 12, md: 3 }}>\n <Card\n sx={{\n height: '100%',\n position: 'relative',\n bgcolor: 'background.paper',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: 14, color: 'text.secondary', mb: 1 }}>\n Data Transfer\n </Typography>\n <Typography sx={{ fontSize: 32, fontWeight: 600 }}>\n ${dataTransferCost.toFixed(2)}\n </Typography>\n </CardContent>\n </Card>\n </Grid2>\n\n <Grid2 size={{ sm: 12, md: 3 }}>\n <Card\n sx={{\n height: '100%',\n position: 'relative',\n bgcolor: 'background.paper',\n }}\n >\n <CardContent>\n <Typography sx={{ fontSize: 14, color: 'text.secondary', mb: 1 }}>\n VPC\n </Typography>\n <Typography sx={{ fontSize: 32, fontWeight: 600 }}>\n ${vpcCost.toFixed(2)}\n </Typography>\n </CardContent>\n </Card>\n </Grid2>\n </Grid2>\n\n {predictedMonthEndCost && (\n <Box sx={{ mt: 3 }}>\n <Card\n sx={{\n backgroundColor: 'rgba(33, 150, 243, 0.08)',\n borderLeft: '4px solid',\n borderColor: 'info.main',\n }}\n >\n <CardContent>\n <Typography variant=\"h6\">Month-End Forecast</Typography>\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n Current spend:{' '}\n <strong>${predictedMonthEndCost.current.toFixed(2)}</strong>\n </Typography>\n <Typography variant=\"body2\">\n Predicted month-end:{' '}\n <strong>${predictedMonthEndCost.predicted.toFixed(2)}</strong>\n </Typography>\n <Typography variant=\"body2\">\n Average daily cost: $\n {predictedMonthEndCost.avgDailyCost.toFixed(2)} (\n {predictedMonthEndCost.remainingDays} days remaining)\n </Typography>\n </CardContent>\n </Card>\n </Box>\n )}\n\n <Typography variant=\"h6\" sx={{ pt: 6, pb: 2, fontWeight: 600 }}>\n Current Month Daily Trend\n </Typography>\n <Box sx={{ mt: 3, mb: 3 }}>\n <ResponsiveContainer width=\"100%\" height={300}>\n <LineChart data={dailyTrendData}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"#e0e0e0\" />\n <XAxis\n dataKey=\"date\"\n tick={{ fontSize: 12 }}\n tickFormatter={value => {\n const date = new Date(value);\n return `${date.getMonth() + 1}/${date.getDate()}`;\n }}\n />\n <YAxis tick={{ fontSize: 12 }} />\n <Tooltip\n formatter={(value: number | undefined) =>\n value !== undefined ? `$${value.toFixed(2)}` : ''\n }\n labelFormatter={label => `Date: ${label}`}\n />\n <Legend />\n <Line\n type=\"monotone\"\n dataKey=\"Instances\"\n stroke=\"#6ac2e5\"\n strokeWidth={2}\n />\n <Line\n type=\"monotone\"\n dataKey=\"Volume\"\n stroke=\"#1fd9a7\"\n strokeWidth={2}\n />\n <Line\n type=\"monotone\"\n dataKey=\"Elastic IP\"\n stroke=\"#f4b630\"\n strokeWidth={2}\n />\n <Line\n type=\"monotone\"\n dataKey=\"NAT Gateway\"\n stroke=\"#1976d2\"\n strokeWidth={2}\n />\n <Line\n type=\"monotone\"\n dataKey=\"Data Transfer\"\n stroke=\"#603fba\"\n strokeWidth={2}\n />\n <Line\n type=\"monotone\"\n dataKey=\"VPC\"\n stroke=\"#aa51f7\"\n strokeWidth={2}\n />\n <Line\n type=\"monotone\"\n dataKey=\"Total\"\n stroke=\"#e552d1\"\n strokeWidth={3}\n />\n </LineChart>\n </ResponsiveContainer>\n </Box>\n\n <Typography variant=\"h6\" sx={{ pt: 6, pb: 2, fontWeight: 600 }}>\n Top Cost Resources This Month\n </Typography>\n <TableContainer component={Paper}>\n <Table>\n <TableHead>\n <TableRow>\n <TableCell>Rank</TableCell>\n <TableCell>Resource ID</TableCell>\n <TableCell>Type</TableCell>\n <TableCell align=\"right\">Cost</TableCell>\n <TableCell align=\"right\">% of Total</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {topResources.map((resource, index) => (\n <TableRow\n key={resource.resourceId}\n sx={{ '&:hover': { backgroundColor: 'action.hover' } }}\n >\n <TableCell>{index + 1}</TableCell>\n <TableCell>{resource.resourceId}</TableCell>\n <TableCell>\n {resource.resourceType === 'instance' && resource.instanceType\n ? resource.instanceType\n : resource.resourceType === 'snapshot' ||\n resource.resourceType === 'volume'\n ? resource.volumeType\n ? `${resource.resourceType} (${resource.volumeType})`\n : resource.resourceType\n : resource.resourceType}\n </TableCell>\n <TableCell align=\"right\">\n <strong>${resource.totalCost.toFixed(2)}</strong>\n </TableCell>\n <TableCell align=\"right\">\n {resource.percentage.toFixed(1)}%\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n\n {monthlyData.length > 0 && (\n <>\n <Box sx={{ pt: 6, pb: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <Typography variant=\"h6\" sx={{ fontWeight: 600 }}>\n Monthly Comparison\n </Typography>\n <ButtonGroup size=\"small\" variant=\"outlined\">\n <Button\n variant={monthsToShow === 6 ? 'contained' : 'outlined'}\n onClick={() => setMonthsToShow(6)}\n >\n 6 Months\n </Button>\n <Button\n variant={monthsToShow === 12 ? 'contained' : 'outlined'}\n onClick={() => setMonthsToShow(12)}\n >\n 12 Months\n </Button>\n </ButtonGroup>\n </Box>\n <Box sx={{ mt: 3, mb: 3 }}>\n <ResponsiveContainer width=\"100%\" height={300}>\n <BarChart data={monthlyData.slice(-monthsToShow)}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"#e0e0e0\" />\n <XAxis dataKey=\"month\" tick={{ fontSize: 12 }} />\n <YAxis tick={{ fontSize: 12 }} />\n <Tooltip\n formatter={(value: number | undefined) =>\n value !== undefined ? `$${value.toFixed(2)}` : ''\n }\n />\n <Legend />\n <Bar\n dataKey=\"instances\"\n stackId=\"a\"\n fill=\"#6ac2e5\"\n name=\"Instances\"\n />\n <Bar\n dataKey=\"volume\"\n stackId=\"a\"\n fill=\"#1fd9a7\"\n name=\"Volume\"\n />\n <Bar\n dataKey=\"elasticIp\"\n stackId=\"a\"\n fill=\"#f4b630\"\n name=\"Elastic IP\"\n />\n <Bar\n dataKey=\"natGateway\"\n stackId=\"a\"\n fill=\"#1976d2\"\n name=\"NAT Gateway\"\n />\n <Bar\n dataKey=\"dataTransfer\"\n stackId=\"a\"\n fill=\"#603fba\"\n name=\"Data Transfer\"\n />\n <Bar dataKey=\"vpc\" stackId=\"a\" fill=\"#aa51f7\" name=\"VPC\" />\n </BarChart>\n </ResponsiveContainer>\n </Box>\n </>\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
|