@red-hat-developer-hub/backstage-plugin-adoption-insights 0.3.0 → 0.3.2
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 +16 -0
- package/dist/components/CardWrapper/CardWrapper.esm.js +20 -32
- package/dist/components/CardWrapper/CardWrapper.esm.js.map +1 -1
- package/dist/components/Common/ChartTooltip.esm.js +35 -28
- package/dist/components/Common/ChartTooltip.esm.js.map +1 -1
- package/dist/utils/utils.esm.js +11 -7
- package/dist/utils/utils.esm.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @red-hat-developer-hub/backstage-plugin-adoption-insights
|
|
2
2
|
|
|
3
|
+
## 0.3.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f86957a: Improve card styling consistency with Backstage design system
|
|
8
|
+
|
|
9
|
+
- Remove explicit borders from CardWrapper component to match standard Backstage card styling
|
|
10
|
+
- Replace custom border styling with Material-UI Paper elevation system for consistent visual appearance
|
|
11
|
+
- Update Divider component to use default styling without custom borders
|
|
12
|
+
|
|
13
|
+
## 0.3.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- fb54d82: safely parse the date so it works in different Node.js/browser environments
|
|
18
|
+
|
|
3
19
|
## 0.3.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
|
@@ -9,38 +9,26 @@ const CardWrapper = ({
|
|
|
9
9
|
title,
|
|
10
10
|
filter
|
|
11
11
|
}) => {
|
|
12
|
-
return /* @__PURE__ */ jsxs(
|
|
13
|
-
Box,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
filter && /* @__PURE__ */ jsx(Box, { children: filter })
|
|
33
|
-
] }),
|
|
34
|
-
/* @__PURE__ */ jsx(
|
|
35
|
-
Divider,
|
|
36
|
-
{
|
|
37
|
-
sx: { border: (theme) => `1px solid ${theme.palette.grey[300]}` }
|
|
38
|
-
}
|
|
39
|
-
),
|
|
40
|
-
/* @__PURE__ */ jsx(Box, { children })
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
);
|
|
12
|
+
return /* @__PURE__ */ jsxs(Paper, { elevation: 1, children: [
|
|
13
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", justifyContent: "space-between" }, children: [
|
|
14
|
+
/* @__PURE__ */ jsx(
|
|
15
|
+
Typography,
|
|
16
|
+
{
|
|
17
|
+
variant: "h5",
|
|
18
|
+
sx: {
|
|
19
|
+
p: 3,
|
|
20
|
+
display: "flex",
|
|
21
|
+
alignItems: "center",
|
|
22
|
+
fontWeight: "bold"
|
|
23
|
+
},
|
|
24
|
+
children: title
|
|
25
|
+
}
|
|
26
|
+
),
|
|
27
|
+
filter && /* @__PURE__ */ jsx(Box, { children: filter })
|
|
28
|
+
] }),
|
|
29
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
30
|
+
/* @__PURE__ */ jsx(Box, { children })
|
|
31
|
+
] });
|
|
44
32
|
};
|
|
45
33
|
|
|
46
34
|
export { CardWrapper as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CardWrapper.esm.js","sources":["../../../src/components/CardWrapper/CardWrapper.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { ReactNode, HTMLProps, FC } from 'react';\n\nimport Paper from '@mui/material/Paper';\nimport Box from '@mui/material/Box';\nimport Divider from '@mui/material/Divider';\nimport Typography from '@mui/material/Typography';\n\ninterface CardWrapperProps extends HTMLProps<HTMLDivElement> {\n children: ReactNode;\n title: string;\n filter?: ReactNode;\n}\n\nconst CardWrapper: FC<CardWrapperProps> = ({\n children,\n title,\n filter,\n}: CardWrapperProps) => {\n return (\n <
|
|
1
|
+
{"version":3,"file":"CardWrapper.esm.js","sources":["../../../src/components/CardWrapper/CardWrapper.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { ReactNode, HTMLProps, FC } from 'react';\n\nimport Paper from '@mui/material/Paper';\nimport Box from '@mui/material/Box';\nimport Divider from '@mui/material/Divider';\nimport Typography from '@mui/material/Typography';\n\ninterface CardWrapperProps extends HTMLProps<HTMLDivElement> {\n children: ReactNode;\n title: string;\n filter?: ReactNode;\n}\n\nconst CardWrapper: FC<CardWrapperProps> = ({\n children,\n title,\n filter,\n}: CardWrapperProps) => {\n return (\n <Paper elevation={1}>\n <Box sx={{ display: 'flex', justifyContent: 'space-between' }}>\n <Typography\n variant=\"h5\"\n sx={{\n p: 3,\n display: 'flex',\n alignItems: 'center',\n fontWeight: 'bold',\n }}\n >\n {title}\n </Typography>\n\n {filter && <Box>{filter}</Box>}\n </Box>\n <Divider />\n <Box>{children}</Box>\n </Paper>\n );\n};\n\nexport default CardWrapper;\n"],"names":[],"mappings":";;;;;;AA4BA,MAAM,cAAoC,CAAC;AAAA,EACzC,QAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAAwB,KAAA;AACtB,EACE,uBAAA,IAAA,CAAC,KAAM,EAAA,EAAA,SAAA,EAAW,CAChB,EAAA,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,OAAI,EAAI,EAAA,EAAE,SAAS,MAAQ,EAAA,cAAA,EAAgB,iBAC1C,EAAA,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,OAAQ,EAAA,IAAA;AAAA,UACR,EAAI,EAAA;AAAA,YACF,CAAG,EAAA,CAAA;AAAA,YACH,OAAS,EAAA,MAAA;AAAA,YACT,UAAY,EAAA,QAAA;AAAA,YACZ,UAAY,EAAA;AAAA,WACd;AAAA,UAEC,QAAA,EAAA;AAAA;AAAA,OACH;AAAA,MAEC,MAAA,oBAAW,GAAA,CAAA,GAAA,EAAA,EAAK,QAAO,EAAA,MAAA,EAAA;AAAA,KAC1B,EAAA,CAAA;AAAA,wBACC,OAAQ,EAAA,EAAA,CAAA;AAAA,oBACT,GAAA,CAAC,OAAK,QAAS,EAAA;AAAA,GACjB,EAAA,CAAA;AAEJ;;;;"}
|
|
@@ -3,7 +3,7 @@ import Paper from '@mui/material/Paper';
|
|
|
3
3
|
import Typography from '@mui/material/Typography';
|
|
4
4
|
import { useTheme } from '@mui/material/styles';
|
|
5
5
|
import Box from '@mui/material/Box';
|
|
6
|
-
import { formatTooltipHeaderLabel, formatLongDate, formatDateWithRange, formatWeeklyBucket, formatHourlyBucket } from '../../utils/utils.esm.js';
|
|
6
|
+
import { safeDate, formatTooltipHeaderLabel, formatLongDate, formatDateWithRange, formatWeeklyBucket, formatHourlyBucket } from '../../utils/utils.esm.js';
|
|
7
7
|
import { useDateRange } from '../Header/DateRangeContext.esm.js';
|
|
8
8
|
import { useTranslation } from '../../hooks/useTranslation.esm.js';
|
|
9
9
|
import { useLanguage } from '../../hooks/useLanguage.esm.js';
|
|
@@ -46,7 +46,7 @@ const ChartTooltip = ({
|
|
|
46
46
|
if (!active || !payload || payload?.length === 0) {
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
|
-
const date = label ?
|
|
49
|
+
const date = label ? safeDate(label) : /* @__PURE__ */ new Date();
|
|
50
50
|
return /* @__PURE__ */ jsxs(
|
|
51
51
|
Paper,
|
|
52
52
|
{
|
|
@@ -65,34 +65,41 @@ const ChartTooltip = ({
|
|
|
65
65
|
fontWeight: 500,
|
|
66
66
|
marginBottom: "12px"
|
|
67
67
|
},
|
|
68
|
-
children: formatBucketLabel(date).split("\n").map((line,
|
|
68
|
+
children: formatBucketLabel(date).split("\n").map((line, index) => /* @__PURE__ */ jsx("div", { children: line }, `tooltip-line-${line.substring(0, 10)}-${index}`))
|
|
69
69
|
}
|
|
70
70
|
),
|
|
71
|
-
/* @__PURE__ */ jsx(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", children: payload.map(({ dataKey, value }, index) => /* @__PURE__ */ jsxs(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
71
|
+
/* @__PURE__ */ jsx(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", children: payload.map(({ dataKey, value }, index) => /* @__PURE__ */ jsxs(
|
|
72
|
+
Box,
|
|
73
|
+
{
|
|
74
|
+
mr: index === payload.length - 1 ? 0 : 3,
|
|
75
|
+
children: [
|
|
76
|
+
/* @__PURE__ */ jsx(
|
|
77
|
+
Typography,
|
|
78
|
+
{
|
|
79
|
+
sx: {
|
|
80
|
+
fontSize: "0.875rem",
|
|
81
|
+
fontWeight: 500,
|
|
82
|
+
color: theme.palette.text.secondary
|
|
83
|
+
},
|
|
84
|
+
children: getLabel(dataKey)
|
|
85
|
+
}
|
|
86
|
+
),
|
|
87
|
+
/* @__PURE__ */ jsx(
|
|
88
|
+
Typography,
|
|
89
|
+
{
|
|
90
|
+
sx: {
|
|
91
|
+
fontSize: "2.5rem",
|
|
92
|
+
fontWeight: 500,
|
|
93
|
+
color: "#009596",
|
|
94
|
+
lineHeight: 1.2
|
|
95
|
+
},
|
|
96
|
+
children: value
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
`tooltip-value-${dataKey}-${value}-${index}`
|
|
102
|
+
)) })
|
|
96
103
|
]
|
|
97
104
|
}
|
|
98
105
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartTooltip.esm.js","sources":["../../../src/components/Common/ChartTooltip.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 Paper from '@mui/material/Paper';\nimport Typography from '@mui/material/Typography';\nimport { useTheme } from '@mui/material/styles';\nimport Box from '@mui/material/Box';\nimport { TranslationFunction } from '@backstage/core-plugin-api/alpha';\nimport {\n formatHourlyBucket,\n formatDateWithRange,\n formatTooltipHeaderLabel,\n formatWeeklyBucket,\n formatLongDate,\n} from '../../utils/utils';\nimport { useDateRange } from '../Header/DateRangeContext';\n\nimport { useTranslation } from '../../hooks/useTranslation';\nimport { useLanguage } from '../../hooks/useLanguage';\nimport { adoptionInsightsTranslationRef } from '../../translations';\n\nconst getLabelOverrides = (\n t: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n) => ({\n count: t('common.numberOfSearches'),\n new_users: t('activeUsers.legend.newUsers'),\n returning_users: t('activeUsers.legend.returningUsers'),\n});\n\nconst ChartTooltip = ({\n active,\n payload,\n label,\n grouping,\n}: {\n active?: boolean;\n payload?: any[];\n label?: string;\n grouping?: string;\n}) => {\n const theme = useTheme();\n const { startDateRange, endDateRange } = useDateRange();\n const { t } = useTranslation();\n const locale = useLanguage();\n const labelOverrides = getLabelOverrides(t);\n\n const getLabel = (key: string) =>\n (labelOverrides as any)[key] || formatTooltipHeaderLabel(key);\n\n const formatBucketLabel = (date: Date) => {\n switch (grouping) {\n case 'hourly':\n return formatHourlyBucket(date, locale);\n case 'weekly':\n return formatWeeklyBucket(date, locale);\n case 'monthly':\n return formatDateWithRange(\n date,\n startDateRange,\n endDateRange,\n t,\n locale,\n );\n default:\n return formatLongDate(date, locale);\n }\n };\n\n if (!active || !payload || payload?.length === 0) {\n return null;\n }\n\n // Parse date from chart label - chart data typically provides ISO strings or timestamps\n const date = label ?
|
|
1
|
+
{"version":3,"file":"ChartTooltip.esm.js","sources":["../../../src/components/Common/ChartTooltip.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 Paper from '@mui/material/Paper';\nimport Typography from '@mui/material/Typography';\nimport { useTheme } from '@mui/material/styles';\nimport Box from '@mui/material/Box';\nimport { TranslationFunction } from '@backstage/core-plugin-api/alpha';\nimport {\n formatHourlyBucket,\n formatDateWithRange,\n formatTooltipHeaderLabel,\n formatWeeklyBucket,\n formatLongDate,\n safeDate,\n} from '../../utils/utils';\nimport { useDateRange } from '../Header/DateRangeContext';\n\nimport { useTranslation } from '../../hooks/useTranslation';\nimport { useLanguage } from '../../hooks/useLanguage';\nimport { adoptionInsightsTranslationRef } from '../../translations';\n\nconst getLabelOverrides = (\n t: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n) => ({\n count: t('common.numberOfSearches'),\n new_users: t('activeUsers.legend.newUsers'),\n returning_users: t('activeUsers.legend.returningUsers'),\n});\n\nconst ChartTooltip = ({\n active,\n payload,\n label,\n grouping,\n}: {\n active?: boolean;\n payload?: any[];\n label?: string;\n grouping?: string;\n}) => {\n const theme = useTheme();\n const { startDateRange, endDateRange } = useDateRange();\n const { t } = useTranslation();\n const locale = useLanguage();\n const labelOverrides = getLabelOverrides(t);\n\n const getLabel = (key: string) =>\n (labelOverrides as any)[key] || formatTooltipHeaderLabel(key);\n\n const formatBucketLabel = (date: Date) => {\n switch (grouping) {\n case 'hourly':\n return formatHourlyBucket(date, locale);\n case 'weekly':\n return formatWeeklyBucket(date, locale);\n case 'monthly':\n return formatDateWithRange(\n date,\n startDateRange,\n endDateRange,\n t,\n locale,\n );\n default:\n return formatLongDate(date, locale);\n }\n };\n\n if (!active || !payload || payload?.length === 0) {\n return null;\n }\n\n // Parse date from chart label - chart data typically provides ISO strings or timestamps\n const date = label ? safeDate(label) : new Date();\n\n return (\n <Paper\n elevation={1}\n sx={{\n padding: '12px 16px',\n boxShadow: 4,\n borderRadius: 2,\n }}\n >\n <Typography\n sx={{\n fontSize: '0.875rem',\n fontWeight: 500,\n marginBottom: '12px',\n }}\n >\n {formatBucketLabel(date)\n .split('\\n')\n .map((line, index) => (\n <div key={`tooltip-line-${line.substring(0, 10)}-${index}`}>\n {line}\n </div>\n ))}\n </Typography>\n\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\">\n {payload.map(({ dataKey, value }, index) => (\n <Box\n key={`tooltip-value-${dataKey}-${value}-${index}`}\n mr={index === payload.length - 1 ? 0 : 3}\n >\n <Typography\n sx={{\n fontSize: '0.875rem',\n fontWeight: 500,\n color: theme.palette.text.secondary,\n }}\n >\n {getLabel(dataKey)}\n </Typography>\n <Typography\n sx={{\n fontSize: '2.5rem',\n fontWeight: 500,\n color: '#009596',\n lineHeight: 1.2,\n }}\n >\n {value}\n </Typography>\n </Box>\n ))}\n </Box>\n </Paper>\n );\n};\n\nexport default ChartTooltip;\n"],"names":["date"],"mappings":"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"}
|
package/dist/utils/utils.esm.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { subDays, isToday, isYesterday, startOfWeek, addDays, getYear, addHours } from 'date-fns';
|
|
2
2
|
import { utcToZonedTime, formatInTimeZone } from 'date-fns-tz';
|
|
3
3
|
|
|
4
|
+
const safeDate = (dateString) => {
|
|
5
|
+
const normalizedDate = dateString.replace(/\+00$/, "Z");
|
|
6
|
+
return new Date(normalizedDate);
|
|
7
|
+
};
|
|
4
8
|
const formatDate = (date, options = {}, locale) => {
|
|
5
9
|
const currentLocale = locale || "en";
|
|
6
10
|
const currentTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
@@ -90,18 +94,18 @@ const getXAxisTickValues = (data, grouping) => {
|
|
|
90
94
|
}
|
|
91
95
|
};
|
|
92
96
|
if (grouping === "hourly") {
|
|
93
|
-
processGrouping((date) =>
|
|
97
|
+
processGrouping((date) => safeDate(date).getHours());
|
|
94
98
|
} else if (grouping === "daily" || grouping === "weekly") {
|
|
95
|
-
processGrouping((date) =>
|
|
99
|
+
processGrouping((date) => safeDate(date).getDate());
|
|
96
100
|
} else if (grouping === "monthly") {
|
|
97
|
-
processGrouping((date) =>
|
|
101
|
+
processGrouping((date) => safeDate(date).getMonth());
|
|
98
102
|
}
|
|
99
103
|
return [first, ...selectedDates, last];
|
|
100
104
|
};
|
|
101
105
|
const getXAxisformat = (date, grouping, locale) => {
|
|
102
|
-
const dateObj =
|
|
106
|
+
const dateObj = safeDate(date);
|
|
103
107
|
if (isNaN(dateObj.getTime())) {
|
|
104
|
-
return formatShortDate(
|
|
108
|
+
return formatShortDate(safeDate(date), locale);
|
|
105
109
|
}
|
|
106
110
|
switch (grouping) {
|
|
107
111
|
case "hourly":
|
|
@@ -119,7 +123,7 @@ const getXAxisformat = (date, grouping, locale) => {
|
|
|
119
123
|
}
|
|
120
124
|
};
|
|
121
125
|
const getLastUsedDay = (timestamp, t, locale) => {
|
|
122
|
-
const date =
|
|
126
|
+
const date = safeDate(timestamp);
|
|
123
127
|
if (isToday(date)) {
|
|
124
128
|
return t ? t("common.today") : "Today";
|
|
125
129
|
} else if (isYesterday(date)) {
|
|
@@ -235,5 +239,5 @@ const getGroupingLabel = (grouping, t, section) => {
|
|
|
235
239
|
}
|
|
236
240
|
};
|
|
237
241
|
|
|
238
|
-
export { determineGrouping, formatDate, formatDateWithRange, formatHourlyBucket, formatLongDate, formatMonthYear, formatNumber, formatRange, formatShortDate, formatTime, formatTooltipHeaderLabel, formatWeeklyBucket, generateEventsUrl, getAverage, getDateRange, getGroupingLabel, getLastUsedDay, getTotal, getUniqueCatalogEntityKinds, getXAxisTickValues, getXAxisformat };
|
|
242
|
+
export { determineGrouping, formatDate, formatDateWithRange, formatHourlyBucket, formatLongDate, formatMonthYear, formatNumber, formatRange, formatShortDate, formatTime, formatTooltipHeaderLabel, formatWeeklyBucket, generateEventsUrl, getAverage, getDateRange, getGroupingLabel, getLastUsedDay, getTotal, getUniqueCatalogEntityKinds, getXAxisTickValues, getXAxisformat, safeDate };
|
|
239
243
|
//# sourceMappingURL=utils.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.esm.js","sources":["../../src/utils/utils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 {\n addDays,\n addHours,\n getYear,\n isToday,\n isYesterday,\n startOfWeek,\n subDays,\n} from 'date-fns';\n\nimport { utcToZonedTime, formatInTimeZone } from 'date-fns-tz';\nimport { TranslationFunction } from '@backstage/core-plugin-api/alpha';\n\nimport { APIsViewOptions } from '../types';\nimport { adoptionInsightsTranslationRef } from '../translations';\n\n// =============================================================================\n// LOCALIZATION UTILITIES\n// =============================================================================\n\n/**\n * Core date formatting utility with locale support and automatic timezone detection.\n * Handles user's language preferences while automatically detecting their timezone.\n *\n * @param date - The date to format\n * @param options - Intl.DateTimeFormat options for customizing output\n * @param locale - User's language preference (e.g., 'en', 'de', 'fr'), defaults to 'en'\n * @returns Formatted date string respecting user's locale and timezone\n *\n * @example\n * formatDate(new Date(), { year: 'numeric', month: 'short' }, 'de')\n * // German user in Tokyo: \"Jan. 2024\" (in Tokyo timezone)\n */\nexport const formatDate = (\n date: Date,\n options: Intl.DateTimeFormatOptions = {},\n locale?: string,\n) => {\n const currentLocale = locale || 'en';\n const currentTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n return new Intl.DateTimeFormat(currentLocale, {\n timeZone: currentTimeZone,\n ...options,\n }).format(date);\n};\n\n/**\n * Format date in short format: \"15 Jan 24\"\n * @param date - Date to format\n * @param locale - User's language preference\n */\nexport const formatShortDate = (date: Date, locale?: string) =>\n formatDate(date, { year: '2-digit', month: 'short', day: 'numeric' }, locale);\n\n/**\n * Format date in long format: \"January 15, 2024\"\n * @param date - Date to format\n * @param locale - User's language preference\n */\nexport const formatLongDate = (date: Date, locale?: string) =>\n formatDate(\n date,\n {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n },\n locale,\n );\n\n/**\n * Format time respecting user's locale (12h vs 24h format): \"14:30\" or \"2:30 PM\"\n * @param date - Date to format\n * @param locale - User's language preference\n */\nexport const formatTime = (date: Date, locale?: string) =>\n formatDate(date, { hour: '2-digit', minute: '2-digit' }, locale);\n\n/**\n * Format month and year: \"Jan 2024\"\n * @param date - Date to format\n * @param locale - User's language preference\n */\nexport const formatMonthYear = (date: Date, locale?: string) =>\n formatDate(date, { year: 'numeric', month: 'short' }, locale);\n\n/**\n * Format numbers with locale-specific formatting (separators, notation)\n * @param value - Number to format\n * @param options - Intl.NumberFormat options\n * @param locale - User's language preference\n *\n * @example\n * formatNumber(1234.56, {}, 'de') // \"1.234,56\" (German)\n * formatNumber(1234.56, {}, 'en') // \"1,234.56\" (English)\n */\nexport const formatNumber = (\n value: number,\n options: Intl.NumberFormatOptions = {},\n locale?: string,\n) => {\n const currentLocale = locale || 'en';\n return new Intl.NumberFormat(currentLocale, options).format(value);\n};\n\n// =============================================================================\n// DATE RANGE UTILITIES\n// =============================================================================\n\n/**\n * Convert date range to API-compatible ISO strings with timezone awareness.\n * Used for backend API calls that expect specific date format.\n *\n * @param start - Start date\n * @param end - End date\n * @param timeZone - Timezone for conversion, defaults to 'UTC'\n * @returns Object with startDate and endDate as ISO strings\n */\nexport const formatRange = (\n start: Date,\n end: Date,\n timeZone: string = 'UTC',\n): { startDate: string; endDate: string } => ({\n startDate: `${formatInTimeZone(start, timeZone, 'yyyy-MM-dd')}T00:00:00`,\n endDate: `${formatInTimeZone(end, timeZone, 'yyyy-MM-dd')}T23:59:59.999`,\n});\n\n/**\n * Convert date range selection to actual dates.\n * Maps user-friendly date range options to specific start/end dates.\n *\n * @param value - Date range option ('today', 'last-week', 'last-month', etc.)\n * @returns Object with startDate and endDate as ISO strings for API calls\n */\nexport const getDateRange = (value: string) => {\n const startDate: Date | null = null;\n const endDate: Date | null = null;\n\n // Get user's timezone for accurate date calculations\n const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n const today = utcToZonedTime(new Date(), timeZone);\n\n switch (value) {\n case 'today':\n return formatRange(today, today, timeZone);\n\n case 'last-week': {\n const startingDate = subDays(today, 6); // 7 days total (including today)\n return formatRange(startingDate, today, timeZone);\n }\n\n case 'last-month': {\n const startDay = subDays(today, 29); // 30 days total (including today)\n return formatRange(startDay, today, timeZone);\n }\n\n case 'last-28-days': {\n const startDay = subDays(today, 27); // 28 days total (including today)\n return formatRange(startDay, today, timeZone);\n }\n\n case 'last-year': {\n const startOfTheYear = subDays(today, 364); // 365 days total (including today)\n return formatRange(startOfTheYear, today, timeZone);\n }\n\n default:\n return { startDate, endDate };\n }\n};\n\n// =============================================================================\n// CHART FORMATTING UTILITIES\n// =============================================================================\n\n/**\n * Calculate optimal X-axis tick positions to avoid overcrowding.\n * Intelligently selects which dates to show on chart axis based on data length and grouping.\n *\n * @param data - Chart data array with date property\n * @param grouping - Time grouping ('hourly', 'daily', 'weekly', 'monthly')\n * @returns Array of date strings to display as X-axis ticks\n */\nexport const getXAxisTickValues = (data: any, grouping: string): string[] => {\n if (!data || data.length === 0) return [];\n if (data.length <= 2) return data.map((d: { date: string }) => d.date);\n\n const first = data[0].date;\n const last = data[data.length - 1].date;\n const selectedDates: string[] = [];\n\n // Helper function to process grouping and avoid duplicate units (hours/days/months)\n const processGrouping = (unitExtractor: (date: string) => number) => {\n const selectedUnits = new Set<number>([\n unitExtractor(first),\n unitExtractor(last),\n ]);\n\n if (data.length <= 4) {\n // For small datasets, try to show unique units\n data.forEach((d: { date: string }) => {\n const unit = unitExtractor(d.date);\n if (!selectedUnits.has(unit)) {\n selectedUnits.add(unit);\n selectedDates.push(d.date);\n }\n });\n } else if (data.length === 6) {\n // For 6 items, show middle two\n selectedDates.push(data[2].date, data[3].date);\n } else if (data.length === 9) {\n // For 9 items, show evenly spaced items\n selectedDates.push(data[3].date, data[5].date);\n } else {\n // For larger datasets, show 1/3 and 2/3 points\n const intervals = [];\n if (data.length !== 5) {\n intervals.push(Math.floor((data.length - 1) / 3));\n }\n intervals.push(Math.floor(((data.length - 1) * 2) / 3));\n intervals.forEach(i => selectedDates.push(data[i].date));\n }\n };\n\n // Apply grouping-specific logic\n if (grouping === 'hourly') {\n processGrouping(date => new Date(date).getHours());\n } else if (grouping === 'daily' || grouping === 'weekly') {\n processGrouping(date => new Date(date).getDate());\n } else if (grouping === 'monthly') {\n processGrouping(date => new Date(date).getMonth());\n }\n\n // Always include first and last dates, plus selected middle dates\n return [first, ...selectedDates, last];\n};\n\n/**\n * Format dates for X-axis display based on grouping level.\n * Automatically chooses appropriate detail level for each time grouping.\n *\n * @param date - Date string to format\n * @param grouping - Time grouping level\n * @param locale - User's language preference\n * @returns Formatted date string for chart axis\n */\nexport const getXAxisformat = (\n date: string,\n grouping: string,\n locale?: string,\n) => {\n const dateObj = new Date(date);\n\n // Handle invalid dates gracefully\n if (isNaN(dateObj.getTime())) {\n return formatShortDate(new Date(date), locale);\n }\n\n // Format according to grouping level\n switch (grouping) {\n case 'hourly':\n return formatTime(dateObj, locale); // \"14:30\" or \"2:30 PM\"\n case 'daily':\n case 'weekly':\n return formatShortDate(dateObj, locale); // \"15 Jan 24\"\n case 'monthly':\n return formatMonthYear(dateObj, locale); // \"Jan 2024\"\n default:\n return date;\n }\n};\n\n/**\n * Format \"last used\" date with smart relative formatting.\n * Shows \"Today\"/\"Yesterday\" for recent dates, otherwise formatted date.\n *\n * @param timestamp - Date timestamp to format\n * @param t - Translation function for \"Today\"/\"Yesterday\"\n * @param locale - User's language preference\n * @returns Formatted date string with relative terms when appropriate\n */\nexport const getLastUsedDay = (\n timestamp: string,\n t?: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n locale?: string,\n) => {\n const date = new Date(timestamp);\n\n if (isToday(date)) {\n return t ? t('common.today') : 'Today';\n } else if (isYesterday(date)) {\n return t ? t('common.yesterday') : 'Yesterday';\n }\n return formatShortDate(date, locale);\n};\n\n// =============================================================================\n// DATA CALCULATION UTILITIES\n// =============================================================================\n\n/**\n * Calculate average value for a specific key across data array.\n * Safely handles missing or invalid data.\n *\n * @param data - Array of data objects\n * @param key - Property key to calculate average for\n * @returns Average value, or 0 if no valid data\n */\nexport const getAverage = <T extends Record<string, any>>(\n data: T[],\n key: keyof T,\n) => {\n if (!data || data.length === 0) return 0;\n\n const totalSum = data.reduce(\n (sum, entry) => sum + Number(entry[key] || 0),\n 0,\n );\n return totalSum / data.length;\n};\n\n/**\n * Calculate total sum for a specific key across data array.\n * Safely handles missing or invalid data.\n *\n * @param data - Array of data objects\n * @param key - Property key to sum up\n * @returns Total sum, or 0 if no valid data\n */\nexport const getTotal = <T extends Record<string, any>>(\n data: T[],\n key: keyof T,\n) => {\n const totalSum = data?.reduce(\n (sum, entry) => sum + Number(entry[key] || 0),\n 0,\n );\n return totalSum;\n};\n\n/**\n * Extract unique catalog entity kinds with proper capitalization.\n * Applies locale-aware capitalization for entity type names.\n *\n * @param data - Array of objects with 'kind' property\n * @param locale - User's language preference for capitalization rules\n * @returns Array of unique, properly capitalized entity kinds\n */\nexport const getUniqueCatalogEntityKinds = (\n data: { kind: string }[],\n locale?: string,\n) => {\n const allKinds = data.map(\n item => item.kind.charAt(0).toLocaleUpperCase(locale) + item.kind.slice(1),\n );\n const uniqueKinds = Array.from(new Set([...allKinds]));\n return uniqueKinds;\n};\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Generate URL with query parameters for API calls.\n * Safely converts options object to URL search parameters.\n *\n * @param baseUrl - Base URL for the API endpoint\n * @param options - Options object to convert to query parameters\n * @returns Complete URL with query string\n */\nexport const generateEventsUrl = (\n baseUrl: string,\n options: APIsViewOptions,\n): string => {\n const params = new URLSearchParams();\n\n // Add each non-empty option as URL parameter\n Object.entries(options).forEach(([key, value]) => {\n if (value && value !== undefined) {\n params.append(key, String(value));\n }\n });\n\n return `${baseUrl}?${params.toString()}`;\n};\n\n/**\n * Determine appropriate data grouping based on date range.\n * Automatically selects optimal time granularity for charts.\n *\n * @param startDate - Range start date\n * @param endDate - Range end date\n * @param t - Translation function for error messages\n * @returns Grouping level: 'hourly', 'daily', 'weekly', or 'monthly'\n */\nexport const determineGrouping = (\n startDate: Date | null,\n endDate: Date | null,\n t?: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n): string => {\n // Validate dates\n if (\n startDate &&\n endDate &&\n (isNaN(startDate.getTime()) || isNaN(endDate.getTime()))\n ) {\n const errorMessage = t\n ? t('common.invalidDateFormat')\n : 'Invalid date format';\n throw new Error(errorMessage);\n }\n\n if (startDate && endDate) {\n const diffInMs = endDate.getTime() - startDate.getTime();\n const daysDiff = Math.ceil(diffInMs / (1000 * 60 * 60 * 24));\n\n // Choose grouping based on date range\n if (daysDiff <= 1) return 'hourly'; // 1 day: show hours\n if (daysDiff <= 7) return 'daily'; // 1 week: show days\n if (daysDiff <= 30) return 'weekly'; // 1 month: show weeks\n }\n\n return 'monthly'; // Default: show months\n};\n\n// =============================================================================\n// CHART TOOLTIP FORMATTING\n// =============================================================================\n\n/**\n * Legacy function for formatting dates with timezone (used by existing code).\n * Consider using formatDate() for new implementations.\n *\n * @param date - Date to format\n * @param formatStr - date-fns format string\n * @returns Formatted date string\n */\nexport const formatWithTimeZone = (\n date: Date,\n formatStr: string = 'yyyy-MM-dd',\n) => {\n const timezone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n return formatInTimeZone(date, timezone, formatStr);\n};\n\n/**\n * Format hourly time bucket for tooltips.\n * Shows full date with start and end times: \"January 15, 2024, 14:30–15:30\"\n *\n * @param date - Start date of the hour bucket\n * @param locale - User's language preference\n * @returns Formatted hourly bucket string\n */\nexport const formatHourlyBucket = (date: Date, locale?: string): string => {\n const start = formatTime(date, locale);\n const end = formatTime(addHours(date, 1), locale);\n const labelDate = formatLongDate(date, locale);\n\n return `${labelDate}, ${start}–${end}`;\n};\n\n/**\n * Format date with filtering range context for monthly tooltips.\n * Shows date with filter range: \"January 15, 2024\\n (filtered by Jan 1, 2024 – Dec 31, 2024)\"\n *\n * @param date - Date to display\n * @param startDateRange - Filter range start\n * @param endDateRange - Filter range end\n * @param t - Translation function\n * @param locale - User's language preference\n * @returns Formatted date with range context\n */\nexport const formatDateWithRange = (\n date: Date,\n startDateRange?: Date | null,\n endDateRange?: Date | null,\n t?: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n locale?: string,\n): string => {\n const currentTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n const today = utcToZonedTime(new Date(), currentTimeZone);\n const end = endDateRange ?? today;\n const start = startDateRange ?? subDays(end, 364); // Default to 1 year range\n\n const startLabel = formatDate(\n start,\n { year: 'numeric', month: 'short', day: 'numeric' },\n locale,\n );\n const endLabel = formatDate(\n end,\n { year: 'numeric', month: 'short', day: 'numeric' },\n locale,\n );\n const labelDate = formatLongDate(date, locale);\n const filteredByText = t ? t('common.filteredBy') : 'filtered by';\n return `${labelDate}\\n (${filteredByText} ${startLabel} – ${endLabel})`;\n};\n\n/**\n * Format weekly date range for tooltips.\n * Shows week span: \"Jan 15 – Jan 21, 2024\" or \"Dec 30, 2023 – Jan 5, 2024\"\n *\n * @param date - Any date within the week\n * @param locale - User's language preference\n * @returns Formatted weekly range string\n */\nexport const formatWeeklyBucket = (date: Date, locale?: string): string => {\n const start = startOfWeek(date, { weekStartsOn: 1 }); // Monday start\n const end = addDays(start, 6); // Sunday end\n\n const sameYear = getYear(start) === getYear(end);\n\n // If same year, omit year from start date for cleaner display\n const startLabel = formatDate(\n start,\n sameYear\n ? { month: 'short', day: 'numeric' }\n : { year: 'numeric', month: 'short', day: 'numeric' },\n locale,\n );\n const endLabel = formatDate(\n end,\n { year: 'numeric', month: 'short', day: 'numeric' },\n locale,\n );\n\n return `${startLabel} – ${endLabel}`;\n};\n\n/**\n * Convert snake_case API keys to human-readable labels.\n * Transforms database field names to display-friendly text.\n *\n * @param key - Snake_case key from API (e.g., \"new_users\", \"search_count\")\n * @returns Human-readable label (e.g., \"New users\", \"Search count\")\n */\nexport const formatTooltipHeaderLabel = (key: string) => {\n const words = key.replace(/_/g, ' ').toLowerCase().split(' ');\n return words\n .map((word, index) =>\n index === 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word,\n )\n .join(' ');\n};\n\n/**\n * Get the appropriate label for a grouping type.\n * Maps grouping values to their corresponding translation keys.\n *\n * @param grouping - The grouping type ('hourly', 'daily', 'weekly', 'monthly')\n * @param t - Translation function\n * @param section - The translation section ('activeUsers' or 'searches')\n * @returns Translated label for the grouping type\n */\nexport const getGroupingLabel = (\n grouping: string,\n t: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n section: 'activeUsers' | 'searches',\n): string => {\n switch (grouping) {\n case 'hourly':\n return t(`${section}.hour`);\n case 'daily':\n return t(`${section}.day`);\n case 'weekly':\n return t(`${section}.week`);\n case 'monthly':\n return t(`${section}.month`);\n default:\n return t(`${section}.day`); // fallback to day\n }\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.esm.js","sources":["../../src/utils/utils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 {\n addDays,\n addHours,\n getYear,\n isToday,\n isYesterday,\n startOfWeek,\n subDays,\n} from 'date-fns';\n\nimport { utcToZonedTime, formatInTimeZone } from 'date-fns-tz';\nimport { TranslationFunction } from '@backstage/core-plugin-api/alpha';\n\nimport { APIsViewOptions } from '../types';\nimport { adoptionInsightsTranslationRef } from '../translations';\n\n/**\n * Parse date string and normalize timezone format.\n * Converts +00 timezone format to Z for better browser compatibility.\n */\nexport const safeDate = (dateString: string): Date => {\n const normalizedDate = dateString.replace(/\\+00$/, 'Z');\n return new Date(normalizedDate);\n};\n\n// =============================================================================\n// LOCALIZATION UTILITIES\n// =============================================================================\n\n/**\n * Core date formatting utility with locale support and automatic timezone detection.\n * Handles user's language preferences while automatically detecting their timezone.\n *\n * @param date - The date to format\n * @param options - Intl.DateTimeFormat options for customizing output\n * @param locale - User's language preference (e.g., 'en', 'de', 'fr'), defaults to 'en'\n * @returns Formatted date string respecting user's locale and timezone\n *\n * @example\n * formatDate(new Date(), { year: 'numeric', month: 'short' }, 'de')\n * // German user in Tokyo: \"Jan. 2024\" (in Tokyo timezone)\n */\nexport const formatDate = (\n date: Date,\n options: Intl.DateTimeFormatOptions = {},\n locale?: string,\n) => {\n const currentLocale = locale || 'en';\n const currentTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n return new Intl.DateTimeFormat(currentLocale, {\n timeZone: currentTimeZone,\n ...options,\n }).format(date);\n};\n\n/**\n * Format date in short format: \"15 Jan 24\"\n * @param date - Date to format\n * @param locale - User's language preference\n */\nexport const formatShortDate = (date: Date, locale?: string) =>\n formatDate(date, { year: '2-digit', month: 'short', day: 'numeric' }, locale);\n\n/**\n * Format date in long format: \"January 15, 2024\"\n * @param date - Date to format\n * @param locale - User's language preference\n */\nexport const formatLongDate = (date: Date, locale?: string) =>\n formatDate(\n date,\n {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n },\n locale,\n );\n\n/**\n * Format time respecting user's locale (12h vs 24h format): \"14:30\" or \"2:30 PM\"\n * @param date - Date to format\n * @param locale - User's language preference\n */\nexport const formatTime = (date: Date, locale?: string) =>\n formatDate(date, { hour: '2-digit', minute: '2-digit' }, locale);\n\n/**\n * Format month and year: \"Jan 2024\"\n * @param date - Date to format\n * @param locale - User's language preference\n */\nexport const formatMonthYear = (date: Date, locale?: string) =>\n formatDate(date, { year: 'numeric', month: 'short' }, locale);\n\n/**\n * Format numbers with locale-specific formatting (separators, notation)\n * @param value - Number to format\n * @param options - Intl.NumberFormat options\n * @param locale - User's language preference\n *\n * @example\n * formatNumber(1234.56, {}, 'de') // \"1.234,56\" (German)\n * formatNumber(1234.56, {}, 'en') // \"1,234.56\" (English)\n */\nexport const formatNumber = (\n value: number,\n options: Intl.NumberFormatOptions = {},\n locale?: string,\n) => {\n const currentLocale = locale || 'en';\n return new Intl.NumberFormat(currentLocale, options).format(value);\n};\n\n// =============================================================================\n// DATE RANGE UTILITIES\n// =============================================================================\n\n/**\n * Convert date range to API-compatible ISO strings with timezone awareness.\n * Used for backend API calls that expect specific date format.\n *\n * @param start - Start date\n * @param end - End date\n * @param timeZone - Timezone for conversion, defaults to 'UTC'\n * @returns Object with startDate and endDate as ISO strings\n */\nexport const formatRange = (\n start: Date,\n end: Date,\n timeZone: string = 'UTC',\n): { startDate: string; endDate: string } => ({\n startDate: `${formatInTimeZone(start, timeZone, 'yyyy-MM-dd')}T00:00:00`,\n endDate: `${formatInTimeZone(end, timeZone, 'yyyy-MM-dd')}T23:59:59.999`,\n});\n\n/**\n * Convert date range selection to actual dates.\n * Maps user-friendly date range options to specific start/end dates.\n *\n * @param value - Date range option ('today', 'last-week', 'last-month', etc.)\n * @returns Object with startDate and endDate as ISO strings for API calls\n */\nexport const getDateRange = (value: string) => {\n const startDate: Date | null = null;\n const endDate: Date | null = null;\n\n // Get user's timezone for accurate date calculations\n const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n const today = utcToZonedTime(new Date(), timeZone);\n\n switch (value) {\n case 'today':\n return formatRange(today, today, timeZone);\n\n case 'last-week': {\n const startingDate = subDays(today, 6); // 7 days total (including today)\n return formatRange(startingDate, today, timeZone);\n }\n\n case 'last-month': {\n const startDay = subDays(today, 29); // 30 days total (including today)\n return formatRange(startDay, today, timeZone);\n }\n\n case 'last-28-days': {\n const startDay = subDays(today, 27); // 28 days total (including today)\n return formatRange(startDay, today, timeZone);\n }\n\n case 'last-year': {\n const startOfTheYear = subDays(today, 364); // 365 days total (including today)\n return formatRange(startOfTheYear, today, timeZone);\n }\n\n default:\n return { startDate, endDate };\n }\n};\n\n// =============================================================================\n// CHART FORMATTING UTILITIES\n// =============================================================================\n\n/**\n * Calculate optimal X-axis tick positions to avoid overcrowding.\n * Intelligently selects which dates to show on chart axis based on data length and grouping.\n *\n * @param data - Chart data array with date property\n * @param grouping - Time grouping ('hourly', 'daily', 'weekly', 'monthly')\n * @returns Array of date strings to display as X-axis ticks\n */\nexport const getXAxisTickValues = (data: any, grouping: string): string[] => {\n if (!data || data.length === 0) return [];\n if (data.length <= 2) return data.map((d: { date: string }) => d.date);\n\n const first = data[0].date;\n const last = data[data.length - 1].date;\n const selectedDates: string[] = [];\n\n // Helper function to process grouping and avoid duplicate units (hours/days/months)\n const processGrouping = (unitExtractor: (date: string) => number) => {\n const selectedUnits = new Set<number>([\n unitExtractor(first),\n unitExtractor(last),\n ]);\n\n if (data.length <= 4) {\n // For small datasets, try to show unique units\n data.forEach((d: { date: string }) => {\n const unit = unitExtractor(d.date);\n if (!selectedUnits.has(unit)) {\n selectedUnits.add(unit);\n selectedDates.push(d.date);\n }\n });\n } else if (data.length === 6) {\n // For 6 items, show middle two\n selectedDates.push(data[2].date, data[3].date);\n } else if (data.length === 9) {\n // For 9 items, show evenly spaced items\n selectedDates.push(data[3].date, data[5].date);\n } else {\n // For larger datasets, show 1/3 and 2/3 points\n const intervals = [];\n if (data.length !== 5) {\n intervals.push(Math.floor((data.length - 1) / 3));\n }\n intervals.push(Math.floor(((data.length - 1) * 2) / 3));\n intervals.forEach(i => selectedDates.push(data[i].date));\n }\n };\n\n // Apply grouping-specific logic\n if (grouping === 'hourly') {\n processGrouping(date => safeDate(date).getHours());\n } else if (grouping === 'daily' || grouping === 'weekly') {\n processGrouping(date => safeDate(date).getDate());\n } else if (grouping === 'monthly') {\n processGrouping(date => safeDate(date).getMonth());\n }\n\n // Always include first and last dates, plus selected middle dates\n return [first, ...selectedDates, last];\n};\n\n/**\n * Format dates for X-axis display based on grouping level.\n * Automatically chooses appropriate detail level for each time grouping.\n *\n * @param date - Date string to format\n * @param grouping - Time grouping level\n * @param locale - User's language preference\n * @returns Formatted date string for chart axis\n */\nexport const getXAxisformat = (\n date: string,\n grouping: string,\n locale?: string,\n) => {\n const dateObj = safeDate(date);\n\n // Handle invalid dates gracefully\n if (isNaN(dateObj.getTime())) {\n return formatShortDate(safeDate(date), locale);\n }\n\n // Format according to grouping level\n switch (grouping) {\n case 'hourly':\n return formatTime(dateObj, locale); // \"14:30\" or \"2:30 PM\"\n case 'daily':\n case 'weekly':\n return formatShortDate(dateObj, locale); // \"15 Jan 24\"\n case 'monthly':\n return formatMonthYear(dateObj, locale); // \"Jan 2024\"\n default:\n return date;\n }\n};\n\n/**\n * Format \"last used\" date with smart relative formatting.\n * Shows \"Today\"/\"Yesterday\" for recent dates, otherwise formatted date.\n *\n * @param timestamp - Date timestamp to format\n * @param t - Translation function for \"Today\"/\"Yesterday\"\n * @param locale - User's language preference\n * @returns Formatted date string with relative terms when appropriate\n */\nexport const getLastUsedDay = (\n timestamp: string,\n t?: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n locale?: string,\n) => {\n const date = safeDate(timestamp);\n\n if (isToday(date)) {\n return t ? t('common.today') : 'Today';\n } else if (isYesterday(date)) {\n return t ? t('common.yesterday') : 'Yesterday';\n }\n return formatShortDate(date, locale);\n};\n\n// =============================================================================\n// DATA CALCULATION UTILITIES\n// =============================================================================\n\n/**\n * Calculate average value for a specific key across data array.\n * Safely handles missing or invalid data.\n *\n * @param data - Array of data objects\n * @param key - Property key to calculate average for\n * @returns Average value, or 0 if no valid data\n */\nexport const getAverage = <T extends Record<string, any>>(\n data: T[],\n key: keyof T,\n) => {\n if (!data || data.length === 0) return 0;\n\n const totalSum = data.reduce(\n (sum, entry) => sum + Number(entry[key] || 0),\n 0,\n );\n return totalSum / data.length;\n};\n\n/**\n * Calculate total sum for a specific key across data array.\n * Safely handles missing or invalid data.\n *\n * @param data - Array of data objects\n * @param key - Property key to sum up\n * @returns Total sum, or 0 if no valid data\n */\nexport const getTotal = <T extends Record<string, any>>(\n data: T[],\n key: keyof T,\n) => {\n const totalSum = data?.reduce(\n (sum, entry) => sum + Number(entry[key] || 0),\n 0,\n );\n return totalSum;\n};\n\n/**\n * Extract unique catalog entity kinds with proper capitalization.\n * Applies locale-aware capitalization for entity type names.\n *\n * @param data - Array of objects with 'kind' property\n * @param locale - User's language preference for capitalization rules\n * @returns Array of unique, properly capitalized entity kinds\n */\nexport const getUniqueCatalogEntityKinds = (\n data: { kind: string }[],\n locale?: string,\n) => {\n const allKinds = data.map(\n item => item.kind.charAt(0).toLocaleUpperCase(locale) + item.kind.slice(1),\n );\n const uniqueKinds = Array.from(new Set([...allKinds]));\n return uniqueKinds;\n};\n\n// =============================================================================\n// UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Generate URL with query parameters for API calls.\n * Safely converts options object to URL search parameters.\n *\n * @param baseUrl - Base URL for the API endpoint\n * @param options - Options object to convert to query parameters\n * @returns Complete URL with query string\n */\nexport const generateEventsUrl = (\n baseUrl: string,\n options: APIsViewOptions,\n): string => {\n const params = new URLSearchParams();\n\n // Add each non-empty option as URL parameter\n Object.entries(options).forEach(([key, value]) => {\n if (value && value !== undefined) {\n params.append(key, String(value));\n }\n });\n\n return `${baseUrl}?${params.toString()}`;\n};\n\n/**\n * Determine appropriate data grouping based on date range.\n * Automatically selects optimal time granularity for charts.\n *\n * @param startDate - Range start date\n * @param endDate - Range end date\n * @param t - Translation function for error messages\n * @returns Grouping level: 'hourly', 'daily', 'weekly', or 'monthly'\n */\nexport const determineGrouping = (\n startDate: Date | null,\n endDate: Date | null,\n t?: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n): string => {\n // Validate dates\n if (\n startDate &&\n endDate &&\n (isNaN(startDate.getTime()) || isNaN(endDate.getTime()))\n ) {\n const errorMessage = t\n ? t('common.invalidDateFormat')\n : 'Invalid date format';\n throw new Error(errorMessage);\n }\n\n if (startDate && endDate) {\n const diffInMs = endDate.getTime() - startDate.getTime();\n const daysDiff = Math.ceil(diffInMs / (1000 * 60 * 60 * 24));\n\n // Choose grouping based on date range\n if (daysDiff <= 1) return 'hourly'; // 1 day: show hours\n if (daysDiff <= 7) return 'daily'; // 1 week: show days\n if (daysDiff <= 30) return 'weekly'; // 1 month: show weeks\n }\n\n return 'monthly'; // Default: show months\n};\n\n// =============================================================================\n// CHART TOOLTIP FORMATTING\n// =============================================================================\n\n/**\n * Legacy function for formatting dates with timezone (used by existing code).\n * Consider using formatDate() for new implementations.\n *\n * @param date - Date to format\n * @param formatStr - date-fns format string\n * @returns Formatted date string\n */\nexport const formatWithTimeZone = (\n date: Date,\n formatStr: string = 'yyyy-MM-dd',\n) => {\n const timezone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n return formatInTimeZone(date, timezone, formatStr);\n};\n\n/**\n * Format hourly time bucket for tooltips.\n * Shows full date with start and end times: \"January 15, 2024, 14:30–15:30\"\n *\n * @param date - Start date of the hour bucket\n * @param locale - User's language preference\n * @returns Formatted hourly bucket string\n */\nexport const formatHourlyBucket = (date: Date, locale?: string): string => {\n const start = formatTime(date, locale);\n const end = formatTime(addHours(date, 1), locale);\n const labelDate = formatLongDate(date, locale);\n\n return `${labelDate}, ${start}–${end}`;\n};\n\n/**\n * Format date with filtering range context for monthly tooltips.\n * Shows date with filter range: \"January 15, 2024\\n (filtered by Jan 1, 2024 – Dec 31, 2024)\"\n *\n * @param date - Date to display\n * @param startDateRange - Filter range start\n * @param endDateRange - Filter range end\n * @param t - Translation function\n * @param locale - User's language preference\n * @returns Formatted date with range context\n */\nexport const formatDateWithRange = (\n date: Date,\n startDateRange?: Date | null,\n endDateRange?: Date | null,\n t?: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n locale?: string,\n): string => {\n const currentTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n const today = utcToZonedTime(new Date(), currentTimeZone);\n const end = endDateRange ?? today;\n const start = startDateRange ?? subDays(end, 364); // Default to 1 year range\n\n const startLabel = formatDate(\n start,\n { year: 'numeric', month: 'short', day: 'numeric' },\n locale,\n );\n const endLabel = formatDate(\n end,\n { year: 'numeric', month: 'short', day: 'numeric' },\n locale,\n );\n const labelDate = formatLongDate(date, locale);\n const filteredByText = t ? t('common.filteredBy') : 'filtered by';\n return `${labelDate}\\n (${filteredByText} ${startLabel} – ${endLabel})`;\n};\n\n/**\n * Format weekly date range for tooltips.\n * Shows week span: \"Jan 15 – Jan 21, 2024\" or \"Dec 30, 2023 – Jan 5, 2024\"\n *\n * @param date - Any date within the week\n * @param locale - User's language preference\n * @returns Formatted weekly range string\n */\nexport const formatWeeklyBucket = (date: Date, locale?: string): string => {\n const start = startOfWeek(date, { weekStartsOn: 1 }); // Monday start\n const end = addDays(start, 6); // Sunday end\n\n const sameYear = getYear(start) === getYear(end);\n\n // If same year, omit year from start date for cleaner display\n const startLabel = formatDate(\n start,\n sameYear\n ? { month: 'short', day: 'numeric' }\n : { year: 'numeric', month: 'short', day: 'numeric' },\n locale,\n );\n const endLabel = formatDate(\n end,\n { year: 'numeric', month: 'short', day: 'numeric' },\n locale,\n );\n\n return `${startLabel} – ${endLabel}`;\n};\n\n/**\n * Convert snake_case API keys to human-readable labels.\n * Transforms database field names to display-friendly text.\n *\n * @param key - Snake_case key from API (e.g., \"new_users\", \"search_count\")\n * @returns Human-readable label (e.g., \"New users\", \"Search count\")\n */\nexport const formatTooltipHeaderLabel = (key: string) => {\n const words = key.replace(/_/g, ' ').toLowerCase().split(' ');\n return words\n .map((word, index) =>\n index === 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word,\n )\n .join(' ');\n};\n\n/**\n * Get the appropriate label for a grouping type.\n * Maps grouping values to their corresponding translation keys.\n *\n * @param grouping - The grouping type ('hourly', 'daily', 'weekly', 'monthly')\n * @param t - Translation function\n * @param section - The translation section ('activeUsers' or 'searches')\n * @returns Translated label for the grouping type\n */\nexport const getGroupingLabel = (\n grouping: string,\n t: TranslationFunction<typeof adoptionInsightsTranslationRef.T>,\n section: 'activeUsers' | 'searches',\n): string => {\n switch (grouping) {\n case 'hourly':\n return t(`${section}.hour`);\n case 'daily':\n return t(`${section}.day`);\n case 'weekly':\n return t(`${section}.week`);\n case 'monthly':\n return t(`${section}.month`);\n default:\n return t(`${section}.day`); // fallback to day\n }\n};\n"],"names":[],"mappings":"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"}
|