Use "automation" instead of "workflow"
[MAILPOET-4793]
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
.mailpoet-automation-workflow-add-trigger {
|
.mailpoet-automation-automation-add-trigger {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px dashed #c3c4c7;
|
border: 1px dashed #c3c4c7;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
.mailpoet-automation-editor-workflow {
|
.mailpoet-automation-editor-automation {
|
||||||
background: #fbfbfb;
|
background: #fbfbfb;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-automation-editor-workflow-wrapper {
|
.mailpoet-automation-editor-automation-wrapper {
|
||||||
display: grid;
|
display: grid;
|
||||||
padding: 50px 20px;
|
padding: 50px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-automation-editor-workflow-end {
|
.mailpoet-automation-editor-automation-end {
|
||||||
background: #8c8f94;
|
background: #8c8f94;
|
||||||
border-radius: 999999px;
|
border-radius: 999999px;
|
||||||
fill: white;
|
fill: white;
|
@ -1,4 +1,4 @@
|
|||||||
.mailpoet-automation-editor-empty-workflow {
|
.mailpoet-automation-editor-empty-automation {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100%;
|
height: 100%;
|
@ -11,17 +11,17 @@
|
|||||||
|
|
||||||
@import './components-automation-editor/add-step-button';
|
@import './components-automation-editor/add-step-button';
|
||||||
@import './components-automation-editor/add-trigger';
|
@import './components-automation-editor/add-trigger';
|
||||||
|
@import './components-automation-editor/automation';
|
||||||
@import './components-automation-editor/block-icon';
|
@import './components-automation-editor/block-icon';
|
||||||
@import './components-automation-editor/chip';
|
@import './components-automation-editor/chip';
|
||||||
@import './components-automation-editor/dropdown';
|
@import './components-automation-editor/dropdown';
|
||||||
@import './components-automation-editor/empty-workflow';
|
@import './components-automation-editor/empty-automation';
|
||||||
@import './components-automation-editor/errors';
|
@import './components-automation-editor/errors';
|
||||||
@import './components-automation-editor/panel';
|
@import './components-automation-editor/panel';
|
||||||
@import './components-automation-editor/separator';
|
@import './components-automation-editor/separator';
|
||||||
@import './components-automation-editor/status';
|
@import './components-automation-editor/status';
|
||||||
@import './components-automation-editor/step';
|
@import './components-automation-editor/step';
|
||||||
@import './components-automation-editor/step-card';
|
@import './components-automation-editor/step-card';
|
||||||
@import './components-automation-editor/workflow';
|
|
||||||
@import './components-automation-editor/notices';
|
@import './components-automation-editor/notices';
|
||||||
@import './components-automation-editor/deactivate-modal';
|
@import './components-automation-editor/deactivate-modal';
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { api } from '../config';
|
import { api } from '../config';
|
||||||
|
|
||||||
const API_URL = `${api.root}/mailpoet/v1/automation`;
|
const API_URL = `${api.root}/mailpoet/v1`;
|
||||||
|
|
||||||
export const request = (
|
export const request = (
|
||||||
path: string,
|
path: string,
|
||||||
|
@ -3,7 +3,7 @@ import { api } from '../config';
|
|||||||
|
|
||||||
export * from './hooks';
|
export * from './hooks';
|
||||||
|
|
||||||
const apiUrl = `${api.root}/mailpoet/v1/automation/`;
|
const apiUrl = `${api.root}/mailpoet/v1/`;
|
||||||
|
|
||||||
export type ApiError = {
|
export type ApiError = {
|
||||||
code?: string;
|
code?: string;
|
||||||
|
@ -10,11 +10,11 @@ import { createStore, storeName } from './listing/store';
|
|||||||
import { AutomationListing, AutomationListingHeader } from './listing';
|
import { AutomationListing, AutomationListingHeader } from './listing';
|
||||||
import { registerApiErrorHandler } from './listing/api-error-handler';
|
import { registerApiErrorHandler } from './listing/api-error-handler';
|
||||||
import { Notices } from './listing/components/notices';
|
import { Notices } from './listing/components/notices';
|
||||||
import { WorkflowListingNotices } from './listing/workflow-listing-notices';
|
import { AutomationListingNotices } from './listing/automation-listing-notices';
|
||||||
import { BuildYourOwnSection, HeroSection, TemplatesSection } from './sections';
|
import { BuildYourOwnSection, HeroSection, TemplatesSection } from './sections';
|
||||||
import {
|
import {
|
||||||
CreateEmptyWorkflowButton,
|
CreateEmptyAutomationButton,
|
||||||
CreateWorkflowFromTemplateButton,
|
CreateAutomationFromTemplateButton,
|
||||||
} from './testing';
|
} from './testing';
|
||||||
import { MailPoet } from '../mailpoet';
|
import { MailPoet } from '../mailpoet';
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ const trackOpenEvent = () => {
|
|||||||
|
|
||||||
function Content(): JSX.Element {
|
function Content(): JSX.Element {
|
||||||
const [isBooting, setIsBooting] = useState(true);
|
const [isBooting, setIsBooting] = useState(true);
|
||||||
const count = useSelect((select) => select(storeName).getWorkflowCount());
|
const count = useSelect((select) => select(storeName).getAutomationCount());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isBooting || count === 0) {
|
if (!isBooting || count === 0) {
|
||||||
@ -63,7 +63,7 @@ function Content(): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Workflows(): JSX.Element {
|
function Automations(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBarWithBeamer />
|
<TopBarWithBeamer />
|
||||||
@ -80,7 +80,7 @@ function RecreateSchemaButton(): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<WorkflowListingNotices />
|
<AutomationListingNotices />
|
||||||
<button
|
<button
|
||||||
className="button button-link-delete"
|
className="button button-link-delete"
|
||||||
type="button"
|
type="button"
|
||||||
@ -127,20 +127,20 @@ function App(): JSX.Element {
|
|||||||
<SlotFillProvider>
|
<SlotFillProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<div>
|
<div>
|
||||||
<Workflows />
|
<Automations />
|
||||||
<div style={{ marginTop: 30, display: 'grid', gridGap: 8 }}>
|
<div style={{ marginTop: 30, display: 'grid', gridGap: 8 }}>
|
||||||
<CreateEmptyWorkflowButton />
|
<CreateEmptyAutomationButton />
|
||||||
<CreateWorkflowFromTemplateButton slug="simple-welcome-email">
|
<CreateAutomationFromTemplateButton slug="simple-welcome-email">
|
||||||
Create testing workflow from template (welcome email)
|
Create testing automation from template (welcome email)
|
||||||
</CreateWorkflowFromTemplateButton>
|
</CreateAutomationFromTemplateButton>
|
||||||
<CreateWorkflowFromTemplateButton slug="welcome-email-sequence">
|
<CreateAutomationFromTemplateButton slug="welcome-email-sequence">
|
||||||
Create testing workflow from template (welcome sequence, only
|
Create testing automation from template (welcome sequence, only
|
||||||
premium)
|
premium)
|
||||||
</CreateWorkflowFromTemplateButton>
|
</CreateAutomationFromTemplateButton>
|
||||||
<CreateWorkflowFromTemplateButton slug="advanced-welcome-email-sequence">
|
<CreateAutomationFromTemplateButton slug="advanced-welcome-email-sequence">
|
||||||
Create testing workflow from template (advanced welcome sequence,
|
Create testing automation from template (advanced welcome
|
||||||
only premium)
|
sequence, only premium)
|
||||||
</CreateWorkflowFromTemplateButton>
|
</CreateAutomationFromTemplateButton>
|
||||||
<RecreateSchemaButton />
|
<RecreateSchemaButton />
|
||||||
<DeleteSchemaButton />
|
<DeleteSchemaButton />
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,9 +4,9 @@ declare global {
|
|||||||
root: string;
|
root: string;
|
||||||
nonce: string;
|
nonce: string;
|
||||||
};
|
};
|
||||||
mailpoet_workflow_count: number;
|
mailpoet_automation_count: number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const api = window.mailpoet_automation_api;
|
export const api = window.mailpoet_automation_api;
|
||||||
export const workflowCount = window.mailpoet_workflow_count;
|
export const automationCount = window.mailpoet_automation_count;
|
||||||
|
@ -19,7 +19,7 @@ export const registerApiErrorHandler = (): void =>
|
|||||||
const status = errorObject.data?.status;
|
const status = errorObject.data?.status;
|
||||||
const code = errorObject.code;
|
const code = errorObject.code;
|
||||||
|
|
||||||
if (code === 'mailpoet_automation_workflow_not_valid') {
|
if (code === 'mailpoet_automation_automation_not_valid') {
|
||||||
dispatch(storeName).setErrors({ steps: errorObject.data.errors });
|
dispatch(storeName).setErrors({ steps: errorObject.data.errors });
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ import { storeName } from '../../store';
|
|||||||
|
|
||||||
export function TrashButton(): JSX.Element {
|
export function TrashButton(): JSX.Element {
|
||||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||||
const { workflow } = useSelect(
|
const { automation } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -33,7 +33,7 @@ export function TrashButton(): JSX.Element {
|
|||||||
>
|
>
|
||||||
{sprintf(
|
{sprintf(
|
||||||
__('You are about to delete the automation "%s".', 'mailpoet'),
|
__('You are about to delete the automation "%s".', 'mailpoet'),
|
||||||
workflow.name,
|
automation.name,
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
{__(' This will stop it for all subscribers immediately.', 'mailpoet')}
|
{__(' This will stop it for all subscribers immediately.', 'mailpoet')}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { __unstableCompositeItem as CompositeItem } from '@wordpress/components';
|
import { __unstableCompositeItem as CompositeItem } from '@wordpress/components';
|
||||||
import { Icon, plus } from '@wordpress/icons';
|
import { Icon, plus } from '@wordpress/icons';
|
||||||
import { WorkflowCompositeContext } from './context';
|
import { AutomationCompositeContext } from './context';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClick?: (element: HTMLButtonElement) => void;
|
onClick?: (element: HTMLButtonElement) => void;
|
||||||
@ -9,7 +9,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function AddStepButton({ onClick, previousStepId }: Props): JSX.Element {
|
export function AddStepButton({ onClick, previousStepId }: Props): JSX.Element {
|
||||||
const compositeState = useContext(WorkflowCompositeContext);
|
const compositeState = useContext(AutomationCompositeContext);
|
||||||
return (
|
return (
|
||||||
<CompositeItem
|
<CompositeItem
|
||||||
state={compositeState}
|
state={compositeState}
|
@ -3,7 +3,7 @@ import { __unstableCompositeItem as CompositeItem } from '@wordpress/components'
|
|||||||
import { Icon, plus } from '@wordpress/icons';
|
import { Icon, plus } from '@wordpress/icons';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { WorkflowCompositeContext } from './context';
|
import { AutomationCompositeContext } from './context';
|
||||||
import { Step } from './types';
|
import { Step } from './types';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
|
|
||||||
@ -12,14 +12,14 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function AddTrigger({ step }: Props): JSX.Element {
|
export function AddTrigger({ step }: Props): JSX.Element {
|
||||||
const compositeState = useContext(WorkflowCompositeContext);
|
const compositeState = useContext(AutomationCompositeContext);
|
||||||
const { setInserterPopover } = useDispatch(storeName);
|
const { setInserterPopover } = useDispatch(storeName);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CompositeItem
|
<CompositeItem
|
||||||
state={compositeState}
|
state={compositeState}
|
||||||
role="treeitem"
|
role="treeitem"
|
||||||
className="mailpoet-automation-workflow-add-trigger"
|
className="mailpoet-automation-automation-add-trigger"
|
||||||
data-previous-step-id={step.id}
|
data-previous-step-id={step.id}
|
||||||
focusable
|
focusable
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
@ -1,5 +1,5 @@
|
|||||||
import { __unstableUseCompositeState as useCompositeState } from '@wordpress/components';
|
import { __unstableUseCompositeState as useCompositeState } from '@wordpress/components';
|
||||||
import { createContext } from '@wordpress/element';
|
import { createContext } from '@wordpress/element';
|
||||||
|
|
||||||
export const WorkflowCompositeContext =
|
export const AutomationCompositeContext =
|
||||||
createContext<ReturnType<typeof useCompositeState>>(undefined);
|
createContext<ReturnType<typeof useCompositeState>>(undefined);
|
@ -0,0 +1,9 @@
|
|||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
export function EmptyAutomation(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="mailpoet-automation-editor-empty-automation">
|
||||||
|
{__('No automation data.', 'mailpoet')}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -7,8 +7,8 @@ import { useSelect } from '@wordpress/data';
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon, check } from '@wordpress/icons';
|
import { Icon, check } from '@wordpress/icons';
|
||||||
import { Hooks } from 'wp-js-hooks';
|
import { Hooks } from 'wp-js-hooks';
|
||||||
import { WorkflowCompositeContext } from './context';
|
import { AutomationCompositeContext } from './context';
|
||||||
import { EmptyWorkflow } from './empty-workflow';
|
import { EmptyAutomation } from './empty-automation';
|
||||||
import { Separator } from './separator';
|
import { Separator } from './separator';
|
||||||
import { Step } from './step';
|
import { Step } from './step';
|
||||||
import { Step as StepData } from './types';
|
import { Step as StepData } from './types';
|
||||||
@ -21,10 +21,10 @@ import {
|
|||||||
RenderStepType,
|
RenderStepType,
|
||||||
} from '../../../types/filters';
|
} from '../../../types/filters';
|
||||||
|
|
||||||
export function Workflow(): JSX.Element {
|
export function Automation(): JSX.Element {
|
||||||
const { workflowData, selectedStep } = useSelect(
|
const { automationData, selectedStep } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowData: select(storeName).getWorkflowData(),
|
automationData: select(storeName).getAutomationData(),
|
||||||
selectedStep: select(storeName).getSelectedStep(),
|
selectedStep: select(storeName).getSelectedStep(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
@ -36,9 +36,9 @@ export function Workflow(): JSX.Element {
|
|||||||
shift: true,
|
shift: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stepMap = workflowData?.steps ?? undefined;
|
const stepMap = automationData?.steps ?? undefined;
|
||||||
|
|
||||||
// serialize steps (for now, we support only one trigger and linear workflows)
|
// serialize steps (for now, we support only one trigger and linear automations)
|
||||||
const steps = useMemo(() => {
|
const steps = useMemo(() => {
|
||||||
const stepArray = [stepMap.root];
|
const stepArray = [stepMap.root];
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ export function Workflow(): JSX.Element {
|
|||||||
const renderStep = useMemo(
|
const renderStep = useMemo(
|
||||||
(): RenderStepType =>
|
(): RenderStepType =>
|
||||||
Hooks.applyFilters(
|
Hooks.applyFilters(
|
||||||
'mailpoet.automation.workflow.render_step',
|
'mailpoet.automation.automation.render_step',
|
||||||
(stepData: StepData) =>
|
(stepData: StepData) =>
|
||||||
stepData.type === 'root' ? (
|
stepData.type === 'root' ? (
|
||||||
<AddTrigger step={stepData} />
|
<AddTrigger step={stepData} />
|
||||||
@ -73,7 +73,7 @@ export function Workflow(): JSX.Element {
|
|||||||
const renderSeparator = useMemo(
|
const renderSeparator = useMemo(
|
||||||
(): RenderStepSeparatorType =>
|
(): RenderStepSeparatorType =>
|
||||||
Hooks.applyFilters(
|
Hooks.applyFilters(
|
||||||
'mailpoet.automation.workflow.render_step_separator',
|
'mailpoet.automation.automation.render_step_separator',
|
||||||
(previousStepData: StepData) => (
|
(previousStepData: StepData) => (
|
||||||
<Separator previousStepId={previousStepData.id} />
|
<Separator previousStepId={previousStepData.id} />
|
||||||
),
|
),
|
||||||
@ -81,20 +81,20 @@ export function Workflow(): JSX.Element {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!workflowData) {
|
if (!automationData) {
|
||||||
return <EmptyWorkflow />;
|
return <EmptyAutomation />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WorkflowCompositeContext.Provider value={compositeState}>
|
<AutomationCompositeContext.Provider value={compositeState}>
|
||||||
<Composite
|
<Composite
|
||||||
state={compositeState}
|
state={compositeState}
|
||||||
role="tree"
|
role="tree"
|
||||||
aria-label={__('Automation', 'mailpoet')}
|
aria-label={__('Automation', 'mailpoet')}
|
||||||
aria-orientation="vertical"
|
aria-orientation="vertical"
|
||||||
className="mailpoet-automation-editor-workflow"
|
className="mailpoet-automation-editor-automation"
|
||||||
>
|
>
|
||||||
<div className="mailpoet-automation-editor-workflow-wrapper">
|
<div className="mailpoet-automation-editor-automation-wrapper">
|
||||||
<Statistics />
|
<Statistics />
|
||||||
{stepMap.root.next_steps.length === 0 ? (
|
{stepMap.root.next_steps.length === 0 ? (
|
||||||
<>
|
<>
|
||||||
@ -119,13 +119,13 @@ export function Workflow(): JSX.Element {
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
<Icon
|
<Icon
|
||||||
className="mailpoet-automation-editor-workflow-end"
|
className="mailpoet-automation-editor-automation-end"
|
||||||
icon={check}
|
icon={check}
|
||||||
/>
|
/>
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
<InserterPopover />
|
<InserterPopover />
|
||||||
</Composite>
|
</Composite>
|
||||||
</WorkflowCompositeContext.Provider>
|
</AutomationCompositeContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -4,9 +4,9 @@ import { storeName } from '../../store';
|
|||||||
import { Statistics as BaseStatistics } from '../../../components/statistics';
|
import { Statistics as BaseStatistics } from '../../../components/statistics';
|
||||||
|
|
||||||
export function Statistics(): JSX.Element {
|
export function Statistics(): JSX.Element {
|
||||||
const { workflow } = useSelect(
|
const { automation } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -19,19 +19,19 @@ export function Statistics(): JSX.Element {
|
|||||||
key: 'entered',
|
key: 'entered',
|
||||||
// translators: Total number of subscribers who entered an automation
|
// translators: Total number of subscribers who entered an automation
|
||||||
label: _x('Total Entered', 'automation stats', 'mailpoet'),
|
label: _x('Total Entered', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.entered,
|
value: automation.stats.totals.entered,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'processing',
|
key: 'processing',
|
||||||
// translators: Total number of subscribers who are being processed in an automation
|
// translators: Total number of subscribers who are being processed in an automation
|
||||||
label: _x('Total Processing', 'automation stats', 'mailpoet'),
|
label: _x('Total Processing', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.in_progress,
|
value: automation.stats.totals.in_progress,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'exited',
|
key: 'exited',
|
||||||
// translators: Total number of subscribers who exited an automation, no matter the result
|
// translators: Total number of subscribers who exited an automation, no matter the result
|
||||||
label: _x('Total Exited', 'automation stats', 'mailpoet'),
|
label: _x('Total Exited', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.exited,
|
value: automation.stats.totals.exited,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
@ -23,7 +23,7 @@ export function StepMoreMenu({ step }: Props): JSX.Element {
|
|||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
const moreControls: StepMoreControlsType = Hooks.applyFilters(
|
const moreControls: StepMoreControlsType = Hooks.applyFilters(
|
||||||
'mailpoet.automation.workflow.step.more-controls',
|
'mailpoet.automation.automation.step.more-controls',
|
||||||
{
|
{
|
||||||
delete: {
|
delete: {
|
||||||
key: 'delete',
|
key: 'delete',
|
@ -4,7 +4,7 @@ import { __unstableCompositeItem as CompositeItem } from '@wordpress/components'
|
|||||||
import { useDispatch, useRegistry, useSelect } from '@wordpress/data';
|
import { useDispatch, useRegistry, useSelect } from '@wordpress/data';
|
||||||
import { blockMeta } from '@wordpress/icons';
|
import { blockMeta } from '@wordpress/icons';
|
||||||
import { __, _x } from '@wordpress/i18n';
|
import { __, _x } from '@wordpress/i18n';
|
||||||
import { WorkflowCompositeContext } from './context';
|
import { AutomationCompositeContext } from './context';
|
||||||
import { StepMoreMenu } from './step-more-menu';
|
import { StepMoreMenu } from './step-more-menu';
|
||||||
import { Step as StepData } from './types';
|
import { Step as StepData } from './types';
|
||||||
import { Chip } from '../chip';
|
import { Chip } from '../chip';
|
||||||
@ -48,7 +48,7 @@ export function Step({ step, isSelected }: Props): JSX.Element {
|
|||||||
[step],
|
[step],
|
||||||
);
|
);
|
||||||
const { openSidebar, selectStep } = useDispatch(storeName);
|
const { openSidebar, selectStep } = useDispatch(storeName);
|
||||||
const compositeState = useContext(WorkflowCompositeContext);
|
const compositeState = useContext(AutomationCompositeContext);
|
||||||
const { batch } = useRegistry();
|
const { batch } = useRegistry();
|
||||||
|
|
||||||
const compositeItemId = `step-${step.id}`;
|
const compositeItemId = `step-${step.id}`;
|
@ -1,4 +1,4 @@
|
|||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
|
|
||||||
export type NextStep = {
|
export type NextStep = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -12,10 +12,10 @@ export type Step = {
|
|||||||
next_steps: NextStep[];
|
next_steps: NextStep[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Workflow = {
|
export type Automation = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
status: WorkflowStatus;
|
status: AutomationStatus;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
activated_at: string;
|
activated_at: string;
|
@ -10,7 +10,7 @@ import { useRef } from '@wordpress/element';
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { chevronDown } from '@wordpress/icons';
|
import { chevronDown } from '@wordpress/icons';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
|
|
||||||
// See: https://github.com/WordPress/gutenberg/blob/eff0cab2b3181c004dbd15398e570ecec28a3726/packages/edit-site/src/components/header/document-actions/index.js
|
// See: https://github.com/WordPress/gutenberg/blob/eff0cab2b3181c004dbd15398e570ecec28a3726/packages/edit-site/src/components/header/document-actions/index.js
|
||||||
|
|
||||||
@ -22,10 +22,10 @@ const Dropdown: ComponentType<
|
|||||||
> = WpDropdown;
|
> = WpDropdown;
|
||||||
|
|
||||||
export function DocumentActions({ children }): JSX.Element {
|
export function DocumentActions({ children }): JSX.Element {
|
||||||
const { workflowName, workflowStatus, showIconLabels } = useSelect(
|
const { automationName, automationStatus, showIconLabels } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowName: select(storeName).getWorkflowData().name,
|
automationName: select(storeName).getAutomationData().name,
|
||||||
workflowStatus: select(storeName).getWorkflowData().status,
|
automationStatus: select(storeName).getAutomationData().status,
|
||||||
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
@ -36,9 +36,9 @@ export function DocumentActions({ children }): JSX.Element {
|
|||||||
const titleRef = useRef();
|
const titleRef = useRef();
|
||||||
|
|
||||||
let chipClass = 'mailpoet-automation-editor-chip-gray';
|
let chipClass = 'mailpoet-automation-editor-chip-gray';
|
||||||
if (workflowStatus === WorkflowStatus.ACTIVE) {
|
if (automationStatus === AutomationStatus.ACTIVE) {
|
||||||
chipClass = 'mailpoet-automation-editor-chip-success';
|
chipClass = 'mailpoet-automation-editor-chip-success';
|
||||||
} else if (workflowStatus === WorkflowStatus.DEACTIVATING) {
|
} else if (automationStatus === AutomationStatus.DEACTIVATING) {
|
||||||
chipClass = 'mailpoet-automation-editor-chip-danger';
|
chipClass = 'mailpoet-automation-editor-chip-danger';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,18 +66,18 @@ export function DocumentActions({ children }): JSX.Element {
|
|||||||
<VisuallyHidden as="span">
|
<VisuallyHidden as="span">
|
||||||
{__('Editing automation:', 'mailpoet')}
|
{__('Editing automation:', 'mailpoet')}
|
||||||
</VisuallyHidden>
|
</VisuallyHidden>
|
||||||
{workflowName}
|
{automationName}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
size="body"
|
size="body"
|
||||||
className={`edit-site-document-actions__secondary-item ${chipClass}`}
|
className={`edit-site-document-actions__secondary-item ${chipClass}`}
|
||||||
>
|
>
|
||||||
{workflowStatus === WorkflowStatus.ACTIVE &&
|
{automationStatus === AutomationStatus.ACTIVE &&
|
||||||
__('Active', 'mailpoet')}
|
__('Active', 'mailpoet')}
|
||||||
{workflowStatus === WorkflowStatus.DEACTIVATING &&
|
{automationStatus === AutomationStatus.DEACTIVATING &&
|
||||||
__('Deactivating', 'mailpoet')}
|
__('Deactivating', 'mailpoet')}
|
||||||
{workflowStatus === WorkflowStatus.DRAFT &&
|
{automationStatus === AutomationStatus.DRAFT &&
|
||||||
__('Draft', 'mailpoet')}
|
__('Draft', 'mailpoet')}
|
||||||
</Text>
|
</Text>
|
||||||
</a>
|
</a>
|
||||||
|
@ -35,17 +35,17 @@ type StepErrorProps = {
|
|||||||
function StepError({ stepId }: StepErrorProps): JSX.Element {
|
function StepError({ stepId }: StepErrorProps): JSX.Element {
|
||||||
const compositeState = useContext(ErrorsCompositeContext);
|
const compositeState = useContext(ErrorsCompositeContext);
|
||||||
|
|
||||||
const { steps, workflowData } = useSelect(
|
const { steps, automationData } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
steps: select(storeName).getSteps(),
|
steps: select(storeName).getSteps(),
|
||||||
workflowData: select(storeName).getWorkflowData(),
|
automationData: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { openSidebar, selectStep } = useDispatch(storeName);
|
const { openSidebar, selectStep } = useDispatch(storeName);
|
||||||
|
|
||||||
const stepData = workflowData.steps[stepId];
|
const stepData = automationData.steps[stepId];
|
||||||
const step = steps.find(({ key }) => key === stepData.key);
|
const step = steps.find(({ key }) => key === stepData.key);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -78,10 +78,10 @@ export function Errors(): JSX.Element | null {
|
|||||||
shift: true,
|
shift: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { errors, workflowData } = useSelect(
|
const { errors, automationData } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
errors: select(storeName).getErrors(),
|
errors: select(storeName).getErrors(),
|
||||||
workflowData: select(storeName).getWorkflowData(),
|
automationData: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -93,18 +93,18 @@ export function Errors(): JSX.Element | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const visited = new Map<string, StepErrorType | undefined>();
|
const visited = new Map<string, StepErrorType | undefined>();
|
||||||
const ids = workflowData.steps.root.next_steps.map(({ id }) => id);
|
const ids = automationData.steps.root.next_steps.map(({ id }) => id);
|
||||||
while (ids.length > 0) {
|
while (ids.length > 0) {
|
||||||
const id = ids.shift();
|
const id = ids.shift();
|
||||||
if (!visited.has(id)) {
|
if (!visited.has(id)) {
|
||||||
visited.set(id, errors.steps[id]);
|
visited.set(id, errors.steps[id]);
|
||||||
workflowData.steps[id]?.next_steps?.forEach((step) =>
|
automationData.steps[id]?.next_steps?.forEach((step) =>
|
||||||
ids.push(step.id),
|
ids.push(step.id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [...visited.values()].filter((error) => !!error);
|
return [...visited.values()].filter((error) => !!error);
|
||||||
}, [errors, workflowData]);
|
}, [errors, automationData]);
|
||||||
|
|
||||||
// automatically open the popover when errors appear
|
// automatically open the popover when errors appear
|
||||||
const hasErrors = stepErrors.length > 0;
|
const hasErrors = stepErrors.length > 0;
|
||||||
|
@ -13,7 +13,7 @@ import { Errors } from './errors';
|
|||||||
import { InserterToggle } from './inserter_toggle';
|
import { InserterToggle } from './inserter_toggle';
|
||||||
import { MoreMenu } from './more_menu';
|
import { MoreMenu } from './more_menu';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
import {
|
import {
|
||||||
DeactivateImmediatelyModal,
|
DeactivateImmediatelyModal,
|
||||||
DeactivateModal,
|
DeactivateModal,
|
||||||
@ -28,8 +28,8 @@ function ActivateButton({ label }): JSX.Element {
|
|||||||
(select) => ({
|
(select) => ({
|
||||||
errors: select(storeName).getErrors(),
|
errors: select(storeName).getErrors(),
|
||||||
isDeactivating:
|
isDeactivating:
|
||||||
select(storeName).getWorkflowData().status ===
|
select(storeName).getAutomationData().status ===
|
||||||
WorkflowStatus.DEACTIVATING,
|
AutomationStatus.DEACTIVATING,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -54,7 +54,7 @@ function ActivateButton({ label }): JSX.Element {
|
|||||||
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
||||||
delay={0}
|
delay={0}
|
||||||
text={__(
|
text={__(
|
||||||
'Editing an active workflow is temporarily unavailable. We are working on introducing this functionality.',
|
'Editing an active automation is temporarily unavailable. We are working on introducing this functionality.',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -69,14 +69,14 @@ function ActivateButton({ label }): JSX.Element {
|
|||||||
function UpdateButton(): JSX.Element {
|
function UpdateButton(): JSX.Element {
|
||||||
const { save } = useDispatch(storeName);
|
const { save } = useDispatch(storeName);
|
||||||
|
|
||||||
const { workflow } = useSelect(
|
const { automation } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (workflow.stats.totals.in_progress === 0) {
|
if (automation.stats.totals.in_progress === 0) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@ -94,7 +94,7 @@ function UpdateButton(): JSX.Element {
|
|||||||
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
||||||
delay={0}
|
delay={0}
|
||||||
text={__(
|
text={__(
|
||||||
'Editing an active workflow is temporarily unavailable. We are working on introducing this functionality.',
|
'Editing an active automation is temporarily unavailable. We are working on introducing this functionality.',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -126,7 +126,7 @@ function DeactivateButton(): JSX.Element {
|
|||||||
const { hasUsersInProgress } = useSelect(
|
const { hasUsersInProgress } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
hasUsersInProgress:
|
hasUsersInProgress:
|
||||||
select(storeName).getWorkflowData().stats.totals.in_progress > 0,
|
select(storeName).getAutomationData().stats.totals.in_progress > 0,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -166,7 +166,7 @@ function DeactivateNowButton(): JSX.Element {
|
|||||||
const { hasUsersInProgress } = useSelect(
|
const { hasUsersInProgress } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
hasUsersInProgress:
|
hasUsersInProgress:
|
||||||
select(storeName).getWorkflowData().stats.totals.in_progress > 0,
|
select(storeName).getAutomationData().stats.totals.in_progress > 0,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -205,11 +205,11 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function Header({ showInserterToggle }: Props): JSX.Element {
|
export function Header({ showInserterToggle }: Props): JSX.Element {
|
||||||
const { setWorkflowName } = useDispatch(storeName);
|
const { setAutomationName } = useDispatch(storeName);
|
||||||
const { workflowName, workflowStatus } = useSelect(
|
const { automationName, automationStatus } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowName: select(storeName).getWorkflowData().name,
|
automationName: select(storeName).getAutomationData().name,
|
||||||
workflowStatus: select(storeName).getWorkflowData().status,
|
automationStatus: select(storeName).getAutomationData().status,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -234,8 +234,8 @@ export function Header({ showInserterToggle }: Props): JSX.Element {
|
|||||||
{__('Automation name', 'mailpoet')}
|
{__('Automation name', 'mailpoet')}
|
||||||
</div>
|
</div>
|
||||||
<TextControl
|
<TextControl
|
||||||
value={workflowName}
|
value={automationName}
|
||||||
onChange={(newName) => setWorkflowName(newName)}
|
onChange={(newName) => setAutomationName(newName)}
|
||||||
help={__(
|
help={__(
|
||||||
`Give the automation a name that indicates its purpose. E.g. "Abandoned cart recovery"`,
|
`Give the automation a name that indicates its purpose. E.g. "Abandoned cart recovery"`,
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
@ -249,19 +249,19 @@ export function Header({ showInserterToggle }: Props): JSX.Element {
|
|||||||
<div className="edit-site-header_end">
|
<div className="edit-site-header_end">
|
||||||
<div className="edit-site-header__actions">
|
<div className="edit-site-header__actions">
|
||||||
<Errors />
|
<Errors />
|
||||||
{workflowStatus === WorkflowStatus.DRAFT && (
|
{automationStatus === AutomationStatus.DRAFT && (
|
||||||
<>
|
<>
|
||||||
<SaveDraftButton />
|
<SaveDraftButton />
|
||||||
<ActivateButton label={__('Activate', 'mailpoet')} />
|
<ActivateButton label={__('Activate', 'mailpoet')} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{workflowStatus === WorkflowStatus.ACTIVE && (
|
{automationStatus === AutomationStatus.ACTIVE && (
|
||||||
<>
|
<>
|
||||||
<DeactivateButton />
|
<DeactivateButton />
|
||||||
<UpdateButton />
|
<UpdateButton />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{workflowStatus === WorkflowStatus.DEACTIVATING && (
|
{automationStatus === AutomationStatus.DEACTIVATING && (
|
||||||
<>
|
<>
|
||||||
<DeactivateNowButton />
|
<DeactivateNowButton />
|
||||||
<ActivateButton label={__('Update & Activate', 'mailpoet')} />
|
<ActivateButton label={__('Update & Activate', 'mailpoet')} />
|
||||||
|
@ -22,7 +22,7 @@ export function InserterPopover(): JSX.Element | null {
|
|||||||
|
|
||||||
const onInsert = useCallback((item: Item) => {
|
const onInsert = useCallback((item: Item) => {
|
||||||
const addStepCallback: AddStepCallbackType = Hooks.applyFilters(
|
const addStepCallback: AddStepCallbackType = Hooks.applyFilters(
|
||||||
'mailpoet.automation.workflow.add_step_callback',
|
'mailpoet.automation.automation.add_step_callback',
|
||||||
() => {
|
() => {
|
||||||
setShowModal(true);
|
setShowModal(true);
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
store as keyboardShortcutsStore,
|
store as keyboardShortcutsStore,
|
||||||
} from '@wordpress/keyboard-shortcuts';
|
} from '@wordpress/keyboard-shortcuts';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||||
|
|
||||||
// See:
|
// See:
|
||||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/keyboard-shortcuts/index.js
|
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/keyboard-shortcuts/index.js
|
||||||
@ -55,7 +55,7 @@ export function KeyboardShortcuts(): null {
|
|||||||
} else {
|
} else {
|
||||||
const sidebarToOpen = selectedStep()
|
const sidebarToOpen = selectedStep()
|
||||||
? stepSidebarKey
|
? stepSidebarKey
|
||||||
: workflowSidebarKey;
|
: automationSidebarKey;
|
||||||
openSidebar(sidebarToOpen);
|
openSidebar(sidebarToOpen);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@ import { Button, Modal } from '@wordpress/components';
|
|||||||
import { __, sprintf } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { dispatch, useSelect } from '@wordpress/data';
|
import { dispatch, useSelect } from '@wordpress/data';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
|
|
||||||
type DeactivateImmediatelyModalProps = {
|
type DeactivateImmediatelyModalProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -49,20 +49,20 @@ type DeactivateModalProps = {
|
|||||||
export function DeactivateModal({
|
export function DeactivateModal({
|
||||||
onClose,
|
onClose,
|
||||||
}: DeactivateModalProps): JSX.Element {
|
}: DeactivateModalProps): JSX.Element {
|
||||||
const { workflowName } = useSelect(
|
const { automationName } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowName: select(storeName).getWorkflowData().name,
|
automationName: select(storeName).getAutomationData().name,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const [selected, setSelected] = useState<
|
const [selected, setSelected] = useState<
|
||||||
WorkflowStatus.DRAFT | WorkflowStatus.DEACTIVATING
|
AutomationStatus.DRAFT | AutomationStatus.DEACTIVATING
|
||||||
>(WorkflowStatus.DEACTIVATING);
|
>(AutomationStatus.DEACTIVATING);
|
||||||
const [isBusy, setIsBusy] = useState<boolean>(false);
|
const [isBusy, setIsBusy] = useState<boolean>(false);
|
||||||
// translators: %s is the name of the automation.
|
// translators: %s is the name of the automation.
|
||||||
const title = sprintf(
|
const title = sprintf(
|
||||||
__('Deactivate the "%s" automation?', 'mailpoet'),
|
__('Deactivate the "%s" automation?', 'mailpoet'),
|
||||||
workflowName,
|
automationName,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -79,7 +79,7 @@ export function DeactivateModal({
|
|||||||
<li>
|
<li>
|
||||||
<label
|
<label
|
||||||
className={
|
className={
|
||||||
selected === WorkflowStatus.DEACTIVATING
|
selected === AutomationStatus.DEACTIVATING
|
||||||
? 'mailpoet-automation-option active'
|
? 'mailpoet-automation-option active'
|
||||||
: 'mailpoet-automation-option'
|
: 'mailpoet-automation-option'
|
||||||
}
|
}
|
||||||
@ -89,8 +89,8 @@ export function DeactivateModal({
|
|||||||
type="radio"
|
type="radio"
|
||||||
disabled={isBusy}
|
disabled={isBusy}
|
||||||
name="deactivation-method"
|
name="deactivation-method"
|
||||||
checked={selected === WorkflowStatus.DEACTIVATING}
|
checked={selected === AutomationStatus.DEACTIVATING}
|
||||||
onChange={() => setSelected(WorkflowStatus.DEACTIVATING)}
|
onChange={() => setSelected(AutomationStatus.DEACTIVATING)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
@ -107,7 +107,7 @@ export function DeactivateModal({
|
|||||||
<li>
|
<li>
|
||||||
<label
|
<label
|
||||||
className={
|
className={
|
||||||
selected === WorkflowStatus.DRAFT
|
selected === AutomationStatus.DRAFT
|
||||||
? 'mailpoet-automation-option active'
|
? 'mailpoet-automation-option active'
|
||||||
: 'mailpoet-automation-option'
|
: 'mailpoet-automation-option'
|
||||||
}
|
}
|
||||||
@ -117,8 +117,8 @@ export function DeactivateModal({
|
|||||||
type="radio"
|
type="radio"
|
||||||
disabled={isBusy}
|
disabled={isBusy}
|
||||||
name="deactivation-method"
|
name="deactivation-method"
|
||||||
checked={selected === WorkflowStatus.DRAFT}
|
checked={selected === AutomationStatus.DRAFT}
|
||||||
onChange={() => setSelected(WorkflowStatus.DRAFT)}
|
onChange={() => setSelected(AutomationStatus.DRAFT)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
@ -140,7 +140,7 @@ export function DeactivateModal({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsBusy(true);
|
setIsBusy(true);
|
||||||
dispatch(storeName).deactivate(
|
dispatch(storeName).deactivate(
|
||||||
selected !== WorkflowStatus.DEACTIVATING,
|
selected !== AutomationStatus.DEACTIVATING,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -4,7 +4,7 @@ import { Button, Spinner } from '@wordpress/components';
|
|||||||
import { closeSmall } from '@wordpress/icons';
|
import { closeSmall } from '@wordpress/icons';
|
||||||
import { __, sprintf } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
import { MailPoet } from '../../../../mailpoet';
|
import { MailPoet } from '../../../../mailpoet';
|
||||||
|
|
||||||
function PreStep({ onClose }): JSX.Element {
|
function PreStep({ onClose }): JSX.Element {
|
||||||
@ -58,9 +58,9 @@ function PreStep({ onClose }): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function PostStep({ onClose }): JSX.Element {
|
function PostStep({ onClose }): JSX.Element {
|
||||||
const { workflow } = useSelect(
|
const { automation } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -81,7 +81,7 @@ function PostStep({ onClose }): JSX.Element {
|
|||||||
|
|
||||||
<div className="mailpoet-automation-activate-panel__body">
|
<div className="mailpoet-automation-activate-panel__body">
|
||||||
<div className="mailpoet-automation-activate-panel__section">
|
<div className="mailpoet-automation-activate-panel__section">
|
||||||
{sprintf(__('"%s" is now live.', 'mailpoet'), workflow.name)}
|
{sprintf(__('"%s" is now live.', 'mailpoet'), automation.name)}
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<strong>{__('What’s next?', 'mailpoet')}</strong>
|
<strong>{__('What’s next?', 'mailpoet')}</strong>
|
||||||
@ -101,10 +101,10 @@ function PostStep({ onClose }): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ActivatePanel(): JSX.Element {
|
export function ActivatePanel(): JSX.Element {
|
||||||
const { workflow, errors } = useSelect(
|
const { automation, errors } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
errors: select(storeName).getErrors(),
|
errors: select(storeName).getErrors(),
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -120,7 +120,7 @@ export function ActivatePanel(): JSX.Element {
|
|||||||
if (errors) {
|
if (errors) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const isActive = workflow.status === WorkflowStatus.ACTIVE;
|
const isActive = automation.status === AutomationStatus.ACTIVE;
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-automation-activate-panel">
|
<div className="mailpoet-automation-activate-panel">
|
||||||
{isActive && <PostStep onClose={closeActivationPanel} />}
|
{isActive && <PostStep onClose={closeActivationPanel} />}
|
||||||
|
@ -4,10 +4,10 @@ import { __ } from '@wordpress/i18n';
|
|||||||
import { storeName } from '../../../store';
|
import { storeName } from '../../../store';
|
||||||
import { TrashButton } from '../../actions/trash-button';
|
import { TrashButton } from '../../actions/trash-button';
|
||||||
|
|
||||||
export function WorkflowSidebar(): JSX.Element {
|
export function AutomationSidebar(): JSX.Element {
|
||||||
const { workflowData } = useSelect(
|
const { automationData } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowData: select(storeName).getWorkflowData(),
|
automationData: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -22,30 +22,30 @@ export function WorkflowSidebar(): JSX.Element {
|
|||||||
<PanelBody title={__('Automation details', 'mailpoet')} initialOpen>
|
<PanelBody title={__('Automation details', 'mailpoet')} initialOpen>
|
||||||
<PanelRow>
|
<PanelRow>
|
||||||
<strong>Date added</strong>{' '}
|
<strong>Date added</strong>{' '}
|
||||||
{new Date(Date.parse(workflowData.created_at)).toLocaleDateString(
|
{new Date(Date.parse(automationData.created_at)).toLocaleDateString(
|
||||||
undefined,
|
undefined,
|
||||||
dateOptions,
|
dateOptions,
|
||||||
)}
|
)}
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow>
|
<PanelRow>
|
||||||
<strong>Activated</strong>{' '}
|
<strong>Activated</strong>{' '}
|
||||||
{workflowData.status === 'active' &&
|
{automationData.status === 'active' &&
|
||||||
new Date(Date.parse(workflowData.updated_at)).toLocaleDateString(
|
new Date(Date.parse(automationData.updated_at)).toLocaleDateString(
|
||||||
undefined,
|
undefined,
|
||||||
dateOptions,
|
dateOptions,
|
||||||
)}
|
)}
|
||||||
{workflowData.status !== 'active' &&
|
{automationData.status !== 'active' &&
|
||||||
workflowData.activated_at &&
|
automationData.activated_at &&
|
||||||
new Date(Date.parse(workflowData.activated_at)).toLocaleDateString(
|
new Date(Date.parse(automationData.activated_at)).toLocaleDateString(
|
||||||
undefined,
|
undefined,
|
||||||
dateOptions,
|
dateOptions,
|
||||||
)}
|
)}
|
||||||
{workflowData.status !== 'active' && !workflowData.activated_at && (
|
{automationData.status !== 'active' && !automationData.activated_at && (
|
||||||
<span className="mailpoet-deactive">Not activated yet.</span>
|
<span className="mailpoet-deactive">Not activated yet.</span>
|
||||||
)}
|
)}
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow>
|
<PanelRow>
|
||||||
<strong>Author</strong> {workflowData.author.name}
|
<strong>Author</strong> {automationData.author.name}
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow>
|
<PanelRow>
|
||||||
<TrashButton />
|
<TrashButton />
|
@ -1,7 +1,7 @@
|
|||||||
import { Button } from '@wordpress/components';
|
import { Button } from '@wordpress/components';
|
||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||||
|
|
||||||
// See:
|
// See:
|
||||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/sidebar/settings-header/index.js
|
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/sidebar/settings-header/index.js
|
||||||
@ -13,11 +13,11 @@ type Props = {
|
|||||||
|
|
||||||
export function Header({ sidebarKey }: Props): JSX.Element {
|
export function Header({ sidebarKey }: Props): JSX.Element {
|
||||||
const { openSidebar } = useDispatch(storeName);
|
const { openSidebar } = useDispatch(storeName);
|
||||||
const openWorkflowSettings = () => openSidebar(workflowSidebarKey);
|
const openAutomationSettings = () => openSidebar(automationSidebarKey);
|
||||||
const openStepSettings = () => openSidebar(stepSidebarKey);
|
const openStepSettings = () => openSidebar(stepSidebarKey);
|
||||||
|
|
||||||
const [workflowAriaLabel, workflowActiveClass] =
|
const [automationAriaLabel, automationActiveClass] =
|
||||||
sidebarKey === workflowSidebarKey
|
sidebarKey === automationSidebarKey
|
||||||
? [__('Automation (selected)', 'mailpoet'), 'is-active']
|
? [__('Automation (selected)', 'mailpoet'), 'is-active']
|
||||||
: [__('Automation', 'mailpoet'), ''];
|
: [__('Automation', 'mailpoet'), ''];
|
||||||
|
|
||||||
@ -30,9 +30,9 @@ export function Header({ sidebarKey }: Props): JSX.Element {
|
|||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<Button
|
<Button
|
||||||
onClick={openWorkflowSettings}
|
onClick={openAutomationSettings}
|
||||||
className={`edit-site-sidebar__panel-tab ${workflowActiveClass}`}
|
className={`edit-site-sidebar__panel-tab ${automationActiveClass}`}
|
||||||
aria-label={workflowAriaLabel}
|
aria-label={automationAriaLabel}
|
||||||
data-label={__('Automation', 'mailpoet')}
|
data-label={__('Automation', 'mailpoet')}
|
||||||
>
|
>
|
||||||
{__('Automation', 'mailpoet')}
|
{__('Automation', 'mailpoet')}
|
||||||
|
@ -10,8 +10,8 @@ import {
|
|||||||
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
|
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
|
||||||
import { Header } from './header';
|
import { Header } from './header';
|
||||||
import { StepSidebar } from './step';
|
import { StepSidebar } from './step';
|
||||||
import { WorkflowSidebar } from './workflow';
|
import { AutomationSidebar } from './automation';
|
||||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||||
|
|
||||||
// See:
|
// See:
|
||||||
// https://github.com/WordPress/gutenberg/blob/5caeae34b3fb303761e3b9432311b26f4e5ea3a6/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js
|
// https://github.com/WordPress/gutenberg/blob/5caeae34b3fb303761e3b9432311b26f4e5ea3a6/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js
|
||||||
@ -26,7 +26,7 @@ const sidebarActiveByDefault = Platform.select({
|
|||||||
type Props = ComponentProps<typeof ComplementaryArea>;
|
type Props = ComponentProps<typeof ComplementaryArea>;
|
||||||
|
|
||||||
export function Sidebar(props: Props): JSX.Element {
|
export function Sidebar(props: Props): JSX.Element {
|
||||||
const { keyboardShortcut, sidebarKey, showIconLabels, workflowName } =
|
const { keyboardShortcut, sidebarKey, showIconLabels, automationName } =
|
||||||
useSelect(
|
useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
keyboardShortcut: select(
|
keyboardShortcut: select(
|
||||||
@ -36,9 +36,9 @@ export function Sidebar(props: Props): JSX.Element {
|
|||||||
),
|
),
|
||||||
sidebarKey:
|
sidebarKey:
|
||||||
select(interfaceStore).getActiveComplementaryArea(storeName) ??
|
select(interfaceStore).getActiveComplementaryArea(storeName) ??
|
||||||
workflowSidebarKey,
|
automationSidebarKey,
|
||||||
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
||||||
workflowName: select(storeName).getWorkflowData().name,
|
automationName: select(storeName).getAutomationData().name,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -53,14 +53,14 @@ export function Sidebar(props: Props): JSX.Element {
|
|||||||
icon={cog}
|
icon={cog}
|
||||||
className="edit-site-sidebar mailpoet-automation-sidebar"
|
className="edit-site-sidebar mailpoet-automation-sidebar"
|
||||||
panelClassName="edit-site-sidebar"
|
panelClassName="edit-site-sidebar"
|
||||||
smallScreenTitle={workflowName || __('(no title)', 'mailpoet')}
|
smallScreenTitle={automationName || __('(no title)', 'mailpoet')}
|
||||||
scope={storeName}
|
scope={storeName}
|
||||||
toggleShortcut={keyboardShortcut}
|
toggleShortcut={keyboardShortcut}
|
||||||
isActiveByDefault={sidebarActiveByDefault}
|
isActiveByDefault={sidebarActiveByDefault}
|
||||||
showIconLabels={showIconLabels}
|
showIconLabels={showIconLabels}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{sidebarKey === workflowSidebarKey && <WorkflowSidebar />}
|
{sidebarKey === automationSidebarKey && <AutomationSidebar />}
|
||||||
{sidebarKey === stepSidebarKey && <StepSidebar />}
|
{sidebarKey === stepSidebarKey && <StepSidebar />}
|
||||||
</ComplementaryArea>
|
</ComplementaryArea>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
|
|
||||||
export function EmptyWorkflow(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className="mailpoet-automation-editor-empty-workflow">
|
|
||||||
{__('No automation data.', 'mailpoet')}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -23,17 +23,17 @@ import { InserterSidebar } from './components/inserter-sidebar';
|
|||||||
import { KeyboardShortcuts } from './components/keyboard-shortcuts';
|
import { KeyboardShortcuts } from './components/keyboard-shortcuts';
|
||||||
import { EditorNotices } from './components/notices';
|
import { EditorNotices } from './components/notices';
|
||||||
import { Sidebar } from './components/sidebar';
|
import { Sidebar } from './components/sidebar';
|
||||||
import { Workflow } from './components/workflow';
|
import { Automation } from './components/automation';
|
||||||
import { createStore, storeName } from './store';
|
import { createStore, storeName } from './store';
|
||||||
import { initializeApi } from '../api';
|
import { initializeApi } from '../api';
|
||||||
import { initialize as initializeCoreIntegration } from '../integrations/core';
|
import { initialize as initializeCoreIntegration } from '../integrations/core';
|
||||||
import { initialize as initializeMailPoetIntegration } from '../integrations/mailpoet';
|
import { initialize as initializeMailPoetIntegration } from '../integrations/mailpoet';
|
||||||
import { MailPoet } from '../../mailpoet';
|
import { MailPoet } from '../../mailpoet';
|
||||||
import { LISTING_NOTICE_PARAMETERS } from '../listing/workflow-listing-notices';
|
import { LISTING_NOTICE_PARAMETERS } from '../listing/automation-listing-notices';
|
||||||
import { registerApiErrorHandler } from './api-error-handler';
|
import { registerApiErrorHandler } from './api-error-handler';
|
||||||
import { ActivatePanel } from './components/panel/activate-panel';
|
import { ActivatePanel } from './components/panel/activate-panel';
|
||||||
import { registerTranslations } from '../i18n';
|
import { registerTranslations } from '../i18n';
|
||||||
import { WorkflowStatus } from '../listing/workflow';
|
import { AutomationStatus } from '../listing/automation';
|
||||||
|
|
||||||
// See:
|
// See:
|
||||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/layout/index.js
|
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/layout/index.js
|
||||||
@ -43,27 +43,27 @@ import { WorkflowStatus } from '../listing/workflow';
|
|||||||
const showInserterSidebar = false;
|
const showInserterSidebar = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show temporary message that active workflows cant be updated
|
* Show temporary message that active automations cant be updated
|
||||||
*
|
*
|
||||||
* see MAILPOET-4744
|
* see MAILPOET-4744
|
||||||
*/
|
*/
|
||||||
function updatingActiveWorkflowNotPossible() {
|
function updatingActiveAutomationNotPossible() {
|
||||||
const workflow = globalSelect(storeName).getWorkflowData();
|
const automation = globalSelect(storeName).getAutomationData();
|
||||||
if (
|
if (
|
||||||
![WorkflowStatus.ACTIVE, WorkflowStatus.DEACTIVATING].includes(
|
![AutomationStatus.ACTIVE, AutomationStatus.DEACTIVATING].includes(
|
||||||
workflow.status,
|
automation.status,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (workflow.stats.totals.in_progress === 0) {
|
if (automation.stats.totals.in_progress === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
||||||
void createNotice(
|
void createNotice(
|
||||||
'success',
|
'success',
|
||||||
__(
|
__(
|
||||||
'Editing an active workflow is temporarily unavailable. We are working on introducing this functionality.',
|
'Editing an active automation is temporarily unavailable. We are working on introducing this functionality.',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
@ -73,7 +73,7 @@ function updatingActiveWorkflowNotPossible() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onUnload(event) {
|
function onUnload(event) {
|
||||||
if (!globalSelect(storeName).getWorkflowSaved()) {
|
if (!globalSelect(storeName).getAutomationSaved()) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
event.returnValue = __(
|
event.returnValue = __(
|
||||||
'There are unsaved changes that will be lost. Do you want to continue?',
|
'There are unsaved changes that will be lost. Do you want to continue?',
|
||||||
@ -98,7 +98,7 @@ function Editor(): JSX.Element {
|
|||||||
isActivationPanelOpened,
|
isActivationPanelOpened,
|
||||||
isSidebarOpened,
|
isSidebarOpened,
|
||||||
showIconLabels,
|
showIconLabels,
|
||||||
workflow,
|
automation,
|
||||||
} = useSelect(
|
} = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
isFullscreenActive: select(storeName).isFeatureActive('fullscreenMode'),
|
isFullscreenActive: select(storeName).isFeatureActive('fullscreenMode'),
|
||||||
@ -106,7 +106,7 @@ function Editor(): JSX.Element {
|
|||||||
isSidebarOpened: select(storeName).isSidebarOpened(),
|
isSidebarOpened: select(storeName).isSidebarOpened(),
|
||||||
isActivationPanelOpened: select(storeName).isActivationPanelOpened(),
|
isActivationPanelOpened: select(storeName).isActivationPanelOpened(),
|
||||||
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -118,7 +118,7 @@ function Editor(): JSX.Element {
|
|||||||
if (!isBooting) {
|
if (!isBooting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updatingActiveWorkflowNotPossible();
|
updatingActiveAutomationNotPossible();
|
||||||
setIsBooting(false);
|
setIsBooting(false);
|
||||||
}, [isBooting]);
|
}, [isBooting]);
|
||||||
const className = classnames('interface-interface-skeleton', {
|
const className = classnames('interface-interface-skeleton', {
|
||||||
@ -126,9 +126,9 @@ function Editor(): JSX.Element {
|
|||||||
'show-icon-labels': showIconLabels,
|
'show-icon-labels': showIconLabels,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (workflow.status === 'trash') {
|
if (automation.status === 'trash') {
|
||||||
window.location.href = addQueryArgs(MailPoet.urls.automationListing, {
|
window.location.href = addQueryArgs(MailPoet.urls.automationListing, {
|
||||||
[LISTING_NOTICE_PARAMETERS.workflowHadBeenDeleted]: workflow.id,
|
[LISTING_NOTICE_PARAMETERS.automationHadBeenDeleted]: automation.id,
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ function Editor(): JSX.Element {
|
|||||||
content={
|
content={
|
||||||
<>
|
<>
|
||||||
<EditorNotices />
|
<EditorNotices />
|
||||||
<Workflow />
|
<Automation />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
sidebar={<ComplementaryArea.Slot scope={storeName} />}
|
sidebar={<ComplementaryArea.Slot scope={storeName} />}
|
||||||
|
@ -7,9 +7,9 @@ import { store as preferencesStore } from '@wordpress/preferences';
|
|||||||
import { addQueryArgs } from '@wordpress/url';
|
import { addQueryArgs } from '@wordpress/url';
|
||||||
import { storeName } from './constants';
|
import { storeName } from './constants';
|
||||||
import { Feature, State } from './types';
|
import { Feature, State } from './types';
|
||||||
import { LISTING_NOTICE_PARAMETERS } from '../../listing/workflow-listing-notices';
|
import { LISTING_NOTICE_PARAMETERS } from '../../listing/automation-listing-notices';
|
||||||
import { MailPoet } from '../../../mailpoet';
|
import { MailPoet } from '../../../mailpoet';
|
||||||
import { WorkflowStatus } from '../../listing/workflow';
|
import { AutomationStatus } from '../../listing/automation';
|
||||||
|
|
||||||
const trackErrors = (errors) => {
|
const trackErrors = (errors) => {
|
||||||
if (!errors?.steps) {
|
if (!errors?.steps) {
|
||||||
@ -24,7 +24,7 @@ const trackErrors = (errors) => {
|
|||||||
return fields;
|
return fields;
|
||||||
});
|
});
|
||||||
|
|
||||||
MailPoet.trackEvent('Automations > Workflow validation error', {
|
MailPoet.trackEvent('Automations > Automation validation error', {
|
||||||
errors: payload,
|
errors: payload,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -74,23 +74,23 @@ export function selectStep(value) {
|
|||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setWorkflowName(name) {
|
export function setAutomationName(name) {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
return {
|
return {
|
||||||
type: 'UPDATE_WORKFLOW',
|
type: 'UPDATE_AUTOMATION',
|
||||||
workflow: {
|
automation: {
|
||||||
...workflow,
|
...automation,
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* save() {
|
export function* save() {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: { ...workflow },
|
data: { ...automation },
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
||||||
@ -106,23 +106,23 @@ export function* save() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'SAVE',
|
type: 'SAVE',
|
||||||
workflow: data?.data ?? workflow,
|
automation: data?.data ?? automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* activate() {
|
export function* activate() {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
...workflow,
|
...automation,
|
||||||
status: WorkflowStatus.ACTIVE,
|
status: AutomationStatus.ACTIVE,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
||||||
if (data?.data.status === WorkflowStatus.ACTIVE) {
|
if (data?.data.status === AutomationStatus.ACTIVE) {
|
||||||
void createNotice(
|
void createNotice(
|
||||||
'success',
|
'success',
|
||||||
__('Well done! Automation is now activated!', 'mailpoet'),
|
__('Well done! Automation is now activated!', 'mailpoet'),
|
||||||
@ -130,30 +130,33 @@ export function* activate() {
|
|||||||
type: 'snackbar',
|
type: 'snackbar',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
MailPoet.trackEvent('Automations > Workflow activated');
|
MailPoet.trackEvent('Automations > Automation activated');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'ACTIVATE',
|
type: 'ACTIVATE',
|
||||||
workflow: data?.data ?? workflow,
|
automation: data?.data ?? automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* deactivate(deactivateWorkflowRuns = true) {
|
export function* deactivate(deactivateAutomationRuns = true) {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
...workflow,
|
...automation,
|
||||||
status: deactivateWorkflowRuns
|
status: deactivateAutomationRuns
|
||||||
? WorkflowStatus.DRAFT
|
? AutomationStatus.DRAFT
|
||||||
: WorkflowStatus.DEACTIVATING,
|
: AutomationStatus.DEACTIVATING,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
||||||
if (deactivateWorkflowRuns && data?.data.status === WorkflowStatus.DRAFT) {
|
if (
|
||||||
|
deactivateAutomationRuns &&
|
||||||
|
data?.data.status === AutomationStatus.DRAFT
|
||||||
|
) {
|
||||||
void createNotice(
|
void createNotice(
|
||||||
'success',
|
'success',
|
||||||
__('Automation is now deactivated!', 'mailpoet'),
|
__('Automation is now deactivated!', 'mailpoet'),
|
||||||
@ -162,13 +165,13 @@ export function* deactivate(deactivateWorkflowRuns = true) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
MailPoet.trackEvent('Automations > Workflow deactivated', {
|
MailPoet.trackEvent('Automations > Automation deactivated', {
|
||||||
type: 'immediate',
|
type: 'immediate',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!deactivateWorkflowRuns &&
|
!deactivateAutomationRuns &&
|
||||||
data?.data.status === WorkflowStatus.DEACTIVATING
|
data?.data.status === AutomationStatus.DEACTIVATING
|
||||||
) {
|
) {
|
||||||
void createNotice(
|
void createNotice(
|
||||||
'success',
|
'success',
|
||||||
@ -180,39 +183,39 @@ export function* deactivate(deactivateWorkflowRuns = true) {
|
|||||||
type: 'snackbar',
|
type: 'snackbar',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
MailPoet.trackEvent('Automations > Workflow deactivated', {
|
MailPoet.trackEvent('Automations > Automation deactivated', {
|
||||||
type: 'continuous',
|
type: 'continuous',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'DEACTIVATE',
|
type: 'DEACTIVATE',
|
||||||
workflow: data?.data ?? workflow,
|
automation: data?.data ?? automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* trash(onTrashed: () => void = undefined) {
|
export function* trash(onTrashed: () => void = undefined) {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
...workflow,
|
...automation,
|
||||||
status: WorkflowStatus.TRASH,
|
status: AutomationStatus.TRASH,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
onTrashed?.();
|
onTrashed?.();
|
||||||
|
|
||||||
if (data?.status === WorkflowStatus.TRASH) {
|
if (data?.status === AutomationStatus.TRASH) {
|
||||||
window.location.href = addQueryArgs(MailPoet.urls.automationListing, {
|
window.location.href = addQueryArgs(MailPoet.urls.automationListing, {
|
||||||
[LISTING_NOTICE_PARAMETERS.workflowDeleted]: workflow.id,
|
[LISTING_NOTICE_PARAMETERS.automationDeleted]: automation.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'TRASH',
|
type: 'TRASH',
|
||||||
workflow: data?.data ?? workflow,
|
automation: data?.data ?? automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export const storeName = 'mailpoet/automation-editor';
|
export const storeName = 'mailpoet/automation-editor';
|
||||||
|
|
||||||
export const workflowSidebarKey = 'mailpoet/automation-editor/workflow';
|
export const automationSidebarKey = 'mailpoet/automation-editor/automation';
|
||||||
export const stepSidebarKey = 'mailpoet/automation-editor/step';
|
export const stepSidebarKey = 'mailpoet/automation-editor/step';
|
||||||
|
@ -5,8 +5,8 @@ declare let window: AutomationEditorWindow;
|
|||||||
export const getInitialState = (): State => ({
|
export const getInitialState = (): State => ({
|
||||||
context: { ...window.mailpoet_automation_context },
|
context: { ...window.mailpoet_automation_context },
|
||||||
stepTypes: {},
|
stepTypes: {},
|
||||||
workflowData: { ...window.mailpoet_automation_workflow },
|
automationData: { ...window.mailpoet_automation_automation },
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
selectedStep: undefined,
|
selectedStep: undefined,
|
||||||
inserterSidebar: {
|
inserterSidebar: {
|
||||||
isOpened: false,
|
isOpened: false,
|
||||||
|
@ -29,35 +29,35 @@ export function reducer(state: State, action: Action): State {
|
|||||||
...state,
|
...state,
|
||||||
selectedStep: action.value,
|
selectedStep: action.value,
|
||||||
};
|
};
|
||||||
case 'UPDATE_WORKFLOW':
|
case 'UPDATE_AUTOMATION':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: false,
|
automationSaved: false,
|
||||||
};
|
};
|
||||||
case 'SAVE':
|
case 'SAVE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
};
|
};
|
||||||
case 'ACTIVATE':
|
case 'ACTIVATE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
};
|
};
|
||||||
case 'DEACTIVATE':
|
case 'DEACTIVATE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
};
|
};
|
||||||
case 'TRASH':
|
case 'TRASH':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
};
|
};
|
||||||
case 'REGISTER_STEP_TYPE':
|
case 'REGISTER_STEP_TYPE':
|
||||||
return {
|
return {
|
||||||
@ -68,7 +68,7 @@ export function reducer(state: State, action: Action): State {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
case 'UPDATE_STEP_ARGS': {
|
case 'UPDATE_STEP_ARGS': {
|
||||||
const prevArgs = state.workflowData.steps[action.stepId].args ?? {};
|
const prevArgs = state.automationData.steps[action.stepId].args ?? {};
|
||||||
|
|
||||||
const value =
|
const value =
|
||||||
typeof action.value === 'function'
|
typeof action.value === 'function'
|
||||||
@ -82,7 +82,7 @@ export function reducer(state: State, action: Action): State {
|
|||||||
)
|
)
|
||||||
: { ...prevArgs, [action.name]: value };
|
: { ...prevArgs, [action.name]: value };
|
||||||
|
|
||||||
const step = { ...state.workflowData.steps[action.stepId], args };
|
const step = { ...state.automationData.steps[action.stepId], args };
|
||||||
|
|
||||||
const stepErrors = Object.values(state.errors?.steps ?? {}).filter(
|
const stepErrors = Object.values(state.errors?.steps ?? {}).filter(
|
||||||
({ step_id }) => step_id !== action.stepId,
|
({ step_id }) => step_id !== action.stepId,
|
||||||
@ -90,14 +90,14 @@ export function reducer(state: State, action: Action): State {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: {
|
automationData: {
|
||||||
...state.workflowData,
|
...state.automationData,
|
||||||
steps: {
|
steps: {
|
||||||
...state.workflowData.steps,
|
...state.automationData.steps,
|
||||||
[action.stepId]: step,
|
[action.stepId]: step,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
workflowSaved: false,
|
automationSaved: false,
|
||||||
selectedStep: step,
|
selectedStep: step,
|
||||||
errors:
|
errors:
|
||||||
stepErrors.length > 0
|
stepErrors.length > 0
|
||||||
|
@ -4,7 +4,7 @@ import { store as preferencesStore } from '@wordpress/preferences';
|
|||||||
import { storeName } from './constants';
|
import { storeName } from './constants';
|
||||||
import { Context, Errors, Feature, State, StepErrors, StepType } from './types';
|
import { Context, Errors, Feature, State, StepErrors, StepType } from './types';
|
||||||
import { Item } from '../components/inserter/item';
|
import { Item } from '../components/inserter/item';
|
||||||
import { Step, Workflow } from '../components/workflow/types';
|
import { Step, Automation } from '../components/automation/types';
|
||||||
|
|
||||||
export const isFeatureActive = createRegistrySelector(
|
export const isFeatureActive = createRegistrySelector(
|
||||||
(select) =>
|
(select) =>
|
||||||
@ -58,12 +58,12 @@ export function getInserterPopover(
|
|||||||
return state.inserterPopover;
|
return state.inserterPopover;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWorkflowData(state: State): Workflow {
|
export function getAutomationData(state: State): Automation {
|
||||||
return state.workflowData;
|
return state.automationData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWorkflowSaved(state: State): boolean {
|
export function getAutomationSaved(state: State): boolean {
|
||||||
return state.workflowSaved;
|
return state.automationSaved;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSelectedStep(state: State): Step | undefined {
|
export function getSelectedStep(state: State): Step | undefined {
|
||||||
@ -71,7 +71,7 @@ export function getSelectedStep(state: State): Step | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getStepById(state: State, id: string): Step | undefined {
|
export function getStepById(state: State, id: string): Step | undefined {
|
||||||
return state.workflowData.steps[id] ?? undefined;
|
return state.automationData.steps[id] ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStepType(state: State, key: string): StepType | undefined {
|
export function getStepType(state: State, key: string): StepType | undefined {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { ComponentType } from 'react';
|
import { ComponentType } from 'react';
|
||||||
import { Step, Workflow } from '../components/workflow/types';
|
import { Step, Automation } from '../components/automation/types';
|
||||||
|
|
||||||
export interface AutomationEditorWindow extends Window {
|
export interface AutomationEditorWindow extends Window {
|
||||||
mailpoet_automation_context: Context;
|
mailpoet_automation_context: Context;
|
||||||
mailpoet_automation_workflow: Workflow;
|
mailpoet_automation_automation: Automation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Context = {
|
export type Context = {
|
||||||
@ -48,8 +48,8 @@ export type Errors = {
|
|||||||
export type State = {
|
export type State = {
|
||||||
context: Context;
|
context: Context;
|
||||||
stepTypes: Record<string, StepType>;
|
stepTypes: Record<string, StepType>;
|
||||||
workflowData: Workflow;
|
automationData: Automation;
|
||||||
workflowSaved: boolean;
|
automationSaved: boolean;
|
||||||
selectedStep: Step | undefined;
|
selectedStep: Step | undefined;
|
||||||
inserterSidebar: {
|
inserterSidebar: {
|
||||||
isOpened: boolean;
|
isOpened: boolean;
|
||||||
|
@ -3,7 +3,7 @@ import { chartBar } from '@wordpress/icons';
|
|||||||
import { Hooks } from 'wp-js-hooks';
|
import { Hooks } from 'wp-js-hooks';
|
||||||
import { MoreControlType, StepMoreControlsType } from '../../../types/filters';
|
import { MoreControlType, StepMoreControlsType } from '../../../types/filters';
|
||||||
import { StepType } from '../../../editor/store';
|
import { StepType } from '../../../editor/store';
|
||||||
import { Step } from '../../../editor/components/workflow/types';
|
import { Step } from '../../../editor/components/automation/types';
|
||||||
|
|
||||||
const emailStatisticsControl = (step: Step): MoreControlType => {
|
const emailStatisticsControl = (step: Step): MoreControlType => {
|
||||||
const hasEmail = step.args?.email_id > 0;
|
const hasEmail = step.args?.email_id > 0;
|
||||||
@ -28,7 +28,7 @@ const emailStatisticsControl = (step: Step): MoreControlType => {
|
|||||||
|
|
||||||
export function registerStepControls() {
|
export function registerStepControls() {
|
||||||
Hooks.addFilter(
|
Hooks.addFilter(
|
||||||
'mailpoet.automation.workflow.step.more-controls',
|
'mailpoet.automation.automation.step.more-controls',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
(
|
(
|
||||||
controls: StepMoreControlsType,
|
controls: StepMoreControlsType,
|
||||||
|
@ -31,11 +31,11 @@ export function EditNewsletter(): JSX.Element {
|
|||||||
useState(false);
|
useState(false);
|
||||||
const [fetchingPreviewLink, setFetchingPreviewLink] = useState(false);
|
const [fetchingPreviewLink, setFetchingPreviewLink] = useState(false);
|
||||||
|
|
||||||
const { selectedStep, workflowId, workflowSaved, errors } = useSelect(
|
const { selectedStep, automationId, automationSaved, errors } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
selectedStep: select(storeName).getSelectedStep(),
|
selectedStep: select(storeName).getSelectedStep(),
|
||||||
workflowId: select(storeName).getWorkflowData().id,
|
automationId: select(storeName).getAutomationData().id,
|
||||||
workflowSaved: select(storeName).getWorkflowSaved(),
|
automationSaved: select(storeName).getAutomationSaved(),
|
||||||
errors: select(storeName).getStepError(
|
errors: select(storeName).getStepError(
|
||||||
select(storeName).getSelectedStep().id,
|
select(storeName).getSelectedStep().id,
|
||||||
),
|
),
|
||||||
@ -44,7 +44,7 @@ export function EditNewsletter(): JSX.Element {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const emailId = selectedStep?.args?.email_id as number | undefined;
|
const emailId = selectedStep?.args?.email_id as number | undefined;
|
||||||
const workflowStepId = selectedStep.id;
|
const automationStepId = selectedStep.id;
|
||||||
const errorFields = errors?.fields ?? {};
|
const errorFields = errors?.fields ?? {};
|
||||||
const emailIdError = errorFields?.email_id ?? '';
|
const emailIdError = errorFields?.email_id ?? '';
|
||||||
|
|
||||||
@ -58,28 +58,28 @@ export function EditNewsletter(): JSX.Element {
|
|||||||
type: 'automation',
|
type: 'automation',
|
||||||
subject: '',
|
subject: '',
|
||||||
options: {
|
options: {
|
||||||
workflowId,
|
automationId,
|
||||||
workflowStepId,
|
automationStepId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(storeName).updateStepArgs(
|
dispatch(storeName).updateStepArgs(
|
||||||
workflowStepId,
|
automationStepId,
|
||||||
'email_id',
|
'email_id',
|
||||||
parseInt(response.data.id as string, 10),
|
parseInt(response.data.id as string, 10),
|
||||||
);
|
);
|
||||||
|
|
||||||
dispatch(storeName).save();
|
dispatch(storeName).save();
|
||||||
}, [workflowId, workflowStepId]);
|
}, [automationId, automationStepId]);
|
||||||
|
|
||||||
// This component is rendered only when no email ID is set. Once we have the ID
|
// This component is rendered only when no email ID is set. Once we have the ID
|
||||||
// and the workflow is saved, we can safely redirect to the email design flow.
|
// and the automation is saved, we can safely redirect to the email design flow.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (redirectToTemplateSelection && emailId && workflowSaved) {
|
if (redirectToTemplateSelection && emailId && automationSaved) {
|
||||||
window.location.href = `admin.php?page=mailpoet-newsletters#/template/${emailId}`;
|
window.location.href = `admin.php?page=mailpoet-newsletters#/template/${emailId}`;
|
||||||
}
|
}
|
||||||
}, [emailId, workflowSaved, redirectToTemplateSelection]);
|
}, [emailId, automationSaved, redirectToTemplateSelection]);
|
||||||
|
|
||||||
if (!emailId || redirectToTemplateSelection) {
|
if (!emailId || redirectToTemplateSelection) {
|
||||||
return (
|
return (
|
||||||
|
@ -3,7 +3,7 @@ import { Hooks } from 'wp-js-hooks';
|
|||||||
import { Icon } from './icon';
|
import { Icon } from './icon';
|
||||||
import { Edit } from './edit';
|
import { Edit } from './edit';
|
||||||
import { State, StepType } from '../../../../editor/store/types';
|
import { State, StepType } from '../../../../editor/store/types';
|
||||||
import { Step } from '../../../../editor/components/workflow/types';
|
import { Step } from '../../../../editor/components/automation/types';
|
||||||
|
|
||||||
export const step: StepType = {
|
export const step: StepType = {
|
||||||
key: 'mailpoet:send-email',
|
key: 'mailpoet:send-email',
|
||||||
@ -20,6 +20,6 @@ export const step: StepType = {
|
|||||||
Hooks.applyFilters(
|
Hooks.applyFilters(
|
||||||
'mailpoet.automation.send_email.create_step',
|
'mailpoet.automation.send_email.create_step',
|
||||||
stepData,
|
stepData,
|
||||||
state.workflowData.id,
|
state.automationData.id,
|
||||||
),
|
),
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
* The types in this file document the expected return types of specific
|
* The types in this file document the expected return types of specific
|
||||||
* filters.
|
* filters.
|
||||||
*/
|
*/
|
||||||
import { Step } from '../../../editor/components/workflow/types';
|
import { Step } from '../../../editor/components/automation/types';
|
||||||
|
|
||||||
// mailpoet.automation.send_email.create_step
|
// mailpoet.automation.send_email.create_step
|
||||||
export type SendEmailCreateStepType = (step: Step, workflowId: number) => Step;
|
export type SendEmailCreateStepType = (
|
||||||
|
step: Step,
|
||||||
|
automationId: number,
|
||||||
|
) => Step;
|
||||||
|
|
||||||
// mailpoet.automation.send_email.google_analytics_panel
|
// mailpoet.automation.send_email.google_analytics_panel
|
||||||
export type GoogleAnalyticsPanelBodyType = JSX.Element;
|
export type GoogleAnalyticsPanelBodyType = JSX.Element;
|
||||||
|
@ -3,27 +3,30 @@ import { __ } from '@wordpress/i18n';
|
|||||||
import { Notice } from '../../notices/notice';
|
import { Notice } from '../../notices/notice';
|
||||||
|
|
||||||
export const LISTING_NOTICE_PARAMETERS = {
|
export const LISTING_NOTICE_PARAMETERS = {
|
||||||
workflowHadBeenDeleted: 'mailpoet-had-been-deleted',
|
automationHadBeenDeleted: 'mailpoet-had-been-deleted',
|
||||||
workflowDeleted: 'mailpoet-workflow-deleted',
|
automationDeleted: 'mailpoet-automation-deleted',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function WorkflowListingNotices(): JSX.Element {
|
export function AutomationListingNotices(): JSX.Element {
|
||||||
const workflowHadBeenDeleted = parseInt(
|
const automationHadBeenDeleted = parseInt(
|
||||||
getQueryArg(
|
getQueryArg(
|
||||||
window.location.href,
|
window.location.href,
|
||||||
LISTING_NOTICE_PARAMETERS.workflowHadBeenDeleted,
|
LISTING_NOTICE_PARAMETERS.automationHadBeenDeleted,
|
||||||
) as string,
|
) as string,
|
||||||
10,
|
10,
|
||||||
);
|
);
|
||||||
const workflowDeleted = parseInt(
|
const automationDeleted = parseInt(
|
||||||
getQueryArg(
|
getQueryArg(
|
||||||
window.location.href,
|
window.location.href,
|
||||||
LISTING_NOTICE_PARAMETERS.workflowDeleted,
|
LISTING_NOTICE_PARAMETERS.automationDeleted,
|
||||||
) as string,
|
) as string,
|
||||||
10,
|
10,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Number.isNaN(workflowHadBeenDeleted) && Number.isNaN(workflowDeleted)) {
|
if (
|
||||||
|
Number.isNaN(automationHadBeenDeleted) &&
|
||||||
|
Number.isNaN(automationDeleted)
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ export function WorkflowListingNotices(): JSX.Element {
|
|||||||
...Object.values(LISTING_NOTICE_PARAMETERS),
|
...Object.values(LISTING_NOTICE_PARAMETERS),
|
||||||
);
|
);
|
||||||
window.history.pushState('', '', urlWithoutNotices);
|
window.history.pushState('', '', urlWithoutNotices);
|
||||||
if (workflowHadBeenDeleted) {
|
if (automationHadBeenDeleted) {
|
||||||
return (
|
return (
|
||||||
<Notice type="error" closable timeout={false}>
|
<Notice type="error" closable timeout={false}>
|
||||||
<p>
|
<p>
|
||||||
@ -44,7 +47,7 @@ export function WorkflowListingNotices(): JSX.Element {
|
|||||||
</Notice>
|
</Notice>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (workflowDeleted) {
|
if (automationDeleted) {
|
||||||
return (
|
return (
|
||||||
<Notice type="success" closable timeout={false}>
|
<Notice type="success" closable timeout={false}>
|
||||||
<p>{__('1 automation moved to the Trash.', 'mailpoet')}</p>
|
<p>{__('1 automation moved to the Trash.', 'mailpoet')}</p>
|
@ -1,14 +1,14 @@
|
|||||||
export enum WorkflowStatus {
|
export enum AutomationStatus {
|
||||||
ACTIVE = 'active',
|
ACTIVE = 'active',
|
||||||
DRAFT = 'draft',
|
DRAFT = 'draft',
|
||||||
TRASH = 'trash',
|
TRASH = 'trash',
|
||||||
DEACTIVATING = 'deactivating',
|
DEACTIVATING = 'deactivating',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Workflow = {
|
export type Automation = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
status: WorkflowStatus;
|
status: AutomationStatus;
|
||||||
stats: {
|
stats: {
|
||||||
totals: {
|
totals: {
|
||||||
entered: number;
|
entered: number;
|
@ -1,19 +1,19 @@
|
|||||||
import { Button } from '@wordpress/components';
|
import { Button } from '@wordpress/components';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { addQueryArgs } from '@wordpress/url';
|
import { addQueryArgs } from '@wordpress/url';
|
||||||
import { Workflow } from '../../workflow';
|
import { Automation } from '../../automation';
|
||||||
import { MailPoet } from '../../../../mailpoet';
|
import { MailPoet } from '../../../../mailpoet';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workflow: Workflow;
|
automation: Automation;
|
||||||
label?: string;
|
label?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EditWorkflow({ workflow, label }: Props): JSX.Element {
|
export function EditAutomation({ automation, label }: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
href={addQueryArgs(MailPoet.urls.automationEditor, { id: workflow.id })}
|
href={addQueryArgs(MailPoet.urls.automationEditor, { id: automation.id })}
|
||||||
>
|
>
|
||||||
{label ?? __('Edit', 'mailpoet')}
|
{label ?? __('Edit', 'mailpoet')}
|
||||||
</Button>
|
</Button>
|
@ -1,2 +1,2 @@
|
|||||||
export * from './edit-workflow';
|
export * from './edit-automation';
|
||||||
export * from './undo-trash';
|
export * from './undo-trash';
|
||||||
|
@ -2,23 +2,23 @@ import { Button } from '@wordpress/components';
|
|||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { storeName } from '../../store/constants';
|
import { storeName } from '../../store/constants';
|
||||||
import { Workflow, WorkflowStatus } from '../../workflow';
|
import { Automation, AutomationStatus } from '../../automation';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workflow: Workflow;
|
automation: Automation;
|
||||||
previousStatus: WorkflowStatus;
|
previousStatus: AutomationStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function UndoTrashButton({
|
export function UndoTrashButton({
|
||||||
workflow,
|
automation,
|
||||||
previousStatus,
|
previousStatus,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const { restoreWorkflow } = useDispatch(storeName);
|
const { restoreAutomation } = useDispatch(storeName);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
onClick={() => restoreWorkflow(workflow, previousStatus)}
|
onClick={() => restoreAutomation(automation, previousStatus)}
|
||||||
>
|
>
|
||||||
{__('Undo', 'mailpoet')}
|
{__('Undo', 'mailpoet')}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -3,25 +3,25 @@ import { __ } from '@wordpress/i18n';
|
|||||||
import { DropdownMenu } from '@wordpress/components';
|
import { DropdownMenu } from '@wordpress/components';
|
||||||
import { moreVertical } from '@wordpress/icons';
|
import { moreVertical } from '@wordpress/icons';
|
||||||
import { useDeleteButton, useRestoreButton, useTrashButton } from '../menu';
|
import { useDeleteButton, useRestoreButton, useTrashButton } from '../menu';
|
||||||
import { Workflow } from '../../workflow';
|
import { Automation } from '../../automation';
|
||||||
import { EditWorkflow } from '../actions';
|
import { EditAutomation } from '../actions';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workflow: Workflow;
|
automation: Automation;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Actions({ workflow }: Props): JSX.Element {
|
export function Actions({ automation }: Props): JSX.Element {
|
||||||
// Menu items are using custom hooks because the "DropdownMenu" component uses the "controls"
|
// Menu items are using custom hooks because the "DropdownMenu" component uses the "controls"
|
||||||
// attribute rather than child components, but we need to render modal confirmation dialogs.
|
// attribute rather than child components, but we need to render modal confirmation dialogs.
|
||||||
const trash = useTrashButton(workflow);
|
const trash = useTrashButton(automation);
|
||||||
const restore = useRestoreButton(workflow);
|
const restore = useRestoreButton(automation);
|
||||||
const del = useDeleteButton(workflow);
|
const del = useDeleteButton(automation);
|
||||||
|
|
||||||
const menuItems = [trash, restore, del].filter((item) => item);
|
const menuItems = [trash, restore, del].filter((item) => item);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-automation-listing-cell-actions">
|
<div className="mailpoet-automation-listing-cell-actions">
|
||||||
<EditWorkflow workflow={workflow} />
|
<EditAutomation automation={automation} />
|
||||||
{menuItems.map(({ control, slot }) => (
|
{menuItems.map(({ control, slot }) => (
|
||||||
<Fragment key={control.title}>{slot}</Fragment>
|
<Fragment key={control.title}>{slot}</Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { EditWorkflow } from '../actions';
|
import { EditAutomation } from '../actions';
|
||||||
import { Workflow } from '../../workflow';
|
import { Automation } from '../../automation';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workflow: Workflow;
|
automation: Automation;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Name({ workflow }: Props): JSX.Element {
|
export function Name({ automation }: Props): JSX.Element {
|
||||||
return <EditWorkflow workflow={workflow} label={workflow.name} />;
|
return <EditAutomation automation={automation} label={automation.name} />;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Workflow, WorkflowStatus } from '../../workflow';
|
import { Automation, AutomationStatus } from '../../automation';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workflow: Workflow;
|
automation: Automation;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Status({ workflow }: Props): JSX.Element {
|
export function Status({ automation }: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-automation-listing-cell-status">
|
<div className="mailpoet-automation-listing-cell-status">
|
||||||
{workflow.status === WorkflowStatus.ACTIVE
|
{automation.status === AutomationStatus.ACTIVE
|
||||||
? __('Active', 'mailpoet')
|
? __('Active', 'mailpoet')
|
||||||
: __('Not active', 'mailpoet')}
|
: __('Not active', 'mailpoet')}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { _x } from '@wordpress/i18n';
|
import { _x } from '@wordpress/i18n';
|
||||||
import { Workflow } from '../../workflow';
|
import { Automation } from '../../automation';
|
||||||
import { Statistics } from '../../../components/statistics';
|
import { Statistics } from '../../../components/statistics';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workflow: Workflow;
|
automation: Automation;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Subscribers({ workflow }: Props): JSX.Element {
|
export function Subscribers({ automation }: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Statistics
|
<Statistics
|
||||||
labelPosition="after"
|
labelPosition="after"
|
||||||
@ -15,19 +15,19 @@ export function Subscribers({ workflow }: Props): JSX.Element {
|
|||||||
key: 'entered',
|
key: 'entered',
|
||||||
// translators: Total number of subscribers who entered an automation
|
// translators: Total number of subscribers who entered an automation
|
||||||
label: _x('Entered', 'automation stats', 'mailpoet'),
|
label: _x('Entered', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.entered,
|
value: automation.stats.totals.entered,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'processing',
|
key: 'processing',
|
||||||
// translators: Total number of subscribers who are being processed in an automation
|
// translators: Total number of subscribers who are being processed in an automation
|
||||||
label: _x('Processing', 'automation stats', 'mailpoet'),
|
label: _x('Processing', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.in_progress,
|
value: automation.stats.totals.in_progress,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'exited',
|
key: 'exited',
|
||||||
// translators: Total number of subscribers who exited an automation, no matter the result
|
// translators: Total number of subscribers who exited an automation, no matter the result
|
||||||
label: _x('Exited', 'automation stats', 'mailpoet'),
|
label: _x('Exited', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.exited,
|
value: automation.stats.totals.exited,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -4,13 +4,13 @@ import { useDispatch } from '@wordpress/data';
|
|||||||
import { __, sprintf } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { Item } from './item';
|
import { Item } from './item';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { Workflow, WorkflowStatus } from '../../workflow';
|
import { Automation, AutomationStatus } from '../../automation';
|
||||||
|
|
||||||
export const useDeleteButton = (workflow: Workflow): Item | undefined => {
|
export const useDeleteButton = (automation: Automation): Item | undefined => {
|
||||||
const [showDialog, setShowDialog] = useState(false);
|
const [showDialog, setShowDialog] = useState(false);
|
||||||
const { deleteWorkflow } = useDispatch(storeName);
|
const { deleteAutomation } = useDispatch(storeName);
|
||||||
|
|
||||||
if (workflow.status !== WorkflowStatus.TRASH) {
|
if (automation.status !== AutomationStatus.TRASH) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export const useDeleteButton = (workflow: Workflow): Item | undefined => {
|
|||||||
title={__('Permanently delete automation', 'mailpoet')}
|
title={__('Permanently delete automation', 'mailpoet')}
|
||||||
confirmButtonText={__('Yes, permanently delete', 'mailpoet')}
|
confirmButtonText={__('Yes, permanently delete', 'mailpoet')}
|
||||||
__experimentalHideHeader={false}
|
__experimentalHideHeader={false}
|
||||||
onConfirm={() => deleteWorkflow(workflow)}
|
onConfirm={() => deleteAutomation(automation)}
|
||||||
onCancel={() => setShowDialog(false)}
|
onCancel={() => setShowDialog(false)}
|
||||||
>
|
>
|
||||||
{sprintf(
|
{sprintf(
|
||||||
@ -36,7 +36,7 @@ export const useDeleteButton = (workflow: Workflow): Item | undefined => {
|
|||||||
'Are you sure you want to permanently delete "%s" and all associated data? This cannot be undone!',
|
'Are you sure you want to permanently delete "%s" and all associated data? This cannot be undone!',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
),
|
),
|
||||||
workflow.name,
|
automation.name,
|
||||||
)}
|
)}
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
),
|
),
|
||||||
|
@ -2,12 +2,14 @@ import { useDispatch } from '@wordpress/data';
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Item } from './item';
|
import { Item } from './item';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { Workflow, WorkflowStatus } from '../../workflow';
|
import { Automation, AutomationStatus } from '../../automation';
|
||||||
|
|
||||||
export const useDuplicateButton = (workflow: Workflow): Item | undefined => {
|
export const useDuplicateButton = (
|
||||||
const { duplicateWorkflow } = useDispatch(storeName);
|
automation: Automation,
|
||||||
|
): Item | undefined => {
|
||||||
|
const { duplicateAutomation } = useDispatch(storeName);
|
||||||
|
|
||||||
if (workflow.status === WorkflowStatus.TRASH) {
|
if (automation.status === AutomationStatus.TRASH) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +18,7 @@ export const useDuplicateButton = (workflow: Workflow): Item | undefined => {
|
|||||||
control: {
|
control: {
|
||||||
title: __('Duplicate', 'mailpoet'),
|
title: __('Duplicate', 'mailpoet'),
|
||||||
icon: null,
|
icon: null,
|
||||||
onClick: () => duplicateWorkflow(workflow),
|
onClick: () => duplicateAutomation(automation),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,12 +2,12 @@ import { useDispatch } from '@wordpress/data';
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Item } from './item';
|
import { Item } from './item';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { Workflow, WorkflowStatus } from '../../workflow';
|
import { Automation, AutomationStatus } from '../../automation';
|
||||||
|
|
||||||
export const useRestoreButton = (workflow: Workflow): Item | undefined => {
|
export const useRestoreButton = (automation: Automation): Item | undefined => {
|
||||||
const { restoreWorkflow } = useDispatch(storeName);
|
const { restoreAutomation } = useDispatch(storeName);
|
||||||
|
|
||||||
if (workflow.status !== WorkflowStatus.TRASH) {
|
if (automation.status !== AutomationStatus.TRASH) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export const useRestoreButton = (workflow: Workflow): Item | undefined => {
|
|||||||
control: {
|
control: {
|
||||||
title: __('Restore', 'mailpoet'),
|
title: __('Restore', 'mailpoet'),
|
||||||
icon: null,
|
icon: null,
|
||||||
onClick: () => restoreWorkflow(workflow, WorkflowStatus.DRAFT),
|
onClick: () => restoreAutomation(automation, AutomationStatus.DRAFT),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -4,13 +4,13 @@ import { useDispatch } from '@wordpress/data';
|
|||||||
import { __, _x, sprintf } from '@wordpress/i18n';
|
import { __, _x, sprintf } from '@wordpress/i18n';
|
||||||
import { Item } from './item';
|
import { Item } from './item';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { Workflow, WorkflowStatus } from '../../workflow';
|
import { Automation, AutomationStatus } from '../../automation';
|
||||||
|
|
||||||
export const useTrashButton = (workflow: Workflow): Item | undefined => {
|
export const useTrashButton = (automation: Automation): Item | undefined => {
|
||||||
const [showDialog, setShowDialog] = useState(false);
|
const [showDialog, setShowDialog] = useState(false);
|
||||||
const { trashWorkflow } = useDispatch(storeName);
|
const { trashAutomation } = useDispatch(storeName);
|
||||||
|
|
||||||
if (workflow.status === WorkflowStatus.TRASH) {
|
if (automation.status === AutomationStatus.TRASH) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export const useTrashButton = (workflow: Workflow): Item | undefined => {
|
|||||||
title={__('Trash automation', 'mailpoet')}
|
title={__('Trash automation', 'mailpoet')}
|
||||||
confirmButtonText={__('Yes, move to trash', 'mailpoet')}
|
confirmButtonText={__('Yes, move to trash', 'mailpoet')}
|
||||||
__experimentalHideHeader={false}
|
__experimentalHideHeader={false}
|
||||||
onConfirm={() => trashWorkflow(workflow)}
|
onConfirm={() => trashAutomation(automation)}
|
||||||
onCancel={() => setShowDialog(false)}
|
onCancel={() => setShowDialog(false)}
|
||||||
>
|
>
|
||||||
{sprintf(
|
{sprintf(
|
||||||
@ -36,7 +36,7 @@ export const useTrashButton = (workflow: Workflow): Item | undefined => {
|
|||||||
'Are you sure you want to move the automation "%s" to the Trash?',
|
'Are you sure you want to move the automation "%s" to the Trash?',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
),
|
),
|
||||||
workflow.name,
|
automation.name,
|
||||||
)}
|
)}
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
),
|
),
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
import { Workflow } from './workflow';
|
import { Automation } from './automation';
|
||||||
import { Actions, Name, Status, Subscribers } from './components/cells';
|
import { Actions, Name, Status, Subscribers } from './components/cells';
|
||||||
|
|
||||||
export function getRow(workflow: Workflow): object[] {
|
export function getRow(automation: Automation): object[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: workflow.id,
|
id: automation.id,
|
||||||
value: workflow.name,
|
value: automation.name,
|
||||||
display: <Name workflow={workflow} />,
|
display: <Name automation={automation} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: workflow.id,
|
id: automation.id,
|
||||||
value: null,
|
value: null,
|
||||||
display: <Subscribers workflow={workflow} />,
|
display: <Subscribers automation={automation} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: workflow.id,
|
id: automation.id,
|
||||||
value: workflow.status,
|
value: automation.status,
|
||||||
display: <Status workflow={workflow} />,
|
display: <Status automation={automation} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: workflow.id,
|
id: automation.id,
|
||||||
value: null,
|
value: null,
|
||||||
display: <Actions workflow={workflow} />,
|
display: <Actions automation={automation} />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { useHistory, useLocation } from 'react-router-dom';
|
|||||||
import { plusIcon } from 'common/button/icon/plus';
|
import { plusIcon } from 'common/button/icon/plus';
|
||||||
import { getRow } from './get-row';
|
import { getRow } from './get-row';
|
||||||
import { storeName } from './store';
|
import { storeName } from './store';
|
||||||
import { Workflow, WorkflowStatus } from './workflow';
|
import { Automation, AutomationStatus } from './automation';
|
||||||
import { MailPoet } from '../../mailpoet';
|
import { MailPoet } from '../../mailpoet';
|
||||||
|
|
||||||
const tabConfig = [
|
const tabConfig = [
|
||||||
@ -17,17 +17,17 @@ const tabConfig = [
|
|||||||
className: 'mailpoet-tab-all',
|
className: 'mailpoet-tab-all',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: WorkflowStatus.ACTIVE,
|
name: AutomationStatus.ACTIVE,
|
||||||
title: __('Active', 'mailpoet'),
|
title: __('Active', 'mailpoet'),
|
||||||
className: 'mailpoet-tab-active',
|
className: 'mailpoet-tab-active',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: WorkflowStatus.DRAFT,
|
name: AutomationStatus.DRAFT,
|
||||||
title: _x('Draft', 'noun', 'mailpoet'),
|
title: _x('Draft', 'noun', 'mailpoet'),
|
||||||
className: 'mailpoet-tab-draft',
|
className: 'mailpoet-tab-draft',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: WorkflowStatus.TRASH,
|
name: AutomationStatus.TRASH,
|
||||||
title: _x('Trash', 'noun', 'mailpoet'),
|
title: _x('Trash', 'noun', 'mailpoet'),
|
||||||
className: 'mailpoet-tab-trash',
|
className: 'mailpoet-tab-trash',
|
||||||
},
|
},
|
||||||
@ -68,14 +68,14 @@ export function AutomationListing(): JSX.Element {
|
|||||||
[location],
|
[location],
|
||||||
);
|
);
|
||||||
|
|
||||||
const workflows = useSelect((select) => select(storeName).getWorkflows());
|
const automations = useSelect((select) => select(storeName).getAutomations());
|
||||||
const { loadWorkflows } = useDispatch(storeName);
|
const { loadAutomations } = useDispatch(storeName);
|
||||||
|
|
||||||
const status = pageSearch.get('status');
|
const status = pageSearch.get('status');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadWorkflows();
|
loadAutomations();
|
||||||
}, [loadWorkflows]);
|
}, [loadAutomations]);
|
||||||
|
|
||||||
// focus tab button on status change (needed due to the force re-mount below)
|
// focus tab button on status change (needed due to the force re-mount below)
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
@ -103,24 +103,24 @@ export function AutomationListing(): JSX.Element {
|
|||||||
[pageSearch, history],
|
[pageSearch, history],
|
||||||
);
|
);
|
||||||
|
|
||||||
const groupedWorkflows = useMemo<Record<string, Workflow[]>>(() => {
|
const groupedAutomations = useMemo<Record<string, Automation[]>>(() => {
|
||||||
const grouped = { all: [] };
|
const grouped = { all: [] };
|
||||||
(workflows ?? []).forEach((workflow) => {
|
(automations ?? []).forEach((automation) => {
|
||||||
if (!grouped[workflow.status]) {
|
if (!grouped[automation.status]) {
|
||||||
grouped[workflow.status] = [];
|
grouped[automation.status] = [];
|
||||||
}
|
}
|
||||||
grouped[workflow.status].push(workflow);
|
grouped[automation.status].push(automation);
|
||||||
if (workflow.status !== WorkflowStatus.TRASH) {
|
if (automation.status !== AutomationStatus.TRASH) {
|
||||||
grouped.all.push(workflow);
|
grouped.all.push(automation);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return grouped;
|
return grouped;
|
||||||
}, [workflows]);
|
}, [automations]);
|
||||||
|
|
||||||
const tabs = useMemo(
|
const tabs = useMemo(
|
||||||
() =>
|
() =>
|
||||||
tabConfig.map((tab) => {
|
tabConfig.map((tab) => {
|
||||||
const count = (groupedWorkflows[tab.name] ?? []).length;
|
const count = (groupedAutomations[tab.name] ?? []).length;
|
||||||
return {
|
return {
|
||||||
name: tab.name,
|
name: tab.name,
|
||||||
title: (
|
title: (
|
||||||
@ -132,38 +132,39 @@ export function AutomationListing(): JSX.Element {
|
|||||||
className: tab.className,
|
className: tab.className,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
[groupedWorkflows],
|
[groupedAutomations],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderTabs = useCallback(
|
const renderTabs = useCallback(
|
||||||
(tab) => {
|
(tab) => {
|
||||||
const filteredWorkflows: Workflow[] = groupedWorkflows[tab.name] ?? [];
|
const filteredAutomations: Automation[] =
|
||||||
|
groupedAutomations[tab.name] ?? [];
|
||||||
const rowsPerPage = parseInt(pageSearch.get('per_page') ?? '25', 10);
|
const rowsPerPage = parseInt(pageSearch.get('per_page') ?? '25', 10);
|
||||||
const currentPage = parseInt(pageSearch.get('paged') ?? '1', 10);
|
const currentPage = parseInt(pageSearch.get('paged') ?? '1', 10);
|
||||||
const start = (currentPage - 1) * rowsPerPage;
|
const start = (currentPage - 1) * rowsPerPage;
|
||||||
const rows = filteredWorkflows
|
const rows = filteredAutomations
|
||||||
.map((workflow) => getRow(workflow))
|
.map((automation) => getRow(automation))
|
||||||
.slice(start, start + rowsPerPage);
|
.slice(start, start + rowsPerPage);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableCard
|
<TableCard
|
||||||
className="mailpoet-automation-listing"
|
className="mailpoet-automation-listing"
|
||||||
title=""
|
title=""
|
||||||
isLoading={!workflows}
|
isLoading={!automations}
|
||||||
headers={tableHeaders}
|
headers={tableHeaders}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
rowKey={(_, i) => filteredWorkflows[i].id}
|
rowKey={(_, i) => filteredAutomations[i].id}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
onQueryChange={(key) => (value) => {
|
onQueryChange={(key) => (value) => {
|
||||||
updateUrlSearchString({ [key]: value });
|
updateUrlSearchString({ [key]: value });
|
||||||
}}
|
}}
|
||||||
totalRows={filteredWorkflows.length}
|
totalRows={filteredAutomations.length}
|
||||||
query={Object.fromEntries(pageSearch)}
|
query={Object.fromEntries(pageSearch)}
|
||||||
showMenu={false}
|
showMenu={false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[workflows, groupedWorkflows, pageSearch, updateUrlSearchString],
|
[automations, groupedAutomations, pageSearch, updateUrlSearchString],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -2,8 +2,8 @@ import { dispatch, StoreDescriptor } from '@wordpress/data';
|
|||||||
import { apiFetch } from '@wordpress/data-controls';
|
import { apiFetch } from '@wordpress/data-controls';
|
||||||
import { __, sprintf } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { store as noticesStore } from '@wordpress/notices';
|
import { store as noticesStore } from '@wordpress/notices';
|
||||||
import { Workflow, WorkflowStatus } from '../workflow';
|
import { Automation, AutomationStatus } from '../automation';
|
||||||
import { EditWorkflow, UndoTrashButton } from '../components/actions';
|
import { EditAutomation, UndoTrashButton } from '../components/actions';
|
||||||
|
|
||||||
const createSuccessNotice = (content: string, options?: unknown) =>
|
const createSuccessNotice = (content: string, options?: unknown) =>
|
||||||
dispatch(noticesStore as StoreDescriptor).createSuccessNotice(
|
dispatch(noticesStore as StoreDescriptor).createSuccessNotice(
|
||||||
@ -14,78 +14,84 @@ const createSuccessNotice = (content: string, options?: unknown) =>
|
|||||||
const removeNotice = (id: string) =>
|
const removeNotice = (id: string) =>
|
||||||
dispatch(noticesStore as StoreDescriptor).removeNotice(id);
|
dispatch(noticesStore as StoreDescriptor).removeNotice(id);
|
||||||
|
|
||||||
export function* loadWorkflows() {
|
export function* loadAutomations() {
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows`,
|
path: `/automations`,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'SET_WORKFLOWS',
|
type: 'SET_AUTOMATIONS',
|
||||||
workflows: data.data,
|
automations: data.data,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* duplicateWorkflow(workflow: Workflow) {
|
export function* duplicateAutomation(automation: Automation) {
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}/duplicate`,
|
path: `/automations/${automation.id}/duplicate`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
|
|
||||||
void createSuccessNotice(
|
void createSuccessNotice(
|
||||||
// translators: %s is the automation name
|
// translators: %s is the automation name
|
||||||
sprintf(__('Automation "%s" was duplicated.', 'mailpoet'), workflow.name),
|
sprintf(__('Automation "%s" was duplicated.', 'mailpoet'), automation.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'ADD_WORKFLOW',
|
type: 'ADD_AUTOMATION',
|
||||||
workflow: data.data,
|
automation: data.data,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* trashWorkflow(workflow: Workflow) {
|
export function* trashAutomation(automation: Automation) {
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
status: WorkflowStatus.TRASH,
|
status: AutomationStatus.TRASH,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const message = __('1 automation moved to the Trash.', 'mailpoet');
|
const message = __('1 automation moved to the Trash.', 'mailpoet');
|
||||||
void createSuccessNotice(message, {
|
void createSuccessNotice(message, {
|
||||||
id: `workflow-trashed-${workflow.id}`,
|
id: `automation-trashed-${automation.id}`,
|
||||||
__unstableHTML: (
|
__unstableHTML: (
|
||||||
<p>
|
<p>
|
||||||
{message}{' '}
|
{message}{' '}
|
||||||
<UndoTrashButton workflow={workflow} previousStatus={workflow.status} />
|
<UndoTrashButton
|
||||||
|
automation={automation}
|
||||||
|
previousStatus={automation.status}
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'UPDATE_WORKFLOW',
|
type: 'UPDATE_AUTOMATION',
|
||||||
workflow: data.data,
|
automation: data.data,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* restoreWorkflow(workflow: Workflow, status: WorkflowStatus) {
|
export function* restoreAutomation(
|
||||||
|
automation: Automation,
|
||||||
|
status: AutomationStatus,
|
||||||
|
) {
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
status,
|
status,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
void removeNotice(`workflow-trashed-${workflow.id}`);
|
void removeNotice(`automation-trashed-${automation.id}`);
|
||||||
|
|
||||||
const message = __('1 automation restored from the Trash.', 'mailpoet');
|
const message = __('1 automation restored from the Trash.', 'mailpoet');
|
||||||
void createSuccessNotice(message, {
|
void createSuccessNotice(message, {
|
||||||
__unstableHTML: (
|
__unstableHTML: (
|
||||||
<p>
|
<p>
|
||||||
{message}{' '}
|
{message}{' '}
|
||||||
<EditWorkflow
|
<EditAutomation
|
||||||
workflow={workflow}
|
automation={automation}
|
||||||
label={__('Edit automation', 'mailpoet')}
|
label={__('Edit automation', 'mailpoet')}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
@ -93,14 +99,14 @@ export function* restoreWorkflow(workflow: Workflow, status: WorkflowStatus) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'UPDATE_WORKFLOW',
|
type: 'UPDATE_AUTOMATION',
|
||||||
workflow: data.data,
|
automation: data.data,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* deleteWorkflow(workflow: Workflow) {
|
export function* deleteAutomation(automation: Automation) {
|
||||||
yield apiFetch({
|
yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -109,7 +115,7 @@ export function* deleteWorkflow(workflow: Workflow) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'DELETE_WORKFLOW',
|
type: 'DELETE_AUTOMATION',
|
||||||
workflow,
|
automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { State } from './types';
|
import { State } from './types';
|
||||||
|
|
||||||
export const getInitialState = (): State => ({
|
export const getInitialState = (): State => ({
|
||||||
workflows: undefined,
|
automations: undefined,
|
||||||
});
|
});
|
||||||
|
@ -1,31 +1,33 @@
|
|||||||
import { Action } from '@wordpress/data';
|
import { Action } from '@wordpress/data';
|
||||||
import { State } from './types';
|
import { State } from './types';
|
||||||
import { Workflow } from '../workflow';
|
import { Automation } from '../automation';
|
||||||
|
|
||||||
export function reducer(state: State, action: Action): State {
|
export function reducer(state: State, action: Action): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'SET_WORKFLOWS':
|
case 'SET_AUTOMATIONS':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflows: action.workflows,
|
automations: action.automations,
|
||||||
};
|
};
|
||||||
case 'ADD_WORKFLOW':
|
case 'ADD_AUTOMATION':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflows: [action.workflow, ...state.workflows],
|
automations: [action.automation, ...state.automations],
|
||||||
};
|
};
|
||||||
case 'UPDATE_WORKFLOW':
|
case 'UPDATE_AUTOMATION':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflows: state.workflows.map((workflow: Workflow) =>
|
automations: state.automations.map((automation: Automation) =>
|
||||||
workflow.id === action.workflow.id ? action.workflow : workflow,
|
automation.id === action.automation.id
|
||||||
|
? action.automation
|
||||||
|
: automation,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
case 'DELETE_WORKFLOW':
|
case 'DELETE_AUTOMATION':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflows: state.workflows.filter(
|
automations: state.automations.filter(
|
||||||
(workflow: Workflow) => workflow.id !== action.workflow.id,
|
(automation: Automation) => automation.id !== action.automation.id,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { State } from './types';
|
import { State } from './types';
|
||||||
import { Workflow } from '../workflow';
|
import { Automation } from '../automation';
|
||||||
import { workflowCount } from '../../config';
|
import { automationCount } from '../../config';
|
||||||
|
|
||||||
export function getWorkflows(state: State): Workflow[] {
|
export function getAutomations(state: State): Automation[] {
|
||||||
return state.workflows;
|
return state.automations;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWorkflowCount(state: State): number {
|
export function getAutomationCount(state: State): number {
|
||||||
return state.workflows ? state.workflows.length : workflowCount;
|
return state.automations ? state.automations.length : automationCount;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Workflow } from '../workflow';
|
import { Automation } from '../automation';
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
workflows?: Workflow[];
|
automations?: Automation[];
|
||||||
};
|
};
|
||||||
|
@ -15,13 +15,13 @@ export function BuildYourOwnSection(): JSX.Element {
|
|||||||
image: `${MailPoet.cdnUrl}automation/sections/start-with-a-trigger.png`,
|
image: `${MailPoet.cdnUrl}automation/sections/start-with-a-trigger.png`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
slug: 'customize-your-workflow',
|
slug: 'customize-your-automation',
|
||||||
title: __('Customize your automation', 'mailpoet'),
|
title: __('Customize your automation', 'mailpoet'),
|
||||||
text: __(
|
text: __(
|
||||||
'Choose steps and create a custom journey to best suit your needs.',
|
'Choose steps and create a custom journey to best suit your needs.',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
),
|
),
|
||||||
image: `${MailPoet.cdnUrl}automation/sections/customize-your-workflow.png`,
|
image: `${MailPoet.cdnUrl}automation/sections/customize-your-automation.png`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
slug: 'design-your-email',
|
slug: 'design-your-email',
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Button } from '@wordpress/components';
|
import { Button } from '@wordpress/components';
|
||||||
import { MailPoet } from '../../mailpoet';
|
import { MailPoet } from '../../mailpoet';
|
||||||
import { workflowTemplates } from '../templates/config';
|
import { automationTemplates } from '../templates/config';
|
||||||
import { TemplateListItem } from '../templates/components/template-list-item';
|
import { TemplateListItem } from '../templates/components/template-list-item';
|
||||||
|
|
||||||
export function TemplatesSection(): JSX.Element {
|
export function TemplatesSection(): JSX.Element {
|
||||||
const templates = workflowTemplates.slice(0, 3);
|
const templates = automationTemplates.slice(0, 3);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="mailpoet-automation-section">
|
<section className="mailpoet-automation-section">
|
||||||
|
@ -2,7 +2,7 @@ import { useState } from 'react';
|
|||||||
import { Button } from '@wordpress/components';
|
import { Button } from '@wordpress/components';
|
||||||
import { addQueryArgs } from '@wordpress/url';
|
import { addQueryArgs } from '@wordpress/url';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { WorkflowTemplate } from '../config';
|
import { AutomationTemplate } from '../config';
|
||||||
import { useMutation } from '../../api';
|
import { useMutation } from '../../api';
|
||||||
import { MailPoet } from '../../../mailpoet';
|
import { MailPoet } from '../../../mailpoet';
|
||||||
import { Notice } from '../../../notices/notice';
|
import { Notice } from '../../../notices/notice';
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
} from '../../../common/premium_modal';
|
} from '../../../common/premium_modal';
|
||||||
|
|
||||||
type TemplateListItemProps = {
|
type TemplateListItemProps = {
|
||||||
template: WorkflowTemplate;
|
template: AutomationTemplate;
|
||||||
heading?: 'h2' | 'h3';
|
heading?: 'h2' | 'h3';
|
||||||
};
|
};
|
||||||
export function TemplateListItem({
|
export function TemplateListItem({
|
||||||
@ -20,8 +20,8 @@ export function TemplateListItem({
|
|||||||
heading,
|
heading,
|
||||||
}: TemplateListItemProps): JSX.Element {
|
}: TemplateListItemProps): JSX.Element {
|
||||||
const [showPremium, setShowPremium] = useState(false);
|
const [showPremium, setShowPremium] = useState(false);
|
||||||
const [createWorkflowFromTemplate, { loading, error, data }] = useMutation(
|
const [createAutomationFromTemplate, { loading, error, data }] = useMutation(
|
||||||
'workflows/create-from-template',
|
'automations/create-from-template',
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@ -32,7 +32,7 @@ export function TemplateListItem({
|
|||||||
|
|
||||||
if (!error && data) {
|
if (!error && data) {
|
||||||
MailPoet.trackEvent('Automations > Template selected', {
|
MailPoet.trackEvent('Automations > Template selected', {
|
||||||
'Workflow slug': template.slug,
|
'Automation slug': template.slug,
|
||||||
});
|
});
|
||||||
window.location.href = addQueryArgs(MailPoet.urls.automationEditor, {
|
window.location.href = addQueryArgs(MailPoet.urls.automationEditor, {
|
||||||
id: data.data.id,
|
id: data.data.id,
|
||||||
@ -66,7 +66,7 @@ export function TemplateListItem({
|
|||||||
setShowPremium(true);
|
setShowPremium(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void createWorkflowFromTemplate();
|
void createAutomationFromTemplate();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="badge">
|
<div className="badge">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export type WorkflowTemplate = {
|
export type AutomationTemplate = {
|
||||||
slug: string;
|
slug: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
@ -7,8 +7,8 @@ export type WorkflowTemplate = {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
mailpoet_automation_templates: WorkflowTemplate[];
|
mailpoet_automation_templates: AutomationTemplate[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const workflowTemplates = window.mailpoet_automation_templates;
|
export const automationTemplates = window.mailpoet_automation_templates;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Flex } from '@wordpress/components';
|
import { Flex } from '@wordpress/components';
|
||||||
import { workflowTemplates } from './config';
|
import { automationTemplates } from './config';
|
||||||
import { TemplateListItem } from './components/template-list-item';
|
import { TemplateListItem } from './components/template-list-item';
|
||||||
import { initializeApi } from '../api';
|
import { initializeApi } from '../api';
|
||||||
import { registerTranslations } from '../i18n';
|
import { registerTranslations } from '../i18n';
|
||||||
@ -23,7 +23,7 @@ function Templates(): JSX.Element {
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<ul className="mailpoet-automation-templates">
|
<ul className="mailpoet-automation-templates">
|
||||||
{workflowTemplates.map((template) => (
|
{automationTemplates.map((template) => (
|
||||||
<TemplateListItem key={template.slug} template={template} />
|
<TemplateListItem key={template.slug} template={template} />
|
||||||
))}
|
))}
|
||||||
<FromScratchListItem />
|
<FromScratchListItem />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { useMutation } from './api';
|
import { useMutation } from './api';
|
||||||
import { Step, Workflow } from './editor/components/workflow/types';
|
import { Step, Automation } from './editor/components/automation/types';
|
||||||
|
|
||||||
export const createRootStep = (): Step => ({
|
export const createRootStep = (): Step => ({
|
||||||
id: 'root',
|
id: 'root',
|
||||||
@ -10,15 +10,15 @@ export const createRootStep = (): Step => ({
|
|||||||
next_steps: [],
|
next_steps: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const createWorkflow = (): Partial<Workflow> => ({
|
const createAutomation = (): Partial<Automation> => ({
|
||||||
name: 'Empty workflow',
|
name: 'Empty automation',
|
||||||
steps: {
|
steps: {
|
||||||
root: createRootStep(),
|
root: createRootStep(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function CreateEmptyWorkflowButton(): JSX.Element {
|
export function CreateEmptyAutomationButton(): JSX.Element {
|
||||||
const [createSchema, { loading, error }] = useMutation('workflows', {
|
const [createSchema, { loading, error }] = useMutation('automations', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -29,13 +29,13 @@ export function CreateEmptyWorkflowButton(): JSX.Element {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await createSchema({
|
await createSchema({
|
||||||
body: JSON.stringify(createWorkflow()),
|
body: JSON.stringify(createAutomation()),
|
||||||
});
|
});
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}}
|
}}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
Create empty workflow (premium required)
|
Create empty automation (premium required)
|
||||||
</button>
|
</button>
|
||||||
{error && (
|
{error && (
|
||||||
<div>{error?.data?.message ?? 'An unknown error occurred'}</div>
|
<div>{error?.data?.message ?? 'An unknown error occurred'}</div>
|
||||||
@ -49,12 +49,12 @@ type TemplateButtonProps = {
|
|||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CreateWorkflowFromTemplateButton({
|
export function CreateAutomationFromTemplateButton({
|
||||||
slug,
|
slug,
|
||||||
children,
|
children,
|
||||||
}: TemplateButtonProps): JSX.Element {
|
}: TemplateButtonProps): JSX.Element {
|
||||||
const [createWorkflowFromTemplate, { loading, error }] = useMutation(
|
const [createAutomationFromTemplate, { loading, error }] = useMutation(
|
||||||
'workflows/create-from-template',
|
'automations/create-from-template',
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@ -69,7 +69,7 @@ export function CreateWorkflowFromTemplateButton({
|
|||||||
className="button button-primary"
|
className="button button-primary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await createWorkflowFromTemplate();
|
await createAutomationFromTemplate();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}}
|
}}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
@ -7,7 +7,7 @@ import { Dispatch } from 'react';
|
|||||||
import { DropdownMenu } from '@wordpress/components';
|
import { DropdownMenu } from '@wordpress/components';
|
||||||
import { StoreConfig } from '@wordpress/data';
|
import { StoreConfig } from '@wordpress/data';
|
||||||
import { Item } from '../editor/components/inserter/item';
|
import { Item } from '../editor/components/inserter/item';
|
||||||
import { Step } from '../editor/components/workflow/types';
|
import { Step } from '../editor/components/automation/types';
|
||||||
import { State } from '../editor/store/types';
|
import { State } from '../editor/store/types';
|
||||||
|
|
||||||
export type MoreControlType = {
|
export type MoreControlType = {
|
||||||
@ -20,17 +20,17 @@ export type MoreControlType = {
|
|||||||
* APPLICATION HOOKS
|
* APPLICATION HOOKS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// mailpoet.automation.workflow.step.more-controls
|
// mailpoet.automation.automation.step.more-controls
|
||||||
// mailpoet.automation.hero.actions
|
// mailpoet.automation.hero.actions
|
||||||
export type StepMoreControlsType = Record<string, MoreControlType>;
|
export type StepMoreControlsType = Record<string, MoreControlType>;
|
||||||
|
|
||||||
// mailpoet.automation.workflow.add_step_callback
|
// mailpoet.automation.automation.add_step_callback
|
||||||
export type AddStepCallbackType = (item?: Item) => void;
|
export type AddStepCallbackType = (item?: Item) => void;
|
||||||
|
|
||||||
// mailpoet.automation.workflow.render_step
|
// mailpoet.automation.automation.render_step
|
||||||
export type RenderStepType = (step: Step) => JSX.Element;
|
export type RenderStepType = (step: Step) => JSX.Element;
|
||||||
|
|
||||||
// mailpoet.automation.workflow.render_step_separator
|
// mailpoet.automation.automation.render_step_separator
|
||||||
export type RenderStepSeparatorType = (step: Step) => JSX.Element;
|
export type RenderStepSeparatorType = (step: Step) => JSX.Element;
|
||||||
|
|
||||||
// mailpoet.automation.editor.create_store
|
// mailpoet.automation.editor.create_store
|
||||||
|
@ -122,7 +122,7 @@ Module.SaveView = Marionette.View.extend({
|
|||||||
'click .mailpoet_save_activate_wc_customizer_button':
|
'click .mailpoet_save_activate_wc_customizer_button':
|
||||||
'activateWooCommerceCustomizer',
|
'activateWooCommerceCustomizer',
|
||||||
/* Automation email */
|
/* Automation email */
|
||||||
'click .mailpoet_save_go_to_workflow': 'saveAndGoToWorkflow',
|
'click .mailpoet_save_go_to_automation': 'saveAndGoToAutomation',
|
||||||
'click .mailpoet_show_preview': 'showPreview',
|
'click .mailpoet_show_preview': 'showPreview',
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -316,13 +316,13 @@ Module.SaveView = Marionette.View.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
saveAndGoToWorkflow: function () {
|
saveAndGoToAutomation: function () {
|
||||||
this.hideSaveOptions();
|
this.hideSaveOptions();
|
||||||
Module._cancelAutosave();
|
Module._cancelAutosave();
|
||||||
Module.save().done(function () {
|
Module.save().done(function () {
|
||||||
const newsletter = App.getNewsletter();
|
const newsletter = App.getNewsletter();
|
||||||
const workflowId = newsletter.get('options').get('workflowId');
|
const automationId = newsletter.get('options').get('automationId');
|
||||||
const goToUrl = `admin.php?page=mailpoet-automation-editor&id=${workflowId}`;
|
const goToUrl = `admin.php?page=mailpoet-automation-editor&id=${automationId}`;
|
||||||
window.location.href = goToUrl;
|
window.location.href = goToUrl;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -18,8 +18,8 @@ const renderHeading = (newsletterType, newsletterOptions) => {
|
|||||||
window.location = `admin.php?page=mailpoet-newsletters`;
|
window.location = `admin.php?page=mailpoet-newsletters`;
|
||||||
};
|
};
|
||||||
if (newsletterType === 'automation') {
|
if (newsletterType === 'automation') {
|
||||||
const workflowId = newsletterOptions.workflowId;
|
const automationId = newsletterOptions.automationId;
|
||||||
const goToUrl = `admin.php?page=mailpoet-automation-editor&id=${workflowId}`;
|
const goToUrl = `admin.php?page=mailpoet-automation-editor&id=${automationId}`;
|
||||||
onLogoClick = () => {
|
onLogoClick = () => {
|
||||||
window.location = goToUrl;
|
window.location = goToUrl;
|
||||||
};
|
};
|
||||||
@ -27,7 +27,7 @@ const renderHeading = (newsletterType, newsletterOptions) => {
|
|||||||
const onClickPreview = () =>
|
const onClickPreview = () =>
|
||||||
document.querySelector('.mailpoet_show_preview').click();
|
document.querySelector('.mailpoet_show_preview').click();
|
||||||
const onClickSave = () =>
|
const onClickSave = () =>
|
||||||
document.querySelector('.mailpoet_save_go_to_workflow').click();
|
document.querySelector('.mailpoet_save_go_to_automation').click();
|
||||||
buttons = (
|
buttons = (
|
||||||
<>
|
<>
|
||||||
<input
|
<input
|
||||||
|
@ -203,11 +203,11 @@ class NewsletterSendComponent extends Component {
|
|||||||
: null;
|
: null;
|
||||||
const item = response.data;
|
const item = response.data;
|
||||||
// Automation type emails should redirect
|
// Automation type emails should redirect
|
||||||
// to an associated workflow from the send page
|
// to an associated automation from the send page
|
||||||
if (item.type === 'automation') {
|
if (item.type === 'automation') {
|
||||||
const workflowId = item.options?.workflowId;
|
const automationId = item.options?.automationId;
|
||||||
const goToUrl = workflowId
|
const goToUrl = automationId
|
||||||
? `admin.php?page=mailpoet-automation-editor&id=${workflowId}`
|
? `admin.php?page=mailpoet-automation-editor&id=${automationId}`
|
||||||
: '/new';
|
: '/new';
|
||||||
return this.setState(
|
return this.setState(
|
||||||
{
|
{
|
||||||
|
@ -317,9 +317,9 @@ class NewsletterTemplates extends Component {
|
|||||||
let buttons = null;
|
let buttons = null;
|
||||||
let onClick;
|
let onClick;
|
||||||
if (this.state.emailType === 'automation') {
|
if (this.state.emailType === 'automation') {
|
||||||
const workflowId = this.state.emailOptions?.workflowId;
|
const automationId = this.state.emailOptions?.automationId;
|
||||||
const goToUrl = workflowId
|
const goToUrl = automationId
|
||||||
? `admin.php?page=mailpoet-automation-editor&id=${workflowId}`
|
? `admin.php?page=mailpoet-automation-editor&id=${automationId}`
|
||||||
: 'admin.php?page=mailpoet-automation';
|
: 'admin.php?page=mailpoet-automation';
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
window.location = goToUrl;
|
window.location = goToUrl;
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
namespace MailPoet\AdminPages\Pages;
|
namespace MailPoet\AdminPages\Pages;
|
||||||
|
|
||||||
use MailPoet\AdminPages\PageRenderer;
|
use MailPoet\AdminPages\PageRenderer;
|
||||||
use MailPoet\Automation\Engine\Data\WorkflowTemplate;
|
use MailPoet\Automation\Engine\Data\AutomationTemplate;
|
||||||
use MailPoet\Automation\Engine\Migrations\Migrator;
|
use MailPoet\Automation\Engine\Migrations\Migrator;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowTemplateStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationTemplateStorage;
|
||||||
use MailPoet\Form\AssetsController;
|
use MailPoet\Form\AssetsController;
|
||||||
use MailPoet\WP\Functions as WPFunctions;
|
use MailPoet\WP\Functions as WPFunctions;
|
||||||
|
|
||||||
@ -23,10 +23,10 @@ class Automation {
|
|||||||
/** @var WPFunctions */
|
/** @var WPFunctions */
|
||||||
private $wp;
|
private $wp;
|
||||||
|
|
||||||
/** @var WorkflowStorage */
|
/** @var AutomationStorage */
|
||||||
private $workflowStorage;
|
private $automationStorage;
|
||||||
|
|
||||||
/** @var WorkflowTemplateStorage */
|
/** @var AutomationTemplateStorage */
|
||||||
private $templateStorage;
|
private $templateStorage;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@ -34,14 +34,14 @@ class Automation {
|
|||||||
Migrator $migrator,
|
Migrator $migrator,
|
||||||
PageRenderer $pageRenderer,
|
PageRenderer $pageRenderer,
|
||||||
WPFunctions $wp,
|
WPFunctions $wp,
|
||||||
WorkflowStorage $workflowStorage,
|
AutomationStorage $automationStorage,
|
||||||
WorkflowTemplateStorage $templateStorage
|
AutomationTemplateStorage $templateStorage
|
||||||
) {
|
) {
|
||||||
$this->assetsController = $assetsController;
|
$this->assetsController = $assetsController;
|
||||||
$this->migrator = $migrator;
|
$this->migrator = $migrator;
|
||||||
$this->pageRenderer = $pageRenderer;
|
$this->pageRenderer = $pageRenderer;
|
||||||
$this->wp = $wp;
|
$this->wp = $wp;
|
||||||
$this->workflowStorage = $workflowStorage;
|
$this->automationStorage = $automationStorage;
|
||||||
$this->templateStorage = $templateStorage;
|
$this->templateStorage = $templateStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,9 +56,9 @@ class Automation {
|
|||||||
'root' => rtrim($this->wp->escUrlRaw($this->wp->restUrl()), '/'),
|
'root' => rtrim($this->wp->escUrlRaw($this->wp->restUrl()), '/'),
|
||||||
'nonce' => $this->wp->wpCreateNonce('wp_rest'),
|
'nonce' => $this->wp->wpCreateNonce('wp_rest'),
|
||||||
],
|
],
|
||||||
'workflowCount' => $this->workflowStorage->getWorkflowCount(),
|
'automationCount' => $this->automationStorage->getAutomationCount(),
|
||||||
'templates' => array_map(
|
'templates' => array_map(
|
||||||
function(WorkflowTemplate $template): array {
|
function(AutomationTemplate $template): array {
|
||||||
return $template->toArray();
|
return $template->toArray();
|
||||||
},
|
},
|
||||||
$this->templateStorage->getTemplates()
|
$this->templateStorage->getTemplates()
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
namespace MailPoet\AdminPages\Pages;
|
namespace MailPoet\AdminPages\Pages;
|
||||||
|
|
||||||
use MailPoet\AdminPages\PageRenderer;
|
use MailPoet\AdminPages\PageRenderer;
|
||||||
use MailPoet\Automation\Engine\Data\Workflow;
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
use MailPoet\Automation\Engine\Hooks;
|
use MailPoet\Automation\Engine\Hooks;
|
||||||
use MailPoet\Automation\Engine\Mappers\WorkflowMapper;
|
use MailPoet\Automation\Engine\Mappers\AutomationMapper;
|
||||||
use MailPoet\Automation\Engine\Registry;
|
use MailPoet\Automation\Engine\Registry;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
use MailPoet\Form\AssetsController;
|
use MailPoet\Form\AssetsController;
|
||||||
use MailPoet\Segments\SegmentsRepository;
|
use MailPoet\Segments\SegmentsRepository;
|
||||||
use MailPoet\WP\Functions as WPFunctions;
|
use MailPoet\WP\Functions as WPFunctions;
|
||||||
@ -17,11 +17,11 @@ class AutomationEditor {
|
|||||||
/** @var AssetsController */
|
/** @var AssetsController */
|
||||||
private $assetsController;
|
private $assetsController;
|
||||||
|
|
||||||
/** @var WorkflowMapper */
|
/** @var AutomationMapper */
|
||||||
private $workflowMapper;
|
private $automationMapper;
|
||||||
|
|
||||||
/** @var WorkflowStorage */
|
/** @var AutomationStorage */
|
||||||
private $workflowStorage;
|
private $automationStorage;
|
||||||
|
|
||||||
/** @var PageRenderer */
|
/** @var PageRenderer */
|
||||||
private $pageRenderer;
|
private $pageRenderer;
|
||||||
@ -37,16 +37,16 @@ class AutomationEditor {
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AssetsController $assetsController,
|
AssetsController $assetsController,
|
||||||
WorkflowMapper $workflowMapper,
|
AutomationMapper $automationMapper,
|
||||||
WorkflowStorage $workflowStorage,
|
AutomationStorage $automationStorage,
|
||||||
PageRenderer $pageRenderer,
|
PageRenderer $pageRenderer,
|
||||||
Registry $registry,
|
Registry $registry,
|
||||||
SegmentsRepository $segmentsRepository,
|
SegmentsRepository $segmentsRepository,
|
||||||
WPFunctions $wp
|
WPFunctions $wp
|
||||||
) {
|
) {
|
||||||
$this->assetsController = $assetsController;
|
$this->assetsController = $assetsController;
|
||||||
$this->workflowMapper = $workflowMapper;
|
$this->automationMapper = $automationMapper;
|
||||||
$this->workflowStorage = $workflowStorage;
|
$this->automationStorage = $automationStorage;
|
||||||
$this->pageRenderer = $pageRenderer;
|
$this->pageRenderer = $pageRenderer;
|
||||||
$this->registry = $registry;
|
$this->registry = $registry;
|
||||||
$this->segmentsRepository = $segmentsRepository;
|
$this->segmentsRepository = $segmentsRepository;
|
||||||
@ -60,8 +60,8 @@ class AutomationEditor {
|
|||||||
|
|
||||||
$this->wp->doAction(Hooks::EDITOR_BEFORE_LOAD, (int)$id);
|
$this->wp->doAction(Hooks::EDITOR_BEFORE_LOAD, (int)$id);
|
||||||
|
|
||||||
$workflow = $id ? $this->workflowStorage->getWorkflow($id) : null;
|
$automation = $id ? $this->automationStorage->getAutomation($id) : null;
|
||||||
if (!$workflow) {
|
if (!$automation) {
|
||||||
$notice = new WPNotice(
|
$notice = new WPNotice(
|
||||||
WPNotice::TYPE_ERROR,
|
WPNotice::TYPE_ERROR,
|
||||||
__('Automation not found.', 'mailpoet')
|
__('Automation not found.', 'mailpoet')
|
||||||
@ -71,7 +71,7 @@ class AutomationEditor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($workflow->getStatus() === Workflow::STATUS_TRASH) {
|
if ($automation->getStatus() === Automation::STATUS_TRASH) {
|
||||||
$this->wp->wpSafeRedirect($this->wp->adminUrl('admin.php?page=mailpoet-automation&status=trash'));
|
$this->wp->wpSafeRedirect($this->wp->adminUrl('admin.php?page=mailpoet-automation&status=trash'));
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ class AutomationEditor {
|
|||||||
$roles = new \WP_Roles();
|
$roles = new \WP_Roles();
|
||||||
$this->pageRenderer->displayPage('automation/editor.html', [
|
$this->pageRenderer->displayPage('automation/editor.html', [
|
||||||
'context' => $this->buildContext(),
|
'context' => $this->buildContext(),
|
||||||
'workflow' => $this->workflowMapper->buildWorkflow($workflow),
|
'automation' => $this->automationMapper->buildAutomation($automation),
|
||||||
'sub_menu' => 'mailpoet-automation',
|
'sub_menu' => 'mailpoet-automation',
|
||||||
'api' => [
|
'api' => [
|
||||||
'root' => rtrim($this->wp->escUrlRaw($this->wp->restUrl()), '/'),
|
'root' => rtrim($this->wp->escUrlRaw($this->wp->restUrl()), '/'),
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
namespace MailPoet\AdminPages\Pages;
|
namespace MailPoet\AdminPages\Pages;
|
||||||
|
|
||||||
use MailPoet\AdminPages\PageRenderer;
|
use MailPoet\AdminPages\PageRenderer;
|
||||||
use MailPoet\Automation\Engine\Data\WorkflowTemplate;
|
use MailPoet\Automation\Engine\Data\AutomationTemplate;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowTemplateStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationTemplateStorage;
|
||||||
use MailPoet\Form\AssetsController;
|
use MailPoet\Form\AssetsController;
|
||||||
use MailPoet\WP\Functions as WPFunctions;
|
use MailPoet\WP\Functions as WPFunctions;
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ class AutomationTemplates {
|
|||||||
/** @var PageRenderer */
|
/** @var PageRenderer */
|
||||||
private $pageRenderer;
|
private $pageRenderer;
|
||||||
|
|
||||||
/** @var WorkflowTemplateStorage */
|
/** @var AutomationTemplateStorage */
|
||||||
private $templateStorage;
|
private $templateStorage;
|
||||||
|
|
||||||
/** @var WPFunctions */
|
/** @var WPFunctions */
|
||||||
@ -24,7 +24,7 @@ class AutomationTemplates {
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
AssetsController $assetsController,
|
AssetsController $assetsController,
|
||||||
PageRenderer $pageRenderer,
|
PageRenderer $pageRenderer,
|
||||||
WorkflowTemplateStorage $templateStorage,
|
AutomationTemplateStorage $templateStorage,
|
||||||
WPFunctions $wp
|
WPFunctions $wp
|
||||||
) {
|
) {
|
||||||
$this->assetsController = $assetsController;
|
$this->assetsController = $assetsController;
|
||||||
@ -45,7 +45,7 @@ class AutomationTemplates {
|
|||||||
'nonce' => $this->wp->wpCreateNonce('wp_rest'),
|
'nonce' => $this->wp->wpCreateNonce('wp_rest'),
|
||||||
],
|
],
|
||||||
'templates' => array_map(
|
'templates' => array_map(
|
||||||
function(WorkflowTemplate $template): array {
|
function(AutomationTemplate $template): array {
|
||||||
return $template->toArray();
|
return $template->toArray();
|
||||||
},
|
},
|
||||||
$this->templateStorage->getTemplates()
|
$this->templateStorage->getTemplates()
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace MailPoet\Analytics;
|
namespace MailPoet\Analytics;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
use MailPoet\Automation\Engine\Data\Step;
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
use MailPoet\Automation\Engine\Data\Workflow;
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
|
||||||
use MailPoet\Config\ServicesChecker;
|
use MailPoet\Config\ServicesChecker;
|
||||||
use MailPoet\Cron\CronTrigger;
|
use MailPoet\Cron\CronTrigger;
|
||||||
use MailPoet\Entities\DynamicSegmentFilterData;
|
use MailPoet\Entities\DynamicSegmentFilterData;
|
||||||
@ -77,8 +77,8 @@ class Reporter {
|
|||||||
/** @var FeaturesController */
|
/** @var FeaturesController */
|
||||||
private $featuresController;
|
private $featuresController;
|
||||||
|
|
||||||
/** @var WorkflowStorage */
|
/** @var AutomationStorage */
|
||||||
private $workflowStorage;
|
private $automationStorage;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
NewslettersRepository $newslettersRepository,
|
NewslettersRepository $newslettersRepository,
|
||||||
@ -93,7 +93,7 @@ class Reporter {
|
|||||||
TrackingConfig $trackingConfig,
|
TrackingConfig $trackingConfig,
|
||||||
SubscriberListingRepository $subscriberListingRepository,
|
SubscriberListingRepository $subscriberListingRepository,
|
||||||
FeaturesController $featuresController,
|
FeaturesController $featuresController,
|
||||||
WorkflowStorage $workflowStorage
|
AutomationStorage $automationStorage
|
||||||
) {
|
) {
|
||||||
$this->newslettersRepository = $newslettersRepository;
|
$this->newslettersRepository = $newslettersRepository;
|
||||||
$this->segmentsRepository = $segmentsRepository;
|
$this->segmentsRepository = $segmentsRepository;
|
||||||
@ -107,7 +107,7 @@ class Reporter {
|
|||||||
$this->trackingConfig = $trackingConfig;
|
$this->trackingConfig = $trackingConfig;
|
||||||
$this->subscriberListingRepository = $subscriberListingRepository;
|
$this->subscriberListingRepository = $subscriberListingRepository;
|
||||||
$this->featuresController = $featuresController;
|
$this->featuresController = $featuresController;
|
||||||
$this->workflowStorage = $workflowStorage;
|
$this->automationStorage = $automationStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getData() {
|
public function getData() {
|
||||||
@ -235,39 +235,39 @@ class Reporter {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$workflows = $this->workflowStorage->getWorkflows();
|
$automations = $this->automationStorage->getAutomations();
|
||||||
$activeWorkflows = array_filter(
|
$activeAutomations = array_filter(
|
||||||
$workflows,
|
$automations,
|
||||||
function(Workflow $workflow): bool {
|
function(Automation $automation): bool {
|
||||||
return $workflow->getStatus() === Workflow::STATUS_ACTIVE;
|
return $automation->getStatus() === Automation::STATUS_ACTIVE;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
$activeWorkflowCount = count($activeWorkflows);
|
$activeAutomationCount = count($activeAutomations);
|
||||||
$draftWorkflows = array_filter(
|
$draftAutomations = array_filter(
|
||||||
$workflows,
|
$automations,
|
||||||
function(Workflow $workflow): bool {
|
function(Automation $automation): bool {
|
||||||
return $workflow->getStatus() === Workflow::STATUS_DRAFT;
|
return $automation->getStatus() === Automation::STATUS_DRAFT;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
$workflowsWithWordPressUserSubscribesTrigger = array_filter(
|
$automationsWithWordPressUserSubscribesTrigger = array_filter(
|
||||||
$activeWorkflows,
|
$activeAutomations,
|
||||||
function(Workflow $workflow): bool {
|
function(Automation $automation): bool {
|
||||||
return $workflow->getTrigger('mailpoet:wp-user-registered') !== null;
|
return $automation->getTrigger('mailpoet:wp-user-registered') !== null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
$workflowsWithSomeoneSubscribesTrigger = array_filter(
|
$automationsWithSomeoneSubscribesTrigger = array_filter(
|
||||||
$activeWorkflows,
|
$activeAutomations,
|
||||||
function(Workflow $workflow): bool {
|
function(Automation $automation): bool {
|
||||||
return $workflow->getTrigger('mailpoet:someone-subscribes') !== null;
|
return $automation->getTrigger('mailpoet:someone-subscribes') !== null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
$totalSteps = 0;
|
$totalSteps = 0;
|
||||||
$minSteps = null;
|
$minSteps = null;
|
||||||
$maxSteps = 0;
|
$maxSteps = 0;
|
||||||
foreach ($activeWorkflows as $workflow) {
|
foreach ($activeAutomations as $automation) {
|
||||||
$steps = array_filter(
|
$steps = array_filter(
|
||||||
$workflow->getSteps(),
|
$automation->getSteps(),
|
||||||
function(Step $step): bool {
|
function(Step $step): bool {
|
||||||
return $step->getType() === Step::TYPE_ACTION;
|
return $step->getType() === Step::TYPE_ACTION;
|
||||||
}
|
}
|
||||||
@ -277,16 +277,16 @@ class Reporter {
|
|||||||
$maxSteps = max($maxSteps, $stepCount);
|
$maxSteps = max($maxSteps, $stepCount);
|
||||||
$totalSteps += $stepCount;
|
$totalSteps += $stepCount;
|
||||||
}
|
}
|
||||||
$averageSteps = $activeWorkflowCount > 0 ? $totalSteps / $activeWorkflowCount : 0;
|
$averageSteps = $activeAutomationCount > 0 ? $totalSteps / $activeAutomationCount : 0;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'Automation > Number of active workflows' => $activeWorkflowCount,
|
'Automation > Number of active automations' => $activeAutomationCount,
|
||||||
'Automation > Number of draft workflows' => count($draftWorkflows),
|
'Automation > Number of draft automations' => count($draftAutomations),
|
||||||
'Automation > Number of "WordPress user registers" active workflows' => count($workflowsWithWordPressUserSubscribesTrigger),
|
'Automation > Number of "WordPress user registers" active automations' => count($automationsWithWordPressUserSubscribesTrigger),
|
||||||
'Automation > Number of "Someone subscribes" active workflows ' => count($workflowsWithSomeoneSubscribesTrigger),
|
'Automation > Number of "Someone subscribes" active automations ' => count($automationsWithSomeoneSubscribesTrigger),
|
||||||
'Automation > Number of steps in shortest active workflow' => $minSteps,
|
'Automation > Number of steps in shortest active automation' => $minSteps,
|
||||||
'Automation > Number of steps in longest active workflow' => $maxSteps,
|
'Automation > Number of steps in longest active automation' => $maxSteps,
|
||||||
'Automation > Average number of steps in active workflows' => $averageSteps,
|
'Automation > Average number of steps in active automations' => $averageSteps,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@ use MailPoet\API\REST\API as MailPoetApi;
|
|||||||
use MailPoet\Automation\Engine\Hooks;
|
use MailPoet\Automation\Engine\Hooks;
|
||||||
use MailPoet\Automation\Engine\WordPress;
|
use MailPoet\Automation\Engine\WordPress;
|
||||||
|
|
||||||
class API {
|
class API extends MailPoetApi {
|
||||||
private const PREFIX = 'automation/';
|
|
||||||
|
|
||||||
/** @var MailPoetApi */
|
/** @var MailPoetApi */
|
||||||
private $api;
|
private $api;
|
||||||
|
|
||||||
@ -30,22 +28,22 @@ class API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function registerGetRoute(string $route, string $endpoint): void {
|
public function registerGetRoute(string $route, string $endpoint): void {
|
||||||
$this->api->registerGetRoute(self::PREFIX . $route, $endpoint);
|
$this->api->registerGetRoute($route, $endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerPostRoute(string $route, string $endpoint): void {
|
public function registerPostRoute(string $route, string $endpoint): void {
|
||||||
$this->api->registerPostRoute(self::PREFIX . $route, $endpoint);
|
$this->api->registerPostRoute($route, $endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerPutRoute(string $route, string $endpoint): void {
|
public function registerPutRoute(string $route, string $endpoint): void {
|
||||||
$this->api->registerPutRoute(self::PREFIX . $route, $endpoint);
|
$this->api->registerPutRoute($route, $endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerPatchRoute(string $route, string $endpoint): void {
|
public function registerPatchRoute(string $route, string $endpoint): void {
|
||||||
$this->api->registerPatchRoute(self::PREFIX . $route, $endpoint);
|
$this->api->registerPatchRoute($route, $endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerDeleteRoute(string $route, string $endpoint): void {
|
public function registerDeleteRoute(string $route, string $endpoint): void {
|
||||||
$this->api->registerDeleteRoute(self::PREFIX . $route, $endpoint);
|
$this->api->registerDeleteRoute($route, $endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Builder;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
|
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
||||||
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
|
use MailPoet\Automation\Engine\Storage\AutomationTemplateStorage;
|
||||||
|
use MailPoet\Automation\Engine\Validation\AutomationValidator;
|
||||||
|
|
||||||
|
class CreateAutomationFromTemplateController {
|
||||||
|
/** @var AutomationStorage */
|
||||||
|
private $storage;
|
||||||
|
|
||||||
|
/** @var AutomationTemplateStorage */
|
||||||
|
private $templateStorage;
|
||||||
|
|
||||||
|
/** @var AutomationValidator */
|
||||||
|
private $automationValidator;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
AutomationStorage $storage,
|
||||||
|
AutomationTemplateStorage $templateStorage,
|
||||||
|
AutomationValidator $automationValidator
|
||||||
|
) {
|
||||||
|
$this->storage = $storage;
|
||||||
|
$this->templateStorage = $templateStorage;
|
||||||
|
$this->automationValidator = $automationValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createAutomation(string $slug): Automation {
|
||||||
|
$template = $this->templateStorage->getTemplateBySlug($slug);
|
||||||
|
if (!$template) {
|
||||||
|
throw Exceptions::automationTemplateNotFound($slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
$automation = $template->getAutomation();
|
||||||
|
$this->automationValidator->validate($automation);
|
||||||
|
$automationId = $this->storage->createAutomation($automation);
|
||||||
|
$savedAutomation = $this->storage->getAutomation($automationId);
|
||||||
|
if (!$savedAutomation) {
|
||||||
|
throw new InvalidStateException('Automation not found.');
|
||||||
|
}
|
||||||
|
return $savedAutomation;
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
<?php declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Builder;
|
|
||||||
|
|
||||||
use MailPoet\Automation\Engine\Data\Workflow;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowTemplateStorage;
|
|
||||||
use MailPoet\Automation\Engine\Validation\WorkflowValidator;
|
|
||||||
|
|
||||||
class CreateWorkflowFromTemplateController {
|
|
||||||
/** @var WorkflowStorage */
|
|
||||||
private $storage;
|
|
||||||
|
|
||||||
/** @var WorkflowTemplateStorage */
|
|
||||||
private $templateStorage;
|
|
||||||
|
|
||||||
/** @var WorkflowValidator */
|
|
||||||
private $workflowValidator;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
WorkflowStorage $storage,
|
|
||||||
WorkflowTemplateStorage $templateStorage,
|
|
||||||
WorkflowValidator $workflowValidator
|
|
||||||
) {
|
|
||||||
$this->storage = $storage;
|
|
||||||
$this->templateStorage = $templateStorage;
|
|
||||||
$this->workflowValidator = $workflowValidator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createWorkflow(string $slug): Workflow {
|
|
||||||
$template = $this->templateStorage->getTemplateBySlug($slug);
|
|
||||||
if (!$template) {
|
|
||||||
throw Exceptions::workflowTemplateNotFound($slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
$workflow = $template->getWorkflow();
|
|
||||||
$this->workflowValidator->validate($workflow);
|
|
||||||
$workflowId = $this->storage->createWorkflow($workflow);
|
|
||||||
$savedWorkflow = $this->storage->getWorkflow($workflowId);
|
|
||||||
if (!$savedWorkflow) {
|
|
||||||
throw new InvalidStateException('Automation not found.');
|
|
||||||
}
|
|
||||||
return $savedWorkflow;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Builder;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
|
|
||||||
|
class DeleteAutomationController {
|
||||||
|
/** @var AutomationStorage */
|
||||||
|
private $automationStorage;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
AutomationStorage $automationStorage
|
||||||
|
) {
|
||||||
|
$this->automationStorage = $automationStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteAutomation(int $id): Automation {
|
||||||
|
$automation = $this->automationStorage->getAutomation($id);
|
||||||
|
if (!$automation) {
|
||||||
|
throw Exceptions::automationNotFound($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($automation->getStatus() !== Automation::STATUS_TRASH) {
|
||||||
|
throw Exceptions::automationNotTrashed($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->automationStorage->deleteAutomation($automation);
|
||||||
|
return $automation;
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
<?php declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Builder;
|
|
||||||
|
|
||||||
use MailPoet\Automation\Engine\Data\Workflow;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
|
||||||
|
|
||||||
class DeleteWorkflowController {
|
|
||||||
/** @var WorkflowStorage */
|
|
||||||
private $workflowStorage;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
WorkflowStorage $workflowStorage
|
|
||||||
) {
|
|
||||||
$this->workflowStorage = $workflowStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteWorkflow(int $id): Workflow {
|
|
||||||
$workflow = $this->workflowStorage->getWorkflow($id);
|
|
||||||
if (!$workflow) {
|
|
||||||
throw Exceptions::workflowNotFound($id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($workflow->getStatus() !== Workflow::STATUS_TRASH) {
|
|
||||||
throw Exceptions::workflowNotTrashed($id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->workflowStorage->deleteWorkflow($workflow);
|
|
||||||
return $workflow;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,55 +2,55 @@
|
|||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Builder;
|
namespace MailPoet\Automation\Engine\Builder;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
use MailPoet\Automation\Engine\Data\NextStep;
|
use MailPoet\Automation\Engine\Data\NextStep;
|
||||||
use MailPoet\Automation\Engine\Data\Step;
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
use MailPoet\Automation\Engine\Data\Workflow;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
use MailPoet\Automation\Engine\WordPress;
|
use MailPoet\Automation\Engine\WordPress;
|
||||||
use MailPoet\Util\Security;
|
use MailPoet\Util\Security;
|
||||||
|
|
||||||
class DuplicateWorkflowController {
|
class DuplicateAutomationController {
|
||||||
/** @var WordPress */
|
/** @var WordPress */
|
||||||
private $wordPress;
|
private $wordPress;
|
||||||
|
|
||||||
/** @var WorkflowStorage */
|
/** @var AutomationStorage */
|
||||||
private $workflowStorage;
|
private $automationStorage;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
WordPress $wordPress,
|
WordPress $wordPress,
|
||||||
WorkflowStorage $workflowStorage
|
AutomationStorage $automationStorage
|
||||||
) {
|
) {
|
||||||
$this->wordPress = $wordPress;
|
$this->wordPress = $wordPress;
|
||||||
$this->workflowStorage = $workflowStorage;
|
$this->automationStorage = $automationStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function duplicateWorkflow(int $id): Workflow {
|
public function duplicateAutomation(int $id): Automation {
|
||||||
$workflow = $this->workflowStorage->getWorkflow($id);
|
$automation = $this->automationStorage->getAutomation($id);
|
||||||
if (!$workflow) {
|
if (!$automation) {
|
||||||
throw Exceptions::workflowNotFound($id);
|
throw Exceptions::automationNotFound($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$duplicate = new Workflow(
|
$duplicate = new Automation(
|
||||||
$this->getName($workflow->getName()),
|
$this->getName($automation->getName()),
|
||||||
$this->getSteps($workflow->getSteps()),
|
$this->getSteps($automation->getSteps()),
|
||||||
$this->wordPress->wpGetCurrentUser()
|
$this->wordPress->wpGetCurrentUser()
|
||||||
);
|
);
|
||||||
$duplicate->setStatus(Workflow::STATUS_DRAFT);
|
$duplicate->setStatus(Automation::STATUS_DRAFT);
|
||||||
|
|
||||||
$workflowId = $this->workflowStorage->createWorkflow($duplicate);
|
$automationId = $this->automationStorage->createAutomation($duplicate);
|
||||||
$savedWorkflow = $this->workflowStorage->getWorkflow($workflowId);
|
$savedAutomation = $this->automationStorage->getAutomation($automationId);
|
||||||
if (!$savedWorkflow) {
|
if (!$savedAutomation) {
|
||||||
throw new InvalidStateException('Workflow not found.');
|
throw new InvalidStateException('Automation not found.');
|
||||||
}
|
}
|
||||||
return $savedWorkflow;
|
return $savedAutomation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getName(string $name): string {
|
private function getName(string $name): string {
|
||||||
// translators: %s is the original automation name.
|
// translators: %s is the original automation name.
|
||||||
$newName = sprintf(__('Copy of %s', 'mailpoet'), $name);
|
$newName = sprintf(__('Copy of %s', 'mailpoet'), $name);
|
||||||
$maxLength = $this->workflowStorage->getNameColumnLength();
|
$maxLength = $this->automationStorage->getNameColumnLength();
|
||||||
if (strlen($newName) > $maxLength) {
|
if (strlen($newName) > $maxLength) {
|
||||||
$append = '…';
|
$append = '…';
|
||||||
return substr($newName, 0, $maxLength - strlen($append)) . $append;
|
return substr($newName, 0, $maxLength - strlen($append)) . $append;
|
@ -0,0 +1,137 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Builder;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
|
use MailPoet\Automation\Engine\Exceptions\UnexpectedValueException;
|
||||||
|
use MailPoet\Automation\Engine\Hooks;
|
||||||
|
use MailPoet\Automation\Engine\Storage\AutomationStatisticsStorage;
|
||||||
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
|
use MailPoet\Automation\Engine\Validation\AutomationValidator;
|
||||||
|
|
||||||
|
class UpdateAutomationController {
|
||||||
|
/** @var Hooks */
|
||||||
|
private $hooks;
|
||||||
|
|
||||||
|
/** @var AutomationStorage */
|
||||||
|
private $storage;
|
||||||
|
|
||||||
|
/** @var AutomationStatisticsStorage */
|
||||||
|
private $statisticsStorage;
|
||||||
|
|
||||||
|
/** @var AutomationValidator */
|
||||||
|
private $automationValidator;
|
||||||
|
|
||||||
|
/** @var UpdateStepsController */
|
||||||
|
private $updateStepsController;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
Hooks $hooks,
|
||||||
|
AutomationStorage $storage,
|
||||||
|
AutomationStatisticsStorage $statisticsStorage,
|
||||||
|
AutomationValidator $automationValidator,
|
||||||
|
UpdateStepsController $updateStepsController
|
||||||
|
) {
|
||||||
|
$this->hooks = $hooks;
|
||||||
|
$this->storage = $storage;
|
||||||
|
$this->statisticsStorage = $statisticsStorage;
|
||||||
|
$this->automationValidator = $automationValidator;
|
||||||
|
$this->updateStepsController = $updateStepsController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateAutomation(int $id, array $data): Automation {
|
||||||
|
$automation = $this->storage->getAutomation($id);
|
||||||
|
if (!$automation) {
|
||||||
|
throw Exceptions::automationNotFound($id);
|
||||||
|
}
|
||||||
|
$this->validateIfAutomationCanBeUpdated($automation, $data);
|
||||||
|
|
||||||
|
if (array_key_exists('name', $data)) {
|
||||||
|
$automation->setName($data['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('status', $data)) {
|
||||||
|
$this->checkAutomationStatus($data['status']);
|
||||||
|
$automation->setStatus($data['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('steps', $data)) {
|
||||||
|
$this->validateAutomationSteps($automation, $data['steps']);
|
||||||
|
$this->updateStepsController->updateSteps($automation, $data['steps']);
|
||||||
|
foreach ($automation->getSteps() as $step) {
|
||||||
|
$this->hooks->doAutomationStepBeforeSave($step);
|
||||||
|
$this->hooks->doAutomationStepByKeyBeforeSave($step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->hooks->doAutomationBeforeSave($automation);
|
||||||
|
|
||||||
|
$this->automationValidator->validate($automation);
|
||||||
|
$this->storage->updateAutomation($automation);
|
||||||
|
|
||||||
|
$automation = $this->storage->getAutomation($id);
|
||||||
|
if (!$automation) {
|
||||||
|
throw Exceptions::automationNotFound($id);
|
||||||
|
}
|
||||||
|
return $automation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a temporary validation, see MAILPOET-4744
|
||||||
|
*/
|
||||||
|
private function validateIfAutomationCanBeUpdated(Automation $automation, array $data): void {
|
||||||
|
|
||||||
|
if (
|
||||||
|
!in_array(
|
||||||
|
$automation->getStatus(),
|
||||||
|
[
|
||||||
|
Automation::STATUS_ACTIVE,
|
||||||
|
Automation::STATUS_DEACTIVATING,
|
||||||
|
],
|
||||||
|
true
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$statistics = $this->statisticsStorage->getAutomationStats($automation->getId());
|
||||||
|
if ($statistics->getInProgress() === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($data['status']) || $data['status'] === $automation->getStatus()) {
|
||||||
|
throw Exceptions::automationHasActiveRuns($automation->getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkAutomationStatus(string $status): void {
|
||||||
|
if (!in_array($status, Automation::STATUS_ALL, true)) {
|
||||||
|
// translators: %s is the status.
|
||||||
|
throw UnexpectedValueException::create()->withMessage(sprintf(__('Invalid status: %s', 'mailpoet'), $status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateAutomationSteps(Automation $automation, array $steps): void {
|
||||||
|
$existingSteps = $automation->getSteps();
|
||||||
|
if (count($steps) !== count($existingSteps)) {
|
||||||
|
throw Exceptions::automationStructureModificationNotSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($steps as $id => $data) {
|
||||||
|
$existingStep = $existingSteps[$id] ?? null;
|
||||||
|
if (!$existingStep || !$this->stepChanged(Step::fromArray($data), $existingStep)) {
|
||||||
|
throw Exceptions::automationStructureModificationNotSupported();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function stepChanged(Step $a, Step $b): bool {
|
||||||
|
$aData = $a->toArray();
|
||||||
|
$bData = $b->toArray();
|
||||||
|
unset($aData['args']);
|
||||||
|
unset($bData['args']);
|
||||||
|
return $aData === $bData;
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Builder;
|
namespace MailPoet\Automation\Engine\Builder;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
use MailPoet\Automation\Engine\Data\Step;
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
use MailPoet\Automation\Engine\Data\Workflow;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
use MailPoet\Automation\Engine\Registry;
|
use MailPoet\Automation\Engine\Registry;
|
||||||
|
|
||||||
@ -17,21 +17,21 @@ class UpdateStepsController {
|
|||||||
$this->registry = $registry;
|
$this->registry = $registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateSteps(Workflow $workflow, array $data): Workflow {
|
public function updateSteps(Automation $automation, array $data): Automation {
|
||||||
$steps = [];
|
$steps = [];
|
||||||
foreach ($data as $index => $stepData) {
|
foreach ($data as $index => $stepData) {
|
||||||
$step = $this->processStep($stepData, $workflow->getStep($stepData['id']));
|
$step = $this->processStep($stepData, $automation->getStep($stepData['id']));
|
||||||
$steps[$index] = $step;
|
$steps[$index] = $step;
|
||||||
}
|
}
|
||||||
$workflow->setSteps($steps);
|
$automation->setSteps($steps);
|
||||||
return $workflow;
|
return $automation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processStep(array $data, ?Step $existingStep): Step {
|
private function processStep(array $data, ?Step $existingStep): Step {
|
||||||
$key = $data['key'];
|
$key = $data['key'];
|
||||||
$step = $this->registry->getStep($key);
|
$step = $this->registry->getStep($key);
|
||||||
if (!$step && $existingStep && $data !== $existingStep->toArray()) {
|
if (!$step && $existingStep && $data !== $existingStep->toArray()) {
|
||||||
throw Exceptions::workflowStepNotFound($key);
|
throw Exceptions::automationStepNotFound($key);
|
||||||
}
|
}
|
||||||
return Step::fromArray($data);
|
return Step::fromArray($data);
|
||||||
}
|
}
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
<?php declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Builder;
|
|
||||||
|
|
||||||
use MailPoet\Automation\Engine\Data\Step;
|
|
||||||
use MailPoet\Automation\Engine\Data\Workflow;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions\UnexpectedValueException;
|
|
||||||
use MailPoet\Automation\Engine\Hooks;
|
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStatisticsStorage;
|
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
|
||||||
use MailPoet\Automation\Engine\Validation\WorkflowValidator;
|
|
||||||
|
|
||||||
class UpdateWorkflowController {
|
|
||||||
/** @var Hooks */
|
|
||||||
private $hooks;
|
|
||||||
|
|
||||||
/** @var WorkflowStorage */
|
|
||||||
private $storage;
|
|
||||||
|
|
||||||
/** @var WorkflowStatisticsStorage */
|
|
||||||
private $statisticsStorage;
|
|
||||||
|
|
||||||
/** @var WorkflowValidator */
|
|
||||||
private $workflowValidator;
|
|
||||||
|
|
||||||
/** @var UpdateStepsController */
|
|
||||||
private $updateStepsController;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
Hooks $hooks,
|
|
||||||
WorkflowStorage $storage,
|
|
||||||
WorkflowStatisticsStorage $statisticsStorage,
|
|
||||||
WorkflowValidator $workflowValidator,
|
|
||||||
UpdateStepsController $updateStepsController
|
|
||||||
) {
|
|
||||||
$this->hooks = $hooks;
|
|
||||||
$this->storage = $storage;
|
|
||||||
$this->statisticsStorage = $statisticsStorage;
|
|
||||||
$this->workflowValidator = $workflowValidator;
|
|
||||||
$this->updateStepsController = $updateStepsController;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateWorkflow(int $id, array $data): Workflow {
|
|
||||||
$workflow = $this->storage->getWorkflow($id);
|
|
||||||
if (!$workflow) {
|
|
||||||
throw Exceptions::workflowNotFound($id);
|
|
||||||
}
|
|
||||||
$this->validateIfWorkflowCanBeUpdated($workflow, $data);
|
|
||||||
|
|
||||||
if (array_key_exists('name', $data)) {
|
|
||||||
$workflow->setName($data['name']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('status', $data)) {
|
|
||||||
$this->checkWorkflowStatus($data['status']);
|
|
||||||
$workflow->setStatus($data['status']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('steps', $data)) {
|
|
||||||
$this->validateWorkflowSteps($workflow, $data['steps']);
|
|
||||||
$this->updateStepsController->updateSteps($workflow, $data['steps']);
|
|
||||||
foreach ($workflow->getSteps() as $step) {
|
|
||||||
$this->hooks->doWorkflowStepBeforeSave($step);
|
|
||||||
$this->hooks->doWorkflowStepByKeyBeforeSave($step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->hooks->doWorkflowBeforeSave($workflow);
|
|
||||||
|
|
||||||
$this->workflowValidator->validate($workflow);
|
|
||||||
$this->storage->updateWorkflow($workflow);
|
|
||||||
|
|
||||||
$workflow = $this->storage->getWorkflow($id);
|
|
||||||
if (!$workflow) {
|
|
||||||
throw Exceptions::workflowNotFound($id);
|
|
||||||
}
|
|
||||||
return $workflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a temporary validation, see MAILPOET-4744
|
|
||||||
*/
|
|
||||||
private function validateIfWorkflowCanBeUpdated(Workflow $workflow, array $data): void {
|
|
||||||
|
|
||||||
if (
|
|
||||||
!in_array(
|
|
||||||
$workflow->getStatus(),
|
|
||||||
[
|
|
||||||
Workflow::STATUS_ACTIVE,
|
|
||||||
Workflow::STATUS_DEACTIVATING,
|
|
||||||
],
|
|
||||||
true
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$statistics = $this->statisticsStorage->getWorkflowStats($workflow->getId());
|
|
||||||
if ($statistics->getInProgress() === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($data['status']) || $data['status'] === $workflow->getStatus()) {
|
|
||||||
throw Exceptions::workflowHasActiveRuns($workflow->getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkWorkflowStatus(string $status): void {
|
|
||||||
if (!in_array($status, Workflow::STATUS_ALL, true)) {
|
|
||||||
// translators: %s is the status.
|
|
||||||
throw UnexpectedValueException::create()->withMessage(sprintf(__('Invalid status: %s', 'mailpoet'), $status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function validateWorkflowSteps(Workflow $workflow, array $steps): void {
|
|
||||||
$existingSteps = $workflow->getSteps();
|
|
||||||
if (count($steps) !== count($existingSteps)) {
|
|
||||||
throw Exceptions::workflowStructureModificationNotSupported();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($steps as $id => $data) {
|
|
||||||
$existingStep = $existingSteps[$id] ?? null;
|
|
||||||
if (!$existingStep || !$this->stepChanged(Step::fromArray($data), $existingStep)) {
|
|
||||||
throw Exceptions::workflowStructureModificationNotSupported();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function stepChanged(Step $a, Step $b): bool {
|
|
||||||
$aData = $a->toArray();
|
|
||||||
$bData = $b->toArray();
|
|
||||||
unset($aData['args']);
|
|
||||||
unset($bData['args']);
|
|
||||||
return $aData === $bData;
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,13 +4,13 @@ namespace MailPoet\Automation\Engine\Control;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use MailPoet\Automation\Engine\Control\Steps\ActionStepRunner;
|
use MailPoet\Automation\Engine\Control\Steps\ActionStepRunner;
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
|
use MailPoet\Automation\Engine\Data\AutomationRun;
|
||||||
|
use MailPoet\Automation\Engine\Data\AutomationRunLog;
|
||||||
use MailPoet\Automation\Engine\Data\Step;
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
use MailPoet\Automation\Engine\Data\StepRunArgs;
|
use MailPoet\Automation\Engine\Data\StepRunArgs;
|
||||||
use MailPoet\Automation\Engine\Data\StepValidationArgs;
|
use MailPoet\Automation\Engine\Data\StepValidationArgs;
|
||||||
use MailPoet\Automation\Engine\Data\SubjectEntry;
|
use MailPoet\Automation\Engine\Data\SubjectEntry;
|
||||||
use MailPoet\Automation\Engine\Data\Workflow;
|
|
||||||
use MailPoet\Automation\Engine\Data\WorkflowRun;
|
|
||||||
use MailPoet\Automation\Engine\Data\WorkflowRunLog;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
||||||
use MailPoet\Automation\Engine\Hooks;
|
use MailPoet\Automation\Engine\Hooks;
|
||||||
@ -18,9 +18,9 @@ use MailPoet\Automation\Engine\Integration\Action;
|
|||||||
use MailPoet\Automation\Engine\Integration\Payload;
|
use MailPoet\Automation\Engine\Integration\Payload;
|
||||||
use MailPoet\Automation\Engine\Integration\Subject;
|
use MailPoet\Automation\Engine\Integration\Subject;
|
||||||
use MailPoet\Automation\Engine\Registry;
|
use MailPoet\Automation\Engine\Registry;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowRunLogStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationRunLogStorage;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowRunStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationRunStorage;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
use MailPoet\Automation\Engine\WordPress;
|
use MailPoet\Automation\Engine\WordPress;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
@ -37,17 +37,17 @@ class StepHandler {
|
|||||||
/** @var WordPress */
|
/** @var WordPress */
|
||||||
private $wordPress;
|
private $wordPress;
|
||||||
|
|
||||||
/** @var WorkflowRunStorage */
|
/** @var AutomationRunStorage */
|
||||||
private $workflowRunStorage;
|
private $automationRunStorage;
|
||||||
|
|
||||||
/** @var WorkflowStorage */
|
/** @var AutomationStorage */
|
||||||
private $workflowStorage;
|
private $automationStorage;
|
||||||
|
|
||||||
/** @var array<string, StepRunner> */
|
/** @var array<string, StepRunner> */
|
||||||
private $stepRunners = [];
|
private $stepRunners = [];
|
||||||
|
|
||||||
/** @var WorkflowRunLogStorage */
|
/** @var AutomationRunLogStorage */
|
||||||
private $workflowRunLogStorage;
|
private $automationRunLogStorage;
|
||||||
|
|
||||||
/** @var Hooks */
|
/** @var Hooks */
|
||||||
private $hooks;
|
private $hooks;
|
||||||
@ -61,9 +61,9 @@ class StepHandler {
|
|||||||
Hooks $hooks,
|
Hooks $hooks,
|
||||||
SubjectLoader $subjectLoader,
|
SubjectLoader $subjectLoader,
|
||||||
WordPress $wordPress,
|
WordPress $wordPress,
|
||||||
WorkflowRunStorage $workflowRunStorage,
|
AutomationRunStorage $automationRunStorage,
|
||||||
WorkflowRunLogStorage $workflowRunLogStorage,
|
AutomationRunLogStorage $automationRunLogStorage,
|
||||||
WorkflowStorage $workflowStorage,
|
AutomationStorage $automationStorage,
|
||||||
Registry $registry
|
Registry $registry
|
||||||
) {
|
) {
|
||||||
$this->actionScheduler = $actionScheduler;
|
$this->actionScheduler = $actionScheduler;
|
||||||
@ -71,14 +71,14 @@ class StepHandler {
|
|||||||
$this->hooks = $hooks;
|
$this->hooks = $hooks;
|
||||||
$this->subjectLoader = $subjectLoader;
|
$this->subjectLoader = $subjectLoader;
|
||||||
$this->wordPress = $wordPress;
|
$this->wordPress = $wordPress;
|
||||||
$this->workflowRunStorage = $workflowRunStorage;
|
$this->automationRunStorage = $automationRunStorage;
|
||||||
$this->workflowRunLogStorage = $workflowRunLogStorage;
|
$this->automationRunLogStorage = $automationRunLogStorage;
|
||||||
$this->workflowStorage = $workflowStorage;
|
$this->automationStorage = $automationStorage;
|
||||||
$this->registry = $registry;
|
$this->registry = $registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialize(): void {
|
public function initialize(): void {
|
||||||
$this->wordPress->addAction(Hooks::WORKFLOW_STEP, [$this, 'handle']);
|
$this->wordPress->addAction(Hooks::AUTOMATION_STEP, [$this, 'handle']);
|
||||||
$this->addStepRunner(Step::TYPE_ACTION, $this->actionStepRunner);
|
$this->addStepRunner(Step::TYPE_ACTION, $this->actionStepRunner);
|
||||||
$this->wordPress->doAction(Hooks::STEP_RUNNER_INITIALIZE, [$this]);
|
$this->wordPress->doAction(Hooks::STEP_RUNNER_INITIALIZE, [$this]);
|
||||||
}
|
}
|
||||||
@ -110,57 +110,57 @@ class StepHandler {
|
|||||||
try {
|
try {
|
||||||
$this->handleStep($args);
|
$this->handleStep($args);
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$status = $e instanceof InvalidStateException && $e->getErrorCode() === 'mailpoet_automation_workflow_not_active' ? WorkflowRun::STATUS_CANCELLED : WorkflowRun::STATUS_FAILED;
|
$status = $e instanceof InvalidStateException && $e->getErrorCode() === 'mailpoet_automation_automation_not_active' ? AutomationRun::STATUS_CANCELLED : AutomationRun::STATUS_FAILED;
|
||||||
$this->workflowRunStorage->updateStatus((int)$args['workflow_run_id'], $status);
|
$this->automationRunStorage->updateStatus((int)$args['automation_run_id'], $status);
|
||||||
$this->postProcessWorkflowRun((int)$args['workflow_run_id']);
|
$this->postProcessAutomationRun((int)$args['automation_run_id']);
|
||||||
if (!$e instanceof Exception) {
|
if (!$e instanceof Exception) {
|
||||||
throw new Exception($e->getMessage(), intval($e->getCode()), $e);
|
throw new Exception($e->getMessage(), intval($e->getCode()), $e);
|
||||||
}
|
}
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
$this->postProcessWorkflowRun((int)$args['workflow_run_id']);
|
$this->postProcessAutomationRun((int)$args['automation_run_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleStep(array $args): void {
|
private function handleStep(array $args): void {
|
||||||
$workflowRunId = $args['workflow_run_id'];
|
$automationRunId = $args['automation_run_id'];
|
||||||
$stepId = $args['step_id'];
|
$stepId = $args['step_id'];
|
||||||
|
|
||||||
$workflowRun = $this->workflowRunStorage->getWorkflowRun($workflowRunId);
|
$automationRun = $this->automationRunStorage->getAutomationRun($automationRunId);
|
||||||
if (!$workflowRun) {
|
if (!$automationRun) {
|
||||||
throw Exceptions::workflowRunNotFound($workflowRunId);
|
throw Exceptions::automationRunNotFound($automationRunId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($workflowRun->getStatus() !== WorkflowRun::STATUS_RUNNING) {
|
if ($automationRun->getStatus() !== AutomationRun::STATUS_RUNNING) {
|
||||||
throw Exceptions::workflowRunNotRunning($workflowRunId, $workflowRun->getStatus());
|
throw Exceptions::automationRunNotRunning($automationRunId, $automationRun->getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
$workflow = $this->workflowStorage->getWorkflow($workflowRun->getWorkflowId(), $workflowRun->getVersionId());
|
$automation = $this->automationStorage->getAutomation($automationRun->getAutomationId(), $automationRun->getVersionId());
|
||||||
if (!$workflow) {
|
if (!$automation) {
|
||||||
throw Exceptions::workflowVersionNotFound($workflowRun->getWorkflowId(), $workflowRun->getVersionId());
|
throw Exceptions::automationVersionNotFound($automationRun->getAutomationId(), $automationRun->getVersionId());
|
||||||
}
|
}
|
||||||
if (!in_array($workflow->getStatus(), [Workflow::STATUS_ACTIVE, Workflow::STATUS_DEACTIVATING], true)) {
|
if (!in_array($automation->getStatus(), [Automation::STATUS_ACTIVE, Automation::STATUS_DEACTIVATING], true)) {
|
||||||
throw Exceptions::workflowNotActive($workflowRun->getWorkflowId());
|
throw Exceptions::automationNotActive($automationRun->getAutomationId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// complete workflow run
|
// complete automation run
|
||||||
if (!$stepId) {
|
if (!$stepId) {
|
||||||
$this->workflowRunStorage->updateStatus($workflowRunId, WorkflowRun::STATUS_COMPLETE);
|
$this->automationRunStorage->updateStatus($automationRunId, AutomationRun::STATUS_COMPLETE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stepData = $workflow->getStep($stepId);
|
$stepData = $automation->getStep($stepId);
|
||||||
if (!$stepData) {
|
if (!$stepData) {
|
||||||
throw Exceptions::workflowStepNotFound($stepId);
|
throw Exceptions::automationStepNotFound($stepId);
|
||||||
}
|
}
|
||||||
$step = $this->registry->getStep($stepData->getKey());
|
$step = $this->registry->getStep($stepData->getKey());
|
||||||
$stepType = $stepData->getType();
|
$stepType = $stepData->getType();
|
||||||
if (isset($this->stepRunners[$stepType])) {
|
if (isset($this->stepRunners[$stepType])) {
|
||||||
$log = new WorkflowRunLog($workflowRun->getId(), $stepData->getId());
|
$log = new AutomationRunLog($automationRun->getId(), $stepData->getId());
|
||||||
try {
|
try {
|
||||||
$requiredSubjects = $step instanceof Action ? $step->getSubjectKeys() : [];
|
$requiredSubjects = $step instanceof Action ? $step->getSubjectKeys() : [];
|
||||||
$subjectEntries = $this->getSubjectEntries($workflowRun, $requiredSubjects);
|
$subjectEntries = $this->getSubjectEntries($automationRun, $requiredSubjects);
|
||||||
$args = new StepRunArgs($workflow, $workflowRun, $stepData, $subjectEntries);
|
$args = new StepRunArgs($automation, $automationRun, $stepData, $subjectEntries);
|
||||||
$validationArgs = new StepValidationArgs($workflow, $stepData, array_map(function (SubjectEntry $entry) {
|
$validationArgs = new StepValidationArgs($automation, $stepData, array_map(function (SubjectEntry $entry) {
|
||||||
return $entry->getSubject();
|
return $entry->getSubject();
|
||||||
}, $subjectEntries));
|
}, $subjectEntries));
|
||||||
$this->stepRunners[$stepType]->run($args, $validationArgs);
|
$this->stepRunners[$stepType]->run($args, $validationArgs);
|
||||||
@ -171,11 +171,11 @@ class StepHandler {
|
|||||||
throw $e;
|
throw $e;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
$this->hooks->doWorkflowStepAfterRun($log);
|
$this->hooks->doAutomationStepAfterRun($log);
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
// Ignore integration errors
|
// Ignore integration errors
|
||||||
}
|
}
|
||||||
$this->workflowRunLogStorage->createWorkflowRunLog($log);
|
$this->automationRunLogStorage->createAutomationRunLog($log);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidStateException();
|
throw new InvalidStateException();
|
||||||
@ -184,33 +184,33 @@ class StepHandler {
|
|||||||
$nextStep = $stepData->getNextSteps()[0] ?? null;
|
$nextStep = $stepData->getNextSteps()[0] ?? null;
|
||||||
$nextStepArgs = [
|
$nextStepArgs = [
|
||||||
[
|
[
|
||||||
'workflow_run_id' => $workflowRunId,
|
'automation_run_id' => $automationRunId,
|
||||||
'step_id' => $nextStep ? $nextStep->getId() : null,
|
'step_id' => $nextStep ? $nextStep->getId() : null,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->workflowRunStorage->updateNextStep($workflowRunId, $nextStep ? $nextStep->getId() : null);
|
$this->automationRunStorage->updateNextStep($automationRunId, $nextStep ? $nextStep->getId() : null);
|
||||||
|
|
||||||
// next step scheduled by action
|
// next step scheduled by action
|
||||||
if ($this->actionScheduler->hasScheduledAction(Hooks::WORKFLOW_STEP, $nextStepArgs)) {
|
if ($this->actionScheduler->hasScheduledAction(Hooks::AUTOMATION_STEP, $nextStepArgs)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no need to schedule a new step if the next step is null, complete the run
|
// no need to schedule a new step if the next step is null, complete the run
|
||||||
if (!$nextStep) {
|
if (!$nextStep) {
|
||||||
$this->workflowRunStorage->updateStatus($workflowRunId, WorkflowRun::STATUS_COMPLETE);
|
$this->automationRunStorage->updateStatus($automationRunId, AutomationRun::STATUS_COMPLETE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// enqueue next step
|
// enqueue next step
|
||||||
$this->actionScheduler->enqueue(Hooks::WORKFLOW_STEP, $nextStepArgs);
|
$this->actionScheduler->enqueue(Hooks::AUTOMATION_STEP, $nextStepArgs);
|
||||||
// TODO: allow long-running steps (that are not done here yet)
|
// TODO: allow long-running steps (that are not done here yet)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return SubjectEntry<Subject<Payload>>[] */
|
/** @return SubjectEntry<Subject<Payload>>[] */
|
||||||
private function getSubjectEntries(WorkflowRun $workflowRun, array $requiredSubjectKeys): array {
|
private function getSubjectEntries(AutomationRun $automationRun, array $requiredSubjectKeys): array {
|
||||||
$subjectDataMap = [];
|
$subjectDataMap = [];
|
||||||
foreach ($workflowRun->getSubjects() as $data) {
|
foreach ($automationRun->getSubjects() as $data) {
|
||||||
$subjectDataMap[$data->getKey()] = array_merge($subjectDataMap[$data->getKey()] ?? [], [$data]);
|
$subjectDataMap[$data->getKey()] = array_merge($subjectDataMap[$data->getKey()] ?? [], [$data]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ class StepHandler {
|
|||||||
foreach ($requiredSubjectKeys as $key) {
|
foreach ($requiredSubjectKeys as $key) {
|
||||||
$subjectData = $subjectDataMap[$key] ?? null;
|
$subjectData = $subjectDataMap[$key] ?? null;
|
||||||
if (!$subjectData) {
|
if (!$subjectData) {
|
||||||
throw Exceptions::subjectDataNotFound($key, $workflowRun->getId());
|
throw Exceptions::subjectDataNotFound($key, $automationRun->getId());
|
||||||
}
|
}
|
||||||
foreach ($subjectData as $data) {
|
foreach ($subjectData as $data) {
|
||||||
$subjectEntries[] = $this->subjectLoader->getSubjectEntry($data);
|
$subjectEntries[] = $this->subjectLoader->getSubjectEntry($data);
|
||||||
@ -227,26 +227,26 @@ class StepHandler {
|
|||||||
return $subjectEntries;
|
return $subjectEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function postProcessWorkflowRun(int $workflowRunId): void {
|
private function postProcessAutomationRun(int $automationRunId): void {
|
||||||
$workflowRun = $this->workflowRunStorage->getWorkflowRun($workflowRunId);
|
$automationRun = $this->automationRunStorage->getAutomationRun($automationRunId);
|
||||||
if (!$workflowRun) {
|
if (!$automationRun) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$workflow = $this->workflowStorage->getWorkflow($workflowRun->getWorkflowId());
|
$automation = $this->automationStorage->getAutomation($automationRun->getAutomationId());
|
||||||
if (!$workflow) {
|
if (!$automation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->postProcessWorkflow($workflow);
|
$this->postProcessAutomation($automation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function postProcessWorkflow(Workflow $workflow): void {
|
private function postProcessAutomation(Automation $automation): void {
|
||||||
if ($workflow->getStatus() === Workflow::STATUS_DEACTIVATING) {
|
if ($automation->getStatus() === Automation::STATUS_DEACTIVATING) {
|
||||||
$activeRuns = $this->workflowRunStorage->getCountForWorkflow($workflow, WorkflowRun::STATUS_RUNNING);
|
$activeRuns = $this->automationRunStorage->getCountForAutomation($automation, AutomationRun::STATUS_RUNNING);
|
||||||
|
|
||||||
// Set a deactivating Workflow to draft once all workflow runs are finished.
|
// Set a deactivating Automation to draft once all automation runs are finished.
|
||||||
if ($activeRuns === 0) {
|
if ($activeRuns === 0) {
|
||||||
$workflow->setStatus(Workflow::STATUS_DRAFT);
|
$automation->setStatus(Automation::STATUS_DRAFT);
|
||||||
$this->workflowStorage->updateWorkflow($workflow);
|
$this->automationStorage->updateAutomation($automation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Control;
|
namespace MailPoet\Automation\Engine\Control;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\AutomationRun;
|
||||||
use MailPoet\Automation\Engine\Data\StepRunArgs;
|
use MailPoet\Automation\Engine\Data\StepRunArgs;
|
||||||
use MailPoet\Automation\Engine\Data\Subject;
|
use MailPoet\Automation\Engine\Data\Subject;
|
||||||
use MailPoet\Automation\Engine\Data\WorkflowRun;
|
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
use MailPoet\Automation\Engine\Hooks;
|
use MailPoet\Automation\Engine\Hooks;
|
||||||
use MailPoet\Automation\Engine\Integration\Trigger;
|
use MailPoet\Automation\Engine\Integration\Trigger;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowRunStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationRunStorage;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
use MailPoet\Automation\Engine\WordPress;
|
use MailPoet\Automation\Engine\WordPress;
|
||||||
|
|
||||||
class TriggerHandler {
|
class TriggerHandler {
|
||||||
@ -22,23 +22,23 @@ class TriggerHandler {
|
|||||||
/** @var WordPress */
|
/** @var WordPress */
|
||||||
private $wordPress;
|
private $wordPress;
|
||||||
|
|
||||||
/** @var WorkflowStorage */
|
/** @var AutomationStorage */
|
||||||
private $workflowStorage;
|
private $automationStorage;
|
||||||
|
|
||||||
/** @var WorkflowRunStorage */
|
/** @var AutomationRunStorage */
|
||||||
private $workflowRunStorage;
|
private $automationRunStorage;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ActionScheduler $actionScheduler,
|
ActionScheduler $actionScheduler,
|
||||||
SubjectLoader $subjectLoader,
|
SubjectLoader $subjectLoader,
|
||||||
WordPress $wordPress,
|
WordPress $wordPress,
|
||||||
WorkflowStorage $workflowStorage,
|
AutomationStorage $automationStorage,
|
||||||
WorkflowRunStorage $workflowRunStorage
|
AutomationRunStorage $automationRunStorage
|
||||||
) {
|
) {
|
||||||
$this->actionScheduler = $actionScheduler;
|
$this->actionScheduler = $actionScheduler;
|
||||||
$this->wordPress = $wordPress;
|
$this->wordPress = $wordPress;
|
||||||
$this->workflowStorage = $workflowStorage;
|
$this->automationStorage = $automationStorage;
|
||||||
$this->workflowRunStorage = $workflowRunStorage;
|
$this->automationRunStorage = $automationRunStorage;
|
||||||
$this->subjectLoader = $subjectLoader;
|
$this->subjectLoader = $subjectLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,11 +48,11 @@ class TriggerHandler {
|
|||||||
|
|
||||||
/** @param Subject[] $subjects */
|
/** @param Subject[] $subjects */
|
||||||
public function processTrigger(Trigger $trigger, array $subjects): void {
|
public function processTrigger(Trigger $trigger, array $subjects): void {
|
||||||
$workflows = $this->workflowStorage->getActiveWorkflowsByTrigger($trigger);
|
$automations = $this->automationStorage->getActiveAutomationsByTrigger($trigger);
|
||||||
foreach ($workflows as $workflow) {
|
foreach ($automations as $automation) {
|
||||||
$step = $workflow->getTrigger($trigger->getKey());
|
$step = $automation->getTrigger($trigger->getKey());
|
||||||
if (!$step) {
|
if (!$step) {
|
||||||
throw Exceptions::workflowTriggerNotFound($workflow->getId(), $trigger->getKey());
|
throw Exceptions::automationTriggerNotFound($automation->getId(), $trigger->getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure subjects are registered and loadable
|
// ensure subjects are registered and loadable
|
||||||
@ -61,21 +61,21 @@ class TriggerHandler {
|
|||||||
$entry->getPayload();
|
$entry->getPayload();
|
||||||
}
|
}
|
||||||
|
|
||||||
$workflowRun = new WorkflowRun($workflow->getId(), $workflow->getVersionId(), $trigger->getKey(), $subjects);
|
$automationRun = new AutomationRun($automation->getId(), $automation->getVersionId(), $trigger->getKey(), $subjects);
|
||||||
if (!$trigger->isTriggeredBy(new StepRunArgs($workflow, $workflowRun, $step, $subjectEntries))) {
|
if (!$trigger->isTriggeredBy(new StepRunArgs($automation, $automationRun, $step, $subjectEntries))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$workflowRunId = $this->workflowRunStorage->createWorkflowRun($workflowRun);
|
$automationRunId = $this->automationRunStorage->createAutomationRun($automationRun);
|
||||||
$nextStep = $step->getNextSteps()[0] ?? null;
|
$nextStep = $step->getNextSteps()[0] ?? null;
|
||||||
$this->actionScheduler->enqueue(Hooks::WORKFLOW_STEP, [
|
$this->actionScheduler->enqueue(Hooks::AUTOMATION_STEP, [
|
||||||
[
|
[
|
||||||
'workflow_run_id' => $workflowRunId,
|
'automation_run_id' => $automationRunId,
|
||||||
'step_id' => $nextStep ? $nextStep->getId() : null,
|
'step_id' => $nextStep ? $nextStep->getId() : null,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->workflowRunStorage->updateNextStep($workflowRunId, $nextStep ? $nextStep->getId() : null);
|
$this->automationRunStorage->updateNextStep($automationRunId, $nextStep ? $nextStep->getId() : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use DateTimeImmutable;
|
|||||||
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
||||||
use MailPoet\Automation\Engine\Utils\Json;
|
use MailPoet\Automation\Engine\Utils\Json;
|
||||||
|
|
||||||
class Workflow {
|
class Automation {
|
||||||
public const STATUS_ACTIVE = 'active';
|
public const STATUS_ACTIVE = 'active';
|
||||||
public const STATUS_DEACTIVATING = 'deactivating';
|
public const STATUS_DEACTIVATING = 'deactivating';
|
||||||
public const STATUS_DRAFT = 'draft';
|
public const STATUS_DRAFT = 'draft';
|
||||||
@ -66,14 +66,14 @@ class Workflow {
|
|||||||
|
|
||||||
public function getId(): int {
|
public function getId(): int {
|
||||||
if (!$this->id) {
|
if (!$this->id) {
|
||||||
throw InvalidStateException::create()->withMessage('No workflow ID was set');
|
throw InvalidStateException::create()->withMessage('No automation ID was set');
|
||||||
}
|
}
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getVersionId(): int {
|
public function getVersionId(): int {
|
||||||
if (!$this->versionId) {
|
if (!$this->versionId) {
|
||||||
throw InvalidStateException::create()->withMessage('No workflow version ID was set');
|
throw InvalidStateException::create()->withMessage('No automation version ID was set');
|
||||||
}
|
}
|
||||||
return $this->versionId;
|
return $this->versionId;
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ class Workflow {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function equals(Workflow $compare): bool {
|
public function equals(Automation $compare): bool {
|
||||||
$compareArray = $compare->toArray();
|
$compareArray = $compare->toArray();
|
||||||
$currentArray = $this->toArray();
|
$currentArray = $this->toArray();
|
||||||
$ignoreValues = [
|
$ignoreValues = [
|
||||||
@ -154,7 +154,7 @@ class Workflow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function needsFullValidation(): bool {
|
public function needsFullValidation(): bool {
|
||||||
return in_array($this->status, [Workflow::STATUS_ACTIVE, Workflow::STATUS_DEACTIVATING], true);
|
return in_array($this->status, [Automation::STATUS_ACTIVE, Automation::STATUS_DEACTIVATING], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toArray(): array {
|
public function toArray(): array {
|
||||||
@ -180,19 +180,19 @@ class Workflow {
|
|||||||
|
|
||||||
public static function fromArray(array $data): self {
|
public static function fromArray(array $data): self {
|
||||||
// TODO: validation
|
// TODO: validation
|
||||||
$workflow = new self(
|
$automation = new self(
|
||||||
$data['name'],
|
$data['name'],
|
||||||
array_map(function (array $stepData): Step {
|
array_map(function (array $stepData): Step {
|
||||||
return Step::fromArray($stepData);
|
return Step::fromArray($stepData);
|
||||||
}, Json::decode($data['steps'])),
|
}, Json::decode($data['steps'])),
|
||||||
new \WP_User((int)$data['author'])
|
new \WP_User((int)$data['author'])
|
||||||
);
|
);
|
||||||
$workflow->id = (int)$data['id'];
|
$automation->id = (int)$data['id'];
|
||||||
$workflow->versionId = (int)$data['version_id'];
|
$automation->versionId = (int)$data['version_id'];
|
||||||
$workflow->status = $data['status'];
|
$automation->status = $data['status'];
|
||||||
$workflow->createdAt = new DateTimeImmutable($data['created_at']);
|
$automation->createdAt = new DateTimeImmutable($data['created_at']);
|
||||||
$workflow->updatedAt = new DateTimeImmutable($data['updated_at']);
|
$automation->updatedAt = new DateTimeImmutable($data['updated_at']);
|
||||||
$workflow->activatedAt = $data['activated_at'] !== null ? new DateTimeImmutable($data['activated_at']) : null;
|
$automation->activatedAt = $data['activated_at'] !== null ? new DateTimeImmutable($data['activated_at']) : null;
|
||||||
return $workflow;
|
return $automation;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ namespace MailPoet\Automation\Engine\Data;
|
|||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use MailPoet\Automation\Engine\Utils\Json;
|
use MailPoet\Automation\Engine\Utils\Json;
|
||||||
|
|
||||||
class WorkflowRun {
|
class AutomationRun {
|
||||||
public const STATUS_RUNNING = 'running';
|
public const STATUS_RUNNING = 'running';
|
||||||
public const STATUS_COMPLETE = 'complete';
|
public const STATUS_COMPLETE = 'complete';
|
||||||
public const STATUS_CANCELLED = 'cancelled';
|
public const STATUS_CANCELLED = 'cancelled';
|
||||||
@ -15,7 +15,7 @@ class WorkflowRun {
|
|||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
/** @var int */
|
/** @var int */
|
||||||
private $workflowId;
|
private $automationId;
|
||||||
|
|
||||||
/** @var int */
|
/** @var int */
|
||||||
private $versionId;
|
private $versionId;
|
||||||
@ -39,13 +39,13 @@ class WorkflowRun {
|
|||||||
* @param Subject[] $subjects
|
* @param Subject[] $subjects
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
int $workflowId,
|
int $automationId,
|
||||||
int $versionId,
|
int $versionId,
|
||||||
string $triggerKey,
|
string $triggerKey,
|
||||||
array $subjects,
|
array $subjects,
|
||||||
int $id = null
|
int $id = null
|
||||||
) {
|
) {
|
||||||
$this->workflowId = $workflowId;
|
$this->automationId = $automationId;
|
||||||
$this->versionId = $versionId;
|
$this->versionId = $versionId;
|
||||||
$this->triggerKey = $triggerKey;
|
$this->triggerKey = $triggerKey;
|
||||||
$this->subjects = $subjects;
|
$this->subjects = $subjects;
|
||||||
@ -63,8 +63,8 @@ class WorkflowRun {
|
|||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWorkflowId(): int {
|
public function getAutomationId(): int {
|
||||||
return $this->workflowId;
|
return $this->automationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getVersionId(): int {
|
public function getVersionId(): int {
|
||||||
@ -101,7 +101,7 @@ class WorkflowRun {
|
|||||||
|
|
||||||
public function toArray(): array {
|
public function toArray(): array {
|
||||||
return [
|
return [
|
||||||
'workflow_id' => $this->workflowId,
|
'automation_id' => $this->automationId,
|
||||||
'version_id' => $this->versionId,
|
'version_id' => $this->versionId,
|
||||||
'trigger_key' => $this->triggerKey,
|
'trigger_key' => $this->triggerKey,
|
||||||
'status' => $this->status,
|
'status' => $this->status,
|
||||||
@ -116,18 +116,18 @@ class WorkflowRun {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function fromArray(array $data): self {
|
public static function fromArray(array $data): self {
|
||||||
$workflowRun = new WorkflowRun(
|
$automationRun = new AutomationRun(
|
||||||
(int)$data['workflow_id'],
|
(int)$data['automation_id'],
|
||||||
(int)$data['version_id'],
|
(int)$data['version_id'],
|
||||||
$data['trigger_key'],
|
$data['trigger_key'],
|
||||||
array_map(function (array $subject) {
|
array_map(function (array $subject) {
|
||||||
return Subject::fromArray($subject);
|
return Subject::fromArray($subject);
|
||||||
}, Json::decode($data['subjects']))
|
}, Json::decode($data['subjects']))
|
||||||
);
|
);
|
||||||
$workflowRun->id = (int)$data['id'];
|
$automationRun->id = (int)$data['id'];
|
||||||
$workflowRun->status = $data['status'];
|
$automationRun->status = $data['status'];
|
||||||
$workflowRun->createdAt = new DateTimeImmutable($data['created_at']);
|
$automationRun->createdAt = new DateTimeImmutable($data['created_at']);
|
||||||
$workflowRun->updatedAt = new DateTimeImmutable($data['updated_at']);
|
$automationRun->updatedAt = new DateTimeImmutable($data['updated_at']);
|
||||||
return $workflowRun;
|
return $automationRun;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ use InvalidArgumentException;
|
|||||||
use MailPoet\Automation\Engine\Utils\Json;
|
use MailPoet\Automation\Engine\Utils\Json;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
class WorkflowRunLog {
|
class AutomationRunLog {
|
||||||
|
|
||||||
const STATUS_RUNNING = 'running';
|
const STATUS_RUNNING = 'running';
|
||||||
const STATUS_COMPLETED = 'completed';
|
const STATUS_COMPLETED = 'completed';
|
||||||
@ -17,7 +17,7 @@ class WorkflowRunLog {
|
|||||||
private $id;
|
private $id;
|
||||||
|
|
||||||
/** @var int */
|
/** @var int */
|
||||||
private $workflowRunId;
|
private $automationRunId;
|
||||||
|
|
||||||
/** @var DateTimeImmutable */
|
/** @var DateTimeImmutable */
|
||||||
private $startedAt;
|
private $startedAt;
|
||||||
@ -38,11 +38,11 @@ class WorkflowRunLog {
|
|||||||
private $stepId;
|
private $stepId;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
int $workflowRunId,
|
int $automationRunId,
|
||||||
string $stepId,
|
string $stepId,
|
||||||
int $id = null
|
int $id = null
|
||||||
) {
|
) {
|
||||||
$this->workflowRunId = $workflowRunId;
|
$this->automationRunId = $automationRunId;
|
||||||
$this->stepId = $stepId;
|
$this->stepId = $stepId;
|
||||||
$this->status = self::STATUS_RUNNING;
|
$this->status = self::STATUS_RUNNING;
|
||||||
|
|
||||||
@ -60,8 +60,8 @@ class WorkflowRunLog {
|
|||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWorkflowRunId(): int {
|
public function getAutomationRunId(): int {
|
||||||
return $this->workflowRunId;
|
return $this->automationRunId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStepId(): string {
|
public function getStepId(): string {
|
||||||
@ -102,7 +102,7 @@ class WorkflowRunLog {
|
|||||||
|
|
||||||
public function toArray(): array {
|
public function toArray(): array {
|
||||||
return [
|
return [
|
||||||
'workflow_run_id' => $this->workflowRunId,
|
'automation_run_id' => $this->automationRunId,
|
||||||
'step_id' => $this->stepId,
|
'step_id' => $this->stepId,
|
||||||
'status' => $this->status,
|
'status' => $this->status,
|
||||||
'started_at' => $this->startedAt->format(DateTimeImmutable::W3C),
|
'started_at' => $this->startedAt->format(DateTimeImmutable::W3C),
|
||||||
@ -134,18 +134,18 @@ class WorkflowRunLog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function fromArray(array $data): self {
|
public static function fromArray(array $data): self {
|
||||||
$workflowRunLog = new WorkflowRunLog((int)$data['workflow_run_id'], $data['step_id']);
|
$automationRunLog = new AutomationRunLog((int)$data['automation_run_id'], $data['step_id']);
|
||||||
$workflowRunLog->id = (int)$data['id'];
|
$automationRunLog->id = (int)$data['id'];
|
||||||
$workflowRunLog->status = $data['status'];
|
$automationRunLog->status = $data['status'];
|
||||||
$workflowRunLog->error = Json::decode($data['error']);
|
$automationRunLog->error = Json::decode($data['error']);
|
||||||
$workflowRunLog->data = Json::decode($data['data']);
|
$automationRunLog->data = Json::decode($data['data']);
|
||||||
$workflowRunLog->startedAt = new DateTimeImmutable($data['started_at']);
|
$automationRunLog->startedAt = new DateTimeImmutable($data['started_at']);
|
||||||
|
|
||||||
if ($data['completed_at']) {
|
if ($data['completed_at']) {
|
||||||
$workflowRunLog->completedAt = new DateTimeImmutable($data['completed_at']);
|
$automationRunLog->completedAt = new DateTimeImmutable($data['completed_at']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $workflowRunLog;
|
return $automationRunLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
@ -2,27 +2,27 @@
|
|||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Data;
|
namespace MailPoet\Automation\Engine\Data;
|
||||||
|
|
||||||
class WorkflowStatistics {
|
class AutomationStatistics {
|
||||||
|
|
||||||
private $workflowId;
|
private $automationId;
|
||||||
private $versionId;
|
private $versionId;
|
||||||
private $entered;
|
private $entered;
|
||||||
private $inProgress;
|
private $inProgress;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
int $workflowId,
|
int $automationId,
|
||||||
int $entered = 0,
|
int $entered = 0,
|
||||||
int $inProcess = 0,
|
int $inProcess = 0,
|
||||||
?int $versionId = null
|
?int $versionId = null
|
||||||
) {
|
) {
|
||||||
$this->workflowId = $workflowId;
|
$this->automationId = $automationId;
|
||||||
$this->entered = $entered;
|
$this->entered = $entered;
|
||||||
$this->inProgress = $inProcess;
|
$this->inProgress = $inProcess;
|
||||||
$this->versionId = $versionId;
|
$this->versionId = $versionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWorkflowId(): int {
|
public function getAutomationId(): int {
|
||||||
return $this->workflowId;
|
return $this->automationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getVersionId(): ?int {
|
public function getVersionId(): ?int {
|
||||||
@ -43,7 +43,7 @@ class WorkflowStatistics {
|
|||||||
|
|
||||||
public function toArray(): array {
|
public function toArray(): array {
|
||||||
return [
|
return [
|
||||||
'workflow_id' => $this->getWorkflowId(),
|
'automation_id' => $this->getAutomationId(),
|
||||||
'totals' => [
|
'totals' => [
|
||||||
'entered' => $this->getEntered(),
|
'entered' => $this->getEntered(),
|
||||||
'in_progress' => $this->getInProgress(),
|
'in_progress' => $this->getInProgress(),
|
@ -4,7 +4,7 @@ namespace MailPoet\Automation\Engine\Data;
|
|||||||
|
|
||||||
use MailPoet\RuntimeException;
|
use MailPoet\RuntimeException;
|
||||||
|
|
||||||
class WorkflowTemplate {
|
class AutomationTemplate {
|
||||||
|
|
||||||
public const TYPE_DEFAULT = 'default';
|
public const TYPE_DEFAULT = 'default';
|
||||||
public const TYPE_FREE_ONLY = 'free-only';
|
public const TYPE_FREE_ONLY = 'free-only';
|
||||||
@ -34,14 +34,14 @@ class WorkflowTemplate {
|
|||||||
/** @var string */
|
/** @var string */
|
||||||
private $description;
|
private $description;
|
||||||
|
|
||||||
/** @var Workflow */
|
/** @var Automation */
|
||||||
private $workflow;
|
private $automation;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $slug,
|
string $slug,
|
||||||
int $category,
|
int $category,
|
||||||
string $description,
|
string $description,
|
||||||
Workflow $workflow,
|
Automation $automation,
|
||||||
string $type = self::TYPE_DEFAULT
|
string $type = self::TYPE_DEFAULT
|
||||||
) {
|
) {
|
||||||
if (!in_array($category, self::ALL_CATEGORIES)) {
|
if (!in_array($category, self::ALL_CATEGORIES)) {
|
||||||
@ -50,7 +50,7 @@ class WorkflowTemplate {
|
|||||||
$this->slug = $slug;
|
$this->slug = $slug;
|
||||||
$this->category = $category;
|
$this->category = $category;
|
||||||
$this->description = $description;
|
$this->description = $description;
|
||||||
$this->workflow = $workflow;
|
$this->automation = $automation;
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ class WorkflowTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getName(): string {
|
public function getName(): string {
|
||||||
return $this->workflow->getName();
|
return $this->automation->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCategory(): int {
|
public function getCategory(): int {
|
||||||
@ -74,8 +74,8 @@ class WorkflowTemplate {
|
|||||||
return $this->description;
|
return $this->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWorkflow(): Workflow {
|
public function getAutomation(): Automation {
|
||||||
return $this->workflow;
|
return $this->automation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toArray(): array {
|
public function toArray(): array {
|
||||||
@ -85,7 +85,7 @@ class WorkflowTemplate {
|
|||||||
'category' => $this->getCategory(),
|
'category' => $this->getCategory(),
|
||||||
'type' => $this->getType(),
|
'type' => $this->getType(),
|
||||||
'description' => $this->getDescription(),
|
'description' => $this->getDescription(),
|
||||||
'workflow' => $this->getWorkflow()->toArray(),
|
'automation' => $this->getAutomation()->toArray(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,11 +8,11 @@ use MailPoet\Automation\Engine\Integration\Payload;
|
|||||||
use MailPoet\Automation\Engine\Integration\Subject;
|
use MailPoet\Automation\Engine\Integration\Subject;
|
||||||
|
|
||||||
class StepRunArgs {
|
class StepRunArgs {
|
||||||
/** @var Workflow */
|
/** @var Automation */
|
||||||
private $workflow;
|
private $automation;
|
||||||
|
|
||||||
/** @var WorkflowRun */
|
/** @var AutomationRun */
|
||||||
private $workflowRun;
|
private $automationRun;
|
||||||
|
|
||||||
/** @var Step */
|
/** @var Step */
|
||||||
private $step;
|
private $step;
|
||||||
@ -25,14 +25,14 @@ class StepRunArgs {
|
|||||||
|
|
||||||
/** @param SubjectEntry<Subject<Payload>>[] $subjectsEntries */
|
/** @param SubjectEntry<Subject<Payload>>[] $subjectsEntries */
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Workflow $workflow,
|
Automation $automation,
|
||||||
WorkflowRun $workflowRun,
|
AutomationRun $automationRun,
|
||||||
Step $step,
|
Step $step,
|
||||||
array $subjectsEntries
|
array $subjectsEntries
|
||||||
) {
|
) {
|
||||||
$this->workflow = $workflow;
|
$this->automation = $automation;
|
||||||
$this->step = $step;
|
$this->step = $step;
|
||||||
$this->workflowRun = $workflowRun;
|
$this->automationRun = $automationRun;
|
||||||
|
|
||||||
foreach ($subjectsEntries as $entry) {
|
foreach ($subjectsEntries as $entry) {
|
||||||
$subject = $entry->getSubject();
|
$subject = $entry->getSubject();
|
||||||
@ -42,12 +42,12 @@ class StepRunArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWorkflow(): Workflow {
|
public function getAutomation(): Automation {
|
||||||
return $this->workflow;
|
return $this->automation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWorkflowRun(): WorkflowRun {
|
public function getAutomationRun(): AutomationRun {
|
||||||
return $this->workflowRun;
|
return $this->automationRun;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStep(): Step {
|
public function getStep(): Step {
|
||||||
@ -58,10 +58,10 @@ class StepRunArgs {
|
|||||||
public function getSingleSubjectEntry(string $key): SubjectEntry {
|
public function getSingleSubjectEntry(string $key): SubjectEntry {
|
||||||
$subjects = $this->subjectEntries[$key] ?? [];
|
$subjects = $this->subjectEntries[$key] ?? [];
|
||||||
if (count($subjects) === 0) {
|
if (count($subjects) === 0) {
|
||||||
throw Exceptions::subjectDataNotFound($key, $this->workflowRun->getId());
|
throw Exceptions::subjectDataNotFound($key, $this->automationRun->getId());
|
||||||
}
|
}
|
||||||
if (count($subjects) > 1) {
|
if (count($subjects) > 1) {
|
||||||
throw Exceptions::multipleSubjectsFound($key, $this->workflowRun->getId());
|
throw Exceptions::multipleSubjectsFound($key, $this->automationRun->getId());
|
||||||
}
|
}
|
||||||
return $subjects[0];
|
return $subjects[0];
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ class StepRunArgs {
|
|||||||
$payloads = [];
|
$payloads = [];
|
||||||
foreach ($this->subjectEntries as $entries) {
|
foreach ($this->subjectEntries as $entries) {
|
||||||
if (count($entries) > 1) {
|
if (count($entries) > 1) {
|
||||||
throw Exceptions::multiplePayloadsFound($class, $this->workflowRun->getId());
|
throw Exceptions::multiplePayloadsFound($class, $this->automationRun->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
$entry = $entries[0];
|
$entry = $entries[0];
|
||||||
@ -103,10 +103,10 @@ class StepRunArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (count($payloads) === 0) {
|
if (count($payloads) === 0) {
|
||||||
throw Exceptions::payloadNotFound($class, $this->workflowRun->getId());
|
throw Exceptions::payloadNotFound($class, $this->automationRun->getId());
|
||||||
}
|
}
|
||||||
if (count($payloads) > 1) {
|
if (count($payloads) > 1) {
|
||||||
throw Exceptions::multiplePayloadsFound($class, $this->workflowRun->getId());
|
throw Exceptions::multiplePayloadsFound($class, $this->automationRun->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure PHPStan we're indeed returning an instance of $class
|
// ensure PHPStan we're indeed returning an instance of $class
|
||||||
|
@ -7,8 +7,8 @@ use MailPoet\Automation\Engine\Integration\Payload;
|
|||||||
use MailPoet\Automation\Engine\Integration\Subject;
|
use MailPoet\Automation\Engine\Integration\Subject;
|
||||||
|
|
||||||
class StepValidationArgs {
|
class StepValidationArgs {
|
||||||
/** @var Workflow */
|
/** @var Automation */
|
||||||
private $workflow;
|
private $automation;
|
||||||
|
|
||||||
/** @var Step */
|
/** @var Step */
|
||||||
private $step;
|
private $step;
|
||||||
@ -21,11 +21,11 @@ class StepValidationArgs {
|
|||||||
|
|
||||||
/** @param Subject<Payload>[] $subjects */
|
/** @param Subject<Payload>[] $subjects */
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Workflow $workflow,
|
Automation $automation,
|
||||||
Step $step,
|
Step $step,
|
||||||
array $subjects
|
array $subjects
|
||||||
) {
|
) {
|
||||||
$this->workflow = $workflow;
|
$this->automation = $automation;
|
||||||
$this->step = $step;
|
$this->step = $step;
|
||||||
|
|
||||||
foreach ($subjects as $subject) {
|
foreach ($subjects as $subject) {
|
||||||
@ -35,8 +35,8 @@ class StepValidationArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWorkflow(): Workflow {
|
public function getAutomation(): Automation {
|
||||||
return $this->workflow;
|
return $this->automation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStep(): Step {
|
public function getStep(): Step {
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
<?php declare(strict_types = 1);
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Endpoints\Workflows;
|
namespace MailPoet\Automation\Engine\Endpoints\Automations;
|
||||||
|
|
||||||
use MailPoet\API\REST\Request;
|
use MailPoet\API\REST\Request;
|
||||||
use MailPoet\API\REST\Response;
|
use MailPoet\API\REST\Response;
|
||||||
use MailPoet\Automation\Engine\API\Endpoint;
|
use MailPoet\Automation\Engine\API\Endpoint;
|
||||||
use MailPoet\Automation\Engine\Data\WorkflowTemplate;
|
use MailPoet\Automation\Engine\Data\AutomationTemplate;
|
||||||
use MailPoet\Automation\Engine\Storage\WorkflowTemplateStorage;
|
use MailPoet\Automation\Engine\Storage\AutomationTemplateStorage;
|
||||||
use MailPoet\Validator\Builder;
|
use MailPoet\Validator\Builder;
|
||||||
|
|
||||||
class WorkflowTemplatesGetEndpoint extends Endpoint {
|
class AutomationTemplatesGetEndpoint extends Endpoint {
|
||||||
|
|
||||||
|
|
||||||
private $storage;
|
private $storage;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
WorkflowTemplateStorage $storage
|
AutomationTemplateStorage $storage
|
||||||
) {
|
) {
|
||||||
$this->storage = $storage;
|
$this->storage = $storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(Request $request): Response {
|
public function handle(Request $request): Response {
|
||||||
$templates = $this->storage->getTemplates((int)$request->getParam('category'));
|
$templates = $this->storage->getTemplates((int)$request->getParam('category'));
|
||||||
return new Response(array_map(function (WorkflowTemplate $workflow) {
|
return new Response(array_map(function (AutomationTemplate $automation) {
|
||||||
return $workflow->toArray();
|
return $automation->toArray();
|
||||||
}, $templates));
|
}, $templates));
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Endpoints\Automations;
|
||||||
|
|
||||||
|
use MailPoet\API\REST\Request;
|
||||||
|
use MailPoet\API\REST\Response;
|
||||||
|
use MailPoet\Automation\Engine\API\Endpoint;
|
||||||
|
use MailPoet\Automation\Engine\Builder\CreateAutomationFromTemplateController;
|
||||||
|
use MailPoet\Automation\Engine\Mappers\AutomationMapper;
|
||||||
|
use MailPoet\Validator\Builder;
|
||||||
|
|
||||||
|
class AutomationsCreateFromTemplateEndpoint extends Endpoint {
|
||||||
|
/** @var CreateAutomationFromTemplateController */
|
||||||
|
private $createAutomationFromTemplateController;
|
||||||
|
|
||||||
|
/** @var AutomationMapper */
|
||||||
|
private $automationMapper;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
CreateAutomationFromTemplateController $createAutomationFromTemplateController,
|
||||||
|
AutomationMapper $automationMapper
|
||||||
|
) {
|
||||||
|
$this->createAutomationFromTemplateController = $createAutomationFromTemplateController;
|
||||||
|
$this->automationMapper = $automationMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Request $request): Response {
|
||||||
|
$automation = $this->createAutomationFromTemplateController->createAutomation((string)$request->getParam('slug'));
|
||||||
|
return new Response($this->automationMapper->buildAutomation($automation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRequestSchema(): array {
|
||||||
|
return [
|
||||||
|
'slug' => Builder::string()->required(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +1,26 @@
|
|||||||
<?php declare(strict_types = 1);
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Endpoints\Workflows;
|
namespace MailPoet\Automation\Engine\Endpoints\Automations;
|
||||||
|
|
||||||
use MailPoet\API\REST\Request;
|
use MailPoet\API\REST\Request;
|
||||||
use MailPoet\API\REST\Response;
|
use MailPoet\API\REST\Response;
|
||||||
use MailPoet\Automation\Engine\API\Endpoint;
|
use MailPoet\Automation\Engine\API\Endpoint;
|
||||||
use MailPoet\Automation\Engine\Builder\DeleteWorkflowController;
|
use MailPoet\Automation\Engine\Builder\DeleteAutomationController;
|
||||||
use MailPoet\Validator\Builder;
|
use MailPoet\Validator\Builder;
|
||||||
|
|
||||||
class WorkflowsDeleteEndpoint extends Endpoint {
|
class AutomationsDeleteEndpoint extends Endpoint {
|
||||||
/** @var DeleteWorkflowController */
|
/** @var DeleteAutomationController */
|
||||||
private $deleteController;
|
private $deleteController;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
DeleteWorkflowController $deleteController
|
DeleteAutomationController $deleteController
|
||||||
) {
|
) {
|
||||||
$this->deleteController = $deleteController;
|
$this->deleteController = $deleteController;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(Request $request): Response {
|
public function handle(Request $request): Response {
|
||||||
$workflowId = intval($request->getParam('id'));
|
$automationId = intval($request->getParam('id'));
|
||||||
$this->deleteController->deleteWorkflow($workflowId);
|
$this->deleteController->deleteAutomation($automationId);
|
||||||
return new Response(null);
|
return new Response(null);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Endpoints\Automations;
|
||||||
|
|
||||||
|
use MailPoet\API\REST\Request;
|
||||||
|
use MailPoet\API\REST\Response;
|
||||||
|
use MailPoet\Automation\Engine\API\Endpoint;
|
||||||
|
use MailPoet\Automation\Engine\Builder\DuplicateAutomationController;
|
||||||
|
use MailPoet\Automation\Engine\Mappers\AutomationMapper;
|
||||||
|
use MailPoet\Validator\Builder;
|
||||||
|
|
||||||
|
class AutomationsDuplicateEndpoint extends Endpoint {
|
||||||
|
/** @var AutomationMapper */
|
||||||
|
private $automationMapper;
|
||||||
|
|
||||||
|
/** @var DuplicateAutomationController */
|
||||||
|
private $duplicateController;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
DuplicateAutomationController $duplicateController,
|
||||||
|
AutomationMapper $automationMapper
|
||||||
|
) {
|
||||||
|
$this->automationMapper = $automationMapper;
|
||||||
|
$this->duplicateController = $duplicateController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Request $request): Response {
|
||||||
|
$automationId = intval($request->getParam('id'));
|
||||||
|
$duplicate = $this->duplicateController->duplicateAutomation($automationId);
|
||||||
|
return new Response($this->automationMapper->buildAutomation($duplicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRequestSchema(): array {
|
||||||
|
return [
|
||||||
|
'id' => Builder::integer()->required(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Endpoints\Automations;
|
||||||
|
|
||||||
|
use MailPoet\API\REST\Request;
|
||||||
|
use MailPoet\API\REST\Response;
|
||||||
|
use MailPoet\Automation\Engine\API\Endpoint;
|
||||||
|
use MailPoet\Automation\Engine\Mappers\AutomationMapper;
|
||||||
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
|
use MailPoet\Validator\Builder;
|
||||||
|
|
||||||
|
class AutomationsGetEndpoint extends Endpoint {
|
||||||
|
/** @var AutomationMapper */
|
||||||
|
private $automationMapper;
|
||||||
|
|
||||||
|
/** @var AutomationStorage */
|
||||||
|
private $automationStorage;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
AutomationMapper $automationMapper,
|
||||||
|
AutomationStorage $automationStorage
|
||||||
|
) {
|
||||||
|
$this->automationMapper = $automationMapper;
|
||||||
|
$this->automationStorage = $automationStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Request $request): Response {
|
||||||
|
$status = $request->getParam('status') ? (array)$request->getParam('status') : null;
|
||||||
|
$automations = $this->automationStorage->getAutomations($status);
|
||||||
|
return new Response($this->automationMapper->buildAutomationList($automations));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRequestSchema(): array {
|
||||||
|
return [
|
||||||
|
'status' => Builder::array(Builder::string()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user