@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.
@@ -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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"}
@@ -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,8 @@
1
+ import { createRouteRef } from '@backstage/core-plugin-api';
2
+
3
+ const rootRouteRef = createRouteRef({
4
+ id: "cost-insights"
5
+ });
6
+
7
+ export { rootRouteRef };
8
+ //# sourceMappingURL=routes.esm.js.map
@@ -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
+ }