Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
0077818bf5 |
@ -71,6 +71,13 @@ anchors:
|
||||
- trunk
|
||||
- release
|
||||
|
||||
only_trunk_and_cot: &only_trunk_and_cot
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- trunk
|
||||
- /^cot-.*/
|
||||
|
||||
multisite_acceptance_config: &multisite_acceptance_config
|
||||
multisite: 1
|
||||
requires:
|
||||
@ -179,10 +186,10 @@ jobs:
|
||||
- run:
|
||||
name: Download additional WP Plugins for tests
|
||||
command: |
|
||||
./do download:woo-commerce-zip 7.1.0
|
||||
./do download:woo-commerce-subscriptions-zip 4.6.0
|
||||
./do download:woo-commerce-memberships-zip 1.23.1
|
||||
./do download:woo-commerce-blocks-zip 8.8.2
|
||||
./do download:woo-commerce-zip 6.8.2
|
||||
./do download:woo-commerce-subscriptions-zip 4.5.1
|
||||
./do download:woo-commerce-memberships-zip 1.23.0
|
||||
./do download:woo-commerce-blocks-zip 8.4.0
|
||||
- run:
|
||||
name: Dump tests ENV variables for acceptance tests
|
||||
command: |
|
||||
@ -311,8 +318,7 @@ jobs:
|
||||
parallelism: 20
|
||||
working_directory: /home/circleci/mailpoet/mailpoet
|
||||
machine:
|
||||
image: ubuntu-2204:2022.10.2
|
||||
docker_layer_caching: false
|
||||
image: ubuntu-2204:2022.07.1
|
||||
parameters:
|
||||
multisite:
|
||||
type: integer
|
||||
@ -350,6 +356,9 @@ jobs:
|
||||
enable_cot_sync:
|
||||
type: integer
|
||||
default: 0
|
||||
allow_fail:
|
||||
type: integer
|
||||
default: 0
|
||||
environment:
|
||||
MYSQL_COMMAND: << parameters.mysql_command >>
|
||||
MYSQL_IMAGE_VERSION: << parameters.mysql_image_version >>
|
||||
@ -412,12 +421,6 @@ jobs:
|
||||
circleci tests glob "tests/acceptance/**/*Cest.php" | circleci tests split --split-by=timings > tests/acceptance/_groups/circleci_split_group
|
||||
fi
|
||||
cat tests/acceptance/_groups/circleci_split_group
|
||||
- run:
|
||||
name: Create docker containers for test
|
||||
# We experienced some failures when creating containers so we do it explicitly with one retry
|
||||
command: |
|
||||
cd tests/docker
|
||||
docker-compose create || docker-compose create
|
||||
- run:
|
||||
name: Run acceptance tests
|
||||
command: |
|
||||
@ -431,6 +434,9 @@ jobs:
|
||||
--xml
|
||||
-g circleci_split_group
|
||||
)
|
||||
if [[ << parameters.allow_fail >> == 1 ]]; then
|
||||
args+=(--no-exit)
|
||||
fi
|
||||
docker-compose run -e SKIP_DEPS=1 \
|
||||
-e CIRCLE_BRANCH=${CIRCLE_BRANCH} \
|
||||
-e CIRCLE_JOB=${CIRCLE_JOB} \
|
||||
@ -438,13 +444,18 @@ jobs:
|
||||
-e ENABLE_COT=<< parameters.enable_cot >> \
|
||||
-e ENABLE_COT_SYNC=<< parameters.enable_cot_sync >> \
|
||||
codeception_acceptance "${args[@]}"
|
||||
- run:
|
||||
name: Check exceptions
|
||||
command: |
|
||||
if [ "$(ls tests/_output/exceptions/*.html)" ]; then
|
||||
echo "There were some exceptions during the tests run"
|
||||
exit 1
|
||||
fi
|
||||
- when:
|
||||
condition:
|
||||
not:
|
||||
equal: [1, << parameters.allow_fail >>]
|
||||
steps:
|
||||
- run:
|
||||
name: Check exceptions
|
||||
command: |
|
||||
if [ "$(ls tests/_output/exceptions/*.html)" ]; then
|
||||
echo "There were some exceptions during the tests run"
|
||||
exit 1
|
||||
fi
|
||||
- store_artifacts:
|
||||
path: tests/_output
|
||||
- store_test_results:
|
||||
@ -483,8 +494,7 @@ jobs:
|
||||
integration_tests:
|
||||
working_directory: /home/circleci/mailpoet/mailpoet
|
||||
machine:
|
||||
image: ubuntu-2204:2022.10.2
|
||||
docker_layer_caching: false
|
||||
image: ubuntu-2204:2022.07.1
|
||||
environment:
|
||||
CODECEPTION_IMAGE_VERSION: << parameters.codeception_image_version >>
|
||||
parameters:
|
||||
@ -509,6 +519,12 @@ jobs:
|
||||
multisite:
|
||||
type: integer
|
||||
default: 0
|
||||
woo_core_version:
|
||||
type: string
|
||||
default: ''
|
||||
allow_fail:
|
||||
type: integer
|
||||
default: 0
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci
|
||||
@ -516,6 +532,14 @@ jobs:
|
||||
name: 'Pull test docker images'
|
||||
# Pull docker images with 3 retries
|
||||
command: i='0';while ! docker-compose -f tests/docker/docker-compose.yml pull && ((i < 3)); do sleep 3 && i=$[$i+1]; done
|
||||
- when:
|
||||
condition: << parameters.woo_core_version >>
|
||||
steps:
|
||||
- run:
|
||||
name: Download WooCommerce Core
|
||||
command: |
|
||||
cd tests/docker
|
||||
docker-compose run --rm -w /project --entrypoint "./do download:woo-commerce-zip << parameters.woo_core_version >>" --no-deps codeception_integration
|
||||
- run:
|
||||
name: 'PHP Integration tests'
|
||||
command: |
|
||||
@ -534,6 +558,9 @@ jobs:
|
||||
if [[ -n '<< parameters.skip_group >>' ]]; then
|
||||
args+=(--skip-group << parameters.skip_group >>)
|
||||
fi
|
||||
if [[ << parameters.allow_fail >> == 1 ]]; then
|
||||
args+=(--no-exit)
|
||||
fi
|
||||
docker-compose run -e SKIP_DEPS=1 \
|
||||
-e CIRCLE_BRANCH=${CIRCLE_BRANCH} \
|
||||
-e CIRCLE_JOB=${CIRCLE_JOB} \
|
||||
@ -626,7 +653,7 @@ workflows:
|
||||
- build
|
||||
- acceptance_tests:
|
||||
<<: *slack-fail-post-step
|
||||
name: acceptance_tests_base_and_woo_cot_off
|
||||
name: acceptance_tests
|
||||
requires:
|
||||
- unit_tests
|
||||
- static_analysis_php8
|
||||
@ -634,10 +661,13 @@ workflows:
|
||||
- qa_php
|
||||
- acceptance_tests:
|
||||
<<: *slack-fail-post-step
|
||||
<<: *only_trunk_and_cot
|
||||
name: acceptance_tests_woo_cot_sync
|
||||
group: woo
|
||||
enable_cot: 1
|
||||
enable_cot_sync: 1
|
||||
allow_fail: 1
|
||||
woo_core_version: woo-cot-beta # Temporarily force COT beta version
|
||||
requires:
|
||||
- unit_tests
|
||||
- static_analysis_php8
|
||||
@ -645,10 +675,24 @@ workflows:
|
||||
- qa_php
|
||||
- acceptance_tests:
|
||||
<<: *slack-fail-post-step
|
||||
<<: *only_trunk_and_cot
|
||||
name: acceptance_tests_woo_cot_no_sync
|
||||
group: woo
|
||||
enable_cot: 1
|
||||
enable_cot_sync: 0
|
||||
allow_fail: 1
|
||||
woo_core_version: woo-cot-beta # Temporarily force COT beta version
|
||||
requires:
|
||||
- unit_tests
|
||||
- static_analysis_php8
|
||||
- qa_js
|
||||
- qa_php
|
||||
- acceptance_tests:
|
||||
<<: *slack-fail-post-step
|
||||
<<: *only_trunk_and_cot
|
||||
name: acceptance_tests_woo_cot_off
|
||||
group: woo
|
||||
woo_core_version: woo-cot-beta # Temporarily force COT beta version
|
||||
requires:
|
||||
- unit_tests
|
||||
- static_analysis_php8
|
||||
@ -661,8 +705,20 @@ workflows:
|
||||
- integration_tests:
|
||||
<<: *slack-fail-post-step
|
||||
group: woo
|
||||
name: integration_test_woocommerce
|
||||
requires:
|
||||
- unit_tests
|
||||
- static_analysis_php8
|
||||
- qa_js
|
||||
- qa_php
|
||||
- integration_tests:
|
||||
<<: *slack-fail-post-step
|
||||
<<: *only_trunk_and_cot
|
||||
group: woo
|
||||
enable_cot: 1
|
||||
enable_cot_sync: 1
|
||||
allow_fail: 1
|
||||
woo_core_version: woo-cot-beta # Temporarily force COT beta version
|
||||
name: integration_test_woo_cot_sync
|
||||
requires:
|
||||
- unit_tests
|
||||
@ -671,9 +727,12 @@ workflows:
|
||||
- qa_php
|
||||
- integration_tests:
|
||||
<<: *slack-fail-post-step
|
||||
<<: *only_trunk_and_cot
|
||||
group: woo
|
||||
enable_cot: 1
|
||||
enable_cot_sync: 0
|
||||
allow_fail: 1
|
||||
woo_core_version: woo-cot-beta # Temporarily force COT beta version
|
||||
name: integration_test_woo_cot_no_sync
|
||||
requires:
|
||||
- unit_tests
|
||||
@ -682,7 +741,9 @@ workflows:
|
||||
- qa_php
|
||||
- integration_tests:
|
||||
<<: *slack-fail-post-step
|
||||
<<: *only_trunk_and_cot
|
||||
group: woo
|
||||
woo_core_version: woo-cot-beta # Temporarily force COT beta version
|
||||
name: integration_test_woo_cot_off
|
||||
requires:
|
||||
- unit_tests
|
||||
@ -718,14 +779,10 @@ workflows:
|
||||
<<: *slack-fail-post-step
|
||||
requires:
|
||||
- build
|
||||
- acceptance_tests_base_and_woo_cot_off
|
||||
- acceptance_tests
|
||||
- js_tests
|
||||
- integration_test_woocommerce
|
||||
- integration_test_base
|
||||
- integration_test_woo_cot_no_sync
|
||||
- integration_test_woo_cot_off
|
||||
- integration_test_woo_cot_sync
|
||||
- acceptance_tests_woo_cot_sync
|
||||
- acceptance_tests_woo_cot_no_sync
|
||||
|
||||
nightly:
|
||||
triggers:
|
||||
@ -750,14 +807,14 @@ workflows:
|
||||
- acceptance_tests:
|
||||
<<: *slack-fail-post-step
|
||||
name: acceptance_oldest
|
||||
woo_core_version: 6.8.0
|
||||
woo_core_version: 6.2.2
|
||||
woo_subscriptions_version: 4.3.0
|
||||
woo_memberships_version: 1.21.0
|
||||
woo_blocks_version: 6.8.0
|
||||
woo_blocks_version: 5.3.2
|
||||
mysql_command: --max_allowed_packet=100M
|
||||
mysql_image_version: 5.7.36
|
||||
codeception_image_version: 7.4-cli_20220605.0
|
||||
wordpress_image_version: wp-5.8_php7.3_20221104.1
|
||||
codeception_image_version: 7.4-cli_20210126.1
|
||||
wordpress_image_version: wp-5.6_php7.2_20220406.1
|
||||
requires:
|
||||
- build
|
||||
- unit_tests:
|
||||
|
15
README.md
15
README.md
@ -126,24 +126,23 @@ You can access this help in your command line running `./do` without parameters.
|
||||
|
||||
[Read the article.](https://mailpoet.atlassian.net/wiki/spaces/MAILPOET/pages/629374977/Adding+new+templates+to+the+plugin)
|
||||
|
||||
## 🚥 Testing with different PHP versions
|
||||
## 🚥 Testing with PHP 7.4 or PHP 8.0
|
||||
|
||||
To switch the environment to a different PHP version:
|
||||
To switch the environment to PHP 7.4/8.0:
|
||||
|
||||
1. Check https://github.com/mailpoet/mailpoet/tree/trunk/dev for a list of available PHP versions. Each directory starting with `php` corresponds to a available version.
|
||||
2. Configure the `wordpress` service in `docker-compose.override.yml` to build from the desired PHP version Dockerfile (replace {PHP_VERSION} with the name of the directory that corresponds to the version that you want to use):
|
||||
1. Configure the `wordpress` service in `docker-compose.override.yml` to build from the php74 Dockerfile:
|
||||
|
||||
```yaml
|
||||
wordpress:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: dev/{PHP_VERSION}/Dockerfile
|
||||
dockerfile: dev/php74/Dockerfile # OR dev/php80/Dockerfile
|
||||
```
|
||||
|
||||
3. Run `docker-compose build wordpress`.
|
||||
4. Start the stack with `./do start`.
|
||||
2. Run `docker-compose build wordpress`.
|
||||
3. Start the stack with `./do start`.
|
||||
|
||||
To switch back to the default PHP version remove what was added in 2) and, run `docker-compose build wordpress` for application container and `docker-compose build test_wordpress` for tests container,
|
||||
To switch back to PHP 8.1 remove what was added in 1) and, run `docker-compose build wordpress` for application container and `docker-compose build test_wordpress` for tests container,
|
||||
and start the stack using `./do start`.
|
||||
|
||||
## ✅ TODO
|
||||
|
@ -1,46 +0,0 @@
|
||||
FROM php:8.2.0RC6-apache
|
||||
|
||||
ARG UID=1000
|
||||
ARG GID=1000
|
||||
|
||||
# additinal extensions
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git zlib1g-dev libzip-dev zip wget gnupg msmtp libpng-dev gettext subversion \
|
||||
&& \
|
||||
# Install NodeJS, enable Corepack
|
||||
curl -sL https://deb.nodesource.com/setup_17.x | bash - && \
|
||||
apt-get install -y nodejs build-essential && \
|
||||
corepack enable && \
|
||||
\
|
||||
# Install WP-CLI
|
||||
curl -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
|
||||
chmod +x /usr/local/bin/wp && \
|
||||
\
|
||||
# Clean up
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY dev/php.ini /usr/local/etc/php/conf.d/php_user.ini
|
||||
|
||||
# msmtp config
|
||||
RUN printf "account default\nhost smtp\nport 1025" > /etc/msmtprc
|
||||
|
||||
# xdebug build an config
|
||||
ENV XDEBUGINI_PATH=/usr/local/etc/php/conf.d/xdebug.ini
|
||||
RUN git clone -b "3.2.0RC2" --depth 1 https://github.com/xdebug/xdebug.git /usr/src/php/ext/xdebug \
|
||||
&& docker-php-ext-configure xdebug --enable-xdebug-dev \
|
||||
&& docker-php-ext-install xdebug \
|
||||
&& mkdir /tmp/debug
|
||||
COPY dev/xdebug.ini /tmp/xdebug.ini
|
||||
RUN cat /tmp/xdebug.ini >> $XDEBUGINI_PATH
|
||||
|
||||
# php extensions
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
RUN docker-php-ext-install mysqli
|
||||
|
||||
# allow .htaccess files (between <Directory /var/www/> and </Directory>, which is WordPress installation)
|
||||
RUN sed -i '/<Directory \/var\/www\/>/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
|
||||
|
||||
# ensure existing content in /var/www/html respects UID and GID, give Node permissions for Corepack
|
||||
RUN chown -R ${UID}:${GID} /var/www/html && \
|
||||
mkdir -p /.node && chown -R ${UID}:${GID} /.node
|
@ -28,7 +28,6 @@ Class `\MailPoet\API\API` becomes available once MailPoet plugin is loaded by Wo
|
||||
- [Add List (addList)](api_methods/AddList.md)
|
||||
- [Add Subscriber (addSubscriber)](api_methods/AddSubscriber.md)
|
||||
- [Add Subscriber Field (addSubscriberField)](api_methods/AddSubscriberField.md)
|
||||
- [Delete List (deleteList)](api_methods/DeleteList.md)
|
||||
- [Get Lists (getLists)](api_methods/GetLists.md)
|
||||
- [Get Subscriber (getSubscriber)](api_methods/GetSubscriber.md)
|
||||
- [Get Subscribers (getSubscribers)](api_methods/GetSubscribers.md)
|
||||
@ -39,7 +38,6 @@ Class `\MailPoet\API\API` becomes available once MailPoet plugin is loaded by Wo
|
||||
- [Subscribe to Lists (subscribeToLists)](api_methods/SubscribeToLists.md)
|
||||
- [Unsubscribe from List (unsubscribeFromList)](api_methods/UnsubscribeFromList.md)
|
||||
- [Unsubscribe from Lists (unsubscribeFromLists)](api_methods/UnsubscribeFromLists.md)
|
||||
- [Update List (updateList)](api_methods/UpdateList.md)
|
||||
|
||||
### Usage examples
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
[back to list](../Readme.md)
|
||||
|
||||
# Delete List
|
||||
|
||||
## `bool deleteList(string $list_id)`
|
||||
|
||||
This method provides functionality for deleting a list that is of the type 'default'.
|
||||
|
||||
It returns a boolean value.
|
||||
|
||||
## Error handling
|
||||
|
||||
All expected errors from the API are exceptions of class `\MailPoet\API\MP\v1\APIException`.
|
||||
Code of the exception is populated to distinguish between different errors.
|
||||
|
||||
An exception of base class `\Exception` can be thrown when something unexpected happens.
|
||||
|
||||
Codes description:
|
||||
|
||||
| Code | Description |
|
||||
| ---- | --------------------------------------------------------------- |
|
||||
| 5 | List does not exist |
|
||||
| 18 | List id is empty |
|
||||
| 20 | List cannot be deleted because it’s used for an automatic email |
|
||||
| 21 | List cannot be deleted because it’s used for a form |
|
||||
| 22 | The list couldn’t be deleted from the database |
|
||||
| 23 | Only lists of the type 'default' can be deleted |
|
@ -18,8 +18,8 @@ This method returns a list of subscribers. To see the subscriber data structure,
|
||||
|
||||
Filter argument supports following array keys.
|
||||
|
||||
| Key | Type | Description |
|
||||
| ------------ | ------------ | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| status | string | Specific status of subscribers. One of values: `unconfirmed`, `subscribed`, `unsubscribed`, `bounced`, `inactive` |
|
||||
| listId | int | List id or dynamic segment id |
|
||||
| minUpdatedAt | DateTime\int | DateTime object or timestamp of the minimal last update of subscribers |
|
||||
| Key | Type | Description |
|
||||
| -------------- | ------------ | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| status | string | Specific status of subscribers. One of values: `unconfirmed`, `subscribed`, `unsubscribed`, `bounced`, `inactive` |
|
||||
| list_id | int | List id or dynamic segment id |
|
||||
| min_updated_at | DateTime\int | DateTime object or timestamp of the minimal last update of subscribers |
|
||||
|
@ -1,39 +0,0 @@
|
||||
[back to list](../Readme.md)
|
||||
|
||||
# Add Subscriber
|
||||
|
||||
## `array updateList(array $list)`
|
||||
|
||||
This method provides functionality for updating a list name or description. Only lists of type 'default' are supported.
|
||||
|
||||
It returns the updated list. See [Get Lists](GetLists.md) for a list data structure description.
|
||||
|
||||
## Arguments
|
||||
|
||||
### `$list` (required)
|
||||
|
||||
An associative array which contains list data.
|
||||
|
||||
| Property | Type | Limits | Description |
|
||||
| ---------------------- | ------------ | --------- | -------------------------- |
|
||||
| id (required) | string | 11 chars | A id of the list. |
|
||||
| name (required) | string | 90 chars | A name of the list. |
|
||||
| description (optional) | string\|null | 250 chars | A description of the list. |
|
||||
|
||||
## Error handling
|
||||
|
||||
All expected errors from the API are exceptions of class `\MailPoet\API\MP\v1\APIException`.
|
||||
Code of the exception is populated to distinguish between different errors.
|
||||
|
||||
An exception of base class `\Exception` can be thrown when something unexpected happens.
|
||||
|
||||
Codes description:
|
||||
|
||||
| Code | Description |
|
||||
| ---- | ----------------------------------------------- |
|
||||
| 5 | The list was not found by id |
|
||||
| 14 | Missing list name |
|
||||
| 15 | Trying to use a list name that is already used |
|
||||
| 18 | Missing list id |
|
||||
| 19 | The list couldn’t be updated in the database |
|
||||
| 23 | Only lists of the type 'default' can be updated |
|
@ -5,6 +5,7 @@
|
||||
"@babel/preset-env"
|
||||
],
|
||||
"plugins": [
|
||||
"babel-plugin-typescript-to-proptypes",
|
||||
[
|
||||
"@babel/plugin-transform-runtime",
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
<?php
|
||||
|
||||
// phpcs:disable PSR1.Classes.ClassDeclaration
|
||||
// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||
@ -117,45 +117,11 @@ class RoboFile extends \Robo\Tasks {
|
||||
}
|
||||
|
||||
public function translationsBuild() {
|
||||
$exclude = implode(',', [
|
||||
'.mp_svn',
|
||||
'assets/css',
|
||||
'assets/img',
|
||||
'assets/js',
|
||||
'generated',
|
||||
'lang',
|
||||
'lib-3rd-party',
|
||||
'mailpoet-premium',
|
||||
'node_modules',
|
||||
'plugin_repository',
|
||||
'prefixer',
|
||||
'tasks',
|
||||
'temp',
|
||||
'tests',
|
||||
'tools',
|
||||
'vendor',
|
||||
'vendor-prefixed',
|
||||
]);
|
||||
|
||||
$headers = escapeshellarg(
|
||||
json_encode([
|
||||
'Report-Msgid-Bugs-To' => 'http://support.mailpoet.com/',
|
||||
'Last-Translator' => 'MailPoet i18n (https://www.transifex.com/organization/wysija)',
|
||||
'Language-Team' => 'MailPoet i18n <https://www.transifex.com/organization/wysija>',
|
||||
'Plural-Forms' => 'nplurals=2; plural=(n != 1);',
|
||||
])
|
||||
);
|
||||
|
||||
$this->collectionBuilder()
|
||||
->taskExec('mkdir -p ' . __DIR__ . '/lang')
|
||||
|
||||
// HTML, HBS
|
||||
->taskExec("php -d memory_limit=-1 tasks/makepot/makepot-views.php . > lang/mailpoet.pot")
|
||||
|
||||
// PHP, JS/TS
|
||||
->taskExec("vendor/bin/wp i18n make-pot --merge --slug=mailpoet --domain=mailpoet --exclude=$exclude --headers=$headers . lang/mailpoet.pot")
|
||||
|
||||
->run();
|
||||
->taskExec(
|
||||
'php -d memory_limit=-1 tasks/makepot/grunt-makepot.php wp-plugin . lang/mailpoet.pot mailpoet .mp_svn,assets,lang,node_modules,plugin_repository,tasks,tests,vendor'
|
||||
)->run();
|
||||
}
|
||||
|
||||
public function translationsGetPotFileFromBuild() {
|
||||
@ -385,25 +351,6 @@ class RoboFile extends \Robo\Tasks {
|
||||
$this->say("Validator metadata generated to: $validatorMetadataDir");
|
||||
}
|
||||
|
||||
public function migrationsNew() {
|
||||
$generator = new \MailPoet\Migrator\Repository();
|
||||
$result = $generator->create();
|
||||
$path = realpath($result['path']);
|
||||
$this->output->writeln('MAILPOET DATABASE MIGRATIONS');
|
||||
$this->output->writeln("============================\n");
|
||||
$this->output->writeln("New migration created ✔\n");
|
||||
$this->output->writeln(" Name: {$result['name']}");
|
||||
$this->output->writeln(" Path: $path");
|
||||
}
|
||||
|
||||
public function migrationsStatus() {
|
||||
return $this->taskExec('vendor/bin/wp mailpoet:migrations:status');
|
||||
}
|
||||
|
||||
public function migrationsRun() {
|
||||
return $this->taskExec('vendor/bin/wp mailpoet:migrations:run');
|
||||
}
|
||||
|
||||
public function qa() {
|
||||
$collection = $this->collectionBuilder();
|
||||
$collection->addCode([$this, 'qaPhp']);
|
||||
@ -425,9 +372,6 @@ class RoboFile extends \Robo\Tasks {
|
||||
$collection->addCode(function() {
|
||||
return $this->qaCodeSniffer([]);
|
||||
});
|
||||
$collection->addCode(function() {
|
||||
return $this->qaMinimalPluginStandard([]);
|
||||
});
|
||||
return $collection->run();
|
||||
}
|
||||
|
||||
@ -539,55 +483,6 @@ class RoboFile extends \Robo\Tasks {
|
||||
|
||||
$stringFilesToCheck = !empty($filesToCheck) ? implode(' ', $filesToCheck) : '.';
|
||||
|
||||
return $this->taskExec($task)
|
||||
->arg('--ignore=' . implode(',', $ignorePatterns))
|
||||
->rawArg($stringFilesToCheck)
|
||||
->run();
|
||||
}
|
||||
|
||||
public function qaMinimalPluginStandard(array $filesToCheck, $opts = ['severity' => 'all']) {
|
||||
$severityFlag = $opts['severity'] === 'all' ? '-w' : '-n';
|
||||
|
||||
$task = implode(' ', [
|
||||
'php -d memory_limit=-1',
|
||||
'./tasks/code_sniffer/vendor/bin/phpcs',
|
||||
'--extensions=php',
|
||||
$severityFlag,
|
||||
'--standard=tasks/code_sniffer/vendor/wporg/plugin-directory/MinimalPluginStandard',
|
||||
'-s',
|
||||
]);
|
||||
|
||||
$ignorePaths = [
|
||||
'.mp_svn',
|
||||
'assets',
|
||||
'doc',
|
||||
'generated',
|
||||
'lib/Config/PopulatorData/Templates',
|
||||
'lib-3rd-party',
|
||||
'node_modules',
|
||||
'plugin_repository',
|
||||
'prefixer/build',
|
||||
'prefixer/vendor',
|
||||
'tasks/code_sniffer/vendor',
|
||||
'tasks/phpstan/vendor',
|
||||
'tasks/makepot',
|
||||
'tools/vendor',
|
||||
'temp',
|
||||
'tests/_data',
|
||||
'tests/_output',
|
||||
'tests/_support/_generated',
|
||||
'vendor',
|
||||
'vendor-prefixed',
|
||||
'views',
|
||||
];
|
||||
|
||||
// the "--ignore" arg takes a list of regexes, we need to anchor and escape them
|
||||
$ignorePatterns = array_map(function (string $path): string {
|
||||
return '^' . preg_quote(__DIR__ . DIRECTORY_SEPARATOR . $path);
|
||||
}, $ignorePaths);
|
||||
|
||||
$stringFilesToCheck = !empty($filesToCheck) ? implode(' ', $filesToCheck) : '.';
|
||||
|
||||
return $this
|
||||
->taskExec($task)
|
||||
->arg('--ignore=' . implode(',', $ignorePatterns))
|
||||
@ -851,9 +746,6 @@ class RoboFile extends \Robo\Tasks {
|
||||
->addCode(function () use ($version) {
|
||||
$this->releaseCreatePullRequest($version);
|
||||
})
|
||||
->addCode(function () use ($version) {
|
||||
$this->releaseRerunCircleWorkflow(\MailPoetTasks\Release\CircleCiController::PROJECT_PREMIUM);
|
||||
})
|
||||
->addCode(function () use ($version) {
|
||||
$this->translationsPrepareLanguagePacks($version);
|
||||
})
|
||||
@ -1171,18 +1063,6 @@ class RoboFile extends \Robo\Tasks {
|
||||
$this->say("Release '$version[name]' info was published on Slack.");
|
||||
}
|
||||
|
||||
public function releaseRerunCircleWorkflow(string $project = null) {
|
||||
$circleciController = $this->createCircleCiController();
|
||||
$result = $circleciController->rerunLatestWorkflow($project);
|
||||
// Sometimes can be useful to know which Circle project workflow was restarted
|
||||
$project = $project ? " for the project '{$project}'" : '';
|
||||
if (!$result) {
|
||||
$this->yell("Circle Workflow{$project} was not restarted", 40, 'red');
|
||||
} else {
|
||||
$this->say("Circle Workflow{$project} was started from the beginning");
|
||||
}
|
||||
}
|
||||
|
||||
public function downloadWooCommerceBlocksZip($tag = null) {
|
||||
$this->createWpOrgDownloader('woo-gutenberg-products-block')
|
||||
->downloadPluginZip('woo-gutenberg-products-block.zip', __DIR__ . '/tests/plugins/', $tag);
|
||||
@ -1207,10 +1087,20 @@ class RoboFile extends \Robo\Tasks {
|
||||
}
|
||||
|
||||
public function downloadWooCommerceZip($tag = null) {
|
||||
if ($tag === 'woo-cot-beta') {
|
||||
$this->downloadWooCommerceCotZip();
|
||||
return;
|
||||
}
|
||||
$this->createWpOrgDownloader('woocommerce')
|
||||
->downloadPluginZip('woocommerce.zip', __DIR__ . '/tests/plugins/', $tag);
|
||||
}
|
||||
|
||||
public function downloadWooCommerceCotZip() {
|
||||
$cotBuildUrl = 'https://github.com/woocommerce/woocommerce/files/9706609/woocommerce.zip';
|
||||
file_put_contents(__DIR__ . '/tests/plugins/woocommerce.zip', file_get_contents($cotBuildUrl));
|
||||
file_put_contents(__DIR__ . '/tests/plugins/woocommerce.zip-info', $cotBuildUrl);
|
||||
}
|
||||
|
||||
public function generateData($generatorName = null, $threads = 1) {
|
||||
require_once __DIR__ . '/tests/DataGenerator/_bootstrap.php';
|
||||
$generator = new \MailPoet\Test\DataGenerator\DataGenerator(new \Codeception\Lib\Console\Output([]));
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
.mailpoet-automation-add-trigger {
|
||||
.mailpoet-automation-workflow-add-trigger {
|
||||
align-items: center;
|
||||
border: 1px dashed #c3c4c7;
|
||||
border-radius: 4px;
|
||||
|
@ -1,4 +1,4 @@
|
||||
.mailpoet-automation-editor-empty-automation {
|
||||
.mailpoet-automation-editor-empty-workflow {
|
||||
align-items: center;
|
||||
display: grid;
|
||||
height: 100%;
|
@ -30,31 +30,3 @@
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-automation-field__error {
|
||||
position: relative;
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
background: right top/26px no-repeat url('../../img/icons/alert.svg');
|
||||
padding-right: 26px;
|
||||
}
|
||||
|
||||
select,
|
||||
input[type=number] {
|
||||
background-position-x: calc(100% - 26px);
|
||||
padding-right: 8px !important;
|
||||
}
|
||||
|
||||
.components-base-control__help,
|
||||
.mailpoet-automation-field-message {
|
||||
color: #d63638;
|
||||
}
|
||||
|
||||
.components-button.mailpoet-automation-button-sidebar-primary,
|
||||
.components-button.mailpoet-automation-button-sidebar-primary.has-text,
|
||||
.components-button.mailpoet-automation-button-sidebar-primary.has-icon {
|
||||
background: #d63638;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
.mailpoet-automation-editor-automation {
|
||||
.mailpoet-automation-editor-workflow {
|
||||
background: #fbfbfb;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.mailpoet-automation-editor-automation-wrapper {
|
||||
.mailpoet-automation-editor-workflow-wrapper {
|
||||
display: grid;
|
||||
padding: 50px 20px;
|
||||
}
|
||||
|
||||
.mailpoet-automation-editor-automation-end {
|
||||
.mailpoet-automation-editor-workflow-end {
|
||||
background: #8c8f94;
|
||||
border-radius: 999999px;
|
||||
fill: white;
|
@ -6,12 +6,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-automation-is-onboarding {
|
||||
.notice {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-automation-listing-heading {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
@ -21,21 +15,6 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.mailpoet-automation-listing-cell-name {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
> a:only-child {
|
||||
bottom: 2px;
|
||||
display: flex;
|
||||
left: 0;
|
||||
padding: 16px 24px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-filter-tab-panel {
|
||||
background-color: #fff;
|
||||
border: 1px solid #dcdcde;
|
||||
|
@ -1,201 +0,0 @@
|
||||
@mixin full-width {
|
||||
margin-left: -20px;
|
||||
padding-left: 104px;
|
||||
padding-right: 104px;
|
||||
width: calc(100% + 60px);
|
||||
|
||||
@media screen and (max-width: 782px) {
|
||||
margin-left: -10px;
|
||||
width: calc(100% + 34px);
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-automation-section {
|
||||
@include full-width;
|
||||
}
|
||||
|
||||
.mailpoet-automation-white-background {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.mailpoet-automation-section-content {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 1072px;
|
||||
padding: 65px 0;
|
||||
|
||||
h2 {
|
||||
font-size: 23px;
|
||||
font-weight: 400;
|
||||
line-height: 32px;
|
||||
margin: 0;
|
||||
padding: 0 0 8px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
margin: 0;
|
||||
padding: 0 0 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-automation-section-hero {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-top: -20px;
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 400;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-top: 16px;
|
||||
max-width: 100%;
|
||||
width: 532px;
|
||||
|
||||
@media screen and (min-width: 1305px) {
|
||||
height: 100%;
|
||||
margin-top: 0;
|
||||
max-height: 294px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-automation-preheading {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
letter-spacing: .2px;
|
||||
line-height: 16px;
|
||||
margin-bottom: 32px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mailpoet-section-templates {
|
||||
padding: 48px 0;
|
||||
|
||||
.components-button {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
text-underline-offset: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-section-template-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 40px;
|
||||
|
||||
> li {
|
||||
flex-grow: 1;
|
||||
margin-right: 8px;
|
||||
max-width: 336px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #fff;
|
||||
border: 1px solid #dcdcde;
|
||||
border-radius: 0;
|
||||
color: #1d2327;
|
||||
cursor: pointer;
|
||||
padding: 24px;
|
||||
text-align: left;
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-section-build-list-button {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: #000;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mailpoet-section-build-your-own {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
|
||||
ol {
|
||||
list-style: decimal-leading-zero inside;
|
||||
margin: 0;
|
||||
max-width: 373px;
|
||||
padding: 0;
|
||||
|
||||
> li {
|
||||
border-bottom: 1px solid #dcdcde;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: 16px auto;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
|
||||
&.open {
|
||||
p {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mailpoet-section-build-list-button {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.marker {
|
||||
color: #ff5301;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
display: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
.mailpoet-option-button {
|
||||
display: flex;
|
||||
margin-top: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mailpoet-option-button-main {
|
||||
border-radius: 2px 0 0 2px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.mailpoet-option-button-opener {
|
||||
background: var(--wp-admin-theme-color);
|
||||
border-radius: 0 2px 2px 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.mailpoet-option-button-opener svg {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.mailpoet-option-button-opener .is-opened svg {
|
||||
transform: scale(-1, -1);
|
||||
transform-origin: center 12.5px;
|
||||
}
|
@ -51,8 +51,4 @@
|
||||
.mailpoet_form_field_block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mailpoet_form_field_input_nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
@ -119,10 +119,6 @@
|
||||
color: $color-stats-average;
|
||||
}
|
||||
|
||||
.mailpoet-statistics-value-number-critical {
|
||||
color: $color-stats-critical;
|
||||
}
|
||||
|
||||
.mailpoet-statistics-value-number-excellent {
|
||||
color: $color-stats-excellent;
|
||||
}
|
||||
|
@ -63,11 +63,6 @@ $form-line-height: 1.4;
|
||||
.mailpoet-has-font-size {
|
||||
line-height: $form-line-height;
|
||||
}
|
||||
|
||||
.mailpoet_submit {
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset fieldset styles in form for backward compatibility. */
|
||||
|
@ -1,12 +0,0 @@
|
||||
.mailpoet_captcha_form {
|
||||
.mailpoet_icon_button {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
}
|
@ -112,11 +112,3 @@
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.authorize-sender-email-and-domain-modal {
|
||||
z-index: 30; // overlay other modals
|
||||
}
|
||||
|
||||
.authorize-sender-email-and-domain-modal-overlay {
|
||||
z-index: $modal-screen-overlay-z-index + 4; // overlay other modals
|
||||
}
|
||||
|
@ -41,15 +41,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-tag-critical {
|
||||
border-color: $color-stats-critical;
|
||||
color: $color-stats-critical;
|
||||
|
||||
&.mailpoet-tag-inverted {
|
||||
background: $color-stats-critical;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-tag-good {
|
||||
border-color: $color-stats-good;
|
||||
color: $color-stats-good;
|
||||
|
@ -11,17 +11,17 @@
|
||||
|
||||
@import './components-automation-editor/add-step-button';
|
||||
@import './components-automation-editor/add-trigger';
|
||||
@import './components-automation-editor/automation';
|
||||
@import './components-automation-editor/block-icon';
|
||||
@import './components-automation-editor/chip';
|
||||
@import './components-automation-editor/dropdown';
|
||||
@import './components-automation-editor/empty-automation';
|
||||
@import './components-automation-editor/empty-workflow';
|
||||
@import './components-automation-editor/errors';
|
||||
@import './components-automation-editor/panel';
|
||||
@import './components-automation-editor/separator';
|
||||
@import './components-automation-editor/status';
|
||||
@import './components-automation-editor/step';
|
||||
@import './components-automation-editor/step-card';
|
||||
@import './components-automation-editor/workflow';
|
||||
@import './components-automation-editor/notices';
|
||||
@import './components-automation-editor/deactivate-modal';
|
||||
|
||||
|
@ -13,34 +13,18 @@ ul.mailpoet-automation-templates {
|
||||
margin: auto;
|
||||
max-width: 982px;
|
||||
padding: 48px 0;
|
||||
}
|
||||
|
||||
.mailpoet-automation-template-list-item {
|
||||
button.components-button {
|
||||
align-content: baseline;
|
||||
align-items: flex-start;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdcde;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
grid-template-rows: 40px auto auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
padding: 24px 24px 26px;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
|
||||
&:disabled,
|
||||
&[aria-disabled='true'] {
|
||||
color: #787c82;
|
||||
cursor: not-allowed;
|
||||
opacity: 1;
|
||||
|
||||
h2 {
|
||||
color: #787c82;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
border: 1px solid #dcdcde;
|
||||
@ -53,15 +37,12 @@ ul.mailpoet-automation-templates {
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, .15);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
>* {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #2271b1;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 21px;
|
||||
@ -73,7 +54,7 @@ ul.mailpoet-automation-templates {
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
&.mailpoet-automation-from-scratch {
|
||||
.mailpoet-automation-from-scratch {
|
||||
button {
|
||||
align-content: center;
|
||||
border: 2px dashed #dcdcde;
|
||||
@ -90,27 +71,4 @@ ul.mailpoet-automation-templates {
|
||||
fill: #dcdcde;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
text-align: right;
|
||||
transform: translateX(24px);
|
||||
|
||||
span {
|
||||
padding: 3px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-automation-template-list-item-coming-soon {
|
||||
.badge span {
|
||||
background: #ffe9cc;
|
||||
color: #1d2327;
|
||||
}
|
||||
}
|
||||
|
||||
.mailpoet-automation-template-list-item-premium {
|
||||
.badge span {
|
||||
background: #ff5301;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,11 @@
|
||||
// automation components
|
||||
|
||||
@import './components-automation/statistics';
|
||||
@import './components-automation/option-button';
|
||||
|
||||
// automation listing
|
||||
|
||||
@import './components-automation-listing/sections';
|
||||
@import './components-automation-listing/listing';
|
||||
@import './components-automation-listing/header';
|
||||
@import './components-automation-listing/search';
|
||||
@import './components-automation-listing/cells/actions';
|
||||
@import './components-automation-listing/cells/status';
|
||||
@import './mailpoet-automation-templates';
|
||||
|
@ -19,4 +19,3 @@
|
||||
@import 'components-public/public';
|
||||
@import 'components-public/animation';
|
||||
@import 'components-public/form_colors';
|
||||
@import 'components-public/captcha';
|
||||
|
@ -69,7 +69,6 @@ $color-badge-video-guide: #46b450;
|
||||
$color-stats-average: #f559c3;
|
||||
$color-stats-good: #ff9f00;
|
||||
$color-stats-excellent: #7ed321;
|
||||
$color-stats-critical: #f00;
|
||||
$color-stats-unknown: $color-primary-inactive;
|
||||
|
||||
// Automation editor
|
||||
|
@ -1,5 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 4.75C7.99594 4.75 4.75 7.99594 4.75 12C4.75 16.0041 7.99594 19.25 12 19.25C16.0041 19.25 19.25 16.0041 19.25 12C19.25 7.99594 16.0041 4.75 12 4.75ZM3.25 12C3.25 7.16751 7.16751 3.25 12 3.25C16.8325 3.25 20.75 7.16751 20.75 12C20.75 16.8325 16.8325 20.75 12 20.75C7.16751 20.75 3.25 16.8325 3.25 12Z" fill="#d63638"/>
|
||||
<path d="M13 7H11V13H13V7Z" fill="#d63638"/>
|
||||
<path d="M13 15H11V17H13V15Z" fill="#d63638"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 565 B |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><rect x="0" fill="none" width="20" height="20"/><g><path d="M2 7h4l5-4v14l-5-4H2V7zm12.69-2.46C14.82 4.59 18 5.92 18 10s-3.18 5.41-3.31 5.46c-.06.03-.13.04-.19.04-.2 0-.39-.12-.46-.31-.11-.26.02-.55.27-.65.11-.05 2.69-1.15 2.69-4.54 0-3.41-2.66-4.53-2.69-4.54-.25-.1-.38-.39-.27-.65.1-.25.39-.38.65-.27zM16 10c0 2.57-2.23 3.43-2.32 3.47-.06.02-.12.03-.18.03-.2 0-.39-.12-.47-.32-.1-.26.04-.55.29-.65.07-.02 1.68-.67 1.68-2.53s-1.61-2.51-1.68-2.53c-.25-.1-.38-.39-.29-.65.1-.25.39-.39.65-.29.09.04 2.32.9 2.32 3.47z"/></g></svg>
|
Before Width: | Height: | Size: 587 B |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><rect x="0" fill="none" width="20" height="20"/><g><path d="M10.25 1.02c5.1 0 8.75 4.04 8.75 9s-3.65 9-8.75 9c-3.2 0-6.02-1.59-7.68-3.99l2.59-1.52c1.1 1.5 2.86 2.51 4.84 2.51 3.3 0 6-2.79 6-6s-2.7-6-6-6c-1.97 0-3.72 1-4.82 2.49L7 8.02l-6 2v-7L2.89 4.6c1.69-2.17 4.36-3.58 7.36-3.58z"/></g></svg>
|
Before Width: | Height: | Size: 355 B |
81
mailpoet/assets/js/src/automation/api/hooks.ts
Normal file
81
mailpoet/assets/js/src/automation/api/hooks.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { api } from '../config';
|
||||
|
||||
const API_URL = `${api.root}/mailpoet/v1/automation`;
|
||||
|
||||
export const request = (
|
||||
path: string,
|
||||
init?: RequestInit,
|
||||
): ReturnType<typeof fetch> => fetch(`${API_URL}/${path}`, init);
|
||||
|
||||
type Error<T> = {
|
||||
response?: Response;
|
||||
data?: T;
|
||||
};
|
||||
|
||||
type State<T> = {
|
||||
data?: T;
|
||||
loading: boolean;
|
||||
error?: Error<T>;
|
||||
};
|
||||
|
||||
type Result<T> = [(init?: RequestInit) => Promise<void>, State<T>];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Data = Record<string, any>;
|
||||
|
||||
export const useMutation = <T extends Data>(
|
||||
path: string,
|
||||
config?: RequestInit,
|
||||
): Result<T> => {
|
||||
const [state, setState] = useState<State<T>>({
|
||||
data: undefined,
|
||||
loading: false,
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const mutation = useCallback(
|
||||
async (init?: RequestInit) => {
|
||||
setState((prevState) => ({ ...prevState, loading: true }));
|
||||
const response = await request(path, {
|
||||
...config,
|
||||
...init,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
...(init?.headers ?? {}),
|
||||
'x-wp-nonce': api.nonce,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const data = await response.json();
|
||||
const error = response.ok ? null : { ...response, data };
|
||||
setState((prevState) => ({ ...prevState, data, error }));
|
||||
} catch (_) {
|
||||
const error = { response };
|
||||
setState((prevState) => ({ ...prevState, error }));
|
||||
} finally {
|
||||
setState((prevState) => ({ ...prevState, loading: false }));
|
||||
}
|
||||
},
|
||||
[config, path],
|
||||
);
|
||||
|
||||
return [mutation, state];
|
||||
};
|
||||
|
||||
export const useQuery = <T extends Data>(
|
||||
path: string,
|
||||
init?: RequestInit,
|
||||
): State<T> => {
|
||||
const [mutation, result] = useMutation<T>(path, init);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
void mutation();
|
||||
},
|
||||
[] /* eslint-disable-line react-hooks/exhaustive-deps -- request only on initial load */,
|
||||
);
|
||||
|
||||
return result;
|
||||
};
|
@ -1,7 +1,9 @@
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { api } from '../config';
|
||||
|
||||
const apiUrl = `${api.root}/mailpoet/v1/`;
|
||||
export * from './hooks';
|
||||
|
||||
const apiUrl = `${api.root}/mailpoet/v1/automation/`;
|
||||
|
||||
export type ApiError = {
|
||||
code?: string;
|
||||
|
@ -1,70 +1,94 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { TopBarWithBeamer } from 'common/top_bar/top_bar';
|
||||
import { Popover, SlotFillProvider } from '@wordpress/components';
|
||||
import { plusIcon } from 'common/button/icon/plus';
|
||||
import { Button, Flex, Popover, SlotFillProvider } from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { initializeApi } from './api';
|
||||
import { registerTranslations } from './i18n';
|
||||
import { initializeApi, useMutation } from './api';
|
||||
import { createStore, storeName } from './listing/store';
|
||||
import { AutomationListing, AutomationListingHeader } from './listing';
|
||||
import { AutomationListing } from './listing';
|
||||
import { registerApiErrorHandler } from './listing/api-error-handler';
|
||||
import { Notices } from './listing/components/notices';
|
||||
import { BuildYourOwnSection, HeroSection, TemplatesSection } from './sections';
|
||||
import { WorkflowListingNotices } from './listing/workflow-listing-notices';
|
||||
import { Onboarding } from './onboarding';
|
||||
import {
|
||||
CreateEmptyWorkflowButton,
|
||||
CreateWorkflowFromTemplateButton,
|
||||
} from './testing';
|
||||
import { MailPoet } from '../mailpoet';
|
||||
|
||||
const trackOpenEvent = () => {
|
||||
MailPoet.trackEvent('Automations > Listing viewed');
|
||||
};
|
||||
|
||||
function Content(): JSX.Element {
|
||||
const [isBooting, setIsBooting] = useState(true);
|
||||
const count = useSelect((select) => select(storeName).getAutomationCount());
|
||||
const count = useSelect((select) => select(storeName).getWorkflowCount());
|
||||
return count > 0 ? <AutomationListing /> : <Onboarding />;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBooting || count === 0) {
|
||||
return;
|
||||
}
|
||||
trackOpenEvent();
|
||||
setIsBooting(false);
|
||||
}, [isBooting, count]);
|
||||
const content =
|
||||
count > 0 ? (
|
||||
<>
|
||||
<AutomationListingHeader />
|
||||
<AutomationListing />
|
||||
</>
|
||||
) : (
|
||||
<HeroSection />
|
||||
);
|
||||
|
||||
// Hide notices on onboarding screen
|
||||
useEffect(() => {
|
||||
const onboardingClass = 'mailpoet-automation-is-onboarding';
|
||||
const element = document.querySelector('body');
|
||||
if (count === 0 && !element.classList.contains(onboardingClass)) {
|
||||
element.classList.add(onboardingClass);
|
||||
}
|
||||
if (count > 0 && element.classList.contains(onboardingClass)) {
|
||||
element.classList.remove(onboardingClass);
|
||||
}
|
||||
}, [count]);
|
||||
function Workflows(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
{content}
|
||||
<TemplatesSection />
|
||||
<BuildYourOwnSection />
|
||||
<TopBarWithBeamer />
|
||||
<Flex className="mailpoet-automation-listing-heading">
|
||||
<h1 className="wp-heading-inline">Automations</h1>
|
||||
<Button
|
||||
href={MailPoet.urls.automationTemplates}
|
||||
icon={plusIcon}
|
||||
variant="primary"
|
||||
className="mailpoet-add-new-button"
|
||||
>
|
||||
New automation
|
||||
</Button>
|
||||
</Flex>
|
||||
<Notices />
|
||||
<Content />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Automations(): JSX.Element {
|
||||
function RecreateSchemaButton(): JSX.Element {
|
||||
const [createSchema, { loading, error }] = useMutation('system/database', {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBarWithBeamer />
|
||||
<Notices />
|
||||
<Content />
|
||||
</>
|
||||
<div>
|
||||
<WorkflowListingNotices />
|
||||
<button
|
||||
className="button button-link-delete"
|
||||
type="button"
|
||||
onClick={() => createSchema()}
|
||||
disabled={loading}
|
||||
>
|
||||
Recreate DB schema (data will be lost)
|
||||
</button>
|
||||
{error && (
|
||||
<div>{error?.data?.message ?? 'An unknown error occurred'}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DeleteSchemaButton(): JSX.Element {
|
||||
const [deleteSchema, { loading, error }] = useMutation('system/database', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
className="button button-link-delete"
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
await deleteSchema();
|
||||
window.location.href =
|
||||
'/wp-admin/admin.php?page=mailpoet-experimental';
|
||||
}}
|
||||
disabled={loading}
|
||||
>
|
||||
Delete DB schema & deactivate feature
|
||||
</button>
|
||||
{error && (
|
||||
<div>{error?.data?.message ?? 'An unknown error occurred'}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -72,8 +96,26 @@ function App(): JSX.Element {
|
||||
return (
|
||||
<SlotFillProvider>
|
||||
<BrowserRouter>
|
||||
<Automations />
|
||||
<Popover.Slot />
|
||||
<div>
|
||||
<Workflows />
|
||||
<div style={{ marginTop: 30, display: 'grid', gridGap: 8 }}>
|
||||
<CreateEmptyWorkflowButton />
|
||||
<CreateWorkflowFromTemplateButton slug="simple-welcome-email">
|
||||
Create testing workflow from template (welcome email)
|
||||
</CreateWorkflowFromTemplateButton>
|
||||
<CreateWorkflowFromTemplateButton slug="welcome-email-sequence">
|
||||
Create testing workflow from template (welcome sequence, only
|
||||
premium)
|
||||
</CreateWorkflowFromTemplateButton>
|
||||
<CreateWorkflowFromTemplateButton slug="advanced-welcome-email-sequence">
|
||||
Create testing workflow from template (advanced welcome sequence,
|
||||
only premium)
|
||||
</CreateWorkflowFromTemplateButton>
|
||||
<RecreateSchemaButton />
|
||||
<DeleteSchemaButton />
|
||||
</div>
|
||||
<Popover.Slot />
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</SlotFillProvider>
|
||||
);
|
||||
@ -84,7 +126,6 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const root = document.getElementById('mailpoet_automation');
|
||||
if (root) {
|
||||
registerTranslations();
|
||||
registerApiErrorHandler();
|
||||
initializeApi();
|
||||
ReactDOM.render(<App />, root);
|
||||
|
@ -1,44 +0,0 @@
|
||||
import { Button, DropdownMenu } from '@wordpress/components';
|
||||
import { chevronDown } from '@wordpress/icons';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Fragment } from '@wordpress/element';
|
||||
import { StepMoreControlsType } from '../../types/filters';
|
||||
|
||||
type OptionButtonPropType = {
|
||||
variant: Button.ButtonVariant;
|
||||
controls: StepMoreControlsType;
|
||||
title: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
export function OptionButton({
|
||||
controls,
|
||||
title,
|
||||
onClick,
|
||||
variant,
|
||||
}: OptionButtonPropType): JSX.Element {
|
||||
const slots = Object.values(controls).filter((item) => item.slot);
|
||||
return (
|
||||
<div className="mailpoet-option-button">
|
||||
<Button
|
||||
variant={variant}
|
||||
className="mailpoet-option-button-main"
|
||||
onClick={onClick}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
{slots.length > 0 &&
|
||||
slots.map(({ key, slot }) => (
|
||||
<Fragment key={`slot-${key}`}>{slot}</Fragment>
|
||||
))}
|
||||
{Object.values(controls).length > 0 && (
|
||||
<DropdownMenu
|
||||
className="mailpoet-option-button-opener"
|
||||
label={__('More', 'mailpoet')}
|
||||
icon={chevronDown}
|
||||
controls={Object.values(controls).map((item) => item.control)}
|
||||
popoverProps={{ position: 'bottom left' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -4,9 +4,9 @@ declare global {
|
||||
root: string;
|
||||
nonce: string;
|
||||
};
|
||||
mailpoet_automation_count: number;
|
||||
mailpoet_workflow_count: number;
|
||||
}
|
||||
}
|
||||
|
||||
export const api = window.mailpoet_automation_api;
|
||||
export const automationCount = window.mailpoet_automation_count;
|
||||
export const workflowCount = window.mailpoet_workflow_count;
|
||||
|
@ -19,7 +19,7 @@ export const registerApiErrorHandler = (): void =>
|
||||
const status = errorObject.data?.status;
|
||||
const code = errorObject.code;
|
||||
|
||||
if (code === 'mailpoet_automation_not_valid') {
|
||||
if (code === 'mailpoet_automation_workflow_not_valid') {
|
||||
dispatch(storeName).setErrors({ steps: errorObject.data.errors });
|
||||
return undefined;
|
||||
}
|
||||
@ -30,7 +30,6 @@ export const registerApiErrorHandler = (): void =>
|
||||
message ?? __('An unknown error occurred.', 'mailpoet'),
|
||||
{ explicitDismiss: true },
|
||||
);
|
||||
dispatch(storeName).setErrors({ steps: [] });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,13 @@ import {
|
||||
Button,
|
||||
} from '@wordpress/components';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { storeName } from '../../store';
|
||||
|
||||
export function TrashButton(): JSX.Element {
|
||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||
const { automation } = useSelect(
|
||||
const { workflow } = useSelect(
|
||||
(select) => ({
|
||||
automation: select(storeName).getAutomationData(),
|
||||
workflow: select(storeName).getWorkflowData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@ -21,8 +20,8 @@ export function TrashButton(): JSX.Element {
|
||||
<>
|
||||
<ConfirmDialog
|
||||
isOpen={showConfirmDialog}
|
||||
title={__('Delete automation', 'mailpoet')}
|
||||
confirmButtonText={__('Yes, delete', 'mailpoet')}
|
||||
title="Delete workflow"
|
||||
confirmButtonText="Yes, delete"
|
||||
onConfirm={async () => {
|
||||
trash(() => {
|
||||
setShowConfirmDialog(false);
|
||||
@ -31,12 +30,7 @@ export function TrashButton(): JSX.Element {
|
||||
onCancel={() => setShowConfirmDialog(false)}
|
||||
__experimentalHideHeader={false}
|
||||
>
|
||||
{sprintf(
|
||||
__('You are about to delete the automation "%s".', 'mailpoet'),
|
||||
automation.name,
|
||||
)}
|
||||
<br />
|
||||
{__(' This will stop it for all subscribers immediately.', 'mailpoet')}
|
||||
You are about to delete the “{workflow.name}” workflow.
|
||||
</ConfirmDialog>
|
||||
|
||||
<Button
|
||||
@ -44,7 +38,7 @@ export function TrashButton(): JSX.Element {
|
||||
isDestructive
|
||||
onClick={() => setShowConfirmDialog(true)}
|
||||
>
|
||||
{__('Move to Trash', 'mailpoet')}
|
||||
Move to Trash
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
|
@ -1,9 +0,0 @@
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export function EmptyAutomation(): JSX.Element {
|
||||
return (
|
||||
<div className="mailpoet-automation-editor-empty-automation">
|
||||
{__('No automation data.', 'mailpoet')}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { _x } from '@wordpress/i18n';
|
||||
import { storeName } from '../../store';
|
||||
import { Statistics as BaseStatistics } from '../../../components/statistics';
|
||||
|
||||
export function Statistics(): JSX.Element {
|
||||
const { automation } = useSelect(
|
||||
(select) => ({
|
||||
automation: select(storeName).getAutomationData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mailpoet-automation-editor-stats">
|
||||
<BaseStatistics
|
||||
items={[
|
||||
{
|
||||
key: 'entered',
|
||||
// translators: Total number of subscribers who entered an automation
|
||||
label: _x('Total Entered', 'automation stats', 'mailpoet'),
|
||||
value: automation.stats.totals.entered,
|
||||
},
|
||||
{
|
||||
key: 'processing',
|
||||
// translators: Total number of subscribers who are being processed in an automation
|
||||
label: _x('Total Processing', 'automation stats', 'mailpoet'),
|
||||
value: automation.stats.totals.in_progress,
|
||||
},
|
||||
{
|
||||
key: 'exited',
|
||||
// translators: Total number of subscribers who exited an automation, no matter the result
|
||||
label: _x('Total Exited', 'automation stats', 'mailpoet'),
|
||||
value: automation.stats.totals.exited,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
import { useState, Fragment } from 'react';
|
||||
import { DropdownMenu } from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { moreVertical, trash } from '@wordpress/icons';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Hooks } from 'wp-js-hooks';
|
||||
import { PremiumModal } from 'common/premium_modal';
|
||||
import { Step as StepData } from './types';
|
||||
import { storeName } from '../../store';
|
||||
import { StepMoreControlsType } from '../../../types/filters';
|
||||
|
||||
type Props = {
|
||||
step: StepData;
|
||||
};
|
||||
|
||||
export function StepMoreMenu({ step }: Props): JSX.Element {
|
||||
const { stepType } = useSelect(
|
||||
(select) => ({
|
||||
stepType: select(storeName).getStepType(step.key),
|
||||
}),
|
||||
[step],
|
||||
);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
const moreControls: StepMoreControlsType = Hooks.applyFilters(
|
||||
'mailpoet.automation.step.more-controls',
|
||||
{
|
||||
delete: {
|
||||
key: 'delete',
|
||||
control: {
|
||||
title: __('Delete step', 'mailpoet'),
|
||||
icon: trash,
|
||||
onClick: () => setShowModal(true),
|
||||
},
|
||||
slot: () => {
|
||||
if (!showModal) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
<PremiumModal
|
||||
onRequestClose={() => {
|
||||
setShowModal(false);
|
||||
}}
|
||||
tracking={{
|
||||
utm_medium: 'upsell_modal',
|
||||
utm_campaign: 'remove_automation_step',
|
||||
}}
|
||||
>
|
||||
{__('You cannot remove a step from the automation.', 'mailpoet')}
|
||||
</PremiumModal>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
step,
|
||||
stepType,
|
||||
);
|
||||
|
||||
const slots = Object.values(moreControls).filter(
|
||||
(item) => item.slot !== undefined,
|
||||
);
|
||||
const controls = Object.values(moreControls).map((item) => item.control);
|
||||
return (
|
||||
<div className="mailpoet-automation-step-more-menu">
|
||||
{slots.map(({ key, slot }) => (
|
||||
<Fragment key={key}>{slot()}</Fragment>
|
||||
))}
|
||||
<DropdownMenu
|
||||
label={__('More', 'mailpoet')}
|
||||
icon={moreVertical}
|
||||
popoverProps={{ position: 'bottom right' }}
|
||||
toggleProps={{ isSmall: true }}
|
||||
controls={Object.values(controls)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
import { ComponentProps, ComponentType, Ref } from 'react';
|
||||
import {
|
||||
__experimentalText as Text,
|
||||
Button,
|
||||
Dropdown as WpDropdown,
|
||||
Button,
|
||||
VisuallyHidden,
|
||||
__experimentalText as Text,
|
||||
} from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useRef } from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { chevronDown } from '@wordpress/icons';
|
||||
import { storeName } from '../../store';
|
||||
import { AutomationStatus } from '../../../listing/automation';
|
||||
import { WorkflowStatus } from '../../../listing/workflow';
|
||||
|
||||
// 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;
|
||||
|
||||
export function DocumentActions({ children }): JSX.Element {
|
||||
const { automationName, automationStatus, showIconLabels } = useSelect(
|
||||
const { workflowName, workflowStatus, showIconLabels } = useSelect(
|
||||
(select) => ({
|
||||
automationName: select(storeName).getAutomationData().name,
|
||||
automationStatus: select(storeName).getAutomationData().status,
|
||||
workflowName: select(storeName).getWorkflowData().name,
|
||||
workflowStatus: select(storeName).getWorkflowData().status,
|
||||
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
||||
}),
|
||||
[],
|
||||
@ -36,9 +36,9 @@ export function DocumentActions({ children }): JSX.Element {
|
||||
const titleRef = useRef();
|
||||
|
||||
let chipClass = 'mailpoet-automation-editor-chip-gray';
|
||||
if (automationStatus === AutomationStatus.ACTIVE) {
|
||||
if (workflowStatus === WorkflowStatus.ACTIVE) {
|
||||
chipClass = 'mailpoet-automation-editor-chip-success';
|
||||
} else if (automationStatus === AutomationStatus.DEACTIVATING) {
|
||||
} else if (workflowStatus === WorkflowStatus.INACTIVE) {
|
||||
chipClass = 'mailpoet-automation-editor-chip-danger';
|
||||
}
|
||||
|
||||
@ -64,21 +64,19 @@ export function DocumentActions({ children }): JSX.Element {
|
||||
as="h1"
|
||||
>
|
||||
<VisuallyHidden as="span">
|
||||
{__('Editing automation:', 'mailpoet')}
|
||||
{__('Editing workflow: ')}
|
||||
</VisuallyHidden>
|
||||
{automationName}
|
||||
{workflowName}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
size="body"
|
||||
className={`edit-site-document-actions__secondary-item ${chipClass}`}
|
||||
>
|
||||
{automationStatus === AutomationStatus.ACTIVE &&
|
||||
__('Active', 'mailpoet')}
|
||||
{automationStatus === AutomationStatus.DEACTIVATING &&
|
||||
__('Deactivating', 'mailpoet')}
|
||||
{automationStatus === AutomationStatus.DRAFT &&
|
||||
__('Draft', 'mailpoet')}
|
||||
{workflowStatus === WorkflowStatus.ACTIVE && __('Active')}
|
||||
{workflowStatus === WorkflowStatus.INACTIVE &&
|
||||
__('Inactive')}
|
||||
{workflowStatus === WorkflowStatus.DRAFT && __('Draft')}
|
||||
</Text>
|
||||
</a>
|
||||
<Button
|
||||
@ -87,9 +85,9 @@ export function DocumentActions({ children }): JSX.Element {
|
||||
aria-expanded={isOpen}
|
||||
aria-haspopup="true"
|
||||
onClick={onToggle}
|
||||
label={__('Change automation name', 'mailpoet')}
|
||||
label={__('Change workflow name')}
|
||||
>
|
||||
{showIconLabels && __('Rename', 'mailpoet')}
|
||||
{showIconLabels && __('Rename')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
@ -12,7 +12,7 @@ import { __ } from '@wordpress/i18n';
|
||||
import { Chip } from '../chip';
|
||||
import { ColoredIcon } from '../icons';
|
||||
import {
|
||||
StepErrors as StepErrorType,
|
||||
StepError as StepErrorType,
|
||||
stepSidebarKey,
|
||||
storeName,
|
||||
} from '../../store';
|
||||
@ -35,17 +35,17 @@ type StepErrorProps = {
|
||||
function StepError({ stepId }: StepErrorProps): JSX.Element {
|
||||
const compositeState = useContext(ErrorsCompositeContext);
|
||||
|
||||
const { steps, automationData } = useSelect(
|
||||
const { steps, workflowData } = useSelect(
|
||||
(select) => ({
|
||||
steps: select(storeName).getSteps(),
|
||||
automationData: select(storeName).getAutomationData(),
|
||||
workflowData: select(storeName).getWorkflowData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const { openSidebar, selectStep } = useDispatch(storeName);
|
||||
|
||||
const stepData = automationData.steps[stepId];
|
||||
const stepData = workflowData.steps[stepId];
|
||||
const step = steps.find(({ key }) => key === stepData.key);
|
||||
|
||||
return (
|
||||
@ -78,10 +78,10 @@ export function Errors(): JSX.Element | null {
|
||||
shift: true,
|
||||
});
|
||||
|
||||
const { errors, automationData } = useSelect(
|
||||
const { errors, workflowData } = useSelect(
|
||||
(select) => ({
|
||||
errors: select(storeName).getErrors(),
|
||||
automationData: select(storeName).getAutomationData(),
|
||||
workflowData: select(storeName).getWorkflowData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@ -93,18 +93,18 @@ export function Errors(): JSX.Element | null {
|
||||
}
|
||||
|
||||
const visited = new Map<string, StepErrorType | undefined>();
|
||||
const ids = automationData.steps.root.next_steps.map(({ id }) => id);
|
||||
const ids = workflowData.steps.root.next_steps.map(({ id }) => id);
|
||||
while (ids.length > 0) {
|
||||
const id = ids.shift();
|
||||
if (!visited.has(id)) {
|
||||
visited.set(id, errors.steps[id]);
|
||||
automationData.steps[id]?.next_steps?.forEach((step) =>
|
||||
workflowData.steps[id]?.next_steps?.forEach((step) =>
|
||||
ids.push(step.id),
|
||||
);
|
||||
}
|
||||
}
|
||||
return [...visited.values()].filter((error) => !!error);
|
||||
}, [errors, automationData]);
|
||||
}, [errors, workflowData]);
|
||||
|
||||
// automatically open the popover when errors appear
|
||||
const hasErrors = stepErrors.length > 0;
|
||||
@ -151,14 +151,11 @@ export function Errors(): JSX.Element | null {
|
||||
<Composite
|
||||
state={compositeState}
|
||||
role="list"
|
||||
aria-label={__('Automation errors', 'mailpoet')}
|
||||
aria-label={__('Workflow errors', 'mailpoet')}
|
||||
className="mailpoet-automation-errors"
|
||||
>
|
||||
<div className="mailpoet-automation-errors-header">
|
||||
{
|
||||
// translators: Label for a list of automation steps that are incomplete or have errors
|
||||
__('The following steps are not fully set:', 'mailpoet')
|
||||
}
|
||||
{__('The following steps are not fully set:', 'mailpoet')}
|
||||
</div>
|
||||
{stepErrors.map((error) => (
|
||||
<StepError key={error.step_id} stepId={error.step_id} />
|
||||
|
@ -1,10 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
NavigableMenu,
|
||||
TextControl,
|
||||
Tooltip,
|
||||
} from '@wordpress/components';
|
||||
import { Button, NavigableMenu, TextControl } from '@wordpress/components';
|
||||
import { dispatch, useDispatch, useSelect } from '@wordpress/data';
|
||||
import { PinnedItems } from '@wordpress/interface';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
@ -13,100 +8,44 @@ import { Errors } from './errors';
|
||||
import { InserterToggle } from './inserter_toggle';
|
||||
import { MoreMenu } from './more_menu';
|
||||
import { storeName } from '../../store';
|
||||
import { AutomationStatus } from '../../../listing/automation';
|
||||
import {
|
||||
DeactivateImmediatelyModal,
|
||||
DeactivateModal,
|
||||
} from '../modals/deactivate-modal';
|
||||
import { WorkflowStatus } from '../../../listing/workflow';
|
||||
import { DeactivateModal } from '../modals/deactivate-modal';
|
||||
|
||||
// See:
|
||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/header/index.js
|
||||
// https://github.com/WordPress/gutenberg/blob/0ee78b1bbe9c6f3e6df99f3b967132fa12bef77d/packages/edit-site/src/components/header/index.js
|
||||
|
||||
function ActivateButton({ label }): JSX.Element {
|
||||
const { errors, isDeactivating } = useSelect(
|
||||
function ActivateButton({ onClick }): JSX.Element {
|
||||
const { errors } = useSelect(
|
||||
(select) => ({
|
||||
errors: select(storeName).getErrors(),
|
||||
isDeactivating:
|
||||
select(storeName).getAutomationData().status ===
|
||||
AutomationStatus.DEACTIVATING,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const { openActivationPanel } = useDispatch(storeName);
|
||||
|
||||
const button = (
|
||||
return (
|
||||
<Button
|
||||
variant="primary"
|
||||
className="editor-post-publish-button"
|
||||
onClick={openActivationPanel}
|
||||
disabled={isDeactivating || !!errors}
|
||||
onClick={onClick}
|
||||
disabled={!!errors}
|
||||
>
|
||||
{label}
|
||||
{__('Activate', 'mailpoet')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
if (isDeactivating) {
|
||||
return (
|
||||
<Tooltip
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
||||
delay={0}
|
||||
text={__(
|
||||
'Editing an active automation is temporarily unavailable. We are working on introducing this functionality.',
|
||||
'mailpoet',
|
||||
)}
|
||||
>
|
||||
{button}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
function UpdateButton(): JSX.Element {
|
||||
const { save } = useDispatch(storeName);
|
||||
|
||||
const { automation } = useSelect(
|
||||
(select) => ({
|
||||
automation: select(storeName).getAutomationData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
if (automation.stats.totals.in_progress === 0) {
|
||||
return (
|
||||
<Button
|
||||
variant="primary"
|
||||
className="editor-post-publish-button"
|
||||
onClick={save}
|
||||
>
|
||||
{__('Update', 'mailpoet')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Tooltip
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
||||
delay={0}
|
||||
text={__(
|
||||
'Editing an active automation is temporarily unavailable. We are working on introducing this functionality.',
|
||||
'mailpoet',
|
||||
)}
|
||||
<Button
|
||||
variant="primary"
|
||||
className="editor-post-publish-button"
|
||||
onClick={save}
|
||||
>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="editor-post-publish-button"
|
||||
onClick={save}
|
||||
disabled
|
||||
>
|
||||
{__('Update', 'mailpoet')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{__('Update', 'mailpoet')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@ -126,7 +65,7 @@ function DeactivateButton(): JSX.Element {
|
||||
const { hasUsersInProgress } = useSelect(
|
||||
(select) => ({
|
||||
hasUsersInProgress:
|
||||
select(storeName).getAutomationData().stats.totals.in_progress > 0,
|
||||
select(storeName).getWorkflowData().stats.totals.in_progress > 0,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@ -160,56 +99,20 @@ function DeactivateButton(): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
function DeactivateNowButton(): JSX.Element {
|
||||
const [showDeactivateModal, setShowDeactivateModal] = useState(false);
|
||||
const [isBusy, setIsBusy] = useState(false);
|
||||
const { hasUsersInProgress } = useSelect(
|
||||
(select) => ({
|
||||
hasUsersInProgress:
|
||||
select(storeName).getAutomationData().stats.totals.in_progress > 0,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const deactivateOrShowModal = () => {
|
||||
if (hasUsersInProgress) {
|
||||
setShowDeactivateModal(true);
|
||||
return;
|
||||
}
|
||||
setIsBusy(true);
|
||||
void dispatch(storeName).deactivate();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{showDeactivateModal && (
|
||||
<DeactivateImmediatelyModal
|
||||
onClose={() => {
|
||||
setShowDeactivateModal(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
isBusy={isBusy}
|
||||
variant="tertiary"
|
||||
onClick={deactivateOrShowModal}
|
||||
>
|
||||
{__('Deactivate now', 'mailpoet')}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type Props = {
|
||||
showInserterToggle: boolean;
|
||||
toggleActivatePanel: () => void;
|
||||
};
|
||||
|
||||
export function Header({ showInserterToggle }: Props): JSX.Element {
|
||||
const { setAutomationName } = useDispatch(storeName);
|
||||
const { automationName, automationStatus } = useSelect(
|
||||
export function Header({
|
||||
showInserterToggle,
|
||||
toggleActivatePanel,
|
||||
}: Props): JSX.Element {
|
||||
const { setWorkflowName } = useDispatch(storeName);
|
||||
const { workflowName, workflowStatus } = useSelect(
|
||||
(select) => ({
|
||||
automationName: select(storeName).getAutomationData().name,
|
||||
automationStatus: select(storeName).getAutomationData().status,
|
||||
workflowName: select(storeName).getWorkflowData().name,
|
||||
workflowStatus: select(storeName).getWorkflowData().status,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@ -234,8 +137,8 @@ export function Header({ showInserterToggle }: Props): JSX.Element {
|
||||
{__('Automation name', 'mailpoet')}
|
||||
</div>
|
||||
<TextControl
|
||||
value={automationName}
|
||||
onChange={(newName) => setAutomationName(newName)}
|
||||
value={workflowName}
|
||||
onChange={(newName) => setWorkflowName(newName)}
|
||||
help={__(
|
||||
`Give the automation a name that indicates its purpose. E.g. "Abandoned cart recovery"`,
|
||||
'mailpoet',
|
||||
@ -249,24 +152,18 @@ export function Header({ showInserterToggle }: Props): JSX.Element {
|
||||
<div className="edit-site-header_end">
|
||||
<div className="edit-site-header__actions">
|
||||
<Errors />
|
||||
{automationStatus === AutomationStatus.DRAFT && (
|
||||
{workflowStatus !== WorkflowStatus.ACTIVE && (
|
||||
<>
|
||||
<SaveDraftButton />
|
||||
<ActivateButton label={__('Activate', 'mailpoet')} />
|
||||
<ActivateButton onClick={toggleActivatePanel} />
|
||||
</>
|
||||
)}
|
||||
{automationStatus === AutomationStatus.ACTIVE && (
|
||||
{workflowStatus === WorkflowStatus.ACTIVE && (
|
||||
<>
|
||||
<DeactivateButton />
|
||||
<UpdateButton />
|
||||
</>
|
||||
)}
|
||||
{automationStatus === AutomationStatus.DEACTIVATING && (
|
||||
<>
|
||||
<DeactivateNowButton />
|
||||
<ActivateButton label={__('Update & Activate', 'mailpoet')} />
|
||||
</>
|
||||
)}
|
||||
<PinnedItems.Slot scope={storeName} />
|
||||
<MoreMenu />
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Button, ToolbarItem } from '@wordpress/components';
|
||||
import { useSelect, useDispatch } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { __, _x } from '@wordpress/i18n';
|
||||
import { plus } from '@wordpress/icons';
|
||||
import { storeName } from '../../store';
|
||||
|
||||
@ -28,11 +28,13 @@ export function InserterToggle(): JSX.Element {
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={toggleInserterSidebar}
|
||||
icon={plus}
|
||||
label={__('Toggle step inserter', 'mailpoet')}
|
||||
label={_x(
|
||||
'Toggle step inserter',
|
||||
'Generic label for step inserter button',
|
||||
)}
|
||||
showTooltip={!showIconLabels}
|
||||
>
|
||||
{showIconLabels &&
|
||||
(!isInserterOpened ? __('Add', 'mailpoet') : __('Close', 'mailpoet'))}
|
||||
{showIconLabels && (!isInserterOpened ? __('Add') : __('Close'))}
|
||||
</ToolbarItem>
|
||||
);
|
||||
}
|
||||
|
@ -20,14 +20,14 @@ export function MoreMenu(): JSX.Element {
|
||||
>
|
||||
{() => (
|
||||
<>
|
||||
<MenuGroup label={_x('View', 'noun', 'mailpoet')}>
|
||||
<MenuGroup label={_x('View', 'noun')}>
|
||||
<PreferenceToggleMenuItem
|
||||
scope={storeName}
|
||||
name="fullscreenMode"
|
||||
label={__('Fullscreen mode', 'mailpoet')}
|
||||
info={__('Work without distraction', 'mailpoet')}
|
||||
messageActivated={__('Fullscreen mode activated', 'mailpoet')}
|
||||
messageDeactivated={__('Fullscreen mode deactivated', 'mailpoet')}
|
||||
label={__('Fullscreen mode')}
|
||||
info={__('Work without distraction')}
|
||||
messageActivated={__('Fullscreen mode activated')}
|
||||
messageDeactivated={__('Fullscreen mode deactivated')}
|
||||
shortcut={displayShortcut.secondary('f')}
|
||||
/>
|
||||
</MenuGroup>
|
||||
|
@ -13,10 +13,7 @@ export const InserterListboxGroup = forwardRef<HTMLDivElement, Props>(
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldSpeak) {
|
||||
speak(
|
||||
// translators: Moving through automation step list using keyboard
|
||||
__('Use left and right arrow keys to move through steps', 'mailpoet'),
|
||||
);
|
||||
speak(__('Use left and right arrow keys to move through blocks'));
|
||||
}
|
||||
}, [shouldSpeak]);
|
||||
|
||||
|
@ -7,7 +7,6 @@ import { PremiumModal } from 'common/premium_modal';
|
||||
import { Inserter } from '../inserter';
|
||||
import { Item } from '../inserter/item';
|
||||
import { storeName } from '../../store';
|
||||
import { AddStepCallbackType } from '../../../types/filters';
|
||||
|
||||
export function InserterPopover(): JSX.Element | null {
|
||||
const popoverRef = useRef<HTMLDivElement>();
|
||||
@ -21,8 +20,8 @@ export function InserterPopover(): JSX.Element | null {
|
||||
const { setInserterPopover } = useDispatch(storeName);
|
||||
|
||||
const onInsert = useCallback((item: Item) => {
|
||||
const addStepCallback: AddStepCallbackType = Hooks.applyFilters(
|
||||
'mailpoet.automation.add_step_callback',
|
||||
const addStepCallback = Hooks.applyFilters(
|
||||
'mailpoet.automation.workflow.add_step_callback',
|
||||
() => {
|
||||
setShowModal(true);
|
||||
},
|
||||
|
@ -2,7 +2,7 @@ import { forwardRef, Fragment, useCallback, useMemo } from 'react';
|
||||
import { SearchControl } from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useRef, useImperativeHandle, useState } from '@wordpress/element';
|
||||
import { __, _x } from '@wordpress/i18n';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { blockDefault, Icon } from '@wordpress/icons';
|
||||
import { Group } from './group';
|
||||
import { Item } from './item';
|
||||
@ -41,26 +41,21 @@ export const Inserter = forwardRef(({ onInsert }: Props, ref): JSX.Element => {
|
||||
{
|
||||
type: 'triggers',
|
||||
title: undefined,
|
||||
// translators: Label for a list of automation steps of type trigger
|
||||
label: _x('Triggers', 'automation steps', 'mailpoet'),
|
||||
label: __('Triggers', 'mailpoet'),
|
||||
items: steps.filter(({ group }) => group === 'triggers'),
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
type: 'actions',
|
||||
// translators: Label for a list of automation steps of type action
|
||||
title: _x('Actions', 'automation steps', 'mailpoet'),
|
||||
// translators: Label for a list of automation steps of type action
|
||||
label: _x('Actions', 'automation steps', 'mailpoet'),
|
||||
title: __('Actions', 'mailpoet'),
|
||||
label: __('Actions', 'mailpoet'),
|
||||
items: steps.filter(({ group }) => group === 'actions'),
|
||||
},
|
||||
{
|
||||
type: 'logical',
|
||||
// translators: Label for a list of logical automation steps (if/else, etc.)
|
||||
title: _x('Logical', 'automation steps', 'mailpoet'),
|
||||
// translators: Label for a list of logical automation steps (if/else, etc.)
|
||||
label: _x('Logical', 'automation steps', 'mailpoet'),
|
||||
title: __('Logical', 'mailpoet'),
|
||||
label: __('Logical', 'mailpoet'),
|
||||
items: steps.filter(({ group }) => group === 'logical'),
|
||||
},
|
||||
],
|
||||
@ -101,8 +96,8 @@ export const Inserter = forwardRef(({ onInsert }: Props, ref): JSX.Element => {
|
||||
setFilterValue(value);
|
||||
}}
|
||||
value={filterValue}
|
||||
label={__('Search for automation steps', 'mailpoet')}
|
||||
placeholder={__('Search', 'mailpoet')}
|
||||
label={__('Search for blocks and patterns')}
|
||||
placeholder={__('Search')}
|
||||
ref={searchRef}
|
||||
/>
|
||||
|
||||
@ -140,7 +135,7 @@ export const Inserter = forwardRef(({ onInsert }: Props, ref): JSX.Element => {
|
||||
className="block-editor-inserter__no-results-icon"
|
||||
icon={blockDefault}
|
||||
/>
|
||||
<p>{__('No results found.', 'mailpoet')}</p>
|
||||
<p>{__('No results found.')}</p>
|
||||
</div>
|
||||
)}
|
||||
</InserterListbox>
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
store as keyboardShortcutsStore,
|
||||
} from '@wordpress/keyboard-shortcuts';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
||||
|
||||
// See:
|
||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/keyboard-shortcuts/index.js
|
||||
@ -25,7 +25,7 @@ export function KeyboardShortcuts(): null {
|
||||
void registerShortcut({
|
||||
name: 'mailpoet/automation-editor/toggle-fullscreen',
|
||||
category: 'global',
|
||||
description: __('Toggle fullscreen mode.', 'mailpoet'),
|
||||
description: __('Toggle fullscreen mode.'),
|
||||
keyCombination: {
|
||||
modifier: 'secondary',
|
||||
character: 'f',
|
||||
@ -35,7 +35,7 @@ export function KeyboardShortcuts(): null {
|
||||
void registerShortcut({
|
||||
name: 'mailpoet/automation-editor/toggle-sidebar',
|
||||
category: 'global',
|
||||
description: __('Show or hide the settings sidebar.', 'mailpoet'),
|
||||
description: __('Show or hide the settings sidebar.'),
|
||||
keyCombination: {
|
||||
modifier: 'primaryShift',
|
||||
character: ',',
|
||||
@ -55,7 +55,7 @@ export function KeyboardShortcuts(): null {
|
||||
} else {
|
||||
const sidebarToOpen = selectedStep()
|
||||
? stepSidebarKey
|
||||
: automationSidebarKey;
|
||||
: workflowSidebarKey;
|
||||
openSidebar(sidebarToOpen);
|
||||
}
|
||||
});
|
||||
|
@ -3,66 +3,23 @@ import { Button, Modal } from '@wordpress/components';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { dispatch, useSelect } from '@wordpress/data';
|
||||
import { storeName } from '../../store';
|
||||
import { AutomationStatus } from '../../../listing/automation';
|
||||
import { WorkflowStatus } from '../../../listing/workflow';
|
||||
|
||||
type DeactivateImmediatelyModalProps = {
|
||||
onClose: () => void;
|
||||
};
|
||||
export function DeactivateImmediatelyModal({
|
||||
onClose,
|
||||
}: DeactivateImmediatelyModalProps): JSX.Element {
|
||||
const [isBusy, setIsBusy] = useState<boolean>(false);
|
||||
return (
|
||||
<Modal
|
||||
className="mailpoet-automatoin-deactivate-modal"
|
||||
title={__('Stop automation for all subscribers?', 'mailpoet')}
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<p>
|
||||
{__(
|
||||
'Are you sure you want to deactivate now? This would stop this automation for all subscribers immediately.',
|
||||
'mailpoet',
|
||||
)}
|
||||
</p>
|
||||
|
||||
<Button
|
||||
isBusy={isBusy}
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
setIsBusy(true);
|
||||
dispatch(storeName).deactivate(true);
|
||||
}}
|
||||
>
|
||||
{__('Deactivate now', 'mailpoet')}
|
||||
</Button>
|
||||
|
||||
<Button disabled={isBusy} variant="tertiary" onClick={onClose}>
|
||||
{__('Cancel', 'mailpoet')}
|
||||
</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
type DeactivateModalProps = {
|
||||
onClose: () => void;
|
||||
};
|
||||
export function DeactivateModal({
|
||||
onClose,
|
||||
}: DeactivateModalProps): JSX.Element {
|
||||
const { automationName } = useSelect(
|
||||
export function DeactivateModal({ onClose }): JSX.Element {
|
||||
const { workflowName } = useSelect(
|
||||
(select) => ({
|
||||
automationName: select(storeName).getAutomationData().name,
|
||||
workflowName: select(storeName).getWorkflowData().name,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const [selected, setSelected] = useState<
|
||||
AutomationStatus.DRAFT | AutomationStatus.DEACTIVATING
|
||||
>(AutomationStatus.DEACTIVATING);
|
||||
WorkflowStatus.INACTIVE | WorkflowStatus.DEACTIVATING
|
||||
>(WorkflowStatus.DEACTIVATING);
|
||||
const [isBusy, setIsBusy] = useState<boolean>(false);
|
||||
// translators: %s is the name of the automation.
|
||||
const title = sprintf(
|
||||
__('Deactivate the "%s" automation?', 'mailpoet'),
|
||||
automationName,
|
||||
workflowName,
|
||||
);
|
||||
|
||||
return (
|
||||
@ -79,7 +36,7 @@ export function DeactivateModal({
|
||||
<li>
|
||||
<label
|
||||
className={
|
||||
selected === AutomationStatus.DEACTIVATING
|
||||
selected === WorkflowStatus.DEACTIVATING
|
||||
? 'mailpoet-automation-option active'
|
||||
: 'mailpoet-automation-option'
|
||||
}
|
||||
@ -89,8 +46,8 @@ export function DeactivateModal({
|
||||
type="radio"
|
||||
disabled={isBusy}
|
||||
name="deactivation-method"
|
||||
checked={selected === AutomationStatus.DEACTIVATING}
|
||||
onChange={() => setSelected(AutomationStatus.DEACTIVATING)}
|
||||
checked={selected === WorkflowStatus.DEACTIVATING}
|
||||
onChange={() => setSelected(WorkflowStatus.DEACTIVATING)}
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
@ -107,7 +64,7 @@ export function DeactivateModal({
|
||||
<li>
|
||||
<label
|
||||
className={
|
||||
selected === AutomationStatus.DRAFT
|
||||
selected === WorkflowStatus.INACTIVE
|
||||
? 'mailpoet-automation-option active'
|
||||
: 'mailpoet-automation-option'
|
||||
}
|
||||
@ -117,8 +74,8 @@ export function DeactivateModal({
|
||||
type="radio"
|
||||
disabled={isBusy}
|
||||
name="deactivation-method"
|
||||
checked={selected === AutomationStatus.DRAFT}
|
||||
onChange={() => setSelected(AutomationStatus.DRAFT)}
|
||||
checked={selected === WorkflowStatus.INACTIVE}
|
||||
onChange={() => setSelected(WorkflowStatus.INACTIVE)}
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
@ -139,9 +96,12 @@ export function DeactivateModal({
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
setIsBusy(true);
|
||||
dispatch(storeName).deactivate(
|
||||
selected !== AutomationStatus.DEACTIVATING,
|
||||
);
|
||||
if (selected === WorkflowStatus.DEACTIVATING) {
|
||||
// @ToDo Use the correct method provided in MAILPOET-4731
|
||||
dispatch(storeName).deactivate();
|
||||
return;
|
||||
}
|
||||
dispatch(storeName).deactivate();
|
||||
}}
|
||||
>
|
||||
{__('Deactivate automation', 'mailpoet')}
|
||||
|
@ -4,7 +4,7 @@ import { Button, Spinner } from '@wordpress/components';
|
||||
import { closeSmall } from '@wordpress/icons';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { storeName } from '../../store';
|
||||
import { AutomationStatus } from '../../../listing/automation';
|
||||
import { WorkflowStatus } from '../../../listing/workflow';
|
||||
import { MailPoet } from '../../../../mailpoet';
|
||||
|
||||
function PreStep({ onClose }): JSX.Element {
|
||||
@ -58,9 +58,9 @@ function PreStep({ onClose }): JSX.Element {
|
||||
}
|
||||
|
||||
function PostStep({ onClose }): JSX.Element {
|
||||
const { automation } = useSelect(
|
||||
const { workflow } = useSelect(
|
||||
(select) => ({
|
||||
automation: select(storeName).getAutomationData(),
|
||||
workflow: select(storeName).getWorkflowData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@ -81,10 +81,10 @@ function PostStep({ onClose }): JSX.Element {
|
||||
|
||||
<div className="mailpoet-automation-activate-panel__body">
|
||||
<div className="mailpoet-automation-activate-panel__section">
|
||||
{sprintf(__('"%s" is now live.', 'mailpoet'), automation.name)}
|
||||
{sprintf(__('"%s" is now live.', 'mailpoet'), workflow.name)}
|
||||
</div>
|
||||
<p>
|
||||
<strong>{__('What’s next?', 'mailpoet')}</strong>
|
||||
<strong>{__("What's next?", 'mailpoet')}</strong>
|
||||
</p>
|
||||
<p>
|
||||
{__(
|
||||
@ -100,31 +100,29 @@ function PostStep({ onClose }): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
export function ActivatePanel(): JSX.Element {
|
||||
const { automation, errors } = useSelect(
|
||||
export function ActivatePanel({ onClose }): JSX.Element {
|
||||
const { workflow, errors } = useSelect(
|
||||
(select) => ({
|
||||
errors: select(storeName).getErrors(),
|
||||
automation: select(storeName).getAutomationData(),
|
||||
workflow: select(storeName).getWorkflowData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const { closeActivationPanel } = useDispatch(storeName);
|
||||
|
||||
useEffect(() => {
|
||||
if (errors) {
|
||||
closeActivationPanel();
|
||||
onClose();
|
||||
}
|
||||
}, [errors, closeActivationPanel]);
|
||||
}, [errors, onClose]);
|
||||
|
||||
if (errors) {
|
||||
return null;
|
||||
}
|
||||
const isActive = automation.status === AutomationStatus.ACTIVE;
|
||||
const isActive = workflow.status === WorkflowStatus.ACTIVE;
|
||||
return (
|
||||
<div className="mailpoet-automation-activate-panel">
|
||||
{isActive && <PostStep onClose={closeActivationPanel} />}
|
||||
{!isActive && <PreStep onClose={closeActivationPanel} />}
|
||||
{isActive && <PostStep onClose={onClose} />}
|
||||
{!isActive && <PreStep onClose={onClose} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
import { PanelBody as WpPanelBody } from '@wordpress/components';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
type Props = WpPanelBody.Props & {
|
||||
hasErrors?: boolean;
|
||||
};
|
||||
|
||||
export function PanelBody({ hasErrors = false, ...props }: Props): JSX.Element {
|
||||
const [isOpened, setIsOpened] = useState(props.initialOpen);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasErrors) {
|
||||
setIsOpened(true);
|
||||
}
|
||||
}, [hasErrors]);
|
||||
|
||||
return (
|
||||
<WpPanelBody
|
||||
opened={isOpened}
|
||||
onToggle={() => setIsOpened((prevState) => !prevState)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { Dropdown, TextControl } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { edit, Icon } from '@wordpress/icons';
|
||||
import { PlainBodyTitle } from './plain-body-title';
|
||||
import { TitleActionButton } from './title-action-button';
|
||||
@ -26,7 +25,7 @@ export function StepName({
|
||||
<TitleActionButton
|
||||
onClick={onToggle}
|
||||
aria-expanded={isOpen}
|
||||
aria-label={__('Edit step name', 'mailpoet')}
|
||||
aria-label="Edit step name"
|
||||
>
|
||||
<Icon icon={edit} size={16} />
|
||||
</TitleActionButton>
|
||||
@ -34,15 +33,13 @@ export function StepName({
|
||||
)}
|
||||
renderContent={() => (
|
||||
<TextControl
|
||||
label={__('Step name', 'mailpoet')}
|
||||
label="Step name"
|
||||
className="mailpoet-step-name-input"
|
||||
placeholder={defaultName}
|
||||
value={currentName}
|
||||
onChange={update}
|
||||
help={__(
|
||||
'Give the automation step a name that indicates its purpose. E.g "Abandoned cart recovery". This name will be displayed only to you and not to the clients.',
|
||||
'mailpoet',
|
||||
)}
|
||||
help="Give the automation step a name that indicates its purpose. E.g
|
||||
“Abandoned cart recovery”. This name will be displayed only to you and not to the clients."
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Button } from '@wordpress/components';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
||||
|
||||
// See:
|
||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/sidebar/settings-header/index.js
|
||||
@ -13,29 +12,29 @@ type Props = {
|
||||
|
||||
export function Header({ sidebarKey }: Props): JSX.Element {
|
||||
const { openSidebar } = useDispatch(storeName);
|
||||
const openAutomationSettings = () => openSidebar(automationSidebarKey);
|
||||
const openWorkflowSettings = () => openSidebar(workflowSidebarKey);
|
||||
const openStepSettings = () => openSidebar(stepSidebarKey);
|
||||
|
||||
const [automationAriaLabel, automationActiveClass] =
|
||||
sidebarKey === automationSidebarKey
|
||||
? [__('Automation (selected)', 'mailpoet'), 'is-active']
|
||||
: [__('Automation', 'mailpoet'), ''];
|
||||
const [workflowAriaLabel, workflowActiveClass] =
|
||||
sidebarKey === workflowSidebarKey
|
||||
? ['Workflow (selected)', 'is-active']
|
||||
: ['Workflow', ''];
|
||||
|
||||
const [stepAriaLabel, stepActiveClass] =
|
||||
sidebarKey === stepSidebarKey
|
||||
? [__('Step (selected)', 'mailpoet'), 'is-active']
|
||||
: [__('Step', 'mailpoet'), ''];
|
||||
? ['Step (selected)', 'is-active']
|
||||
: ['Step', ''];
|
||||
|
||||
return (
|
||||
<ul>
|
||||
<li>
|
||||
<Button
|
||||
onClick={openAutomationSettings}
|
||||
className={`edit-site-sidebar__panel-tab ${automationActiveClass}`}
|
||||
aria-label={automationAriaLabel}
|
||||
data-label={__('Automation', 'mailpoet')}
|
||||
onClick={openWorkflowSettings}
|
||||
className={`edit-site-sidebar__panel-tab ${workflowActiveClass}`}
|
||||
aria-label={workflowAriaLabel}
|
||||
data-label="Workflow"
|
||||
>
|
||||
{__('Automation', 'mailpoet')}
|
||||
Workflow
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
@ -43,9 +42,9 @@ export function Header({ sidebarKey }: Props): JSX.Element {
|
||||
onClick={openStepSettings}
|
||||
className={`edit-site-sidebar__panel-tab ${stepActiveClass}`}
|
||||
aria-label={stepAriaLabel}
|
||||
data-label={__('Step', 'mailpoet')}
|
||||
data-label="Workflow"
|
||||
>
|
||||
{__('Step', 'mailpoet')}
|
||||
Step
|
||||
</Button>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -10,8 +10,8 @@ import {
|
||||
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
|
||||
import { Header } from './header';
|
||||
import { StepSidebar } from './step';
|
||||
import { AutomationSidebar } from './automation';
|
||||
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||
import { WorkflowSidebar } from './workflow';
|
||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
||||
|
||||
// See:
|
||||
// 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>;
|
||||
|
||||
export function Sidebar(props: Props): JSX.Element {
|
||||
const { keyboardShortcut, sidebarKey, showIconLabels, automationName } =
|
||||
const { keyboardShortcut, sidebarKey, showIconLabels, workflowName } =
|
||||
useSelect(
|
||||
(select) => ({
|
||||
keyboardShortcut: select(
|
||||
@ -36,9 +36,9 @@ export function Sidebar(props: Props): JSX.Element {
|
||||
),
|
||||
sidebarKey:
|
||||
select(interfaceStore).getActiveComplementaryArea(storeName) ??
|
||||
automationSidebarKey,
|
||||
workflowSidebarKey,
|
||||
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
||||
automationName: select(storeName).getAutomationData().name,
|
||||
workflowName: select(storeName).getWorkflowData().name,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@ -47,20 +47,20 @@ export function Sidebar(props: Props): JSX.Element {
|
||||
<ComplementaryArea
|
||||
identifier={sidebarKey}
|
||||
header={<Header sidebarKey={sidebarKey} />}
|
||||
closeLabel={__('Close settings', 'mailpoet')}
|
||||
closeLabel={__('Close settings')}
|
||||
headerClassName="edit-site-sidebar__panel-tabs"
|
||||
title={__('Settings', 'mailpoet')}
|
||||
title={__('Settings')}
|
||||
icon={cog}
|
||||
className="edit-site-sidebar mailpoet-automation-sidebar"
|
||||
panelClassName="edit-site-sidebar"
|
||||
smallScreenTitle={automationName || __('(no title)', 'mailpoet')}
|
||||
smallScreenTitle={workflowName || __('(no title)')}
|
||||
scope={storeName}
|
||||
toggleShortcut={keyboardShortcut}
|
||||
isActiveByDefault={sidebarActiveByDefault}
|
||||
showIconLabels={showIconLabels}
|
||||
{...props}
|
||||
>
|
||||
{sidebarKey === automationSidebarKey && <AutomationSidebar />}
|
||||
{sidebarKey === workflowSidebarKey && <WorkflowSidebar />}
|
||||
{sidebarKey === stepSidebarKey && <StepSidebar />}
|
||||
</ComplementaryArea>
|
||||
);
|
||||
|
@ -30,11 +30,22 @@ export function StepSidebar(): JSX.Element {
|
||||
icon={selectedStepType.icon}
|
||||
/>
|
||||
|
||||
<Edit
|
||||
// Force sidebar remount to avoid different steps mixing their data.
|
||||
// This can happen e.g. when having "useState" or "useRef" internally.
|
||||
key={selectedStep.id}
|
||||
/>
|
||||
<Edit />
|
||||
|
||||
<PanelBody title="Debug info" initialOpen={false}>
|
||||
<div>
|
||||
<strong>ID:</strong> {selectedStep.id}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Type:</strong> {selectedStep.type}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Key:</strong> {selectedStep.key}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Args:</strong> {JSON.stringify(selectedStep.args)}
|
||||
</div>
|
||||
</PanelBody>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { PanelBody, PanelRow } from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { storeName } from '../../../store';
|
||||
import { TrashButton } from '../../actions/trash-button';
|
||||
|
||||
export function AutomationSidebar(): JSX.Element {
|
||||
const { automationData } = useSelect(
|
||||
export function WorkflowSidebar(): JSX.Element {
|
||||
const { workflowData } = useSelect(
|
||||
(select) => ({
|
||||
automationData: select(storeName).getAutomationData(),
|
||||
workflowData: select(storeName).getWorkflowData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@ -19,33 +18,33 @@ export function AutomationSidebar(): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<PanelBody title={__('Automation details', 'mailpoet')} initialOpen>
|
||||
<PanelBody title="Automation details" initialOpen>
|
||||
<PanelRow>
|
||||
<strong>Date added</strong>{' '}
|
||||
{new Date(Date.parse(automationData.created_at)).toLocaleDateString(
|
||||
{new Date(Date.parse(workflowData.created_at)).toLocaleDateString(
|
||||
undefined,
|
||||
dateOptions,
|
||||
)}
|
||||
</PanelRow>
|
||||
<PanelRow>
|
||||
<strong>Activated</strong>{' '}
|
||||
{automationData.status === 'active' &&
|
||||
new Date(Date.parse(automationData.updated_at)).toLocaleDateString(
|
||||
{workflowData.status === 'active' &&
|
||||
new Date(Date.parse(workflowData.updated_at)).toLocaleDateString(
|
||||
undefined,
|
||||
dateOptions,
|
||||
)}
|
||||
{automationData.status !== 'active' &&
|
||||
automationData.activated_at &&
|
||||
new Date(Date.parse(automationData.activated_at)).toLocaleDateString(
|
||||
{workflowData.status !== 'active' &&
|
||||
workflowData.activated_at &&
|
||||
new Date(Date.parse(workflowData.activated_at)).toLocaleDateString(
|
||||
undefined,
|
||||
dateOptions,
|
||||
)}
|
||||
{automationData.status !== 'active' && !automationData.activated_at && (
|
||||
{workflowData.status !== 'active' && !workflowData.activated_at && (
|
||||
<span className="mailpoet-deactive">Not activated yet.</span>
|
||||
)}
|
||||
</PanelRow>
|
||||
<PanelRow>
|
||||
<strong>Author</strong> {automationData.author.name}
|
||||
<strong>Author</strong> {workflowData.author.name}
|
||||
</PanelRow>
|
||||
<PanelRow>
|
||||
<TrashButton />
|
@ -1,7 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { __unstableCompositeItem as CompositeItem } from '@wordpress/components';
|
||||
import { Icon, plus } from '@wordpress/icons';
|
||||
import { AutomationCompositeContext } from './context';
|
||||
import { WorkflowCompositeContext } from './context';
|
||||
|
||||
type Props = {
|
||||
onClick?: (element: HTMLButtonElement) => void;
|
||||
@ -9,7 +9,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export function AddStepButton({ onClick, previousStepId }: Props): JSX.Element {
|
||||
const compositeState = useContext(AutomationCompositeContext);
|
||||
const compositeState = useContext(WorkflowCompositeContext);
|
||||
return (
|
||||
<CompositeItem
|
||||
state={compositeState}
|
@ -3,7 +3,7 @@ import { __unstableCompositeItem as CompositeItem } from '@wordpress/components'
|
||||
import { Icon, plus } from '@wordpress/icons';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { AutomationCompositeContext } from './context';
|
||||
import { WorkflowCompositeContext } from './context';
|
||||
import { Step } from './types';
|
||||
import { storeName } from '../../store';
|
||||
|
||||
@ -12,14 +12,14 @@ type Props = {
|
||||
};
|
||||
|
||||
export function AddTrigger({ step }: Props): JSX.Element {
|
||||
const compositeState = useContext(AutomationCompositeContext);
|
||||
const compositeState = useContext(WorkflowCompositeContext);
|
||||
const { setInserterPopover } = useDispatch(storeName);
|
||||
|
||||
return (
|
||||
<CompositeItem
|
||||
state={compositeState}
|
||||
role="treeitem"
|
||||
className="mailpoet-automation-add-trigger"
|
||||
className="mailpoet-automation-workflow-add-trigger"
|
||||
data-previous-step-id={step.id}
|
||||
focusable
|
||||
onClick={(event) => {
|
@ -1,5 +1,5 @@
|
||||
import { __unstableUseCompositeState as useCompositeState } from '@wordpress/components';
|
||||
import { createContext } from '@wordpress/element';
|
||||
|
||||
export const AutomationCompositeContext =
|
||||
export const WorkflowCompositeContext =
|
||||
createContext<ReturnType<typeof useCompositeState>>(undefined);
|
@ -0,0 +1,7 @@
|
||||
export function EmptyWorkflow(): JSX.Element {
|
||||
return (
|
||||
<div className="mailpoet-automation-editor-empty-workflow">
|
||||
No workflow data.
|
||||
</div>
|
||||
);
|
||||
}
|
@ -7,8 +7,8 @@ import { useSelect } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Icon, check } from '@wordpress/icons';
|
||||
import { Hooks } from 'wp-js-hooks';
|
||||
import { AutomationCompositeContext } from './context';
|
||||
import { EmptyAutomation } from './empty-automation';
|
||||
import { WorkflowCompositeContext } from './context';
|
||||
import { EmptyWorkflow } from './empty-workflow';
|
||||
import { Separator } from './separator';
|
||||
import { Step } from './step';
|
||||
import { Step as StepData } from './types';
|
||||
@ -16,15 +16,11 @@ import { InserterPopover } from '../inserter-popover';
|
||||
import { storeName } from '../../store';
|
||||
import { AddTrigger } from './add-trigger';
|
||||
import { Statistics } from './statistics';
|
||||
import {
|
||||
RenderStepSeparatorType,
|
||||
RenderStepType,
|
||||
} from '../../../types/filters';
|
||||
|
||||
export function Automation(): JSX.Element {
|
||||
const { automationData, selectedStep } = useSelect(
|
||||
export function Workflow(): JSX.Element {
|
||||
const { workflowData, selectedStep } = useSelect(
|
||||
(select) => ({
|
||||
automationData: select(storeName).getAutomationData(),
|
||||
workflowData: select(storeName).getWorkflowData(),
|
||||
selectedStep: select(storeName).getSelectedStep(),
|
||||
}),
|
||||
[],
|
||||
@ -36,9 +32,9 @@ export function Automation(): JSX.Element {
|
||||
shift: true,
|
||||
});
|
||||
|
||||
const stepMap = automationData?.steps ?? undefined;
|
||||
const stepMap = workflowData?.steps ?? undefined;
|
||||
|
||||
// serialize steps (for now, we support only one trigger and linear automations)
|
||||
// serialize steps (for now, we support only one trigger and linear workflows)
|
||||
const steps = useMemo(() => {
|
||||
const stepArray = [stepMap.root];
|
||||
|
||||
@ -54,9 +50,9 @@ export function Automation(): JSX.Element {
|
||||
}, [stepMap]);
|
||||
|
||||
const renderStep = useMemo(
|
||||
(): RenderStepType =>
|
||||
() =>
|
||||
Hooks.applyFilters(
|
||||
'mailpoet.automation.render_step',
|
||||
'mailpoet.automation.workflow.render_step',
|
||||
(stepData: StepData) =>
|
||||
stepData.type === 'root' ? (
|
||||
<AddTrigger step={stepData} />
|
||||
@ -71,9 +67,9 @@ export function Automation(): JSX.Element {
|
||||
);
|
||||
|
||||
const renderSeparator = useMemo(
|
||||
(): RenderStepSeparatorType =>
|
||||
() =>
|
||||
Hooks.applyFilters(
|
||||
'mailpoet.automation.render_step_separator',
|
||||
'mailpoet.automation.workflow.render_step_separator',
|
||||
(previousStepData: StepData) => (
|
||||
<Separator previousStepId={previousStepData.id} />
|
||||
),
|
||||
@ -81,20 +77,20 @@ export function Automation(): JSX.Element {
|
||||
[],
|
||||
);
|
||||
|
||||
if (!automationData) {
|
||||
return <EmptyAutomation />;
|
||||
if (!workflowData) {
|
||||
return <EmptyWorkflow />;
|
||||
}
|
||||
|
||||
return (
|
||||
<AutomationCompositeContext.Provider value={compositeState}>
|
||||
<WorkflowCompositeContext.Provider value={compositeState}>
|
||||
<Composite
|
||||
state={compositeState}
|
||||
role="tree"
|
||||
aria-label={__('Automation', 'mailpoet')}
|
||||
aria-label={__('Workflow', 'mailpoet')}
|
||||
aria-orientation="vertical"
|
||||
className="mailpoet-automation-editor-automation"
|
||||
className="mailpoet-automation-editor-workflow"
|
||||
>
|
||||
<div className="mailpoet-automation-editor-automation-wrapper">
|
||||
<div className="mailpoet-automation-editor-workflow-wrapper">
|
||||
<Statistics />
|
||||
{stepMap.root.next_steps.length === 0 ? (
|
||||
<>
|
||||
@ -119,13 +115,13 @@ export function Automation(): JSX.Element {
|
||||
</Fragment>
|
||||
))}
|
||||
<Icon
|
||||
className="mailpoet-automation-editor-automation-end"
|
||||
className="mailpoet-automation-editor-workflow-end"
|
||||
icon={check}
|
||||
/>
|
||||
<div />
|
||||
</div>
|
||||
<InserterPopover />
|
||||
</Composite>
|
||||
</AutomationCompositeContext.Provider>
|
||||
</WorkflowCompositeContext.Provider>
|
||||
);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { storeName } from '../../store';
|
||||
import { Statistics as BaseStatistics } from '../../../components/statistics';
|
||||
|
||||
export function Statistics(): JSX.Element {
|
||||
const { workflow } = useSelect(
|
||||
(select) => ({
|
||||
workflow: select(storeName).getWorkflowData(),
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mailpoet-automation-editor-stats">
|
||||
<BaseStatistics
|
||||
items={[
|
||||
{
|
||||
key: 'entered',
|
||||
label: __('Total Entered', 'mailpoet'),
|
||||
value: workflow.stats.totals.entered,
|
||||
},
|
||||
{
|
||||
key: 'processing',
|
||||
label: __('Total Processing', 'mailpoet'),
|
||||
value: workflow.stats.totals.in_progress,
|
||||
},
|
||||
{
|
||||
key: 'exited',
|
||||
label: __('Total Exited', 'mailpoet'),
|
||||
value: workflow.stats.totals.exited,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user