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",