Add control for selecting segments
[MAILPOET-3502]
This commit is contained in:
@@ -1,11 +1,17 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import { compose, get, filter } from 'lodash/fp';
|
||||||
import { useDispatch, useSelect } from '@wordpress/data';
|
import { useDispatch, useSelect } from '@wordpress/data';
|
||||||
|
|
||||||
import MailPoet from 'mailpoet';
|
import MailPoet from 'mailpoet';
|
||||||
import { Grid } from 'common/grid';
|
|
||||||
import Select from 'common/form/select/select';
|
import Select from 'common/form/select/select';
|
||||||
|
import ReactSelect from 'common/form/react_select/react_select';
|
||||||
|
|
||||||
import { AnyValueTypes, WordpressRoleFormItem } from '../types';
|
import {
|
||||||
|
AnyValueTypes,
|
||||||
|
SelectOption,
|
||||||
|
StaticSegment,
|
||||||
|
WordpressRoleFormItem,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
export function validateSubscribedToList(formItems: WordpressRoleFormItem): boolean {
|
export function validateSubscribedToList(formItems: WordpressRoleFormItem): boolean {
|
||||||
return (
|
return (
|
||||||
@@ -24,6 +30,10 @@ export const SubscribedToList: React.FunctionComponent<Props> = ({ filterIndex }
|
|||||||
(select) => select('mailpoet-dynamic-segments-form').getSegmentFilter(filterIndex),
|
(select) => select('mailpoet-dynamic-segments-form').getSegmentFilter(filterIndex),
|
||||||
[filterIndex]
|
[filterIndex]
|
||||||
);
|
);
|
||||||
|
const staticSegmentsList: StaticSegment[] = useSelect(
|
||||||
|
(select) => select('mailpoet-dynamic-segments-form').getStaticSegmentsList(),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const { updateSegmentFilter, updateSegmentFilterFromEvent } = useDispatch('mailpoet-dynamic-segments-form');
|
const { updateSegmentFilter, updateSegmentFilterFromEvent } = useDispatch('mailpoet-dynamic-segments-form');
|
||||||
|
|
||||||
@@ -38,9 +48,13 @@ export const SubscribedToList: React.FunctionComponent<Props> = ({ filterIndex }
|
|||||||
updateSegmentFilter({ operator: AnyValueTypes.ANY }, filterIndex);
|
updateSegmentFilter({ operator: AnyValueTypes.ANY }, filterIndex);
|
||||||
}
|
}
|
||||||
}, [updateSegmentFilter, segment, filterIndex]);
|
}, [updateSegmentFilter, segment, filterIndex]);
|
||||||
|
const options = staticSegmentsList.map((currentValue) => ({
|
||||||
|
value: currentValue.id.toString(),
|
||||||
|
label: currentValue.name,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid.CenteredRow>
|
<>
|
||||||
<Select
|
<Select
|
||||||
key="select"
|
key="select"
|
||||||
value={segment.operator}
|
value={segment.operator}
|
||||||
@@ -52,6 +66,29 @@ export const SubscribedToList: React.FunctionComponent<Props> = ({ filterIndex }
|
|||||||
<option value={AnyValueTypes.ALL}>{MailPoet.I18n.t('allOf')}</option>
|
<option value={AnyValueTypes.ALL}>{MailPoet.I18n.t('allOf')}</option>
|
||||||
<option value={AnyValueTypes.NONE}>{MailPoet.I18n.t('noneOf')}</option>
|
<option value={AnyValueTypes.NONE}>{MailPoet.I18n.t('noneOf')}</option>
|
||||||
</Select>
|
</Select>
|
||||||
</Grid.CenteredRow>
|
<ReactSelect
|
||||||
|
dimension="small"
|
||||||
|
isFullWidth
|
||||||
|
isMulti
|
||||||
|
placeholder={MailPoet.I18n.t('searchLists')}
|
||||||
|
options={options}
|
||||||
|
value={
|
||||||
|
filter(
|
||||||
|
(option) => {
|
||||||
|
if (!segment.segments) return undefined;
|
||||||
|
const segmentId = Number(option.value);
|
||||||
|
return segment.segments.indexOf(segmentId) !== -1;
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onChange={(options: SelectOption[]): void => {
|
||||||
|
updateSegmentFilter(
|
||||||
|
{ segments: options.map(compose([Number, get('value')])) },
|
||||||
|
filterIndex
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -5,6 +5,7 @@ import {
|
|||||||
GroupFilterValue,
|
GroupFilterValue,
|
||||||
Segment,
|
Segment,
|
||||||
StateType,
|
StateType,
|
||||||
|
StaticSegment,
|
||||||
SubscriberCount,
|
SubscriberCount,
|
||||||
WindowCustomFields,
|
WindowCustomFields,
|
||||||
WindowEditableRoles,
|
WindowEditableRoles,
|
||||||
@@ -45,6 +46,9 @@ export const getCustomFieldsList = (state: StateType): WindowCustomFields => (
|
|||||||
export const getSegment = (state: StateType): Segment => (
|
export const getSegment = (state: StateType): Segment => (
|
||||||
state.segment
|
state.segment
|
||||||
);
|
);
|
||||||
|
export const getStaticSegmentsList = (state: StateType): StaticSegment[] => (
|
||||||
|
state.staticSegmentsList
|
||||||
|
);
|
||||||
export const getSubscriberCount = (state: StateType): SubscriberCount => (
|
export const getSubscriberCount = (state: StateType): SubscriberCount => (
|
||||||
state.subscriberCount
|
state.subscriberCount
|
||||||
);
|
);
|
||||||
|
@@ -23,6 +23,7 @@ const STORE = 'mailpoet-dynamic-segments-form';
|
|||||||
export const createStore = (): void => {
|
export const createStore = (): void => {
|
||||||
const defaultState: StateType = {
|
const defaultState: StateType = {
|
||||||
products: window.mailpoet_products,
|
products: window.mailpoet_products,
|
||||||
|
staticSegmentsList: window.mailpoet_static_segments_list,
|
||||||
subscriptionProducts: window.mailpoet_subscription_products,
|
subscriptionProducts: window.mailpoet_subscription_products,
|
||||||
productCategories: window.mailpoet_product_categories,
|
productCategories: window.mailpoet_product_categories,
|
||||||
newslettersList: window.mailpoet_newsletters_list,
|
newslettersList: window.mailpoet_newsletters_list,
|
||||||
|
@@ -68,6 +68,7 @@ export interface WordpressRoleFormItem extends FormItem {
|
|||||||
custom_field_id?: string;
|
custom_field_id?: string;
|
||||||
custom_field_type?: string;
|
custom_field_type?: string;
|
||||||
date_type?: string;
|
date_type?: string;
|
||||||
|
segments?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WooCommerceFormItem extends FormItem {
|
export interface WooCommerceFormItem extends FormItem {
|
||||||
@@ -156,6 +157,13 @@ export type WindowCustomFields = {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
|
export type StaticSegment = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
export interface SegmentFormDataWindow extends Window {
|
export interface SegmentFormDataWindow extends Window {
|
||||||
wordpress_editable_roles_list: WindowEditableRoles;
|
wordpress_editable_roles_list: WindowEditableRoles;
|
||||||
mailpoet_products: WindowProducts;
|
mailpoet_products: WindowProducts;
|
||||||
@@ -166,6 +174,7 @@ export interface SegmentFormDataWindow extends Window {
|
|||||||
mailpoet_custom_fields: WindowCustomFields;
|
mailpoet_custom_fields: WindowCustomFields;
|
||||||
mailpoet_can_use_woocommerce_subscriptions: boolean;
|
mailpoet_can_use_woocommerce_subscriptions: boolean;
|
||||||
mailpoet_woocommerce_currency_symbol: string;
|
mailpoet_woocommerce_currency_symbol: string;
|
||||||
|
mailpoet_static_segments_list: StaticSegment[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StateType {
|
export interface StateType {
|
||||||
@@ -182,6 +191,7 @@ export interface StateType {
|
|||||||
subscriberCount: SubscriberCount,
|
subscriberCount: SubscriberCount,
|
||||||
errors: string[],
|
errors: string[],
|
||||||
allAvailableFilters: GroupFilterValue[],
|
allAvailableFilters: GroupFilterValue[],
|
||||||
|
staticSegmentsList: StaticSegment[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Actions {
|
export enum Actions {
|
||||||
|
@@ -8,9 +8,11 @@ use MailPoet\Cache\TransientCache;
|
|||||||
use MailPoet\Config\ServicesChecker;
|
use MailPoet\Config\ServicesChecker;
|
||||||
use MailPoet\CustomFields\CustomFieldsRepository;
|
use MailPoet\CustomFields\CustomFieldsRepository;
|
||||||
use MailPoet\Entities\DynamicSegmentFilterData;
|
use MailPoet\Entities\DynamicSegmentFilterData;
|
||||||
|
use MailPoet\Entities\SegmentEntity;
|
||||||
use MailPoet\Listing\PageLimit;
|
use MailPoet\Listing\PageLimit;
|
||||||
use MailPoet\Models\Newsletter;
|
use MailPoet\Models\Newsletter;
|
||||||
use MailPoet\Segments\SegmentDependencyValidator;
|
use MailPoet\Segments\SegmentDependencyValidator;
|
||||||
|
use MailPoet\Segments\SegmentsRepository;
|
||||||
use MailPoet\Services\Bridge;
|
use MailPoet\Services\Bridge;
|
||||||
use MailPoet\Settings\SettingsController;
|
use MailPoet\Settings\SettingsController;
|
||||||
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
||||||
@@ -18,6 +20,7 @@ use MailPoet\WooCommerce\Helper as WooCommerceHelper;
|
|||||||
use MailPoet\WP\AutocompletePostListLoader as WPPostListLoader;
|
use MailPoet\WP\AutocompletePostListLoader as WPPostListLoader;
|
||||||
use MailPoet\WP\Functions as WPFunctions;
|
use MailPoet\WP\Functions as WPFunctions;
|
||||||
use MailPoetVendor\Carbon\Carbon;
|
use MailPoetVendor\Carbon\Carbon;
|
||||||
|
use MailPoetVendor\Doctrine\Common\Collections\Criteria;
|
||||||
|
|
||||||
class Segments {
|
class Segments {
|
||||||
/** @var PageRenderer */
|
/** @var PageRenderer */
|
||||||
@@ -56,6 +59,9 @@ class Segments {
|
|||||||
/** @var TransientCache */
|
/** @var TransientCache */
|
||||||
private $transientCache;
|
private $transientCache;
|
||||||
|
|
||||||
|
/** @var SegmentsRepository */
|
||||||
|
private $segmentsRepository;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
PageRenderer $pageRenderer,
|
PageRenderer $pageRenderer,
|
||||||
PageLimit $listingPageLimit,
|
PageLimit $listingPageLimit,
|
||||||
@@ -68,6 +74,7 @@ class Segments {
|
|||||||
CustomFieldsRepository $customFieldsRepository,
|
CustomFieldsRepository $customFieldsRepository,
|
||||||
CustomFieldsResponseBuilder $customFieldsResponseBuilder,
|
CustomFieldsResponseBuilder $customFieldsResponseBuilder,
|
||||||
SegmentDependencyValidator $segmentDependencyValidator,
|
SegmentDependencyValidator $segmentDependencyValidator,
|
||||||
|
SegmentsRepository $segmentsRepository,
|
||||||
TransientCache $transientCache
|
TransientCache $transientCache
|
||||||
) {
|
) {
|
||||||
$this->pageRenderer = $pageRenderer;
|
$this->pageRenderer = $pageRenderer;
|
||||||
@@ -82,6 +89,7 @@ class Segments {
|
|||||||
$this->customFieldsRepository = $customFieldsRepository;
|
$this->customFieldsRepository = $customFieldsRepository;
|
||||||
$this->customFieldsResponseBuilder = $customFieldsResponseBuilder;
|
$this->customFieldsResponseBuilder = $customFieldsResponseBuilder;
|
||||||
$this->transientCache = $transientCache;
|
$this->transientCache = $transientCache;
|
||||||
|
$this->segmentsRepository = $segmentsRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
@@ -112,6 +120,22 @@ class Segments {
|
|||||||
->where('type', Newsletter::TYPE_STANDARD)
|
->where('type', Newsletter::TYPE_STANDARD)
|
||||||
->orderByExpr('ISNULL(sent_at) DESC, sent_at DESC')->findArray();
|
->orderByExpr('ISNULL(sent_at) DESC, sent_at DESC')->findArray();
|
||||||
|
|
||||||
|
$data['static_segments_list'] = [];
|
||||||
|
$criteria = new Criteria();
|
||||||
|
$criteria->where(Criteria::expr()->isNull('deletedAt'));
|
||||||
|
$criteria->andWhere(Criteria::expr()->neq('type', SegmentEntity::TYPE_DYNAMIC));
|
||||||
|
$criteria->orderBy(['name' => 'ASC']);
|
||||||
|
$segments = $this->segmentsRepository->matching($criteria);
|
||||||
|
foreach ($segments as $segment) {
|
||||||
|
$data['static_segments_list'][] = [
|
||||||
|
'id' => $segment->getId(),
|
||||||
|
'name' => $segment->getName(),
|
||||||
|
'type' => $segment->getType(),
|
||||||
|
'description' => $segment->getDescription(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$data['product_categories'] = $this->wpPostListLoader->getWooCommerceCategories();
|
$data['product_categories'] = $this->wpPostListLoader->getWooCommerceCategories();
|
||||||
|
|
||||||
$data['products'] = $this->wpPostListLoader->getProducts();
|
$data['products'] = $this->wpPostListLoader->getProducts();
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace MailPoet\Doctrine;
|
namespace MailPoet\Doctrine;
|
||||||
|
|
||||||
|
use MailPoetVendor\Doctrine\Common\Collections\Collection;
|
||||||
|
use MailPoetVendor\Doctrine\Common\Collections\Criteria;
|
||||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||||
use MailPoetVendor\Doctrine\ORM\EntityRepository as DoctrineEntityRepository;
|
use MailPoetVendor\Doctrine\ORM\EntityRepository as DoctrineEntityRepository;
|
||||||
use MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadata;
|
use MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
@@ -41,6 +43,14 @@ abstract class Repository {
|
|||||||
return $this->doctrineRepository->findBy($criteria, $orderBy, $limit, $offset);
|
return $this->doctrineRepository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Criteria $criteria
|
||||||
|
* @return Collection<int, T>
|
||||||
|
*/
|
||||||
|
public function matching(Criteria $criteria) {
|
||||||
|
return $this->doctrineRepository->matching($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
public function countBy(array $criteria): int {
|
public function countBy(array $criteria): int {
|
||||||
return $this->doctrineRepository->count($criteria);
|
return $this->doctrineRepository->count($criteria);
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
var mailpoet_mss_key_invalid = <%= mss_key_invalid ? 'true' : 'false' %>;
|
var mailpoet_mss_key_invalid = <%= mss_key_invalid ? 'true' : 'false' %>;
|
||||||
var mailpoet_subscribers_count = <%= subscriber_count %>;
|
var mailpoet_subscribers_count = <%= subscriber_count %>;
|
||||||
var mailpoet_custom_fields = <%= json_encode(custom_fields) %>;
|
var mailpoet_custom_fields = <%= json_encode(custom_fields) %>;
|
||||||
|
var mailpoet_static_segments_list = <%= json_encode(static_segments_list) %>;
|
||||||
var mailpoet_has_premium_support = <%= has_premium_support ? 'true' : 'false' %>;
|
var mailpoet_has_premium_support = <%= has_premium_support ? 'true' : 'false' %>;
|
||||||
var wordpress_editable_roles_list = <%= json_encode(wordpress_editable_roles_list) %>;
|
var wordpress_editable_roles_list = <%= json_encode(wordpress_editable_roles_list) %>;
|
||||||
var mailpoet_newsletters_list = <%= json_encode(newsletters_list) %>;
|
var mailpoet_newsletters_list = <%= json_encode(newsletters_list) %>;
|
||||||
@@ -166,7 +167,6 @@
|
|||||||
'inTheLast': _x('in the last', 'Meaning: "Subscriber subscribed in the last 3 days"'),
|
'inTheLast': _x('in the last', 'Meaning: "Subscriber subscribed in the last 3 days"'),
|
||||||
'notInTheLast': _x('not in the last', 'Meaning: "Subscriber subscribed not in the last 3 days"'),
|
'notInTheLast': _x('not in the last', 'Meaning: "Subscriber subscribed not in the last 3 days"'),
|
||||||
|
|
||||||
|
|
||||||
'emailActionNotOpened': _x('not opened', 'Dynamic segment creation: when newsletter was not opened'),
|
'emailActionNotOpened': _x('not opened', 'Dynamic segment creation: when newsletter was not opened'),
|
||||||
'emailActionClicked': _x('clicked', 'Dynamic segment creation: when a newsletter link was clicked'),
|
'emailActionClicked': _x('clicked', 'Dynamic segment creation: when a newsletter link was clicked'),
|
||||||
'emailActionClickedAnyEmail': _x('clicked any email', 'Dynamic segment creation: when a newsletter link in any email was clicked'),
|
'emailActionClickedAnyEmail': _x('clicked any email', 'Dynamic segment creation: when a newsletter link in any email was clicked'),
|
||||||
@@ -185,6 +185,7 @@
|
|||||||
'segmentsActiveSubscription': __('has an active subscription'),
|
'segmentsActiveSubscription': __('has an active subscription'),
|
||||||
'woocommerceSubscriptions': _x('WooCommerce Subscriptions', 'Dynamic segment creation: User selects this to use any WooCommerce Subscriptions filters'),
|
'woocommerceSubscriptions': _x('WooCommerce Subscriptions', 'Dynamic segment creation: User selects this to use any WooCommerce Subscriptions filters'),
|
||||||
'selectWooSubscription': __('Search subscriptions'),
|
'selectWooSubscription': __('Search subscriptions'),
|
||||||
|
'searchLists': __('Search lists'),
|
||||||
|
|
||||||
'anyOf': __('any of'),
|
'anyOf': __('any of'),
|
||||||
'allOf': __('all of'),
|
'allOf': __('all of'),
|
||||||
|
Reference in New Issue
Block a user