diff --git a/libs/sdk-ui-ext/src/internal/components/configurationControls/forecast/ForecastConfidenceControl.tsx b/libs/sdk-ui-ext/src/internal/components/configurationControls/forecast/ForecastConfidenceControl.tsx
new file mode 100644
index 00000000000..c6cd0c74946
--- /dev/null
+++ b/libs/sdk-ui-ext/src/internal/components/configurationControls/forecast/ForecastConfidenceControl.tsx
@@ -0,0 +1,42 @@
+// (C) 2019-2024 GoodData Corporation
+import React from "react";
+import { injectIntl, WrappedComponentProps } from "react-intl";
+
+import DropdownControl from "../DropdownControl.js";
+import { confidenceDropdownItems } from "../../../constants/dropdowns.js";
+import { getTranslatedDropdownItems } from "../../../utils/translations.js";
+import { IVisualizationProperties } from "../../../interfaces/Visualization.js";
+import { messages } from "../../../../locales.js";
+
+export interface IForecastConfidenceControl {
+ disabled: boolean;
+ value: string;
+ showDisabledMessage: boolean;
+ properties: IVisualizationProperties;
+ pushData: (data: any) => any;
+}
+
+class ForecastConfidenceControl extends React.PureComponent<
+ IForecastConfidenceControl & WrappedComponentProps
+> {
+ public render() {
+ return (
+
+ );
+ }
+
+ private generateDropdownItems() {
+ return getTranslatedDropdownItems(confidenceDropdownItems, this.props.intl);
+ }
+}
+
+export default injectIntl(ForecastConfidenceControl);
diff --git a/libs/sdk-ui-ext/src/internal/components/configurationControls/forecast/ForecastSection.tsx b/libs/sdk-ui-ext/src/internal/components/configurationControls/forecast/ForecastSection.tsx
new file mode 100644
index 00000000000..8ef43e1b4a8
--- /dev/null
+++ b/libs/sdk-ui-ext/src/internal/components/configurationControls/forecast/ForecastSection.tsx
@@ -0,0 +1,86 @@
+// (C) 2019-2024 GoodData Corporation
+import React from "react";
+import ConfigSection from "../ConfigSection.js";
+import ForecastConfidenceControl from "./ForecastConfidenceControl.js";
+import { IVisualizationProperties } from "../../../interfaces/Visualization.js";
+import InputControl from "../InputControl.js";
+import { messages } from "../../../../locales.js";
+import noop from "lodash/noop.js";
+import CheckboxControl from "../CheckboxControl.js";
+
+export interface IForecastSection {
+ controlsDisabled: boolean;
+ enabled: boolean;
+ properties: IVisualizationProperties;
+ propertiesMeta: any;
+ defaultForecastEnabled?: boolean;
+ pushData: (data: any) => any;
+}
+
+class ForecastSection extends React.PureComponent {
+ public static defaultProps: IForecastSection = {
+ controlsDisabled: false,
+ enabled: false,
+ properties: {},
+ propertiesMeta: {},
+ defaultForecastEnabled: true,
+ pushData: noop,
+ };
+
+ public render() {
+ const { controlsDisabled, properties, pushData, defaultForecastEnabled, enabled } = this.props;
+
+ const forecastEnabled = this.props.properties?.controls?.forecast?.enabled ?? defaultForecastEnabled;
+ const forecastConfidence = this.props.properties?.controls?.forecast?.confidence ?? "95";
+ const forecastPeriod = this.props.properties?.controls?.forecast?.period ?? 3;
+ const forecastSeasonal = this.props.properties?.controls?.forecast?.seasonal ?? false;
+ const forecastToggleDisabledByVisualization = !(this.props.propertiesMeta?.forecast_enabled ?? true);
+
+ const toggleDisabled = controlsDisabled || forecastToggleDisabledByVisualization || !enabled;
+ const forecastControlsDisabled = !forecastEnabled || toggleDisabled;
+ const showDisabledMessage = controlsDisabled || forecastToggleDisabledByVisualization || !enabled;
+
+ return (
+
+
+
+
+
+ );
+ }
+}
+
+export default ForecastSection;
diff --git a/libs/sdk-ui-ext/src/internal/components/configurationPanels/ConfigurationPanelContent.tsx b/libs/sdk-ui-ext/src/internal/components/configurationPanels/ConfigurationPanelContent.tsx
index 60e981e76db..49583c28a50 100644
--- a/libs/sdk-ui-ext/src/internal/components/configurationPanels/ConfigurationPanelContent.tsx
+++ b/libs/sdk-ui-ext/src/internal/components/configurationPanels/ConfigurationPanelContent.tsx
@@ -1,8 +1,14 @@
// (C) 2019-2024 GoodData Corporation
import React from "react";
import noop from "lodash/noop.js";
-import { ChartType, DefaultLocale } from "@gooddata/sdk-ui";
-import { IInsightDefinition, insightHasMeasures, ISettings } from "@gooddata/sdk-model";
+import { ChartType, DefaultLocale, BucketNames } from "@gooddata/sdk-ui";
+import {
+ IInsightDefinition,
+ ISettings,
+ insightHasMeasures,
+ insightBuckets,
+ bucketsFind,
+} from "@gooddata/sdk-model";
import {
IReferences,
@@ -15,6 +21,7 @@ import LegendSection from "../configurationControls/legend/LegendSection.js";
import { InternalIntlWrapper } from "../../utils/internalIntlProvider.js";
import { getMeasuresFromMdObject } from "../../utils/bucketHelper.js";
import InteractionsSection from "../configurationControls/interactions/InteractionsSection.js";
+import ForecastSection from "../configurationControls/forecast/ForecastSection.js";
export interface IConfigurationPanelContentProps {
properties?: IVisualizationProperties;
@@ -119,4 +126,33 @@ export default abstract class ConfigurationPanelContent<
/>
) : null;
}
+
+ protected renderForecastSection(): React.ReactNode {
+ const { pushData, properties, propertiesMeta, insight, type, featureFlags } = this.props;
+
+ if (!featureFlags.enableSmartFunctions) {
+ return null;
+ }
+
+ //line chart only now
+ if (type === "line") {
+ const buckets = insightBuckets(insight);
+ const measures = bucketsFind(buckets, (b) => b.localIdentifier === BucketNames.MEASURES);
+ const trends = bucketsFind(buckets, (b) => b.localIdentifier === BucketNames.TREND);
+ //TODO: check if the trend is date attribute somehow
+ const enabled = measures?.items.length === 1 && trends?.items.length === 1;
+
+ return (
+
+ );
+ }
+
+ return null;
+ }
}
diff --git a/libs/sdk-ui-ext/src/internal/components/configurationPanels/LineChartBasedConfigurationPanel.tsx b/libs/sdk-ui-ext/src/internal/components/configurationPanels/LineChartBasedConfigurationPanel.tsx
index 33629536ddc..df09140d010 100644
--- a/libs/sdk-ui-ext/src/internal/components/configurationPanels/LineChartBasedConfigurationPanel.tsx
+++ b/libs/sdk-ui-ext/src/internal/components/configurationPanels/LineChartBasedConfigurationPanel.tsx
@@ -1,4 +1,4 @@
-// (C) 2019-2023 GoodData Corporation
+// (C) 2019-2024 GoodData Corporation
import React from "react";
import { FormattedMessage } from "react-intl";
import { Bubble, BubbleHoverTrigger } from "@gooddata/sdk-ui-kit";
@@ -87,6 +87,7 @@ export default class LineChartBasedConfigurationPanel extends BaseChartConfigura
pushData={pushData}
/>
+ {this.renderForecastSection()}
= defineMessages({
image: { id: "properties.canvas.image" },
imageFit: { id: "properties.canvas.image.fit" },
imageFill: { id: "properties.canvas.image.fill" },
+ forecastTitle: { id: "properties.forecast.title" },
+ forecastDisabledTooltip: { id: "properties.forecastDisabled.title" },
+ forecastPeriod: { id: "properties.forecastPeriod.title" },
+ forecastPeriodPlaceholder: { id: "properties.forecastPeriod.placeholder" },
+ forecastSeasonal: { id: "properties.forecastSeasonal.title" },
+ forecastConfidence: { id: "properties.forecastConfidence.title" },
+ forecastConfidence70: { id: "properties.forecastConfidence.70" },
+ forecastConfidence75: { id: "properties.forecastConfidence.75" },
+ forecastConfidence80: { id: "properties.forecastConfidence.80" },
+ forecastConfidence85: { id: "properties.forecastConfidence.85" },
+ forecastConfidence90: { id: "properties.forecastConfidence.90" },
+ forecastConfidence95: { id: "properties.forecastConfidence.95" },
});
export const comparisonMessages: Record = defineMessages({
diff --git a/libs/sdk-ui-tests/tests/api-regression/charts/__snapshots__/repeater.test.tsx.snap b/libs/sdk-ui-tests/tests/api-regression/charts/__snapshots__/repeater.test.tsx.snap
index 14b1f912cd5..957d6495a9f 100644
--- a/libs/sdk-ui-tests/tests/api-regression/charts/__snapshots__/repeater.test.tsx.snap
+++ b/libs/sdk-ui-tests/tests/api-regression/charts/__snapshots__/repeater.test.tsx.snap
@@ -894,6 +894,18 @@ Once done, you'll be able to save it.",
"properties.config.not_applicable": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|insight": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|report": "Configuration Panel is not applicable for this configuration of the report",
+ "properties.forecast.title": "Forecast",
+ "properties.forecastConfidence.70": "70%",
+ "properties.forecastConfidence.75": "75%",
+ "properties.forecastConfidence.80": "80%",
+ "properties.forecastConfidence.85": "85%",
+ "properties.forecastConfidence.90": "90%",
+ "properties.forecastConfidence.95": "95%",
+ "properties.forecastConfidence.title": "Confidence",
+ "properties.forecastDisabled.title": "Forecast is not available for this combination of buckets",
+ "properties.forecastPeriod.placeholder": "Period",
+ "properties.forecastPeriod.title": "Period",
+ "properties.forecastSeasonal.title": "Seasonality",
"properties.interactions.drillDown": "Drill down",
"properties.interactions.title": "Interactions",
"properties.legend.position": "Position",
@@ -2206,6 +2218,18 @@ Once done, you'll be able to save it.",
"properties.config.not_applicable": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|insight": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|report": "Configuration Panel is not applicable for this configuration of the report",
+ "properties.forecast.title": "Forecast",
+ "properties.forecastConfidence.70": "70%",
+ "properties.forecastConfidence.75": "75%",
+ "properties.forecastConfidence.80": "80%",
+ "properties.forecastConfidence.85": "85%",
+ "properties.forecastConfidence.90": "90%",
+ "properties.forecastConfidence.95": "95%",
+ "properties.forecastConfidence.title": "Confidence",
+ "properties.forecastDisabled.title": "Forecast is not available for this combination of buckets",
+ "properties.forecastPeriod.placeholder": "Period",
+ "properties.forecastPeriod.title": "Period",
+ "properties.forecastSeasonal.title": "Seasonality",
"properties.interactions.drillDown": "Drill down",
"properties.interactions.title": "Interactions",
"properties.legend.position": "Position",
@@ -3553,6 +3577,18 @@ Once done, you'll be able to save it.",
"properties.config.not_applicable": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|insight": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|report": "Configuration Panel is not applicable for this configuration of the report",
+ "properties.forecast.title": "Forecast",
+ "properties.forecastConfidence.70": "70%",
+ "properties.forecastConfidence.75": "75%",
+ "properties.forecastConfidence.80": "80%",
+ "properties.forecastConfidence.85": "85%",
+ "properties.forecastConfidence.90": "90%",
+ "properties.forecastConfidence.95": "95%",
+ "properties.forecastConfidence.title": "Confidence",
+ "properties.forecastDisabled.title": "Forecast is not available for this combination of buckets",
+ "properties.forecastPeriod.placeholder": "Period",
+ "properties.forecastPeriod.title": "Period",
+ "properties.forecastSeasonal.title": "Seasonality",
"properties.interactions.drillDown": "Drill down",
"properties.interactions.title": "Interactions",
"properties.legend.position": "Position",
@@ -4907,6 +4943,18 @@ Once done, you'll be able to save it.",
"properties.config.not_applicable": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|insight": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|report": "Configuration Panel is not applicable for this configuration of the report",
+ "properties.forecast.title": "Forecast",
+ "properties.forecastConfidence.70": "70%",
+ "properties.forecastConfidence.75": "75%",
+ "properties.forecastConfidence.80": "80%",
+ "properties.forecastConfidence.85": "85%",
+ "properties.forecastConfidence.90": "90%",
+ "properties.forecastConfidence.95": "95%",
+ "properties.forecastConfidence.title": "Confidence",
+ "properties.forecastDisabled.title": "Forecast is not available for this combination of buckets",
+ "properties.forecastPeriod.placeholder": "Period",
+ "properties.forecastPeriod.title": "Period",
+ "properties.forecastSeasonal.title": "Seasonality",
"properties.interactions.drillDown": "Drill down",
"properties.interactions.title": "Interactions",
"properties.legend.position": "Position",
@@ -6212,6 +6260,18 @@ Once done, you'll be able to save it.",
"properties.config.not_applicable": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|insight": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|report": "Configuration Panel is not applicable for this configuration of the report",
+ "properties.forecast.title": "Forecast",
+ "properties.forecastConfidence.70": "70%",
+ "properties.forecastConfidence.75": "75%",
+ "properties.forecastConfidence.80": "80%",
+ "properties.forecastConfidence.85": "85%",
+ "properties.forecastConfidence.90": "90%",
+ "properties.forecastConfidence.95": "95%",
+ "properties.forecastConfidence.title": "Confidence",
+ "properties.forecastDisabled.title": "Forecast is not available for this combination of buckets",
+ "properties.forecastPeriod.placeholder": "Period",
+ "properties.forecastPeriod.title": "Period",
+ "properties.forecastSeasonal.title": "Seasonality",
"properties.interactions.drillDown": "Drill down",
"properties.interactions.title": "Interactions",
"properties.legend.position": "Position",
@@ -7432,6 +7492,18 @@ Once done, you'll be able to save it.",
"properties.config.not_applicable": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|insight": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|report": "Configuration Panel is not applicable for this configuration of the report",
+ "properties.forecast.title": "Forecast",
+ "properties.forecastConfidence.70": "70%",
+ "properties.forecastConfidence.75": "75%",
+ "properties.forecastConfidence.80": "80%",
+ "properties.forecastConfidence.85": "85%",
+ "properties.forecastConfidence.90": "90%",
+ "properties.forecastConfidence.95": "95%",
+ "properties.forecastConfidence.title": "Confidence",
+ "properties.forecastDisabled.title": "Forecast is not available for this combination of buckets",
+ "properties.forecastPeriod.placeholder": "Period",
+ "properties.forecastPeriod.title": "Period",
+ "properties.forecastSeasonal.title": "Seasonality",
"properties.interactions.drillDown": "Drill down",
"properties.interactions.title": "Interactions",
"properties.legend.position": "Position",
@@ -8684,6 +8756,18 @@ Once done, you'll be able to save it.",
"properties.config.not_applicable": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|insight": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|report": "Configuration Panel is not applicable for this configuration of the report",
+ "properties.forecast.title": "Forecast",
+ "properties.forecastConfidence.70": "70%",
+ "properties.forecastConfidence.75": "75%",
+ "properties.forecastConfidence.80": "80%",
+ "properties.forecastConfidence.85": "85%",
+ "properties.forecastConfidence.90": "90%",
+ "properties.forecastConfidence.95": "95%",
+ "properties.forecastConfidence.title": "Confidence",
+ "properties.forecastDisabled.title": "Forecast is not available for this combination of buckets",
+ "properties.forecastPeriod.placeholder": "Period",
+ "properties.forecastPeriod.title": "Period",
+ "properties.forecastSeasonal.title": "Seasonality",
"properties.interactions.drillDown": "Drill down",
"properties.interactions.title": "Interactions",
"properties.legend.position": "Position",
@@ -9989,6 +10073,18 @@ Once done, you'll be able to save it.",
"properties.config.not_applicable": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|insight": "Configuration Panel is not applicable for this configuration of the visualization",
"properties.config.not_applicable|report": "Configuration Panel is not applicable for this configuration of the report",
+ "properties.forecast.title": "Forecast",
+ "properties.forecastConfidence.70": "70%",
+ "properties.forecastConfidence.75": "75%",
+ "properties.forecastConfidence.80": "80%",
+ "properties.forecastConfidence.85": "85%",
+ "properties.forecastConfidence.90": "90%",
+ "properties.forecastConfidence.95": "95%",
+ "properties.forecastConfidence.title": "Confidence",
+ "properties.forecastDisabled.title": "Forecast is not available for this combination of buckets",
+ "properties.forecastPeriod.placeholder": "Period",
+ "properties.forecastPeriod.title": "Period",
+ "properties.forecastSeasonal.title": "Seasonality",
"properties.interactions.drillDown": "Drill down",
"properties.interactions.title": "Interactions",
"properties.legend.position": "Position",