@backstage-community/plugin-cost-insights 0.27.0 → 0.29.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/CHANGELOG.md +22 -0
- package/dist/alerts/ProjectGrowthAlert.esm.js.map +1 -1
- package/dist/alerts/UnlabeledDataflowAlert.esm.js.map +1 -1
- package/dist/alpha/plugin.esm.js +1 -1
- package/dist/alpha/plugin.esm.js.map +1 -1
- package/dist/alpha/router.esm.js.map +1 -1
- package/dist/alpha.d.ts +31 -10
- package/dist/api/CostInsightsApi.esm.js.map +1 -1
- package/dist/components/ActionItems/ActionItemCard.esm.js.map +1 -1
- package/dist/components/ActionItems/ActionItems.esm.js.map +1 -1
- package/dist/components/AlertInsights/AlertDialog.esm.js.map +1 -1
- package/dist/components/AlertInsights/AlertInsights.esm.js.map +1 -1
- package/dist/components/AlertInsights/AlertInsightsHeader.esm.js.map +1 -1
- package/dist/components/AlertInsights/AlertInsightsSection.esm.js.map +1 -1
- package/dist/components/AlertInsights/AlertInsightsSectionHeader.esm.js.map +1 -1
- package/dist/components/AlertInsights/AlertStatusSummary.esm.js.map +1 -1
- package/dist/components/AlertInsights/AlertStatusSummaryButton.esm.js.map +1 -1
- package/dist/components/AlertInstructionsLayout/AlertInstructionsLayout.esm.js.map +1 -1
- package/dist/components/BarChart/BarChart.esm.js.map +1 -1
- package/dist/components/BarChart/BarChartLabel.esm.js.map +1 -1
- package/dist/components/BarChart/BarChartLegend.esm.js.map +1 -1
- package/dist/components/BarChart/BarChartStepper.esm.js.map +1 -1
- package/dist/components/BarChart/BarChartStepperButton.esm.js.map +1 -1
- package/dist/components/BarChart/BarChartSteps.esm.js.map +1 -1
- package/dist/components/BarChart/BarChartTick.esm.js.map +1 -1
- package/dist/components/BarChart/BarChartTooltip.esm.js.map +1 -1
- package/dist/components/BarChart/BarChartTooltipItem.esm.js.map +1 -1
- package/dist/components/CopyUrlToClipboard/CopyUrlToClipboard.esm.js.map +1 -1
- package/dist/components/CostGrowth/CostGrowth.esm.js.map +1 -1
- package/dist/components/CostGrowth/CostGrowthIndicator.esm.js.map +1 -1
- package/dist/components/CostInsightsHeader/CostInsightsHeader.esm.js.map +1 -1
- package/dist/components/CostInsightsLayout/CostInsightsLayout.esm.js.map +1 -1
- package/dist/components/CostInsightsNavigation/CostInsightsNavigation.esm.js.map +1 -1
- package/dist/components/CostInsightsPage/CostInsightsPage.esm.js.map +1 -1
- package/dist/components/CostInsightsPage/CostInsightsPageRoot.esm.js.map +1 -1
- package/dist/components/CostInsightsPage/CostInsightsThemeProvider.esm.js.map +1 -1
- package/dist/components/CostInsightsPage/index.esm.js +5 -1
- package/dist/components/CostInsightsPage/index.esm.js.map +1 -1
- package/dist/components/CostInsightsPage/selector.esm.js +1 -1
- package/dist/components/CostInsightsPage/selector.esm.js.map +1 -1
- package/dist/components/CostInsightsTabs/CostInsightsTabs.esm.js.map +1 -1
- package/dist/components/CostInsightsTabs/selector.esm.js.map +1 -1
- package/dist/components/CostOverviewCard/CostOverviewBreakdownChart.esm.js.map +1 -1
- package/dist/components/CostOverviewCard/CostOverviewCard.esm.js.map +1 -1
- package/dist/components/CostOverviewCard/CostOverviewChart.esm.js +1 -1
- package/dist/components/CostOverviewCard/CostOverviewChart.esm.js.map +1 -1
- package/dist/components/CostOverviewCard/CostOverviewHeader.esm.js.map +1 -1
- package/dist/components/CostOverviewCard/CostOverviewLegend.esm.js.map +1 -1
- package/dist/components/CostOverviewCard/selector.esm.js.map +1 -1
- package/dist/components/CurrencySelect/CurrencySelect.esm.js.map +1 -1
- package/dist/components/EntityCosts/EntityCosts.esm.js.map +1 -1
- package/dist/components/EntityCosts/index.esm.js +5 -1
- package/dist/components/EntityCosts/index.esm.js.map +1 -1
- package/dist/components/LabelDataflowInstructionsPage/LabelDataflowInstructionsPage.esm.js.map +1 -1
- package/dist/components/LabelDataflowInstructionsPage/index.esm.js +5 -1
- package/dist/components/LabelDataflowInstructionsPage/index.esm.js.map +1 -1
- package/dist/components/LegendItem/LegendItem.esm.js.map +1 -1
- package/dist/components/MetricSelect/MetricSelect.esm.js.map +1 -1
- package/dist/components/PeriodSelect/DateRangePicker.esm.js.map +1 -1
- package/dist/components/ProductInsights/ProductInsights.esm.js.map +1 -1
- package/dist/components/ProductInsightsCard/ProductEntityDialog.esm.js.map +1 -1
- package/dist/components/ProductInsightsCard/ProductEntityTable.esm.js.map +1 -1
- package/dist/components/ProductInsightsCard/ProductInsightsCard.esm.js.map +1 -1
- package/dist/components/ProductInsightsCard/ProductInsightsCardList.esm.js.map +1 -1
- package/dist/components/ProductInsightsCard/ProductInsightsChart.esm.js.map +1 -1
- package/dist/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.esm.js.map +1 -1
- package/dist/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.esm.js.map +1 -1
- package/dist/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.esm.js +0 -1
- package/dist/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.esm.js.map +1 -1
- package/dist/components/ProjectGrowthInstructionsPage/index.esm.js +5 -1
- package/dist/components/ProjectGrowthInstructionsPage/index.esm.js.map +1 -1
- package/dist/components/ProjectSelect/ProjectSelect.esm.js.map +1 -1
- package/dist/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.esm.js.map +1 -1
- package/dist/components/WhyCostsMatter/WhyCostsMatter.esm.js.map +1 -1
- package/dist/example/alerts/KubernetesMigrationAlert.esm.js.map +1 -1
- package/dist/example/client.esm.js +1 -1
- package/dist/example/client.esm.js.map +1 -1
- package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationAlertCard.esm.js.map +1 -1
- package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChart.esm.js.map +1 -1
- package/dist/example/components/KubernetesMigrationAlertCard/KubernetesMigrationBarChartLegend.esm.js.map +1 -1
- package/dist/example/forms/KubernetesMigrationDismissForm.esm.js.map +1 -1
- package/dist/forms/AlertAcceptForm.esm.js.map +1 -1
- package/dist/forms/AlertDismissForm.esm.js.map +1 -1
- package/dist/forms/AlertSnoozeForm.esm.js.map +1 -1
- package/dist/hooks/useConfig.esm.js +2 -2
- package/dist/hooks/useConfig.esm.js.map +1 -1
- package/dist/hooks/useCurrency.esm.js +1 -1
- package/dist/hooks/useCurrency.esm.js.map +1 -1
- package/dist/hooks/useFilters.esm.js +1 -1
- package/dist/hooks/useFilters.esm.js.map +1 -1
- package/dist/hooks/useGroups.esm.js +1 -1
- package/dist/hooks/useGroups.esm.js.map +1 -1
- package/dist/hooks/useLastCompleteBillingDate.esm.js +1 -1
- package/dist/hooks/useLastCompleteBillingDate.esm.js.map +1 -1
- package/dist/hooks/useLoading.esm.js +1 -1
- package/dist/hooks/useLoading.esm.js.map +1 -1
- package/dist/hooks/useScroll.esm.js +1 -1
- package/dist/hooks/useScroll.esm.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/plugin.esm.js +1 -1
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js.map +1 -1
- package/dist/testUtils/filters.esm.js.map +1 -1
- package/dist/testUtils/loading.esm.js.map +1 -1
- package/dist/testUtils/mockData.esm.js.map +1 -1
- package/dist/testUtils/products.esm.js.map +1 -1
- package/dist/testUtils/providers.esm.js.map +1 -1
- package/dist/testUtils/testUtils.esm.js.map +1 -1
- package/dist/types/Alert.esm.js.map +1 -1
- package/dist/types/ChangeStatistic.esm.js.map +1 -1
- package/dist/types/CurrencyType.esm.js.map +1 -1
- package/dist/types/DateFormat.esm.js.map +1 -1
- package/dist/types/Duration.esm.js.map +1 -1
- package/dist/types/Icon.esm.js.map +1 -1
- package/dist/utils/alerts.esm.js.map +1 -1
- package/dist/utils/assert.esm.js.map +1 -1
- package/dist/utils/change.esm.js.map +1 -1
- package/dist/utils/charts.esm.js.map +1 -1
- package/dist/utils/config.esm.js.map +1 -1
- package/dist/utils/currency.esm.js.map +1 -1
- package/dist/utils/duration.esm.js.map +1 -1
- package/dist/utils/filters.esm.js.map +1 -1
- package/dist/utils/formatters.esm.js.map +1 -1
- package/dist/utils/grammar.esm.js.map +1 -1
- package/dist/utils/graphs.esm.js +1 -1
- package/dist/utils/graphs.esm.js.map +1 -1
- package/dist/utils/history.esm.js.map +1 -1
- package/dist/utils/loading.esm.js.map +1 -1
- package/dist/utils/navigation.esm.js.map +1 -1
- package/dist/utils/scroll.esm.js.map +1 -1
- package/dist/utils/sort.esm.js.map +1 -1
- package/dist/utils/styles.esm.js.map +1 -1
- package/dist/utils/sum.esm.js.map +1 -1
- package/package.json +14 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MetricSelect.esm.js","sources":["../../../src/components/MetricSelect/MetricSelect.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ChangeEvent } from 'react';\nimport InputLabel from '@material-ui/core/InputLabel';\nimport FormControl from '@material-ui/core/FormControl';\nimport Select from '@material-ui/core/Select';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport {\n Maybe,\n Metric,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { useSelectStyles as useStyles } from '../../utils/styles';\n\nexport type MetricSelectProps = {\n metric: Maybe<string>;\n metrics: Array<Metric>;\n onSelect: (metric: Maybe<string>) => void;\n};\n\nexport const MetricSelect = ({\n metric,\n metrics,\n onSelect,\n}: MetricSelectProps) => {\n const classes = useStyles();\n\n function onChange(e: ChangeEvent<{ value: unknown }>) {\n if (e.target.value === 'none') {\n onSelect(null);\n } else {\n onSelect(e.target.value as string);\n }\n }\n\n return (\n <FormControl variant=\"outlined\">\n <InputLabel shrink id=\"metric-select-label\">\n Compare to:\n </InputLabel>\n <Select\n id=\"metric-select\"\n labelId=\"metric-select-label\"\n labelWidth={100}\n className={classes.select}\n value={metric ?? 'none'}\n onChange={onChange}\n >\n <MenuItem className={classes.menuItem} key=\"none\" value=\"none\">\n <em>None</em>\n </MenuItem>\n {metrics.map((m: Metric) => (\n <MenuItem className={classes.menuItem} key={m.kind} value={m.kind}>\n <b>{m.name}</b>\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;;;AAiCO,MAAM,eAAe,CAAC;AAAA,EAC3B,MAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,
|
|
1
|
+
{"version":3,"file":"MetricSelect.esm.js","sources":["../../../src/components/MetricSelect/MetricSelect.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ChangeEvent } from 'react';\nimport InputLabel from '@material-ui/core/InputLabel';\nimport FormControl from '@material-ui/core/FormControl';\nimport Select from '@material-ui/core/Select';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport {\n Maybe,\n Metric,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { useSelectStyles as useStyles } from '../../utils/styles';\n\nexport type MetricSelectProps = {\n metric: Maybe<string>;\n metrics: Array<Metric>;\n onSelect: (metric: Maybe<string>) => void;\n};\n\nexport const MetricSelect = ({\n metric,\n metrics,\n onSelect,\n}: MetricSelectProps) => {\n const classes = useStyles();\n\n function onChange(e: ChangeEvent<{ value: unknown }>) {\n if (e.target.value === 'none') {\n onSelect(null);\n } else {\n onSelect(e.target.value as string);\n }\n }\n\n return (\n <FormControl variant=\"outlined\">\n <InputLabel shrink id=\"metric-select-label\">\n Compare to:\n </InputLabel>\n <Select\n id=\"metric-select\"\n labelId=\"metric-select-label\"\n labelWidth={100}\n className={classes.select}\n value={metric ?? 'none'}\n onChange={onChange}\n >\n <MenuItem className={classes.menuItem} key=\"none\" value=\"none\">\n <em>None</em>\n </MenuItem>\n {metrics.map((m: Metric) => (\n <MenuItem className={classes.menuItem} key={m.kind} value={m.kind}>\n <b>{m.name}</b>\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;;;AAiCO,MAAM,eAAe,CAAC;AAAA,EAC3B,MAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,KAAyB;AACvB,EAAA,MAAM,UAAUA,eAAA,EAAU;AAE1B,EAAA,SAAS,SAAS,CAAA,EAAoC;AACpD,IAAA,IAAI,CAAA,CAAE,MAAA,CAAO,KAAA,KAAU,MAAA,EAAQ;AAC7B,MAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACf,CAAA,MAAO;AACL,MAAA,QAAA,CAAS,CAAA,CAAE,OAAO,KAAe,CAAA;AAAA,IACnC;AAAA,EACF;AAEA,EAAA,uBACE,IAAA,CAAC,WAAA,EAAA,EAAY,OAAA,EAAQ,UAAA,EACnB,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,MAAA,EAAM,IAAA,EAAC,EAAA,EAAG,uBAAsB,QAAA,EAAA,aAAA,EAE5C,CAAA;AAAA,oBACA,IAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAG,eAAA;AAAA,QACH,OAAA,EAAQ,qBAAA;AAAA,QACR,UAAA,EAAY,GAAA;AAAA,QACZ,WAAW,OAAA,CAAQ,MAAA;AAAA,QACnB,OAAO,MAAA,IAAU,MAAA;AAAA,QACjB,QAAA;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,QAAA,EAAA,EAAS,SAAA,EAAW,OAAA,CAAQ,QAAA,EAAqB,KAAA,EAAM,QACtD,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAG,QAAA,EAAA,MAAA,EAAI,CAAA,EAAA,EADiC,MAE3C,CAAA;AAAA,UACC,QAAQ,GAAA,CAAI,CAAC,sBACZ,GAAA,CAAC,QAAA,EAAA,EAAS,WAAW,OAAA,CAAQ,QAAA,EAAuB,OAAO,CAAA,CAAE,IAAA,EAC3D,8BAAC,GAAA,EAAA,EAAG,QAAA,EAAA,CAAA,CAAE,MAAK,CAAA,EAAA,EAD+B,CAAA,CAAE,IAE9C,CACD;AAAA;AAAA;AAAA;AACH,GAAA,EACF,CAAA;AAEJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateRangePicker.esm.js","sources":["../../../src/components/PeriodSelect/DateRangePicker.tsx"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useState } from 'react';\nimport Button from '@material-ui/core/Button';\nimport Popover from '@material-ui/core/Popover';\nimport Box from '@material-ui/core/Box';\nimport Chip from '@material-ui/core/Chip';\nimport Typography from '@material-ui/core/Typography';\nimport Divider from '@material-ui/core/Divider';\nimport { makeStyles } from '@material-ui/core/styles';\nimport CalendarTodayIcon from '@material-ui/icons/CalendarToday';\nimport { MuiPickersUtilsProvider } from '@material-ui/pickers/MuiPickersUtilsProvider';\nimport { KeyboardDatePicker } from '@material-ui/pickers/DatePicker';\nimport LuxonUtils from '@date-io/luxon';\nimport { DateTime } from 'luxon';\nimport { Duration } from '../../types';\nimport { formatLastTwoLookaheadQuarters } from '../../utils/formatters';\nimport { useLastCompleteBillingDate } from '../../hooks';\n\nconst useStyles = makeStyles(theme => ({\n button: {\n fontWeight: theme.typography.fontWeightBold,\n textTransform: 'none',\n color: theme.palette.text.primary,\n borderColor: theme.palette.divider,\n '&:hover': {\n borderColor: theme.palette.primary.main,\n },\n },\n popover: {\n padding: theme.spacing(2),\n minWidth: 320,\n },\n presetSection: {\n marginBottom: theme.spacing(2),\n },\n presetChip: {\n margin: theme.spacing(0.5),\n },\n datePickersSection: {\n marginTop: theme.spacing(2),\n },\n datePickerWrapper: {\n marginBottom: theme.spacing(2),\n '& .MuiTextField-root': {\n width: '100%',\n },\n },\n actionButtons: {\n display: 'flex',\n justifyContent: 'flex-end',\n gap: theme.spacing(1),\n marginTop: theme.spacing(2),\n },\n}));\n\nexport type PeriodOption = {\n value: Duration;\n label: string;\n};\n\nexport function getDefaultOptions(\n lastCompleteBillingDate: string,\n): PeriodOption[] {\n return [\n {\n value: Duration.P90D,\n label: 'Past 6 Months',\n },\n {\n value: Duration.P30D,\n label: 'Past 60 Days',\n },\n {\n value: Duration.P3M,\n label: formatLastTwoLookaheadQuarters(lastCompleteBillingDate),\n },\n ];\n}\n\ntype DateRangePickerProps = {\n duration: Duration;\n onSelect: (\n duration: Duration,\n customDateRange?: { start: string; end: string },\n ) => void;\n options?: PeriodOption[];\n customDateRange?: { start: string; end: string };\n};\n\nexport const DateRangePicker = ({\n duration,\n onSelect,\n options,\n customDateRange,\n}: DateRangePickerProps) => {\n const classes = useStyles();\n const lastCompleteBillingDate = useLastCompleteBillingDate();\n const optionsOrDefault =\n options ?? getDefaultOptions(lastCompleteBillingDate);\n\n const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);\n const [startDate, setStartDate] = useState<DateTime | null>(\n customDateRange ? DateTime.fromISO(customDateRange.start) : null,\n );\n const [endDate, setEndDate] = useState<DateTime | null>(\n customDateRange ? DateTime.fromISO(customDateRange.end) : null,\n );\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const handlePresetSelect = (preset: Duration) => {\n onSelect(preset);\n handleClose();\n };\n\n const handleCustomApply = () => {\n if (startDate) {\n // If end date is not provided, default to lastCompleteBillingDate (day before today)\n const finalEndDate = endDate || DateTime.fromISO(lastCompleteBillingDate);\n const customRange = {\n start: startDate.toFormat('yyyy-MM-dd'),\n end: finalEndDate.toFormat('yyyy-MM-dd'),\n };\n onSelect(Duration.CUSTOM, customRange);\n handleClose();\n }\n };\n\n const open = Boolean(anchorEl);\n const id = open ? 'date-range-popover' : undefined;\n\n // Display label\n const getDisplayLabel = () => {\n if (duration === Duration.CUSTOM && customDateRange) {\n return `${customDateRange.start} to ${customDateRange.end}`;\n }\n const option = optionsOrDefault.find(o => o.value === duration);\n return option?.label || 'Select Period';\n };\n\n return (\n <MuiPickersUtilsProvider utils={LuxonUtils}>\n <Button\n className={classes.button}\n variant=\"outlined\"\n startIcon={<CalendarTodayIcon />}\n onClick={handleClick}\n data-testid=\"period-select\"\n >\n {getDisplayLabel()}\n </Button>\n <Popover\n id={id}\n open={open}\n anchorEl={anchorEl}\n onClose={handleClose}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left',\n }}\n transformOrigin={{\n vertical: 'top',\n horizontal: 'left',\n }}\n >\n <Box className={classes.popover}>\n <div className={classes.presetSection}>\n <Typography variant=\"subtitle2\" gutterBottom>\n Quick Select\n </Typography>\n <Box display=\"flex\" flexWrap=\"wrap\">\n {optionsOrDefault.map(option => (\n <Chip\n key={option.value}\n label={option.label}\n onClick={() => handlePresetSelect(option.value)}\n color={duration === option.value ? 'primary' : 'default'}\n className={classes.presetChip}\n data-testid={`period-preset-${option.value}`}\n />\n ))}\n </Box>\n </div>\n\n <Divider />\n\n <div className={classes.datePickersSection}>\n <Typography variant=\"subtitle2\" gutterBottom>\n Custom Date Range\n </Typography>\n <Box className={classes.datePickerWrapper}>\n <KeyboardDatePicker\n label=\"Start Date\"\n value={startDate}\n onChange={newValue => setStartDate(newValue)}\n format=\"yyyy-MM-dd\"\n variant=\"inline\"\n inputVariant=\"outlined\"\n size=\"small\"\n />\n </Box>\n <Box className={classes.datePickerWrapper}>\n <KeyboardDatePicker\n label=\"End Date (optional)\"\n value={endDate}\n onChange={newValue => setEndDate(newValue)}\n minDate={startDate || undefined}\n format=\"yyyy-MM-dd\"\n variant=\"inline\"\n inputVariant=\"outlined\"\n size=\"small\"\n helperText={endDate ? '' : 'Defaults to yesterday'}\n />\n </Box>\n <Box className={classes.actionButtons}>\n <Button onClick={handleClose} size=\"small\">\n Cancel\n </Button>\n <Button\n onClick={handleCustomApply}\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n disabled={!startDate}\n data-testid=\"apply-custom-range\"\n >\n Apply\n </Button>\n </Box>\n </div>\n </Box>\n </Popover>\n </MuiPickersUtilsProvider>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DateRangePicker.esm.js","sources":["../../../src/components/PeriodSelect/DateRangePicker.tsx"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useState } from 'react';\nimport Button from '@material-ui/core/Button';\nimport Popover from '@material-ui/core/Popover';\nimport Box from '@material-ui/core/Box';\nimport Chip from '@material-ui/core/Chip';\nimport Typography from '@material-ui/core/Typography';\nimport Divider from '@material-ui/core/Divider';\nimport { makeStyles } from '@material-ui/core/styles';\nimport CalendarTodayIcon from '@material-ui/icons/CalendarToday';\nimport { MuiPickersUtilsProvider } from '@material-ui/pickers/MuiPickersUtilsProvider';\nimport { KeyboardDatePicker } from '@material-ui/pickers/DatePicker';\nimport LuxonUtils from '@date-io/luxon';\nimport { DateTime } from 'luxon';\nimport { Duration } from '../../types';\nimport { formatLastTwoLookaheadQuarters } from '../../utils/formatters';\nimport { useLastCompleteBillingDate } from '../../hooks';\n\nconst useStyles = makeStyles(theme => ({\n button: {\n fontWeight: theme.typography.fontWeightBold,\n textTransform: 'none',\n color: theme.palette.text.primary,\n borderColor: theme.palette.divider,\n '&:hover': {\n borderColor: theme.palette.primary.main,\n },\n },\n popover: {\n padding: theme.spacing(2),\n minWidth: 320,\n },\n presetSection: {\n marginBottom: theme.spacing(2),\n },\n presetChip: {\n margin: theme.spacing(0.5),\n },\n datePickersSection: {\n marginTop: theme.spacing(2),\n },\n datePickerWrapper: {\n marginBottom: theme.spacing(2),\n '& .MuiTextField-root': {\n width: '100%',\n },\n },\n actionButtons: {\n display: 'flex',\n justifyContent: 'flex-end',\n gap: theme.spacing(1),\n marginTop: theme.spacing(2),\n },\n}));\n\nexport type PeriodOption = {\n value: Duration;\n label: string;\n};\n\nexport function getDefaultOptions(\n lastCompleteBillingDate: string,\n): PeriodOption[] {\n return [\n {\n value: Duration.P90D,\n label: 'Past 6 Months',\n },\n {\n value: Duration.P30D,\n label: 'Past 60 Days',\n },\n {\n value: Duration.P3M,\n label: formatLastTwoLookaheadQuarters(lastCompleteBillingDate),\n },\n ];\n}\n\ntype DateRangePickerProps = {\n duration: Duration;\n onSelect: (\n duration: Duration,\n customDateRange?: { start: string; end: string },\n ) => void;\n options?: PeriodOption[];\n customDateRange?: { start: string; end: string };\n};\n\nexport const DateRangePicker = ({\n duration,\n onSelect,\n options,\n customDateRange,\n}: DateRangePickerProps) => {\n const classes = useStyles();\n const lastCompleteBillingDate = useLastCompleteBillingDate();\n const optionsOrDefault =\n options ?? getDefaultOptions(lastCompleteBillingDate);\n\n const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);\n const [startDate, setStartDate] = useState<DateTime | null>(\n customDateRange ? DateTime.fromISO(customDateRange.start) : null,\n );\n const [endDate, setEndDate] = useState<DateTime | null>(\n customDateRange ? DateTime.fromISO(customDateRange.end) : null,\n );\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const handlePresetSelect = (preset: Duration) => {\n onSelect(preset);\n handleClose();\n };\n\n const handleCustomApply = () => {\n if (startDate) {\n // If end date is not provided, default to lastCompleteBillingDate (day before today)\n const finalEndDate = endDate || DateTime.fromISO(lastCompleteBillingDate);\n const customRange = {\n start: startDate.toFormat('yyyy-MM-dd'),\n end: finalEndDate.toFormat('yyyy-MM-dd'),\n };\n onSelect(Duration.CUSTOM, customRange);\n handleClose();\n }\n };\n\n const open = Boolean(anchorEl);\n const id = open ? 'date-range-popover' : undefined;\n\n // Display label\n const getDisplayLabel = () => {\n if (duration === Duration.CUSTOM && customDateRange) {\n return `${customDateRange.start} to ${customDateRange.end}`;\n }\n const option = optionsOrDefault.find(o => o.value === duration);\n return option?.label || 'Select Period';\n };\n\n return (\n <MuiPickersUtilsProvider utils={LuxonUtils}>\n <Button\n className={classes.button}\n variant=\"outlined\"\n startIcon={<CalendarTodayIcon />}\n onClick={handleClick}\n data-testid=\"period-select\"\n >\n {getDisplayLabel()}\n </Button>\n <Popover\n id={id}\n open={open}\n anchorEl={anchorEl}\n onClose={handleClose}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left',\n }}\n transformOrigin={{\n vertical: 'top',\n horizontal: 'left',\n }}\n >\n <Box className={classes.popover}>\n <div className={classes.presetSection}>\n <Typography variant=\"subtitle2\" gutterBottom>\n Quick Select\n </Typography>\n <Box display=\"flex\" flexWrap=\"wrap\">\n {optionsOrDefault.map(option => (\n <Chip\n key={option.value}\n label={option.label}\n onClick={() => handlePresetSelect(option.value)}\n color={duration === option.value ? 'primary' : 'default'}\n className={classes.presetChip}\n data-testid={`period-preset-${option.value}`}\n />\n ))}\n </Box>\n </div>\n\n <Divider />\n\n <div className={classes.datePickersSection}>\n <Typography variant=\"subtitle2\" gutterBottom>\n Custom Date Range\n </Typography>\n <Box className={classes.datePickerWrapper}>\n <KeyboardDatePicker\n label=\"Start Date\"\n value={startDate}\n onChange={newValue => setStartDate(newValue)}\n format=\"yyyy-MM-dd\"\n variant=\"inline\"\n inputVariant=\"outlined\"\n size=\"small\"\n />\n </Box>\n <Box className={classes.datePickerWrapper}>\n <KeyboardDatePicker\n label=\"End Date (optional)\"\n value={endDate}\n onChange={newValue => setEndDate(newValue)}\n minDate={startDate || undefined}\n format=\"yyyy-MM-dd\"\n variant=\"inline\"\n inputVariant=\"outlined\"\n size=\"small\"\n helperText={endDate ? '' : 'Defaults to yesterday'}\n />\n </Box>\n <Box className={classes.actionButtons}>\n <Button onClick={handleClose} size=\"small\">\n Cancel\n </Button>\n <Button\n onClick={handleCustomApply}\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n disabled={!startDate}\n data-testid=\"apply-custom-range\"\n >\n Apply\n </Button>\n </Box>\n </div>\n </Box>\n </Popover>\n </MuiPickersUtilsProvider>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductInsights.esm.js","sources":["../../../src/components/ProductInsights/ProductInsights.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport Box from '@material-ui/core/Box';\nimport Typography from '@material-ui/core/Typography';\nimport { default as Alert } from '@material-ui/lab/Alert';\nimport { costInsightsApiRef } from '../../api';\nimport { ProductInsightsCardList } from '../ProductInsightsCard/ProductInsightsCardList';\nimport { Duration } from '../../types';\nimport {\n Entity,\n Maybe,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { intervalsOf, DEFAULT_DURATION } from '../../utils/duration';\nimport {\n DefaultLoadingAction,\n initialStatesOf,\n settledResponseOf,\n ProductState,\n} from '../../utils/loading';\nimport { totalAggregationSort } from '../../utils/sort';\nimport {\n useLoading,\n useLastCompleteBillingDate,\n MapLoadingToProps,\n} from '../../hooks';\nimport { useApi } from '@backstage/core-plugin-api';\n\ntype LoadingProps = (isLoading: boolean) => void;\n\nconst mapLoadingToProps: MapLoadingToProps<LoadingProps> =\n ({ dispatch }) =>\n (isLoading: boolean) =>\n dispatch({ [DefaultLoadingAction.CostInsightsProducts]: isLoading });\n\ntype ProductInsightsProps = {\n group: string;\n project: Maybe<string>;\n products: Product[];\n onLoaded: (entities: Product[]) => void;\n};\n\nexport const ProductInsights = ({\n group,\n project,\n products,\n onLoaded,\n}: ProductInsightsProps) => {\n const client = useApi(costInsightsApiRef);\n const onceRef = useRef(false);\n const [initialStates, setStates] = useState<ProductState[]>([]);\n const [error, setError] = useState<Maybe<Error>>(null);\n const lastCompleteBillingDate = useLastCompleteBillingDate();\n const dispatchLoading = useLoading(mapLoadingToProps);\n\n /* eslint-disable react-hooks/exhaustive-deps */\n // See @CostInsightsPage\n const dispatchLoadingProducts = useCallback(dispatchLoading, []);\n /* eslint-enable react-hooks/exhaustive-deps */\n\n const onSelectAsyncMemo = useCallback(\n async function onSelectAsync(\n product: Product,\n duration: Duration,\n customDateRange?: { start: string; end: string },\n ): Promise<Entity> {\n return client.getProductInsights({\n group: group,\n project: project,\n product: product.kind,\n intervals: intervalsOf(\n duration,\n lastCompleteBillingDate,\n 2,\n customDateRange,\n true, // Use comparison mode for ProductInsights to split periods\n ),\n });\n },\n [client, group, project, lastCompleteBillingDate],\n );\n\n useEffect(() => {\n async function getAllProductInsights() {\n try {\n dispatchLoadingProducts(true);\n const responses = await Promise.allSettled(\n products.map(product =>\n client.getProductInsights({\n group: group,\n project: project,\n product: product.kind,\n intervals: intervalsOf(DEFAULT_DURATION, lastCompleteBillingDate),\n }),\n ),\n ).then(settledResponseOf);\n\n const updatedInitialStates = initialStatesOf(products, responses).sort(\n totalAggregationSort,\n );\n setStates(updatedInitialStates);\n } catch (e) {\n setError(e);\n } finally {\n dispatchLoadingProducts(false);\n }\n }\n\n getAllProductInsights();\n }, [\n client,\n group,\n project,\n products,\n lastCompleteBillingDate,\n dispatchLoadingProducts,\n ]);\n\n useEffect(\n function handleOnLoaded() {\n if (onceRef.current) {\n const initialProducts = initialStates.map(state => state.product);\n onLoaded(initialProducts);\n } else {\n onceRef.current = true;\n }\n },\n [initialStates, onLoaded],\n );\n\n return (\n <Box px={3} py={6}>\n <Box mt={0} mb={5} textAlign=\"center\">\n <Typography variant=\"h4\" gutterBottom>\n Your team's product usage\n </Typography>\n </Box>\n {error ? (\n <Alert severity=\"error\">{error.message}</Alert>\n ) : (\n <ProductInsightsCardList\n initialStates={initialStates}\n onSelectAsync={onSelectAsyncMemo}\n />\n )}\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AA6CA,MAAM,
|
|
1
|
+
{"version":3,"file":"ProductInsights.esm.js","sources":["../../../src/components/ProductInsights/ProductInsights.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport Box from '@material-ui/core/Box';\nimport Typography from '@material-ui/core/Typography';\nimport { default as Alert } from '@material-ui/lab/Alert';\nimport { costInsightsApiRef } from '../../api';\nimport { ProductInsightsCardList } from '../ProductInsightsCard/ProductInsightsCardList';\nimport { Duration } from '../../types';\nimport {\n Entity,\n Maybe,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { intervalsOf, DEFAULT_DURATION } from '../../utils/duration';\nimport {\n DefaultLoadingAction,\n initialStatesOf,\n settledResponseOf,\n ProductState,\n} from '../../utils/loading';\nimport { totalAggregationSort } from '../../utils/sort';\nimport {\n useLoading,\n useLastCompleteBillingDate,\n MapLoadingToProps,\n} from '../../hooks';\nimport { useApi } from '@backstage/core-plugin-api';\n\ntype LoadingProps = (isLoading: boolean) => void;\n\nconst mapLoadingToProps: MapLoadingToProps<LoadingProps> =\n ({ dispatch }) =>\n (isLoading: boolean) =>\n dispatch({ [DefaultLoadingAction.CostInsightsProducts]: isLoading });\n\ntype ProductInsightsProps = {\n group: string;\n project: Maybe<string>;\n products: Product[];\n onLoaded: (entities: Product[]) => void;\n};\n\nexport const ProductInsights = ({\n group,\n project,\n products,\n onLoaded,\n}: ProductInsightsProps) => {\n const client = useApi(costInsightsApiRef);\n const onceRef = useRef(false);\n const [initialStates, setStates] = useState<ProductState[]>([]);\n const [error, setError] = useState<Maybe<Error>>(null);\n const lastCompleteBillingDate = useLastCompleteBillingDate();\n const dispatchLoading = useLoading(mapLoadingToProps);\n\n /* eslint-disable react-hooks/exhaustive-deps */\n // See @CostInsightsPage\n const dispatchLoadingProducts = useCallback(dispatchLoading, []);\n /* eslint-enable react-hooks/exhaustive-deps */\n\n const onSelectAsyncMemo = useCallback(\n async function onSelectAsync(\n product: Product,\n duration: Duration,\n customDateRange?: { start: string; end: string },\n ): Promise<Entity> {\n return client.getProductInsights({\n group: group,\n project: project,\n product: product.kind,\n intervals: intervalsOf(\n duration,\n lastCompleteBillingDate,\n 2,\n customDateRange,\n true, // Use comparison mode for ProductInsights to split periods\n ),\n });\n },\n [client, group, project, lastCompleteBillingDate],\n );\n\n useEffect(() => {\n async function getAllProductInsights() {\n try {\n dispatchLoadingProducts(true);\n const responses = await Promise.allSettled(\n products.map(product =>\n client.getProductInsights({\n group: group,\n project: project,\n product: product.kind,\n intervals: intervalsOf(DEFAULT_DURATION, lastCompleteBillingDate),\n }),\n ),\n ).then(settledResponseOf);\n\n const updatedInitialStates = initialStatesOf(products, responses).sort(\n totalAggregationSort,\n );\n setStates(updatedInitialStates);\n } catch (e) {\n setError(e);\n } finally {\n dispatchLoadingProducts(false);\n }\n }\n\n getAllProductInsights();\n }, [\n client,\n group,\n project,\n products,\n lastCompleteBillingDate,\n dispatchLoadingProducts,\n ]);\n\n useEffect(\n function handleOnLoaded() {\n if (onceRef.current) {\n const initialProducts = initialStates.map(state => state.product);\n onLoaded(initialProducts);\n } else {\n onceRef.current = true;\n }\n },\n [initialStates, onLoaded],\n );\n\n return (\n <Box px={3} py={6}>\n <Box mt={0} mb={5} textAlign=\"center\">\n <Typography variant=\"h4\" gutterBottom>\n Your team's product usage\n </Typography>\n </Box>\n {error ? (\n <Alert severity=\"error\">{error.message}</Alert>\n ) : (\n <ProductInsightsCardList\n initialStates={initialStates}\n onSelectAsync={onSelectAsyncMemo}\n />\n )}\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductEntityDialog.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductEntityDialog.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useState } from 'react';\nimport Dialog from '@material-ui/core/Dialog';\nimport IconButton from '@material-ui/core/IconButton';\nimport { default as CloseButton } from '@material-ui/icons/Close';\nimport { useEntityDialogStyles as useStyles } from '../../utils/styles';\nimport { Entity } from '@backstage-community/plugin-cost-insights-common';\nimport {\n ProductEntityTable,\n ProductEntityTableOptions,\n} from './ProductEntityTable';\nimport { findAlways } from '../../utils/assert';\nimport { HeaderTabs } from '@backstage/core-components';\n\ntype ProductEntityDialogProps = {\n open: boolean;\n entity: Entity;\n options?: ProductEntityTableOptions;\n onClose: () => void;\n};\n\nexport const ProductEntityDialog = ({\n open,\n entity,\n options = {},\n onClose,\n}: ProductEntityDialogProps) => {\n const classes = useStyles();\n const labels = Object.keys(entity.entities);\n const [selectedLabel, setSelectedLabel] = useState(\n findAlways(labels, _ => true),\n );\n\n const tabs = labels.map((label, index) => ({\n id: index.toString(),\n label: `Breakdown by ${label}`,\n }));\n\n return (\n <Dialog open={open} onClose={onClose} scroll=\"body\" fullWidth maxWidth=\"lg\">\n <IconButton className={classes.closeButton} onClick={onClose}>\n <CloseButton />\n </IconButton>\n <HeaderTabs\n tabs={tabs}\n onChange={index => setSelectedLabel(labels[index])}\n />\n <ProductEntityTable\n entityLabel={selectedLabel}\n entity={entity}\n options={options}\n />\n </Dialog>\n );\n};\n"],"names":["useStyles","CloseButton"],"mappings":";;;;;;;;;;AAoCO,MAAM,sBAAsB,CAAC;AAAA,EAClC,IAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAU,EAAC;AAAA,EACX;AACF,
|
|
1
|
+
{"version":3,"file":"ProductEntityDialog.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductEntityDialog.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useState } from 'react';\nimport Dialog from '@material-ui/core/Dialog';\nimport IconButton from '@material-ui/core/IconButton';\nimport { default as CloseButton } from '@material-ui/icons/Close';\nimport { useEntityDialogStyles as useStyles } from '../../utils/styles';\nimport { Entity } from '@backstage-community/plugin-cost-insights-common';\nimport {\n ProductEntityTable,\n ProductEntityTableOptions,\n} from './ProductEntityTable';\nimport { findAlways } from '../../utils/assert';\nimport { HeaderTabs } from '@backstage/core-components';\n\ntype ProductEntityDialogProps = {\n open: boolean;\n entity: Entity;\n options?: ProductEntityTableOptions;\n onClose: () => void;\n};\n\nexport const ProductEntityDialog = ({\n open,\n entity,\n options = {},\n onClose,\n}: ProductEntityDialogProps) => {\n const classes = useStyles();\n const labels = Object.keys(entity.entities);\n const [selectedLabel, setSelectedLabel] = useState(\n findAlways(labels, _ => true),\n );\n\n const tabs = labels.map((label, index) => ({\n id: index.toString(),\n label: `Breakdown by ${label}`,\n }));\n\n return (\n <Dialog open={open} onClose={onClose} scroll=\"body\" fullWidth maxWidth=\"lg\">\n <IconButton className={classes.closeButton} onClick={onClose}>\n <CloseButton />\n </IconButton>\n <HeaderTabs\n tabs={tabs}\n onChange={index => setSelectedLabel(labels[index])}\n />\n <ProductEntityTable\n entityLabel={selectedLabel}\n entity={entity}\n options={options}\n />\n </Dialog>\n );\n};\n"],"names":["useStyles","CloseButton"],"mappings":";;;;;;;;;;AAoCO,MAAM,sBAAsB,CAAC;AAAA,EAClC,IAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAU,EAAC;AAAA,EACX;AACF,CAAA,KAAgC;AAC9B,EAAA,MAAM,UAAUA,qBAAA,EAAU;AAC1B,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA;AAC1C,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,QAAA;AAAA,IACxC,UAAA,CAAW,MAAA,EAAQ,CAAA,CAAA,KAAK,IAAI;AAAA,GAC9B;AAEA,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,GAAA,CAAI,CAAC,OAAO,KAAA,MAAW;AAAA,IACzC,EAAA,EAAI,MAAM,QAAA,EAAS;AAAA,IACnB,KAAA,EAAO,gBAAgB,KAAK,CAAA;AAAA,GAC9B,CAAE,CAAA;AAEF,EAAA,uBACE,IAAA,CAAC,UAAO,IAAA,EAAY,OAAA,EAAkB,QAAO,MAAA,EAAO,SAAA,EAAS,IAAA,EAAC,QAAA,EAAS,IAAA,EACrE,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,WAAW,OAAA,CAAQ,WAAA,EAAa,SAAS,OAAA,EACnD,QAAA,kBAAA,GAAA,CAACC,aAAY,CAAA,EACf,CAAA;AAAA,oBACA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,IAAA;AAAA,QACA,QAAA,EAAU,CAAA,KAAA,KAAS,gBAAA,CAAiB,MAAA,CAAO,KAAK,CAAC;AAAA;AAAA,KACnD;AAAA,oBACA,GAAA;AAAA,MAAC,kBAAA;AAAA,MAAA;AAAA,QACC,WAAA,EAAa,aAAA;AAAA,QACb,MAAA;AAAA,QACA;AAAA;AAAA;AACF,GAAA,EACF,CAAA;AAEJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductEntityTable.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductEntityTable.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport classnames from 'classnames';\nimport Typography from '@material-ui/core/Typography';\nimport { formatChange } from '../../utils/formatters';\nimport { useEntityDialogStyles as useStyles } from '../../utils/styles';\nimport { CostGrowthIndicator } from '../CostGrowth';\nimport { BarChartOptions } from '../../types';\nimport {\n ChangeStatistic,\n Entity,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { Table, TableColumn } from '@backstage/core-components';\nimport { useConfig } from '../../hooks';\n\nexport type ProductEntityTableOptions = Partial<\n Pick<BarChartOptions, 'previousName' | 'currentName'>\n>;\n\ntype RowData = {\n id: string;\n label: string;\n previous: number;\n current: number;\n change: ChangeStatistic;\n};\n\nconst createRenderer =\n (baseCurrency: Intl.NumberFormat) =>\n (col: keyof RowData, classes: Record<string, string>) => {\n return function render(rowData: {}): JSX.Element {\n const row = rowData as RowData;\n const rowStyles = classnames(classes.row, {\n [classes.rowTotal]: row.id === 'total',\n [classes.colFirst]: col === 'label',\n [classes.colLast]: col === 'change',\n });\n\n switch (col) {\n case 'previous':\n case 'current':\n return (\n <Typography className={rowStyles}>\n {baseCurrency.format(row[col])}\n </Typography>\n );\n case 'change':\n return (\n <CostGrowthIndicator\n className={rowStyles}\n change={row.change}\n formatter={formatChange}\n />\n );\n default:\n return <Typography className={rowStyles}>{row.label}</Typography>;\n }\n };\n };\n\n// material-table does not support fixed rows. Override the sorting algorithm\n// to force Total row to bottom by default or when a user sort toggles a column.\nfunction createSorter(field?: keyof Omit<RowData, 'id'>) {\n return function rowSort(data1: {}, data2: {}): number {\n const a = data1 as RowData;\n const b = data2 as RowData;\n if (a.id === 'total') return 1;\n if (b.id === 'total') return 1;\n if (field === 'label') return a.label.localeCompare(b.label);\n if (field === 'change') {\n if (formatChange(a[field]) === '∞' || formatChange(b[field]) === '-∞')\n return 1;\n if (formatChange(a[field]) === '-∞' || formatChange(b[field]) === '∞')\n return -1;\n return a[field].ratio! - b[field].ratio!;\n }\n\n return b.previous + b.current - (a.previous + a.current);\n };\n}\n\ntype ProductEntityTableProps = {\n entityLabel: string;\n entity: Entity;\n options: ProductEntityTableOptions;\n};\n\nexport const ProductEntityTable = ({\n entityLabel,\n entity,\n options,\n}: ProductEntityTableProps) => {\n const classes = useStyles();\n const { baseCurrency } = useConfig();\n const entities = entity.entities[entityLabel];\n\n const data = Object.assign(\n {\n previousName: 'Previous',\n currentName: 'Current',\n },\n options,\n );\n\n const firstColClasses = classnames(classes.column, classes.colFirst);\n const lastColClasses = classnames(classes.column, classes.colLast);\n\n const columns: TableColumn[] = [\n {\n field: 'label',\n title: <Typography className={firstColClasses}>{entityLabel}</Typography>,\n render: createRenderer(baseCurrency)('label', classes),\n customSort: createSorter('label'),\n width: '33.33%',\n },\n {\n field: 'previous',\n title: (\n <Typography className={classes.column}>{data.previousName}</Typography>\n ),\n align: 'right',\n render: createRenderer(baseCurrency)('previous', classes),\n customSort: createSorter('previous'),\n },\n {\n field: 'current',\n title: (\n <Typography className={classes.column}>{data.currentName}</Typography>\n ),\n align: 'right',\n render: createRenderer(baseCurrency)('current', classes),\n customSort: createSorter('current'),\n },\n {\n field: 'change',\n title: <Typography className={lastColClasses}>Change</Typography>,\n align: 'right',\n render: createRenderer(baseCurrency)('change', classes),\n customSort: createSorter('change'),\n },\n ];\n\n const rowData: RowData[] = entities\n .map(e => ({\n id: e.id || 'Unknown',\n label: e.id || 'Unknown',\n previous: e.aggregation[0],\n current: e.aggregation[1],\n change: e.change,\n }))\n .concat({\n id: 'total',\n label: 'Total',\n previous: entity.aggregation[0],\n current: entity.aggregation[1],\n change: entity.change,\n })\n .sort(createSorter());\n\n return (\n <Table\n columns={columns}\n data={rowData}\n title={entity.id || 'Unlabeled'}\n options={{\n paging: false,\n search: false,\n hideFilterIcons: true,\n }}\n />\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;;;;;;;;;;;;;;;AAyCA,MAAM,
|
|
1
|
+
{"version":3,"file":"ProductEntityTable.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductEntityTable.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport classnames from 'classnames';\nimport Typography from '@material-ui/core/Typography';\nimport { formatChange } from '../../utils/formatters';\nimport { useEntityDialogStyles as useStyles } from '../../utils/styles';\nimport { CostGrowthIndicator } from '../CostGrowth';\nimport { BarChartOptions } from '../../types';\nimport {\n ChangeStatistic,\n Entity,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { Table, TableColumn } from '@backstage/core-components';\nimport { useConfig } from '../../hooks';\n\nexport type ProductEntityTableOptions = Partial<\n Pick<BarChartOptions, 'previousName' | 'currentName'>\n>;\n\ntype RowData = {\n id: string;\n label: string;\n previous: number;\n current: number;\n change: ChangeStatistic;\n};\n\nconst createRenderer =\n (baseCurrency: Intl.NumberFormat) =>\n (col: keyof RowData, classes: Record<string, string>) => {\n return function render(rowData: {}): JSX.Element {\n const row = rowData as RowData;\n const rowStyles = classnames(classes.row, {\n [classes.rowTotal]: row.id === 'total',\n [classes.colFirst]: col === 'label',\n [classes.colLast]: col === 'change',\n });\n\n switch (col) {\n case 'previous':\n case 'current':\n return (\n <Typography className={rowStyles}>\n {baseCurrency.format(row[col])}\n </Typography>\n );\n case 'change':\n return (\n <CostGrowthIndicator\n className={rowStyles}\n change={row.change}\n formatter={formatChange}\n />\n );\n default:\n return <Typography className={rowStyles}>{row.label}</Typography>;\n }\n };\n };\n\n// material-table does not support fixed rows. Override the sorting algorithm\n// to force Total row to bottom by default or when a user sort toggles a column.\nfunction createSorter(field?: keyof Omit<RowData, 'id'>) {\n return function rowSort(data1: {}, data2: {}): number {\n const a = data1 as RowData;\n const b = data2 as RowData;\n if (a.id === 'total') return 1;\n if (b.id === 'total') return 1;\n if (field === 'label') return a.label.localeCompare(b.label);\n if (field === 'change') {\n if (formatChange(a[field]) === '∞' || formatChange(b[field]) === '-∞')\n return 1;\n if (formatChange(a[field]) === '-∞' || formatChange(b[field]) === '∞')\n return -1;\n return a[field].ratio! - b[field].ratio!;\n }\n\n return b.previous + b.current - (a.previous + a.current);\n };\n}\n\ntype ProductEntityTableProps = {\n entityLabel: string;\n entity: Entity;\n options: ProductEntityTableOptions;\n};\n\nexport const ProductEntityTable = ({\n entityLabel,\n entity,\n options,\n}: ProductEntityTableProps) => {\n const classes = useStyles();\n const { baseCurrency } = useConfig();\n const entities = entity.entities[entityLabel];\n\n const data = Object.assign(\n {\n previousName: 'Previous',\n currentName: 'Current',\n },\n options,\n );\n\n const firstColClasses = classnames(classes.column, classes.colFirst);\n const lastColClasses = classnames(classes.column, classes.colLast);\n\n const columns: TableColumn[] = [\n {\n field: 'label',\n title: <Typography className={firstColClasses}>{entityLabel}</Typography>,\n render: createRenderer(baseCurrency)('label', classes),\n customSort: createSorter('label'),\n width: '33.33%',\n },\n {\n field: 'previous',\n title: (\n <Typography className={classes.column}>{data.previousName}</Typography>\n ),\n align: 'right',\n render: createRenderer(baseCurrency)('previous', classes),\n customSort: createSorter('previous'),\n },\n {\n field: 'current',\n title: (\n <Typography className={classes.column}>{data.currentName}</Typography>\n ),\n align: 'right',\n render: createRenderer(baseCurrency)('current', classes),\n customSort: createSorter('current'),\n },\n {\n field: 'change',\n title: <Typography className={lastColClasses}>Change</Typography>,\n align: 'right',\n render: createRenderer(baseCurrency)('change', classes),\n customSort: createSorter('change'),\n },\n ];\n\n const rowData: RowData[] = entities\n .map(e => ({\n id: e.id || 'Unknown',\n label: e.id || 'Unknown',\n previous: e.aggregation[0],\n current: e.aggregation[1],\n change: e.change,\n }))\n .concat({\n id: 'total',\n label: 'Total',\n previous: entity.aggregation[0],\n current: entity.aggregation[1],\n change: entity.change,\n })\n .sort(createSorter());\n\n return (\n <Table\n columns={columns}\n data={rowData}\n title={entity.id || 'Unlabeled'}\n options={{\n paging: false,\n search: false,\n hideFilterIcons: true,\n }}\n />\n );\n};\n"],"names":["useStyles"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductInsightsCard.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductInsightsCard.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport pluralize from 'pluralize';\nimport Typography from '@material-ui/core/Typography';\nimport { default as Alert } from '@material-ui/lab/Alert';\nimport { DateRangePicker } from '../PeriodSelect';\nimport { ProductInsightsChart } from './ProductInsightsChart';\nimport { useProductInsightsCardStyles as useStyles } from '../../utils/styles';\nimport { DefaultLoadingAction } from '../../utils/loading';\nimport { Duration } from '../../types';\nimport {\n Entity,\n Maybe,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport {\n MapLoadingToProps,\n useLastCompleteBillingDate,\n useLoading,\n} from '../../hooks';\nimport { findAnyKey } from '../../utils/assert';\nimport { ScrollAnchor } from '../../utils/scroll';\nimport { InfoCard } from '@backstage/core-components';\n\ntype LoadingProps = (isLoading: boolean) => void;\n\nexport type ProductInsightsCardProps = {\n product: Product;\n initialState: {\n entity: Maybe<Entity>;\n duration: Duration;\n };\n onSelectAsync: (\n product: Product,\n duration: Duration,\n customDateRange?: { start: string; end: string },\n ) => Promise<Entity>;\n};\n\nconst mapLoadingToProps: MapLoadingToProps<LoadingProps> =\n ({ dispatch }) =>\n (isLoading: boolean) =>\n dispatch({ [DefaultLoadingAction.CostInsightsProducts]: isLoading });\n\nexport const ProductInsightsCard = ({\n initialState,\n product,\n onSelectAsync,\n}: PropsWithChildren<ProductInsightsCardProps>) => {\n const classes = useStyles();\n const mountedRef = useRef(false);\n const [error, setError] = useState<Maybe<Error>>(null);\n const dispatchLoading = useLoading(mapLoadingToProps);\n const lastCompleteBillingDate = useLastCompleteBillingDate();\n const [entity, setEntity] = useState<Maybe<Entity>>(initialState.entity);\n const [duration, setDuration] = useState<Duration>(initialState.duration);\n const [customDateRange, setCustomDateRange] = useState<\n { start: string; end: string } | undefined\n >(undefined);\n\n /* eslint-disable react-hooks/exhaustive-deps */\n const dispatchLoadingProduct = useCallback(dispatchLoading, []);\n /* eslint-enable react-hooks/exhaustive-deps */\n\n useEffect(() => {\n async function handleOnSelectAsync() {\n dispatchLoadingProduct(true);\n try {\n const e = await onSelectAsync(product, duration, customDateRange);\n setEntity(e);\n } catch (e) {\n setEntity(null);\n setError(e);\n } finally {\n dispatchLoadingProduct(false);\n }\n }\n\n if (mountedRef.current) {\n handleOnSelectAsync();\n } else {\n mountedRef.current = true;\n }\n }, [\n product,\n duration,\n customDateRange,\n onSelectAsync,\n dispatchLoadingProduct,\n ]);\n\n // Only a single entities Record for the root product entity is supported\n const entityKey = findAnyKey(entity?.entities);\n const entities = entityKey ? entity!.entities[entityKey] : [];\n\n const subheader =\n entityKey && entities.length\n ? `${pluralize(entityKey, entities.length, true)}, sorted by cost`\n : null;\n const handleDurationSelect = (\n newDuration: Duration,\n newCustomDateRange?: { start: string; end: string },\n ) => {\n setDuration(newDuration);\n setCustomDateRange(newCustomDateRange);\n };\n\n const headerProps = {\n classes: classes,\n action: (\n <DateRangePicker\n duration={duration}\n onSelect={handleDurationSelect}\n customDateRange={customDateRange}\n />\n ),\n };\n\n if (error || !entity) {\n return (\n <InfoCard title={product.name} headerProps={headerProps}>\n <ScrollAnchor id={product.kind} />\n <Alert severity=\"error\">\n {error\n ? error.message\n : `Error: Could not fetch product insights for ${product.name}`}\n </Alert>\n </InfoCard>\n );\n }\n\n return (\n <InfoCard\n title={product.name}\n subheader={subheader}\n headerProps={headerProps}\n >\n <ScrollAnchor id={product.kind} />\n {entities.length ? (\n <ProductInsightsChart\n entity={entity}\n duration={duration}\n billingDate={lastCompleteBillingDate}\n customDateRange={customDateRange}\n />\n ) : (\n <Typography>\n There are no {product.name} costs within this time frame for your\n team's projects.\n </Typography>\n )}\n </InfoCard>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,MAAM,
|
|
1
|
+
{"version":3,"file":"ProductInsightsCard.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductInsightsCard.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport pluralize from 'pluralize';\nimport Typography from '@material-ui/core/Typography';\nimport { default as Alert } from '@material-ui/lab/Alert';\nimport { DateRangePicker } from '../PeriodSelect';\nimport { ProductInsightsChart } from './ProductInsightsChart';\nimport { useProductInsightsCardStyles as useStyles } from '../../utils/styles';\nimport { DefaultLoadingAction } from '../../utils/loading';\nimport { Duration } from '../../types';\nimport {\n Entity,\n Maybe,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport {\n MapLoadingToProps,\n useLastCompleteBillingDate,\n useLoading,\n} from '../../hooks';\nimport { findAnyKey } from '../../utils/assert';\nimport { ScrollAnchor } from '../../utils/scroll';\nimport { InfoCard } from '@backstage/core-components';\n\ntype LoadingProps = (isLoading: boolean) => void;\n\nexport type ProductInsightsCardProps = {\n product: Product;\n initialState: {\n entity: Maybe<Entity>;\n duration: Duration;\n };\n onSelectAsync: (\n product: Product,\n duration: Duration,\n customDateRange?: { start: string; end: string },\n ) => Promise<Entity>;\n};\n\nconst mapLoadingToProps: MapLoadingToProps<LoadingProps> =\n ({ dispatch }) =>\n (isLoading: boolean) =>\n dispatch({ [DefaultLoadingAction.CostInsightsProducts]: isLoading });\n\nexport const ProductInsightsCard = ({\n initialState,\n product,\n onSelectAsync,\n}: PropsWithChildren<ProductInsightsCardProps>) => {\n const classes = useStyles();\n const mountedRef = useRef(false);\n const [error, setError] = useState<Maybe<Error>>(null);\n const dispatchLoading = useLoading(mapLoadingToProps);\n const lastCompleteBillingDate = useLastCompleteBillingDate();\n const [entity, setEntity] = useState<Maybe<Entity>>(initialState.entity);\n const [duration, setDuration] = useState<Duration>(initialState.duration);\n const [customDateRange, setCustomDateRange] = useState<\n { start: string; end: string } | undefined\n >(undefined);\n\n /* eslint-disable react-hooks/exhaustive-deps */\n const dispatchLoadingProduct = useCallback(dispatchLoading, []);\n /* eslint-enable react-hooks/exhaustive-deps */\n\n useEffect(() => {\n async function handleOnSelectAsync() {\n dispatchLoadingProduct(true);\n try {\n const e = await onSelectAsync(product, duration, customDateRange);\n setEntity(e);\n } catch (e) {\n setEntity(null);\n setError(e);\n } finally {\n dispatchLoadingProduct(false);\n }\n }\n\n if (mountedRef.current) {\n handleOnSelectAsync();\n } else {\n mountedRef.current = true;\n }\n }, [\n product,\n duration,\n customDateRange,\n onSelectAsync,\n dispatchLoadingProduct,\n ]);\n\n // Only a single entities Record for the root product entity is supported\n const entityKey = findAnyKey(entity?.entities);\n const entities = entityKey ? entity!.entities[entityKey] : [];\n\n const subheader =\n entityKey && entities.length\n ? `${pluralize(entityKey, entities.length, true)}, sorted by cost`\n : null;\n const handleDurationSelect = (\n newDuration: Duration,\n newCustomDateRange?: { start: string; end: string },\n ) => {\n setDuration(newDuration);\n setCustomDateRange(newCustomDateRange);\n };\n\n const headerProps = {\n classes: classes,\n action: (\n <DateRangePicker\n duration={duration}\n onSelect={handleDurationSelect}\n customDateRange={customDateRange}\n />\n ),\n };\n\n if (error || !entity) {\n return (\n <InfoCard title={product.name} headerProps={headerProps}>\n <ScrollAnchor id={product.kind} />\n <Alert severity=\"error\">\n {error\n ? error.message\n : `Error: Could not fetch product insights for ${product.name}`}\n </Alert>\n </InfoCard>\n );\n }\n\n return (\n <InfoCard\n title={product.name}\n subheader={subheader}\n headerProps={headerProps}\n >\n <ScrollAnchor id={product.kind} />\n {entities.length ? (\n <ProductInsightsChart\n entity={entity}\n duration={duration}\n billingDate={lastCompleteBillingDate}\n customDateRange={customDateRange}\n />\n ) : (\n <Typography>\n There are no {product.name} costs within this time frame for your\n team's projects.\n </Typography>\n )}\n </InfoCard>\n );\n};\n"],"names":["useStyles"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductInsightsCardList.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductInsightsCardList.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Box from '@material-ui/core/Box';\nimport CircularProgress from '@material-ui/core/CircularProgress';\nimport Collapse from '@material-ui/core/Collapse';\nimport { ProductInsightsCard } from './ProductInsightsCard';\nimport { Duration } from '../../types';\nimport {\n Entity,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { ProductState } from '../../utils/loading';\n\ntype ProductInsightsCardListProps = {\n initialStates: ProductState[];\n onSelectAsync: (product: Product, duration: Duration) => Promise<Entity>;\n};\n\nexport const ProductInsightsCardList = ({\n initialStates,\n onSelectAsync,\n}: ProductInsightsCardListProps) => {\n if (!initialStates.length) {\n return (\n <Box display=\"flex\" justifyContent=\"space-around\" alignItems=\"center\">\n <CircularProgress />\n </Box>\n );\n }\n\n return (\n <Collapse in timeout={1000}>\n {initialStates.map(({ product, entity, duration }) => (\n <Box\n key={product.kind}\n mb={6}\n position=\"relative\"\n data-testid={`product-list-item-${product.kind}`}\n >\n <ProductInsightsCard\n product={product}\n onSelectAsync={onSelectAsync}\n initialState={{ entity: entity, duration: duration }}\n />\n </Box>\n ))}\n </Collapse>\n );\n};\n"],"names":[],"mappings":";;;;;;AAgCO,MAAM,0BAA0B,CAAC;AAAA,EACtC,aAAA;AAAA,EACA;AACF,
|
|
1
|
+
{"version":3,"file":"ProductInsightsCardList.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductInsightsCardList.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Box from '@material-ui/core/Box';\nimport CircularProgress from '@material-ui/core/CircularProgress';\nimport Collapse from '@material-ui/core/Collapse';\nimport { ProductInsightsCard } from './ProductInsightsCard';\nimport { Duration } from '../../types';\nimport {\n Entity,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { ProductState } from '../../utils/loading';\n\ntype ProductInsightsCardListProps = {\n initialStates: ProductState[];\n onSelectAsync: (product: Product, duration: Duration) => Promise<Entity>;\n};\n\nexport const ProductInsightsCardList = ({\n initialStates,\n onSelectAsync,\n}: ProductInsightsCardListProps) => {\n if (!initialStates.length) {\n return (\n <Box display=\"flex\" justifyContent=\"space-around\" alignItems=\"center\">\n <CircularProgress />\n </Box>\n );\n }\n\n return (\n <Collapse in timeout={1000}>\n {initialStates.map(({ product, entity, duration }) => (\n <Box\n key={product.kind}\n mb={6}\n position=\"relative\"\n data-testid={`product-list-item-${product.kind}`}\n >\n <ProductInsightsCard\n product={product}\n onSelectAsync={onSelectAsync}\n initialState={{ entity: entity, duration: duration }}\n />\n </Box>\n ))}\n </Collapse>\n );\n};\n"],"names":[],"mappings":";;;;;;AAgCO,MAAM,0BAA0B,CAAC;AAAA,EACtC,aAAA;AAAA,EACA;AACF,CAAA,KAAoC;AAClC,EAAA,IAAI,CAAC,cAAc,MAAA,EAAQ;AACzB,IAAA,uBACE,GAAA,CAAC,GAAA,EAAA,EAAI,OAAA,EAAQ,MAAA,EAAO,cAAA,EAAe,gBAAe,UAAA,EAAW,QAAA,EAC3D,QAAA,kBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,CAAA,EACpB,CAAA;AAAA,EAEJ;AAEA,EAAA,uBACE,GAAA,CAAC,QAAA,EAAA,EAAS,EAAA,EAAE,IAAA,EAAC,OAAA,EAAS,GAAA,EACnB,QAAA,EAAA,aAAA,CAAc,GAAA,CAAI,CAAC,EAAE,OAAA,EAAS,MAAA,EAAQ,UAAS,qBAC9C,GAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MAEC,EAAA,EAAI,CAAA;AAAA,MACJ,QAAA,EAAS,UAAA;AAAA,MACT,aAAA,EAAa,CAAA,kBAAA,EAAqB,OAAA,CAAQ,IAAI,CAAA,CAAA;AAAA,MAE9C,QAAA,kBAAA,GAAA;AAAA,QAAC,mBAAA;AAAA,QAAA;AAAA,UACC,OAAA;AAAA,UACA,aAAA;AAAA,UACA,YAAA,EAAc,EAAE,MAAA,EAAgB,QAAA;AAAmB;AAAA;AACrD,KAAA;AAAA,IATK,OAAA,CAAQ;AAAA,GAWhB,CAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductInsightsChart.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductInsightsChart.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useMemo, useState } from 'react';\nimport pluralize from 'pluralize';\nimport Box from '@material-ui/core/Box';\nimport Typography from '@material-ui/core/Typography';\nimport { default as FullScreenIcon } from '@material-ui/icons/Fullscreen';\nimport { LegendItem } from '../LegendItem';\nimport { ProductEntityDialog } from './ProductEntityDialog';\nimport { CostGrowth, CostGrowthIndicator } from '../CostGrowth';\nimport {\n BarChart,\n BarChartLegend,\n BarChartTooltip,\n BarChartTooltipItem,\n BarChartLegendOptions,\n} from '../BarChart';\nimport {\n findAlways,\n notEmpty,\n isUndefined,\n findAnyKey,\n assertAlways,\n} from '../../utils/assert';\nimport { formatPeriod, formatChange } from '../../utils/formatters';\nimport {\n titleOf,\n tooltipItemOf,\n resourceOf,\n isInvalid,\n isLabeled,\n isUnlabeled,\n} from '../../utils/graphs';\nimport {\n useProductInsightsChartStyles as useStyles,\n useBarChartLayoutStyles as useLayoutStyles,\n} from '../../utils/styles';\nimport { Duration } from '../../types';\nimport {\n Entity,\n Maybe,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { choose } from '../../utils/change';\nimport { TooltipRenderer } from '../../types';\nimport { useConfig } from '../../hooks';\n\nexport type ProductInsightsChartProps = {\n billingDate: string;\n entity: Entity;\n duration: Duration;\n customDateRange?: { start: string; end: string };\n};\n\nexport const ProductInsightsChart = ({\n billingDate,\n entity,\n duration,\n customDateRange,\n}: ProductInsightsChartProps) => {\n const classes = useStyles();\n const layoutClasses = useLayoutStyles();\n const { baseCurrency } = useConfig();\n\n // Only a single entities Record for the root product entity is supported\n const entities = useMemo(() => {\n const entityLabel = assertAlways(findAnyKey(entity.entities));\n return entity.entities[entityLabel] ?? [];\n }, [entity]);\n\n const [activeLabel, setActive] = useState<Maybe<string>>();\n const [selectLabel, setSelected] = useState<Maybe<string>>();\n const isSelected = useMemo(() => !isUndefined(selectLabel), [selectLabel]);\n\n const isClickable = useMemo(() => {\n const breakdowns = Object.keys(\n entities.find(e => e.id === activeLabel)?.entities ?? {},\n );\n return breakdowns.length > 0;\n }, [entities, activeLabel]);\n\n const costStart = entity.aggregation[0];\n const costEnd = entity.aggregation[1];\n const resources = entities.map(resourceOf);\n\n const options: Partial<BarChartLegendOptions> = {\n previousName: formatPeriod(\n duration,\n billingDate,\n false,\n customDateRange,\n true,\n ),\n currentName: formatPeriod(\n duration,\n billingDate,\n true,\n customDateRange,\n true,\n ),\n };\n\n const onMouseMove: (...args: any[]) => void = (\n data: Record<'activeLabel', string | undefined>,\n ) => {\n if (isLabeled(data)) {\n setActive(data.activeLabel!);\n } else if (isUnlabeled(data)) {\n setActive(null);\n } else {\n setActive(undefined);\n }\n };\n\n const onClick: (...args: any[]) => void = (\n data: Record<'activeLabel', string>,\n ) => {\n if (isLabeled(data)) {\n setSelected(data.activeLabel);\n } else if (isUnlabeled(data)) {\n setSelected(null);\n } else {\n setSelected(undefined);\n }\n };\n\n const renderProductInsightsTooltip: TooltipRenderer = ({\n label,\n payload = [],\n }) => {\n /* Labels and payloads may be undefined or empty */\n if (isInvalid({ label, payload })) return null;\n\n /*\n * recharts coerces null values to strings\n * entity -> resource -> payload\n * { id: null } -> { name: null } -> { label: '' }\n */\n const id = label === '' ? null : label;\n\n const title = titleOf(label);\n const items = payload.map(tooltipItemOf(baseCurrency)).filter(notEmpty);\n\n const activeEntity = findAlways(entities, e => e.id === id);\n const breakdowns = Object.keys(activeEntity.entities);\n\n if (breakdowns.length) {\n const subtitle = breakdowns\n .map(b => pluralize(b, activeEntity.entities[b].length, true))\n .join(', ');\n return (\n <BarChartTooltip\n title={title}\n subtitle={subtitle}\n topRight={\n !!activeEntity.change.ratio && (\n <CostGrowthIndicator\n formatter={formatChange}\n change={activeEntity.change}\n className={classes.indicator}\n />\n )\n }\n actions={\n <Box className={classes.actions}>\n <FullScreenIcon />\n <Typography>Click for breakdown</Typography>\n </Box>\n }\n >\n {items.map((item, index) => (\n <BarChartTooltipItem key={`${item.label}-${index}`} item={item} />\n ))}\n </BarChartTooltip>\n );\n }\n\n // If an entity doesn't have any sub-entities, there aren't any costs to break down.\n return (\n <BarChartTooltip\n title={title}\n topRight={\n !!activeEntity.change.ratio && (\n <CostGrowthIndicator\n formatter={formatChange}\n change={activeEntity.change}\n className={classes.indicator}\n />\n )\n }\n content={\n id\n ? null\n : \"This product has costs that are not labeled and therefore can't be attributed to a specific entity.\"\n }\n >\n {items.map((item, index) => (\n <BarChartTooltipItem key={`${item.label}-${index}`} item={item} />\n ))}\n </BarChartTooltip>\n );\n };\n\n const barChartProps = isClickable ? { onClick } : {};\n\n return (\n <Box className={layoutClasses.wrapper}>\n <BarChartLegend costStart={costStart} costEnd={costEnd} options={options}>\n <LegendItem\n title={choose(['Cost Savings', 'Cost Excess'], entity.change)}\n >\n <CostGrowth\n change={entity.change}\n duration={duration}\n customDateRange={customDateRange}\n />\n </LegendItem>\n </BarChartLegend>\n <BarChart\n resources={resources}\n tooltip={renderProductInsightsTooltip}\n onMouseMove={onMouseMove}\n options={options}\n {...barChartProps}\n />\n {isSelected && entities.length && (\n <ProductEntityDialog\n open={isSelected}\n onClose={() => setSelected(undefined)}\n entity={findAlways(entities, e => e.id === selectLabel)}\n options={options}\n />\n )}\n </Box>\n );\n};\n"],"names":["useStyles","useLayoutStyles"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProductInsightsChart.esm.js","sources":["../../../src/components/ProductInsightsCard/ProductInsightsChart.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useMemo, useState } from 'react';\nimport pluralize from 'pluralize';\nimport Box from '@material-ui/core/Box';\nimport Typography from '@material-ui/core/Typography';\nimport { default as FullScreenIcon } from '@material-ui/icons/Fullscreen';\nimport { LegendItem } from '../LegendItem';\nimport { ProductEntityDialog } from './ProductEntityDialog';\nimport { CostGrowth, CostGrowthIndicator } from '../CostGrowth';\nimport {\n BarChart,\n BarChartLegend,\n BarChartTooltip,\n BarChartTooltipItem,\n BarChartLegendOptions,\n} from '../BarChart';\nimport {\n findAlways,\n notEmpty,\n isUndefined,\n findAnyKey,\n assertAlways,\n} from '../../utils/assert';\nimport { formatPeriod, formatChange } from '../../utils/formatters';\nimport {\n titleOf,\n tooltipItemOf,\n resourceOf,\n isInvalid,\n isLabeled,\n isUnlabeled,\n} from '../../utils/graphs';\nimport {\n useProductInsightsChartStyles as useStyles,\n useBarChartLayoutStyles as useLayoutStyles,\n} from '../../utils/styles';\nimport { Duration } from '../../types';\nimport {\n Entity,\n Maybe,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { choose } from '../../utils/change';\nimport { TooltipRenderer } from '../../types';\nimport { useConfig } from '../../hooks';\n\nexport type ProductInsightsChartProps = {\n billingDate: string;\n entity: Entity;\n duration: Duration;\n customDateRange?: { start: string; end: string };\n};\n\nexport const ProductInsightsChart = ({\n billingDate,\n entity,\n duration,\n customDateRange,\n}: ProductInsightsChartProps) => {\n const classes = useStyles();\n const layoutClasses = useLayoutStyles();\n const { baseCurrency } = useConfig();\n\n // Only a single entities Record for the root product entity is supported\n const entities = useMemo(() => {\n const entityLabel = assertAlways(findAnyKey(entity.entities));\n return entity.entities[entityLabel] ?? [];\n }, [entity]);\n\n const [activeLabel, setActive] = useState<Maybe<string>>();\n const [selectLabel, setSelected] = useState<Maybe<string>>();\n const isSelected = useMemo(() => !isUndefined(selectLabel), [selectLabel]);\n\n const isClickable = useMemo(() => {\n const breakdowns = Object.keys(\n entities.find(e => e.id === activeLabel)?.entities ?? {},\n );\n return breakdowns.length > 0;\n }, [entities, activeLabel]);\n\n const costStart = entity.aggregation[0];\n const costEnd = entity.aggregation[1];\n const resources = entities.map(resourceOf);\n\n const options: Partial<BarChartLegendOptions> = {\n previousName: formatPeriod(\n duration,\n billingDate,\n false,\n customDateRange,\n true,\n ),\n currentName: formatPeriod(\n duration,\n billingDate,\n true,\n customDateRange,\n true,\n ),\n };\n\n const onMouseMove: (...args: any[]) => void = (\n data: Record<'activeLabel', string | undefined>,\n ) => {\n if (isLabeled(data)) {\n setActive(data.activeLabel!);\n } else if (isUnlabeled(data)) {\n setActive(null);\n } else {\n setActive(undefined);\n }\n };\n\n const onClick: (...args: any[]) => void = (\n data: Record<'activeLabel', string>,\n ) => {\n if (isLabeled(data)) {\n setSelected(data.activeLabel);\n } else if (isUnlabeled(data)) {\n setSelected(null);\n } else {\n setSelected(undefined);\n }\n };\n\n const renderProductInsightsTooltip: TooltipRenderer = ({\n label,\n payload = [],\n }) => {\n /* Labels and payloads may be undefined or empty */\n if (isInvalid({ label, payload })) return null;\n\n /*\n * recharts coerces null values to strings\n * entity -> resource -> payload\n * { id: null } -> { name: null } -> { label: '' }\n */\n const id = label === '' ? null : label;\n\n const title = titleOf(label);\n const items = payload.map(tooltipItemOf(baseCurrency)).filter(notEmpty);\n\n const activeEntity = findAlways(entities, e => e.id === id);\n const breakdowns = Object.keys(activeEntity.entities);\n\n if (breakdowns.length) {\n const subtitle = breakdowns\n .map(b => pluralize(b, activeEntity.entities[b].length, true))\n .join(', ');\n return (\n <BarChartTooltip\n title={title}\n subtitle={subtitle}\n topRight={\n !!activeEntity.change.ratio && (\n <CostGrowthIndicator\n formatter={formatChange}\n change={activeEntity.change}\n className={classes.indicator}\n />\n )\n }\n actions={\n <Box className={classes.actions}>\n <FullScreenIcon />\n <Typography>Click for breakdown</Typography>\n </Box>\n }\n >\n {items.map((item, index) => (\n <BarChartTooltipItem key={`${item.label}-${index}`} item={item} />\n ))}\n </BarChartTooltip>\n );\n }\n\n // If an entity doesn't have any sub-entities, there aren't any costs to break down.\n return (\n <BarChartTooltip\n title={title}\n topRight={\n !!activeEntity.change.ratio && (\n <CostGrowthIndicator\n formatter={formatChange}\n change={activeEntity.change}\n className={classes.indicator}\n />\n )\n }\n content={\n id\n ? null\n : \"This product has costs that are not labeled and therefore can't be attributed to a specific entity.\"\n }\n >\n {items.map((item, index) => (\n <BarChartTooltipItem key={`${item.label}-${index}`} item={item} />\n ))}\n </BarChartTooltip>\n );\n };\n\n const barChartProps = isClickable ? { onClick } : {};\n\n return (\n <Box className={layoutClasses.wrapper}>\n <BarChartLegend costStart={costStart} costEnd={costEnd} options={options}>\n <LegendItem\n title={choose(['Cost Savings', 'Cost Excess'], entity.change)}\n >\n <CostGrowth\n change={entity.change}\n duration={duration}\n customDateRange={customDateRange}\n />\n </LegendItem>\n </BarChartLegend>\n <BarChart\n resources={resources}\n tooltip={renderProductInsightsTooltip}\n onMouseMove={onMouseMove}\n options={options}\n {...barChartProps}\n />\n {isSelected && entities.length && (\n <ProductEntityDialog\n open={isSelected}\n onClose={() => setSelected(undefined)}\n entity={findAlways(entities, e => e.id === selectLabel)}\n options={options}\n />\n )}\n </Box>\n );\n};\n"],"names":["useStyles","useLayoutStyles"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectGrowthAlertCard.esm.js","sources":["../../../src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport pluralize from 'pluralize';\nimport { ProjectGrowthAlertChart } from './ProjectGrowthAlertChart';\nimport { ProjectGrowthData } from '../../types';\nimport { InfoCard } from '@backstage/core-components';\n\ntype ProjectGrowthAlertProps = {\n alert: ProjectGrowthData;\n};\n\nexport const ProjectGrowthAlertCard = ({ alert }: ProjectGrowthAlertProps) => {\n const subheader = `\n ${pluralize('product', alert.products.length, true)}${\n alert.products.length > 1 ? ', sorted by cost' : ''\n }`;\n\n return (\n <InfoCard\n title={`Project growth for ${alert.project}`}\n subheader={subheader}\n >\n <ProjectGrowthAlertChart alert={alert} />\n </InfoCard>\n );\n};\n"],"names":[],"mappings":";;;;;AAyBO,MAAM,
|
|
1
|
+
{"version":3,"file":"ProjectGrowthAlertCard.esm.js","sources":["../../../src/components/ProjectGrowthAlertCard/ProjectGrowthAlertCard.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport pluralize from 'pluralize';\nimport { ProjectGrowthAlertChart } from './ProjectGrowthAlertChart';\nimport { ProjectGrowthData } from '../../types';\nimport { InfoCard } from '@backstage/core-components';\n\ntype ProjectGrowthAlertProps = {\n alert: ProjectGrowthData;\n};\n\nexport const ProjectGrowthAlertCard = ({ alert }: ProjectGrowthAlertProps) => {\n const subheader = `\n ${pluralize('product', alert.products.length, true)}${\n alert.products.length > 1 ? ', sorted by cost' : ''\n }`;\n\n return (\n <InfoCard\n title={`Project growth for ${alert.project}`}\n subheader={subheader}\n >\n <ProjectGrowthAlertChart alert={alert} />\n </InfoCard>\n );\n};\n"],"names":[],"mappings":";;;;;AAyBO,MAAM,sBAAA,GAAyB,CAAC,EAAE,KAAA,EAAM,KAA+B;AAC5E,EAAA,MAAM,SAAA,GAAY;AAAA,IAAA,EACd,SAAA,CAAU,SAAA,EAAW,KAAA,CAAM,QAAA,CAAS,MAAA,EAAQ,IAAI,CAAC,CAAA,EACnD,KAAA,CAAM,QAAA,CAAS,MAAA,GAAS,CAAA,GAAI,qBAAqB,EACnD,CAAA,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,mBAAA,EAAsB,KAAA,CAAM,OAAO,CAAA,CAAA;AAAA,MAC1C,SAAA;AAAA,MAEA,QAAA,kBAAA,GAAA,CAAC,2BAAwB,KAAA,EAAc;AAAA;AAAA,GACzC;AAEJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectGrowthAlertChart.esm.js","sources":["../../../src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DateTime } from 'luxon';\nimport Box from '@material-ui/core/Box';\nimport { BarChart, BarChartLegend, BarChartLegendOptions } from '../BarChart';\nimport { LegendItem } from '../LegendItem';\nimport { CostGrowth } from '../CostGrowth';\nimport { Duration, ProjectGrowthData } from '../../types';\nimport { useBarChartLayoutStyles as useStyles } from '../../utils/styles';\nimport { resourceOf } from '../../utils/graphs';\n\ntype ProjectGrowthAlertChartProps = {\n alert: ProjectGrowthData;\n};\n\nexport const ProjectGrowthAlertChart = ({\n alert,\n}: ProjectGrowthAlertChartProps) => {\n const classes = useStyles();\n\n const costStart = alert.aggregation[0];\n const costEnd = alert.aggregation[1];\n const resourceData = alert.products.map(resourceOf);\n\n const options: Partial<BarChartLegendOptions> = {\n previousName: DateTime.fromFormat(alert.periodStart, \"yyyy-'Q'q\").toFormat(\n \"'Q'q yyyy\",\n ),\n currentName: DateTime.fromFormat(alert.periodEnd, \"yyyy-'Q'q\").toFormat(\n \"'Q'q yyyy\",\n ),\n };\n\n return (\n <Box className={classes.wrapper}>\n <BarChartLegend costStart={costStart} costEnd={costEnd} options={options}>\n <LegendItem title=\"Cost Growth\">\n <CostGrowth change={alert.change} duration={Duration.P3M} />\n </LegendItem>\n </BarChartLegend>\n <BarChart resources={resourceData} options={options} />\n </Box>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6BO,MAAM,0BAA0B,CAAC;AAAA,EACtC;AACF,
|
|
1
|
+
{"version":3,"file":"ProjectGrowthAlertChart.esm.js","sources":["../../../src/components/ProjectGrowthAlertCard/ProjectGrowthAlertChart.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DateTime } from 'luxon';\nimport Box from '@material-ui/core/Box';\nimport { BarChart, BarChartLegend, BarChartLegendOptions } from '../BarChart';\nimport { LegendItem } from '../LegendItem';\nimport { CostGrowth } from '../CostGrowth';\nimport { Duration, ProjectGrowthData } from '../../types';\nimport { useBarChartLayoutStyles as useStyles } from '../../utils/styles';\nimport { resourceOf } from '../../utils/graphs';\n\ntype ProjectGrowthAlertChartProps = {\n alert: ProjectGrowthData;\n};\n\nexport const ProjectGrowthAlertChart = ({\n alert,\n}: ProjectGrowthAlertChartProps) => {\n const classes = useStyles();\n\n const costStart = alert.aggregation[0];\n const costEnd = alert.aggregation[1];\n const resourceData = alert.products.map(resourceOf);\n\n const options: Partial<BarChartLegendOptions> = {\n previousName: DateTime.fromFormat(alert.periodStart, \"yyyy-'Q'q\").toFormat(\n \"'Q'q yyyy\",\n ),\n currentName: DateTime.fromFormat(alert.periodEnd, \"yyyy-'Q'q\").toFormat(\n \"'Q'q yyyy\",\n ),\n };\n\n return (\n <Box className={classes.wrapper}>\n <BarChartLegend costStart={costStart} costEnd={costEnd} options={options}>\n <LegendItem title=\"Cost Growth\">\n <CostGrowth change={alert.change} duration={Duration.P3M} />\n </LegendItem>\n </BarChartLegend>\n <BarChart resources={resourceData} options={options} />\n </Box>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6BO,MAAM,0BAA0B,CAAC;AAAA,EACtC;AACF,CAAA,KAAoC;AAClC,EAAA,MAAM,UAAUA,uBAAA,EAAU;AAE1B,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,WAAA,CAAY,CAAC,CAAA;AACrC,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,WAAA,CAAY,CAAC,CAAA;AACnC,EAAA,MAAM,YAAA,GAAe,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,UAAU,CAAA;AAElD,EAAA,MAAM,OAAA,GAA0C;AAAA,IAC9C,cAAc,QAAA,CAAS,UAAA,CAAW,KAAA,CAAM,WAAA,EAAa,WAAW,CAAA,CAAE,QAAA;AAAA,MAChE;AAAA,KACF;AAAA,IACA,aAAa,QAAA,CAAS,UAAA,CAAW,KAAA,CAAM,SAAA,EAAW,WAAW,CAAA,CAAE,QAAA;AAAA,MAC7D;AAAA;AACF,GACF;AAEA,EAAA,uBACE,IAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,OAAA,EACtB,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,kBAAe,SAAA,EAAsB,OAAA,EAAkB,OAAA,EACtD,QAAA,kBAAA,GAAA,CAAC,cAAW,KAAA,EAAM,aAAA,EAChB,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,QAAQ,KAAA,CAAM,MAAA,EAAQ,UAAU,QAAA,CAAS,GAAA,EAAK,GAC5D,CAAA,EACF,CAAA;AAAA,oBACA,GAAA,CAAC,QAAA,EAAA,EAAS,SAAA,EAAW,YAAA,EAAc,OAAA,EAAkB;AAAA,GAAA,EACvD,CAAA;AAEJ;;;;"}
|
package/dist/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectGrowthInstructionsPage.esm.js","sources":["../../../src/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DateTime } from 'luxon';\nimport Box from '@material-ui/core/Box';\nimport Typography from '@material-ui/core/Typography';\nimport { AlertInstructionsLayout } from '../AlertInstructionsLayout';\nimport { ProductInsightsChart } from '../ProductInsightsCard';\nimport {\n Alert,\n DEFAULT_DATE_FORMAT,\n Duration,\n ProjectGrowthData,\n} from '../../types';\nimport {\n Entity,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { ProjectGrowthAlert } from '../../alerts';\nimport { InfoCard } from '@backstage/core-components';\n\nconst today = DateTime.now().toFormat(DEFAULT_DATE_FORMAT);\n\nexport const ProjectGrowthInstructionsPage = () => {\n const alertData: ProjectGrowthData = {\n project: 'example-project',\n periodStart: 'Q1 2020',\n periodEnd: 'Q2 2020',\n aggregation: [60000, 120000],\n change: {\n ratio: 1,\n amount: 60000,\n },\n products: [\n {\n id: 'Compute Engine',\n aggregation: [58000, 118000],\n },\n {\n id: 'Cloud Dataflow',\n aggregation: [1200, 1500],\n },\n {\n id: 'Cloud Storage',\n aggregation: [800, 500],\n },\n ],\n };\n\n const projectGrowthAlert: Alert = new ProjectGrowthAlert(alertData);\n\n const product: Product = {\n kind: 'ComputeEngine',\n name: 'Compute Engine',\n };\n\n const entity: Entity = {\n id: 'example-id',\n aggregation: [20_000, 60_000],\n change: {\n ratio: 3,\n amount: 40_000,\n },\n entities: {\n service: [\n {\n id: 'service-one',\n aggregation: [18_200, 58_500],\n entities: {},\n change: { ratio: 2.21, amount: 40_300 },\n },\n {\n id: 'service-two',\n aggregation: [1200, 1300],\n entities: {},\n change: { ratio: 0.083, amount: 100 },\n },\n {\n id: 'service-three',\n aggregation: [600, 200],\n entities: {},\n change: { ratio: -0.666, amount: -400 },\n },\n ],\n },\n };\n\n return (\n <AlertInstructionsLayout title=\"Investigating Growth\">\n <Typography variant=\"h1\">Investigating cloud cost growth</Typography>\n <Typography paragraph>\n Cost Insights shows an alert when costs for a particular billing entity,\n such as a GCP project, have grown at a rate faster than our alerting\n threshold. The responsible team should follow this guide to decide\n whether this warrants further investigation.\n </Typography>\n\n <Box mt={4}>\n <Typography variant=\"h3\">Is the growth expected?</Typography>\n <Typography paragraph>\n The first question to ask is whether growth is expected. Perhaps a new\n product has been deployed, or additional regions added for\n reliability.\n </Typography>\n <Typography paragraph>\n Many services increase cost linearly with load. Has the demand\n increased? This may happen as you open new markets, or run marketing\n offers. Costs should be compared against a business metric, such as\n daily users, to normalize natural increases from business growth.\n </Typography>\n <Typography paragraph>\n Seasonal variance may also cause cost growth; yearly campaigns, an\n increase in demand during certain times of year.\n </Typography>\n <Typography paragraph>\n Cloud costs will often go up before they go down, in the case of\n migrations. Teams moving to new infrastructure may run in both the old\n and new environment during the migration.\n </Typography>\n </Box>\n\n <Box mt={4}>\n <Typography variant=\"h3\">Is the growth significant?</Typography>\n <Typography paragraph>\n Next, evaluate whether the growth is significant. This helps avoid\n premature optimization, where cost in engineering time is more than\n would be saved from the optimization over a reasonable time frame.\n </Typography>\n <Typography paragraph>\n We recommend reframing the cost growth itself in terms of engineering\n time. How much engineering time, for an <i>average</i> fully-loaded\n engineer cost at the company, is being overspent each month? Compare\n this to expected engineering time for optimization to decide whether\n the optimization is worthwhile.\n </Typography>\n </Box>\n\n <Box mt={4}>\n <Typography variant=\"h3\">\n Identifying which cloud product contributed most\n </Typography>\n <Typography paragraph>\n For projects meeting the alert threshold, Cost Insights shows a cost\n comparison of cloud products over the examined time period:\n </Typography>\n <Box mt={2} mb={2}>\n {projectGrowthAlert.element}\n </Box>\n <Typography paragraph>\n This allows you to quickly see which cloud products contributed to the\n growth in cloud costs.\n </Typography>\n </Box>\n\n <Box mt={4}>\n <Typography variant=\"h3\">\n Identifying the responsible workload\n </Typography>\n <Typography paragraph>\n After identifying the cloud product, use the corresponding product\n panel in Cost Insights to find a particular workload (or <i>entity</i>\n ) that has grown in cost:\n </Typography>\n <Box mt={2} mb={2}>\n <InfoCard title={product.name} subheader=\"3 entities, sorted by cost\">\n <ProductInsightsChart\n billingDate={today}\n duration={Duration.P3M}\n entity={entity}\n />\n </InfoCard>\n </Box>\n <Typography paragraph>\n From here, you can dig into commit history or deployment logs to find\n probable causes of an unexpected spike in cost.\n </Typography>\n </Box>\n\n <Box mt={4}>\n <Typography variant=\"h3\">Optimizing the workload</Typography>\n <Typography paragraph>\n Workload optimization varies between cloud products, but there are a\n few general optimization areas to consider:\n </Typography>\n <Typography variant=\"h5\">Retention</Typography>\n <Typography paragraph>\n Is the workload or storage necessary? Truly idle or unused resources\n can be cleaned up for immediate cost savings. For storage, how long do\n we need the data? Many cloud products support retention policies to\n automatically delete data after a certain time period.\n </Typography>\n <Typography variant=\"h5\">Efficiency</Typography>\n <Typography paragraph>\n Is the workload using cloud resources efficiently? For compute\n resources, do the utilization metrics look reasonable? Autoscaling\n infrastructure, such as Kubernetes, can run workloads more efficiently\n without compromising reliability.\n </Typography>\n <Typography variant=\"h5\">Lifecycle</Typography>\n <Typography paragraph>\n Is the workload using an optimal pricing model? Some cloud products\n offer better pricing for data that is accessed less frequently.\n </Typography>\n </Box>\n </AlertInstructionsLayout>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectGrowthInstructionsPage.esm.js","sources":["../../../src/components/ProjectGrowthInstructionsPage/ProjectGrowthInstructionsPage.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DateTime } from 'luxon';\nimport Box from '@material-ui/core/Box';\nimport Typography from '@material-ui/core/Typography';\nimport { AlertInstructionsLayout } from '../AlertInstructionsLayout';\nimport { ProductInsightsChart } from '../ProductInsightsCard';\nimport {\n Alert,\n DEFAULT_DATE_FORMAT,\n Duration,\n ProjectGrowthData,\n} from '../../types';\nimport {\n Entity,\n Product,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { ProjectGrowthAlert } from '../../alerts';\nimport { InfoCard } from '@backstage/core-components';\n\nconst today = DateTime.now().toFormat(DEFAULT_DATE_FORMAT);\n\nexport const ProjectGrowthInstructionsPage = () => {\n const alertData: ProjectGrowthData = {\n project: 'example-project',\n periodStart: 'Q1 2020',\n periodEnd: 'Q2 2020',\n aggregation: [60000, 120000],\n change: {\n ratio: 1,\n amount: 60000,\n },\n products: [\n {\n id: 'Compute Engine',\n aggregation: [58000, 118000],\n },\n {\n id: 'Cloud Dataflow',\n aggregation: [1200, 1500],\n },\n {\n id: 'Cloud Storage',\n aggregation: [800, 500],\n },\n ],\n };\n\n const projectGrowthAlert: Alert = new ProjectGrowthAlert(alertData);\n\n const product: Product = {\n kind: 'ComputeEngine',\n name: 'Compute Engine',\n };\n\n const entity: Entity = {\n id: 'example-id',\n aggregation: [20_000, 60_000],\n change: {\n ratio: 3,\n amount: 40_000,\n },\n entities: {\n service: [\n {\n id: 'service-one',\n aggregation: [18_200, 58_500],\n entities: {},\n change: { ratio: 2.21, amount: 40_300 },\n },\n {\n id: 'service-two',\n aggregation: [1200, 1300],\n entities: {},\n change: { ratio: 0.083, amount: 100 },\n },\n {\n id: 'service-three',\n aggregation: [600, 200],\n entities: {},\n change: { ratio: -0.666, amount: -400 },\n },\n ],\n },\n };\n\n return (\n <AlertInstructionsLayout title=\"Investigating Growth\">\n <Typography variant=\"h1\">Investigating cloud cost growth</Typography>\n <Typography paragraph>\n Cost Insights shows an alert when costs for a particular billing entity,\n such as a GCP project, have grown at a rate faster than our alerting\n threshold. The responsible team should follow this guide to decide\n whether this warrants further investigation.\n </Typography>\n\n <Box mt={4}>\n <Typography variant=\"h3\">Is the growth expected?</Typography>\n <Typography paragraph>\n The first question to ask is whether growth is expected. Perhaps a new\n product has been deployed, or additional regions added for\n reliability.\n </Typography>\n <Typography paragraph>\n Many services increase cost linearly with load. Has the demand\n increased? This may happen as you open new markets, or run marketing\n offers. Costs should be compared against a business metric, such as\n daily users, to normalize natural increases from business growth.\n </Typography>\n <Typography paragraph>\n Seasonal variance may also cause cost growth; yearly campaigns, an\n increase in demand during certain times of year.\n </Typography>\n <Typography paragraph>\n Cloud costs will often go up before they go down, in the case of\n migrations. Teams moving to new infrastructure may run in both the old\n and new environment during the migration.\n </Typography>\n </Box>\n\n <Box mt={4}>\n <Typography variant=\"h3\">Is the growth significant?</Typography>\n <Typography paragraph>\n Next, evaluate whether the growth is significant. This helps avoid\n premature optimization, where cost in engineering time is more than\n would be saved from the optimization over a reasonable time frame.\n </Typography>\n <Typography paragraph>\n We recommend reframing the cost growth itself in terms of engineering\n time. How much engineering time, for an <i>average</i> fully-loaded\n engineer cost at the company, is being overspent each month? Compare\n this to expected engineering time for optimization to decide whether\n the optimization is worthwhile.\n </Typography>\n </Box>\n\n <Box mt={4}>\n <Typography variant=\"h3\">\n Identifying which cloud product contributed most\n </Typography>\n <Typography paragraph>\n For projects meeting the alert threshold, Cost Insights shows a cost\n comparison of cloud products over the examined time period:\n </Typography>\n <Box mt={2} mb={2}>\n {projectGrowthAlert.element}\n </Box>\n <Typography paragraph>\n This allows you to quickly see which cloud products contributed to the\n growth in cloud costs.\n </Typography>\n </Box>\n\n <Box mt={4}>\n <Typography variant=\"h3\">\n Identifying the responsible workload\n </Typography>\n <Typography paragraph>\n After identifying the cloud product, use the corresponding product\n panel in Cost Insights to find a particular workload (or <i>entity</i>\n ) that has grown in cost:\n </Typography>\n <Box mt={2} mb={2}>\n <InfoCard title={product.name} subheader=\"3 entities, sorted by cost\">\n <ProductInsightsChart\n billingDate={today}\n duration={Duration.P3M}\n entity={entity}\n />\n </InfoCard>\n </Box>\n <Typography paragraph>\n From here, you can dig into commit history or deployment logs to find\n probable causes of an unexpected spike in cost.\n </Typography>\n </Box>\n\n <Box mt={4}>\n <Typography variant=\"h3\">Optimizing the workload</Typography>\n <Typography paragraph>\n Workload optimization varies between cloud products, but there are a\n few general optimization areas to consider:\n </Typography>\n <Typography variant=\"h5\">Retention</Typography>\n <Typography paragraph>\n Is the workload or storage necessary? Truly idle or unused resources\n can be cleaned up for immediate cost savings. For storage, how long do\n we need the data? Many cloud products support retention policies to\n automatically delete data after a certain time period.\n </Typography>\n <Typography variant=\"h5\">Efficiency</Typography>\n <Typography paragraph>\n Is the workload using cloud resources efficiently? For compute\n resources, do the utilization metrics look reasonable? Autoscaling\n infrastructure, such as Kubernetes, can run workloads more efficiently\n without compromising reliability.\n </Typography>\n <Typography variant=\"h5\">Lifecycle</Typography>\n <Typography paragraph>\n Is the workload using an optimal pricing model? Some cloud products\n offer better pricing for data that is accessed less frequently.\n </Typography>\n </Box>\n </AlertInstructionsLayout>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectSelect.esm.js","sources":["../../../src/components/ProjectSelect/ProjectSelect.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { ChangeEvent } from 'react';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport Select from '@material-ui/core/Select';\nimport {\n Maybe,\n Project,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { useSelectStyles as useStyles } from '../../utils/styles';\n\ntype ProjectSelectProps = {\n project: Maybe<string>;\n projects: Array<Project>;\n onSelect: (project: Maybe<string>) => void;\n};\n\nexport const ProjectSelect = ({\n project,\n projects,\n onSelect,\n}: ProjectSelectProps) => {\n const classes = useStyles();\n\n const projectOptions = projects\n .filter(p => p.id)\n .sort((a, b) =>\n ((a.name ?? a.id) as string).localeCompare((b.name ?? b.id) as string),\n );\n\n const handleOnChange = (e: ChangeEvent<{ value: unknown }>) => {\n onSelect(e.target.value as string);\n };\n\n const renderValue = (value: unknown) => {\n const proj = value as string;\n const projectObj = projects.find(p => p.id === proj);\n return (\n <b data-testid={`selected-${proj}`}>\n {proj === 'all' ? 'All Projects' : projectObj?.name ?? proj}\n </b>\n );\n };\n\n return (\n <Select\n className={classes.select}\n variant=\"outlined\"\n value={project ?? 'all'}\n renderValue={renderValue}\n onChange={handleOnChange}\n data-testid=\"project-filter-select\"\n >\n {[{ id: 'all' }, ...projectOptions].map(proj => (\n <MenuItem\n className={`${classes.menuItem} compact`}\n key={proj.id}\n value={proj.id}\n data-testid={`option-${proj.id}`}\n >\n {proj.id === 'all' ? 'All Projects' : proj.name ?? proj.id}\n </MenuItem>\n ))}\n </Select>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;AA8BO,MAAM,gBAAgB,CAAC;AAAA,EAC5B,OAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,
|
|
1
|
+
{"version":3,"file":"ProjectSelect.esm.js","sources":["../../../src/components/ProjectSelect/ProjectSelect.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { ChangeEvent } from 'react';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport Select from '@material-ui/core/Select';\nimport {\n Maybe,\n Project,\n} from '@backstage-community/plugin-cost-insights-common';\nimport { useSelectStyles as useStyles } from '../../utils/styles';\n\ntype ProjectSelectProps = {\n project: Maybe<string>;\n projects: Array<Project>;\n onSelect: (project: Maybe<string>) => void;\n};\n\nexport const ProjectSelect = ({\n project,\n projects,\n onSelect,\n}: ProjectSelectProps) => {\n const classes = useStyles();\n\n const projectOptions = projects\n .filter(p => p.id)\n .sort((a, b) =>\n ((a.name ?? a.id) as string).localeCompare((b.name ?? b.id) as string),\n );\n\n const handleOnChange = (e: ChangeEvent<{ value: unknown }>) => {\n onSelect(e.target.value as string);\n };\n\n const renderValue = (value: unknown) => {\n const proj = value as string;\n const projectObj = projects.find(p => p.id === proj);\n return (\n <b data-testid={`selected-${proj}`}>\n {proj === 'all' ? 'All Projects' : projectObj?.name ?? proj}\n </b>\n );\n };\n\n return (\n <Select\n className={classes.select}\n variant=\"outlined\"\n value={project ?? 'all'}\n renderValue={renderValue}\n onChange={handleOnChange}\n data-testid=\"project-filter-select\"\n >\n {[{ id: 'all' }, ...projectOptions].map(proj => (\n <MenuItem\n className={`${classes.menuItem} compact`}\n key={proj.id}\n value={proj.id}\n data-testid={`option-${proj.id}`}\n >\n {proj.id === 'all' ? 'All Projects' : proj.name ?? proj.id}\n </MenuItem>\n ))}\n </Select>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;AA8BO,MAAM,gBAAgB,CAAC;AAAA,EAC5B,OAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,KAA0B;AACxB,EAAA,MAAM,UAAUA,eAAA,EAAU;AAE1B,EAAA,MAAM,iBAAiB,QAAA,CACpB,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,EAAE,CAAA,CAChB,IAAA;AAAA,IAAK,CAAC,CAAA,EAAG,CAAA,KAAA,CACN,CAAA,CAAE,IAAA,IAAQ,CAAA,CAAE,EAAA,EAAe,aAAA,CAAe,CAAA,CAAE,IAAA,IAAQ,CAAA,CAAE,EAAa;AAAA,GACvE;AAEF,EAAA,MAAM,cAAA,GAAiB,CAAC,CAAA,KAAuC;AAC7D,IAAA,QAAA,CAAS,CAAA,CAAE,OAAO,KAAe,CAAA;AAAA,EACnC,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAmB;AACtC,IAAA,MAAM,IAAA,GAAO,KAAA;AACb,IAAA,MAAM,aAAa,QAAA,CAAS,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,IAAI,CAAA;AACnD,IAAA,uBACE,GAAA,CAAC,GAAA,EAAA,EAAE,aAAA,EAAa,CAAA,SAAA,EAAY,IAAI,CAAA,CAAA,EAC7B,QAAA,EAAA,IAAA,KAAS,KAAA,GAAQ,cAAA,GAAiB,UAAA,EAAY,IAAA,IAAQ,IAAA,EACzD,CAAA;AAAA,EAEJ,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAA,CAAQ,MAAA;AAAA,MACnB,OAAA,EAAQ,UAAA;AAAA,MACR,OAAO,OAAA,IAAW,KAAA;AAAA,MAClB,WAAA;AAAA,MACA,QAAA,EAAU,cAAA;AAAA,MACV,aAAA,EAAY,uBAAA;AAAA,MAEX,QAAA,EAAA,CAAC,EAAE,EAAA,EAAI,KAAA,IAAS,GAAG,cAAc,CAAA,CAAE,GAAA,CAAI,CAAA,IAAA,qBACtC,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,SAAA,EAAW,CAAA,EAAG,OAAA,CAAQ,QAAQ,CAAA,QAAA,CAAA;AAAA,UAE9B,OAAO,IAAA,CAAK,EAAA;AAAA,UACZ,aAAA,EAAa,CAAA,OAAA,EAAU,IAAA,CAAK,EAAE,CAAA,CAAA;AAAA,UAE7B,eAAK,EAAA,KAAO,KAAA,GAAQ,cAAA,GAAiB,IAAA,CAAK,QAAQ,IAAA,CAAK;AAAA,SAAA;AAAA,QAJnD,IAAA,CAAK;AAAA,OAMb;AAAA;AAAA,GACH;AAEJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnlabeledDataflowAlertCard.esm.js","sources":["../../../src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport pluralize from 'pluralize';\nimport Box from '@material-ui/core/Box';\nimport { BarChart, BarChartLegend } from '../BarChart';\nimport { UnlabeledDataflowData, ResourceData } from '../../types';\nimport { useBarChartLayoutStyles as useStyles } from '../../utils/styles';\nimport { InfoCard } from '@backstage/core-components';\n\ntype UnlabeledDataflowAlertProps = {\n alert: UnlabeledDataflowData;\n};\n\nexport const UnlabeledDataflowAlertCard = ({\n alert,\n}: UnlabeledDataflowAlertProps) => {\n const classes = useStyles();\n const projects = pluralize('project', alert.projects.length, true);\n const subheader = `\n Showing costs from ${projects} with unlabeled Dataflow jobs in the last 30 days.\n `;\n const options = {\n previousName: 'Unlabeled Cost',\n currentName: 'Labeled Cost',\n };\n\n const resources: ResourceData[] = alert.projects.map(project => ({\n name: project.id,\n previous: project.unlabeledCost,\n current: project.labeledCost,\n }));\n\n return (\n <InfoCard title=\"Label Dataflow\" subheader={subheader}>\n <Box className={classes.wrapper}>\n <BarChartLegend\n costStart={alert.unlabeledCost}\n costEnd={alert.labeledCost}\n options={options}\n />\n <BarChart resources={resources} options={options} />\n </Box>\n </InfoCard>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;;;;;;;;AA2BO,MAAM,6BAA6B,CAAC;AAAA,EACzC;AACF,
|
|
1
|
+
{"version":3,"file":"UnlabeledDataflowAlertCard.esm.js","sources":["../../../src/components/UnlabeledDataflowAlertCard/UnlabeledDataflowAlertCard.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport pluralize from 'pluralize';\nimport Box from '@material-ui/core/Box';\nimport { BarChart, BarChartLegend } from '../BarChart';\nimport { UnlabeledDataflowData, ResourceData } from '../../types';\nimport { useBarChartLayoutStyles as useStyles } from '../../utils/styles';\nimport { InfoCard } from '@backstage/core-components';\n\ntype UnlabeledDataflowAlertProps = {\n alert: UnlabeledDataflowData;\n};\n\nexport const UnlabeledDataflowAlertCard = ({\n alert,\n}: UnlabeledDataflowAlertProps) => {\n const classes = useStyles();\n const projects = pluralize('project', alert.projects.length, true);\n const subheader = `\n Showing costs from ${projects} with unlabeled Dataflow jobs in the last 30 days.\n `;\n const options = {\n previousName: 'Unlabeled Cost',\n currentName: 'Labeled Cost',\n };\n\n const resources: ResourceData[] = alert.projects.map(project => ({\n name: project.id,\n previous: project.unlabeledCost,\n current: project.labeledCost,\n }));\n\n return (\n <InfoCard title=\"Label Dataflow\" subheader={subheader}>\n <Box className={classes.wrapper}>\n <BarChartLegend\n costStart={alert.unlabeledCost}\n costEnd={alert.labeledCost}\n options={options}\n />\n <BarChart resources={resources} options={options} />\n </Box>\n </InfoCard>\n );\n};\n"],"names":["useStyles"],"mappings":";;;;;;;;;;;;AA2BO,MAAM,6BAA6B,CAAC;AAAA,EACzC;AACF,CAAA,KAAmC;AACjC,EAAA,MAAM,UAAUA,uBAAA,EAAU;AAC1B,EAAA,MAAM,WAAW,SAAA,CAAU,SAAA,EAAW,KAAA,CAAM,QAAA,CAAS,QAAQ,IAAI,CAAA;AACjE,EAAA,MAAM,SAAA,GAAY;AAAA,uBAAA,EACK,QAAQ,CAAA;AAAA,EAAA,CAAA;AAE/B,EAAA,MAAM,OAAA,GAAU;AAAA,IACd,YAAA,EAAc,gBAAA;AAAA,IACd,WAAA,EAAa;AAAA,GACf;AAEA,EAAA,MAAM,SAAA,GAA4B,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,CAAA,OAAA,MAAY;AAAA,IAC/D,MAAM,OAAA,CAAQ,EAAA;AAAA,IACd,UAAU,OAAA,CAAQ,aAAA;AAAA,IAClB,SAAS,OAAA,CAAQ;AAAA,GACnB,CAAE,CAAA;AAEF,EAAA,uBACE,GAAA,CAAC,YAAS,KAAA,EAAM,gBAAA,EAAiB,WAC/B,QAAA,kBAAA,IAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,OAAA,EACtB,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,cAAA;AAAA,MAAA;AAAA,QACC,WAAW,KAAA,CAAM,aAAA;AAAA,QACjB,SAAS,KAAA,CAAM,WAAA;AAAA,QACf;AAAA;AAAA,KACF;AAAA,oBACA,GAAA,CAAC,QAAA,EAAA,EAAS,SAAA,EAAsB,OAAA,EAAkB;AAAA,GAAA,EACpD,CAAA,EACF,CAAA;AAEJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WhyCostsMatter.esm.js","sources":["../../../src/components/WhyCostsMatter/WhyCostsMatter.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Typography from '@material-ui/core/Typography';\nimport Box from '@material-ui/core/Box';\nimport Grid from '@material-ui/core/Grid';\nimport Container from '@material-ui/core/Container';\nimport Divider from '@material-ui/core/Divider';\n\nexport const WhyCostsMatter = () => {\n return (\n <Box mt={10} mb={4}>\n <Container maxWidth=\"md\">\n <Box mt={2} mb={2}>\n <Typography variant=\"h5\" align=\"center\" gutterBottom>\n Why cloud costs matter\n </Typography>\n </Box>\n <Grid container alignContent=\"space-around\" spacing={3} wrap=\"nowrap\">\n <Grid item>\n <Typography variant=\"h6\">\n Sustainability{' '}\n <Typography component=\"span\" role=\"img\" aria-label=\"globe\">\n 🌎\n </Typography>\n </Typography>\n <Typography>\n Reducing cloud usage improves our carbon footprint.\n </Typography>\n </Grid>\n <Grid item>\n <Divider orientation=\"vertical\" />\n </Grid>\n <Grid item>\n <Typography variant=\"h6\">\n Revenue{' '}\n <Typography\n component=\"span\"\n role=\"img\"\n aria-label=\"money-with-wings\"\n >\n 💸\n </Typography>\n </Typography>\n <Typography>\n Keeping cloud costs well-tuned prevents infrastructure from eating\n into revenue.\n </Typography>\n </Grid>\n <Grid item>\n <Divider orientation=\"vertical\" />\n </Grid>\n <Grid item>\n <Typography variant=\"h6\">\n Innovation{' '}\n <Typography component=\"span\" role=\"img\" aria-label=\"medal\">\n 🥇\n </Typography>\n </Typography>\n <Typography>\n The more we save, the more we can reinvest in speed and\n innovation.\n </Typography>\n </Grid>\n </Grid>\n </Container>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAsBO,MAAM,iBAAiB,MAAM;AAClC,
|
|
1
|
+
{"version":3,"file":"WhyCostsMatter.esm.js","sources":["../../../src/components/WhyCostsMatter/WhyCostsMatter.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport Typography from '@material-ui/core/Typography';\nimport Box from '@material-ui/core/Box';\nimport Grid from '@material-ui/core/Grid';\nimport Container from '@material-ui/core/Container';\nimport Divider from '@material-ui/core/Divider';\n\nexport const WhyCostsMatter = () => {\n return (\n <Box mt={10} mb={4}>\n <Container maxWidth=\"md\">\n <Box mt={2} mb={2}>\n <Typography variant=\"h5\" align=\"center\" gutterBottom>\n Why cloud costs matter\n </Typography>\n </Box>\n <Grid container alignContent=\"space-around\" spacing={3} wrap=\"nowrap\">\n <Grid item>\n <Typography variant=\"h6\">\n Sustainability{' '}\n <Typography component=\"span\" role=\"img\" aria-label=\"globe\">\n 🌎\n </Typography>\n </Typography>\n <Typography>\n Reducing cloud usage improves our carbon footprint.\n </Typography>\n </Grid>\n <Grid item>\n <Divider orientation=\"vertical\" />\n </Grid>\n <Grid item>\n <Typography variant=\"h6\">\n Revenue{' '}\n <Typography\n component=\"span\"\n role=\"img\"\n aria-label=\"money-with-wings\"\n >\n 💸\n </Typography>\n </Typography>\n <Typography>\n Keeping cloud costs well-tuned prevents infrastructure from eating\n into revenue.\n </Typography>\n </Grid>\n <Grid item>\n <Divider orientation=\"vertical\" />\n </Grid>\n <Grid item>\n <Typography variant=\"h6\">\n Innovation{' '}\n <Typography component=\"span\" role=\"img\" aria-label=\"medal\">\n 🥇\n </Typography>\n </Typography>\n <Typography>\n The more we save, the more we can reinvest in speed and\n innovation.\n </Typography>\n </Grid>\n </Grid>\n </Container>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAsBO,MAAM,iBAAiB,MAAM;AAClC,EAAA,uBACE,GAAA,CAAC,OAAI,EAAA,EAAI,EAAA,EAAI,IAAI,CAAA,EACf,QAAA,kBAAA,IAAA,CAAC,SAAA,EAAA,EAAU,QAAA,EAAS,IAAA,EAClB,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,GACd,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,IAAA,EAAK,KAAA,EAAM,QAAA,EAAS,YAAA,EAAY,IAAA,EAAC,oCAErD,CAAA,EACF,CAAA;AAAA,oBACA,IAAA,CAAC,QAAK,SAAA,EAAS,IAAA,EAAC,cAAa,cAAA,EAAe,OAAA,EAAS,CAAA,EAAG,IAAA,EAAK,QAAA,EAC3D,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,MAAI,IAAA,EACR,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,IAAA,EAAK,QAAA,EAAA;AAAA,UAAA,gBAAA;AAAA,UACR,GAAA;AAAA,0BACf,GAAA,CAAC,cAAW,SAAA,EAAU,MAAA,EAAO,MAAK,KAAA,EAAM,YAAA,EAAW,SAAQ,QAAA,EAAA,WAAA,EAE3D;AAAA,SAAA,EACF,CAAA;AAAA,wBACA,GAAA,CAAC,cAAW,QAAA,EAAA,qDAAA,EAEZ;AAAA,OAAA,EACF,CAAA;AAAA,sBACA,GAAA,CAAC,QAAK,IAAA,EAAI,IAAA,EACR,8BAAC,OAAA,EAAA,EAAQ,WAAA,EAAY,YAAW,CAAA,EAClC,CAAA;AAAA,sBACA,IAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EACR,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,IAAA,EAAK,QAAA,EAAA;AAAA,UAAA,SAAA;AAAA,UACf,GAAA;AAAA,0BACR,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,MAAA;AAAA,cACV,IAAA,EAAK,KAAA;AAAA,cACL,YAAA,EAAW,kBAAA;AAAA,cACZ,QAAA,EAAA;AAAA;AAAA;AAED,SAAA,EACF,CAAA;AAAA,wBACA,GAAA,CAAC,cAAW,QAAA,EAAA,kFAAA,EAGZ;AAAA,OAAA,EACF,CAAA;AAAA,sBACA,GAAA,CAAC,QAAK,IAAA,EAAI,IAAA,EACR,8BAAC,OAAA,EAAA,EAAQ,WAAA,EAAY,YAAW,CAAA,EAClC,CAAA;AAAA,sBACA,IAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EACR,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,IAAA,EAAK,QAAA,EAAA;AAAA,UAAA,YAAA;AAAA,UACZ,GAAA;AAAA,0BACX,GAAA,CAAC,cAAW,SAAA,EAAU,MAAA,EAAO,MAAK,KAAA,EAAM,YAAA,EAAW,SAAQ,QAAA,EAAA,WAAA,EAE3D;AAAA,SAAA,EACF,CAAA;AAAA,wBACA,GAAA,CAAC,cAAW,QAAA,EAAA,qEAAA,EAGZ;AAAA,OAAA,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EACF,CAAA,EACF,CAAA;AAEJ;;;;"}
|