Compare commits
455 Commits
Author | SHA1 | Date | |
---|---|---|---|
03b1a6e9ce | |||
69440ec2a6 | |||
0bbdf4b47f | |||
f11aca925f | |||
e4029a607a | |||
eb53973799 | |||
aab8281af0 | |||
3df00548ab | |||
ada346f4ef | |||
b3691a4625 | |||
e02b631172 | |||
2200411455 | |||
b0ab2f404f | |||
4195428643 | |||
1692c9bef5 | |||
cb25cf2944 | |||
794c5ce2d5 | |||
974d2d5a59 | |||
2ebcad1468 | |||
671c7eed7d | |||
72de76fe9b | |||
53cc357632 | |||
47d3472fef | |||
05c5b46089 | |||
f85bd8622f | |||
503cf61c0f | |||
d9eca55189 | |||
e850eaa90c | |||
478eab61e4 | |||
ea53728799 | |||
914d0d37c0 | |||
c1aae2c192 | |||
03cdcb30f8 | |||
a462e72b30 | |||
83db3db90d | |||
9495a40900 | |||
dbc2c9c240 | |||
d6bd1a5527 | |||
c5cd863f15 | |||
8ef1bfaf24 | |||
abac8d7147 | |||
367bcaf233 | |||
fdbb9428d9 | |||
bd047a0108 | |||
05202d572f | |||
a698e416ad | |||
1efc417f82 | |||
4dc31bbdb0 | |||
12dc727fda | |||
6602103d0f | |||
f3e8fb8ae2 | |||
a205d4d7f4 | |||
1783ca6e42 | |||
6555833fb6 | |||
ce3b9b2b7b | |||
1a653c5676 | |||
85a8d8aedb | |||
5c453fcd54 | |||
ec9adf8a6c | |||
b5064ca46f | |||
84b97ead76 | |||
4e578238f7 | |||
bb6d6137aa | |||
74c7728e3a | |||
e13113e38b | |||
68db0dbefe | |||
e015302a94 | |||
20fcf23f8f | |||
11beebf74d | |||
5a53406d33 | |||
ee83e4d748 | |||
970db8f0d8 | |||
71828f9f6e | |||
0525dd0f56 | |||
e3ba525195 | |||
1eed55cbce | |||
fd1331e602 | |||
02b82c04f3 | |||
ad5ef6ebbc | |||
3c5bc5b384 | |||
f229df0383 | |||
9092f892d0 | |||
9c3fb0856f | |||
2a8c665e12 | |||
9b1942ce48 | |||
932b4532f0 | |||
6533a1444d | |||
05a109dcf4 | |||
5b5b7856c6 | |||
4ceb508218 | |||
3afc7dc140 | |||
1c1058667b | |||
129aed2ae9 | |||
f8c7adf5a6 | |||
6346d39507 | |||
1b76f3b862 | |||
40c140a631 | |||
7fd11d4fb5 | |||
7fa694314b | |||
080e385b02 | |||
7c0b625793 | |||
f0b858b1f8 | |||
b218663e7e | |||
d8ea29423d | |||
ff038a1c56 | |||
588e8fca6b | |||
8d9f23d5f8 | |||
ea4637c740 | |||
c0e56a4f92 | |||
53a4722e91 | |||
d175870adf | |||
a64229f688 | |||
cd69e1e7b9 | |||
321db0b004 | |||
aaf01475b0 | |||
1cd38a396e | |||
1f1c9dd077 | |||
0da6d84309 | |||
3a05598166 | |||
5035d64298 | |||
56f6d20244 | |||
650d730c3f | |||
decaa0f636 | |||
c2b9e33b79 | |||
34347f241d | |||
5802f2132d | |||
8e502e5f50 | |||
0187f0cede | |||
8457aa7e2b | |||
67156b7c6f | |||
f9250b66a9 | |||
1fcdccaa33 | |||
5b93b88f46 | |||
cbd41cd1be | |||
95a8943b7e | |||
dd363370f6 | |||
1027a6a676 | |||
0fd93b0ff9 | |||
bfade87b62 | |||
26d9030544 | |||
5688a0daea | |||
92996ac781 | |||
1f471782ab | |||
320459eaab | |||
e65b2cce02 | |||
520ee981d3 | |||
f100ef72da | |||
146e1c871f | |||
669367c9c6 | |||
77d62c431a | |||
4a5fc507ca | |||
06f59dd320 | |||
4367f44449 | |||
856e0f69d3 | |||
c4bd2acd84 | |||
210c240aab | |||
d19f5dc732 | |||
6541c20466 | |||
3952484848 | |||
7d918604c3 | |||
ffaf5b88b6 | |||
eca4a9e923 | |||
1cee4e67e8 | |||
b1c35b60d8 | |||
1e5cfb4eaf | |||
5e5956420f | |||
78446d174a | |||
917d64a3e3 | |||
a3d51b2b08 | |||
3ffbd6e236 | |||
9753331d52 | |||
b266edb8e7 | |||
f1899055b8 | |||
c3f0a36665 | |||
88efb1f3d1 | |||
146f5881f7 | |||
3279fb1154 | |||
5f5efbe876 | |||
12afcfb656 | |||
0aac9e9d60 | |||
4e3b1527cd | |||
b27dcdd40a | |||
7b584dd482 | |||
5e71d94b62 | |||
8bb4338627 | |||
4bcd4a2cf2 | |||
ea7971cb3b | |||
9036b210a0 | |||
f337ac388b | |||
b0ae21ec60 | |||
7d11eeeddd | |||
4577ca5476 | |||
846794b60d | |||
5757af95aa | |||
b8c3b38652 | |||
3d21e4f35e | |||
73ce89d051 | |||
49d9dd666b | |||
53dda33c02 | |||
9b9f61bf13 | |||
651e2d3c56 | |||
0bd627d3b1 | |||
99d0eede80 | |||
d09b4ca409 | |||
caa76983e4 | |||
7b6bbb0bc3 | |||
cdf7c81a94 | |||
86812c5259 | |||
657302b720 | |||
172b5215d2 | |||
52a9d9f76c | |||
8eb7a48d3e | |||
a8bceffc9c | |||
2c728c793a | |||
8d974d8147 | |||
8fbc5c270a | |||
92dc5921df | |||
3c3ce37720 | |||
f369e399ed | |||
01aa1e1e52 | |||
2bbc1b7063 | |||
04b8a0ed73 | |||
761f7c6537 | |||
723dc2c9d3 | |||
d66fbb1c20 | |||
c2107b8d59 | |||
926620e8f8 | |||
82aeb89854 | |||
c67c58709b | |||
75b5958a53 | |||
bba5101669 | |||
b9c5dddbaf | |||
0566b3f5fa | |||
ac4adac1ab | |||
33a9097719 | |||
08c74cdf8b | |||
8c5222d850 | |||
08c76a46af | |||
3d24cb1deb | |||
a151f93be1 | |||
bce6a06c15 | |||
10a6e387f3 | |||
b6a37f274c | |||
2cdbc68643 | |||
7db40b27b5 | |||
b1760ff676 | |||
293eef2c78 | |||
299c6b779e | |||
842f435976 | |||
11faf925cc | |||
1295aa21cc | |||
84cd137c76 | |||
37067dff67 | |||
c7f850e1ba | |||
b25f2cd5f4 | |||
af95080b67 | |||
6b8c35d5fe | |||
a35e0dced4 | |||
c6259eb185 | |||
670017b342 | |||
ce7b210da9 | |||
1a2d8b8d40 | |||
fe2df00a8c | |||
20577d974f | |||
7369465a3a | |||
5769fa45f5 | |||
193b4cbf98 | |||
461df1a560 | |||
d5f78680eb | |||
96dde7107b | |||
3e756bfadd | |||
82c755200e | |||
735d33d05b | |||
90c8e5b2c9 | |||
cd1f06b116 | |||
173f347431 | |||
353d3389cd | |||
fe364978a1 | |||
cd512bbb01 | |||
f5193df721 | |||
25b5d24867 | |||
1d99ff0943 | |||
99d6d96b05 | |||
6ddfe2c605 | |||
d2ec7bba42 | |||
2557171953 | |||
0d7e45ea03 | |||
e677169a51 | |||
e268eac378 | |||
375f2b13c5 | |||
34406a2f72 | |||
cc658bcc3d | |||
973aefe7ae | |||
34e91251e8 | |||
882402645d | |||
d9d4dccc09 | |||
9d2fcccbf5 | |||
c4fdd881e6 | |||
83f7eee8ae | |||
dc1f63d8bb | |||
3e2425abd8 | |||
65e78c7990 | |||
2bdc5d3683 | |||
1de276378f | |||
cc76fe77f0 | |||
c8636aac6f | |||
a09f41143e | |||
8b29694446 | |||
5e0e89408c | |||
86076547c3 | |||
b1bbabe14d | |||
f5c678ec2d | |||
223625bd9b | |||
fbd7cf8cc7 | |||
26c7e4d1cc | |||
4832771185 | |||
7fb8d64628 | |||
36fed3bbf8 | |||
8ab738b315 | |||
b84b9606e9 | |||
aa68b7e148 | |||
ca44339512 | |||
91f5376dfc | |||
d51ef70db6 | |||
c164268c20 | |||
4c7c94c75d | |||
ccf141b279 | |||
e42808f22d | |||
75a4b2c538 | |||
f1d64c0572 | |||
25c57bf9fe | |||
079c90dbba | |||
0fc342ee4c | |||
7570c9fa0e | |||
7642c4da4f | |||
dce13e5b8e | |||
8b922681b6 | |||
bd33cdbc24 | |||
bc04ad2528 | |||
c54a5213b3 | |||
07cf0cd79b | |||
e64dc1669f | |||
48181994d8 | |||
31c2915075 | |||
67f05ecfe2 | |||
c121c60e8c | |||
110859508e | |||
efd3377ecd | |||
a7c1d8431d | |||
127f3e6363 | |||
6bec12c762 | |||
d1df2d4f42 | |||
5598f0af19 | |||
8dd80ce0f2 | |||
e45510e064 | |||
bfafff132a | |||
d6748fe979 | |||
2169522818 | |||
06cfa3fc60 | |||
f9e507eb15 | |||
90332a23b3 | |||
a82a810269 | |||
5a378b3c5f | |||
49a2df3dbb | |||
dee5ff38f5 | |||
70aefb421b | |||
764e1a1bf0 | |||
b140011f92 | |||
b48c9e437e | |||
f34efb4d6f | |||
324c03e8b9 | |||
e718101425 | |||
080ce50fea | |||
1b9eb223b0 | |||
ea2fa794ac | |||
c46c61a923 | |||
1136431551 | |||
957be23212 | |||
91a88b3e91 | |||
7c5d239267 | |||
c748b80447 | |||
725e0ecb00 | |||
eb71dd8a68 | |||
ce1687cf97 | |||
2e328b6d7f | |||
8489e63d34 | |||
f8c17730fc | |||
93e43eee7a | |||
9063dc3079 | |||
9d55d3f134 | |||
d199c3768a | |||
0e0c2447d9 | |||
99dfb3d24b | |||
c4366e009b | |||
1d31202607 | |||
78d25ffb69 | |||
b710682c66 | |||
52c6b94315 | |||
fd6b49e598 | |||
df2982454e | |||
8a19fd906f | |||
99198e5c2d | |||
9204f37560 | |||
765aa6efab | |||
50b613365f | |||
4fe8d10d6c | |||
162dab790d | |||
6e8c9731d8 | |||
235552f91d | |||
4ee08c296b | |||
bda979ec4f | |||
78f10f064e | |||
eca4a68fec | |||
9daa5d58c7 | |||
6cc232fbad | |||
6a07bd44ff | |||
63cd326191 | |||
40b15b0eb1 | |||
3825e9cb11 | |||
7db2942140 | |||
3a5d28f24a | |||
d02c63844d | |||
c3045dba07 | |||
c239566e1e | |||
dfdc8cfd09 | |||
4977b7ffa2 | |||
8e69299a6a | |||
903bcbb92e | |||
8d6492ac8c | |||
c39ae1fe1f | |||
e3539b06a2 | |||
d34a265ac2 | |||
9a72e361b1 | |||
a520e4bd93 | |||
9ab6ebbe0d | |||
6a3cfd05e7 | |||
f825e535e3 | |||
a2c5420d7f | |||
c6d3573652 | |||
e41bcd0d02 | |||
f805907954 | |||
d970efc0da | |||
c0ce5944dc | |||
467f354eb1 | |||
3f016b45f9 | |||
679f74e498 | |||
08b314e0b4 | |||
cc5959805b | |||
407f3d1609 | |||
503df3584c | |||
fa9510f0c1 | |||
cc92df4e7f | |||
d76c5d32f2 | |||
c6198cba4c | |||
e29dd4286e |
@ -179,10 +179,10 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Download additional WP Plugins for tests
|
name: Download additional WP Plugins for tests
|
||||||
command: |
|
command: |
|
||||||
./do download:woo-commerce-zip 6.8.2
|
./do download:woo-commerce-zip 7.1.0
|
||||||
./do download:woo-commerce-subscriptions-zip 4.5.1
|
./do download:woo-commerce-subscriptions-zip 4.6.0
|
||||||
./do download:woo-commerce-memberships-zip 1.23.0
|
./do download:woo-commerce-memberships-zip 1.23.1
|
||||||
./do download:woo-commerce-blocks-zip 8.4.0
|
./do download:woo-commerce-blocks-zip 8.8.2
|
||||||
- run:
|
- run:
|
||||||
name: Dump tests ENV variables for acceptance tests
|
name: Dump tests ENV variables for acceptance tests
|
||||||
command: |
|
command: |
|
||||||
@ -311,7 +311,8 @@ jobs:
|
|||||||
parallelism: 20
|
parallelism: 20
|
||||||
working_directory: /home/circleci/mailpoet/mailpoet
|
working_directory: /home/circleci/mailpoet/mailpoet
|
||||||
machine:
|
machine:
|
||||||
image: ubuntu-2204:2022.07.1
|
image: ubuntu-2204:2022.10.2
|
||||||
|
docker_layer_caching: false
|
||||||
parameters:
|
parameters:
|
||||||
multisite:
|
multisite:
|
||||||
type: integer
|
type: integer
|
||||||
@ -322,7 +323,7 @@ jobs:
|
|||||||
mysql_command:
|
mysql_command:
|
||||||
type: string
|
type: string
|
||||||
default: ''
|
default: ''
|
||||||
mysql_image_version:
|
mysql_image:
|
||||||
type: string
|
type: string
|
||||||
default: ''
|
default: ''
|
||||||
codeception_image_version:
|
codeception_image_version:
|
||||||
@ -351,7 +352,7 @@ jobs:
|
|||||||
default: 0
|
default: 0
|
||||||
environment:
|
environment:
|
||||||
MYSQL_COMMAND: << parameters.mysql_command >>
|
MYSQL_COMMAND: << parameters.mysql_command >>
|
||||||
MYSQL_IMAGE_VERSION: << parameters.mysql_image_version >>
|
MYSQL_IMAGE: << parameters.mysql_image >>
|
||||||
CODECEPTION_IMAGE_VERSION: << parameters.codeception_image_version >>
|
CODECEPTION_IMAGE_VERSION: << parameters.codeception_image_version >>
|
||||||
WORDPRESS_IMAGE_VERSION: << parameters.wordpress_image_version >>
|
WORDPRESS_IMAGE_VERSION: << parameters.wordpress_image_version >>
|
||||||
steps:
|
steps:
|
||||||
@ -411,6 +412,12 @@ jobs:
|
|||||||
circleci tests glob "tests/acceptance/**/*Cest.php" | circleci tests split --split-by=timings > tests/acceptance/_groups/circleci_split_group
|
circleci tests glob "tests/acceptance/**/*Cest.php" | circleci tests split --split-by=timings > tests/acceptance/_groups/circleci_split_group
|
||||||
fi
|
fi
|
||||||
cat tests/acceptance/_groups/circleci_split_group
|
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:
|
- run:
|
||||||
name: Run acceptance tests
|
name: Run acceptance tests
|
||||||
command: |
|
command: |
|
||||||
@ -476,9 +483,12 @@ jobs:
|
|||||||
integration_tests:
|
integration_tests:
|
||||||
working_directory: /home/circleci/mailpoet/mailpoet
|
working_directory: /home/circleci/mailpoet/mailpoet
|
||||||
machine:
|
machine:
|
||||||
image: ubuntu-2204:2022.07.1
|
image: ubuntu-2204:2022.10.2
|
||||||
|
docker_layer_caching: false
|
||||||
environment:
|
environment:
|
||||||
CODECEPTION_IMAGE_VERSION: << parameters.codeception_image_version >>
|
CODECEPTION_IMAGE_VERSION: << parameters.codeception_image_version >>
|
||||||
|
MYSQL_COMMAND: << parameters.mysql_command >>
|
||||||
|
MYSQL_IMAGE: << parameters.mysql_image >>
|
||||||
parameters:
|
parameters:
|
||||||
codeception_image_version:
|
codeception_image_version:
|
||||||
type: string
|
type: string
|
||||||
@ -501,7 +511,10 @@ jobs:
|
|||||||
multisite:
|
multisite:
|
||||||
type: integer
|
type: integer
|
||||||
default: 0
|
default: 0
|
||||||
woo_core_version:
|
mysql_command:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
mysql_image:
|
||||||
type: string
|
type: string
|
||||||
default: ''
|
default: ''
|
||||||
steps:
|
steps:
|
||||||
@ -511,14 +524,6 @@ jobs:
|
|||||||
name: 'Pull test docker images'
|
name: 'Pull test docker images'
|
||||||
# Pull docker images with 3 retries
|
# 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
|
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:
|
- run:
|
||||||
name: 'PHP Integration tests'
|
name: 'PHP Integration tests'
|
||||||
command: |
|
command: |
|
||||||
@ -629,7 +634,7 @@ workflows:
|
|||||||
- build
|
- build
|
||||||
- acceptance_tests:
|
- acceptance_tests:
|
||||||
<<: *slack-fail-post-step
|
<<: *slack-fail-post-step
|
||||||
name: acceptance_tests
|
name: acceptance_tests_base_and_woo_cot_off
|
||||||
requires:
|
requires:
|
||||||
- unit_tests
|
- unit_tests
|
||||||
- static_analysis_php8
|
- static_analysis_php8
|
||||||
@ -641,7 +646,6 @@ workflows:
|
|||||||
group: woo
|
group: woo
|
||||||
enable_cot: 1
|
enable_cot: 1
|
||||||
enable_cot_sync: 1
|
enable_cot_sync: 1
|
||||||
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
|
|
||||||
requires:
|
requires:
|
||||||
- unit_tests
|
- unit_tests
|
||||||
- static_analysis_php8
|
- static_analysis_php8
|
||||||
@ -653,17 +657,6 @@ workflows:
|
|||||||
group: woo
|
group: woo
|
||||||
enable_cot: 1
|
enable_cot: 1
|
||||||
enable_cot_sync: 0
|
enable_cot_sync: 0
|
||||||
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
|
|
||||||
requires:
|
|
||||||
- unit_tests
|
|
||||||
- static_analysis_php8
|
|
||||||
- qa_js
|
|
||||||
- qa_php
|
|
||||||
- acceptance_tests:
|
|
||||||
<<: *slack-fail-post-step
|
|
||||||
name: acceptance_tests_woo_cot_off
|
|
||||||
group: woo
|
|
||||||
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
|
|
||||||
requires:
|
requires:
|
||||||
- unit_tests
|
- unit_tests
|
||||||
- static_analysis_php8
|
- static_analysis_php8
|
||||||
@ -673,21 +666,11 @@ workflows:
|
|||||||
<<: *slack-fail-post-step
|
<<: *slack-fail-post-step
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
- integration_tests:
|
|
||||||
<<: *slack-fail-post-step
|
|
||||||
group: woo
|
|
||||||
name: integration_test_woocommerce
|
|
||||||
requires:
|
|
||||||
- unit_tests
|
|
||||||
- static_analysis_php8
|
|
||||||
- qa_js
|
|
||||||
- qa_php
|
|
||||||
- integration_tests:
|
- integration_tests:
|
||||||
<<: *slack-fail-post-step
|
<<: *slack-fail-post-step
|
||||||
group: woo
|
group: woo
|
||||||
enable_cot: 1
|
enable_cot: 1
|
||||||
enable_cot_sync: 1
|
enable_cot_sync: 1
|
||||||
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
|
|
||||||
name: integration_test_woo_cot_sync
|
name: integration_test_woo_cot_sync
|
||||||
requires:
|
requires:
|
||||||
- unit_tests
|
- unit_tests
|
||||||
@ -699,7 +682,6 @@ workflows:
|
|||||||
group: woo
|
group: woo
|
||||||
enable_cot: 1
|
enable_cot: 1
|
||||||
enable_cot_sync: 0
|
enable_cot_sync: 0
|
||||||
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
|
|
||||||
name: integration_test_woo_cot_no_sync
|
name: integration_test_woo_cot_no_sync
|
||||||
requires:
|
requires:
|
||||||
- unit_tests
|
- unit_tests
|
||||||
@ -709,7 +691,6 @@ workflows:
|
|||||||
- integration_tests:
|
- integration_tests:
|
||||||
<<: *slack-fail-post-step
|
<<: *slack-fail-post-step
|
||||||
group: woo
|
group: woo
|
||||||
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
|
|
||||||
name: integration_test_woo_cot_off
|
name: integration_test_woo_cot_off
|
||||||
requires:
|
requires:
|
||||||
- unit_tests
|
- unit_tests
|
||||||
@ -745,10 +726,14 @@ workflows:
|
|||||||
<<: *slack-fail-post-step
|
<<: *slack-fail-post-step
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
- acceptance_tests
|
- acceptance_tests_base_and_woo_cot_off
|
||||||
- js_tests
|
- js_tests
|
||||||
- integration_test_woocommerce
|
|
||||||
- integration_test_base
|
- 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:
|
nightly:
|
||||||
triggers:
|
triggers:
|
||||||
@ -773,14 +758,14 @@ workflows:
|
|||||||
- acceptance_tests:
|
- acceptance_tests:
|
||||||
<<: *slack-fail-post-step
|
<<: *slack-fail-post-step
|
||||||
name: acceptance_oldest
|
name: acceptance_oldest
|
||||||
woo_core_version: 6.2.2
|
woo_core_version: 6.8.0
|
||||||
woo_subscriptions_version: 4.3.0
|
woo_subscriptions_version: 4.3.0
|
||||||
woo_memberships_version: 1.21.0
|
woo_memberships_version: 1.21.0
|
||||||
woo_blocks_version: 5.3.2
|
woo_blocks_version: 6.8.0
|
||||||
mysql_command: --max_allowed_packet=100M
|
mysql_command: --max_allowed_packet=100M --default-storage-engine=MYISAM
|
||||||
mysql_image_version: 5.7.36
|
mysql_image: mysql:5.5
|
||||||
codeception_image_version: 7.4-cli_20210126.1
|
codeception_image_version: 7.4-cli_20220605.0
|
||||||
wordpress_image_version: wp-5.6_php7.2_20220406.1
|
wordpress_image_version: wp-5.8_php7.3_20221104.1
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
- unit_tests:
|
- unit_tests:
|
||||||
@ -803,6 +788,8 @@ workflows:
|
|||||||
<<: *slack-fail-post-step
|
<<: *slack-fail-post-step
|
||||||
name: integration_oldest
|
name: integration_oldest
|
||||||
codeception_image_version: 7.2-cli_20220605.0
|
codeception_image_version: 7.2-cli_20220605.0
|
||||||
|
mysql_command: --max_allowed_packet=100M --default-storage-engine=MYISAM
|
||||||
|
mysql_image: mysql:5.5
|
||||||
requires:
|
requires:
|
||||||
- build
|
- build
|
||||||
- build_premium:
|
- build_premium:
|
||||||
|
15
README.md
15
README.md
@ -126,23 +126,24 @@ 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)
|
[Read the article.](https://mailpoet.atlassian.net/wiki/spaces/MAILPOET/pages/629374977/Adding+new+templates+to+the+plugin)
|
||||||
|
|
||||||
## 🚥 Testing with PHP 7.4 or PHP 8.0
|
## 🚥 Testing with different PHP versions
|
||||||
|
|
||||||
To switch the environment to PHP 7.4/8.0:
|
To switch the environment to a different PHP version:
|
||||||
|
|
||||||
1. Configure the `wordpress` service in `docker-compose.override.yml` to build from the php74 Dockerfile:
|
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):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
wordpress:
|
wordpress:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: dev/php74/Dockerfile # OR dev/php80/Dockerfile
|
dockerfile: dev/{PHP_VERSION}/Dockerfile
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Run `docker-compose build wordpress`.
|
3. Run `docker-compose build wordpress`.
|
||||||
3. Start the stack with `./do start`.
|
4. Start the stack with `./do start`.
|
||||||
|
|
||||||
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,
|
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,
|
||||||
and start the stack using `./do start`.
|
and start the stack using `./do start`.
|
||||||
|
|
||||||
## ✅ TODO
|
## ✅ TODO
|
||||||
|
46
dev/php82/Dockerfile
Normal file
46
dev/php82/Dockerfile
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
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
|
@ -15,6 +15,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- my-datavolume:/var/lib/mysql
|
- my-datavolume:/var/lib/mysql
|
||||||
- ./dev/database/create_test_db.sh:/docker-entrypoint-initdb.d/10-create_test_db.sh
|
- ./dev/database/create_test_db.sh:/docker-entrypoint-initdb.d/10-create_test_db.sh
|
||||||
|
command: --sql_mode=STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,ANSI,ONLY_FULL_GROUP_BY
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: somewordpress
|
MYSQL_ROOT_PASSWORD: somewordpress
|
||||||
MYSQL_DATABASE: wordpress
|
MYSQL_DATABASE: wordpress
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?php
|
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||||
|
|
||||||
// phpcs:disable PSR1.Classes.ClassDeclaration
|
// phpcs:disable PSR1.Classes.ClassDeclaration
|
||||||
// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||||
@ -425,6 +425,9 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
$collection->addCode(function() {
|
$collection->addCode(function() {
|
||||||
return $this->qaCodeSniffer([]);
|
return $this->qaCodeSniffer([]);
|
||||||
});
|
});
|
||||||
|
$collection->addCode(function() {
|
||||||
|
return $this->qaMinimalPluginStandard([]);
|
||||||
|
});
|
||||||
return $collection->run();
|
return $collection->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,6 +539,55 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
|
|
||||||
$stringFilesToCheck = !empty($filesToCheck) ? implode(' ', $filesToCheck) : '.';
|
$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
|
return $this
|
||||||
->taskExec($task)
|
->taskExec($task)
|
||||||
->arg('--ignore=' . implode(',', $ignorePatterns))
|
->arg('--ignore=' . implode(',', $ignorePatterns))
|
||||||
@ -799,6 +851,9 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
->addCode(function () use ($version) {
|
->addCode(function () use ($version) {
|
||||||
$this->releaseCreatePullRequest($version);
|
$this->releaseCreatePullRequest($version);
|
||||||
})
|
})
|
||||||
|
->addCode(function () use ($version) {
|
||||||
|
$this->releaseRerunCircleWorkflow(\MailPoetTasks\Release\CircleCiController::PROJECT_PREMIUM);
|
||||||
|
})
|
||||||
->addCode(function () use ($version) {
|
->addCode(function () use ($version) {
|
||||||
$this->translationsPrepareLanguagePacks($version);
|
$this->translationsPrepareLanguagePacks($version);
|
||||||
})
|
})
|
||||||
@ -1116,6 +1171,18 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
$this->say("Release '$version[name]' info was published on Slack.");
|
$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) {
|
public function downloadWooCommerceBlocksZip($tag = null) {
|
||||||
$this->createWpOrgDownloader('woo-gutenberg-products-block')
|
$this->createWpOrgDownloader('woo-gutenberg-products-block')
|
||||||
->downloadPluginZip('woo-gutenberg-products-block.zip', __DIR__ . '/tests/plugins/', $tag);
|
->downloadPluginZip('woo-gutenberg-products-block.zip', __DIR__ . '/tests/plugins/', $tag);
|
||||||
|
BIN
mailpoet/assets/audio/0.mp3
Normal file
BIN
mailpoet/assets/audio/0.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/1.mp3
Normal file
BIN
mailpoet/assets/audio/1.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/2.mp3
Normal file
BIN
mailpoet/assets/audio/2.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/3.mp3
Normal file
BIN
mailpoet/assets/audio/3.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/4.mp3
Normal file
BIN
mailpoet/assets/audio/4.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/5.mp3
Normal file
BIN
mailpoet/assets/audio/5.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/6.mp3
Normal file
BIN
mailpoet/assets/audio/6.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/7.mp3
Normal file
BIN
mailpoet/assets/audio/7.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/8.mp3
Normal file
BIN
mailpoet/assets/audio/8.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/9.mp3
Normal file
BIN
mailpoet/assets/audio/9.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/a.mp3
Normal file
BIN
mailpoet/assets/audio/a.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/b.mp3
Normal file
BIN
mailpoet/assets/audio/b.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/c.mp3
Normal file
BIN
mailpoet/assets/audio/c.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/d.mp3
Normal file
BIN
mailpoet/assets/audio/d.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/e.mp3
Normal file
BIN
mailpoet/assets/audio/e.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/f.mp3
Normal file
BIN
mailpoet/assets/audio/f.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/g.mp3
Normal file
BIN
mailpoet/assets/audio/g.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/h.mp3
Normal file
BIN
mailpoet/assets/audio/h.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/i.mp3
Normal file
BIN
mailpoet/assets/audio/i.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/j.mp3
Normal file
BIN
mailpoet/assets/audio/j.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/k.mp3
Normal file
BIN
mailpoet/assets/audio/k.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/l.mp3
Normal file
BIN
mailpoet/assets/audio/l.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/m.mp3
Normal file
BIN
mailpoet/assets/audio/m.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/n.mp3
Normal file
BIN
mailpoet/assets/audio/n.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/o.mp3
Normal file
BIN
mailpoet/assets/audio/o.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/p.mp3
Normal file
BIN
mailpoet/assets/audio/p.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/q.mp3
Normal file
BIN
mailpoet/assets/audio/q.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/r.mp3
Normal file
BIN
mailpoet/assets/audio/r.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/s.mp3
Normal file
BIN
mailpoet/assets/audio/s.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/t.mp3
Normal file
BIN
mailpoet/assets/audio/t.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/u.mp3
Normal file
BIN
mailpoet/assets/audio/u.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/v.mp3
Normal file
BIN
mailpoet/assets/audio/v.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/w.mp3
Normal file
BIN
mailpoet/assets/audio/w.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/x.mp3
Normal file
BIN
mailpoet/assets/audio/x.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/y.mp3
Normal file
BIN
mailpoet/assets/audio/y.mp3
Normal file
Binary file not shown.
BIN
mailpoet/assets/audio/z.mp3
Normal file
BIN
mailpoet/assets/audio/z.mp3
Normal file
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
.mailpoet-automation-workflow-add-trigger {
|
.mailpoet-automation-add-trigger {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px dashed #c3c4c7;
|
border: 1px dashed #c3c4c7;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
.mailpoet-automation-editor-workflow {
|
.mailpoet-automation-editor-automation {
|
||||||
background: #fbfbfb;
|
background: #fbfbfb;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-automation-editor-workflow-wrapper {
|
.mailpoet-automation-editor-automation-wrapper {
|
||||||
display: grid;
|
display: grid;
|
||||||
padding: 50px 20px;
|
padding: 50px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-automation-editor-workflow-end {
|
.mailpoet-automation-editor-automation-end {
|
||||||
background: #8c8f94;
|
background: #8c8f94;
|
||||||
border-radius: 999999px;
|
border-radius: 999999px;
|
||||||
fill: white;
|
fill: white;
|
@ -1,4 +1,4 @@
|
|||||||
.mailpoet-automation-editor-empty-workflow {
|
.mailpoet-automation-editor-empty-automation {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100%;
|
height: 100%;
|
@ -36,7 +36,8 @@
|
|||||||
|
|
||||||
input,
|
input,
|
||||||
select,
|
select,
|
||||||
textarea {
|
textarea,
|
||||||
|
input[type='text'].components-form-token-field__input {
|
||||||
background: right top/26px no-repeat url('../../img/icons/alert.svg');
|
background: right top/26px no-repeat url('../../img/icons/alert.svg');
|
||||||
padding-right: 26px;
|
padding-right: 26px;
|
||||||
}
|
}
|
||||||
|
@ -51,4 +51,8 @@
|
|||||||
.mailpoet_form_field_block {
|
.mailpoet_form_field_block {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mailpoet_form_field_input_nowrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,3 +122,21 @@ body .components-modal__screen-overlay {
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: $grid-gap-half;
|
margin-top: $grid-gap-half;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mailpoet-locked-badge {
|
||||||
|
align-items: center;
|
||||||
|
background: #fcf9e8;
|
||||||
|
border: .5px solid #f5e6ab;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #bd8600;
|
||||||
|
display: flex;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
gap: 4px;
|
||||||
|
height: 20px;
|
||||||
|
letter-spacing: .2px;
|
||||||
|
line-height: 16px;
|
||||||
|
padding: 2px 8px 2px 4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
width: 82px;
|
||||||
|
}
|
||||||
|
11
mailpoet/assets/css/src/components-plugin/_landingpage.scss
Normal file
11
mailpoet/assets/css/src/components-plugin/_landingpage.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#mailpoet_landingpage_container {
|
||||||
|
.mailpoet-content-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-footer {
|
||||||
|
box-shadow: 0 -1px 0 0 $color-tertiary-light;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
}
|
@ -119,6 +119,10 @@
|
|||||||
color: $color-stats-average;
|
color: $color-stats-average;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mailpoet-statistics-value-number-critical {
|
||||||
|
color: $color-stats-critical;
|
||||||
|
}
|
||||||
|
|
||||||
.mailpoet-statistics-value-number-excellent {
|
.mailpoet-statistics-value-number-excellent {
|
||||||
color: $color-stats-excellent;
|
color: $color-stats-excellent;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#mailpoet-wizard-container {
|
||||||
|
.mailpoet-top-bar {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mailpoet-wizard-logo {
|
.mailpoet-wizard-logo {
|
||||||
margin-bottom: 100px;
|
margin-bottom: 100px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -35,6 +41,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
@include respond-to(medium-screen) {
|
@include respond-to(medium-screen) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -42,7 +49,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-wizard-step-illustration {
|
.mailpoet-wizard-step-illustration {
|
||||||
margin-right: $grid-gap;
|
margin-right: $grid-gap-xl;
|
||||||
max-width: $grid-column;
|
max-width: $grid-column;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -63,12 +70,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-wizard-step-content {
|
.mailpoet-wizard-step-content {
|
||||||
max-width: $grid-column-small + $grid-gap + $grid-column;
|
max-width: 480px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@include respond-to(medium-screen) {
|
@include respond-to(medium-screen) {
|
||||||
max-width: $grid-column;
|
max-width: $grid-column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mailpoet-button {
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-wizard-label {
|
.mailpoet-wizard-label {
|
||||||
@ -111,12 +122,16 @@
|
|||||||
|
|
||||||
.mailpoet-wizard-woocommerce-option {
|
.mailpoet-wizard-woocommerce-option {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: 0 -1px 0 0 $color-tertiary-light;
|
box-shadow: 0 1px 0 0 $color-tertiary-light;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-bottom: 25px;
|
padding-bottom: 25px;
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
box-shadow: 0 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-wizard-note {
|
.mailpoet-wizard-note {
|
||||||
|
12
mailpoet/assets/css/src/components-public/captcha.scss
Normal file
12
mailpoet/assets/css/src/components-public/captcha.scss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.mailpoet_captcha_form {
|
||||||
|
.mailpoet_icon_button {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -112,3 +112,11 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
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,6 +41,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mailpoet-tag-critical {
|
||||||
|
border-color: $color-stats-critical;
|
||||||
|
color: $color-stats-critical;
|
||||||
|
|
||||||
|
&.mailpoet-tag-inverted {
|
||||||
|
background: $color-stats-critical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mailpoet-tag-good {
|
.mailpoet-tag-good {
|
||||||
border-color: $color-stats-good;
|
border-color: $color-stats-good;
|
||||||
color: $color-stats-good;
|
color: $color-stats-good;
|
||||||
|
@ -29,7 +29,6 @@ $beamer-dot-size: 8px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-top-bar-logo {
|
.mailpoet-top-bar-logo {
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@ -39,6 +38,10 @@ $beamer-dot-size: 8px;
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.mailpoet-top-bar-logo {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mailpoet-top-bar-logo-desktop {
|
.mailpoet-top-bar-logo-desktop {
|
||||||
|
@ -11,17 +11,17 @@
|
|||||||
|
|
||||||
@import './components-automation-editor/add-step-button';
|
@import './components-automation-editor/add-step-button';
|
||||||
@import './components-automation-editor/add-trigger';
|
@import './components-automation-editor/add-trigger';
|
||||||
|
@import './components-automation-editor/automation';
|
||||||
@import './components-automation-editor/block-icon';
|
@import './components-automation-editor/block-icon';
|
||||||
@import './components-automation-editor/chip';
|
@import './components-automation-editor/chip';
|
||||||
@import './components-automation-editor/dropdown';
|
@import './components-automation-editor/dropdown';
|
||||||
@import './components-automation-editor/empty-workflow';
|
@import './components-automation-editor/empty-automation';
|
||||||
@import './components-automation-editor/errors';
|
@import './components-automation-editor/errors';
|
||||||
@import './components-automation-editor/panel';
|
@import './components-automation-editor/panel';
|
||||||
@import './components-automation-editor/separator';
|
@import './components-automation-editor/separator';
|
||||||
@import './components-automation-editor/status';
|
@import './components-automation-editor/status';
|
||||||
@import './components-automation-editor/step';
|
@import './components-automation-editor/step';
|
||||||
@import './components-automation-editor/step-card';
|
@import './components-automation-editor/step-card';
|
||||||
@import './components-automation-editor/workflow';
|
|
||||||
@import './components-automation-editor/notices';
|
@import './components-automation-editor/notices';
|
||||||
@import './components-automation-editor/deactivate-modal';
|
@import './components-automation-editor/deactivate-modal';
|
||||||
|
|
||||||
|
@ -17,17 +17,30 @@ ul.mailpoet-automation-templates {
|
|||||||
|
|
||||||
.mailpoet-automation-template-list-item {
|
.mailpoet-automation-template-list-item {
|
||||||
button.components-button {
|
button.components-button {
|
||||||
|
align-content: baseline;
|
||||||
|
align-items: flex-start;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #dcdcde;
|
border: 1px solid #dcdcde;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-rows: 40px auto auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 24px 24px 26px;
|
padding: 24px 24px 26px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&[aria-disabled='true'] {
|
||||||
|
color: #787c82;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: #787c82;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #dcdcde;
|
border: 1px solid #dcdcde;
|
||||||
@ -49,7 +62,6 @@ ul.mailpoet-automation-templates {
|
|||||||
h2 {
|
h2 {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: #2271b1;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 21px;
|
line-height: 21px;
|
||||||
@ -78,4 +90,27 @@ ul.mailpoet-automation-templates {
|
|||||||
fill: #dcdcde;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,3 +85,4 @@
|
|||||||
@import 'components-plugin/set-from-address-modal';
|
@import 'components-plugin/set-from-address-modal';
|
||||||
@import 'components-plugin/stats';
|
@import 'components-plugin/stats';
|
||||||
@import 'components-plugin/import-export';
|
@import 'components-plugin/import-export';
|
||||||
|
@import 'components-plugin/landingpage';
|
||||||
|
@ -19,3 +19,4 @@
|
|||||||
@import 'components-public/public';
|
@import 'components-public/public';
|
||||||
@import 'components-public/animation';
|
@import 'components-public/animation';
|
||||||
@import 'components-public/form_colors';
|
@import 'components-public/form_colors';
|
||||||
|
@import 'components-public/captcha';
|
||||||
|
@ -69,6 +69,7 @@ $color-badge-video-guide: #46b450;
|
|||||||
$color-stats-average: #f559c3;
|
$color-stats-average: #f559c3;
|
||||||
$color-stats-good: #ff9f00;
|
$color-stats-good: #ff9f00;
|
||||||
$color-stats-excellent: #7ed321;
|
$color-stats-excellent: #7ed321;
|
||||||
|
$color-stats-critical: #f00;
|
||||||
$color-stats-unknown: $color-primary-inactive;
|
$color-stats-unknown: $color-primary-inactive;
|
||||||
|
|
||||||
// Automation editor
|
// Automation editor
|
||||||
|
1
mailpoet/assets/img/icons/controls-volumeon.svg
Normal file
1
mailpoet/assets/img/icons/controls-volumeon.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<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>
|
After Width: | Height: | Size: 587 B |
1
mailpoet/assets/img/icons/image-rotate.svg
Normal file
1
mailpoet/assets/img/icons/image-rotate.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<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>
|
After Width: | Height: | Size: 355 B |
@ -1,81 +0,0 @@
|
|||||||
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,9 +1,7 @@
|
|||||||
import apiFetch from '@wordpress/api-fetch';
|
import apiFetch from '@wordpress/api-fetch';
|
||||||
import { api } from '../config';
|
import { api } from '../config';
|
||||||
|
|
||||||
export * from './hooks';
|
const apiUrl = `${api.root}/mailpoet/v1/`;
|
||||||
|
|
||||||
const apiUrl = `${api.root}/mailpoet/v1/automation/`;
|
|
||||||
|
|
||||||
export type ApiError = {
|
export type ApiError = {
|
||||||
code?: string;
|
code?: string;
|
||||||
|
@ -1,24 +1,33 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { TopBarWithBeamer } from 'common/top_bar/top_bar';
|
import { TopBarWithBeamer } from 'common/top_bar/top_bar';
|
||||||
import { Popover, SlotFillProvider } from '@wordpress/components';
|
import { Popover, SlotFillProvider } from '@wordpress/components';
|
||||||
import { useSelect } from '@wordpress/data';
|
import { useSelect } from '@wordpress/data';
|
||||||
import { initializeApi, useMutation } from './api';
|
import { initializeApi } from './api';
|
||||||
import { registerTranslations } from './i18n';
|
import { registerTranslations } from './i18n';
|
||||||
import { createStore, storeName } from './listing/store';
|
import { createStore, storeName } from './listing/store';
|
||||||
import { AutomationListing, AutomationListingHeader } from './listing';
|
import { AutomationListing, AutomationListingHeader } from './listing';
|
||||||
import { registerApiErrorHandler } from './listing/api-error-handler';
|
import { registerApiErrorHandler } from './listing/api-error-handler';
|
||||||
import { Notices } from './listing/components/notices';
|
import { Notices } from './listing/components/notices';
|
||||||
import { WorkflowListingNotices } from './listing/workflow-listing-notices';
|
|
||||||
import { BuildYourOwnSection, HeroSection, TemplatesSection } from './sections';
|
import { BuildYourOwnSection, HeroSection, TemplatesSection } from './sections';
|
||||||
import {
|
import { MailPoet } from '../mailpoet';
|
||||||
CreateEmptyWorkflowButton,
|
|
||||||
CreateWorkflowFromTemplateButton,
|
const trackOpenEvent = () => {
|
||||||
} from './testing';
|
MailPoet.trackEvent('Automations > Listing viewed');
|
||||||
|
};
|
||||||
|
|
||||||
function Content(): JSX.Element {
|
function Content(): JSX.Element {
|
||||||
const count = useSelect((select) => select(storeName).getWorkflowCount());
|
const [isBooting, setIsBooting] = useState(true);
|
||||||
|
const count = useSelect((select) => select(storeName).getAutomationCount());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isBooting || count === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
trackOpenEvent();
|
||||||
|
setIsBooting(false);
|
||||||
|
}, [isBooting, count]);
|
||||||
const content =
|
const content =
|
||||||
count > 0 ? (
|
count > 0 ? (
|
||||||
<>
|
<>
|
||||||
@ -49,7 +58,7 @@ function Content(): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Workflows(): JSX.Element {
|
function Automations(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBarWithBeamer />
|
<TopBarWithBeamer />
|
||||||
@ -59,79 +68,12 @@ function Workflows(): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RecreateSchemaButton(): JSX.Element {
|
|
||||||
const [createSchema, { loading, error }] = useMutation('system/database', {
|
|
||||||
method: 'POST',
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function App(): JSX.Element {
|
function App(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<SlotFillProvider>
|
<SlotFillProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<div>
|
<Automations />
|
||||||
<Workflows />
|
<Popover.Slot />
|
||||||
<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>
|
</BrowserRouter>
|
||||||
</SlotFillProvider>
|
</SlotFillProvider>
|
||||||
);
|
);
|
||||||
|
@ -4,9 +4,9 @@ declare global {
|
|||||||
root: string;
|
root: string;
|
||||||
nonce: string;
|
nonce: string;
|
||||||
};
|
};
|
||||||
mailpoet_workflow_count: number;
|
mailpoet_automation_count: number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const api = window.mailpoet_automation_api;
|
export const api = window.mailpoet_automation_api;
|
||||||
export const workflowCount = window.mailpoet_workflow_count;
|
export const automationCount = window.mailpoet_automation_count;
|
||||||
|
@ -19,7 +19,7 @@ export const registerApiErrorHandler = (): void =>
|
|||||||
const status = errorObject.data?.status;
|
const status = errorObject.data?.status;
|
||||||
const code = errorObject.code;
|
const code = errorObject.code;
|
||||||
|
|
||||||
if (code === 'mailpoet_automation_workflow_not_valid') {
|
if (code === 'mailpoet_automation_not_valid') {
|
||||||
dispatch(storeName).setErrors({ steps: errorObject.data.errors });
|
dispatch(storeName).setErrors({ steps: errorObject.data.errors });
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ import { storeName } from '../../store';
|
|||||||
|
|
||||||
export function TrashButton(): JSX.Element {
|
export function TrashButton(): JSX.Element {
|
||||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||||
const { workflow } = useSelect(
|
const { automation } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -33,7 +33,7 @@ export function TrashButton(): JSX.Element {
|
|||||||
>
|
>
|
||||||
{sprintf(
|
{sprintf(
|
||||||
__('You are about to delete the automation "%s".', 'mailpoet'),
|
__('You are about to delete the automation "%s".', 'mailpoet'),
|
||||||
workflow.name,
|
automation.name,
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
{__(' This will stop it for all subscribers immediately.', 'mailpoet')}
|
{__(' This will stop it for all subscribers immediately.', 'mailpoet')}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { __unstableCompositeItem as CompositeItem } from '@wordpress/components';
|
import { __unstableCompositeItem as CompositeItem } from '@wordpress/components';
|
||||||
import { Icon, plus } from '@wordpress/icons';
|
import { Icon, plus } from '@wordpress/icons';
|
||||||
import { WorkflowCompositeContext } from './context';
|
import { AutomationCompositeContext } from './context';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClick?: (element: HTMLButtonElement) => void;
|
onClick?: (element: HTMLButtonElement) => void;
|
||||||
@ -9,7 +9,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function AddStepButton({ onClick, previousStepId }: Props): JSX.Element {
|
export function AddStepButton({ onClick, previousStepId }: Props): JSX.Element {
|
||||||
const compositeState = useContext(WorkflowCompositeContext);
|
const compositeState = useContext(AutomationCompositeContext);
|
||||||
return (
|
return (
|
||||||
<CompositeItem
|
<CompositeItem
|
||||||
state={compositeState}
|
state={compositeState}
|
@ -3,7 +3,7 @@ import { __unstableCompositeItem as CompositeItem } from '@wordpress/components'
|
|||||||
import { Icon, plus } from '@wordpress/icons';
|
import { Icon, plus } from '@wordpress/icons';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { WorkflowCompositeContext } from './context';
|
import { AutomationCompositeContext } from './context';
|
||||||
import { Step } from './types';
|
import { Step } from './types';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
|
|
||||||
@ -12,14 +12,14 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function AddTrigger({ step }: Props): JSX.Element {
|
export function AddTrigger({ step }: Props): JSX.Element {
|
||||||
const compositeState = useContext(WorkflowCompositeContext);
|
const compositeState = useContext(AutomationCompositeContext);
|
||||||
const { setInserterPopover } = useDispatch(storeName);
|
const { setInserterPopover } = useDispatch(storeName);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CompositeItem
|
<CompositeItem
|
||||||
state={compositeState}
|
state={compositeState}
|
||||||
role="treeitem"
|
role="treeitem"
|
||||||
className="mailpoet-automation-workflow-add-trigger"
|
className="mailpoet-automation-add-trigger"
|
||||||
data-previous-step-id={step.id}
|
data-previous-step-id={step.id}
|
||||||
focusable
|
focusable
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
@ -1,5 +1,5 @@
|
|||||||
import { __unstableUseCompositeState as useCompositeState } from '@wordpress/components';
|
import { __unstableUseCompositeState as useCompositeState } from '@wordpress/components';
|
||||||
import { createContext } from '@wordpress/element';
|
import { createContext } from '@wordpress/element';
|
||||||
|
|
||||||
export const WorkflowCompositeContext =
|
export const AutomationCompositeContext =
|
||||||
createContext<ReturnType<typeof useCompositeState>>(undefined);
|
createContext<ReturnType<typeof useCompositeState>>(undefined);
|
@ -0,0 +1,9 @@
|
|||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
export function EmptyAutomation(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="mailpoet-automation-editor-empty-automation">
|
||||||
|
{__('No automation data.', 'mailpoet')}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -7,8 +7,8 @@ import { useSelect } from '@wordpress/data';
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon, check } from '@wordpress/icons';
|
import { Icon, check } from '@wordpress/icons';
|
||||||
import { Hooks } from 'wp-js-hooks';
|
import { Hooks } from 'wp-js-hooks';
|
||||||
import { WorkflowCompositeContext } from './context';
|
import { AutomationCompositeContext } from './context';
|
||||||
import { EmptyWorkflow } from './empty-workflow';
|
import { EmptyAutomation } from './empty-automation';
|
||||||
import { Separator } from './separator';
|
import { Separator } from './separator';
|
||||||
import { Step } from './step';
|
import { Step } from './step';
|
||||||
import { Step as StepData } from './types';
|
import { Step as StepData } from './types';
|
||||||
@ -21,10 +21,10 @@ import {
|
|||||||
RenderStepType,
|
RenderStepType,
|
||||||
} from '../../../types/filters';
|
} from '../../../types/filters';
|
||||||
|
|
||||||
export function Workflow(): JSX.Element {
|
export function Automation(): JSX.Element {
|
||||||
const { workflowData, selectedStep } = useSelect(
|
const { automationData, selectedStep } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowData: select(storeName).getWorkflowData(),
|
automationData: select(storeName).getAutomationData(),
|
||||||
selectedStep: select(storeName).getSelectedStep(),
|
selectedStep: select(storeName).getSelectedStep(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
@ -36,9 +36,9 @@ export function Workflow(): JSX.Element {
|
|||||||
shift: true,
|
shift: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stepMap = workflowData?.steps ?? undefined;
|
const stepMap = automationData?.steps ?? undefined;
|
||||||
|
|
||||||
// serialize steps (for now, we support only one trigger and linear workflows)
|
// serialize steps (for now, we support only one trigger and linear automations)
|
||||||
const steps = useMemo(() => {
|
const steps = useMemo(() => {
|
||||||
const stepArray = [stepMap.root];
|
const stepArray = [stepMap.root];
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ export function Workflow(): JSX.Element {
|
|||||||
const renderStep = useMemo(
|
const renderStep = useMemo(
|
||||||
(): RenderStepType =>
|
(): RenderStepType =>
|
||||||
Hooks.applyFilters(
|
Hooks.applyFilters(
|
||||||
'mailpoet.automation.workflow.render_step',
|
'mailpoet.automation.render_step',
|
||||||
(stepData: StepData) =>
|
(stepData: StepData) =>
|
||||||
stepData.type === 'root' ? (
|
stepData.type === 'root' ? (
|
||||||
<AddTrigger step={stepData} />
|
<AddTrigger step={stepData} />
|
||||||
@ -73,7 +73,7 @@ export function Workflow(): JSX.Element {
|
|||||||
const renderSeparator = useMemo(
|
const renderSeparator = useMemo(
|
||||||
(): RenderStepSeparatorType =>
|
(): RenderStepSeparatorType =>
|
||||||
Hooks.applyFilters(
|
Hooks.applyFilters(
|
||||||
'mailpoet.automation.workflow.render_step_separator',
|
'mailpoet.automation.render_step_separator',
|
||||||
(previousStepData: StepData) => (
|
(previousStepData: StepData) => (
|
||||||
<Separator previousStepId={previousStepData.id} />
|
<Separator previousStepId={previousStepData.id} />
|
||||||
),
|
),
|
||||||
@ -81,20 +81,20 @@ export function Workflow(): JSX.Element {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!workflowData) {
|
if (!automationData) {
|
||||||
return <EmptyWorkflow />;
|
return <EmptyAutomation />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WorkflowCompositeContext.Provider value={compositeState}>
|
<AutomationCompositeContext.Provider value={compositeState}>
|
||||||
<Composite
|
<Composite
|
||||||
state={compositeState}
|
state={compositeState}
|
||||||
role="tree"
|
role="tree"
|
||||||
aria-label={__('Automation', 'mailpoet')}
|
aria-label={__('Automation', 'mailpoet')}
|
||||||
aria-orientation="vertical"
|
aria-orientation="vertical"
|
||||||
className="mailpoet-automation-editor-workflow"
|
className="mailpoet-automation-editor-automation"
|
||||||
>
|
>
|
||||||
<div className="mailpoet-automation-editor-workflow-wrapper">
|
<div className="mailpoet-automation-editor-automation-wrapper">
|
||||||
<Statistics />
|
<Statistics />
|
||||||
{stepMap.root.next_steps.length === 0 ? (
|
{stepMap.root.next_steps.length === 0 ? (
|
||||||
<>
|
<>
|
||||||
@ -119,13 +119,13 @@ export function Workflow(): JSX.Element {
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
<Icon
|
<Icon
|
||||||
className="mailpoet-automation-editor-workflow-end"
|
className="mailpoet-automation-editor-automation-end"
|
||||||
icon={check}
|
icon={check}
|
||||||
/>
|
/>
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
<InserterPopover />
|
<InserterPopover />
|
||||||
</Composite>
|
</Composite>
|
||||||
</WorkflowCompositeContext.Provider>
|
</AutomationCompositeContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -4,9 +4,9 @@ import { storeName } from '../../store';
|
|||||||
import { Statistics as BaseStatistics } from '../../../components/statistics';
|
import { Statistics as BaseStatistics } from '../../../components/statistics';
|
||||||
|
|
||||||
export function Statistics(): JSX.Element {
|
export function Statistics(): JSX.Element {
|
||||||
const { workflow } = useSelect(
|
const { automation } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -19,19 +19,19 @@ export function Statistics(): JSX.Element {
|
|||||||
key: 'entered',
|
key: 'entered',
|
||||||
// translators: Total number of subscribers who entered an automation
|
// translators: Total number of subscribers who entered an automation
|
||||||
label: _x('Total Entered', 'automation stats', 'mailpoet'),
|
label: _x('Total Entered', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.entered,
|
value: automation.stats.totals.entered,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'processing',
|
key: 'processing',
|
||||||
// translators: Total number of subscribers who are being processed in an automation
|
// translators: Total number of subscribers who are being processed in an automation
|
||||||
label: _x('Total Processing', 'automation stats', 'mailpoet'),
|
label: _x('Total Processing', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.in_progress,
|
value: automation.stats.totals.in_progress,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'exited',
|
key: 'exited',
|
||||||
// translators: Total number of subscribers who exited an automation, no matter the result
|
// translators: Total number of subscribers who exited an automation, no matter the result
|
||||||
label: _x('Total Exited', 'automation stats', 'mailpoet'),
|
label: _x('Total Exited', 'automation stats', 'mailpoet'),
|
||||||
value: workflow.stats.totals.exited,
|
value: automation.stats.totals.exited,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
@ -1,12 +1,10 @@
|
|||||||
import { useState, Fragment } from 'react';
|
import { useState, Fragment } from 'react';
|
||||||
import { DropdownMenu } from '@wordpress/components';
|
import { DropdownMenu } from '@wordpress/components';
|
||||||
import { useSelect } from '@wordpress/data';
|
|
||||||
import { moreVertical, trash } from '@wordpress/icons';
|
import { moreVertical, trash } from '@wordpress/icons';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Hooks } from 'wp-js-hooks';
|
import { Hooks } from 'wp-js-hooks';
|
||||||
import { PremiumModal } from 'common/premium_modal';
|
import { PremiumModal } from 'common/premium_modal';
|
||||||
import { Step as StepData } from './types';
|
import { Step as StepData } from './types';
|
||||||
import { storeName } from '../../store';
|
|
||||||
import { StepMoreControlsType } from '../../../types/filters';
|
import { StepMoreControlsType } from '../../../types/filters';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -14,16 +12,10 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function StepMoreMenu({ step }: Props): JSX.Element {
|
export function StepMoreMenu({ step }: Props): JSX.Element {
|
||||||
const { stepType } = useSelect(
|
|
||||||
(select) => ({
|
|
||||||
stepType: select(storeName).getStepType(step.key),
|
|
||||||
}),
|
|
||||||
[step],
|
|
||||||
);
|
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
const moreControls: StepMoreControlsType = Hooks.applyFilters(
|
const moreControls: StepMoreControlsType = Hooks.applyFilters(
|
||||||
'mailpoet.automation.workflow.step.more-controls',
|
'mailpoet.automation.step.more-controls',
|
||||||
{
|
{
|
||||||
delete: {
|
delete: {
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
@ -53,7 +45,6 @@ export function StepMoreMenu({ step }: Props): JSX.Element {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
step,
|
step,
|
||||||
stepType,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const slots = Object.values(moreControls).filter(
|
const slots = Object.values(moreControls).filter(
|
@ -4,7 +4,7 @@ import { __unstableCompositeItem as CompositeItem } from '@wordpress/components'
|
|||||||
import { useDispatch, useRegistry, useSelect } from '@wordpress/data';
|
import { useDispatch, useRegistry, useSelect } from '@wordpress/data';
|
||||||
import { blockMeta } from '@wordpress/icons';
|
import { blockMeta } from '@wordpress/icons';
|
||||||
import { __, _x } from '@wordpress/i18n';
|
import { __, _x } from '@wordpress/i18n';
|
||||||
import { WorkflowCompositeContext } from './context';
|
import { AutomationCompositeContext } from './context';
|
||||||
import { StepMoreMenu } from './step-more-menu';
|
import { StepMoreMenu } from './step-more-menu';
|
||||||
import { Step as StepData } from './types';
|
import { Step as StepData } from './types';
|
||||||
import { Chip } from '../chip';
|
import { Chip } from '../chip';
|
||||||
@ -48,7 +48,7 @@ export function Step({ step, isSelected }: Props): JSX.Element {
|
|||||||
[step],
|
[step],
|
||||||
);
|
);
|
||||||
const { openSidebar, selectStep } = useDispatch(storeName);
|
const { openSidebar, selectStep } = useDispatch(storeName);
|
||||||
const compositeState = useContext(WorkflowCompositeContext);
|
const compositeState = useContext(AutomationCompositeContext);
|
||||||
const { batch } = useRegistry();
|
const { batch } = useRegistry();
|
||||||
|
|
||||||
const compositeItemId = `step-${step.id}`;
|
const compositeItemId = `step-${step.id}`;
|
@ -1,4 +1,4 @@
|
|||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
|
|
||||||
export type NextStep = {
|
export type NextStep = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -12,10 +12,10 @@ export type Step = {
|
|||||||
next_steps: NextStep[];
|
next_steps: NextStep[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Workflow = {
|
export type Automation = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
status: WorkflowStatus;
|
status: AutomationStatus;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
activated_at: string;
|
activated_at: string;
|
@ -10,7 +10,7 @@ import { useRef } from '@wordpress/element';
|
|||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { chevronDown } from '@wordpress/icons';
|
import { chevronDown } from '@wordpress/icons';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
|
|
||||||
// See: https://github.com/WordPress/gutenberg/blob/eff0cab2b3181c004dbd15398e570ecec28a3726/packages/edit-site/src/components/header/document-actions/index.js
|
// See: https://github.com/WordPress/gutenberg/blob/eff0cab2b3181c004dbd15398e570ecec28a3726/packages/edit-site/src/components/header/document-actions/index.js
|
||||||
|
|
||||||
@ -21,11 +21,11 @@ const Dropdown: ComponentType<
|
|||||||
}
|
}
|
||||||
> = WpDropdown;
|
> = WpDropdown;
|
||||||
|
|
||||||
export function DocumentActions({ children }): JSX.Element {
|
function DocumentActions({ children }): JSX.Element {
|
||||||
const { workflowName, workflowStatus, showIconLabels } = useSelect(
|
const { automationName, automationStatus, showIconLabels } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowName: select(storeName).getWorkflowData().name,
|
automationName: select(storeName).getAutomationData().name,
|
||||||
workflowStatus: select(storeName).getWorkflowData().status,
|
automationStatus: select(storeName).getAutomationData().status,
|
||||||
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
@ -36,9 +36,9 @@ export function DocumentActions({ children }): JSX.Element {
|
|||||||
const titleRef = useRef();
|
const titleRef = useRef();
|
||||||
|
|
||||||
let chipClass = 'mailpoet-automation-editor-chip-gray';
|
let chipClass = 'mailpoet-automation-editor-chip-gray';
|
||||||
if (workflowStatus === WorkflowStatus.ACTIVE) {
|
if (automationStatus === AutomationStatus.ACTIVE) {
|
||||||
chipClass = 'mailpoet-automation-editor-chip-success';
|
chipClass = 'mailpoet-automation-editor-chip-success';
|
||||||
} else if (workflowStatus === WorkflowStatus.DEACTIVATING) {
|
} else if (automationStatus === AutomationStatus.DEACTIVATING) {
|
||||||
chipClass = 'mailpoet-automation-editor-chip-danger';
|
chipClass = 'mailpoet-automation-editor-chip-danger';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,18 +66,18 @@ export function DocumentActions({ children }): JSX.Element {
|
|||||||
<VisuallyHidden as="span">
|
<VisuallyHidden as="span">
|
||||||
{__('Editing automation:', 'mailpoet')}
|
{__('Editing automation:', 'mailpoet')}
|
||||||
</VisuallyHidden>
|
</VisuallyHidden>
|
||||||
{workflowName}
|
{automationName}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
size="body"
|
size="body"
|
||||||
className={`edit-site-document-actions__secondary-item ${chipClass}`}
|
className={`edit-site-document-actions__secondary-item ${chipClass}`}
|
||||||
>
|
>
|
||||||
{workflowStatus === WorkflowStatus.ACTIVE &&
|
{automationStatus === AutomationStatus.ACTIVE &&
|
||||||
__('Active', 'mailpoet')}
|
__('Active', 'mailpoet')}
|
||||||
{workflowStatus === WorkflowStatus.DEACTIVATING &&
|
{automationStatus === AutomationStatus.DEACTIVATING &&
|
||||||
__('Deactivating', 'mailpoet')}
|
__('Deactivating', 'mailpoet')}
|
||||||
{workflowStatus === WorkflowStatus.DRAFT &&
|
{automationStatus === AutomationStatus.DRAFT &&
|
||||||
__('Draft', 'mailpoet')}
|
__('Draft', 'mailpoet')}
|
||||||
</Text>
|
</Text>
|
||||||
</a>
|
</a>
|
||||||
@ -101,3 +101,6 @@ export function DocumentActions({ children }): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DocumentActions.displayName = 'DocumentActions';
|
||||||
|
export { DocumentActions };
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
import { useDispatch, useSelect } from '@wordpress/data';
|
import { useDispatch, useSelect } from '@wordpress/data';
|
||||||
import { createContext } from '@wordpress/element';
|
import { createContext } from '@wordpress/element';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { ErrorBoundary } from 'common';
|
||||||
import { Chip } from '../chip';
|
import { Chip } from '../chip';
|
||||||
import { ColoredIcon } from '../icons';
|
import { ColoredIcon } from '../icons';
|
||||||
import {
|
import {
|
||||||
@ -35,17 +36,17 @@ type StepErrorProps = {
|
|||||||
function StepError({ stepId }: StepErrorProps): JSX.Element {
|
function StepError({ stepId }: StepErrorProps): JSX.Element {
|
||||||
const compositeState = useContext(ErrorsCompositeContext);
|
const compositeState = useContext(ErrorsCompositeContext);
|
||||||
|
|
||||||
const { steps, workflowData } = useSelect(
|
const { steps, automationData } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
steps: select(storeName).getSteps(),
|
steps: select(storeName).getSteps(),
|
||||||
workflowData: select(storeName).getWorkflowData(),
|
automationData: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { openSidebar, selectStep } = useDispatch(storeName);
|
const { openSidebar, selectStep } = useDispatch(storeName);
|
||||||
|
|
||||||
const stepData = workflowData.steps[stepId];
|
const stepData = automationData.steps[stepId];
|
||||||
const step = steps.find(({ key }) => key === stepData.key);
|
const step = steps.find(({ key }) => key === stepData.key);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -70,6 +71,8 @@ function StepError({ stepId }: StepErrorProps): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StepError.displayName = 'StepError';
|
||||||
|
|
||||||
export function Errors(): JSX.Element | null {
|
export function Errors(): JSX.Element | null {
|
||||||
const [showPopover, setShowPopover] = useState(false);
|
const [showPopover, setShowPopover] = useState(false);
|
||||||
|
|
||||||
@ -78,10 +81,10 @@ export function Errors(): JSX.Element | null {
|
|||||||
shift: true,
|
shift: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { errors, workflowData } = useSelect(
|
const { errors, automationData } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
errors: select(storeName).getErrors(),
|
errors: select(storeName).getErrors(),
|
||||||
workflowData: select(storeName).getWorkflowData(),
|
automationData: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -93,18 +96,18 @@ export function Errors(): JSX.Element | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const visited = new Map<string, StepErrorType | undefined>();
|
const visited = new Map<string, StepErrorType | undefined>();
|
||||||
const ids = workflowData.steps.root.next_steps.map(({ id }) => id);
|
const ids = automationData.steps.root.next_steps.map(({ id }) => id);
|
||||||
while (ids.length > 0) {
|
while (ids.length > 0) {
|
||||||
const id = ids.shift();
|
const id = ids.shift();
|
||||||
if (!visited.has(id)) {
|
if (!visited.has(id)) {
|
||||||
visited.set(id, errors.steps[id]);
|
visited.set(id, errors.steps[id]);
|
||||||
workflowData.steps[id]?.next_steps?.forEach((step) =>
|
automationData.steps[id]?.next_steps?.forEach((step) =>
|
||||||
ids.push(step.id),
|
ids.push(step.id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [...visited.values()].filter((error) => !!error);
|
return [...visited.values()].filter((error) => !!error);
|
||||||
}, [errors, workflowData]);
|
}, [errors, automationData]);
|
||||||
|
|
||||||
// automatically open the popover when errors appear
|
// automatically open the popover when errors appear
|
||||||
const hasErrors = stepErrors.length > 0;
|
const hasErrors = stepErrors.length > 0;
|
||||||
@ -160,9 +163,11 @@ export function Errors(): JSX.Element | null {
|
|||||||
__('The following steps are not fully set:', 'mailpoet')
|
__('The following steps are not fully set:', 'mailpoet')
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{stepErrors.map((error) => (
|
<ErrorBoundary>
|
||||||
<StepError key={error.step_id} stepId={error.step_id} />
|
{stepErrors.map((error) => (
|
||||||
))}
|
<StepError key={error.step_id} stepId={error.step_id} />
|
||||||
|
))}
|
||||||
|
</ErrorBoundary>
|
||||||
</Composite>
|
</Composite>
|
||||||
</ErrorsCompositeContext.Provider>
|
</ErrorsCompositeContext.Provider>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@ -8,12 +8,13 @@ import {
|
|||||||
import { dispatch, useDispatch, useSelect } from '@wordpress/data';
|
import { dispatch, useDispatch, useSelect } from '@wordpress/data';
|
||||||
import { PinnedItems } from '@wordpress/interface';
|
import { PinnedItems } from '@wordpress/interface';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { ErrorBoundary } from 'common';
|
||||||
import { DocumentActions } from './document_actions';
|
import { DocumentActions } from './document_actions';
|
||||||
import { Errors } from './errors';
|
import { Errors } from './errors';
|
||||||
import { InserterToggle } from './inserter_toggle';
|
import { InserterToggle } from './inserter_toggle';
|
||||||
import { MoreMenu } from './more_menu';
|
import { MoreMenu } from './more_menu';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
import {
|
import {
|
||||||
DeactivateImmediatelyModal,
|
DeactivateImmediatelyModal,
|
||||||
DeactivateModal,
|
DeactivateModal,
|
||||||
@ -23,22 +24,23 @@ import {
|
|||||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/header/index.js
|
// 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
|
// https://github.com/WordPress/gutenberg/blob/0ee78b1bbe9c6f3e6df99f3b967132fa12bef77d/packages/edit-site/src/components/header/index.js
|
||||||
|
|
||||||
function ActivateButton({ onClick, label }): JSX.Element {
|
function ActivateButton({ label }): JSX.Element {
|
||||||
const { errors, isDeactivating } = useSelect(
|
const { errors, isDeactivating } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
errors: select(storeName).getErrors(),
|
errors: select(storeName).getErrors(),
|
||||||
isDeactivating:
|
isDeactivating:
|
||||||
select(storeName).getWorkflowData().status ===
|
select(storeName).getAutomationData().status ===
|
||||||
WorkflowStatus.DEACTIVATING,
|
AutomationStatus.DEACTIVATING,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
const { openActivationPanel } = useDispatch(storeName);
|
||||||
|
|
||||||
const button = (
|
const button = (
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
className="editor-post-publish-button"
|
className="editor-post-publish-button"
|
||||||
onClick={onClick}
|
onClick={openActivationPanel}
|
||||||
disabled={isDeactivating || !!errors}
|
disabled={isDeactivating || !!errors}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
@ -53,7 +55,7 @@ function ActivateButton({ onClick, label }): JSX.Element {
|
|||||||
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
||||||
delay={0}
|
delay={0}
|
||||||
text={__(
|
text={__(
|
||||||
'Editing an active workflow is temporarily unavailable. We are working on introducing this functionality.',
|
'Editing an active automation is temporarily unavailable. We are working on introducing this functionality.',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -68,14 +70,14 @@ function ActivateButton({ onClick, label }): JSX.Element {
|
|||||||
function UpdateButton(): JSX.Element {
|
function UpdateButton(): JSX.Element {
|
||||||
const { save } = useDispatch(storeName);
|
const { save } = useDispatch(storeName);
|
||||||
|
|
||||||
const { workflow } = useSelect(
|
const { automation } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (workflow.stats.totals.in_progress === 0) {
|
if (automation.stats.totals.in_progress === 0) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@ -93,7 +95,7 @@ function UpdateButton(): JSX.Element {
|
|||||||
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
// The following error seems to be a mismatch. It claims the 'delay' prop does not exist, but it does.
|
||||||
delay={0}
|
delay={0}
|
||||||
text={__(
|
text={__(
|
||||||
'Editing an active workflow is temporarily unavailable. We are working on introducing this functionality.',
|
'Editing an active automation is temporarily unavailable. We are working on introducing this functionality.',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -125,7 +127,7 @@ function DeactivateButton(): JSX.Element {
|
|||||||
const { hasUsersInProgress } = useSelect(
|
const { hasUsersInProgress } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
hasUsersInProgress:
|
hasUsersInProgress:
|
||||||
select(storeName).getWorkflowData().stats.totals.in_progress > 0,
|
select(storeName).getAutomationData().stats.totals.in_progress > 0,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -165,7 +167,7 @@ function DeactivateNowButton(): JSX.Element {
|
|||||||
const { hasUsersInProgress } = useSelect(
|
const { hasUsersInProgress } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
hasUsersInProgress:
|
hasUsersInProgress:
|
||||||
select(storeName).getWorkflowData().stats.totals.in_progress > 0,
|
select(storeName).getAutomationData().stats.totals.in_progress > 0,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -201,18 +203,14 @@ function DeactivateNowButton(): JSX.Element {
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
showInserterToggle: boolean;
|
showInserterToggle: boolean;
|
||||||
toggleActivatePanel: () => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Header({
|
export function Header({ showInserterToggle }: Props): JSX.Element {
|
||||||
showInserterToggle,
|
const { setAutomationName } = useDispatch(storeName);
|
||||||
toggleActivatePanel,
|
const { automationName, automationStatus } = useSelect(
|
||||||
}: Props): JSX.Element {
|
|
||||||
const { setWorkflowName } = useDispatch(storeName);
|
|
||||||
const { workflowName, workflowStatus } = useSelect(
|
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowName: select(storeName).getWorkflowData().name,
|
automationName: select(storeName).getAutomationData().name,
|
||||||
workflowStatus: select(storeName).getWorkflowData().status,
|
automationStatus: select(storeName).getAutomationData().status,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -230,50 +228,48 @@ export function Header({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="edit-site-header_center">
|
<div className="edit-site-header_center">
|
||||||
<DocumentActions>
|
<ErrorBoundary>
|
||||||
{() => (
|
<DocumentActions>
|
||||||
<div className="mailpoet-automation-editor-dropdown-name-edit">
|
{() => (
|
||||||
<div className="mailpoet-automation-editor-dropdown-name-edit-title">
|
<div className="mailpoet-automation-editor-dropdown-name-edit">
|
||||||
{__('Automation name', 'mailpoet')}
|
<div className="mailpoet-automation-editor-dropdown-name-edit-title">
|
||||||
|
{__('Automation name', 'mailpoet')}
|
||||||
|
</div>
|
||||||
|
<TextControl
|
||||||
|
value={automationName}
|
||||||
|
onChange={(newName) => setAutomationName(newName)}
|
||||||
|
help={__(
|
||||||
|
`Give the automation a name that indicates its purpose. E.g. "Abandoned cart recovery"`,
|
||||||
|
'mailpoet',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<TextControl
|
)}
|
||||||
value={workflowName}
|
</DocumentActions>
|
||||||
onChange={(newName) => setWorkflowName(newName)}
|
</ErrorBoundary>
|
||||||
help={__(
|
|
||||||
`Give the automation a name that indicates its purpose. E.g. "Abandoned cart recovery"`,
|
|
||||||
'mailpoet',
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DocumentActions>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="edit-site-header_end">
|
<div className="edit-site-header_end">
|
||||||
<div className="edit-site-header__actions">
|
<div className="edit-site-header__actions">
|
||||||
<Errors />
|
<ErrorBoundary>
|
||||||
{workflowStatus === WorkflowStatus.DRAFT && (
|
<Errors />
|
||||||
|
</ErrorBoundary>
|
||||||
|
{automationStatus === AutomationStatus.DRAFT && (
|
||||||
<>
|
<>
|
||||||
<SaveDraftButton />
|
<SaveDraftButton />
|
||||||
<ActivateButton
|
<ActivateButton label={__('Activate', 'mailpoet')} />
|
||||||
onClick={toggleActivatePanel}
|
|
||||||
label={__('Activate', 'mailpoet')}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{workflowStatus === WorkflowStatus.ACTIVE && (
|
{automationStatus === AutomationStatus.ACTIVE && (
|
||||||
<>
|
<>
|
||||||
<DeactivateButton />
|
<DeactivateButton />
|
||||||
<UpdateButton />
|
<UpdateButton />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{workflowStatus === WorkflowStatus.DEACTIVATING && (
|
{automationStatus === AutomationStatus.DEACTIVATING && (
|
||||||
<>
|
<>
|
||||||
<DeactivateNowButton />
|
<DeactivateNowButton />
|
||||||
<ActivateButton
|
<ActivateButton label={__('Update & Activate', 'mailpoet')} />
|
||||||
onClick={toggleActivatePanel}
|
|
||||||
label={__('Update & Activate', 'mailpoet')}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<PinnedItems.Slot scope={storeName} />
|
<PinnedItems.Slot scope={storeName} />
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from './panel';
|
||||||
|
export * from './form-token-field';
|
@ -22,7 +22,7 @@ export function InserterPopover(): JSX.Element | null {
|
|||||||
|
|
||||||
const onInsert = useCallback((item: Item) => {
|
const onInsert = useCallback((item: Item) => {
|
||||||
const addStepCallback: AddStepCallbackType = Hooks.applyFilters(
|
const addStepCallback: AddStepCallbackType = Hooks.applyFilters(
|
||||||
'mailpoet.automation.workflow.add_step_callback',
|
'mailpoet.automation.add_step_callback',
|
||||||
() => {
|
() => {
|
||||||
setShowModal(true);
|
setShowModal(true);
|
||||||
},
|
},
|
||||||
@ -45,7 +45,7 @@ export function InserterPopover(): JSX.Element | null {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Inserter onInsert={onInsert} />
|
<Inserter onInsert={onInsert} showInserterHelpPanel={false} />
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
{showModal && (
|
{showModal && (
|
||||||
|
@ -20,134 +20,139 @@ const filterItems = (value: string, item: Item[]): Item[] =>
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onInsert?: (item: Item) => void;
|
onInsert?: (item: Item) => void;
|
||||||
|
showInserterHelpPanel?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Inserter = forwardRef(({ onInsert }: Props, ref): JSX.Element => {
|
export const Inserter = forwardRef(
|
||||||
const [filterValue, setFilterValue] = useState('');
|
({ onInsert, showInserterHelpPanel = true }: Props, ref): JSX.Element => {
|
||||||
const [hoveredItem, setHoveredItem] = useState(null);
|
const [filterValue, setFilterValue] = useState('');
|
||||||
|
const [hoveredItem, setHoveredItem] = useState(null);
|
||||||
|
|
||||||
const { steps, type } = useSelect(
|
const { steps, type } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
steps: select(storeName).getSteps(),
|
steps: select(storeName).getSteps(),
|
||||||
type: select(storeName).getInserterPopover().type,
|
type: select(storeName).getInserterPopover().type,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const groups: Group[] = useMemo(
|
const groups: Group[] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
type === 'triggers'
|
type === 'triggers'
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
type: 'triggers',
|
type: 'triggers',
|
||||||
title: undefined,
|
title: undefined,
|
||||||
// translators: Label for a list of automation steps of type trigger
|
// translators: Label for a list of automation steps of type trigger
|
||||||
label: _x('Triggers', 'automation steps', 'mailpoet'),
|
label: _x('Triggers', 'automation steps', 'mailpoet'),
|
||||||
items: steps.filter(({ group }) => group === 'triggers'),
|
items: steps.filter(({ group }) => group === 'triggers'),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
type: 'actions',
|
type: 'actions',
|
||||||
// translators: Label for a list of automation steps of type action
|
// translators: Label for a list of automation steps of type action
|
||||||
title: _x('Actions', 'automation steps', 'mailpoet'),
|
title: _x('Actions', 'automation steps', 'mailpoet'),
|
||||||
// translators: Label for a list of automation steps of type action
|
// translators: Label for a list of automation steps of type action
|
||||||
label: _x('Actions', 'automation steps', 'mailpoet'),
|
label: _x('Actions', 'automation steps', 'mailpoet'),
|
||||||
items: steps.filter(({ group }) => group === 'actions'),
|
items: steps.filter(({ group }) => group === 'actions'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'logical',
|
type: 'logical',
|
||||||
// translators: Label for a list of logical automation steps (if/else, etc.)
|
// translators: Label for a list of logical automation steps (if/else, etc.)
|
||||||
title: _x('Logical', 'automation steps', 'mailpoet'),
|
title: _x('Logical', 'automation steps', 'mailpoet'),
|
||||||
// translators: Label for a list of logical automation steps (if/else, etc.)
|
// translators: Label for a list of logical automation steps (if/else, etc.)
|
||||||
label: _x('Logical', 'automation steps', 'mailpoet'),
|
label: _x('Logical', 'automation steps', 'mailpoet'),
|
||||||
items: steps.filter(({ group }) => group === 'logical'),
|
items: steps.filter(({ group }) => group === 'logical'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[steps, type],
|
[steps, type],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onHover = useCallback(
|
const onHover = useCallback(
|
||||||
(item) => {
|
(item) => {
|
||||||
setHoveredItem(item);
|
setHoveredItem(item);
|
||||||
},
|
},
|
||||||
[setHoveredItem],
|
[setHoveredItem],
|
||||||
);
|
);
|
||||||
|
|
||||||
const searchRef = useRef<HTMLInputElement>();
|
const searchRef = useRef<HTMLInputElement>();
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
focusSearch: () => {
|
focusSearch: () => {
|
||||||
searchRef.current?.focus();
|
searchRef.current?.focus();
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const filteredGroups = useMemo(
|
const filteredGroups = useMemo(
|
||||||
() =>
|
() =>
|
||||||
groups.map((group) => ({
|
groups.map((group) => ({
|
||||||
...group,
|
...group,
|
||||||
items: filterItems(filterValue, group.items),
|
items: filterItems(filterValue, group.items),
|
||||||
})),
|
})),
|
||||||
[filterValue, groups],
|
[filterValue, groups],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="block-editor-inserter__menu">
|
<div className="block-editor-inserter__menu">
|
||||||
<div className="block-editor-inserter__main-area">
|
<div className="block-editor-inserter__main-area">
|
||||||
<div className="block-editor-inserter__content">
|
<div className="block-editor-inserter__content">
|
||||||
<SearchControl
|
<SearchControl
|
||||||
className="block-editor-inserter__search"
|
className="block-editor-inserter__search"
|
||||||
onChange={(value: string) => {
|
onChange={(value: string) => {
|
||||||
if (hoveredItem) setHoveredItem(null);
|
if (hoveredItem) setHoveredItem(null);
|
||||||
setFilterValue(value);
|
setFilterValue(value);
|
||||||
}}
|
}}
|
||||||
value={filterValue}
|
value={filterValue}
|
||||||
label={__('Search for automation steps', 'mailpoet')}
|
label={__('Search for automation steps', 'mailpoet')}
|
||||||
placeholder={__('Search', 'mailpoet')}
|
placeholder={__('Search', 'mailpoet')}
|
||||||
ref={searchRef}
|
ref={searchRef}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="block-editor-inserter__block-list">
|
<div className="block-editor-inserter__block-list">
|
||||||
<InserterListbox>
|
<InserterListbox>
|
||||||
{filteredGroups.map(
|
{filteredGroups.map(
|
||||||
(group) =>
|
(group) =>
|
||||||
group.items.length > 0 && (
|
group.items.length > 0 && (
|
||||||
<Fragment key={group.type}>
|
<Fragment key={group.type}>
|
||||||
{group.title && (
|
{group.title && (
|
||||||
<div className="block-editor-inserter__panel-header">
|
<div className="block-editor-inserter__panel-header">
|
||||||
<h2 className="block-editor-inserter__panel-title">
|
<h2 className="block-editor-inserter__panel-title">
|
||||||
<div>{group.title}</div>
|
<div>{group.title}</div>
|
||||||
</h2>
|
</h2>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="block-editor-inserter__panel-content">
|
||||||
|
<StepList
|
||||||
|
items={group.items}
|
||||||
|
onHover={onHover}
|
||||||
|
onSelect={(item: Item) => onInsert(item)}
|
||||||
|
label={group.label}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</Fragment>
|
||||||
<div className="block-editor-inserter__panel-content">
|
),
|
||||||
<StepList
|
)}
|
||||||
items={group.items}
|
|
||||||
onHover={onHover}
|
|
||||||
onSelect={(item: Item) => onInsert(item)}
|
|
||||||
label={group.label}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
|
|
||||||
{filteredGroups.reduce(
|
{filteredGroups.reduce(
|
||||||
(sum, { items }) => sum + items.length,
|
(sum, { items }) => sum + items.length,
|
||||||
0,
|
0,
|
||||||
) === 0 && (
|
) === 0 && (
|
||||||
<div className="block-editor-inserter__no-results">
|
<div className="block-editor-inserter__no-results">
|
||||||
<Icon
|
<Icon
|
||||||
className="block-editor-inserter__no-results-icon"
|
className="block-editor-inserter__no-results-icon"
|
||||||
icon={blockDefault}
|
icon={blockDefault}
|
||||||
/>
|
/>
|
||||||
<p>{__('No results found.', 'mailpoet')}</p>
|
<p>{__('No results found.', 'mailpoet')}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</InserterListbox>
|
</InserterListbox>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{showInserterHelpPanel && hoveredItem && (
|
||||||
|
<StepInfoPanel item={hoveredItem} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{hoveredItem && <StepInfoPanel item={hoveredItem} />}
|
);
|
||||||
</div>
|
},
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
store as keyboardShortcutsStore,
|
store as keyboardShortcutsStore,
|
||||||
} from '@wordpress/keyboard-shortcuts';
|
} from '@wordpress/keyboard-shortcuts';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||||
|
|
||||||
// See:
|
// See:
|
||||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/keyboard-shortcuts/index.js
|
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/keyboard-shortcuts/index.js
|
||||||
@ -55,7 +55,7 @@ export function KeyboardShortcuts(): null {
|
|||||||
} else {
|
} else {
|
||||||
const sidebarToOpen = selectedStep()
|
const sidebarToOpen = selectedStep()
|
||||||
? stepSidebarKey
|
? stepSidebarKey
|
||||||
: workflowSidebarKey;
|
: automationSidebarKey;
|
||||||
openSidebar(sidebarToOpen);
|
openSidebar(sidebarToOpen);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@ import { Button, Modal } from '@wordpress/components';
|
|||||||
import { __, sprintf } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { dispatch, useSelect } from '@wordpress/data';
|
import { dispatch, useSelect } from '@wordpress/data';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
|
|
||||||
type DeactivateImmediatelyModalProps = {
|
type DeactivateImmediatelyModalProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -15,7 +15,7 @@ export function DeactivateImmediatelyModal({
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="mailpoet-automatoin-deactivate-modal"
|
className="mailpoet-automatoin-deactivate-modal"
|
||||||
title={__('Stop automatoin for all subscribers?', 'mailpoet')}
|
title={__('Stop automation for all subscribers?', 'mailpoet')}
|
||||||
onRequestClose={onClose}
|
onRequestClose={onClose}
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
@ -49,20 +49,20 @@ type DeactivateModalProps = {
|
|||||||
export function DeactivateModal({
|
export function DeactivateModal({
|
||||||
onClose,
|
onClose,
|
||||||
}: DeactivateModalProps): JSX.Element {
|
}: DeactivateModalProps): JSX.Element {
|
||||||
const { workflowName } = useSelect(
|
const { automationName } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowName: select(storeName).getWorkflowData().name,
|
automationName: select(storeName).getAutomationData().name,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const [selected, setSelected] = useState<
|
const [selected, setSelected] = useState<
|
||||||
WorkflowStatus.DRAFT | WorkflowStatus.DEACTIVATING
|
AutomationStatus.DRAFT | AutomationStatus.DEACTIVATING
|
||||||
>(WorkflowStatus.DEACTIVATING);
|
>(AutomationStatus.DEACTIVATING);
|
||||||
const [isBusy, setIsBusy] = useState<boolean>(false);
|
const [isBusy, setIsBusy] = useState<boolean>(false);
|
||||||
// translators: %s is the name of the automation.
|
// translators: %s is the name of the automation.
|
||||||
const title = sprintf(
|
const title = sprintf(
|
||||||
__('Deactivate the "%s" automation?', 'mailpoet'),
|
__('Deactivate the "%s" automation?', 'mailpoet'),
|
||||||
workflowName,
|
automationName,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -79,7 +79,7 @@ export function DeactivateModal({
|
|||||||
<li>
|
<li>
|
||||||
<label
|
<label
|
||||||
className={
|
className={
|
||||||
selected === WorkflowStatus.DEACTIVATING
|
selected === AutomationStatus.DEACTIVATING
|
||||||
? 'mailpoet-automation-option active'
|
? 'mailpoet-automation-option active'
|
||||||
: 'mailpoet-automation-option'
|
: 'mailpoet-automation-option'
|
||||||
}
|
}
|
||||||
@ -89,8 +89,8 @@ export function DeactivateModal({
|
|||||||
type="radio"
|
type="radio"
|
||||||
disabled={isBusy}
|
disabled={isBusy}
|
||||||
name="deactivation-method"
|
name="deactivation-method"
|
||||||
checked={selected === WorkflowStatus.DEACTIVATING}
|
checked={selected === AutomationStatus.DEACTIVATING}
|
||||||
onChange={() => setSelected(WorkflowStatus.DEACTIVATING)}
|
onChange={() => setSelected(AutomationStatus.DEACTIVATING)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
@ -107,7 +107,7 @@ export function DeactivateModal({
|
|||||||
<li>
|
<li>
|
||||||
<label
|
<label
|
||||||
className={
|
className={
|
||||||
selected === WorkflowStatus.DRAFT
|
selected === AutomationStatus.DRAFT
|
||||||
? 'mailpoet-automation-option active'
|
? 'mailpoet-automation-option active'
|
||||||
: 'mailpoet-automation-option'
|
: 'mailpoet-automation-option'
|
||||||
}
|
}
|
||||||
@ -117,8 +117,8 @@ export function DeactivateModal({
|
|||||||
type="radio"
|
type="radio"
|
||||||
disabled={isBusy}
|
disabled={isBusy}
|
||||||
name="deactivation-method"
|
name="deactivation-method"
|
||||||
checked={selected === WorkflowStatus.DRAFT}
|
checked={selected === AutomationStatus.DRAFT}
|
||||||
onChange={() => setSelected(WorkflowStatus.DRAFT)}
|
onChange={() => setSelected(AutomationStatus.DRAFT)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
@ -140,7 +140,7 @@ export function DeactivateModal({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsBusy(true);
|
setIsBusy(true);
|
||||||
dispatch(storeName).deactivate(
|
dispatch(storeName).deactivate(
|
||||||
selected !== WorkflowStatus.DEACTIVATING,
|
selected !== AutomationStatus.DEACTIVATING,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -4,7 +4,7 @@ import { Button, Spinner } from '@wordpress/components';
|
|||||||
import { closeSmall } from '@wordpress/icons';
|
import { closeSmall } from '@wordpress/icons';
|
||||||
import { __, sprintf } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { storeName } from '../../store';
|
import { storeName } from '../../store';
|
||||||
import { WorkflowStatus } from '../../../listing/workflow';
|
import { AutomationStatus } from '../../../listing/automation';
|
||||||
import { MailPoet } from '../../../../mailpoet';
|
import { MailPoet } from '../../../../mailpoet';
|
||||||
|
|
||||||
function PreStep({ onClose }): JSX.Element {
|
function PreStep({ onClose }): JSX.Element {
|
||||||
@ -58,9 +58,9 @@ function PreStep({ onClose }): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function PostStep({ onClose }): JSX.Element {
|
function PostStep({ onClose }): JSX.Element {
|
||||||
const { workflow } = useSelect(
|
const { automation } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -81,7 +81,7 @@ function PostStep({ onClose }): JSX.Element {
|
|||||||
|
|
||||||
<div className="mailpoet-automation-activate-panel__body">
|
<div className="mailpoet-automation-activate-panel__body">
|
||||||
<div className="mailpoet-automation-activate-panel__section">
|
<div className="mailpoet-automation-activate-panel__section">
|
||||||
{sprintf(__('"%s" is now live.', 'mailpoet'), workflow.name)}
|
{sprintf(__('"%s" is now live.', 'mailpoet'), automation.name)}
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<strong>{__('What’s next?', 'mailpoet')}</strong>
|
<strong>{__('What’s next?', 'mailpoet')}</strong>
|
||||||
@ -100,29 +100,31 @@ function PostStep({ onClose }): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ActivatePanel({ onClose }): JSX.Element {
|
export function ActivatePanel(): JSX.Element {
|
||||||
const { workflow, errors } = useSelect(
|
const { automation, errors } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
errors: select(storeName).getErrors(),
|
errors: select(storeName).getErrors(),
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { closeActivationPanel } = useDispatch(storeName);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (errors) {
|
if (errors) {
|
||||||
onClose();
|
closeActivationPanel();
|
||||||
}
|
}
|
||||||
}, [errors, onClose]);
|
}, [errors, closeActivationPanel]);
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const isActive = workflow.status === WorkflowStatus.ACTIVE;
|
const isActive = automation.status === AutomationStatus.ACTIVE;
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-automation-activate-panel">
|
<div className="mailpoet-automation-activate-panel">
|
||||||
{isActive && <PostStep onClose={onClose} />}
|
{isActive && <PostStep onClose={closeActivationPanel} />}
|
||||||
{!isActive && <PreStep onClose={onClose} />}
|
{!isActive && <PreStep onClose={closeActivationPanel} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ import { __ } from '@wordpress/i18n';
|
|||||||
import { storeName } from '../../../store';
|
import { storeName } from '../../../store';
|
||||||
import { TrashButton } from '../../actions/trash-button';
|
import { TrashButton } from '../../actions/trash-button';
|
||||||
|
|
||||||
export function WorkflowSidebar(): JSX.Element {
|
export function AutomationSidebar(): JSX.Element {
|
||||||
const { workflowData } = useSelect(
|
const { automationData } = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
workflowData: select(storeName).getWorkflowData(),
|
automationData: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -22,30 +22,30 @@ export function WorkflowSidebar(): JSX.Element {
|
|||||||
<PanelBody title={__('Automation details', 'mailpoet')} initialOpen>
|
<PanelBody title={__('Automation details', 'mailpoet')} initialOpen>
|
||||||
<PanelRow>
|
<PanelRow>
|
||||||
<strong>Date added</strong>{' '}
|
<strong>Date added</strong>{' '}
|
||||||
{new Date(Date.parse(workflowData.created_at)).toLocaleDateString(
|
{new Date(Date.parse(automationData.created_at)).toLocaleDateString(
|
||||||
undefined,
|
undefined,
|
||||||
dateOptions,
|
dateOptions,
|
||||||
)}
|
)}
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow>
|
<PanelRow>
|
||||||
<strong>Activated</strong>{' '}
|
<strong>Activated</strong>{' '}
|
||||||
{workflowData.status === 'active' &&
|
{automationData.status === 'active' &&
|
||||||
new Date(Date.parse(workflowData.updated_at)).toLocaleDateString(
|
new Date(Date.parse(automationData.updated_at)).toLocaleDateString(
|
||||||
undefined,
|
undefined,
|
||||||
dateOptions,
|
dateOptions,
|
||||||
)}
|
)}
|
||||||
{workflowData.status !== 'active' &&
|
{automationData.status !== 'active' &&
|
||||||
workflowData.activated_at &&
|
automationData.activated_at &&
|
||||||
new Date(Date.parse(workflowData.activated_at)).toLocaleDateString(
|
new Date(Date.parse(automationData.activated_at)).toLocaleDateString(
|
||||||
undefined,
|
undefined,
|
||||||
dateOptions,
|
dateOptions,
|
||||||
)}
|
)}
|
||||||
{workflowData.status !== 'active' && !workflowData.activated_at && (
|
{automationData.status !== 'active' && !automationData.activated_at && (
|
||||||
<span className="mailpoet-deactive">Not activated yet.</span>
|
<span className="mailpoet-deactive">Not activated yet.</span>
|
||||||
)}
|
)}
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow>
|
<PanelRow>
|
||||||
<strong>Author</strong> {workflowData.author.name}
|
<strong>Author</strong> {automationData.author.name}
|
||||||
</PanelRow>
|
</PanelRow>
|
||||||
<PanelRow>
|
<PanelRow>
|
||||||
<TrashButton />
|
<TrashButton />
|
@ -1,7 +1,7 @@
|
|||||||
import { Button } from '@wordpress/components';
|
import { Button } from '@wordpress/components';
|
||||||
import { useDispatch } from '@wordpress/data';
|
import { useDispatch } from '@wordpress/data';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||||
|
|
||||||
// See:
|
// See:
|
||||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/sidebar/settings-header/index.js
|
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/sidebar/settings-header/index.js
|
||||||
@ -13,11 +13,11 @@ type Props = {
|
|||||||
|
|
||||||
export function Header({ sidebarKey }: Props): JSX.Element {
|
export function Header({ sidebarKey }: Props): JSX.Element {
|
||||||
const { openSidebar } = useDispatch(storeName);
|
const { openSidebar } = useDispatch(storeName);
|
||||||
const openWorkflowSettings = () => openSidebar(workflowSidebarKey);
|
const openAutomationSettings = () => openSidebar(automationSidebarKey);
|
||||||
const openStepSettings = () => openSidebar(stepSidebarKey);
|
const openStepSettings = () => openSidebar(stepSidebarKey);
|
||||||
|
|
||||||
const [workflowAriaLabel, workflowActiveClass] =
|
const [automationAriaLabel, automationActiveClass] =
|
||||||
sidebarKey === workflowSidebarKey
|
sidebarKey === automationSidebarKey
|
||||||
? [__('Automation (selected)', 'mailpoet'), 'is-active']
|
? [__('Automation (selected)', 'mailpoet'), 'is-active']
|
||||||
: [__('Automation', 'mailpoet'), ''];
|
: [__('Automation', 'mailpoet'), ''];
|
||||||
|
|
||||||
@ -30,9 +30,9 @@ export function Header({ sidebarKey }: Props): JSX.Element {
|
|||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<Button
|
<Button
|
||||||
onClick={openWorkflowSettings}
|
onClick={openAutomationSettings}
|
||||||
className={`edit-site-sidebar__panel-tab ${workflowActiveClass}`}
|
className={`edit-site-sidebar__panel-tab ${automationActiveClass}`}
|
||||||
aria-label={workflowAriaLabel}
|
aria-label={automationAriaLabel}
|
||||||
data-label={__('Automation', 'mailpoet')}
|
data-label={__('Automation', 'mailpoet')}
|
||||||
>
|
>
|
||||||
{__('Automation', 'mailpoet')}
|
{__('Automation', 'mailpoet')}
|
||||||
|
@ -10,8 +10,8 @@ import {
|
|||||||
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
|
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
|
||||||
import { Header } from './header';
|
import { Header } from './header';
|
||||||
import { StepSidebar } from './step';
|
import { StepSidebar } from './step';
|
||||||
import { WorkflowSidebar } from './workflow';
|
import { AutomationSidebar } from './automation';
|
||||||
import { stepSidebarKey, storeName, workflowSidebarKey } from '../../store';
|
import { stepSidebarKey, storeName, automationSidebarKey } from '../../store';
|
||||||
|
|
||||||
// See:
|
// See:
|
||||||
// https://github.com/WordPress/gutenberg/blob/5caeae34b3fb303761e3b9432311b26f4e5ea3a6/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js
|
// https://github.com/WordPress/gutenberg/blob/5caeae34b3fb303761e3b9432311b26f4e5ea3a6/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js
|
||||||
@ -26,7 +26,7 @@ const sidebarActiveByDefault = Platform.select({
|
|||||||
type Props = ComponentProps<typeof ComplementaryArea>;
|
type Props = ComponentProps<typeof ComplementaryArea>;
|
||||||
|
|
||||||
export function Sidebar(props: Props): JSX.Element {
|
export function Sidebar(props: Props): JSX.Element {
|
||||||
const { keyboardShortcut, sidebarKey, showIconLabels, workflowName } =
|
const { keyboardShortcut, sidebarKey, showIconLabels, automationName } =
|
||||||
useSelect(
|
useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
keyboardShortcut: select(
|
keyboardShortcut: select(
|
||||||
@ -36,9 +36,9 @@ export function Sidebar(props: Props): JSX.Element {
|
|||||||
),
|
),
|
||||||
sidebarKey:
|
sidebarKey:
|
||||||
select(interfaceStore).getActiveComplementaryArea(storeName) ??
|
select(interfaceStore).getActiveComplementaryArea(storeName) ??
|
||||||
workflowSidebarKey,
|
automationSidebarKey,
|
||||||
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
||||||
workflowName: select(storeName).getWorkflowData().name,
|
automationName: select(storeName).getAutomationData().name,
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@ -53,14 +53,14 @@ export function Sidebar(props: Props): JSX.Element {
|
|||||||
icon={cog}
|
icon={cog}
|
||||||
className="edit-site-sidebar mailpoet-automation-sidebar"
|
className="edit-site-sidebar mailpoet-automation-sidebar"
|
||||||
panelClassName="edit-site-sidebar"
|
panelClassName="edit-site-sidebar"
|
||||||
smallScreenTitle={workflowName || __('(no title)', 'mailpoet')}
|
smallScreenTitle={automationName || __('(no title)', 'mailpoet')}
|
||||||
scope={storeName}
|
scope={storeName}
|
||||||
toggleShortcut={keyboardShortcut}
|
toggleShortcut={keyboardShortcut}
|
||||||
isActiveByDefault={sidebarActiveByDefault}
|
isActiveByDefault={sidebarActiveByDefault}
|
||||||
showIconLabels={showIconLabels}
|
showIconLabels={showIconLabels}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{sidebarKey === workflowSidebarKey && <WorkflowSidebar />}
|
{sidebarKey === automationSidebarKey && <AutomationSidebar />}
|
||||||
{sidebarKey === stepSidebarKey && <StepSidebar />}
|
{sidebarKey === stepSidebarKey && <StepSidebar />}
|
||||||
</ComplementaryArea>
|
</ComplementaryArea>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
|
|
||||||
export function EmptyWorkflow(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<div className="mailpoet-automation-editor-empty-workflow">
|
|
||||||
{__('No automation data.', 'mailpoet')}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -23,17 +23,17 @@ import { InserterSidebar } from './components/inserter-sidebar';
|
|||||||
import { KeyboardShortcuts } from './components/keyboard-shortcuts';
|
import { KeyboardShortcuts } from './components/keyboard-shortcuts';
|
||||||
import { EditorNotices } from './components/notices';
|
import { EditorNotices } from './components/notices';
|
||||||
import { Sidebar } from './components/sidebar';
|
import { Sidebar } from './components/sidebar';
|
||||||
import { Workflow } from './components/workflow';
|
import { Automation } from './components/automation';
|
||||||
import { createStore, storeName } from './store';
|
import { createStore, storeName } from './store';
|
||||||
import { initializeApi } from '../api';
|
import { initializeApi } from '../api';
|
||||||
import { initialize as initializeCoreIntegration } from '../integrations/core';
|
import { initialize as initializeCoreIntegration } from '../integrations/core';
|
||||||
import { initialize as initializeMailPoetIntegration } from '../integrations/mailpoet';
|
import { initialize as initializeMailPoetIntegration } from '../integrations/mailpoet';
|
||||||
import { MailPoet } from '../../mailpoet';
|
import { MailPoet } from '../../mailpoet';
|
||||||
import { LISTING_NOTICE_PARAMETERS } from '../listing/workflow-listing-notices';
|
import { LISTING_NOTICE_PARAMETERS } from '../listing/automation-listing-notices';
|
||||||
import { registerApiErrorHandler } from './api-error-handler';
|
import { registerApiErrorHandler } from './api-error-handler';
|
||||||
import { ActivatePanel } from './components/panel/activate-panel';
|
import { ActivatePanel } from './components/panel/activate-panel';
|
||||||
import { registerTranslations } from '../i18n';
|
import { registerTranslations } from '../i18n';
|
||||||
import { WorkflowStatus } from '../listing/workflow';
|
import { AutomationStatus } from '../listing/automation';
|
||||||
|
|
||||||
// See:
|
// See:
|
||||||
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/layout/index.js
|
// https://github.com/WordPress/gutenberg/blob/9601a33e30ba41bac98579c8d822af63dd961488/packages/edit-post/src/components/layout/index.js
|
||||||
@ -43,27 +43,27 @@ import { WorkflowStatus } from '../listing/workflow';
|
|||||||
const showInserterSidebar = false;
|
const showInserterSidebar = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show temporary message that active workflows cant be updated
|
* Show temporary message that active automations cant be updated
|
||||||
*
|
*
|
||||||
* see MAILPOET-4744
|
* see MAILPOET-4744
|
||||||
*/
|
*/
|
||||||
function updatingActiveWorkflowNotPossible() {
|
function updatingActiveAutomationNotPossible() {
|
||||||
const workflow = globalSelect(storeName).getWorkflowData();
|
const automation = globalSelect(storeName).getAutomationData();
|
||||||
if (
|
if (
|
||||||
![WorkflowStatus.ACTIVE, WorkflowStatus.DEACTIVATING].includes(
|
![AutomationStatus.ACTIVE, AutomationStatus.DEACTIVATING].includes(
|
||||||
workflow.status,
|
automation.status,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (workflow.stats.totals.in_progress === 0) {
|
if (automation.stats.totals.in_progress === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
||||||
void createNotice(
|
void createNotice(
|
||||||
'success',
|
'success',
|
||||||
__(
|
__(
|
||||||
'Editing an active workflow is temporarily unavailable. We are working on introducing this functionality.',
|
'Editing an active automation is temporarily unavailable. We are working on introducing this functionality.',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
@ -72,31 +72,53 @@ function updatingActiveWorkflowNotPossible() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onUnload(event) {
|
||||||
|
if (!globalSelect(storeName).getAutomationSaved()) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
event.returnValue = __(
|
||||||
|
'There are unsaved changes that will be lost. Do you want to continue?',
|
||||||
|
'mailpoet',
|
||||||
|
);
|
||||||
|
return event.returnValue;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function useConfirmUnsaved() {
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('beforeunload', onUnload);
|
||||||
|
return () => window.removeEventListener('beforeunload', onUnload);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
function Editor(): JSX.Element {
|
function Editor(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
isFullscreenActive,
|
isFullscreenActive,
|
||||||
isInserterOpened,
|
isInserterOpened,
|
||||||
|
isActivationPanelOpened,
|
||||||
isSidebarOpened,
|
isSidebarOpened,
|
||||||
showIconLabels,
|
showIconLabels,
|
||||||
workflow,
|
automation,
|
||||||
} = useSelect(
|
} = useSelect(
|
||||||
(select) => ({
|
(select) => ({
|
||||||
isFullscreenActive: select(storeName).isFeatureActive('fullscreenMode'),
|
isFullscreenActive: select(storeName).isFeatureActive('fullscreenMode'),
|
||||||
isInserterOpened: select(storeName).isInserterSidebarOpened(),
|
isInserterOpened: select(storeName).isInserterSidebarOpened(),
|
||||||
isSidebarOpened: select(storeName).isSidebarOpened(),
|
isSidebarOpened: select(storeName).isSidebarOpened(),
|
||||||
|
isActivationPanelOpened: select(storeName).isActivationPanelOpened(),
|
||||||
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
showIconLabels: select(storeName).isFeatureActive('showIconLabels'),
|
||||||
workflow: select(storeName).getWorkflowData(),
|
automation: select(storeName).getAutomationData(),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const [showActivatePanel, setShowActivatePanel] = useState(false);
|
|
||||||
const [isBooting, setIsBooting] = useState(true);
|
const [isBooting, setIsBooting] = useState(true);
|
||||||
|
|
||||||
|
useConfirmUnsaved();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isBooting) {
|
if (!isBooting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updatingActiveWorkflowNotPossible();
|
updatingActiveAutomationNotPossible();
|
||||||
setIsBooting(false);
|
setIsBooting(false);
|
||||||
}, [isBooting]);
|
}, [isBooting]);
|
||||||
const className = classnames('interface-interface-skeleton', {
|
const className = classnames('interface-interface-skeleton', {
|
||||||
@ -104,17 +126,13 @@ function Editor(): JSX.Element {
|
|||||||
'show-icon-labels': showIconLabels,
|
'show-icon-labels': showIconLabels,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (workflow.status === 'trash') {
|
if (automation.status === 'trash') {
|
||||||
window.location.href = addQueryArgs(MailPoet.urls.automationListing, {
|
window.location.href = addQueryArgs(MailPoet.urls.automationListing, {
|
||||||
[LISTING_NOTICE_PARAMETERS.workflowHadBeenDeleted]: workflow.id,
|
[LISTING_NOTICE_PARAMETERS.automationHadBeenDeleted]: automation.id,
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleActivatePanel = () => {
|
|
||||||
setShowActivatePanel(!showActivatePanel);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShortcutProvider>
|
<ShortcutProvider>
|
||||||
<SlotFillProvider>
|
<SlotFillProvider>
|
||||||
@ -135,16 +153,11 @@ function Editor(): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
header={
|
header={<Header showInserterToggle={showInserterSidebar} />}
|
||||||
<Header
|
|
||||||
showInserterToggle={showInserterSidebar}
|
|
||||||
toggleActivatePanel={toggleActivatePanel}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
content={
|
content={
|
||||||
<>
|
<>
|
||||||
<EditorNotices />
|
<EditorNotices />
|
||||||
<Workflow />
|
<Automation />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
sidebar={<ComplementaryArea.Slot scope={storeName} />}
|
sidebar={<ComplementaryArea.Slot scope={storeName} />}
|
||||||
@ -152,7 +165,7 @@ function Editor(): JSX.Element {
|
|||||||
showInserterSidebar && isInserterOpened ? <InserterSidebar /> : null
|
showInserterSidebar && isInserterOpened ? <InserterSidebar /> : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{showActivatePanel && <ActivatePanel onClose={toggleActivatePanel} />}
|
{isActivationPanelOpened && <ActivatePanel />}
|
||||||
<Popover.Slot />
|
<Popover.Slot />
|
||||||
</SlotFillProvider>
|
</SlotFillProvider>
|
||||||
</ShortcutProvider>
|
</ShortcutProvider>
|
||||||
|
@ -7,14 +7,42 @@ import { store as preferencesStore } from '@wordpress/preferences';
|
|||||||
import { addQueryArgs } from '@wordpress/url';
|
import { addQueryArgs } from '@wordpress/url';
|
||||||
import { storeName } from './constants';
|
import { storeName } from './constants';
|
||||||
import { Feature, State } from './types';
|
import { Feature, State } from './types';
|
||||||
import { LISTING_NOTICE_PARAMETERS } from '../../listing/workflow-listing-notices';
|
import { LISTING_NOTICE_PARAMETERS } from '../../listing/automation-listing-notices';
|
||||||
import { MailPoet } from '../../../mailpoet';
|
import { MailPoet } from '../../../mailpoet';
|
||||||
import { WorkflowStatus } from '../../listing/workflow';
|
import { AutomationStatus } from '../../listing/automation';
|
||||||
|
|
||||||
export const openSidebar =
|
const trackErrors = (errors) => {
|
||||||
(key) =>
|
if (!errors?.steps) {
|
||||||
({ registry }) =>
|
return;
|
||||||
|
}
|
||||||
|
const payload = Object.keys(errors.steps as object).map((stepId) => {
|
||||||
|
const error = errors.steps[stepId];
|
||||||
|
const stepKey = select(storeName).getStepById(stepId)?.key;
|
||||||
|
const fields = Object.keys(error.fields as object)
|
||||||
|
.map((field) => `${stepKey}/${field}`)
|
||||||
|
.reduce((prev, next) => prev.concat(next));
|
||||||
|
return fields;
|
||||||
|
});
|
||||||
|
|
||||||
|
MailPoet.trackEvent('Automations > Automation validation error', {
|
||||||
|
errors: payload,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const openActivationPanel = () => ({
|
||||||
|
type: 'SET_ACTIVATION_PANEL_VISIBILITY',
|
||||||
|
value: true,
|
||||||
|
});
|
||||||
|
export const closeActivationPanel = () => ({
|
||||||
|
type: 'SET_ACTIVATION_PANEL_VISIBILITY',
|
||||||
|
value: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const openSidebar = (key) => {
|
||||||
|
dispatch(storeName).closeActivationPanel();
|
||||||
|
return ({ registry }) =>
|
||||||
registry.dispatch(interfaceStore).enableComplementaryArea(storeName, key);
|
registry.dispatch(interfaceStore).enableComplementaryArea(storeName, key);
|
||||||
|
};
|
||||||
|
|
||||||
export const closeSidebar =
|
export const closeSidebar =
|
||||||
() =>
|
() =>
|
||||||
@ -46,23 +74,23 @@ export function selectStep(value) {
|
|||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setWorkflowName(name) {
|
export function setAutomationName(name) {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
return {
|
return {
|
||||||
type: 'UPDATE_WORKFLOW',
|
type: 'UPDATE_AUTOMATION',
|
||||||
workflow: {
|
automation: {
|
||||||
...workflow,
|
...automation,
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* save() {
|
export function* save() {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: { ...workflow },
|
data: { ...automation },
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
||||||
@ -78,23 +106,23 @@ export function* save() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'SAVE',
|
type: 'SAVE',
|
||||||
workflow: data?.data ?? workflow,
|
automation: data?.data ?? automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* activate() {
|
export function* activate() {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
...workflow,
|
...automation,
|
||||||
status: WorkflowStatus.ACTIVE,
|
status: AutomationStatus.ACTIVE,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
||||||
if (data?.data.status === WorkflowStatus.ACTIVE) {
|
if (data?.data.status === AutomationStatus.ACTIVE) {
|
||||||
void createNotice(
|
void createNotice(
|
||||||
'success',
|
'success',
|
||||||
__('Well done! Automation is now activated!', 'mailpoet'),
|
__('Well done! Automation is now activated!', 'mailpoet'),
|
||||||
@ -102,29 +130,33 @@ export function* activate() {
|
|||||||
type: 'snackbar',
|
type: 'snackbar',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
MailPoet.trackEvent('Automations > Automation activated');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'ACTIVATE',
|
type: 'ACTIVATE',
|
||||||
workflow: data?.data ?? workflow,
|
automation: data?.data ?? automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* deactivate(deactivateWorkflowRuns = true) {
|
export function* deactivate(deactivateAutomationRuns = true) {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
...workflow,
|
...automation,
|
||||||
status: deactivateWorkflowRuns
|
status: deactivateAutomationRuns
|
||||||
? WorkflowStatus.DRAFT
|
? AutomationStatus.DRAFT
|
||||||
: WorkflowStatus.DEACTIVATING,
|
: AutomationStatus.DEACTIVATING,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
const { createNotice } = dispatch(noticesStore as StoreDescriptor);
|
||||||
if (deactivateWorkflowRuns && data?.data.status === WorkflowStatus.DRAFT) {
|
if (
|
||||||
|
deactivateAutomationRuns &&
|
||||||
|
data?.data.status === AutomationStatus.DRAFT
|
||||||
|
) {
|
||||||
void createNotice(
|
void createNotice(
|
||||||
'success',
|
'success',
|
||||||
__('Automation is now deactivated!', 'mailpoet'),
|
__('Automation is now deactivated!', 'mailpoet'),
|
||||||
@ -132,10 +164,14 @@ export function* deactivate(deactivateWorkflowRuns = true) {
|
|||||||
type: 'snackbar',
|
type: 'snackbar',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
MailPoet.trackEvent('Automations > Automation deactivated', {
|
||||||
|
type: 'immediate',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!deactivateWorkflowRuns &&
|
!deactivateAutomationRuns &&
|
||||||
data?.data.status === WorkflowStatus.DEACTIVATING
|
data?.data.status === AutomationStatus.DEACTIVATING
|
||||||
) {
|
) {
|
||||||
void createNotice(
|
void createNotice(
|
||||||
'success',
|
'success',
|
||||||
@ -147,36 +183,39 @@ export function* deactivate(deactivateWorkflowRuns = true) {
|
|||||||
type: 'snackbar',
|
type: 'snackbar',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
MailPoet.trackEvent('Automations > Automation deactivated', {
|
||||||
|
type: 'continuous',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'DEACTIVATE',
|
type: 'DEACTIVATE',
|
||||||
workflow: data?.data ?? workflow,
|
automation: data?.data ?? automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* trash(onTrashed: () => void = undefined) {
|
export function* trash(onTrashed: () => void = undefined) {
|
||||||
const workflow = select(storeName).getWorkflowData();
|
const automation = select(storeName).getAutomationData();
|
||||||
const data = yield apiFetch({
|
const data = yield apiFetch({
|
||||||
path: `/workflows/${workflow.id}`,
|
path: `/automations/${automation.id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
...workflow,
|
...automation,
|
||||||
status: WorkflowStatus.TRASH,
|
status: AutomationStatus.TRASH,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
onTrashed?.();
|
onTrashed?.();
|
||||||
|
|
||||||
if (data?.status === WorkflowStatus.TRASH) {
|
if (data?.status === AutomationStatus.TRASH) {
|
||||||
window.location.href = addQueryArgs(MailPoet.urls.automationListing, {
|
window.location.href = addQueryArgs(MailPoet.urls.automationListing, {
|
||||||
[LISTING_NOTICE_PARAMETERS.workflowDeleted]: workflow.id,
|
[LISTING_NOTICE_PARAMETERS.automationDeleted]: automation.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'TRASH',
|
type: 'TRASH',
|
||||||
workflow: data?.data ?? workflow,
|
automation: data?.data ?? automation,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +236,7 @@ export function updateStepArgs(stepId, name, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function setErrors(errors) {
|
export function setErrors(errors) {
|
||||||
|
trackErrors(errors);
|
||||||
return {
|
return {
|
||||||
type: 'SET_ERRORS',
|
type: 'SET_ERRORS',
|
||||||
errors,
|
errors,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export const storeName = 'mailpoet/automation-editor';
|
export const storeName = 'mailpoet/automation-editor';
|
||||||
|
|
||||||
export const workflowSidebarKey = 'mailpoet/automation-editor/workflow';
|
export const automationSidebarKey = 'mailpoet/automation-editor/automation';
|
||||||
export const stepSidebarKey = 'mailpoet/automation-editor/step';
|
export const stepSidebarKey = 'mailpoet/automation-editor/step';
|
||||||
|
@ -5,12 +5,15 @@ declare let window: AutomationEditorWindow;
|
|||||||
export const getInitialState = (): State => ({
|
export const getInitialState = (): State => ({
|
||||||
context: { ...window.mailpoet_automation_context },
|
context: { ...window.mailpoet_automation_context },
|
||||||
stepTypes: {},
|
stepTypes: {},
|
||||||
workflowData: { ...window.mailpoet_automation_workflow },
|
automationData: { ...window.mailpoet_automation },
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
selectedStep: undefined,
|
selectedStep: undefined,
|
||||||
inserterSidebar: {
|
inserterSidebar: {
|
||||||
isOpened: false,
|
isOpened: false,
|
||||||
},
|
},
|
||||||
|
activationPanel: {
|
||||||
|
isOpened: false,
|
||||||
|
},
|
||||||
inserterPopover: undefined,
|
inserterPopover: undefined,
|
||||||
errors: undefined,
|
errors: undefined,
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,14 @@ import { State } from './types';
|
|||||||
|
|
||||||
export function reducer(state: State, action: Action): State {
|
export function reducer(state: State, action: Action): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case 'SET_ACTIVATION_PANEL_VISIBILITY':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activationPanel: {
|
||||||
|
...state.activationPanel,
|
||||||
|
isOpened: action.value,
|
||||||
|
},
|
||||||
|
};
|
||||||
case 'TOGGLE_INSERTER_SIDEBAR':
|
case 'TOGGLE_INSERTER_SIDEBAR':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -21,35 +29,35 @@ export function reducer(state: State, action: Action): State {
|
|||||||
...state,
|
...state,
|
||||||
selectedStep: action.value,
|
selectedStep: action.value,
|
||||||
};
|
};
|
||||||
case 'UPDATE_WORKFLOW':
|
case 'UPDATE_AUTOMATION':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: false,
|
automationSaved: false,
|
||||||
};
|
};
|
||||||
case 'SAVE':
|
case 'SAVE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
};
|
};
|
||||||
case 'ACTIVATE':
|
case 'ACTIVATE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
};
|
};
|
||||||
case 'DEACTIVATE':
|
case 'DEACTIVATE':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
};
|
};
|
||||||
case 'TRASH':
|
case 'TRASH':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: action.workflow,
|
automationData: action.automation,
|
||||||
workflowSaved: true,
|
automationSaved: true,
|
||||||
};
|
};
|
||||||
case 'REGISTER_STEP_TYPE':
|
case 'REGISTER_STEP_TYPE':
|
||||||
return {
|
return {
|
||||||
@ -60,7 +68,7 @@ export function reducer(state: State, action: Action): State {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
case 'UPDATE_STEP_ARGS': {
|
case 'UPDATE_STEP_ARGS': {
|
||||||
const prevArgs = state.workflowData.steps[action.stepId].args ?? {};
|
const prevArgs = state.automationData.steps[action.stepId].args ?? {};
|
||||||
|
|
||||||
const value =
|
const value =
|
||||||
typeof action.value === 'function'
|
typeof action.value === 'function'
|
||||||
@ -74,7 +82,7 @@ export function reducer(state: State, action: Action): State {
|
|||||||
)
|
)
|
||||||
: { ...prevArgs, [action.name]: value };
|
: { ...prevArgs, [action.name]: value };
|
||||||
|
|
||||||
const step = { ...state.workflowData.steps[action.stepId], args };
|
const step = { ...state.automationData.steps[action.stepId], args };
|
||||||
|
|
||||||
const stepErrors = Object.values(state.errors?.steps ?? {}).filter(
|
const stepErrors = Object.values(state.errors?.steps ?? {}).filter(
|
||||||
({ step_id }) => step_id !== action.stepId,
|
({ step_id }) => step_id !== action.stepId,
|
||||||
@ -82,14 +90,14 @@ export function reducer(state: State, action: Action): State {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
workflowData: {
|
automationData: {
|
||||||
...state.workflowData,
|
...state.automationData,
|
||||||
steps: {
|
steps: {
|
||||||
...state.workflowData.steps,
|
...state.automationData.steps,
|
||||||
[action.stepId]: step,
|
[action.stepId]: step,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
workflowSaved: false,
|
automationSaved: false,
|
||||||
selectedStep: step,
|
selectedStep: step,
|
||||||
errors:
|
errors:
|
||||||
stepErrors.length > 0
|
stepErrors.length > 0
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { dispatch } from '@wordpress/data';
|
import { dispatch } from '@wordpress/data';
|
||||||
|
import { Hooks } from 'wp-js-hooks';
|
||||||
import { storeName } from './constants';
|
import { storeName } from './constants';
|
||||||
import { StepType } from './types';
|
import { StepType } from './types';
|
||||||
|
|
||||||
export const registerStepType = (stepType: StepType): void => {
|
export const registerStepType = (stepType: StepType): void => {
|
||||||
dispatch(storeName).registerStepType(stepType);
|
dispatch(storeName).registerStepType(
|
||||||
|
Hooks.applyFilters('mailpoet.automation.register_step_type', stepType),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import { store as preferencesStore } from '@wordpress/preferences';
|
|||||||
import { storeName } from './constants';
|
import { storeName } from './constants';
|
||||||
import { Context, Errors, Feature, State, StepErrors, StepType } from './types';
|
import { Context, Errors, Feature, State, StepErrors, StepType } from './types';
|
||||||
import { Item } from '../components/inserter/item';
|
import { Item } from '../components/inserter/item';
|
||||||
import { Step, Workflow } from '../components/workflow/types';
|
import { Step, Automation } from '../components/automation/types';
|
||||||
|
|
||||||
export const isFeatureActive = createRegistrySelector(
|
export const isFeatureActive = createRegistrySelector(
|
||||||
(select) =>
|
(select) =>
|
||||||
@ -21,6 +21,10 @@ export function isInserterSidebarOpened(state: State): boolean {
|
|||||||
return state.inserterSidebar.isOpened;
|
return state.inserterSidebar.isOpened;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isActivationPanelOpened(state: State): boolean {
|
||||||
|
return state.activationPanel.isOpened;
|
||||||
|
}
|
||||||
|
|
||||||
export function getContext(state: State): Context {
|
export function getContext(state: State): Context {
|
||||||
return state.context;
|
return state.context;
|
||||||
}
|
}
|
||||||
@ -54,18 +58,22 @@ export function getInserterPopover(
|
|||||||
return state.inserterPopover;
|
return state.inserterPopover;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWorkflowData(state: State): Workflow {
|
export function getAutomationData(state: State): Automation {
|
||||||
return state.workflowData;
|
return state.automationData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWorkflowSaved(state: State): boolean {
|
export function getAutomationSaved(state: State): boolean {
|
||||||
return state.workflowSaved;
|
return state.automationSaved;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSelectedStep(state: State): Step | undefined {
|
export function getSelectedStep(state: State): Step | undefined {
|
||||||
return state.selectedStep;
|
return state.selectedStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getStepById(state: State, id: string): Step | undefined {
|
||||||
|
return state.automationData.steps[id] ?? undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export function getStepType(state: State, key: string): StepType | undefined {
|
export function getStepType(state: State, key: string): StepType | undefined {
|
||||||
return state.stepTypes[key] ?? undefined;
|
return state.stepTypes[key] ?? undefined;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { ComponentType } from 'react';
|
import { ComponentType } from 'react';
|
||||||
import { Step, Workflow } from '../components/workflow/types';
|
import { Step, Automation } from '../components/automation/types';
|
||||||
|
|
||||||
export interface AutomationEditorWindow extends Window {
|
export interface AutomationEditorWindow extends Window {
|
||||||
mailpoet_automation_context: Context;
|
mailpoet_automation_context: Context;
|
||||||
mailpoet_automation_workflow: Workflow;
|
mailpoet_automation: Automation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Context = {
|
export type Context = {
|
||||||
@ -48,12 +48,15 @@ export type Errors = {
|
|||||||
export type State = {
|
export type State = {
|
||||||
context: Context;
|
context: Context;
|
||||||
stepTypes: Record<string, StepType>;
|
stepTypes: Record<string, StepType>;
|
||||||
workflowData: Workflow;
|
automationData: Automation;
|
||||||
workflowSaved: boolean;
|
automationSaved: boolean;
|
||||||
selectedStep: Step | undefined;
|
selectedStep: Step | undefined;
|
||||||
inserterSidebar: {
|
inserterSidebar: {
|
||||||
isOpened: boolean;
|
isOpened: boolean;
|
||||||
};
|
};
|
||||||
|
activationPanel: {
|
||||||
|
isOpened: boolean;
|
||||||
|
};
|
||||||
inserterPopover?: {
|
inserterPopover?: {
|
||||||
anchor: HTMLElement;
|
anchor: HTMLElement;
|
||||||
type: 'steps' | 'triggers';
|
type: 'steps' | 'triggers';
|
||||||
|
@ -19,7 +19,7 @@ export const step: StepType = {
|
|||||||
foreground: '#7F54B3',
|
foreground: '#7F54B3',
|
||||||
background: '#f7edf7',
|
background: '#f7edf7',
|
||||||
description: __(
|
description: __(
|
||||||
'Wait some time before proceeding with the steps below',
|
'Wait some time before proceeding with the steps below.',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
),
|
),
|
||||||
subtitle: (data): string => {
|
subtitle: (data): string => {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user