@flagsmith/backstage-plugin 0.1.0-pr.7.ffc27ed → 0.2.1
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/dist/api/FlagsmithClient.esm.js +28 -2
- package/dist/api/FlagsmithClient.esm.js.map +1 -1
- package/dist/components/FlagsTab/EnvironmentTable.esm.js +83 -45
- package/dist/components/FlagsTab/EnvironmentTable.esm.js.map +1 -1
- package/dist/components/FlagsTab/ExpandableRow.esm.js +188 -101
- package/dist/components/FlagsTab/ExpandableRow.esm.js.map +1 -1
- package/dist/components/FlagsTab/FeatureAnalyticsSection.esm.js +178 -0
- package/dist/components/FlagsTab/FeatureAnalyticsSection.esm.js.map +1 -0
- package/dist/components/FlagsTab/FeatureDetailsGrid.esm.js +111 -35
- package/dist/components/FlagsTab/FeatureDetailsGrid.esm.js.map +1 -1
- package/dist/components/FlagsTab/SegmentOverridesSection.esm.js +1 -0
- package/dist/components/FlagsTab/SegmentOverridesSection.esm.js.map +1 -1
- package/dist/components/FlagsTab/index.esm.js +48 -16
- package/dist/components/FlagsTab/index.esm.js.map +1 -1
- package/dist/components/FlagsmithOverviewCard/FeatureFlagRow.esm.js +1 -0
- package/dist/components/FlagsmithOverviewCard/FeatureFlagRow.esm.js.map +1 -1
- package/dist/components/FlagsmithOverviewCard/FlagStatsRow.esm.js +1 -0
- package/dist/components/FlagsmithOverviewCard/FlagStatsRow.esm.js.map +1 -1
- package/dist/components/FlagsmithOverviewCard/index.esm.js +4 -5
- package/dist/components/FlagsmithOverviewCard/index.esm.js.map +1 -1
- package/dist/components/FlagsmithUsageCard/UsageChart.esm.js +40 -3
- package/dist/components/FlagsmithUsageCard/UsageChart.esm.js.map +1 -1
- package/dist/components/FlagsmithUsageCard/index.esm.js +11 -13
- package/dist/components/FlagsmithUsageCard/index.esm.js.map +1 -1
- package/dist/components/shared/ChartTooltip.esm.js +33 -0
- package/dist/components/shared/ChartTooltip.esm.js.map +1 -0
- package/dist/components/shared/ErrorState.esm.js +13 -0
- package/dist/components/shared/ErrorState.esm.js.map +1 -0
- package/dist/components/shared/FlagsmithLink.esm.js +12 -4
- package/dist/components/shared/FlagsmithLink.esm.js.map +1 -1
- package/dist/constants/index.esm.js +38 -0
- package/dist/constants/index.esm.js.map +1 -0
- package/dist/hooks/useFlagsmithProject.esm.js +7 -1
- package/dist/hooks/useFlagsmithProject.esm.js.map +1 -1
- package/dist/hooks/useFlagsmithUsage.esm.js +6 -3
- package/dist/hooks/useFlagsmithUsage.esm.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/theme/sharedStyles.esm.js +19 -0
- package/dist/theme/sharedStyles.esm.js.map +1 -0
- package/dist/utils/dateFormatters.esm.js +15 -0
- package/dist/utils/dateFormatters.esm.js.map +1 -0
- package/dist/utils/flagTypeHelpers.esm.js +42 -0
- package/dist/utils/flagTypeHelpers.esm.js.map +1 -0
- package/package.json +1 -1
- package/dist/components/FlagsmithUsageCard/UsageTooltip.esm.js +0 -52
- package/dist/components/FlagsmithUsageCard/UsageTooltip.esm.js.map +0 -1
|
@@ -1,9 +1,46 @@
|
|
|
1
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { Box, Typography } from '@material-ui/core';
|
|
3
3
|
import { ResponsiveContainer, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Bar } from 'recharts';
|
|
4
4
|
import { flagsmithColors } from '../../theme/flagsmithTheme.esm.js';
|
|
5
|
-
import
|
|
5
|
+
import '../shared/FlagStatusIndicator.esm.js';
|
|
6
|
+
import '../shared/SearchInput.esm.js';
|
|
7
|
+
import '../shared/FlagsmithLink.esm.js';
|
|
8
|
+
import '@material-ui/icons/ChevronLeft';
|
|
9
|
+
import '@material-ui/icons/ChevronRight';
|
|
10
|
+
import { ChartTooltip, ChartTooltipText } from '../shared/ChartTooltip.esm.js';
|
|
6
11
|
|
|
12
|
+
const UsageChartTooltip = ({ active, payload }) => /* @__PURE__ */ jsx(ChartTooltip, { active, payload, children: (data) => {
|
|
13
|
+
const usageData = data[0].payload;
|
|
14
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15
|
+
/* @__PURE__ */ jsx(ChartTooltipText, { variant: "subtitle2", fontWeight: 600, children: new Date(usageData.day).toLocaleDateString("en-US", {
|
|
16
|
+
month: "short",
|
|
17
|
+
day: "numeric",
|
|
18
|
+
year: "numeric"
|
|
19
|
+
}) }),
|
|
20
|
+
/* @__PURE__ */ jsxs(Box, { mt: 1, children: [
|
|
21
|
+
/* @__PURE__ */ jsxs(ChartTooltipText, { children: [
|
|
22
|
+
/* @__PURE__ */ jsx("strong", { children: "Flags:" }),
|
|
23
|
+
" ",
|
|
24
|
+
usageData.flags ?? 0
|
|
25
|
+
] }),
|
|
26
|
+
/* @__PURE__ */ jsxs(ChartTooltipText, { children: [
|
|
27
|
+
/* @__PURE__ */ jsx("strong", { children: "Identities:" }),
|
|
28
|
+
" ",
|
|
29
|
+
usageData.identities
|
|
30
|
+
] }),
|
|
31
|
+
/* @__PURE__ */ jsxs(ChartTooltipText, { children: [
|
|
32
|
+
/* @__PURE__ */ jsx("strong", { children: "Traits:" }),
|
|
33
|
+
" ",
|
|
34
|
+
usageData.traits
|
|
35
|
+
] }),
|
|
36
|
+
/* @__PURE__ */ jsxs(ChartTooltipText, { children: [
|
|
37
|
+
/* @__PURE__ */ jsx("strong", { children: "Environment Document:" }),
|
|
38
|
+
" ",
|
|
39
|
+
usageData.environment_document
|
|
40
|
+
] })
|
|
41
|
+
] })
|
|
42
|
+
] });
|
|
43
|
+
} });
|
|
7
44
|
const UsageChart = ({ data }) => {
|
|
8
45
|
if (data.length === 0) {
|
|
9
46
|
return /* @__PURE__ */ jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", height: 300, children: /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No usage data available" }) });
|
|
@@ -29,7 +66,7 @@ const UsageChart = ({ data }) => {
|
|
|
29
66
|
}
|
|
30
67
|
),
|
|
31
68
|
/* @__PURE__ */ jsx(YAxis, {}),
|
|
32
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(
|
|
69
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(UsageChartTooltip, {}) }),
|
|
33
70
|
/* @__PURE__ */ jsx(
|
|
34
71
|
Bar,
|
|
35
72
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UsageChart.esm.js","sources":["../../../src/components/FlagsmithUsageCard/UsageChart.tsx"],"sourcesContent":["import { Box, Typography } from '@material-ui/core';\nimport {\n BarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n} from 'recharts';\nimport { FlagsmithUsageData } from '../../api/FlagsmithClient';\nimport { flagsmithColors } from '../../theme/flagsmithTheme';\nimport {
|
|
1
|
+
{"version":3,"file":"UsageChart.esm.js","sources":["../../../src/components/FlagsmithUsageCard/UsageChart.tsx"],"sourcesContent":["import { Box, Typography } from '@material-ui/core';\nimport {\n BarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n} from 'recharts';\nimport { FlagsmithUsageData } from '../../api/FlagsmithClient';\nimport { flagsmithColors } from '../../theme/flagsmithTheme';\nimport { ChartTooltip, ChartTooltipText } from '../shared';\n\ninterface UsageChartProps {\n data: FlagsmithUsageData[];\n}\n\n/**\n * Custom tooltip for usage chart displaying flags, identities, traits, and environment document\n */\nconst UsageChartTooltip = ({ active, payload }: any) => (\n <ChartTooltip active={active} payload={payload}>\n {(data) => {\n const usageData = data[0].payload as FlagsmithUsageData;\n return (\n <>\n <ChartTooltipText variant=\"subtitle2\" fontWeight={600}>\n {new Date(usageData.day).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n })}\n </ChartTooltipText>\n <Box mt={1}>\n <ChartTooltipText>\n <strong>Flags:</strong> {usageData.flags ?? 0}\n </ChartTooltipText>\n <ChartTooltipText>\n <strong>Identities:</strong> {usageData.identities}\n </ChartTooltipText>\n <ChartTooltipText>\n <strong>Traits:</strong> {usageData.traits}\n </ChartTooltipText>\n <ChartTooltipText>\n <strong>Environment Document:</strong> {usageData.environment_document}\n </ChartTooltipText>\n </Box>\n </>\n );\n }}\n </ChartTooltip>\n);\n\nexport const UsageChart = ({ data }: UsageChartProps) => {\n if (data.length === 0) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" height={300}>\n <Typography color=\"textSecondary\">\n No usage data available\n </Typography>\n </Box>\n );\n }\n\n return (\n <ResponsiveContainer width=\"100%\" height={300}>\n <BarChart\n data={data}\n margin={{ top: 5, right: 30, left: 20, bottom: 5 }}\n >\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis\n dataKey=\"day\"\n tickFormatter={value => {\n const date = new Date(value);\n return `${date.getMonth() + 1}/${date.getDate()}`;\n }}\n angle={-45}\n textAnchor=\"end\"\n height={80}\n />\n <YAxis />\n <Tooltip content={<UsageChartTooltip />} />\n <Bar\n dataKey=\"flags\"\n fill={flagsmithColors.primary}\n radius={[2, 2, 0, 0]}\n />\n </BarChart>\n </ResponsiveContainer>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;AAqBA,MAAM,iBAAA,GAAoB,CAAC,EAAE,MAAA,EAAQ,OAAA,EAAQ,qBAC3C,GAAA,CAAC,YAAA,EAAA,EAAa,MAAA,EAAgB,OAAA,EAC3B,QAAA,EAAA,CAAC,IAAA,KAAS;AACT,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,CAAC,CAAA,CAAE,OAAA;AAC1B,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,OAAA,EAAQ,WAAA,EAAY,UAAA,EAAY,GAAA,EAC/C,QAAA,EAAA,IAAI,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA,CAAE,kBAAA,CAAmB,OAAA,EAAS;AAAA,MACnD,KAAA,EAAO,OAAA;AAAA,MACP,GAAA,EAAK,SAAA;AAAA,MACL,IAAA,EAAM;AAAA,KACP,CAAA,EACH,CAAA;AAAA,oBACA,IAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,CAAA,EACP,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,gBAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,YAAO,QAAA,EAAA,QAAA,EAAM,CAAA;AAAA,QAAS,GAAA;AAAA,QAAE,UAAU,KAAA,IAAS;AAAA,OAAA,EAC9C,CAAA;AAAA,2BACC,gBAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,YAAO,QAAA,EAAA,aAAA,EAAW,CAAA;AAAA,QAAS,GAAA;AAAA,QAAE,SAAA,CAAU;AAAA,OAAA,EAC1C,CAAA;AAAA,2BACC,gBAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,YAAO,QAAA,EAAA,SAAA,EAAO,CAAA;AAAA,QAAS,GAAA;AAAA,QAAE,SAAA,CAAU;AAAA,OAAA,EACtC,CAAA;AAAA,2BACC,gBAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,YAAO,QAAA,EAAA,uBAAA,EAAqB,CAAA;AAAA,QAAS,GAAA;AAAA,QAAE,SAAA,CAAU;AAAA,OAAA,EACpD;AAAA,KAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ,CAAA,EACF,CAAA;AAGK,MAAM,UAAA,GAAa,CAAC,EAAE,IAAA,EAAK,KAAuB;AACvD,EAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,IAAA,uBACE,GAAA,CAAC,GAAA,EAAA,EAAI,OAAA,EAAQ,MAAA,EAAO,gBAAe,QAAA,EAAS,UAAA,EAAW,QAAA,EAAS,MAAA,EAAQ,KACtE,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAM,eAAA,EAAgB,qCAElC,CAAA,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,uBACE,GAAA,CAAC,mBAAA,EAAA,EAAoB,KAAA,EAAM,MAAA,EAAO,QAAQ,GAAA,EACxC,QAAA,kBAAA,IAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA;AAAA,MACA,MAAA,EAAQ,EAAE,GAAA,EAAK,CAAA,EAAG,OAAO,EAAA,EAAI,IAAA,EAAM,EAAA,EAAI,MAAA,EAAQ,CAAA,EAAE;AAAA,MAEjD,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,aAAA,EAAA,EAAc,iBAAgB,KAAA,EAAM,CAAA;AAAA,wBACrC,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAQ,KAAA;AAAA,YACR,eAAe,CAAA,KAAA,KAAS;AACtB,cAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,KAAK,CAAA;AAC3B,cAAA,OAAO,CAAA,EAAG,KAAK,QAAA,EAAS,GAAI,CAAC,CAAA,CAAA,EAAI,IAAA,CAAK,SAAS,CAAA,CAAA;AAAA,YACjD,CAAA;AAAA,YACA,KAAA,EAAO,GAAA;AAAA,YACP,UAAA,EAAW,KAAA;AAAA,YACX,MAAA,EAAQ;AAAA;AAAA,SACV;AAAA,4BACC,KAAA,EAAA,EAAM,CAAA;AAAA,wBACP,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAA,kBAAS,GAAA,CAAC,qBAAkB,CAAA,EAAI,CAAA;AAAA,wBACzC,GAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAQ,OAAA;AAAA,YACR,MAAM,eAAA,CAAgB,OAAA;AAAA,YACtB,MAAA,EAAQ,CAAC,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC;AAAA;AAAA;AACrB;AAAA;AAAA,GACF,EACF,CAAA;AAEJ;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { jsx
|
|
2
|
-
import { Box
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box } from '@material-ui/core';
|
|
3
3
|
import { makeStyles } from '@material-ui/core/styles';
|
|
4
4
|
import { InfoCard } from '@backstage/core-components';
|
|
5
5
|
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
@@ -9,6 +9,8 @@ import { FlagsmithLink } from '../shared/FlagsmithLink.esm.js';
|
|
|
9
9
|
import '@material-ui/icons/ChevronLeft';
|
|
10
10
|
import '@material-ui/icons/ChevronRight';
|
|
11
11
|
import { LoadingState } from '../shared/LoadingState.esm.js';
|
|
12
|
+
import { ErrorState } from '../shared/ErrorState.esm.js';
|
|
13
|
+
import '../shared/ChartTooltip.esm.js';
|
|
12
14
|
import { FLAGSMITH_DASHBOARD_URL } from '../../theme/flagsmithTheme.esm.js';
|
|
13
15
|
import 'react';
|
|
14
16
|
import '@backstage/core-plugin-api';
|
|
@@ -36,17 +38,13 @@ const FlagsmithUsageCard = () => {
|
|
|
36
38
|
return /* @__PURE__ */ jsx(InfoCard, { title: "Flags Usage Data (30 Days)", children: /* @__PURE__ */ jsx(LoadingState, { message: "Loading usage data...", size: 24 }) });
|
|
37
39
|
}
|
|
38
40
|
if (error) {
|
|
39
|
-
return /* @__PURE__ */ jsx(InfoCard, { title: "Flags Usage Data (30 Days)", children: /* @__PURE__ */
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
error
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/* @__PURE__ */ jsx("code", { children: "flagsmith.com/organization-id" }),
|
|
47
|
-
" annotation to this entity."
|
|
48
|
-
] })
|
|
49
|
-
] }) });
|
|
41
|
+
return /* @__PURE__ */ jsx(InfoCard, { title: "Flags Usage Data (30 Days)", children: /* @__PURE__ */ jsx(
|
|
42
|
+
ErrorState,
|
|
43
|
+
{
|
|
44
|
+
message: error,
|
|
45
|
+
hint: !orgId ? "Add a flagsmith.com/organization-id annotation to this entity." : void 0
|
|
46
|
+
}
|
|
47
|
+
) });
|
|
50
48
|
}
|
|
51
49
|
const subheader = project?.name ? `${project.name} - ${totalFlags.toLocaleString()} total flag calls` : void 0;
|
|
52
50
|
return /* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../../../src/components/FlagsmithUsageCard/index.tsx"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../../../src/components/FlagsmithUsageCard/index.tsx"],"sourcesContent":["import { Box } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { InfoCard } from '@backstage/core-components';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { FlagsmithLink, LoadingState, ErrorState } from '../shared';\nimport { FLAGSMITH_DASHBOARD_URL } from '../../theme/flagsmithTheme';\nimport { useFlagsmithUsage } from '../../hooks';\nimport { UsageChart } from './UsageChart';\n\nconst useStyles = makeStyles(theme => ({\n headerActions: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n },\n}));\n\nexport const FlagsmithUsageCard = () => {\n const classes = useStyles();\n const { entity } = useEntity();\n\n const projectId = entity.metadata.annotations?.['flagsmith.com/project-id'];\n const orgId = entity.metadata.annotations?.['flagsmith.com/org-id'];\n\n const { project, usageData, totalFlags, loading, error } = useFlagsmithUsage(\n projectId,\n orgId,\n );\n\n const usageUrl = `${FLAGSMITH_DASHBOARD_URL}/organisation/${orgId}/usage`;\n\n if (loading) {\n return (\n <InfoCard title=\"Flags Usage Data (30 Days)\">\n <LoadingState message=\"Loading usage data...\" size={24} />\n </InfoCard>\n );\n }\n\n if (error) {\n return (\n <InfoCard title=\"Flags Usage Data (30 Days)\">\n <ErrorState\n message={error}\n hint={!orgId ? 'Add a flagsmith.com/organization-id annotation to this entity.' : undefined}\n />\n </InfoCard>\n );\n }\n\n const subheader = project?.name\n ? `${project.name} - ${totalFlags.toLocaleString()} total flag calls`\n : undefined;\n\n return (\n <InfoCard\n title=\"Flags Usage Data (30 Days)\"\n subheader={subheader}\n action={\n orgId && (\n <Box className={classes.headerActions}>\n <FlagsmithLink href={usageUrl} iconOnly tooltip=\"View Usage Analytics\" />\n </Box>\n )\n }\n >\n <Box p={2}>\n <UsageChart data={usageData} />\n </Box>\n </InfoCard>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AASA,MAAM,SAAA,GAAY,WAAW,CAAA,KAAA,MAAU;AAAA,EACrC,aAAA,EAAe;AAAA,IACb,OAAA,EAAS,MAAA;AAAA,IACT,UAAA,EAAY,QAAA;AAAA,IACZ,GAAA,EAAK,KAAA,CAAM,OAAA,CAAQ,CAAC;AAAA;AAExB,CAAA,CAAE,CAAA;AAEK,MAAM,qBAAqB,MAAM;AACtC,EAAA,MAAM,UAAU,SAAA,EAAU;AAC1B,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAE7B,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,QAAA,CAAS,WAAA,GAAc,0BAA0B,CAAA;AAC1E,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,QAAA,CAAS,WAAA,GAAc,sBAAsB,CAAA;AAElE,EAAA,MAAM,EAAE,OAAA,EAAS,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,OAAM,GAAI,iBAAA;AAAA,IACzD,SAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,QAAA,GAAW,CAAA,EAAG,uBAAuB,CAAA,cAAA,EAAiB,KAAK,CAAA,MAAA,CAAA;AAEjE,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,uBACE,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,4BAAA,EACd,QAAA,kBAAA,GAAA,CAAC,gBAAa,OAAA,EAAQ,uBAAA,EAAwB,IAAA,EAAM,EAAA,EAAI,CAAA,EAC1D,CAAA;AAAA,EAEJ;AAEA,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,uBACE,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,4BAAA,EACd,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAS,KAAA;AAAA,QACT,IAAA,EAAM,CAAC,KAAA,GAAQ,gEAAA,GAAmE;AAAA;AAAA,KACpF,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,MAAM,SAAA,GAAY,OAAA,EAAS,IAAA,GACvB,CAAA,EAAG,OAAA,CAAQ,IAAI,CAAA,GAAA,EAAM,UAAA,CAAW,cAAA,EAAgB,CAAA,iBAAA,CAAA,GAChD,MAAA;AAEJ,EAAA,uBACE,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,4BAAA;AAAA,MACN,SAAA;AAAA,MACA,MAAA,EACE,KAAA,oBACE,GAAA,CAAC,GAAA,EAAA,EAAI,WAAW,OAAA,CAAQ,aAAA,EACtB,QAAA,kBAAA,GAAA,CAAC,aAAA,EAAA,EAAc,MAAM,QAAA,EAAU,QAAA,EAAQ,IAAA,EAAC,OAAA,EAAQ,wBAAuB,CAAA,EACzE,CAAA;AAAA,MAIJ,QAAA,kBAAA,GAAA,CAAC,OAAI,CAAA,EAAG,CAAA,EACN,8BAAC,UAAA,EAAA,EAAW,IAAA,EAAM,WAAW,CAAA,EAC/B;AAAA;AAAA,GACF;AAEJ;;;;"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Typography } from '@material-ui/core';
|
|
3
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
4
|
+
|
|
5
|
+
const useStyles = makeStyles((theme) => ({
|
|
6
|
+
tooltipBox: {
|
|
7
|
+
backgroundColor: theme.palette.grey[800],
|
|
8
|
+
border: "none",
|
|
9
|
+
borderRadius: theme.shape.borderRadius,
|
|
10
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.15)"
|
|
11
|
+
},
|
|
12
|
+
tooltipText: {
|
|
13
|
+
color: theme.palette.common.white
|
|
14
|
+
}
|
|
15
|
+
}));
|
|
16
|
+
const ChartTooltip = ({ active, payload, label, children }) => {
|
|
17
|
+
const classes = useStyles();
|
|
18
|
+
if (!active || !payload?.length) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return /* @__PURE__ */ jsx(Box, { p: 1.5, className: classes.tooltipBox, children: children(payload, label) });
|
|
22
|
+
};
|
|
23
|
+
const ChartTooltipText = ({
|
|
24
|
+
variant = "body2",
|
|
25
|
+
fontWeight,
|
|
26
|
+
children
|
|
27
|
+
}) => {
|
|
28
|
+
const classes = useStyles();
|
|
29
|
+
return /* @__PURE__ */ jsx(Typography, { variant, className: classes.tooltipText, style: { fontWeight }, children });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { ChartTooltip, ChartTooltipText };
|
|
33
|
+
//# sourceMappingURL=ChartTooltip.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartTooltip.esm.js","sources":["../../../src/components/shared/ChartTooltip.tsx"],"sourcesContent":["import { Box, Typography } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { ReactNode } from 'react';\n\nconst useStyles = makeStyles((theme) => ({\n tooltipBox: {\n backgroundColor: theme.palette.grey[800],\n border: 'none',\n borderRadius: theme.shape.borderRadius,\n boxShadow: '0 2px 8px rgba(0,0,0,0.15)',\n },\n tooltipText: {\n color: theme.palette.common.white,\n },\n}));\n\ninterface ChartTooltipProps {\n active?: boolean;\n payload?: any[];\n label?: string;\n children: (payload: any[], label?: string) => ReactNode;\n}\n\n/**\n * Generic Recharts custom tooltip component\n * Uses theme colors for consistent theming across light and dark modes\n * Accepts a render function to customize content per use case\n *\n * @example\n * <Tooltip content={\n * <ChartTooltip>\n * {(payload, label) => (\n * <>\n * <ChartTooltipText variant=\"subtitle2\">{label}</ChartTooltipText>\n * <ChartTooltipText>Value: {payload[0].value}</ChartTooltipText>\n * </>\n * )}\n * </ChartTooltip>\n * } />\n */\nexport const ChartTooltip = ({ active, payload, label, children }: ChartTooltipProps) => {\n const classes = useStyles();\n\n if (!active || !payload?.length) {\n return null;\n }\n\n return (\n <Box p={1.5} className={classes.tooltipBox}>\n {children(payload, label)}\n </Box>\n );\n};\n\ninterface ChartTooltipTextProps {\n variant?: 'subtitle2' | 'body2';\n fontWeight?: number;\n children: ReactNode;\n}\n\n/**\n * Text component for chart tooltips with theme-aware white color\n */\nexport const ChartTooltipText = ({\n variant = 'body2',\n fontWeight,\n children,\n}: ChartTooltipTextProps) => {\n const classes = useStyles();\n\n return (\n <Typography variant={variant} className={classes.tooltipText} style={{ fontWeight }}>\n {children}\n </Typography>\n );\n};\n"],"names":[],"mappings":";;;;AAIA,MAAM,SAAA,GAAY,UAAA,CAAW,CAAC,KAAA,MAAW;AAAA,EACvC,UAAA,EAAY;AAAA,IACV,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA;AAAA,IACvC,MAAA,EAAQ,MAAA;AAAA,IACR,YAAA,EAAc,MAAM,KAAA,CAAM,YAAA;AAAA,IAC1B,SAAA,EAAW;AAAA,GACb;AAAA,EACA,WAAA,EAAa;AAAA,IACX,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO;AAAA;AAEhC,CAAA,CAAE,CAAA;AA0BK,MAAM,eAAe,CAAC,EAAE,QAAQ,OAAA,EAAS,KAAA,EAAO,UAAS,KAAyB;AACvF,EAAA,MAAM,UAAU,SAAA,EAAU;AAE1B,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,OAAA,EAAS,MAAA,EAAQ;AAC/B,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,uBACE,GAAA,CAAC,GAAA,EAAA,EAAI,CAAA,EAAG,GAAA,EAAK,SAAA,EAAW,QAAQ,UAAA,EAC7B,QAAA,EAAA,QAAA,CAAS,OAAA,EAAS,KAAK,CAAA,EAC1B,CAAA;AAEJ;AAWO,MAAM,mBAAmB,CAAC;AAAA,EAC/B,OAAA,GAAU,OAAA;AAAA,EACV,UAAA;AAAA,EACA;AACF,CAAA,KAA6B;AAC3B,EAAA,MAAM,UAAU,SAAA,EAAU;AAE1B,EAAA,uBACE,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAkB,SAAA,EAAW,OAAA,CAAQ,aAAa,KAAA,EAAO,EAAE,UAAA,EAAW,EAC/E,QAAA,EACH,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Typography } from '@material-ui/core';
|
|
3
|
+
|
|
4
|
+
const ErrorState = ({ message, hint }) => /* @__PURE__ */ jsxs(Box, { p: 2, children: [
|
|
5
|
+
/* @__PURE__ */ jsxs(Typography, { color: "error", children: [
|
|
6
|
+
"Error: ",
|
|
7
|
+
message
|
|
8
|
+
] }),
|
|
9
|
+
hint && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "textSecondary", style: { marginTop: 8 }, children: hint })
|
|
10
|
+
] });
|
|
11
|
+
|
|
12
|
+
export { ErrorState };
|
|
13
|
+
//# sourceMappingURL=ErrorState.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorState.esm.js","sources":["../../../src/components/shared/ErrorState.tsx"],"sourcesContent":["import { Box, Typography } from '@material-ui/core';\n\ninterface ErrorStateProps {\n message: string;\n hint?: string;\n}\n\nexport const ErrorState = ({ message, hint }: ErrorStateProps) => (\n <Box p={2}>\n <Typography color=\"error\">Error: {message}</Typography>\n {hint && (\n <Typography variant=\"body2\" color=\"textSecondary\" style={{ marginTop: 8 }}>\n {hint}\n </Typography>\n )}\n </Box>\n);\n"],"names":[],"mappings":";;;AAOO,MAAM,UAAA,GAAa,CAAC,EAAE,OAAA,EAAS,MAAK,qBACzC,IAAA,CAAC,GAAA,EAAA,EAAI,CAAA,EAAG,CAAA,EACN,QAAA,EAAA;AAAA,kBAAA,IAAA,CAAC,UAAA,EAAA,EAAW,OAAM,OAAA,EAAQ,QAAA,EAAA;AAAA,IAAA,SAAA;AAAA,IAAQ;AAAA,GAAA,EAAQ,CAAA;AAAA,EACzC,IAAA,oBACC,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,KAAA,EAAM,eAAA,EAAgB,KAAA,EAAO,EAAE,SAAA,EAAW,CAAA,EAAE,EACrE,QAAA,EAAA,IAAA,EACH;AAAA,CAAA,EAEJ;;;;"}
|
|
@@ -4,7 +4,7 @@ import { makeStyles } from '@material-ui/core/styles';
|
|
|
4
4
|
import LaunchIcon from '@material-ui/icons/Launch';
|
|
5
5
|
import { flagsmithColors } from '../../theme/flagsmithTheme.esm.js';
|
|
6
6
|
|
|
7
|
-
const useStyles = makeStyles(() => ({
|
|
7
|
+
const useStyles = makeStyles((theme) => ({
|
|
8
8
|
link: {
|
|
9
9
|
display: "inline-flex",
|
|
10
10
|
alignItems: "center",
|
|
@@ -23,17 +23,23 @@ const useStyles = makeStyles(() => ({
|
|
|
23
23
|
iconButton: {
|
|
24
24
|
padding: 4,
|
|
25
25
|
color: flagsmithColors.primary
|
|
26
|
+
},
|
|
27
|
+
tooltip: {
|
|
28
|
+
backgroundColor: theme.palette.grey[700],
|
|
29
|
+
color: theme.palette.common.white,
|
|
30
|
+
fontSize: "0.75rem"
|
|
26
31
|
}
|
|
27
32
|
}));
|
|
28
33
|
const FlagsmithLink = ({
|
|
29
34
|
href,
|
|
30
35
|
children,
|
|
31
36
|
tooltip = "Open in Flagsmith",
|
|
32
|
-
iconOnly = false
|
|
37
|
+
iconOnly = false,
|
|
38
|
+
onClick
|
|
33
39
|
}) => {
|
|
34
40
|
const classes = useStyles();
|
|
35
41
|
if (iconOnly) {
|
|
36
|
-
return /* @__PURE__ */ jsx(Tooltip, { title: tooltip, children: /* @__PURE__ */ jsx(
|
|
42
|
+
return /* @__PURE__ */ jsx(Tooltip, { title: tooltip, classes: { tooltip: classes.tooltip }, children: /* @__PURE__ */ jsx(
|
|
37
43
|
IconButton,
|
|
38
44
|
{
|
|
39
45
|
className: classes.iconButton,
|
|
@@ -42,11 +48,12 @@ const FlagsmithLink = ({
|
|
|
42
48
|
rel: "noopener noreferrer",
|
|
43
49
|
size: "small",
|
|
44
50
|
"aria-label": tooltip,
|
|
51
|
+
onClick,
|
|
45
52
|
children: /* @__PURE__ */ jsx(LaunchIcon, { fontSize: "small", "aria-hidden": "true" })
|
|
46
53
|
}
|
|
47
54
|
) });
|
|
48
55
|
}
|
|
49
|
-
return /* @__PURE__ */ jsx(Tooltip, { title: tooltip, children: /* @__PURE__ */ jsxs(
|
|
56
|
+
return /* @__PURE__ */ jsx(Tooltip, { title: tooltip, classes: { tooltip: classes.tooltip }, children: /* @__PURE__ */ jsxs(
|
|
50
57
|
Link,
|
|
51
58
|
{
|
|
52
59
|
className: classes.link,
|
|
@@ -54,6 +61,7 @@ const FlagsmithLink = ({
|
|
|
54
61
|
target: "_blank",
|
|
55
62
|
rel: "noopener noreferrer",
|
|
56
63
|
"aria-label": `${tooltip} (opens in new tab)`,
|
|
64
|
+
onClick,
|
|
57
65
|
children: [
|
|
58
66
|
children,
|
|
59
67
|
/* @__PURE__ */ jsx(LaunchIcon, { className: classes.icon, "aria-hidden": "true" })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlagsmithLink.esm.js","sources":["../../../src/components/shared/FlagsmithLink.tsx"],"sourcesContent":["import { Link, Tooltip, IconButton } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport LaunchIcon from '@material-ui/icons/Launch';\nimport { flagsmithColors } from '../../theme/flagsmithTheme';\n\nconst useStyles = makeStyles(
|
|
1
|
+
{"version":3,"file":"FlagsmithLink.esm.js","sources":["../../../src/components/shared/FlagsmithLink.tsx"],"sourcesContent":["import { Link, Tooltip, IconButton } from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport LaunchIcon from '@material-ui/icons/Launch';\nimport { flagsmithColors } from '../../theme/flagsmithTheme';\n\nconst useStyles = makeStyles(theme => ({\n link: {\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n color: 'inherit',\n textDecoration: 'none',\n '&:hover': {\n color: flagsmithColors.primary,\n textDecoration: 'underline',\n },\n },\n icon: {\n fontSize: '0.875rem',\n opacity: 0.7,\n },\n iconButton: {\n padding: 4,\n color: flagsmithColors.primary,\n },\n tooltip: {\n backgroundColor: theme.palette.grey[700],\n color: theme.palette.common.white,\n fontSize: '0.75rem',\n },\n}));\n\ninterface FlagsmithLinkProps {\n href: string;\n children?: React.ReactNode;\n tooltip?: string;\n iconOnly?: boolean;\n onClick?: (event: React.MouseEvent) => void;\n}\n\n/**\n * External link to Flagsmith dashboard\n * Opens in a new tab with appropriate security attributes\n */\nexport const FlagsmithLink = ({\n href,\n children,\n tooltip = 'Open in Flagsmith',\n iconOnly = false,\n onClick,\n}: FlagsmithLinkProps) => {\n const classes = useStyles();\n\n if (iconOnly) {\n return (\n <Tooltip title={tooltip} classes={{ tooltip: classes.tooltip }}>\n <IconButton\n className={classes.iconButton}\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n size=\"small\"\n aria-label={tooltip}\n onClick={onClick}\n >\n <LaunchIcon fontSize=\"small\" aria-hidden=\"true\" />\n </IconButton>\n </Tooltip>\n );\n }\n\n return (\n <Tooltip title={tooltip} classes={{ tooltip: classes.tooltip }}>\n <Link\n className={classes.link}\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={`${tooltip} (opens in new tab)`}\n onClick={onClick}\n >\n {children}\n <LaunchIcon className={classes.icon} aria-hidden=\"true\" />\n </Link>\n </Tooltip>\n );\n};\n"],"names":[],"mappings":";;;;;;AAKA,MAAM,SAAA,GAAY,WAAW,CAAA,KAAA,MAAU;AAAA,EACrC,IAAA,EAAM;AAAA,IACJ,OAAA,EAAS,aAAA;AAAA,IACT,UAAA,EAAY,QAAA;AAAA,IACZ,GAAA,EAAK,CAAA;AAAA,IACL,KAAA,EAAO,SAAA;AAAA,IACP,cAAA,EAAgB,MAAA;AAAA,IAChB,SAAA,EAAW;AAAA,MACT,OAAO,eAAA,CAAgB,OAAA;AAAA,MACvB,cAAA,EAAgB;AAAA;AAClB,GACF;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,QAAA,EAAU,UAAA;AAAA,IACV,OAAA,EAAS;AAAA,GACX;AAAA,EACA,UAAA,EAAY;AAAA,IACV,OAAA,EAAS,CAAA;AAAA,IACT,OAAO,eAAA,CAAgB;AAAA,GACzB;AAAA,EACA,OAAA,EAAS;AAAA,IACP,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA;AAAA,IACvC,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,IAC5B,QAAA,EAAU;AAAA;AAEd,CAAA,CAAE,CAAA;AAcK,MAAM,gBAAgB,CAAC;AAAA,EAC5B,IAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA,GAAU,mBAAA;AAAA,EACV,QAAA,GAAW,KAAA;AAAA,EACX;AACF,CAAA,KAA0B;AACxB,EAAA,MAAM,UAAU,SAAA,EAAU;AAE1B,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,uBACE,GAAA,CAAC,WAAQ,KAAA,EAAO,OAAA,EAAS,SAAS,EAAE,OAAA,EAAS,OAAA,CAAQ,OAAA,EAAQ,EAC3D,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,WAAW,OAAA,CAAQ,UAAA;AAAA,QACnB,IAAA;AAAA,QACA,MAAA,EAAO,QAAA;AAAA,QACP,GAAA,EAAI,qBAAA;AAAA,QACJ,IAAA,EAAK,OAAA;AAAA,QACL,YAAA,EAAY,OAAA;AAAA,QACZ,OAAA;AAAA,QAEA,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,QAAA,EAAS,OAAA,EAAQ,eAAY,MAAA,EAAO;AAAA;AAAA,KAClD,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,uBACE,GAAA,CAAC,WAAQ,KAAA,EAAO,OAAA,EAAS,SAAS,EAAE,OAAA,EAAS,OAAA,CAAQ,OAAA,EAAQ,EAC3D,QAAA,kBAAA,IAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAA,CAAQ,IAAA;AAAA,MACnB,IAAA;AAAA,MACA,MAAA,EAAO,QAAA;AAAA,MACP,GAAA,EAAI,qBAAA;AAAA,MACJ,YAAA,EAAY,GAAG,OAAO,CAAA,mBAAA,CAAA;AAAA,MACtB,OAAA;AAAA,MAEC,QAAA,EAAA;AAAA,QAAA,QAAA;AAAA,4BACA,UAAA,EAAA,EAAW,SAAA,EAAW,OAAA,CAAQ,IAAA,EAAM,eAAY,MAAA,EAAO;AAAA;AAAA;AAAA,GAC1D,EACF,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const FEATURE_TYPES = {
|
|
2
|
+
CONFIG: "CONFIG"};
|
|
3
|
+
const MAX_DISPLAY_TAGS = 3;
|
|
4
|
+
const MAX_TABLE_ENVIRONMENTS = 6;
|
|
5
|
+
const MAX_DETAIL_ENVIRONMENTS = 10;
|
|
6
|
+
const DESCRIPTION_TRUNCATE_LENGTH = 60;
|
|
7
|
+
const PAGINATION_OPTIONS = [10, 25, 50, 100];
|
|
8
|
+
const DEFAULT_ROWS_PER_PAGE = 50;
|
|
9
|
+
const CHART_CONFIG = {
|
|
10
|
+
HEIGHT: 250,
|
|
11
|
+
MARGIN: { top: 5, right: 30, left: 0, bottom: 5 }
|
|
12
|
+
};
|
|
13
|
+
const ENV_COLORS = {
|
|
14
|
+
development: "#4caf50",
|
|
15
|
+
dev: "#4caf50",
|
|
16
|
+
staging: "#ff9800",
|
|
17
|
+
stage: "#ff9800",
|
|
18
|
+
production: "#f44336",
|
|
19
|
+
prod: "#f44336"
|
|
20
|
+
};
|
|
21
|
+
const DEFAULT_ENV_COLORS = [
|
|
22
|
+
"#2196f3",
|
|
23
|
+
"#9c27b0",
|
|
24
|
+
"#00bcd4",
|
|
25
|
+
"#795548",
|
|
26
|
+
"#607d8b",
|
|
27
|
+
"#e91e63"
|
|
28
|
+
];
|
|
29
|
+
const getEnvColor = (envName, index) => {
|
|
30
|
+
const lowerName = envName.toLowerCase();
|
|
31
|
+
for (const [key, color] of Object.entries(ENV_COLORS)) {
|
|
32
|
+
if (lowerName.includes(key)) return color;
|
|
33
|
+
}
|
|
34
|
+
return DEFAULT_ENV_COLORS[index % DEFAULT_ENV_COLORS.length] || "#2196f3";
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { CHART_CONFIG, DEFAULT_ENV_COLORS, DEFAULT_ROWS_PER_PAGE, DESCRIPTION_TRUNCATE_LENGTH, ENV_COLORS, FEATURE_TYPES, MAX_DETAIL_ENVIRONMENTS, MAX_DISPLAY_TAGS, MAX_TABLE_ENVIRONMENTS, PAGINATION_OPTIONS, getEnvColor };
|
|
38
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../../src/constants/index.ts"],"sourcesContent":["/**\n * Constants for FlagsTab and related components\n */\n\n/** Feature types from Flagsmith API */\nexport const FEATURE_TYPES = {\n CONFIG: 'CONFIG',\n FLAG: 'FLAG',\n} as const;\n\n/** Maximum number of tags to display inline before showing \"+N more\" */\nexport const MAX_DISPLAY_TAGS = 3;\n\n/** Maximum number of environments to show in the main table columns */\nexport const MAX_TABLE_ENVIRONMENTS = 6;\n\n/** Maximum number of environments to show in the detailed environment table */\nexport const MAX_DETAIL_ENVIRONMENTS = 10;\n\n/** Maximum characters for description truncation */\nexport const DESCRIPTION_TRUNCATE_LENGTH = 60;\n\n/** Pagination options for the flags table */\nexport const PAGINATION_OPTIONS = [10, 25, 50, 100];\n\n/** Default rows per page */\nexport const DEFAULT_ROWS_PER_PAGE = 50;\n\n/** Chart dimensions */\nexport const CHART_CONFIG = {\n HEIGHT: 250,\n MARGIN: { top: 5, right: 30, left: 0, bottom: 5 },\n} as const;\n\n/** Environment colors for analytics chart */\nexport const ENV_COLORS: Record<string, string> = {\n development: '#4caf50',\n dev: '#4caf50',\n staging: '#ff9800',\n stage: '#ff9800',\n production: '#f44336',\n prod: '#f44336',\n};\n\n/** Fallback colors for environments not matching predefined names */\nexport const DEFAULT_ENV_COLORS = [\n '#2196f3',\n '#9c27b0',\n '#00bcd4',\n '#795548',\n '#607d8b',\n '#e91e63',\n] as const;\n\n/**\n * Get color for an environment based on its name\n */\nexport const getEnvColor = (envName: string, index: number): string => {\n const lowerName = envName.toLowerCase();\n for (const [key, color] of Object.entries(ENV_COLORS)) {\n if (lowerName.includes(key)) return color;\n }\n return DEFAULT_ENV_COLORS[index % DEFAULT_ENV_COLORS.length] || '#2196f3';\n};\n"],"names":[],"mappings":"AAKO,MAAM,aAAA,GAAgB;AAAA,EAC3B,MAAA,EAAQ,QAEV;AAGO,MAAM,gBAAA,GAAmB;AAGzB,MAAM,sBAAA,GAAyB;AAG/B,MAAM,uBAAA,GAA0B;AAGhC,MAAM,2BAAA,GAA8B;AAGpC,MAAM,kBAAA,GAAqB,CAAC,EAAA,EAAI,EAAA,EAAI,IAAI,GAAG;AAG3C,MAAM,qBAAA,GAAwB;AAG9B,MAAM,YAAA,GAAe;AAAA,EAC1B,MAAA,EAAQ,GAAA;AAAA,EACR,MAAA,EAAQ,EAAE,GAAA,EAAK,CAAA,EAAG,OAAO,EAAA,EAAI,IAAA,EAAM,CAAA,EAAG,MAAA,EAAQ,CAAA;AAChD;AAGO,MAAM,UAAA,GAAqC;AAAA,EAChD,WAAA,EAAa,SAAA;AAAA,EACb,GAAA,EAAK,SAAA;AAAA,EACL,OAAA,EAAS,SAAA;AAAA,EACT,KAAA,EAAO,SAAA;AAAA,EACP,UAAA,EAAY,SAAA;AAAA,EACZ,IAAA,EAAM;AACR;AAGO,MAAM,kBAAA,GAAqB;AAAA,EAChC,SAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF;AAKO,MAAM,WAAA,GAAc,CAAC,OAAA,EAAiB,KAAA,KAA0B;AACrE,EAAA,MAAM,SAAA,GAAY,QAAQ,WAAA,EAAY;AACtC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AACrD,IAAA,IAAI,SAAA,CAAU,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,KAAA;AAAA,EACtC;AACA,EAAA,OAAO,kBAAA,CAAmB,KAAA,GAAQ,kBAAA,CAAmB,MAAM,CAAA,IAAK,SAAA;AAClE;;;;"}
|
|
@@ -36,7 +36,13 @@ function useFlagsmithProject(projectId) {
|
|
|
36
36
|
};
|
|
37
37
|
fetchData();
|
|
38
38
|
}, [projectId, client]);
|
|
39
|
-
|
|
39
|
+
const envIds = environments.map((e) => e.id).join(",");
|
|
40
|
+
const memoizedEnvironments = useMemo(
|
|
41
|
+
() => environments,
|
|
42
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
43
|
+
[envIds]
|
|
44
|
+
);
|
|
45
|
+
return { project, environments: memoizedEnvironments, features, loading, error, client };
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
export { useFlagsmithProject };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFlagsmithProject.esm.js","sources":["../../src/hooks/useFlagsmithProject.ts"],"sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport { useApi, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport {\n FlagsmithClient,\n FlagsmithProject,\n FlagsmithEnvironment,\n FlagsmithFeature,\n} from '../api/FlagsmithClient';\n\nexport interface UseFlagsmithProjectResult {\n project: FlagsmithProject | null;\n environments: FlagsmithEnvironment[];\n features: FlagsmithFeature[];\n loading: boolean;\n error: string | null;\n client: FlagsmithClient;\n}\n\nexport function useFlagsmithProject(\n projectId: string | undefined,\n): UseFlagsmithProjectResult {\n const discoveryApi = useApi(discoveryApiRef);\n const fetchApi = useApi(fetchApiRef);\n\n const client = useMemo(\n () => new FlagsmithClient(discoveryApi, fetchApi),\n [discoveryApi, fetchApi],\n );\n\n const [project, setProject] = useState<FlagsmithProject | null>(null);\n const [environments, setEnvironments] = useState<FlagsmithEnvironment[]>([]);\n const [features, setFeatures] = useState<FlagsmithFeature[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!projectId) {\n setError('No Flagsmith project ID found in entity annotations');\n setLoading(false);\n return;\n }\n\n const fetchData = async () => {\n try {\n const projectData = await client.getProject(parseInt(projectId, 10));\n setProject(projectData);\n\n const envs = await client.getProjectEnvironments(parseInt(projectId, 10));\n setEnvironments(envs || []);\n\n const projectFeatures = await client.getProjectFeatures(projectId);\n setFeatures(projectFeatures || []);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setLoading(false);\n }\n };\n\n fetchData();\n }, [projectId, client]);\n\n return { project, environments, features, loading, error, client };\n}\n"],"names":[],"mappings":";;;;AAkBO,SAAS,oBACd,SAAA,EAC2B;AAC3B,EAAA,MAAM,YAAA,GAAe,OAAO,eAAe,CAAA;AAC3C,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AAEnC,EAAA,MAAM,MAAA,GAAS,OAAA;AAAA,IACb,MAAM,IAAI,eAAA,CAAgB,YAAA,EAAc,QAAQ,CAAA;AAAA,IAChD,CAAC,cAAc,QAAQ;AAAA,GACzB;AAEA,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAkC,IAAI,CAAA;AACpE,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,QAAA,CAAiC,EAAE,CAAA;AAC3E,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,QAAA,CAA6B,EAAE,CAAA;AAC/D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AAEtD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,QAAA,CAAS,qDAAqD,CAAA;AAC9D,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,YAAY,YAAY;AAC5B,MAAA,IAAI;AACF,QAAA,MAAM,cAAc,MAAM,MAAA,CAAO,WAAW,QAAA,CAAS,SAAA,EAAW,EAAE,CAAC,CAAA;AACnE,QAAA,UAAA,CAAW,WAAW,CAAA;AAEtB,QAAA,MAAM,OAAO,MAAM,MAAA,CAAO,uBAAuB,QAAA,CAAS,SAAA,EAAW,EAAE,CAAC,CAAA;AACxE,QAAA,eAAA,CAAgB,IAAA,IAAQ,EAAE,CAAA;AAE1B,QAAA,MAAM,eAAA,GAAkB,MAAM,MAAA,CAAO,kBAAA,CAAmB,SAAS,CAAA;AACjE,QAAA,WAAA,CAAY,eAAA,IAAmB,EAAE,CAAA;AAAA,MACnC,SAAS,GAAA,EAAK;AACZ,QAAA,QAAA,CAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,eAAe,CAAA;AAAA,MAC/D,CAAA,SAAE;AACA,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAClB;AAAA,IACF,CAAA;AAEA,IAAA,SAAA,EAAU;AAAA,EACZ,CAAA,EAAG,CAAC,SAAA,EAAW,MAAM,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"useFlagsmithProject.esm.js","sources":["../../src/hooks/useFlagsmithProject.ts"],"sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport { useApi, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport {\n FlagsmithClient,\n FlagsmithProject,\n FlagsmithEnvironment,\n FlagsmithFeature,\n} from '../api/FlagsmithClient';\n\nexport interface UseFlagsmithProjectResult {\n project: FlagsmithProject | null;\n environments: FlagsmithEnvironment[];\n features: FlagsmithFeature[];\n loading: boolean;\n error: string | null;\n client: FlagsmithClient;\n}\n\nexport function useFlagsmithProject(\n projectId: string | undefined,\n): UseFlagsmithProjectResult {\n const discoveryApi = useApi(discoveryApiRef);\n const fetchApi = useApi(fetchApiRef);\n\n const client = useMemo(\n () => new FlagsmithClient(discoveryApi, fetchApi),\n [discoveryApi, fetchApi],\n );\n\n const [project, setProject] = useState<FlagsmithProject | null>(null);\n const [environments, setEnvironments] = useState<FlagsmithEnvironment[]>([]);\n const [features, setFeatures] = useState<FlagsmithFeature[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!projectId) {\n setError('No Flagsmith project ID found in entity annotations');\n setLoading(false);\n return;\n }\n\n const fetchData = async () => {\n try {\n const projectData = await client.getProject(parseInt(projectId, 10));\n setProject(projectData);\n\n const envs = await client.getProjectEnvironments(parseInt(projectId, 10));\n setEnvironments(envs || []);\n\n const projectFeatures = await client.getProjectFeatures(projectId);\n setFeatures(projectFeatures || []);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setLoading(false);\n }\n };\n\n fetchData();\n }, [projectId, client]);\n\n // Memoize environments to prevent unnecessary re-renders in child components\n // Only create new reference when environment IDs actually change\n const envIds = environments.map(e => e.id).join(',');\n const memoizedEnvironments = useMemo(\n () => environments,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [envIds],\n );\n\n return { project, environments: memoizedEnvironments, features, loading, error, client };\n}\n"],"names":[],"mappings":";;;;AAkBO,SAAS,oBACd,SAAA,EAC2B;AAC3B,EAAA,MAAM,YAAA,GAAe,OAAO,eAAe,CAAA;AAC3C,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AAEnC,EAAA,MAAM,MAAA,GAAS,OAAA;AAAA,IACb,MAAM,IAAI,eAAA,CAAgB,YAAA,EAAc,QAAQ,CAAA;AAAA,IAChD,CAAC,cAAc,QAAQ;AAAA,GACzB;AAEA,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAkC,IAAI,CAAA;AACpE,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,QAAA,CAAiC,EAAE,CAAA;AAC3E,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,QAAA,CAA6B,EAAE,CAAA;AAC/D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AAEtD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,QAAA,CAAS,qDAAqD,CAAA;AAC9D,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,YAAY,YAAY;AAC5B,MAAA,IAAI;AACF,QAAA,MAAM,cAAc,MAAM,MAAA,CAAO,WAAW,QAAA,CAAS,SAAA,EAAW,EAAE,CAAC,CAAA;AACnE,QAAA,UAAA,CAAW,WAAW,CAAA;AAEtB,QAAA,MAAM,OAAO,MAAM,MAAA,CAAO,uBAAuB,QAAA,CAAS,SAAA,EAAW,EAAE,CAAC,CAAA;AACxE,QAAA,eAAA,CAAgB,IAAA,IAAQ,EAAE,CAAA;AAE1B,QAAA,MAAM,eAAA,GAAkB,MAAM,MAAA,CAAO,kBAAA,CAAmB,SAAS,CAAA;AACjE,QAAA,WAAA,CAAY,eAAA,IAAmB,EAAE,CAAA;AAAA,MACnC,SAAS,GAAA,EAAK;AACZ,QAAA,QAAA,CAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,eAAe,CAAA;AAAA,MAC/D,CAAA,SAAE;AACA,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAClB;AAAA,IACF,CAAA;AAEA,IAAA,SAAA,EAAU;AAAA,EACZ,CAAA,EAAG,CAAC,SAAA,EAAW,MAAM,CAAC,CAAA;AAItB,EAAA,MAAM,MAAA,GAAS,aAAa,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,EAAE,CAAA,CAAE,KAAK,GAAG,CAAA;AACnD,EAAA,MAAM,oBAAA,GAAuB,OAAA;AAAA,IAC3B,MAAM,YAAA;AAAA;AAAA,IAEN,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,OAAO,EAAE,OAAA,EAAS,YAAA,EAAc,sBAAsB,QAAA,EAAU,OAAA,EAAS,OAAO,MAAA,EAAO;AACzF;;;;"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import { useMemo, useState, useEffect } from 'react';
|
|
2
2
|
import { useApi, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';
|
|
3
3
|
import { FlagsmithClient } from '../api/FlagsmithClient.esm.js';
|
|
4
4
|
|
|
5
5
|
function useFlagsmithUsage(projectId, orgId) {
|
|
6
6
|
const discoveryApi = useApi(discoveryApiRef);
|
|
7
7
|
const fetchApi = useApi(fetchApiRef);
|
|
8
|
+
const client = useMemo(
|
|
9
|
+
() => new FlagsmithClient(discoveryApi, fetchApi),
|
|
10
|
+
[discoveryApi, fetchApi]
|
|
11
|
+
);
|
|
8
12
|
const [project, setProject] = useState(null);
|
|
9
13
|
const [usageData, setUsageData] = useState([]);
|
|
10
14
|
const [loading, setLoading] = useState(true);
|
|
@@ -17,7 +21,6 @@ function useFlagsmithUsage(projectId, orgId) {
|
|
|
17
21
|
}
|
|
18
22
|
const fetchData = async () => {
|
|
19
23
|
try {
|
|
20
|
-
const client = new FlagsmithClient(discoveryApi, fetchApi);
|
|
21
24
|
const projectData = await client.getProject(parseInt(projectId, 10));
|
|
22
25
|
setProject(projectData);
|
|
23
26
|
const usage = await client.getUsageData(
|
|
@@ -32,7 +35,7 @@ function useFlagsmithUsage(projectId, orgId) {
|
|
|
32
35
|
}
|
|
33
36
|
};
|
|
34
37
|
fetchData();
|
|
35
|
-
}, [projectId, orgId,
|
|
38
|
+
}, [projectId, orgId, client]);
|
|
36
39
|
const totalFlags = usageData.reduce((sum, day) => sum + (day.flags ?? 0), 0);
|
|
37
40
|
return { project, usageData, totalFlags, loading, error };
|
|
38
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFlagsmithUsage.esm.js","sources":["../../src/hooks/useFlagsmithUsage.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { useApi, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport {\n FlagsmithClient,\n FlagsmithProject,\n FlagsmithUsageData,\n} from '../api/FlagsmithClient';\n\nexport interface UseFlagsmithUsageResult {\n project: FlagsmithProject | null;\n usageData: FlagsmithUsageData[];\n totalFlags: number;\n loading: boolean;\n error: string | null;\n}\n\nexport function useFlagsmithUsage(\n projectId: string | undefined,\n orgId: string | undefined,\n): UseFlagsmithUsageResult {\n const discoveryApi = useApi(discoveryApiRef);\n const fetchApi = useApi(fetchApiRef);\n\n const [project, setProject] = useState<FlagsmithProject | null>(null);\n const [usageData, setUsageData] = useState<FlagsmithUsageData[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!projectId || !orgId) {\n setError('Missing Flagsmith project ID or organization ID in entity annotations');\n setLoading(false);\n return;\n }\n\n const fetchData = async () => {\n try {\n const
|
|
1
|
+
{"version":3,"file":"useFlagsmithUsage.esm.js","sources":["../../src/hooks/useFlagsmithUsage.ts"],"sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport { useApi, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport {\n FlagsmithClient,\n FlagsmithProject,\n FlagsmithUsageData,\n} from '../api/FlagsmithClient';\n\nexport interface UseFlagsmithUsageResult {\n project: FlagsmithProject | null;\n usageData: FlagsmithUsageData[];\n totalFlags: number;\n loading: boolean;\n error: string | null;\n}\n\nexport function useFlagsmithUsage(\n projectId: string | undefined,\n orgId: string | undefined,\n): UseFlagsmithUsageResult {\n const discoveryApi = useApi(discoveryApiRef);\n const fetchApi = useApi(fetchApiRef);\n\n // Memoize client to prevent recreation on every render\n const client = useMemo(\n () => new FlagsmithClient(discoveryApi, fetchApi),\n [discoveryApi, fetchApi],\n );\n\n const [project, setProject] = useState<FlagsmithProject | null>(null);\n const [usageData, setUsageData] = useState<FlagsmithUsageData[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!projectId || !orgId) {\n setError('Missing Flagsmith project ID or organization ID in entity annotations');\n setLoading(false);\n return;\n }\n\n const fetchData = async () => {\n try {\n const projectData = await client.getProject(parseInt(projectId, 10));\n setProject(projectData);\n\n const usage = await client.getUsageData(\n parseInt(orgId, 10),\n parseInt(projectId, 10),\n );\n setUsageData(usage);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Unknown error');\n } finally {\n setLoading(false);\n }\n };\n\n fetchData();\n }, [projectId, orgId, client]);\n\n const totalFlags = usageData.reduce((sum, day) => sum + (day.flags ?? 0), 0);\n\n return { project, usageData, totalFlags, loading, error };\n}\n"],"names":[],"mappings":";;;;AAgBO,SAAS,iBAAA,CACd,WACA,KAAA,EACyB;AACzB,EAAA,MAAM,YAAA,GAAe,OAAO,eAAe,CAAA;AAC3C,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AAGnC,EAAA,MAAM,MAAA,GAAS,OAAA;AAAA,IACb,MAAM,IAAI,eAAA,CAAgB,YAAA,EAAc,QAAQ,CAAA;AAAA,IAChD,CAAC,cAAc,QAAQ;AAAA,GACzB;AAEA,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAkC,IAAI,CAAA;AACpE,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,QAAA,CAA+B,EAAE,CAAA;AACnE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AAEtD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,KAAA,EAAO;AACxB,MAAA,QAAA,CAAS,uEAAuE,CAAA;AAChF,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,YAAY,YAAY;AAC5B,MAAA,IAAI;AACF,QAAA,MAAM,cAAc,MAAM,MAAA,CAAO,WAAW,QAAA,CAAS,SAAA,EAAW,EAAE,CAAC,CAAA;AACnE,QAAA,UAAA,CAAW,WAAW,CAAA;AAEtB,QAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,YAAA;AAAA,UACzB,QAAA,CAAS,OAAO,EAAE,CAAA;AAAA,UAClB,QAAA,CAAS,WAAW,EAAE;AAAA,SACxB;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB,SAAS,GAAA,EAAK;AACZ,QAAA,QAAA,CAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,eAAe,CAAA;AAAA,MAC/D,CAAA,SAAE;AACA,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,MAClB;AAAA,IACF,CAAA;AAEA,IAAA,SAAA,EAAU;AAAA,EACZ,CAAA,EAAG,CAAC,SAAA,EAAW,KAAA,EAAO,MAAM,CAAC,CAAA;AAE7B,EAAA,MAAM,UAAA,GAAa,SAAA,CAAU,MAAA,CAAO,CAAC,GAAA,EAAK,QAAQ,GAAA,IAAO,GAAA,CAAI,KAAA,IAAS,CAAA,CAAA,EAAI,CAAC,CAAA;AAE3E,EAAA,OAAO,EAAE,OAAA,EAAS,SAAA,EAAW,UAAA,EAAY,SAAS,KAAA,EAAM;AAC1D;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -100,7 +100,7 @@ declare const flagsmithPlugin: _backstage_frontend_plugin_api.OverridableFronten
|
|
|
100
100
|
defaultTitle?: [Error: `Use the 'title' param instead`];
|
|
101
101
|
title: string;
|
|
102
102
|
defaultGroup?: [Error: `Use the 'group' param instead`];
|
|
103
|
-
group?: ("
|
|
103
|
+
group?: ("development" | "overview" | "documentation" | "deployment" | "operation" | "observability") | (string & {});
|
|
104
104
|
loader: () => Promise<JSX.Element>;
|
|
105
105
|
routeRef?: _backstage_frontend_plugin_api.RouteRef;
|
|
106
106
|
filter?: string | _backstage_plugin_catalog_react_alpha.EntityPredicate | ((entity: _backstage_catalog_model.Entity) => boolean);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { flagsmithColors } from './flagsmithTheme.esm.js';
|
|
2
|
+
|
|
3
|
+
const switchOnStyle = {
|
|
4
|
+
"& .MuiSwitch-switchBase.Mui-checked": {
|
|
5
|
+
color: flagsmithColors.primary
|
|
6
|
+
},
|
|
7
|
+
"& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track": {
|
|
8
|
+
backgroundColor: flagsmithColors.primary
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const detailCardStyle = (theme) => ({
|
|
12
|
+
padding: theme.spacing(1.5),
|
|
13
|
+
marginBottom: theme.spacing(1),
|
|
14
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
15
|
+
borderRadius: theme.shape.borderRadius
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export { detailCardStyle, switchOnStyle };
|
|
19
|
+
//# sourceMappingURL=sharedStyles.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sharedStyles.esm.js","sources":["../../src/theme/sharedStyles.ts"],"sourcesContent":["import { Theme } from '@material-ui/core/styles';\nimport { flagsmithColors } from './flagsmithTheme';\n\n/**\n * Shared style for colored Switch components\n */\nexport const switchOnStyle = {\n '& .MuiSwitch-switchBase.Mui-checked': {\n color: flagsmithColors.primary,\n },\n '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': {\n backgroundColor: flagsmithColors.primary,\n },\n};\n\n/**\n * Shared styles for small chips (tags, badges)\n */\nexport const smallChipStyle = (theme: Theme) => ({\n fontSize: '0.7rem',\n height: 20,\n marginRight: theme.spacing(0.5),\n});\n\n/**\n * Shared styles for detail cards\n */\nexport const detailCardStyle = (theme: Theme) => ({\n padding: theme.spacing(1.5),\n marginBottom: theme.spacing(1),\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: theme.shape.borderRadius,\n});\n"],"names":[],"mappings":";;AAMO,MAAM,aAAA,GAAgB;AAAA,EAC3B,qCAAA,EAAuC;AAAA,IACrC,OAAO,eAAA,CAAgB;AAAA,GACzB;AAAA,EACA,wDAAA,EAA0D;AAAA,IACxD,iBAAiB,eAAA,CAAgB;AAAA;AAErC;AAcO,MAAM,eAAA,GAAkB,CAAC,KAAA,MAAkB;AAAA,EAChD,OAAA,EAAS,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AAAA,EAC1B,YAAA,EAAc,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,EAC7B,MAAA,EAAQ,CAAA,UAAA,EAAa,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,CAAA;AAAA,EAC1C,YAAA,EAAc,MAAM,KAAA,CAAM;AAC5B,CAAA;;;;"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const formatDate = (date) => {
|
|
2
|
+
return new Date(date).toLocaleDateString();
|
|
3
|
+
};
|
|
4
|
+
const formatDateTime = (date) => {
|
|
5
|
+
return new Date(date).toLocaleString();
|
|
6
|
+
};
|
|
7
|
+
const formatShortDate = (date) => {
|
|
8
|
+
return new Date(date).toLocaleDateString("en-US", {
|
|
9
|
+
month: "short",
|
|
10
|
+
day: "numeric"
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { formatDate, formatDateTime, formatShortDate };
|
|
15
|
+
//# sourceMappingURL=dateFormatters.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dateFormatters.esm.js","sources":["../../src/utils/dateFormatters.ts"],"sourcesContent":["/**\n * Consistent date formatting utilities\n */\n\n/**\n * Format a date as a short date string (e.g., \"1/15/2024\")\n */\nexport const formatDate = (date: string | Date): string => {\n return new Date(date).toLocaleDateString();\n};\n\n/**\n * Format a date with time (e.g., \"1/15/2024, 10:30 AM\")\n */\nexport const formatDateTime = (date: string | Date): string => {\n return new Date(date).toLocaleString();\n};\n\n/**\n * Format a date as short month and day (e.g., \"Jan 15\")\n */\nexport const formatShortDate = (date: string | Date): string => {\n return new Date(date).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n });\n};\n\n/**\n * Check if a date is in the future\n */\nexport const isFutureDate = (date: string | Date): boolean => {\n return new Date(date) > new Date();\n};\n"],"names":[],"mappings":"AAOO,MAAM,UAAA,GAAa,CAAC,IAAA,KAAgC;AACzD,EAAA,OAAO,IAAI,IAAA,CAAK,IAAI,CAAA,CAAE,kBAAA,EAAmB;AAC3C;AAKO,MAAM,cAAA,GAAiB,CAAC,IAAA,KAAgC;AAC7D,EAAA,OAAO,IAAI,IAAA,CAAK,IAAI,CAAA,CAAE,cAAA,EAAe;AACvC;AAKO,MAAM,eAAA,GAAkB,CAAC,IAAA,KAAgC;AAC9D,EAAA,OAAO,IAAI,IAAA,CAAK,IAAI,CAAA,CAAE,mBAAmB,OAAA,EAAS;AAAA,IAChD,KAAA,EAAO,OAAA;AAAA,IACP,GAAA,EAAK;AAAA,GACN,CAAA;AACH;;;;"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { FEATURE_TYPES } from '../constants/index.esm.js';
|
|
2
|
+
|
|
3
|
+
const isDefined = (value) => {
|
|
4
|
+
return value !== null && value !== void 0;
|
|
5
|
+
};
|
|
6
|
+
const isMultivariateFeature = (feature) => {
|
|
7
|
+
return Boolean(
|
|
8
|
+
feature.multivariate_options && feature.multivariate_options.length > 0
|
|
9
|
+
);
|
|
10
|
+
};
|
|
11
|
+
const getFlagType = (feature) => {
|
|
12
|
+
if (isMultivariateFeature(feature)) return "Multivariate";
|
|
13
|
+
if (feature.type === FEATURE_TYPES.CONFIG) return "Remote Config";
|
|
14
|
+
return "Standard";
|
|
15
|
+
};
|
|
16
|
+
const getValueType = (feature) => {
|
|
17
|
+
if (isMultivariateFeature(feature) && feature.multivariate_options) {
|
|
18
|
+
const firstOption = feature.multivariate_options[0];
|
|
19
|
+
if (isDefined(firstOption.string_value)) return "String";
|
|
20
|
+
if (isDefined(firstOption.integer_value)) return "Number";
|
|
21
|
+
if (isDefined(firstOption.boolean_value)) return "Boolean";
|
|
22
|
+
}
|
|
23
|
+
if (feature.type === FEATURE_TYPES.CONFIG && isDefined(feature.initial_value)) {
|
|
24
|
+
const value = feature.initial_value;
|
|
25
|
+
if (value === "true" || value === "false") return "Boolean";
|
|
26
|
+
if (!isNaN(Number(value))) return "Number";
|
|
27
|
+
return "String";
|
|
28
|
+
}
|
|
29
|
+
return "Boolean";
|
|
30
|
+
};
|
|
31
|
+
const truncateText = (text, maxLength) => {
|
|
32
|
+
if (text.length <= maxLength) return text;
|
|
33
|
+
return `${text.substring(0, maxLength)}...`;
|
|
34
|
+
};
|
|
35
|
+
const getErrorMessage = (error, fallback) => {
|
|
36
|
+
if (error instanceof Error) return error.message;
|
|
37
|
+
if (typeof error === "string") return error;
|
|
38
|
+
return fallback;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { getErrorMessage, getFlagType, getValueType, isDefined, isMultivariateFeature, truncateText };
|
|
42
|
+
//# sourceMappingURL=flagTypeHelpers.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagTypeHelpers.esm.js","sources":["../../src/utils/flagTypeHelpers.ts"],"sourcesContent":["import { FlagsmithFeature } from '../api/FlagsmithClient';\nimport { FEATURE_TYPES } from '../constants';\n\nexport type FlagType = 'Multivariate' | 'Remote Config' | 'Standard';\nexport type ValueType = 'Boolean' | 'String' | 'Number';\n\nexport interface FlagTypeInfo {\n flagType: FlagType;\n valueType: ValueType;\n isMultivariate: boolean;\n}\n\n/**\n * Check if a value is defined (not null or undefined)\n */\nexport const isDefined = <T>(value: T | null | undefined): value is T => {\n return value !== null && value !== undefined;\n};\n\n/**\n * Check if a feature is multivariate\n */\nexport const isMultivariateFeature = (feature: FlagsmithFeature): boolean => {\n return Boolean(\n feature.multivariate_options && feature.multivariate_options.length > 0\n );\n};\n\n/**\n * Determine the flag type for display\n */\nexport const getFlagType = (feature: FlagsmithFeature): FlagType => {\n if (isMultivariateFeature(feature)) return 'Multivariate';\n if (feature.type === FEATURE_TYPES.CONFIG) return 'Remote Config';\n return 'Standard';\n};\n\n/**\n * Determine the value type based on feature configuration\n */\nexport const getValueType = (feature: FlagsmithFeature): ValueType => {\n // Check multivariate options first\n if (isMultivariateFeature(feature) && feature.multivariate_options) {\n const firstOption = feature.multivariate_options[0];\n if (isDefined(firstOption.string_value)) return 'String';\n if (isDefined(firstOption.integer_value)) return 'Number';\n if (isDefined(firstOption.boolean_value)) return 'Boolean';\n }\n\n // Check CONFIG type with initial_value\n if (feature.type === FEATURE_TYPES.CONFIG && isDefined(feature.initial_value)) {\n const value = feature.initial_value;\n if (value === 'true' || value === 'false') return 'Boolean';\n if (!isNaN(Number(value))) return 'Number';\n return 'String';\n }\n\n // Default for FLAG type\n return 'Boolean';\n};\n\n/**\n * Get complete flag type information\n */\nexport const getFlagTypeInfo = (feature: FlagsmithFeature): FlagTypeInfo => ({\n flagType: getFlagType(feature),\n valueType: getValueType(feature),\n isMultivariate: isMultivariateFeature(feature),\n});\n\n/**\n * Truncate text to a maximum length with ellipsis\n */\nexport const truncateText = (text: string, maxLength: number): string => {\n if (text.length <= maxLength) return text;\n return `${text.substring(0, maxLength)}...`;\n};\n\n/**\n * Get error message from unknown error\n */\nexport const getErrorMessage = (error: unknown, fallback: string): string => {\n if (error instanceof Error) return error.message;\n if (typeof error === 'string') return error;\n return fallback;\n};\n"],"names":[],"mappings":";;AAeO,MAAM,SAAA,GAAY,CAAI,KAAA,KAA4C;AACvE,EAAA,OAAO,KAAA,KAAU,QAAQ,KAAA,KAAU,MAAA;AACrC;AAKO,MAAM,qBAAA,GAAwB,CAAC,OAAA,KAAuC;AAC3E,EAAA,OAAO,OAAA;AAAA,IACL,OAAA,CAAQ,oBAAA,IAAwB,OAAA,CAAQ,oBAAA,CAAqB,MAAA,GAAS;AAAA,GACxE;AACF;AAKO,MAAM,WAAA,GAAc,CAAC,OAAA,KAAwC;AAClE,EAAA,IAAI,qBAAA,CAAsB,OAAO,CAAA,EAAG,OAAO,cAAA;AAC3C,EAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,aAAA,CAAc,MAAA,EAAQ,OAAO,eAAA;AAClD,EAAA,OAAO,UAAA;AACT;AAKO,MAAM,YAAA,GAAe,CAAC,OAAA,KAAyC;AAEpE,EAAA,IAAI,qBAAA,CAAsB,OAAO,CAAA,IAAK,OAAA,CAAQ,oBAAA,EAAsB;AAClE,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,oBAAA,CAAqB,CAAC,CAAA;AAClD,IAAA,IAAI,SAAA,CAAU,WAAA,CAAY,YAAY,CAAA,EAAG,OAAO,QAAA;AAChD,IAAA,IAAI,SAAA,CAAU,WAAA,CAAY,aAAa,CAAA,EAAG,OAAO,QAAA;AACjD,IAAA,IAAI,SAAA,CAAU,WAAA,CAAY,aAAa,CAAA,EAAG,OAAO,SAAA;AAAA,EACnD;AAGA,EAAA,IAAI,QAAQ,IAAA,KAAS,aAAA,CAAc,UAAU,SAAA,CAAU,OAAA,CAAQ,aAAa,CAAA,EAAG;AAC7E,IAAA,MAAM,QAAQ,OAAA,CAAQ,aAAA;AACtB,IAAA,IAAI,KAAA,KAAU,MAAA,IAAU,KAAA,KAAU,OAAA,EAAS,OAAO,SAAA;AAClD,IAAA,IAAI,CAAC,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,GAAG,OAAO,QAAA;AAClC,IAAA,OAAO,QAAA;AAAA,EACT;AAGA,EAAA,OAAO,SAAA;AACT;AAcO,MAAM,YAAA,GAAe,CAAC,IAAA,EAAc,SAAA,KAA8B;AACvE,EAAA,IAAI,IAAA,CAAK,MAAA,IAAU,SAAA,EAAW,OAAO,IAAA;AACrC,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,CAAA,EAAG,SAAS,CAAC,CAAA,GAAA,CAAA;AACxC;AAKO,MAAM,eAAA,GAAkB,CAAC,KAAA,EAAgB,QAAA,KAA6B;AAC3E,EAAA,IAAI,KAAA,YAAiB,KAAA,EAAO,OAAO,KAAA,CAAM,OAAA;AACzC,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AACtC,EAAA,OAAO,QAAA;AACT;;;;"}
|
package/package.json
CHANGED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { Box, Typography } from '@material-ui/core';
|
|
3
|
-
|
|
4
|
-
const UsageTooltip = ({ active, payload }) => {
|
|
5
|
-
if (!active || !payload || !payload.length) {
|
|
6
|
-
return null;
|
|
7
|
-
}
|
|
8
|
-
const data = payload[0].payload;
|
|
9
|
-
return /* @__PURE__ */ jsxs(
|
|
10
|
-
Box,
|
|
11
|
-
{
|
|
12
|
-
p: 1.5,
|
|
13
|
-
style: {
|
|
14
|
-
backgroundColor: "rgba(12, 0, 0, 0.95)",
|
|
15
|
-
border: "1px solid #ccc",
|
|
16
|
-
borderRadius: 4
|
|
17
|
-
},
|
|
18
|
-
children: [
|
|
19
|
-
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", style: { fontWeight: 600 }, children: new Date(data.day).toLocaleDateString("en-US", {
|
|
20
|
-
month: "short",
|
|
21
|
-
day: "numeric",
|
|
22
|
-
year: "numeric"
|
|
23
|
-
}) }),
|
|
24
|
-
/* @__PURE__ */ jsxs(Box, { mt: 1, children: [
|
|
25
|
-
/* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
|
|
26
|
-
/* @__PURE__ */ jsx("strong", { children: "Flags:" }),
|
|
27
|
-
" ",
|
|
28
|
-
data.flags ?? 0
|
|
29
|
-
] }),
|
|
30
|
-
/* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
|
|
31
|
-
/* @__PURE__ */ jsx("strong", { children: "Identities:" }),
|
|
32
|
-
" ",
|
|
33
|
-
data.identities
|
|
34
|
-
] }),
|
|
35
|
-
/* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
|
|
36
|
-
/* @__PURE__ */ jsx("strong", { children: "Traits:" }),
|
|
37
|
-
" ",
|
|
38
|
-
data.traits
|
|
39
|
-
] }),
|
|
40
|
-
/* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
|
|
41
|
-
/* @__PURE__ */ jsx("strong", { children: "Environment Document:" }),
|
|
42
|
-
" ",
|
|
43
|
-
data.environment_document
|
|
44
|
-
] })
|
|
45
|
-
] })
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export { UsageTooltip };
|
|
52
|
-
//# sourceMappingURL=UsageTooltip.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"UsageTooltip.esm.js","sources":["../../../src/components/FlagsmithUsageCard/UsageTooltip.tsx"],"sourcesContent":["import { Box, Typography } from '@material-ui/core';\nimport { FlagsmithUsageData } from '../../api/FlagsmithClient';\n\ninterface UsageTooltipProps {\n active?: boolean;\n payload?: Array<{\n payload: FlagsmithUsageData;\n }>;\n}\n\nexport const UsageTooltip = ({ active, payload }: UsageTooltipProps) => {\n if (!active || !payload || !payload.length) {\n return null;\n }\n\n const data = payload[0].payload;\n\n return (\n <Box\n p={1.5}\n style={{\n backgroundColor: 'rgba(12, 0, 0, 0.95)',\n border: '1px solid #ccc',\n borderRadius: 4,\n }}\n >\n <Typography variant=\"subtitle2\" style={{ fontWeight: 600 }}>\n {new Date(data.day).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n })}\n </Typography>\n <Box mt={1}>\n <Typography variant=\"body2\">\n <strong>Flags:</strong> {data.flags ?? 0}\n </Typography>\n <Typography variant=\"body2\">\n <strong>Identities:</strong> {data.identities}\n </Typography>\n <Typography variant=\"body2\">\n <strong>Traits:</strong> {data.traits}\n </Typography>\n <Typography variant=\"body2\">\n <strong>Environment Document:</strong> {data.environment_document}\n </Typography>\n </Box>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;AAUO,MAAM,YAAA,GAAe,CAAC,EAAE,MAAA,EAAQ,SAAQ,KAAyB;AACtE,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,OAAA,IAAW,CAAC,QAAQ,MAAA,EAAQ;AAC1C,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,CAAC,CAAA,CAAE,OAAA;AAExB,EAAA,uBACE,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,CAAA,EAAG,GAAA;AAAA,MACH,KAAA,EAAO;AAAA,QACL,eAAA,EAAiB,sBAAA;AAAA,QACjB,MAAA,EAAQ,gBAAA;AAAA,QACR,YAAA,EAAc;AAAA,OAChB;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,WAAA,EAAY,KAAA,EAAO,EAAE,UAAA,EAAY,GAAA,EAAI,EACtD,QAAA,EAAA,IAAI,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA,CAAE,mBAAmB,OAAA,EAAS;AAAA,UAC9C,KAAA,EAAO,OAAA;AAAA,UACP,GAAA,EAAK,SAAA;AAAA,UACL,IAAA,EAAM;AAAA,SACP,CAAA,EACH,CAAA;AAAA,wBACA,IAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,CAAA,EACP,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,OAAA,EAClB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAO,QAAA,EAAA,QAAA,EAAM,CAAA;AAAA,YAAS,GAAA;AAAA,YAAE,KAAK,KAAA,IAAS;AAAA,WAAA,EACzC,CAAA;AAAA,0BACA,IAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAClB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAO,QAAA,EAAA,aAAA,EAAW,CAAA;AAAA,YAAS,GAAA;AAAA,YAAE,IAAA,CAAK;AAAA,WAAA,EACrC,CAAA;AAAA,0BACA,IAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAClB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAO,QAAA,EAAA,SAAA,EAAO,CAAA;AAAA,YAAS,GAAA;AAAA,YAAE,IAAA,CAAK;AAAA,WAAA,EACjC,CAAA;AAAA,0BACA,IAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAClB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAO,QAAA,EAAA,uBAAA,EAAqB,CAAA;AAAA,YAAS,GAAA;AAAA,YAAE,IAAA,CAAK;AAAA,WAAA,EAC/C;AAAA,SAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
|