Refactor form into two separate files
[MAILPOET-3231]
This commit is contained in:
182
assets/js/src/segments/dynamic/dynamic_segments_form.tsx
Normal file
182
assets/js/src/segments/dynamic/dynamic_segments_form.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
assign,
|
||||
compose,
|
||||
has,
|
||||
prop,
|
||||
} from 'lodash/fp';
|
||||
import { useRouteMatch, Link, useHistory } from 'react-router-dom';
|
||||
|
||||
import MailPoet from 'mailpoet';
|
||||
import Background from 'common/background/background';
|
||||
import Heading from 'common/typography/heading/heading';
|
||||
import HideScreenOptions from 'common/hide_screen_options/hide_screen_options';
|
||||
import { EmailSegmentOptions } from './dynamic_segments_filters/email';
|
||||
import { WooCommerceOptions } from './dynamic_segments_filters/woocommerce';
|
||||
import { SubscriberSegmentOptions } from './dynamic_segments_filters/subscriber';
|
||||
import { WooCommerceSubscriptionOptions } from './dynamic_segments_filters/woocommerce_subscription';
|
||||
import { SegmentFormData } from './segment_form_data';
|
||||
import { Form } from './form';
|
||||
|
||||
import {
|
||||
AnyFormItem,
|
||||
FilterValue,
|
||||
GroupFilterValue,
|
||||
SubscriberActionTypes,
|
||||
} from './types';
|
||||
import APIErrorsNotice from '../../notices/api_errors_notice';
|
||||
|
||||
const messages = {
|
||||
onUpdate: (): void => {
|
||||
MailPoet.Notice.success(MailPoet.I18n.t('dynamicSegmentUpdated'));
|
||||
},
|
||||
onCreate: (data): void => {
|
||||
MailPoet.Notice.success(MailPoet.I18n.t('dynamicSegmentAdded'));
|
||||
MailPoet.trackEvent('Segments > Add new', {
|
||||
'MailPoet Free version': MailPoet.version,
|
||||
type: data.segmentType || 'unknown type',
|
||||
subtype: data.action || data.wordpressRole || 'unknown subtype',
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function getAvailableFilters(): GroupFilterValue[] {
|
||||
const filters: GroupFilterValue[] = [
|
||||
{
|
||||
label: MailPoet.I18n.t('email'),
|
||||
options: EmailSegmentOptions,
|
||||
},
|
||||
{
|
||||
label: MailPoet.I18n.t('wpUserRole'),
|
||||
options: SubscriberSegmentOptions,
|
||||
},
|
||||
];
|
||||
if (MailPoet.isWoocommerceActive) {
|
||||
filters.push({
|
||||
label: MailPoet.I18n.t('woocommerce'),
|
||||
options: WooCommerceOptions,
|
||||
});
|
||||
}
|
||||
if (MailPoet.isWoocommerceActive && SegmentFormData.canUseWooSubscriptions) {
|
||||
filters.push({
|
||||
label: MailPoet.I18n.t('woocommerceSubscriptions'),
|
||||
options: WooCommerceSubscriptionOptions,
|
||||
});
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
const DynamicSegmentForm: React.FunctionComponent = () => {
|
||||
const [segmentFilters] = useState(getAvailableFilters());
|
||||
const [errors, setErrors] = useState([]);
|
||||
const [segmentType, setSegmentType] = useState<FilterValue | undefined>(undefined);
|
||||
const [item, setItem] = useState<AnyFormItem>({});
|
||||
const match = useRouteMatch<{id: string}>();
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
function findSegmentType(itemSearch): FilterValue | undefined {
|
||||
let found: FilterValue | undefined;
|
||||
if (itemSearch.action === undefined) {
|
||||
// bc compatibility, the wordpress user role segment doesn't have action
|
||||
return SubscriberSegmentOptions.find(
|
||||
(value) => value.value === SubscriberActionTypes.WORDPRESS_ROLE
|
||||
);
|
||||
}
|
||||
|
||||
segmentFilters.forEach((filter: GroupFilterValue) => {
|
||||
filter.options.forEach((option: FilterValue) => {
|
||||
if (option.group === itemSearch.segmentType) {
|
||||
if (itemSearch.action === option.value) {
|
||||
found = option;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
function convertSavedData(data: {
|
||||
[key: string]: string | number;
|
||||
}): AnyFormItem {
|
||||
let converted: AnyFormItem = JSON.parse(JSON.stringify(data));
|
||||
// for compatibility with older data
|
||||
if (has('link_id', data)) converted = assign(converted, { link_id: data.link_id.toString() });
|
||||
if (has('newsletter_id', data)) converted = assign(converted, { newsletter_id: data.newsletter_id.toString() });
|
||||
if (has('product_id', data)) converted = assign(converted, { product_id: data.product_id.toString() });
|
||||
if (has('category_id', data)) converted = assign(converted, { category_id: data.category_id.toString() });
|
||||
return converted;
|
||||
}
|
||||
|
||||
function loadSegment(segmentId): void {
|
||||
MailPoet.Ajax.post({
|
||||
api_version: MailPoet.apiVersion,
|
||||
endpoint: 'dynamic_segments',
|
||||
action: 'get',
|
||||
data: {
|
||||
id: segmentId,
|
||||
},
|
||||
})
|
||||
.done((response) => {
|
||||
if (response.data.is_plugin_missing) {
|
||||
history.push('/segments');
|
||||
} else {
|
||||
setItem(convertSavedData(response.data));
|
||||
setSegmentType(findSegmentType(response.data));
|
||||
}
|
||||
})
|
||||
.fail(() => {
|
||||
history.push('/segments');
|
||||
});
|
||||
}
|
||||
|
||||
if (match.params.id !== undefined) {
|
||||
loadSegment(match.params.id);
|
||||
}
|
||||
}, [segmentFilters, match.params.id, history]);
|
||||
|
||||
function handleSave(e: Event): void {
|
||||
e.preventDefault();
|
||||
setErrors([]);
|
||||
MailPoet.Ajax.post({
|
||||
api_version: MailPoet.apiVersion,
|
||||
endpoint: 'dynamic_segments',
|
||||
action: 'save',
|
||||
data: item,
|
||||
}).done(() => {
|
||||
history.push('/segments');
|
||||
|
||||
if (match.params.id !== undefined) {
|
||||
messages.onUpdate();
|
||||
} else {
|
||||
messages.onCreate(item);
|
||||
}
|
||||
}).fail(compose([setErrors, prop('errors')]));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Background color="#fff" />
|
||||
<HideScreenOptions />
|
||||
{(errors.length > 0 && (
|
||||
<APIErrorsNotice errors={errors} />
|
||||
))}
|
||||
|
||||
<Heading level={1} className="mailpoet-title">
|
||||
<span>{MailPoet.I18n.t('formPageTitle')}</span>
|
||||
<Link className="mailpoet-button mailpoet-button-small" to="/segments">{MailPoet.I18n.t('backToList')}</Link>
|
||||
</Heading>
|
||||
|
||||
<Form
|
||||
onSave={handleSave}
|
||||
segmentType={segmentType}
|
||||
item={item}
|
||||
onItemChange={setItem}
|
||||
onSegmentTypeChange={setSegmentType}
|
||||
segmentFilters={segmentFilters}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicSegmentForm;
|
@@ -1,258 +1,117 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
assign,
|
||||
compose,
|
||||
has,
|
||||
prop,
|
||||
} from 'lodash/fp';
|
||||
import { useRouteMatch, Link, useHistory } from 'react-router-dom';
|
||||
|
||||
import MailPoet from 'mailpoet';
|
||||
import Background from 'common/background/background';
|
||||
import Button from 'common/button/button';
|
||||
import Heading from 'common/typography/heading/heading';
|
||||
import Input from 'common/form/input/input';
|
||||
import HideScreenOptions from 'common/hide_screen_options/hide_screen_options';
|
||||
import Select from 'common/form/react_select/react_select';
|
||||
import Textarea from 'common/form/textarea/textarea';
|
||||
import { EmailSegmentOptions } from './dynamic_segments_filters/email';
|
||||
import { WooCommerceOptions } from './dynamic_segments_filters/woocommerce';
|
||||
import { SubscriberSegmentOptions } from './dynamic_segments_filters/subscriber';
|
||||
import { WooCommerceSubscriptionOptions } from './dynamic_segments_filters/woocommerce_subscription';
|
||||
import { SubscribersCounter } from './subscribers_counter';
|
||||
import { FormFilterFields } from './form_filter_fields';
|
||||
import { SegmentFormData } from './segment_form_data';
|
||||
import { isFormValid } from './validator';
|
||||
|
||||
import {
|
||||
AnyFormItem,
|
||||
FilterValue,
|
||||
SubscriberActionTypes,
|
||||
GroupFilterValue,
|
||||
} from './types';
|
||||
import APIErrorsNotice from '../../notices/api_errors_notice';
|
||||
|
||||
const messages = {
|
||||
onUpdate: (): void => MailPoet.Notice.success(MailPoet.I18n.t('dynamicSegmentUpdated')),
|
||||
onCreate: (data): void => {
|
||||
MailPoet.Notice.success(MailPoet.I18n.t('dynamicSegmentAdded'));
|
||||
MailPoet.trackEvent('Segments > Add new', {
|
||||
'MailPoet Free version': MailPoet.version,
|
||||
type: data.segmentType || 'unknown type',
|
||||
subtype: data.action || data.wordpressRole || 'unknown subtype',
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
type GroupFilterValue = {
|
||||
label: string;
|
||||
options: FilterValue[];
|
||||
interface Props {
|
||||
onSave: (Event) => void;
|
||||
item: AnyFormItem;
|
||||
segmentType: FilterValue | undefined;
|
||||
onItemChange: (AnyFormItem) => void;
|
||||
onSegmentTypeChange: (FilterValue) => void;
|
||||
segmentFilters: GroupFilterValue[];
|
||||
}
|
||||
|
||||
function getAvailableFilters(): GroupFilterValue[] {
|
||||
const filters: GroupFilterValue[] = [
|
||||
{
|
||||
label: MailPoet.I18n.t('email'),
|
||||
options: EmailSegmentOptions,
|
||||
},
|
||||
{
|
||||
label: MailPoet.I18n.t('wpUserRole'),
|
||||
options: SubscriberSegmentOptions,
|
||||
},
|
||||
];
|
||||
if (MailPoet.isWoocommerceActive) {
|
||||
filters.push({
|
||||
label: MailPoet.I18n.t('woocommerce'),
|
||||
options: WooCommerceOptions,
|
||||
});
|
||||
}
|
||||
if (MailPoet.isWoocommerceActive && SegmentFormData.canUseWooSubscriptions) {
|
||||
filters.push({
|
||||
label: MailPoet.I18n.t('woocommerceSubscriptions'),
|
||||
options: WooCommerceSubscriptionOptions,
|
||||
});
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
const DynamicSegmentForm: React.FunctionComponent = () => {
|
||||
const [segmentFilters] = useState(getAvailableFilters());
|
||||
const [errors, setErrors] = useState([]);
|
||||
const [segmentType, setSegmentType] = useState<FilterValue | undefined>(undefined);
|
||||
const [item, setItem] = useState<AnyFormItem>({});
|
||||
const match = useRouteMatch<{id: string}>();
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
function findSegmentType(itemSearch): FilterValue | undefined {
|
||||
let found;
|
||||
if (itemSearch.action === undefined) {
|
||||
// bc compatibility, the wordpress user role segment doesn't have action
|
||||
return SubscriberSegmentOptions.find(
|
||||
(value) => value.value === SubscriberActionTypes.WORDPRESS_ROLE
|
||||
);
|
||||
}
|
||||
|
||||
segmentFilters.forEach((filter) => {
|
||||
filter.options.forEach((option) => {
|
||||
if (option.group === itemSearch.segmentType) {
|
||||
if (itemSearch.action === option.value) {
|
||||
found = option;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
function convertSavedData(data: {
|
||||
[key: string]: string | number;
|
||||
}): AnyFormItem {
|
||||
const converted = JSON.parse(JSON.stringify(data));
|
||||
// for compatibility with older data
|
||||
if (has('link_id', data)) converted.link_id = data.link_id.toString();
|
||||
if (has('newsletter_id', data)) converted.newsletter_id = data.newsletter_id.toString();
|
||||
if (has('product_id', data)) converted.product_id = data.product_id.toString();
|
||||
if (has('category_id', data)) converted.category_id = data.category_id.toString();
|
||||
return converted;
|
||||
}
|
||||
|
||||
function loadSegment(segmentId): void {
|
||||
MailPoet.Ajax.post({
|
||||
api_version: MailPoet.apiVersion,
|
||||
endpoint: 'dynamic_segments',
|
||||
action: 'get',
|
||||
data: {
|
||||
id: segmentId,
|
||||
},
|
||||
})
|
||||
.done((response) => {
|
||||
if (response.data.is_plugin_missing) {
|
||||
history.push('/segments');
|
||||
} else {
|
||||
setItem(convertSavedData(response.data));
|
||||
setSegmentType(findSegmentType(response.data));
|
||||
}
|
||||
})
|
||||
.fail(() => {
|
||||
history.push('/segments');
|
||||
});
|
||||
}
|
||||
|
||||
if (match.params.id !== undefined) {
|
||||
loadSegment(match.params.id);
|
||||
}
|
||||
}, [segmentFilters, match.params.id, history]);
|
||||
|
||||
function handleSave(e): void {
|
||||
e.preventDefault();
|
||||
setErrors([]);
|
||||
MailPoet.Ajax.post({
|
||||
api_version: MailPoet.apiVersion,
|
||||
endpoint: 'dynamic_segments',
|
||||
action: 'save',
|
||||
data: item,
|
||||
}).done(() => {
|
||||
history.push('/segments');
|
||||
|
||||
if (match.params.id !== undefined) {
|
||||
messages.onUpdate();
|
||||
} else {
|
||||
messages.onCreate(item);
|
||||
}
|
||||
}).fail(compose([setErrors, prop('errors')]));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Background color="#fff" />
|
||||
<HideScreenOptions />
|
||||
{(errors.length > 0 && (
|
||||
<APIErrorsNotice errors={errors} />
|
||||
))}
|
||||
|
||||
<Heading level={1} className="mailpoet-title">
|
||||
<span>{MailPoet.I18n.t('formPageTitle')}</span>
|
||||
<Link className="mailpoet-button mailpoet-button-small" to="/segments">{MailPoet.I18n.t('backToList')}</Link>
|
||||
</Heading>
|
||||
|
||||
<form className="mailpoet_form">
|
||||
<div className="mailpoet-form-grid">
|
||||
<div className="mailpoet-form-field-name form-field-row-name">
|
||||
<Heading level={4}>
|
||||
<label htmlFor="field_name">
|
||||
{MailPoet.I18n.t('name')}
|
||||
</label>
|
||||
</Heading>
|
||||
<div className="mailpoet-form-field">
|
||||
<Input
|
||||
type="text"
|
||||
name="name"
|
||||
id="field_name"
|
||||
defaultValue={item.name}
|
||||
onChange={
|
||||
(e): void => setItem(assign(item, { name: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mailpoet-form-field-description form-field-row-description">
|
||||
<Heading level={4}>
|
||||
<label htmlFor="field_description">
|
||||
{MailPoet.I18n.t('description')}
|
||||
</label>
|
||||
</Heading>
|
||||
<p className="mailpoet-form-description">
|
||||
{MailPoet.I18n.t('segmentDescriptionTip')}
|
||||
</p>
|
||||
<div className="mailpoet-form-field">
|
||||
<Textarea
|
||||
name="description"
|
||||
id="field_description"
|
||||
defaultValue={item.description}
|
||||
onChange={
|
||||
(e): void => setItem(assign(item, { description: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Heading level={4}>
|
||||
<label htmlFor="field_filters">
|
||||
{MailPoet.I18n.t('formPageTitle')}
|
||||
</label>
|
||||
</Heading>
|
||||
<Select
|
||||
placeholder={MailPoet.I18n.t('selectActionPlaceholder')}
|
||||
options={segmentFilters}
|
||||
value={segmentType}
|
||||
onChange={(newValue: FilterValue): void => {
|
||||
setItem(assign(item, {
|
||||
segmentType: newValue.group,
|
||||
action: newValue.value,
|
||||
}));
|
||||
setSegmentType(newValue);
|
||||
}}
|
||||
automationId="select-segment-action"
|
||||
isFullWidth
|
||||
export const Form: React.FunctionComponent<Props> = ({
|
||||
onSave,
|
||||
item,
|
||||
segmentType,
|
||||
onItemChange,
|
||||
onSegmentTypeChange,
|
||||
segmentFilters,
|
||||
}) => (
|
||||
<>
|
||||
<form className="mailpoet_form">
|
||||
<div className="mailpoet-form-grid">
|
||||
<div className="mailpoet-form-field-name form-field-row-name">
|
||||
<Heading level={4}>
|
||||
<label htmlFor="field_name">
|
||||
{MailPoet.I18n.t('name')}
|
||||
</label>
|
||||
</Heading>
|
||||
<div className="mailpoet-form-field">
|
||||
<Input
|
||||
type="text"
|
||||
name="name"
|
||||
id="field_name"
|
||||
defaultValue={item.name}
|
||||
onChange={
|
||||
(e): void => onItemChange(assign(item, { name: e.target.value }))
|
||||
}
|
||||
/>
|
||||
{segmentType !== undefined && (
|
||||
<FormFilterFields
|
||||
segmentType={segmentType}
|
||||
updateItem={setItem}
|
||||
item={item}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<SubscribersCounter item={item} />
|
||||
<div className="mailpoet-form-actions">
|
||||
<Button type="submit" onClick={handleSave} isDisabled={!isFormValid(item)}>
|
||||
{MailPoet.I18n.t('save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicSegmentForm;
|
||||
<div className="mailpoet-form-field-description form-field-row-description">
|
||||
<Heading level={4}>
|
||||
<label htmlFor="field_description">
|
||||
{MailPoet.I18n.t('description')}
|
||||
</label>
|
||||
</Heading>
|
||||
<p className="mailpoet-form-description">
|
||||
{MailPoet.I18n.t('segmentDescriptionTip')}
|
||||
</p>
|
||||
<div className="mailpoet-form-field">
|
||||
<Textarea
|
||||
name="description"
|
||||
id="field_description"
|
||||
defaultValue={item.description}
|
||||
onChange={
|
||||
(e): void => onItemChange(assign(item, { description: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Heading level={4}>
|
||||
<label htmlFor="field_filters">
|
||||
{MailPoet.I18n.t('formPageTitle')}
|
||||
</label>
|
||||
</Heading>
|
||||
<Select
|
||||
placeholder={MailPoet.I18n.t('selectActionPlaceholder')}
|
||||
options={segmentFilters}
|
||||
value={segmentType}
|
||||
onChange={(newValue: FilterValue): void => {
|
||||
onItemChange(assign(item, {
|
||||
segmentType: newValue.group,
|
||||
action: newValue.value,
|
||||
}));
|
||||
onSegmentTypeChange(newValue);
|
||||
}}
|
||||
automationId="select-segment-action"
|
||||
isFullWidth
|
||||
/>
|
||||
{segmentType !== undefined && (
|
||||
<FormFilterFields
|
||||
segmentType={segmentType}
|
||||
updateItem={onItemChange}
|
||||
item={item}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<SubscribersCounter item={item} />
|
||||
<div className="mailpoet-form-actions">
|
||||
<Button type="submit" onClick={onSave} isDisabled={!isFormValid(item)}>
|
||||
{MailPoet.I18n.t('save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
|
@@ -21,6 +21,11 @@ export enum SubscriberActionTypes {
|
||||
SUBSCRIBED_DATE = 'subscribedDate',
|
||||
}
|
||||
|
||||
export type GroupFilterValue = {
|
||||
label: string;
|
||||
options: FilterValue[];
|
||||
}
|
||||
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
|
@@ -11,7 +11,7 @@ import SegmentList from 'segments/list.jsx';
|
||||
import SegmentForm from 'segments/form.jsx';
|
||||
import { GlobalContext, useGlobalContextValue } from 'context/index.jsx';
|
||||
import Notices from 'notices/notices.jsx';
|
||||
import DynamicSegmentForm from './dynamic/form';
|
||||
import DynamicSegmentForm from './dynamic/dynamic_segments_form';
|
||||
import DynamicSegmentList from './dynamic/list.jsx';
|
||||
import ListHeading from './heading';
|
||||
|
||||
|
Reference in New Issue
Block a user