@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,193 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|
3
|
+
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
|
4
|
+
import { Box, Typography, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, IconButton, Collapse, Chip } from '@mui/material';
|
|
5
|
+
import Grid2 from '@mui/material/Grid2';
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
import { ResponsiveContainer, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, Line } from 'recharts';
|
|
8
|
+
|
|
9
|
+
function EC2ResourceRow({ resource }) {
|
|
10
|
+
const [open, setOpen] = useState(false);
|
|
11
|
+
const maxCost = Math.max(...resource.dailyCosts.map((d) => d.cost), 1);
|
|
12
|
+
const avgCost = resource.dailyCosts.length > 0 ? resource.dailyCosts.reduce((sum, d) => sum + d.cost, 0) / resource.dailyCosts.length : 0;
|
|
13
|
+
const peakCost = Math.max(...resource.dailyCosts.map((d) => d.cost), 0);
|
|
14
|
+
const peakDate = resource.dailyCosts.find((d) => d.cost === peakCost)?.date || "";
|
|
15
|
+
const getResourceTypeLabel = () => {
|
|
16
|
+
if (resource.resourceType === "instance" && resource.instanceType) {
|
|
17
|
+
return resource.instanceType;
|
|
18
|
+
}
|
|
19
|
+
if (resource.resourceType === "snapshot" || resource.resourceType === "volume") {
|
|
20
|
+
return resource.volumeType ? `${resource.resourceType} (${resource.volumeType})` : resource.resourceType;
|
|
21
|
+
}
|
|
22
|
+
return resource.resourceType || "N/A";
|
|
23
|
+
};
|
|
24
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
|
+
/* @__PURE__ */ jsxs(TableRow, { children: [
|
|
26
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => setOpen(!open), children: open ? /* @__PURE__ */ jsx(KeyboardArrowUpIcon, {}) : /* @__PURE__ */ jsx(KeyboardArrowDownIcon, {}) }) }),
|
|
27
|
+
/* @__PURE__ */ jsx(TableCell, { children: resource.resourceId }),
|
|
28
|
+
/* @__PURE__ */ jsx(TableCell, { children: getResourceTypeLabel() }),
|
|
29
|
+
/* @__PURE__ */ jsx(TableCell, { align: "right", children: /* @__PURE__ */ jsxs("strong", { children: [
|
|
30
|
+
"$",
|
|
31
|
+
resource.totalCost.toFixed(2)
|
|
32
|
+
] }) })
|
|
33
|
+
] }),
|
|
34
|
+
/* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { style: { paddingBottom: 0, paddingTop: 0 }, colSpan: 4, children: /* @__PURE__ */ jsx(Collapse, { in: open, timeout: "auto", unmountOnExit: true, children: /* @__PURE__ */ jsx(Box, { sx: { p: 4, m: 2, backgroundColor: "background.default", borderRadius: 1 }, children: /* @__PURE__ */ jsxs(Grid2, { container: true, spacing: 3, children: [
|
|
35
|
+
/* @__PURE__ */ jsxs(Grid2, { size: { sm: 12, md: 4 }, children: [
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
Typography,
|
|
38
|
+
{
|
|
39
|
+
variant: "h6",
|
|
40
|
+
sx: { mb: 2, fontWeight: 600 },
|
|
41
|
+
children: "Resource Info"
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 2, children: [
|
|
45
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "textSecondary", children: [
|
|
46
|
+
"Usage: ",
|
|
47
|
+
resource.usageAmount.toFixed(2),
|
|
48
|
+
" ",
|
|
49
|
+
resource.resourceType === "instance" ? "hours" : "GB-month"
|
|
50
|
+
] }),
|
|
51
|
+
/* @__PURE__ */ jsxs(
|
|
52
|
+
Typography,
|
|
53
|
+
{
|
|
54
|
+
variant: "body2",
|
|
55
|
+
color: "textSecondary",
|
|
56
|
+
style: { marginTop: 8 },
|
|
57
|
+
children: [
|
|
58
|
+
"Total Cost:",
|
|
59
|
+
" ",
|
|
60
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
61
|
+
"$",
|
|
62
|
+
resource.totalCost.toFixed(2)
|
|
63
|
+
] })
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
/* @__PURE__ */ jsxs(
|
|
68
|
+
Typography,
|
|
69
|
+
{
|
|
70
|
+
variant: "body2",
|
|
71
|
+
color: "textSecondary",
|
|
72
|
+
style: { marginTop: 8 },
|
|
73
|
+
children: [
|
|
74
|
+
"Type: ",
|
|
75
|
+
getResourceTypeLabel()
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
] })
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ jsxs(Grid2, { size: { sm: 12, md: 8 }, children: [
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
Typography,
|
|
84
|
+
{
|
|
85
|
+
variant: "h6",
|
|
86
|
+
sx: { mb: 2, fontWeight: 600 },
|
|
87
|
+
children: "Daily Cost Trend"
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
resource.dailyCosts.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
91
|
+
/* @__PURE__ */ jsx(Box, { sx: { mt: 2, mb: 2 }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 150, children: /* @__PURE__ */ jsxs(LineChart, { data: resource.dailyCosts, children: [
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
CartesianGrid,
|
|
94
|
+
{
|
|
95
|
+
strokeDasharray: "3 3",
|
|
96
|
+
stroke: "#e0e0e0"
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
/* @__PURE__ */ jsx(
|
|
100
|
+
XAxis,
|
|
101
|
+
{
|
|
102
|
+
dataKey: "date",
|
|
103
|
+
tick: { fontSize: 12 },
|
|
104
|
+
tickFormatter: (value) => {
|
|
105
|
+
const date = new Date(value);
|
|
106
|
+
return `${date.getMonth() + 1}/${date.getDate()}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
YAxis,
|
|
112
|
+
{
|
|
113
|
+
tick: { fontSize: 12 },
|
|
114
|
+
domain: [0, maxCost * 1.1]
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
/* @__PURE__ */ jsx(
|
|
118
|
+
Tooltip,
|
|
119
|
+
{
|
|
120
|
+
formatter: (value) => value !== void 0 ? `$${value.toFixed(2)}` : "",
|
|
121
|
+
labelFormatter: (label) => `Date: ${label}`
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
Line,
|
|
126
|
+
{
|
|
127
|
+
type: "monotone",
|
|
128
|
+
dataKey: "cost",
|
|
129
|
+
stroke: "#1fd9a7",
|
|
130
|
+
strokeWidth: 2,
|
|
131
|
+
dot: { r: 2 }
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
] }) }) }),
|
|
135
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
136
|
+
/* @__PURE__ */ jsx(
|
|
137
|
+
Chip,
|
|
138
|
+
{
|
|
139
|
+
label: `Avg: $${avgCost.toFixed(2)}/day`,
|
|
140
|
+
size: "small",
|
|
141
|
+
sx: { mr: 1 }
|
|
142
|
+
}
|
|
143
|
+
),
|
|
144
|
+
/* @__PURE__ */ jsx(
|
|
145
|
+
Chip,
|
|
146
|
+
{
|
|
147
|
+
label: `Peak: $${peakCost.toFixed(2)} (${peakDate})`,
|
|
148
|
+
size: "small",
|
|
149
|
+
color: "secondary",
|
|
150
|
+
sx: { mr: 1 }
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
] })
|
|
154
|
+
] }) : /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "textSecondary", children: "No daily cost data available" })
|
|
155
|
+
] })
|
|
156
|
+
] }) }) }) }) })
|
|
157
|
+
] });
|
|
158
|
+
}
|
|
159
|
+
function EC2ResourceTable({
|
|
160
|
+
resources,
|
|
161
|
+
title = "EC2 Resources"
|
|
162
|
+
}) {
|
|
163
|
+
const totalCost = resources.reduce((sum, r) => sum + r.totalCost, 0);
|
|
164
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { mt: 2 }, children: [
|
|
165
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 2, children: [
|
|
166
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "h6", children: [
|
|
167
|
+
title,
|
|
168
|
+
" (",
|
|
169
|
+
resources.length,
|
|
170
|
+
")"
|
|
171
|
+
] }),
|
|
172
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "textSecondary", children: [
|
|
173
|
+
"Total Cost: ",
|
|
174
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
175
|
+
"$",
|
|
176
|
+
totalCost.toFixed(2)
|
|
177
|
+
] })
|
|
178
|
+
] })
|
|
179
|
+
] }),
|
|
180
|
+
/* @__PURE__ */ jsx(TableContainer, { component: Paper, children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
181
|
+
/* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
182
|
+
/* @__PURE__ */ jsx(TableCell, { style: { width: 50 } }),
|
|
183
|
+
/* @__PURE__ */ jsx(TableCell, { children: "Resource ID" }),
|
|
184
|
+
/* @__PURE__ */ jsx(TableCell, { children: "Type" }),
|
|
185
|
+
/* @__PURE__ */ jsx(TableCell, { align: "right", children: "Total Cost" })
|
|
186
|
+
] }) }),
|
|
187
|
+
/* @__PURE__ */ jsx(TableBody, { children: resources.map((resource) => /* @__PURE__ */ jsx(EC2ResourceRow, { resource }, resource.resourceId)) })
|
|
188
|
+
] }) })
|
|
189
|
+
] });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export { EC2ResourceTable };
|
|
193
|
+
//# sourceMappingURL=EC2ResourceTable.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EC2ResourceTable.esm.js","sources":["../../src/components/EC2ResourceTable.tsx"],"sourcesContent":["import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';\nimport KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';\nimport {\n Box,\n Chip,\n Collapse,\n IconButton,\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 { useState } from 'react';\nimport {\n CartesianGrid,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\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<{\n date: string;\n cost: number;\n }>;\n}\n\ninterface EC2ResourceRowProps {\n resource: EC2Resource;\n}\n\nfunction EC2ResourceRow({ resource }: EC2ResourceRowProps) {\n const [open, setOpen] = useState(false);\n\n const maxCost = Math.max(...resource.dailyCosts.map(d => d.cost), 1);\n const avgCost =\n resource.dailyCosts.length > 0\n ? resource.dailyCosts.reduce((sum, d) => sum + d.cost, 0) /\n resource.dailyCosts.length\n : 0;\n const peakCost = Math.max(...resource.dailyCosts.map(d => d.cost), 0);\n const peakDate =\n resource.dailyCosts.find(d => d.cost === peakCost)?.date || '';\n\n const getResourceTypeLabel = () => {\n if (resource.resourceType === 'instance' && resource.instanceType) {\n return resource.instanceType;\n }\n if (\n resource.resourceType === 'snapshot' ||\n resource.resourceType === 'volume'\n ) {\n return resource.volumeType\n ? `${resource.resourceType} (${resource.volumeType})`\n : resource.resourceType;\n }\n return resource.resourceType || 'N/A';\n };\n\n return (\n <>\n <TableRow>\n <TableCell>\n <IconButton size=\"small\" onClick={() => setOpen(!open)}>\n {open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}\n </IconButton>\n </TableCell>\n <TableCell>{resource.resourceId}</TableCell>\n <TableCell>{getResourceTypeLabel()}</TableCell>\n <TableCell align=\"right\">\n <strong>${resource.totalCost.toFixed(2)}</strong>\n </TableCell>\n </TableRow>\n <TableRow>\n <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={4}>\n <Collapse in={open} timeout=\"auto\" unmountOnExit>\n <Box sx={{ p: 4, m: 2, backgroundColor: 'background.default', borderRadius: 1 }}>\n <Grid2 container spacing={3}>\n <Grid2 size={{ sm: 12, md: 4 }}>\n <Typography\n variant=\"h6\"\n sx={{ mb: 2, fontWeight: 600 }}\n >\n Resource Info\n </Typography>\n <Box marginTop={2}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Usage: {resource.usageAmount.toFixed(2)}{' '}\n {resource.resourceType === 'instance'\n ? 'hours'\n : 'GB-month'}\n </Typography>\n <Typography\n variant=\"body2\"\n color=\"textSecondary\"\n style={{ marginTop: 8 }}\n >\n Total Cost:{' '}\n <strong>${resource.totalCost.toFixed(2)}</strong>\n </Typography>\n <Typography\n variant=\"body2\"\n color=\"textSecondary\"\n style={{ marginTop: 8 }}\n >\n Type: {getResourceTypeLabel()}\n </Typography>\n </Box>\n </Grid2>\n\n <Grid2 size={{ sm: 12, md: 8 }}>\n <Typography\n variant=\"h6\"\n sx={{ mb: 2, fontWeight: 600 }}\n >\n Daily Cost Trend\n </Typography>\n {resource.dailyCosts.length > 0 ? (\n <>\n <Box sx={{ mt: 2, mb: 2 }}>\n <ResponsiveContainer width=\"100%\" height={150}>\n <LineChart data={resource.dailyCosts}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"#e0e0e0\"\n />\n <XAxis\n dataKey=\"date\"\n tick={{ fontSize: 12 }}\n tickFormatter={value => {\n const date = new Date(value);\n return `${\n date.getMonth() + 1\n }/${date.getDate()}`;\n }}\n />\n <YAxis\n tick={{ fontSize: 12 }}\n domain={[0, maxCost * 1.1]}\n />\n <Tooltip\n formatter={(value: number | undefined) =>\n value !== undefined\n ? `$${value.toFixed(2)}`\n : ''\n }\n labelFormatter={label => `Date: ${label}`}\n />\n <Line\n type=\"monotone\"\n dataKey=\"cost\"\n stroke=\"#1fd9a7\"\n strokeWidth={2}\n dot={{ r: 2 }}\n />\n </LineChart>\n </ResponsiveContainer>\n </Box>\n <Box>\n <Chip\n label={`Avg: $${avgCost.toFixed(2)}/day`}\n size=\"small\"\n sx={{ mr: 1 }}\n />\n <Chip\n label={`Peak: $${peakCost.toFixed(2)} (${peakDate})`}\n size=\"small\"\n color=\"secondary\"\n sx={{ mr: 1 }}\n />\n </Box>\n </>\n ) : (\n <Typography variant=\"body2\" color=\"textSecondary\">\n No daily cost data available\n </Typography>\n )}\n </Grid2>\n </Grid2>\n </Box>\n </Collapse>\n </TableCell>\n </TableRow>\n </>\n );\n}\n\ninterface EC2ResourceTableProps {\n resources: EC2Resource[];\n title?: string;\n}\n\nexport function EC2ResourceTable({\n resources,\n title = 'EC2 Resources',\n}: EC2ResourceTableProps) {\n const totalCost = resources.reduce((sum, r) => sum + r.totalCost, 0);\n\n return (\n <Box sx={{ mt: 2 }}>\n <Box marginBottom={2}>\n <Typography variant=\"h6\">\n {title} ({resources.length})\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Total Cost: <strong>${totalCost.toFixed(2)}</strong>\n </Typography>\n </Box>\n\n <TableContainer component={Paper}>\n <Table>\n <TableHead>\n <TableRow>\n <TableCell style={{ width: 50 }} />\n <TableCell>Resource ID</TableCell>\n <TableCell>Type</TableCell>\n <TableCell align=\"right\">Total Cost</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {resources.map(resource => (\n <EC2ResourceRow key={resource.resourceId} resource={resource} />\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
3
|
+
|
|
4
|
+
declare function EC2CostPage(): react_jsx_runtime.JSX.Element;
|
|
5
|
+
|
|
6
|
+
declare const costInsightsPlugin: _backstage_core_plugin_api.BackstagePlugin<{
|
|
7
|
+
root: _backstage_core_plugin_api.RouteRef<undefined>;
|
|
8
|
+
}, {}, {}>;
|
|
9
|
+
declare const CostInsightsPage: typeof EC2CostPage;
|
|
10
|
+
|
|
11
|
+
interface EC2Resource$1 {
|
|
12
|
+
resourceId: string;
|
|
13
|
+
resourceType: 'instance' | 'elastic-ip' | 'other' | 'snapshot' | 'volume' | 'nat-gateway' | 'data-transfer' | 'vpc';
|
|
14
|
+
instanceType?: string;
|
|
15
|
+
volumeType?: string;
|
|
16
|
+
totalCost: number;
|
|
17
|
+
usageAmount: number;
|
|
18
|
+
dailyCosts: Array<{
|
|
19
|
+
date: string;
|
|
20
|
+
cost: number;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
interface EC2OverviewProps {
|
|
24
|
+
resources: EC2Resource$1[];
|
|
25
|
+
monthlyData?: Array<{
|
|
26
|
+
month: string;
|
|
27
|
+
instances: number;
|
|
28
|
+
volume: number;
|
|
29
|
+
elasticIp: number;
|
|
30
|
+
natGateway: number;
|
|
31
|
+
dataTransfer: number;
|
|
32
|
+
vpc: number;
|
|
33
|
+
total: number;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
declare function EC2Overview({ resources, monthlyData }: EC2OverviewProps): react_jsx_runtime.JSX.Element;
|
|
37
|
+
|
|
38
|
+
interface EC2Resource {
|
|
39
|
+
resourceId: string;
|
|
40
|
+
resourceType: 'instance' | 'elastic-ip' | 'other' | 'snapshot' | 'volume' | 'nat-gateway' | 'data-transfer' | 'vpc';
|
|
41
|
+
instanceType?: string;
|
|
42
|
+
volumeType?: string;
|
|
43
|
+
totalCost: number;
|
|
44
|
+
usageAmount: number;
|
|
45
|
+
dailyCosts: Array<{
|
|
46
|
+
date: string;
|
|
47
|
+
cost: number;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
interface EC2ResourceTableProps {
|
|
51
|
+
resources: EC2Resource[];
|
|
52
|
+
title?: string;
|
|
53
|
+
}
|
|
54
|
+
declare function EC2ResourceTable({ resources, title, }: EC2ResourceTableProps): react_jsx_runtime.JSX.Element;
|
|
55
|
+
|
|
56
|
+
export { CostInsightsPage, EC2CostPage, EC2Overview, EC2ResourceTable, costInsightsPlugin };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { CostInsightsPage, costInsightsPlugin } from './plugin.esm.js';
|
|
2
|
+
export { EC2CostPage } from './components/EC2CostPage.esm.js';
|
|
3
|
+
export { EC2Overview } from './components/EC2Overview.esm.js';
|
|
4
|
+
export { EC2ResourceTable } from './components/EC2ResourceTable.esm.js';
|
|
5
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createPlugin, createRoutableExtension } from '@backstage/core-plugin-api';
|
|
2
|
+
import { rootRouteRef } from './routes.esm.js';
|
|
3
|
+
|
|
4
|
+
const costInsightsPlugin = createPlugin({
|
|
5
|
+
id: "cost-insights",
|
|
6
|
+
routes: {
|
|
7
|
+
root: rootRouteRef
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
const CostInsightsPage = costInsightsPlugin.provide(
|
|
11
|
+
createRoutableExtension({
|
|
12
|
+
name: "CostInsightsPage",
|
|
13
|
+
component: () => import('./components/EC2CostPage.esm.js').then((m) => m.EC2CostPage),
|
|
14
|
+
mountPoint: rootRouteRef
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export { CostInsightsPage, costInsightsPlugin };
|
|
19
|
+
//# sourceMappingURL=plugin.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n createRoutableExtension,\n} from '@backstage/core-plugin-api';\n\nimport { rootRouteRef } from './routes';\n\nexport const costInsightsPlugin = createPlugin({\n id: 'cost-insights',\n routes: {\n root: rootRouteRef,\n },\n});\n\nexport const CostInsightsPage = costInsightsPlugin.provide(\n createRoutableExtension({\n name: 'CostInsightsPage',\n component: () =>\n import('./components/EC2CostPage').then(m => m.EC2CostPage),\n mountPoint: rootRouteRef,\n }),\n);\n"],"names":[],"mappings":";;;AAOO,MAAM,qBAAqB,YAAA,CAAa;AAAA,EAC7C,EAAA,EAAI,eAAA;AAAA,EACJ,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM;AAAA;AAEV,CAAC;AAEM,MAAM,mBAAmB,kBAAA,CAAmB,OAAA;AAAA,EACjD,uBAAA,CAAwB;AAAA,IACtB,IAAA,EAAM,kBAAA;AAAA,IACN,SAAA,EAAW,MACT,OAAO,iCAA0B,EAAE,IAAA,CAAK,CAAA,CAAA,KAAK,EAAE,WAAW,CAAA;AAAA,IAC5D,UAAA,EAAY;AAAA,GACb;AACH;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'cost-insights',\n});\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,cAAA,CAAe;AAAA,EACzC,EAAA,EAAI;AACN,CAAC;;;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@letthem/backstage-plugin-cost-insights",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/index.esm.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public",
|
|
9
|
+
"main": "dist/index.esm.js",
|
|
10
|
+
"types": "dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/letthem/backstage-plugin-cost-insights.git",
|
|
15
|
+
"directory": "plugins/cost-insights"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/letthem/backstage-plugin-cost-insights/issues"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"backstage",
|
|
22
|
+
"plugin",
|
|
23
|
+
"cost-insights",
|
|
24
|
+
"aws",
|
|
25
|
+
"ec2",
|
|
26
|
+
"cost-analysis",
|
|
27
|
+
"finops",
|
|
28
|
+
"cloud-cost"
|
|
29
|
+
],
|
|
30
|
+
"homepage": "https://github.com/letthem/backstage-plugin-cost-insights#readme",
|
|
31
|
+
"backstage": {
|
|
32
|
+
"role": "frontend-plugin",
|
|
33
|
+
"pluginId": "cost-insights",
|
|
34
|
+
"pluginPackages": [
|
|
35
|
+
"@letthem/backstage-plugin-cost-insights",
|
|
36
|
+
"@letthem/backstage-plugin-cost-insights-backend"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"sideEffects": false,
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "backstage-cli package build",
|
|
42
|
+
"lint": "backstage-cli package lint",
|
|
43
|
+
"test": "backstage-cli package test",
|
|
44
|
+
"clean": "backstage-cli package clean",
|
|
45
|
+
"prepack": "backstage-cli package prepack",
|
|
46
|
+
"postpack": "backstage-cli package postpack"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@backstage/core-components": "^0.16.3",
|
|
50
|
+
"@backstage/core-plugin-api": "^1.9.4",
|
|
51
|
+
"@mui/icons-material": "^6.1.11",
|
|
52
|
+
"@mui/material": "^6.1.11",
|
|
53
|
+
"@mui/x-date-pickers": "^7.22.2",
|
|
54
|
+
"luxon": "^3.4.4",
|
|
55
|
+
"react-use": "^17.2.4",
|
|
56
|
+
"recharts": "^3.6.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"react": "^18.0.2",
|
|
60
|
+
"react-dom": "^18.0.2",
|
|
61
|
+
"react-router-dom": "^6.3.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@backstage/cli": "^0.29.3",
|
|
65
|
+
"@backstage/core-app-api": "^1.16.0",
|
|
66
|
+
"@backstage/dev-utils": "^1.1.9",
|
|
67
|
+
"@backstage/test-utils": "^1.6.3",
|
|
68
|
+
"@testing-library/jest-dom": "^6.0.0",
|
|
69
|
+
"@testing-library/react": "^14.0.0",
|
|
70
|
+
"@types/luxon": "^3.4.2",
|
|
71
|
+
"@types/node": "^18",
|
|
72
|
+
"@types/react": "^18",
|
|
73
|
+
"react": "^18.0.2",
|
|
74
|
+
"react-dom": "^18.0.2",
|
|
75
|
+
"react-router-dom": "^6.3.0"
|
|
76
|
+
},
|
|
77
|
+
"files": [
|
|
78
|
+
"dist"
|
|
79
|
+
],
|
|
80
|
+
"typesVersions": {
|
|
81
|
+
"*": {
|
|
82
|
+
"index": [
|
|
83
|
+
"dist/index.d.ts"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"module": "./dist/index.esm.js"
|
|
88
|
+
}
|