diff --git a/libs/sdk-model/api/sdk-model.api.md b/libs/sdk-model/api/sdk-model.api.md index 412c4a70461..81461bcee24 100644 --- a/libs/sdk-model/api/sdk-model.api.md +++ b/libs/sdk-model/api/sdk-model.api.md @@ -778,7 +778,7 @@ export interface IAutomationMetadataObjectBase { // @alpha (undocumented) export interface IAutomationMetadataObjectDefinition extends Omit, IMetadataObjectDefinition, IAuditable { // (undocumented) - exportDefinitions: IExportDefinitionMetadataObjectDefinition[]; + exportDefinitions?: (IExportDefinitionMetadataObjectDefinition | IExportDefinitionMetadataObject)[]; // (undocumented) type: "automation"; } @@ -2776,6 +2776,7 @@ export interface ISettings { enableRichTextDescriptions?: boolean; enableScatterPlotClustering?: boolean; enableScatterPlotSegmentation?: boolean; + enableScheduling?: boolean; enableSeparateTotalLabels?: boolean; enableTableColumnsAutoResizing?: boolean; enableTableColumnsGrowToFit?: boolean; diff --git a/libs/sdk-model/src/automations/index.ts b/libs/sdk-model/src/automations/index.ts index 7ee2c693fc4..f9348e5e68e 100644 --- a/libs/sdk-model/src/automations/index.ts +++ b/libs/sdk-model/src/automations/index.ts @@ -75,7 +75,7 @@ export interface IAutomationMetadataObjectDefinition IMetadataObjectDefinition, IAuditable { type: "automation"; - exportDefinitions: IExportDefinitionMetadataObjectDefinition[]; + exportDefinitions?: (IExportDefinitionMetadataObjectDefinition | IExportDefinitionMetadataObject)[]; } /** diff --git a/libs/sdk-model/src/settings/index.ts b/libs/sdk-model/src/settings/index.ts index c1c2c2054a5..c6af22d839e 100644 --- a/libs/sdk-model/src/settings/index.ts +++ b/libs/sdk-model/src/settings/index.ts @@ -397,6 +397,11 @@ export interface ISettings { */ enableRichTextDescriptions?: boolean; + /** + * Enables scheduling of the dashboard pdf export. + */ + enableScheduling?: boolean; + [key: string]: number | boolean | string | object | undefined; } diff --git a/libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md b/libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md index 8974c9aa7f1..b8c0f5d5854 100644 --- a/libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md +++ b/libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md @@ -136,6 +136,8 @@ import { IAttributeFilter } from '@gooddata/sdk-model'; import { IAttributeFilterBaseProps } from '@gooddata/sdk-ui-filters'; import { IAttributeMetadataObject } from '@gooddata/sdk-model'; import { IAttributeOrMeasure } from '@gooddata/sdk-model'; +import { IAutomationMetadataObject } from '@gooddata/sdk-model'; +import { IAutomationMetadataObjectDefinition } from '@gooddata/sdk-model'; import { IAvailableDrillTargets } from '@gooddata/sdk-ui'; import { IBackendCapabilities } from '@gooddata/sdk-backend-spi'; import { IBaseWidget } from '@gooddata/sdk-model'; @@ -223,14 +225,13 @@ import { INegativeAttributeFilter } from '@gooddata/sdk-model'; import { InsightDisplayFormUsage } from '@gooddata/sdk-model'; import { InsightDrillDefinition } from '@gooddata/sdk-model'; import { IntlShape } from 'react-intl'; +import { IOrganizationUser } from '@gooddata/sdk-model'; import { IPositiveAttributeFilter } from '@gooddata/sdk-model'; import { IPushData } from '@gooddata/sdk-ui'; import { IRelativeDateFilter } from '@gooddata/sdk-model'; import { IRenderListItemProps } from '@gooddata/sdk-ui-kit'; import { IResultWarning } from '@gooddata/sdk-model'; import { IRichTextWidget } from '@gooddata/sdk-model'; -import { IScheduledMail } from '@gooddata/sdk-model'; -import { IScheduledMailDefinition } from '@gooddata/sdk-model'; import { ISeparators } from '@gooddata/sdk-model'; import { ISettings } from '@gooddata/sdk-model'; import { IShareDialogInteractionData } from '@gooddata/sdk-ui-kit'; @@ -243,12 +244,12 @@ import { ITranslations } from '@gooddata/sdk-ui'; import { IUser } from '@gooddata/sdk-model'; import { IUserWorkspaceSettings } from '@gooddata/sdk-backend-spi'; import { IVisualizationCallbacks } from '@gooddata/sdk-ui'; +import { IWebhookMetadataObject } from '@gooddata/sdk-model'; import { IWidget } from '@gooddata/sdk-model'; import { IWidgetAlert } from '@gooddata/sdk-model'; import { IWidgetAlertDefinition } from '@gooddata/sdk-model'; import { IWidgetDefinition } from '@gooddata/sdk-model'; import { IWorkspacePermissions } from '@gooddata/sdk-model'; -import { IWorkspaceUser } from '@gooddata/sdk-model'; import { LocalIdRef } from '@gooddata/sdk-model'; import { MemoizedFunction } from 'lodash'; import { MessageDescriptor } from 'react-intl'; @@ -1015,12 +1016,12 @@ export interface CreateScheduledEmail extends IDashboardCommand { } // @beta -export function createScheduledEmail(scheduledEmail: IScheduledMailDefinition, filterContext?: IFilterContextDefinition, correlationId?: string): CreateScheduledEmail; +export function createScheduledEmail(scheduledEmail: IAutomationMetadataObjectDefinition, filterContext?: IFilterContextDefinition, correlationId?: string): CreateScheduledEmail; // @beta export interface CreateScheduledEmailPayload { readonly filterContext?: IFilterContextDefinition; - readonly scheduledEmail: IScheduledMailDefinition; + readonly scheduledEmail: IAutomationMetadataObjectDefinition; } // @beta (undocumented) @@ -2506,7 +2507,7 @@ export interface DashboardScheduledEmailCreated extends IDashboardEvent { // @beta export interface DashboardScheduledEmailCreatedPayload { - readonly scheduledEmail: IScheduledMail; + readonly scheduledEmail: IAutomationMetadataObject; } // @beta @@ -2858,7 +2859,7 @@ export function DefaultSaveAsNewButton({ isVisible, onSaveAsNewClick }: ISaveAsN export function DefaultSaveButton({ isVisible, isEnabled, isSaving, buttonTitle, buttonValue, onSaveClick, }: ISaveButtonProps): React_2.JSX.Element | null; // @alpha (undocumented) -export const DefaultScheduledEmailDialog: (props: IScheduledEmailDialogProps) => JSX.Element | null; +export const DefaultScheduledEmailDialog: React_2.FC; // @alpha (undocumented) export const DefaultScheduledEmailManagementDialog: React_2.FC; @@ -4708,27 +4709,32 @@ export function isBrokenAlertDateFilterInfo(item: IBrokenAlertFilterBasicInfo): // @alpha (undocumented) export interface IScheduledEmailDialogProps { - editSchedule?: IScheduledMail; + automations: IAutomationMetadataObject[]; + editSchedule?: IAutomationMetadataObject; isVisible?: boolean; onCancel?: () => void; onError?: (error: GoodDataSdkError) => void; - onSave?: (scheduledEmailDefinition: IScheduledMailDefinition) => void; + onSave?: (scheduledEmailDefinition: IAutomationMetadataObject) => void; onSaveError?: (error: GoodDataSdkError) => void; onSaveSuccess?: () => void; - onSubmit?: (scheduledEmailDefinition: IScheduledMailDefinition) => void; + onSubmit?: (scheduledEmailDefinition: IAutomationMetadataObject | IAutomationMetadataObjectDefinition) => void; onSuccess?: () => void; - users: IWorkspaceUser[]; + users: IOrganizationUser[]; + webhooks: IWebhookMetadataObject[]; } // @alpha (undocumented) export interface IScheduledEmailManagementDialogProps { + automations: IAutomationMetadataObject[]; + isLoadingScheduleData: boolean; isVisible?: boolean; onAdd?: () => void; onClose?: () => void; onDeleteError?: (error: GoodDataSdkError) => void; onDeleteSuccess?: () => void; - onEdit?: (scheduledMail: IScheduledMail, users: IWorkspaceUser[]) => void; - onLoadError?: (error: GoodDataSdkError) => void; + onEdit?: (scheduledMail: IAutomationMetadataObject) => void; + scheduleDataError?: GoodDataSdkError; + webhooks: IWebhookMetadataObject[]; } // @internal @@ -6398,12 +6404,12 @@ export interface SaveScheduledEmail extends IDashboardCommand { } // @beta -export function saveScheduledEmail(scheduledEmail: IScheduledMailDefinition, filterContextRef?: ObjRef, correlationId?: string): SaveScheduledEmail; +export function saveScheduledEmail(scheduledEmail: IAutomationMetadataObject, filterContextRef?: ObjRef, correlationId?: string): SaveScheduledEmail; // @beta export interface SaveScheduledEmailPayload { readonly filterContextRef?: ObjRef; - readonly scheduledEmail: IScheduledMailDefinition; + readonly scheduledEmail: IAutomationMetadataObject; } // @public (undocumented) @@ -6845,6 +6851,9 @@ export const selectEnableRenamingProjectToWorkspace: DashboardSelector; // @internal export const selectEnableRichTextDescriptions: DashboardSelector; +// @alpha (undocumented) +export const selectEnableScheduling: DashboardSelector; + // @internal export const selectEnableUnavailableItemsVisibility: DashboardSelector; @@ -8234,16 +8243,16 @@ export type UseDashboardQueryProcessingResult { +export const useDashboardScheduledEmails: ({ onReload }?: { + onReload?: (() => void) | undefined; +}) => { isScheduledEmailingVisible: boolean; - enableInsightExportScheduling: boolean; defaultOnScheduleEmailing: () => void; isScheduleEmailingDialogOpen: boolean; isScheduleEmailingManagementDialogOpen: boolean; - onScheduleEmailingOpen: (attachmentRef?: ObjRef) => void; - onScheduleEmailingManagementEdit: (schedule: IScheduledMail, users: IWorkspaceUser[]) => void; - scheduledEmailToEdit: IScheduledMail | undefined; - users: IWorkspaceUser[]; + onScheduleEmailingOpen: () => void; + onScheduleEmailingManagementEdit: (schedule: IAutomationMetadataObject) => void; + scheduledEmailToEdit: IAutomationMetadataObject | undefined; onScheduleEmailingCancel: () => void; onScheduleEmailingCreateError: () => void; onScheduleEmailingCreateSuccess: () => void; @@ -8256,6 +8265,18 @@ export const useDashboardScheduledEmails: () => { onScheduleEmailingManagementDeleteError: () => void; }; +// @alpha +export const useDashboardScheduledEmailsData: ({ reloadId, onLoadError, }: { + reloadId: number; + onLoadError: () => void; +}) => { + isLoading: boolean; + loadError: any; + webhooks: IWebhookMetadataObject[]; + automations: IAutomationMetadataObject[]; + users: IOrganizationUser[]; +}; + // @public export const useDashboardSelector: TypedUseSelectorHook; diff --git a/libs/sdk-ui-dashboard/src/internal.ts b/libs/sdk-ui-dashboard/src/internal.ts index 80ea51484cc..be8c566dd8c 100644 --- a/libs/sdk-ui-dashboard/src/internal.ts +++ b/libs/sdk-ui-dashboard/src/internal.ts @@ -1,4 +1,4 @@ -// (C) 2022 GoodData Corporation +// (C) 2022-2024 GoodData Corporation /** * This file is used to re-export internal parts of the package that are used in other GoodData applications. @@ -10,7 +10,7 @@ export * from "./presentation/constants/index.js"; export * from "./presentation/layout/DefaultDashboardLayoutRenderer/index.js"; export * from "./presentation/presentationComponents/index.js"; export * from "./presentation/scheduledEmail/index.js"; -export * from "./presentation/scheduledEmail/DefaultScheduledEmailDialog/ScheduledMailDialogRenderer/ScheduledMailDialogRenderer.js"; +export * from "./presentation/scheduledEmail/DefaultScheduledEmailDialog/DefaultScheduledEmailDialog.js"; export { PLATFORM_DATE_FORMAT } from "./presentation/scheduledEmail/DefaultScheduledEmailDialog/constants.js"; export * from "./presentation/widget/kpi/ViewModeDashboardKpi/KpiAlerts/index.js"; export * from "./presentation/widget/kpi/common/KpiContent/index.js"; diff --git a/libs/sdk-ui-dashboard/src/locales.ts b/libs/sdk-ui-dashboard/src/locales.ts index 8c59774947d..eecde91b8d3 100644 --- a/libs/sdk-ui-dashboard/src/locales.ts +++ b/libs/sdk-ui-dashboard/src/locales.ts @@ -18,11 +18,9 @@ export const messages: Record = defineMessages({ scheduleEmailDeleteSuccess: { id: "dialogs.schedule.email.delete.success" }, scheduleManagementLoadError: { id: "dialogs.schedule.management.load.error" }, scheduleManagementDeleteError: { id: "dialogs.schedule.management.delete.error" }, - - scheduleManagementTabUser: { id: "dialogs.schedule.management.tab.user" }, - scheduleManagementTabAll: { id: "dialogs.schedule.management.tab.all" }, scheduleManagementNoSchedules: { id: "dialogs.schedule.management.noSchedules" }, - scheduleManagementNoSchedulesByUser: { id: "dialogs.schedule.management.noSchedules.byUser" }, + scheduleManagementCreate: { id: "dialogs.schedule.management.create" }, + scheduleManagementListTitle: { id: "dialogs.schedule.management.list.title" }, saveAsNewAlertsAndEmailsMessage: { id: "dialogs.save.as.new.alertsAndEmailsMessage" }, saveAsNewAlertsMessage: { id: "dialogs.save.as.new.alertsMessage" }, @@ -42,6 +40,7 @@ export const messages: Record = defineMessages({ controlButtonsSaveAndPublishNoChanges: { id: "controlButtons.saveAndPublish.disable.noChanges.title" }, controlButtonsSaveAndPublishEmpty: { id: "controlButtons.saveAndPublish.disable.empty.title" }, + scheduleDialogEmailRepeats_cron: { id: "dialogs.schedule.email.repeats.types.cron" }, scheduleDialogEmailRepeats_custom: { id: "dialogs.schedule.email.repeats.types.custom" }, scheduleDialogEmailRepeats_daily: { id: "dialogs.schedule.email.repeats.types.daily" }, scheduleDialogEmailRepeats_monthly: { id: "dialogs.schedule.email.repeats.types.monthly" }, diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/scheduledEmail/createScheduledEmailHandler.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/scheduledEmail/createScheduledEmailHandler.ts index eeb12bb88e9..cb3cbe902d0 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/scheduledEmail/createScheduledEmailHandler.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/scheduledEmail/createScheduledEmailHandler.ts @@ -1,20 +1,19 @@ -// (C) 2021-2022 GoodData Corporation +// (C) 2021-2024 GoodData Corporation import { SagaIterator } from "redux-saga"; import { call } from "redux-saga/effects"; import { DashboardContext } from "../../types/commonTypes.js"; -import { IFilterContextDefinition, IScheduledMail, IScheduledMailDefinition } from "@gooddata/sdk-model"; +import { IAutomationMetadataObject, IAutomationMetadataObjectDefinition } from "@gooddata/sdk-model"; import { PromiseFnReturnType } from "../../types/sagas.js"; import { CreateScheduledEmail } from "../../commands/scheduledEmail.js"; import { DashboardScheduledEmailCreated, scheduledEmailCreated } from "../../events/scheduledEmail.js"; function createScheduledEmail( ctx: DashboardContext, - scheduledEmail: IScheduledMailDefinition, - filterContext?: IFilterContextDefinition, -): Promise { + scheduledEmail: IAutomationMetadataObjectDefinition, +): Promise { const { backend, workspace } = ctx; - return backend.workspace(workspace).dashboards().createScheduledMail(scheduledEmail, filterContext); + return backend.workspace(workspace).automations().createAutomation(scheduledEmail); } export function* createScheduledEmailHandler( @@ -25,7 +24,6 @@ export function* createScheduledEmailHandler( createScheduledEmail, ctx, cmd.payload.scheduledEmail, - cmd.payload.filterContext, ); return scheduledEmailCreated(ctx, scheduledEmail, cmd.correlationId); diff --git a/libs/sdk-ui-dashboard/src/model/commandHandlers/scheduledEmail/saveScheduledEmailHandler.ts b/libs/sdk-ui-dashboard/src/model/commandHandlers/scheduledEmail/saveScheduledEmailHandler.ts index aeb53a978d9..8c0817be92f 100644 --- a/libs/sdk-ui-dashboard/src/model/commandHandlers/scheduledEmail/saveScheduledEmailHandler.ts +++ b/libs/sdk-ui-dashboard/src/model/commandHandlers/scheduledEmail/saveScheduledEmailHandler.ts @@ -1,32 +1,28 @@ -// (C) 2021-2022 GoodData Corporation +// (C) 2021-2024 GoodData Corporation import { SagaIterator } from "redux-saga"; import { call } from "redux-saga/effects"; import { DashboardContext } from "../../types/commonTypes.js"; -import { IScheduledMailDefinition, ObjRef, isObjRef } from "@gooddata/sdk-model"; +import { IAutomationMetadataObject, isObjRef } from "@gooddata/sdk-model"; import { SaveScheduledEmail } from "../../commands/scheduledEmail.js"; import { DashboardScheduledEmailSaved, scheduledEmailSaved } from "../../events/scheduledEmail.js"; function saveScheduledEmail( ctx: DashboardContext, - scheduledEmail: IScheduledMailDefinition, - filterContextRef?: ObjRef, -): Promise { + scheduledEmail: IAutomationMetadataObject, +): Promise { const { backend, workspace } = ctx; if (!isObjRef(scheduledEmail)) { throw new Error("Cannot save schedule not referencing to an persisted object"); } - return backend - .workspace(workspace) - .dashboards() - .updateScheduledMail(scheduledEmail, scheduledEmail, filterContextRef); + return backend.workspace(workspace).automations().updateAutomation(scheduledEmail); } export function* saveScheduledEmailHandler( ctx: DashboardContext, cmd: SaveScheduledEmail, ): SagaIterator { - const { scheduledEmail, filterContextRef } = cmd.payload; - yield call(saveScheduledEmail, ctx, scheduledEmail, filterContextRef); + const { scheduledEmail } = cmd.payload; + yield call(saveScheduledEmail, ctx, scheduledEmail); return scheduledEmailSaved(ctx, cmd.correlationId); } diff --git a/libs/sdk-ui-dashboard/src/model/commands/scheduledEmail.ts b/libs/sdk-ui-dashboard/src/model/commands/scheduledEmail.ts index 23f4a084f4a..eafa71acb14 100644 --- a/libs/sdk-ui-dashboard/src/model/commands/scheduledEmail.ts +++ b/libs/sdk-ui-dashboard/src/model/commands/scheduledEmail.ts @@ -1,6 +1,11 @@ -// (C) 2021-2023 GoodData Corporation +// (C) 2021-2024 GoodData Corporation -import { IFilterContextDefinition, IScheduledMailDefinition, ObjRef } from "@gooddata/sdk-model"; +import { + IFilterContextDefinition, + IAutomationMetadataObjectDefinition, + IAutomationMetadataObject, + ObjRef, +} from "@gooddata/sdk-model"; import { IDashboardCommand } from "./base.js"; /** @@ -11,7 +16,7 @@ export interface CreateScheduledEmailPayload { /** * The scheduled email to create. */ - readonly scheduledEmail: IScheduledMailDefinition; + readonly scheduledEmail: IAutomationMetadataObjectDefinition; /** * Filter context to use for the scheduled email. If no filter context is provided, stored dashboard filter context will be used. */ @@ -41,7 +46,7 @@ export interface CreateScheduledEmail extends IDashboardCommand { * @beta */ export function createScheduledEmail( - scheduledEmail: IScheduledMailDefinition, + scheduledEmail: IAutomationMetadataObjectDefinition, filterContext?: IFilterContextDefinition, correlationId?: string, ): CreateScheduledEmail { @@ -73,7 +78,7 @@ export interface SaveScheduledEmailPayload { /** * The scheduled email to save. */ - readonly scheduledEmail: IScheduledMailDefinition; + readonly scheduledEmail: IAutomationMetadataObject; /** * optionally specify existing filter context reference to be used for all attachments */ @@ -91,7 +96,7 @@ export interface SaveScheduledEmailPayload { * @beta */ export function saveScheduledEmail( - scheduledEmail: IScheduledMailDefinition, + scheduledEmail: IAutomationMetadataObject, filterContextRef?: ObjRef, correlationId?: string, ): SaveScheduledEmail { diff --git a/libs/sdk-ui-dashboard/src/model/events/scheduledEmail.ts b/libs/sdk-ui-dashboard/src/model/events/scheduledEmail.ts index 3ab6e0dc839..509824bd41f 100644 --- a/libs/sdk-ui-dashboard/src/model/events/scheduledEmail.ts +++ b/libs/sdk-ui-dashboard/src/model/events/scheduledEmail.ts @@ -1,5 +1,5 @@ -// (C) 2021-2023 GoodData Corporation -import { IScheduledMail } from "@gooddata/sdk-model"; +// (C) 2021-2024 GoodData Corporation +import { IAutomationMetadataObject } from "@gooddata/sdk-model"; import { DashboardContext } from "../types/commonTypes.js"; import { IDashboardEvent } from "./base.js"; import { eventGuard } from "./util.js"; @@ -12,7 +12,7 @@ export interface DashboardScheduledEmailCreatedPayload { /** * The scheduled email created. */ - readonly scheduledEmail: IScheduledMail; + readonly scheduledEmail: IAutomationMetadataObject; } /** @@ -27,7 +27,7 @@ export interface DashboardScheduledEmailCreated extends IDashboardEvent { export function scheduledEmailCreated( ctx: DashboardContext, - scheduledEmail: IScheduledMail, + scheduledEmail: IAutomationMetadataObject, correlationId?: string, ): DashboardScheduledEmailCreated { return { diff --git a/libs/sdk-ui-dashboard/src/model/react/index.ts b/libs/sdk-ui-dashboard/src/model/react/index.ts index 3661f5f2c2e..4370a404a04 100644 --- a/libs/sdk-ui-dashboard/src/model/react/index.ts +++ b/libs/sdk-ui-dashboard/src/model/react/index.ts @@ -1,4 +1,4 @@ -// (C) 2021-2022 GoodData Corporation +// (C) 2021-2024 GoodData Corporation export { DashboardStoreProvider, useDashboardDispatch, @@ -25,4 +25,5 @@ export { IDashboardStoreProviderProps } from "./types.js"; export { useDispatchDashboardCommand } from "./useDispatchDashboardCommand.js"; export { useWidgetExecutionsHandler } from "./useWidgetExecutionsHandler.js"; export { useDashboardScheduledEmails } from "./useDashboardScheduledEmails.js"; +export { useDashboardScheduledEmailsData } from "./useDashboardScheduledEmailsData.js"; export { useWidgetSelection, IUseWidgetSelectionResult } from "./useWidgetSelection.js"; diff --git a/libs/sdk-ui-dashboard/src/model/react/useCurrentOrganization.ts b/libs/sdk-ui-dashboard/src/model/react/useCurrentOrganization.ts new file mode 100644 index 00000000000..6c4a177b9f2 --- /dev/null +++ b/libs/sdk-ui-dashboard/src/model/react/useCurrentOrganization.ts @@ -0,0 +1,52 @@ +// (C) 2020-2024 GoodData Corporation +import { IAnalyticalBackend, IOrganization } from "@gooddata/sdk-backend-spi"; +import { + GoodDataSdkError, + useBackendStrict, + useCancelablePromise, + UseCancelablePromiseCallbacks, + UseCancelablePromiseState, +} from "@gooddata/sdk-ui"; + +interface IUseCurrentOrganizationConfig + extends UseCancelablePromiseCallbacks { + /** + * Backend to work with. + * + * Note: the backend must come either from this property or from BackendContext. If you do not specify + * backend here, then the hook MUST be called within an existing BackendContext. + */ + backend?: IAnalyticalBackend; + + /** + * Enable or disable the hook. + */ + enable?: boolean; +} + +/** + * @internal + */ +export function useCurrentOrganization({ + enable, + backend, + onCancel, + onError, + onLoading, + onPending, + onSuccess, +}: IUseCurrentOrganizationConfig): UseCancelablePromiseState { + const effectiveBackend = useBackendStrict(backend); + + return useCancelablePromise( + { + promise: enable ? () => effectiveBackend.organizations().getCurrentOrganization() : undefined, + onCancel, + onError, + onLoading, + onPending, + onSuccess, + }, + [effectiveBackend], + ); +} diff --git a/libs/sdk-ui-dashboard/src/model/react/useDashboardScheduledEmails.ts b/libs/sdk-ui-dashboard/src/model/react/useDashboardScheduledEmails.ts index 8c5df582ba9..c23e83fd990 100644 --- a/libs/sdk-ui-dashboard/src/model/react/useDashboardScheduledEmails.ts +++ b/libs/sdk-ui-dashboard/src/model/react/useDashboardScheduledEmails.ts @@ -1,15 +1,13 @@ -// (C) 2022 GoodData Corporation +// (C) 2022-2024 GoodData Corporation import { useCallback, useState } from "react"; import { useToastMessage } from "@gooddata/sdk-ui-kit"; -import { IScheduledMail, IWorkspaceUser, ObjRef } from "@gooddata/sdk-model"; +import { IAutomationMetadataObject } from "@gooddata/sdk-model"; import { useDashboardDispatch, useDashboardSelector } from "./DashboardStoreProvider.js"; import { - selectCanCreateScheduledMail, selectDashboardRef, - selectEnableInsightExportScheduling, - selectEnableKPIDashboardSchedule, + selectEnableScheduling, selectIsInViewMode, selectIsReadOnly, selectIsScheduleEmailDialogOpen, @@ -25,7 +23,7 @@ import { messages } from "../../locales.js"; * * @alpha */ -export const useDashboardScheduledEmails = () => { +export const useDashboardScheduledEmails = ({ onReload }: { onReload?: () => void } = {}) => { const { addSuccess, addError } = useToastMessage(); const isScheduleEmailingDialogOpen = useDashboardSelector(selectIsScheduleEmailDialogOpen); const isScheduleEmailingManagementDialogOpen = useDashboardSelector( @@ -33,31 +31,33 @@ export const useDashboardScheduledEmails = () => { ); const dispatch = useDashboardDispatch(); const dashboardRef = useDashboardSelector(selectDashboardRef); - const enableInsightExportScheduling = useDashboardSelector(selectEnableInsightExportScheduling); const isReadOnly = useDashboardSelector(selectIsReadOnly); const isInViewMode = useDashboardSelector(selectIsInViewMode); - const canCreateScheduledMail = useDashboardSelector(selectCanCreateScheduledMail); - const isScheduledEmailingEnabled = useDashboardSelector(selectEnableKPIDashboardSchedule); const menuButtonItemsVisibility = useDashboardSelector(selectMenuButtonItemsVisibility); + const isScheduledEmailingEnabled = useDashboardSelector(selectEnableScheduling); - const openScheduleEmailingDialog = () => dispatch(uiActions.openScheduleEmailDialog()); - const closeScheduleEmailingDialog = () => dispatch(uiActions.closeScheduleEmailDialog()); - const openScheduleEmailingManagementDialog = () => - enableInsightExportScheduling && dispatch(uiActions.openScheduleEmailManagementDialog()); - const closeScheduleEmailingManagementDialog = () => - enableInsightExportScheduling && dispatch(uiActions.closeScheduleEmailManagementDialog()); - const setScheduledEmailDefaultAttachment = (attachmentRef: ObjRef) => - enableInsightExportScheduling && - dispatch(uiActions.setScheduleEmailDialogDefaultAttachment(attachmentRef)); - const resetScheduledEmailDefaultAttachment = () => - enableInsightExportScheduling && dispatch(uiActions.resetScheduleEmailDialogDefaultAttachment()); - const [scheduledEmailToEdit, setScheduledEmailToEdit] = useState(); - const [users, setUsers] = useState([]); + const openScheduleEmailingDialog = useCallback( + () => isScheduledEmailingEnabled && dispatch(uiActions.openScheduleEmailDialog()), + [dispatch, isScheduledEmailingEnabled], + ); + const closeScheduleEmailingDialog = useCallback( + () => isScheduledEmailingEnabled && dispatch(uiActions.closeScheduleEmailDialog()), + [dispatch, isScheduledEmailingEnabled], + ); + const openScheduleEmailingManagementDialog = useCallback( + () => isScheduledEmailingEnabled && dispatch(uiActions.openScheduleEmailManagementDialog()), + [dispatch, isScheduledEmailingEnabled], + ); + const closeScheduleEmailingManagementDialog = useCallback( + () => isScheduledEmailingEnabled && dispatch(uiActions.closeScheduleEmailManagementDialog()), + [dispatch, isScheduledEmailingEnabled], + ); + + const [scheduledEmailToEdit, setScheduledEmailToEdit] = useState(); const isScheduledEmailingVisible = isInViewMode && !isReadOnly && - canCreateScheduledMail && isScheduledEmailingEnabled && (menuButtonItemsVisibility.scheduleEmailButton ?? true); @@ -72,92 +72,91 @@ export const useDashboardScheduledEmails = () => { return; } - if (enableInsightExportScheduling) { - openScheduleEmailingManagementDialog(); - } else { - openScheduleEmailingDialog(); - } - }, [dashboardRef, enableInsightExportScheduling]); + openScheduleEmailingManagementDialog(); + }, [dashboardRef, openScheduleEmailingManagementDialog]); - const onScheduleEmailingOpen = useCallback((attachmentRef?: ObjRef) => { + const onScheduleEmailingOpen = useCallback(() => { openScheduleEmailingDialog(); - attachmentRef && setScheduledEmailDefaultAttachment(attachmentRef); - }, []); + }, [openScheduleEmailingDialog]); const onScheduleEmailingCreateError = useCallback(() => { closeScheduleEmailingDialog(); addError(messages.scheduleEmailSubmitError); - }, []); + }, [closeScheduleEmailingDialog, addError]); const onScheduleEmailingCreateSuccess = useCallback(() => { closeScheduleEmailingDialog(); addSuccess(messages.scheduleEmailSubmitSuccess); - resetScheduledEmailDefaultAttachment(); - }, []); + onReload?.(); + }, [closeScheduleEmailingDialog, addSuccess, onReload]); const onScheduleEmailingSaveError = useCallback(() => { closeScheduleEmailingDialog(); addError(messages.scheduleEmailSaveError); setScheduledEmailToEdit(undefined); - }, []); + }, [closeScheduleEmailingDialog, addError, setScheduledEmailToEdit]); const onScheduleEmailingSaveSuccess = useCallback(() => { closeScheduleEmailingDialog(); openScheduleEmailingManagementDialog(); addSuccess(messages.scheduleEmailSaveSuccess); setScheduledEmailToEdit(undefined); - }, []); + onReload?.(); + }, [ + closeScheduleEmailingDialog, + openScheduleEmailingManagementDialog, + addSuccess, + setScheduledEmailToEdit, + onReload, + ]); const onScheduleEmailingCancel = useCallback(() => { closeScheduleEmailingDialog(); openScheduleEmailingManagementDialog(); - resetScheduledEmailDefaultAttachment(); setScheduledEmailToEdit(undefined); - }, []); + }, [closeScheduleEmailingDialog, openScheduleEmailingManagementDialog, setScheduledEmailToEdit]); const onScheduleEmailingManagementDeleteSuccess = useCallback(() => { addSuccess(messages.scheduleEmailDeleteSuccess); - }, []); + onReload?.(); + }, [addSuccess, onReload]); const onScheduleEmailingManagementAdd = useCallback(() => { closeScheduleEmailingManagementDialog(); openScheduleEmailingDialog(); - }, []); + }, [closeScheduleEmailingManagementDialog, openScheduleEmailingDialog]); const onScheduleEmailingManagementEdit = useCallback( - (schedule: IScheduledMail, users: IWorkspaceUser[]) => { + (schedule: IAutomationMetadataObject) => { closeScheduleEmailingManagementDialog(); setScheduledEmailToEdit(schedule); - setUsers(users); openScheduleEmailingDialog(); }, - [], + [closeScheduleEmailingManagementDialog, openScheduleEmailingDialog, setScheduledEmailToEdit], ); const onScheduleEmailingManagementClose = useCallback(() => { closeScheduleEmailingManagementDialog(); - }, []); + }, [closeScheduleEmailingManagementDialog]); const onScheduleEmailingManagementLoadingError = useCallback(() => { closeScheduleEmailingManagementDialog(); addError(messages.scheduleManagementLoadError); - }, []); + }, [closeScheduleEmailingManagementDialog, addError]); const onScheduleEmailingManagementDeleteError = useCallback(() => { closeScheduleEmailingManagementDialog(); addError(messages.scheduleManagementDeleteError); - }, []); + }, [closeScheduleEmailingManagementDialog, addError]); return { isScheduledEmailingVisible, - enableInsightExportScheduling, defaultOnScheduleEmailing, isScheduleEmailingDialogOpen, isScheduleEmailingManagementDialogOpen, onScheduleEmailingOpen, onScheduleEmailingManagementEdit, scheduledEmailToEdit, - users, onScheduleEmailingCancel, onScheduleEmailingCreateError, onScheduleEmailingCreateSuccess, diff --git a/libs/sdk-ui-dashboard/src/model/react/useDashboardScheduledEmailsData.ts b/libs/sdk-ui-dashboard/src/model/react/useDashboardScheduledEmailsData.ts new file mode 100644 index 00000000000..f1c8bbd051b --- /dev/null +++ b/libs/sdk-ui-dashboard/src/model/react/useDashboardScheduledEmailsData.ts @@ -0,0 +1,82 @@ +// (C) 2022-2024 GoodData Corporation +import { resolveUseCancelablePromisesStatus, useBackendStrict, useWorkspaceStrict } from "@gooddata/sdk-ui"; +import { useCurrentOrganization } from "./useCurrentOrganization.js"; +import { useOrganizationUsers } from "./useOrganizationUsers.js"; +import { useOrganizationWebhooks } from "./useOrganizationWebhooks.js"; +import { useWorkspaceAutomations } from "./useWorkspaceAutomations.js"; +import { useDashboardSelector } from "./DashboardStoreProvider.js"; +import { selectEnableScheduling } from "../store/index.js"; + +/** + * Hook that handles schedule emailing data. + * + * @alpha + */ +export const useDashboardScheduledEmailsData = ({ + reloadId, + onLoadError, +}: { + reloadId: number; + onLoadError: () => void; +}) => { + const isSchedulingEnabled = useDashboardSelector(selectEnableScheduling); + const backend = useBackendStrict(undefined, "useDashboardScheduledEmails"); + const workspace = useWorkspaceStrict(undefined, "useDashboardScheduledEmails"); + + const currentOrganizationPromise = useCurrentOrganization({ + enable: isSchedulingEnabled, + backend, + onError: onLoadError, + }); + + const organizationUsersPromise = useOrganizationUsers({ + enable: isSchedulingEnabled, + organization: currentOrganizationPromise.result, + onError: onLoadError, + }); + + const organizationWebhooksPromise = useOrganizationWebhooks({ + enable: isSchedulingEnabled, + organization: currentOrganizationPromise.result, + onError: onLoadError, + }); + + const automationsPromise = useWorkspaceAutomations( + { + enable: isSchedulingEnabled, + backend, + workspace, + onError: onLoadError, + }, + [reloadId], + ); + + const promisesStatus = resolveUseCancelablePromisesStatus( + [ + currentOrganizationPromise, + organizationUsersPromise, + organizationWebhooksPromise, + automationsPromise, + ], + { strategy: "parallel" }, + ); + const loadError = [ + currentOrganizationPromise.error, + organizationUsersPromise.error, + organizationWebhooksPromise.error, + automationsPromise.error, + ].filter(Boolean)[0]; + + const isLoading = promisesStatus === "loading" || promisesStatus === "pending"; + const webhooks = organizationWebhooksPromise.result ?? []; + const users = organizationUsersPromise.result ?? []; + const automations = automationsPromise.result ?? []; + + return { + isLoading, + loadError, + webhooks, + automations, + users, + }; +}; diff --git a/libs/sdk-ui-dashboard/src/model/react/useOrganizationUsers.ts b/libs/sdk-ui-dashboard/src/model/react/useOrganizationUsers.ts new file mode 100644 index 00000000000..060b77816bf --- /dev/null +++ b/libs/sdk-ui-dashboard/src/model/react/useOrganizationUsers.ts @@ -0,0 +1,64 @@ +// (C) 2020-2024 GoodData Corporation +import { IOrganization } from "@gooddata/sdk-backend-spi"; +import { IOrganizationUser } from "@gooddata/sdk-model"; +import { + GoodDataSdkError, + useCancelablePromise, + UseCancelablePromiseCallbacks, + UseCancelablePromiseState, +} from "@gooddata/sdk-ui"; + +interface IUseOrganizationUsersConfig + extends UseCancelablePromiseCallbacks { + /** + * Option to filter users by the provided string. + */ + search?: string; + + /** + * Organization to work with. + */ + organization?: IOrganization; + + /** + * Enable or disable the hook. + */ + enable?: boolean; +} + +/** + * Hook allowing to download organization users + * @param config - configuration of the hook + * @internal + */ +export function useOrganizationUsers({ + search, + organization, + enable, + onCancel, + onError, + onLoading, + onPending, + onSuccess, +}: IUseOrganizationUsersConfig): UseCancelablePromiseState { + return useCancelablePromise( + { + promise: + enable && organization + ? () => { + return organization + .users() + .getUsersQuery() + .query() + .then((result) => result.all()); + } + : null, + onCancel, + onError, + onLoading, + onPending, + onSuccess, + }, + [organization, search], + ); +} diff --git a/libs/sdk-ui-dashboard/src/model/react/useOrganizationWebhooks.ts b/libs/sdk-ui-dashboard/src/model/react/useOrganizationWebhooks.ts new file mode 100644 index 00000000000..7e3154bb08f --- /dev/null +++ b/libs/sdk-ui-dashboard/src/model/react/useOrganizationWebhooks.ts @@ -0,0 +1,60 @@ +// (C) 2020-2024 GoodData Corporation +import { IOrganization } from "@gooddata/sdk-backend-spi"; +import { IWebhookMetadataObject } from "@gooddata/sdk-model"; +import { + GoodDataSdkError, + useCancelablePromise, + UseCancelablePromiseCallbacks, + UseCancelablePromiseState, +} from "@gooddata/sdk-ui"; + +interface IUseOrganizationWebhooksConfig + extends UseCancelablePromiseCallbacks { + /** + * Option to filter users by the provided string. + */ + search?: string; + + /** + * Organization to work with. + */ + organization?: IOrganization; + + /** + * Enable or disable the hook. + */ + enable?: boolean; +} + +/** + * Hook allowing to download workspace users + * @param config - configuration of the hook + * @internal + */ +export function useOrganizationWebhooks({ + enable, + search, + organization, + onCancel, + onError, + onLoading, + onPending, + onSuccess, +}: IUseOrganizationWebhooksConfig): UseCancelablePromiseState { + return useCancelablePromise( + { + promise: + enable && organization + ? () => { + return organization.notificationChannels().getWebhooks(); + } + : null, + onCancel, + onError, + onLoading, + onPending, + onSuccess, + }, + [organization, search], + ); +} diff --git a/libs/sdk-ui-dashboard/src/model/react/useWorkspaceAutomations.ts b/libs/sdk-ui-dashboard/src/model/react/useWorkspaceAutomations.ts new file mode 100644 index 00000000000..9ca75ae90c4 --- /dev/null +++ b/libs/sdk-ui-dashboard/src/model/react/useWorkspaceAutomations.ts @@ -0,0 +1,65 @@ +// (C) 2020-2024 GoodData Corporation +import { IAnalyticalBackend } from "@gooddata/sdk-backend-spi"; +import { IAutomationMetadataObject } from "@gooddata/sdk-model"; +import { + GoodDataSdkError, + useBackendStrict, + useCancelablePromise, + UseCancelablePromiseCallbacks, + UseCancelablePromiseState, + useWorkspaceStrict, +} from "@gooddata/sdk-ui"; + +interface IUseWorkspaceAutomationsConfig + extends UseCancelablePromiseCallbacks { + /** + * Enable or disable the hook. + */ + enable?: boolean; + + /** + * Organization to work with. + */ + backend?: IAnalyticalBackend; + + /** + * Workspace to work with. + */ + workspace?: string; +} + +/** + * Hook allowing to download organization users + * @param config - configuration of the hook + * @internal + */ +export function useWorkspaceAutomations( + { + enable, + workspace, + backend, + onCancel, + onError, + onLoading, + onPending, + onSuccess, + }: IUseWorkspaceAutomationsConfig, + dependencies?: any[], +): UseCancelablePromiseState { + const effectiveWorkspace = useWorkspaceStrict(workspace, "useWorkspaceAutomations"); + const effectiveBackend = useBackendStrict(backend, "useWorkspaceAutomations"); + + return useCancelablePromise( + { + promise: enable + ? () => effectiveBackend.workspace(effectiveWorkspace).automations().getAutomations() + : undefined, + onCancel, + onError, + onLoading, + onPending, + onSuccess, + }, + [effectiveWorkspace, effectiveBackend, ...(dependencies ?? [])], + ); +} diff --git a/libs/sdk-ui-dashboard/src/model/store/config/configSelectors.ts b/libs/sdk-ui-dashboard/src/model/store/config/configSelectors.ts index c8ddad39117..74e6d5375fc 100644 --- a/libs/sdk-ui-dashboard/src/model/store/config/configSelectors.ts +++ b/libs/sdk-ui-dashboard/src/model/store/config/configSelectors.ts @@ -394,6 +394,12 @@ export const selectEnableInsightExportScheduling: DashboardSelector = c }, ); +/** + * @alpha + */ +export const selectEnableScheduling: DashboardSelector = createSelector(selectConfig, (state) => { + return state.settings?.enableScheduling ?? false; +}); /** * Returns whether analytical dashboard permissions are enabled * diff --git a/libs/sdk-ui-dashboard/src/model/store/index.ts b/libs/sdk-ui-dashboard/src/model/store/index.ts index 5ac674a57a7..b1d7082a57b 100644 --- a/libs/sdk-ui-dashboard/src/model/store/index.ts +++ b/libs/sdk-ui-dashboard/src/model/store/index.ts @@ -82,6 +82,7 @@ export { selectEnableRichTextDescriptions, selectIsDisabledCrossFiltering, selectIsDisableUserFilterReset, + selectEnableScheduling, } from "./config/configSelectors.js"; export { EntitlementsState } from "./entitlements/entitlementsState.js"; export { selectEntitlementExportPdf } from "./entitlements/entitlementsSelectors.js"; diff --git a/libs/sdk-ui-dashboard/src/presentation/dashboard/DashboardHeader/ScheduledEmailDialogProvider.tsx b/libs/sdk-ui-dashboard/src/presentation/dashboard/DashboardHeader/ScheduledEmailDialogProvider.tsx index 944bf1be8aa..e3e0cf6b9f6 100644 --- a/libs/sdk-ui-dashboard/src/presentation/dashboard/DashboardHeader/ScheduledEmailDialogProvider.tsx +++ b/libs/sdk-ui-dashboard/src/presentation/dashboard/DashboardHeader/ScheduledEmailDialogProvider.tsx @@ -1,12 +1,18 @@ -// (C) 2022 GoodData Corporation +// (C) 2022-2024 GoodData Corporation -import React from "react"; +import React, { useState } from "react"; import { ScheduledEmailDialog, ScheduledEmailManagementDialog } from "../../scheduledEmail/index.js"; -import { useDashboardScheduledEmails } from "../../../model/index.js"; +import { useDashboardScheduledEmails, useDashboardScheduledEmailsData } from "../../../model/index.js"; export const ScheduledEmailDialogProvider = () => { + const [reloadId, setReloadId] = useState(0); + + const reloadAutomations = () => { + setReloadId((prev) => prev + 1); + }; + const { isScheduleEmailingDialogOpen, isScheduleEmailingManagementDialogOpen, @@ -16,14 +22,18 @@ export const ScheduledEmailDialogProvider = () => { onScheduleEmailingManagementAdd, onScheduleEmailingManagementEdit, scheduledEmailToEdit, - users, onScheduleEmailingSaveError, onScheduleEmailingSaveSuccess, onScheduleEmailingManagementClose, - onScheduleEmailingManagementLoadingError, onScheduleEmailingManagementDeleteSuccess, onScheduleEmailingManagementDeleteError, - } = useDashboardScheduledEmails(); + onScheduleEmailingManagementLoadingError, + } = useDashboardScheduledEmails({ onReload: reloadAutomations }); + + const { webhooks, users, automations, isLoading, loadError } = useDashboardScheduledEmailsData({ + reloadId, + onLoadError: onScheduleEmailingManagementLoadingError, + }); return ( <> @@ -34,8 +44,11 @@ export const ScheduledEmailDialogProvider = () => { onEdit={onScheduleEmailingManagementEdit} onClose={onScheduleEmailingManagementClose} onDeleteSuccess={onScheduleEmailingManagementDeleteSuccess} - onLoadError={onScheduleEmailingManagementLoadingError} onDeleteError={onScheduleEmailingManagementDeleteError} + isLoadingScheduleData={isLoading} + automations={automations} + webhooks={webhooks} + scheduleDataError={loadError} /> ) : null} {isScheduleEmailingDialogOpen ? ( @@ -48,6 +61,8 @@ export const ScheduledEmailDialogProvider = () => { onSaveError={onScheduleEmailingSaveError} onSaveSuccess={onScheduleEmailingSaveSuccess} users={users} + webhooks={webhooks} + automations={automations} /> ) : null} diff --git a/libs/sdk-ui-dashboard/src/presentation/localization/bundles/en-US.json b/libs/sdk-ui-dashboard/src/presentation/localization/bundles/en-US.json index dfb7935f209..135e806b528 100644 --- a/libs/sdk-ui-dashboard/src/presentation/localization/bundles/en-US.json +++ b/libs/sdk-ui-dashboard/src/presentation/localization/bundles/en-US.json @@ -454,12 +454,12 @@ "limit": 0 }, "dialogs.schedule.email.attachment.label": { - "value": "Attachment:", + "value": "Attachment", "comment": "Attachment of the scheduled email.", "limit": 0 }, "dialogs.schedule.email.attachments.label": { - "value": "Attachments:", + "value": "Attachments", "comment": "Attachments of the scheduled email.", "limit": 0 }, @@ -505,8 +505,13 @@ "comment": "Translate as imperative. Schedule exporting whole dashboard and visualizations", "limit": 0 }, + "dialogs.schedule.email.title.placeholder": { + "value": "Untitled schedule", + "comment": "Placeholder for schedule title input", + "limit": 0 + }, "dialogs.schedule.email.message.label": { - "value": "Message:", + "value": "Message", "comment": "Message of the scheduled email.", "limit": 0 }, @@ -516,7 +521,7 @@ "limit": 0 }, "dialogs.schedule.email.repeats.label": { - "value": "Repeats:", + "value": "Repeats", "comment": "Count of repeats of the scheduled email.", "limit": 0 }, @@ -555,6 +560,11 @@ "comment": "Repeats the schedule email using custom rules defined by the user.", "limit": 0 }, + "dialogs.schedule.email.repeats.types.cron": { + "value": "Cron expression", + "comment": "Repeats the automation schedule using cron expression.", + "limit": 0 + }, "dialogs.schedule.email.repeats.types.daily": { "value": "Daily", "comment": "Repeats the schedule email daily.", @@ -571,28 +581,38 @@ "limit": 0 }, "dialogs.schedule.email.subject.label": { - "value": "Subject:", + "value": "Subject", "comment": "Subject of the schedule email.", "limit": 0 }, "dialogs.schedule.email.time.label": { - "value": "First occurrence:", + "value": "Starts on", "comment": "Translate as imperative. Date time of the schedule email.", "limit": 0 }, "dialogs.schedule.email.to.label": { - "value": "To:", + "value": "To", "comment": "Emails of the schedule email.", "limit": 0 }, + "dialogs.schedule.email.destination": { + "value": "Destination", + "comment": "Way how to send automated schedule", + "limit": 0 + }, "dialogs.schedule.email.message.placeholder": { "value": "Hello,\n\nYour scheduled email is ready. You can download the dashboard in attachments.", "comment": "Don't translate '\n\n' which adds 2 new lines to the text.", "limit": 0 }, - "dialogs.schedule.email.submit": { - "value": "Schedule", - "comment": "Translate as imperative. Submit the schedule config form.", + "dialogs.schedule.email.create": { + "value": "Create", + "comment": "Translate as imperative. Create the schedule by submitting form.", + "limit": 0 + }, + "dialogs.schedule.email.footer.title": { + "value": "How to schedule export?", + "comment": "Title of a link to documentation", "limit": 0 }, "dialogs.schedule.email.save": { @@ -646,18 +666,23 @@ "comment": "Do not translate html tags , ", "limit": 0 }, - "dialogs.schedule.management.noSchedules": { - "value": "The dashboard contains no scheduled exports.{br}Add one now.", + "dialogs.schedule.management.list.title": { + "value": "Your scheduled exports", "comment": "", "limit": 0 }, - "dialogs.schedule.management.noSchedules.byUser": { - "value": "You have not scheduled any exports yet.{br}Add one now.", + "dialogs.schedule.management.create": { + "value": "Create", "comment": "", "limit": 0 }, - "dialogs.schedule.management.addSchedule": { - "value": "Add schedule", + "dialogs.schedule.management.create.tooManyMessage": { + "value": "Too many schedules. You can create up to 10 schedules.", + "comment": "", + "limit": 0 + }, + "dialogs.schedule.management.noSchedules": { + "value": "There are no scheduled exports.{br}Create new.", "comment": "", "limit": 0 }, @@ -708,16 +733,6 @@ "comment": "", "limit": 0 }, - "dialogs.schedule.management.tab.all": { - "value": "all", - "comment": "", - "limit": 0 - }, - "dialogs.schedule.management.tab.user": { - "value": "created by me", - "comment": "", - "limit": 0 - }, "options.menu.schedule.email.recipient.invalid": { "value": "This is not a valid email address.", "comment": "Translate as imperative. Error message for recipient email address", diff --git a/libs/sdk-ui-dashboard/src/presentation/scheduledEmail/DefaultScheduledEmailDialog/DefaultScheduledEmailDialog.tsx b/libs/sdk-ui-dashboard/src/presentation/scheduledEmail/DefaultScheduledEmailDialog/DefaultScheduledEmailDialog.tsx new file mode 100644 index 00000000000..53c1ac1e8f3 --- /dev/null +++ b/libs/sdk-ui-dashboard/src/presentation/scheduledEmail/DefaultScheduledEmailDialog/DefaultScheduledEmailDialog.tsx @@ -0,0 +1,242 @@ +// (C) 2019-2024 GoodData Corporation +import React, { useState } from "react"; +import noop from "lodash/noop.js"; +import { useIntl } from "react-intl"; +import { + ConfirmDialogBase, + Overlay, + ContentDivider, + Button, + EditableLabel, + Hyperlink, + RecurrenceForm, + normalizeTime, + Alignment, +} from "@gooddata/sdk-ui-kit"; +import { Textarea } from "./components/Textarea.js"; +import { Input } from "./components/Input.js"; +import { Attachments } from "./components/Attachments/Attachments.js"; +import { RecipientsSelect } from "./components/RecipientsSelect/RecipientsSelect.js"; +import { IntlWrapper } from "../../localization/index.js"; +import { DestinationSelect } from "./components/DestinationSelect/DestinationSelect.js"; +import { + selectLocale, + useDashboardSelector, + selectDateFormat, + selectWeekStart, + selectEnableScheduling, + selectDashboardTitle, + selectDashboardId, +} from "../../../model/index.js"; +import { IScheduledEmailDialogProps } from "../types.js"; +import { invariant } from "ts-invariant"; +import { getDefaultCronExpression, useEditScheduledEmail } from "./hooks/useEditScheduledEmail.js"; +import { useSaveScheduledEmailToBackend } from "./hooks/useSaveScheduledEmailToBackend.js"; +import isEqual from "lodash/isEqual.js"; +import { isExportDefinitionDashboardContent } from "@gooddata/sdk-model"; +import { getTimezoneByIdentifier, TIMEZONE_DEFAULT } from "./utils/timezone.js"; +import { DASHBOARD_TITLE_MAX_LENGTH } from "../../constants/index.js"; +import parseISO from "date-fns/parseISO/index.js"; + +const MAX_MESSAGE_LENGTH = 200; +const MAX_SUBJECT_LENGTH = 200; + +export function ScheduledMailDialogRenderer(props: IScheduledEmailDialogProps) { + const { onCancel, isVisible, editSchedule, users, webhooks } = props; + const intl = useIntl(); + const dashboardId = useDashboardSelector(selectDashboardId); + const locale = useDashboardSelector(selectLocale); + const dashboardTitle = useDashboardSelector(selectDashboardTitle); + const dateFormat = useDashboardSelector(selectDateFormat); + const weekStart = useDashboardSelector(selectWeekStart); + const enableKPIDashboardSchedule = useDashboardSelector(selectEnableScheduling); + + const { alignPoints, onAlign } = useScheduledEmailDialogAlignment(); + const { + automation, + originalAutomation, + onAttachmentsChange, + onDestinationChange, + onMessageChange, + onRecipientsChange, + onRecurrenceChange, + onSubjectChange, + onTitleChange, + } = useEditScheduledEmail(props); + const { handleSaveScheduledEmail, isSavingScheduledEmail } = useSaveScheduledEmailToBackend( + automation, + props, + ); + + const isDashboardExportSelected = + automation.exportDefinitions?.some((exportDefinition) => { + if (isExportDefinitionDashboardContent(exportDefinition.requestPayload.content)) { + return exportDefinition.requestPayload.content.dashboard === dashboardId; + } + + return false; + }) ?? false; + + const isSubmitDisabled = editSchedule && isEqual(originalAutomation, automation); + + const startDate = parseISO( + automation.schedule?.firstRun ?? normalizeTime(new Date(), undefined, 60).toISOString(), + ); + + // trigger the invariant only if the user tries to open the dialog + if (isVisible) { + invariant( + enableKPIDashboardSchedule, + "Feature flag enableKPIDashboardSchedule must be enabled to make ScheduledMailDialog work properly.", + ); + } + + if (!isVisible) { + return null; + } + + return ( + + ( +
+ +
+ )} + isSubmitDisabled={isSubmitDisabled || isSavingScheduledEmail} + submitOnEnterKey={false} + onCancel={onCancel} + onSubmit={handleSaveScheduledEmail} + headline={undefined} + headerLeftButtonRenderer={() => ( +
+
+ )} + > +
+ + + + + + + DASHBOARD_TITLE_MAX_LENGTH + ? dashboardTitle.substring(0, DASHBOARD_TITLE_MAX_LENGTH) + : dashboardTitle + } + value={automation.details?.subject ?? ""} + onChange={onSubjectChange} + /> +