Add code to create segment from template
[MAILPOET-5394]
This commit is contained in:
committed by
Aschepikov
parent
41bed445e7
commit
dba4ba4dfc
@ -59,15 +59,30 @@
|
|||||||
|
|
||||||
.mailpoet-templates-card {
|
.mailpoet-templates-card {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
|
border: 1px solid white;
|
||||||
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 336px;
|
width: 334px;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #dcdcde;
|
||||||
|
box-shadow: 0 3px 6px rgba(0, 0, 0, .15);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #2271b1;
|
||||||
|
box-shadow: 0 3px 6px rgba(0, 0, 0, .15);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-templates-card-header {
|
.mailpoet-templates-card-header {
|
||||||
@ -83,7 +98,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 24px;
|
right: 24px;
|
||||||
top: 0;
|
top: -1px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
@ -91,7 +106,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.is-link {
|
button.is-link {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
@ -13,11 +13,15 @@ import { storeName } from './store';
|
|||||||
export function Editor(): JSX.Element {
|
export function Editor(): JSX.Element {
|
||||||
const match = useRouteMatch<{ id: string }>();
|
const match = useRouteMatch<{ id: string }>();
|
||||||
|
|
||||||
const { pageLoaded } = useDispatch(storeName);
|
const { pageLoaded, pageUnloaded } = useDispatch(storeName);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void pageLoaded(match.params.id);
|
void pageLoaded(match.params.id);
|
||||||
}, [match.params.id, pageLoaded]);
|
|
||||||
|
return () => {
|
||||||
|
void pageUnloaded();
|
||||||
|
};
|
||||||
|
}, [match.params.id, pageLoaded, pageUnloaded]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -2,8 +2,10 @@ import { ChangeEvent } from 'react';
|
|||||||
import { select } from '@wordpress/data';
|
import { select } from '@wordpress/data';
|
||||||
import { MailPoet } from 'mailpoet';
|
import { MailPoet } from 'mailpoet';
|
||||||
|
|
||||||
|
import * as ROUTES from '../../routes';
|
||||||
import {
|
import {
|
||||||
Actions,
|
Actions,
|
||||||
|
ActionType,
|
||||||
AnyFormItem,
|
AnyFormItem,
|
||||||
SetSegmentActionType,
|
SetSegmentActionType,
|
||||||
SetErrorsActionType,
|
SetErrorsActionType,
|
||||||
@ -21,6 +23,12 @@ export function setSegment(segment: AnyFormItem): SetSegmentActionType {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unsetSegment(): ActionType {
|
||||||
|
return {
|
||||||
|
type: Actions.UNSET_SEGMENT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setErrors(errors: string[]): SetErrorsActionType {
|
export function setErrors(errors: string[]): SetErrorsActionType {
|
||||||
return {
|
return {
|
||||||
type: Actions.SET_ERRORS,
|
type: Actions.SET_ERRORS,
|
||||||
@ -103,6 +111,10 @@ export function* pageLoaded(segmentId?: number | string): Generator<{
|
|||||||
MailPoet.Modal.loading(false);
|
MailPoet.Modal.loading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function* pageUnloaded() {
|
||||||
|
yield unsetSegment();
|
||||||
|
}
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
onUpdate: (): void => {
|
onUpdate: (): void => {
|
||||||
MailPoet.Notice.success(MailPoet.I18n.t('dynamicSegmentUpdated'));
|
MailPoet.Notice.success(MailPoet.I18n.t('dynamicSegmentUpdated'));
|
||||||
@ -141,3 +153,26 @@ export function* handleSave(segmentId?: number): Generator<{
|
|||||||
yield setErrors(error as string[]);
|
yield setErrors(error as string[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function* createFromTemplate(): Generator<{
|
||||||
|
type: string;
|
||||||
|
segment?: AnyFormItem;
|
||||||
|
}> {
|
||||||
|
MailPoet.Modal.loading(true);
|
||||||
|
const segment = select(storeName).getSegment();
|
||||||
|
yield setErrors([]);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore -- I don't know how to configure typescript to understand this
|
||||||
|
const { error, success } = yield {
|
||||||
|
type: 'SAVE_SEGMENT',
|
||||||
|
segment,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
window.location.href = `admin.php?page=mailpoet-segments#${ROUTES.EDIT_DYNAMIC_SEGMENT}/${segment.id}`;
|
||||||
|
} else {
|
||||||
|
yield setErrors(error as string[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MailPoet.Modal.loading(false);
|
||||||
|
}
|
||||||
|
@ -55,12 +55,13 @@ export async function SAVE_SEGMENT(actionData): Promise<{
|
|||||||
}> {
|
}> {
|
||||||
const segment: AnyFormItem = actionData.segment;
|
const segment: AnyFormItem = actionData.segment;
|
||||||
try {
|
try {
|
||||||
await MailPoet.Ajax.post({
|
const response = await MailPoet.Ajax.post({
|
||||||
api_version: MailPoet.apiVersion,
|
api_version: MailPoet.apiVersion,
|
||||||
endpoint: 'dynamic_segments',
|
endpoint: 'dynamic_segments',
|
||||||
action: 'save',
|
action: 'save',
|
||||||
data: segment,
|
data: segment,
|
||||||
});
|
});
|
||||||
|
segment.id = response.data.id;
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,13 @@ function setSegment(state: StateType, action: SetSegmentActionType): StateType {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unsetSegment(state: StateType): StateType {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
segment: { filters: [] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function setErrors(state: StateType, action: SetErrorsActionType): StateType {
|
function setErrors(state: StateType, action: SetErrorsActionType): StateType {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -69,6 +76,8 @@ export const createReducer =
|
|||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case Actions.SET_SEGMENT:
|
case Actions.SET_SEGMENT:
|
||||||
return setSegment(state, action as SetSegmentActionType);
|
return setSegment(state, action as SetSegmentActionType);
|
||||||
|
case Actions.UNSET_SEGMENT:
|
||||||
|
return unsetSegment(state);
|
||||||
case Actions.SET_ERRORS:
|
case Actions.SET_ERRORS:
|
||||||
return setErrors(state, action as SetErrorsActionType);
|
return setErrors(state, action as SetErrorsActionType);
|
||||||
case Actions.UPDATE_SEGMENT:
|
case Actions.UPDATE_SEGMENT:
|
||||||
|
@ -9,8 +9,10 @@ import {
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { starFilled } from '@wordpress/icons';
|
import { starFilled } from '@wordpress/icons';
|
||||||
import { Tag } from '@woocommerce/components';
|
import { Tag } from '@woocommerce/components';
|
||||||
import { SegmentTemplate } from 'segments/types';
|
import { Segment, SegmentTemplate } from 'segments/types';
|
||||||
import { getCategoryNameBySlug } from 'segments/dynamic/templates/templates';
|
import { getCategoryNameBySlug } from 'segments/dynamic/templates/templates';
|
||||||
|
import { useDispatch, useSelect } from '@wordpress/data';
|
||||||
|
import { storeName } from 'segments/dynamic/store';
|
||||||
|
|
||||||
type TemplateListItemProps = {
|
type TemplateListItemProps = {
|
||||||
template: SegmentTemplate;
|
template: SegmentTemplate;
|
||||||
@ -19,8 +21,36 @@ type TemplateListItemProps = {
|
|||||||
export function TemplateListItem({
|
export function TemplateListItem({
|
||||||
template,
|
template,
|
||||||
}: TemplateListItemProps): JSX.Element {
|
}: TemplateListItemProps): JSX.Element {
|
||||||
|
const segment: Segment = useSelect(
|
||||||
|
(select) => select(storeName).getSegment(),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { updateSegment, createFromTemplate } = useDispatch(storeName);
|
||||||
|
|
||||||
|
function handleSelectTemplate(segmentTemplate: SegmentTemplate): void {
|
||||||
|
segment.name = segmentTemplate.name;
|
||||||
|
segment.description = segmentTemplate.description;
|
||||||
|
segment.filters = segmentTemplate.filters;
|
||||||
|
|
||||||
|
if (segmentTemplate.filtersConnect) {
|
||||||
|
segment.filters_connect = segmentTemplate.filtersConnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSegment({
|
||||||
|
...segment,
|
||||||
|
});
|
||||||
|
createFromTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="mailpoet-templates-card">
|
<Card
|
||||||
|
className="mailpoet-templates-card"
|
||||||
|
onClick={(e): void => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSelectTemplate(template);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<CardHeader className="mailpoet-templates-card-header">
|
<CardHeader className="mailpoet-templates-card-header">
|
||||||
{template.isEssential && (
|
{template.isEssential && (
|
||||||
<Tooltip text={__('Essential segment', 'mailpoet')}>
|
<Tooltip text={__('Essential segment', 'mailpoet')}>
|
||||||
@ -29,9 +59,7 @@ export function TemplateListItem({
|
|||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Button variant="link" href="#/">
|
<Button variant="link">{template.name}</Button>
|
||||||
{template.name}
|
|
||||||
</Button>
|
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody className="mailpoet-templates-card-body">
|
<CardBody className="mailpoet-templates-card-body">
|
||||||
<p>{template.description}</p>
|
<p>{template.description}</p>
|
||||||
|
@ -17,6 +17,9 @@ import {
|
|||||||
templateCategories,
|
templateCategories,
|
||||||
} from 'segments/dynamic/templates/templates';
|
} from 'segments/dynamic/templates/templates';
|
||||||
import * as ROUTES from 'segments/routes';
|
import * as ROUTES from 'segments/routes';
|
||||||
|
import { useSelect } from '@wordpress/data';
|
||||||
|
import { storeName } from 'segments/dynamic/store';
|
||||||
|
import { APIErrorsNotice } from 'notices/api_errors_notice';
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
@ -47,6 +50,11 @@ templateCategories.forEach((category) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function SegmentTemplates(): JSX.Element {
|
export function SegmentTemplates(): JSX.Element {
|
||||||
|
const errors: string[] = useSelect(
|
||||||
|
(select) => select(storeName).getErrors(),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-templates-container">
|
<div className="mailpoet-templates-container">
|
||||||
<HideScreenOptions />
|
<HideScreenOptions />
|
||||||
@ -79,6 +87,10 @@ export function SegmentTemplates(): JSX.Element {
|
|||||||
</FlexItem>
|
</FlexItem>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
{errors.length > 0 && (
|
||||||
|
<APIErrorsNotice errors={errors.map((error) => ({ message: error }))} />
|
||||||
|
)}
|
||||||
|
|
||||||
<TabPanel tabs={tabs}>
|
<TabPanel tabs={tabs}>
|
||||||
{(tab) => (
|
{(tab) => (
|
||||||
<div className="mailpoet-templates-card-grid">
|
<div className="mailpoet-templates-card-grid">
|
||||||
|
@ -302,6 +302,7 @@ export interface StateType {
|
|||||||
export enum Actions {
|
export enum Actions {
|
||||||
SET_SEGMENT = 'SET_SEGMENT',
|
SET_SEGMENT = 'SET_SEGMENT',
|
||||||
SET_ERRORS = 'SET_ERRORS',
|
SET_ERRORS = 'SET_ERRORS',
|
||||||
|
UNSET_SEGMENT = 'UNSET_SEGMENT',
|
||||||
UPDATE_SEGMENT = 'UPDATE_SEGMENT',
|
UPDATE_SEGMENT = 'UPDATE_SEGMENT',
|
||||||
UPDATE_SEGMENT_FILTER = 'UPDATE_SEGMENT_FILTER',
|
UPDATE_SEGMENT_FILTER = 'UPDATE_SEGMENT_FILTER',
|
||||||
UPDATE_SUBSCRIBER_COUNT = 'UPDATE_SUBSCRIBER_COUNT',
|
UPDATE_SUBSCRIBER_COUNT = 'UPDATE_SUBSCRIBER_COUNT',
|
||||||
|
Reference in New Issue
Block a user