@strapi/admin 4.0.8 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/admin/src/components/AuthenticatedApp/index.js +25 -3
  2. package/admin/src/components/AuthenticatedApp/utils/api.js +15 -1
  3. package/admin/src/components/GuidedTour/Homepage/components/Step.js +61 -0
  4. package/admin/src/components/GuidedTour/Homepage/components/Stepper.js +58 -0
  5. package/admin/src/components/GuidedTour/Homepage/index.js +63 -0
  6. package/admin/src/components/GuidedTour/Modal/components/Content.js +50 -0
  7. package/admin/src/components/GuidedTour/Modal/components/Modal.js +75 -0
  8. package/admin/src/components/GuidedTour/Modal/components/StepNumberWithPadding.js +24 -0
  9. package/admin/src/components/GuidedTour/Modal/components/Stepper.js +119 -0
  10. package/admin/src/components/GuidedTour/Modal/index.js +90 -0
  11. package/admin/src/components/GuidedTour/Modal/reducer.js +29 -0
  12. package/admin/src/components/GuidedTour/Stepper/StepLine.js +27 -0
  13. package/admin/src/components/GuidedTour/Stepper/StepNumber.js +71 -0
  14. package/admin/src/components/GuidedTour/constants.js +3 -0
  15. package/admin/src/components/GuidedTour/index.js +100 -0
  16. package/admin/src/components/GuidedTour/init.js +36 -0
  17. package/admin/src/components/GuidedTour/layout.js +153 -0
  18. package/admin/src/components/GuidedTour/reducer.js +50 -0
  19. package/admin/src/components/GuidedTour/utils/arePreviousSectionsDone.js +13 -0
  20. package/admin/src/components/GuidedTour/utils/arePreviousStepsDone.js +12 -0
  21. package/admin/src/components/GuidedTour/utils/isGuidedTourCompleted.js +6 -0
  22. package/admin/src/components/GuidedTour/utils/persistStateToLocaleStorage.js +34 -0
  23. package/admin/src/components/Providers/index.js +4 -1
  24. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +14 -1
  25. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +5 -1
  26. package/admin/src/content-manager/pages/App/index.js +15 -2
  27. package/admin/src/pages/Admin/index.js +2 -0
  28. package/admin/src/pages/AuthPage/components/Register/index.js +11 -6
  29. package/admin/src/pages/AuthPage/index.js +15 -1
  30. package/admin/src/pages/HomePage/index.js +8 -2
  31. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +3 -0
  32. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +10 -1
  33. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/components/RoleRow/index.js +2 -2
  34. package/admin/src/translations/ar.json +0 -4
  35. package/admin/src/translations/cs.json +0 -6
  36. package/admin/src/translations/de.json +0 -12
  37. package/admin/src/translations/dk.json +158 -170
  38. package/admin/src/translations/en.json +43 -29
  39. package/admin/src/translations/es.json +15 -25
  40. package/admin/src/translations/fr.json +158 -169
  41. package/admin/src/translations/he.json +0 -10
  42. package/admin/src/translations/hu.json +16 -26
  43. package/admin/src/translations/id.json +0 -12
  44. package/admin/src/translations/it.json +0 -12
  45. package/admin/src/translations/ja.json +16 -28
  46. package/admin/src/translations/ko.json +656 -670
  47. package/admin/src/translations/ms.json +0 -6
  48. package/admin/src/translations/nl.json +16 -30
  49. package/admin/src/translations/no.json +0 -10
  50. package/admin/src/translations/pl.json +0 -12
  51. package/admin/src/translations/pt-BR.json +19 -29
  52. package/admin/src/translations/pt.json +0 -4
  53. package/admin/src/translations/ru.json +0 -12
  54. package/admin/src/translations/sk.json +0 -12
  55. package/admin/src/translations/sv.json +0 -4
  56. package/admin/src/translations/th.json +0 -12
  57. package/admin/src/translations/tr.json +0 -4
  58. package/admin/src/translations/uk.json +0 -6
  59. package/admin/src/translations/vi.json +0 -4
  60. package/admin/src/translations/zh-Hans.json +15 -25
  61. package/admin/src/translations/zh.json +24 -27
  62. package/build/2736.e2b1233b.chunk.js +2 -0
  63. package/build/{460.639962f0.chunk.js.LICENSE.txt → 2736.e2b1233b.chunk.js.LICENSE.txt} +0 -0
  64. package/build/4362.d0c1a04a.chunk.js +1 -0
  65. package/build/4800.18e59c83.chunk.js +1 -0
  66. package/build/{3215.4d042146.chunk.js → 8042.9b85175a.chunk.js} +2 -2
  67. package/build/{3215.4d042146.chunk.js.LICENSE.txt → 8042.9b85175a.chunk.js.LICENSE.txt} +0 -0
  68. package/build/9988.b4229043.chunk.js +2 -0
  69. package/build/{4741.c2bfe032.chunk.js.LICENSE.txt → 9988.b4229043.chunk.js.LICENSE.txt} +0 -0
  70. package/build/Admin-authenticatedApp.3bb032bf.chunk.js +1 -0
  71. package/build/Admin_homePage.e4779166.chunk.js +1 -0
  72. package/build/{Admin_settingsPage.05877e0b.chunk.js → Admin_settingsPage.2d0d2cca.chunk.js} +1 -1
  73. package/build/{api-tokens-create-page.10586e16.chunk.js → api-tokens-create-page.2ffd893a.chunk.js} +1 -1
  74. package/build/{api-tokens-edit-page.f9e3038d.chunk.js → api-tokens-edit-page.a939bd0a.chunk.js} +1 -1
  75. package/build/{api-tokens-list-page.e071058b.chunk.js → api-tokens-list-page.1ccc8615.chunk.js} +1 -1
  76. package/build/ar-json.6a2565af.chunk.js +1 -0
  77. package/build/content-manager.5f6197c8.chunk.js +1 -0
  78. package/build/{content-type-builder-translation-cs-json.2f7e2289.chunk.js → content-type-builder-translation-cs-json.89f7272e.chunk.js} +1 -1
  79. package/build/{content-type-builder-translation-de-json.46017754.chunk.js → content-type-builder-translation-de-json.0205697c.chunk.js} +1 -1
  80. package/build/content-type-builder-translation-dk-json.235ff56e.chunk.js +1 -0
  81. package/build/{content-type-builder-translation-en-json.d70fc3af.chunk.js → content-type-builder-translation-en-json.b3d8e9d4.chunk.js} +1 -1
  82. package/build/{content-type-builder-translation-es-json.20c177ee.chunk.js → content-type-builder-translation-es-json.13b2e2aa.chunk.js} +1 -1
  83. package/build/{content-type-builder-translation-fr-json.8f66eb66.chunk.js → content-type-builder-translation-fr-json.bee621f7.chunk.js} +1 -1
  84. package/build/{content-type-builder-translation-id-json.aab2f426.chunk.js → content-type-builder-translation-id-json.2fbf4f8c.chunk.js} +1 -1
  85. package/build/{content-type-builder-translation-it-json.4c91e895.chunk.js → content-type-builder-translation-it-json.a1afd7a9.chunk.js} +1 -1
  86. package/build/{content-type-builder-translation-ja-json.c239ba90.chunk.js → content-type-builder-translation-ja-json.1459fb88.chunk.js} +1 -1
  87. package/build/{content-type-builder-translation-ko-json.2a5e0769.chunk.js → content-type-builder-translation-ko-json.bc6fb3dc.chunk.js} +1 -1
  88. package/build/{content-type-builder-translation-ms-json.124be88c.chunk.js → content-type-builder-translation-ms-json.048122eb.chunk.js} +1 -1
  89. package/build/{content-type-builder-translation-nl-json.98a240b8.chunk.js → content-type-builder-translation-nl-json.8d59e86b.chunk.js} +1 -1
  90. package/build/{content-type-builder-translation-pl-json.2abc61bd.chunk.js → content-type-builder-translation-pl-json.01dc068c.chunk.js} +1 -1
  91. package/build/{content-type-builder-translation-pt-BR-json.c0415545.chunk.js → content-type-builder-translation-pt-BR-json.d311d056.chunk.js} +1 -1
  92. package/build/{content-type-builder-translation-pt-json.ab3e086d.chunk.js → content-type-builder-translation-pt-json.4893266f.chunk.js} +1 -1
  93. package/build/{content-type-builder-translation-ru-json.1a6779fd.chunk.js → content-type-builder-translation-ru-json.1285874d.chunk.js} +1 -1
  94. package/build/{content-type-builder-translation-sk-json.5c82f020.chunk.js → content-type-builder-translation-sk-json.0064156b.chunk.js} +1 -1
  95. package/build/{content-type-builder-translation-th-json.24ee19eb.chunk.js → content-type-builder-translation-th-json.5f690524.chunk.js} +1 -1
  96. package/build/{content-type-builder-translation-tr-json.30434835.chunk.js → content-type-builder-translation-tr-json.696283a5.chunk.js} +1 -1
  97. package/build/{content-type-builder-translation-uk-json.771662ef.chunk.js → content-type-builder-translation-uk-json.87496bf9.chunk.js} +1 -1
  98. package/build/{content-type-builder-translation-zh-Hans-json.070020ae.chunk.js → content-type-builder-translation-zh-Hans-json.283c640e.chunk.js} +1 -1
  99. package/build/{content-type-builder-translation-zh-json.9708310d.chunk.js → content-type-builder-translation-zh-json.77aa2275.chunk.js} +1 -1
  100. package/build/content-type-builder.f1cef05c.chunk.js +1 -0
  101. package/build/cs-json.8df09876.chunk.js +1 -0
  102. package/build/de-json.6e14f607.chunk.js +1 -0
  103. package/build/dk-json.be388470.chunk.js +1 -0
  104. package/build/en-json.bb614bb0.chunk.js +1 -0
  105. package/build/es-json.61553168.chunk.js +1 -0
  106. package/build/fr-json.d1de8155.chunk.js +1 -0
  107. package/build/he-json.1742494e.chunk.js +1 -0
  108. package/build/hu-json.e667d285.chunk.js +1 -0
  109. package/build/i18n-translation-dk-json.ecf02d28.chunk.js +1 -0
  110. package/build/i18n-translation-en-json.4d823f62.chunk.js +1 -0
  111. package/build/i18n-translation-es-json.7049afa2.chunk.js +1 -0
  112. package/build/i18n-translation-fr-json.c6367bc9.chunk.js +1 -0
  113. package/build/i18n-translation-ko-json.6591fe59.chunk.js +1 -0
  114. package/build/i18n-translation-zh-Hans-json.258b2e1a.chunk.js +1 -0
  115. package/build/id-json.d87ebb20.chunk.js +1 -0
  116. package/build/index.html +1 -1
  117. package/build/it-json.a2880b81.chunk.js +1 -0
  118. package/build/ja-json.46e29f04.chunk.js +1 -0
  119. package/build/ko-json.63d1660a.chunk.js +1 -0
  120. package/build/{main.59b96514.js → main.a5659553.js} +2 -2
  121. package/build/{main.59b96514.js.LICENSE.txt → main.a5659553.js.LICENSE.txt} +0 -0
  122. package/build/ms-json.3a062984.chunk.js +1 -0
  123. package/build/nl-json.30ce02cb.chunk.js +1 -0
  124. package/build/no-json.9af40e9d.chunk.js +1 -0
  125. package/build/pl-json.fd373053.chunk.js +1 -0
  126. package/build/pt-BR-json.30e2d716.chunk.js +1 -0
  127. package/build/pt-json.3aaf9e05.chunk.js +1 -0
  128. package/build/ru-json.78c56e1c.chunk.js +1 -0
  129. package/build/{runtime~main.75c67df1.js → runtime~main.cfbc273d.js} +1 -1
  130. package/build/sk-json.c0bf144c.chunk.js +1 -0
  131. package/build/sv-json.aad187b9.chunk.js +1 -0
  132. package/build/th-json.e2b4a0fb.chunk.js +1 -0
  133. package/build/tr-json.0add11cd.chunk.js +1 -0
  134. package/build/uk-json.eb78e77e.chunk.js +1 -0
  135. package/build/{upload-settings.62631a39.chunk.js → upload-settings.8e7cbc3b.chunk.js} +1 -1
  136. package/build/{upload-translation-de-json.00f90715.chunk.js → upload-translation-de-json.1308dce5.chunk.js} +1 -1
  137. package/build/upload-translation-dk-json.0d4e855f.chunk.js +1 -0
  138. package/build/upload-translation-en-json.c3373c8d.chunk.js +1 -0
  139. package/build/upload-translation-es-json.81b13eac.chunk.js +1 -0
  140. package/build/{upload-translation-fr-json.ccb4ad8b.chunk.js → upload-translation-fr-json.1bec79ec.chunk.js} +1 -1
  141. package/build/upload-translation-he-json.1d28982f.chunk.js +1 -0
  142. package/build/{upload-translation-it-json.c1809a47.chunk.js → upload-translation-it-json.7d4bdc5a.chunk.js} +1 -1
  143. package/build/{upload-translation-ja-json.71aa85eb.chunk.js → upload-translation-ja-json.97fcacd8.chunk.js} +1 -1
  144. package/build/upload-translation-ko-json.90424b11.chunk.js +1 -0
  145. package/build/{upload-translation-ms-json.be669f81.chunk.js → upload-translation-ms-json.081effd5.chunk.js} +1 -1
  146. package/build/{upload-translation-pl-json.67685825.chunk.js → upload-translation-pl-json.2dfe78bb.chunk.js} +1 -1
  147. package/build/{upload-translation-pt-BR-json.f7b1133d.chunk.js → upload-translation-pt-BR-json.65936d7b.chunk.js} +1 -1
  148. package/build/{upload-translation-ru-json.54c031aa.chunk.js → upload-translation-ru-json.2d3b6f69.chunk.js} +1 -1
  149. package/build/{upload-translation-sk-json.f643dfc2.chunk.js → upload-translation-sk-json.f15c7fd6.chunk.js} +1 -1
  150. package/build/upload-translation-th-json.6d3c2370.chunk.js +1 -0
  151. package/build/{upload-translation-uk-json.1a90e73c.chunk.js → upload-translation-uk-json.a6c38449.chunk.js} +1 -1
  152. package/build/{upload-translation-zh-Hans-json.ac1dc0b9.chunk.js → upload-translation-zh-Hans-json.f6b26c45.chunk.js} +1 -1
  153. package/build/{upload-translation-zh-json.164ac601.chunk.js → upload-translation-zh-json.06052336.chunk.js} +1 -1
  154. package/build/users-permissions-translation-dk-json.89d41c4b.chunk.js +1 -0
  155. package/build/vi-json.55a11ac0.chunk.js +1 -0
  156. package/build/zh-Hans-json.55f6475b.chunk.js +1 -0
  157. package/build/zh-json.c3c2b225.chunk.js +1 -0
  158. package/package.json +5 -5
  159. package/build/4362.e3d2d72b.chunk.js +0 -1
  160. package/build/460.639962f0.chunk.js +0 -2
  161. package/build/4741.c2bfe032.chunk.js +0 -2
  162. package/build/Admin-authenticatedApp.f65c428a.chunk.js +0 -1
  163. package/build/Admin_homePage.a20b5e76.chunk.js +0 -1
  164. package/build/ar-json.d79e4709.chunk.js +0 -1
  165. package/build/content-manager.07db1dd9.chunk.js +0 -1
  166. package/build/content-type-builder-translation-dk-json.098bd218.chunk.js +0 -1
  167. package/build/content-type-builder.52f5975c.chunk.js +0 -1
  168. package/build/cs-json.b8ba75b8.chunk.js +0 -1
  169. package/build/de-json.e01bdeae.chunk.js +0 -1
  170. package/build/dk-json.7356ea4b.chunk.js +0 -1
  171. package/build/en-json.3422a59e.chunk.js +0 -1
  172. package/build/es-json.ed9c8bef.chunk.js +0 -1
  173. package/build/fr-json.390bcdeb.chunk.js +0 -1
  174. package/build/he-json.86f9e663.chunk.js +0 -1
  175. package/build/hu-json.a741d263.chunk.js +0 -1
  176. package/build/i18n-translation-dk-json.932d3cc2.chunk.js +0 -1
  177. package/build/i18n-translation-en-json.239b740f.chunk.js +0 -1
  178. package/build/i18n-translation-es-json.347904f3.chunk.js +0 -1
  179. package/build/i18n-translation-fr-json.b52474fc.chunk.js +0 -1
  180. package/build/i18n-translation-ko-json.e88e11ef.chunk.js +0 -1
  181. package/build/i18n-translation-zh-Hans-json.4c17fed0.chunk.js +0 -1
  182. package/build/id-json.0b0c9731.chunk.js +0 -1
  183. package/build/it-json.939916bc.chunk.js +0 -1
  184. package/build/ja-json.52581a2a.chunk.js +0 -1
  185. package/build/ko-json.29633034.chunk.js +0 -1
  186. package/build/ms-json.5e5d12f9.chunk.js +0 -1
  187. package/build/nl-json.ac661b7f.chunk.js +0 -1
  188. package/build/no-json.ff46b126.chunk.js +0 -1
  189. package/build/pl-json.6bff1d54.chunk.js +0 -1
  190. package/build/pt-BR-json.8b3f799d.chunk.js +0 -1
  191. package/build/pt-json.b23d9a79.chunk.js +0 -1
  192. package/build/ru-json.bff93229.chunk.js +0 -1
  193. package/build/sk-json.a40bc2c8.chunk.js +0 -1
  194. package/build/sv-json.8ac61ecf.chunk.js +0 -1
  195. package/build/th-json.6e4502a3.chunk.js +0 -1
  196. package/build/tr-json.eaca955b.chunk.js +0 -1
  197. package/build/uk-json.da2ed14e.chunk.js +0 -1
  198. package/build/upload-translation-dk-json.bc6af8b4.chunk.js +0 -1
  199. package/build/upload-translation-en-json.6b529046.chunk.js +0 -1
  200. package/build/upload-translation-es-json.b53d6641.chunk.js +0 -1
  201. package/build/upload-translation-he-json.5dc34ea8.chunk.js +0 -1
  202. package/build/upload-translation-ko-json.da369eef.chunk.js +0 -1
  203. package/build/upload-translation-th-json.88ee2090.chunk.js +0 -1
  204. package/build/users-permissions-translation-dk-json.3e0295e5.chunk.js +0 -1
  205. package/build/vi-json.e993857a.chunk.js +0 -1
  206. package/build/zh-Hans-json.5843950b.chunk.js +0 -1
  207. package/build/zh-json.2e4c9ef4.chunk.js +0 -1
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { pxToRem } from '@strapi/helper-plugin';
4
+ import { Flex } from '@strapi/design-system/Flex';
5
+ import { Typography } from '@strapi/design-system/Typography';
6
+ import { Icon } from '@strapi/design-system/Icon';
7
+ import Check from '@strapi/icons/Check';
8
+ import { IS_DONE, IS_ACTIVE, IS_NOT_DONE } from '../constants';
9
+
10
+ const StepNumber = ({ type, number }) => {
11
+ if (type === IS_DONE) {
12
+ return (
13
+ <Flex
14
+ background="primary600"
15
+ padding={2}
16
+ borderRadius="50%"
17
+ width={pxToRem(30)}
18
+ height={pxToRem(30)}
19
+ justifyContent="center"
20
+ >
21
+ <Icon as={Check} aria-hidden width={pxToRem(16)} color="neutral0" />
22
+ </Flex>
23
+ );
24
+ }
25
+
26
+ if (type === IS_ACTIVE) {
27
+ return (
28
+ <Flex
29
+ background="primary600"
30
+ padding={2}
31
+ borderRadius="50%"
32
+ width={pxToRem(30)}
33
+ height={pxToRem(30)}
34
+ justifyContent="center"
35
+ >
36
+ <Typography fontWeight="semiBold" textColor="neutral0">
37
+ {number}
38
+ </Typography>
39
+ </Flex>
40
+ );
41
+ }
42
+
43
+ return (
44
+ <Flex
45
+ borderColor="neutral500"
46
+ borderWidth="1px"
47
+ borderStyle="solid"
48
+ padding={2}
49
+ borderRadius="50%"
50
+ width={pxToRem(30)}
51
+ height={pxToRem(30)}
52
+ justifyContent="center"
53
+ >
54
+ <Typography fontWeight="semiBold" textColor="neutral600">
55
+ {number}
56
+ </Typography>
57
+ </Flex>
58
+ );
59
+ };
60
+
61
+ StepNumber.defaultProps = {
62
+ number: undefined,
63
+ type: IS_NOT_DONE,
64
+ };
65
+
66
+ StepNumber.propTypes = {
67
+ number: PropTypes.number,
68
+ type: PropTypes.oneOf([IS_ACTIVE, IS_DONE, IS_NOT_DONE]),
69
+ };
70
+
71
+ export default StepNumber;
@@ -0,0 +1,3 @@
1
+ export const IS_ACTIVE = 'isActive';
2
+ export const IS_DONE = 'isDone';
3
+ export const IS_NOT_DONE = 'isNotDone';
@@ -0,0 +1,100 @@
1
+ import React, { useReducer } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import get from 'lodash/get';
4
+ import { GuidedTourProvider } from '@strapi/helper-plugin';
5
+ import persistStateToLocaleStorage from './utils/persistStateToLocaleStorage';
6
+ import arePreviousSectionsDone from './utils/arePreviousSectionsDone';
7
+ import arePreviousStepsDone from './utils/arePreviousStepsDone';
8
+ import reducer, { initialState } from './reducer';
9
+ import init from './init';
10
+
11
+ const GuidedTour = ({ children }) => {
12
+ const [{ currentStep, guidedTourState, isGuidedTourVisible, isSkipped }, dispatch] = useReducer(
13
+ reducer,
14
+ initialState,
15
+ init
16
+ );
17
+
18
+ const setCurrentStep = step => {
19
+ // if step is null it is intentional, we need to dispatch it
20
+ if (step !== null) {
21
+ const isStepAlreadyDone = get(guidedTourState, step);
22
+ const isStepToShow = arePreviousStepsDone(step, guidedTourState);
23
+
24
+ if (isStepAlreadyDone || isSkipped || !isStepToShow) {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ persistStateToLocaleStorage.addCurrentStep(step);
30
+
31
+ return dispatch({
32
+ type: 'SET_CURRENT_STEP',
33
+ step,
34
+ });
35
+ };
36
+
37
+ const setGuidedTourVisibility = value => {
38
+ dispatch({
39
+ type: 'SET_GUIDED_TOUR_VISIBILITY',
40
+ value,
41
+ });
42
+ };
43
+
44
+ const setStepState = (currentStep, value) => {
45
+ persistStateToLocaleStorage.addCompletedStep(currentStep);
46
+
47
+ dispatch({
48
+ type: 'SET_STEP_STATE',
49
+ currentStep,
50
+ value,
51
+ });
52
+ };
53
+
54
+ const startSection = sectionName => {
55
+ const sectionSteps = guidedTourState[sectionName];
56
+
57
+ if (sectionSteps) {
58
+ const isSectionToShow = arePreviousSectionsDone(sectionName, guidedTourState);
59
+ const firstStep = Object.keys(sectionSteps)[0];
60
+ const isFirstStepDone = sectionSteps[firstStep];
61
+
62
+ if (isSectionToShow && !currentStep && !isFirstStepDone) {
63
+ return setCurrentStep(`${sectionName}.${firstStep}`);
64
+ }
65
+ }
66
+
67
+ return null;
68
+ };
69
+
70
+ const setSkipped = value => {
71
+ persistStateToLocaleStorage.setSkipped(value);
72
+
73
+ dispatch({
74
+ type: 'SET_SKIPPED',
75
+ value,
76
+ });
77
+ };
78
+
79
+ return (
80
+ <GuidedTourProvider
81
+ guidedTourState={guidedTourState}
82
+ currentStep={currentStep}
83
+ setCurrentStep={setCurrentStep}
84
+ setGuidedTourVisibility={setGuidedTourVisibility}
85
+ setSkipped={setSkipped}
86
+ setStepState={setStepState}
87
+ startSection={startSection}
88
+ isGuidedTourVisible={isGuidedTourVisible}
89
+ isSkipped={isSkipped}
90
+ >
91
+ {children}
92
+ </GuidedTourProvider>
93
+ );
94
+ };
95
+
96
+ GuidedTour.propTypes = {
97
+ children: PropTypes.element.isRequired,
98
+ };
99
+
100
+ export default GuidedTour;
@@ -0,0 +1,36 @@
1
+ import set from 'lodash/set';
2
+ import persistStateToLocaleStorage, {
3
+ COMPLETED_STEPS,
4
+ CURRENT_STEP,
5
+ SKIPPED,
6
+ } from './utils/persistStateToLocaleStorage';
7
+
8
+ const init = initialState => {
9
+ const copyInitialState = { ...initialState };
10
+ const guidedTourLocaleStorage = persistStateToLocaleStorage.get(COMPLETED_STEPS);
11
+ const currentStepLocaleStorage = persistStateToLocaleStorage.get(CURRENT_STEP);
12
+ const skippedLocaleStorage = persistStateToLocaleStorage.get(SKIPPED);
13
+
14
+ if (guidedTourLocaleStorage) {
15
+ guidedTourLocaleStorage.forEach(step => {
16
+ const [sectionName, stepName] = step.split('.');
17
+ set(copyInitialState, ['guidedTourState', sectionName, stepName], true);
18
+ });
19
+ }
20
+
21
+ // if current step when initializing mark it as done
22
+ if (currentStepLocaleStorage) {
23
+ const [sectionName, stepName] = currentStepLocaleStorage.split('.');
24
+ set(copyInitialState, ['guidedTourState', sectionName, stepName], true);
25
+ persistStateToLocaleStorage.addCompletedStep(currentStepLocaleStorage);
26
+ persistStateToLocaleStorage.addCurrentStep(null);
27
+ }
28
+
29
+ if (skippedLocaleStorage !== null) {
30
+ set(copyInitialState, 'isSkipped', skippedLocaleStorage);
31
+ }
32
+
33
+ return copyInitialState;
34
+ };
35
+
36
+ export default init;
@@ -0,0 +1,153 @@
1
+ const layout = {
2
+ contentTypeBuilder: {
3
+ home: {
4
+ title: {
5
+ id: 'app.components.GuidedTour.home.CTB.title',
6
+ defaultMessage: '🧠 Build the content structure',
7
+ },
8
+ cta: {
9
+ title: {
10
+ id: 'app.components.GuidedTour.home.CTB.cta.title',
11
+ defaultMessage: 'Go to the Content type Builder',
12
+ },
13
+ type: 'REDIRECT',
14
+ target: '/plugins/content-type-builder',
15
+ },
16
+ },
17
+ create: {
18
+ title: {
19
+ id: 'app.components.GuidedTour.CTB.create.title',
20
+ defaultMessage: '🧠 Create a first Collection type',
21
+ },
22
+ content: {
23
+ id: 'app.components.GuidedTour.CTB.create.content',
24
+ defaultMessage:
25
+ '<p>Collection types help you manage several entries, Single types are suitable to manage only one entry.</p> <p>Ex: For a Blog website, Articles would be a Collection type whereas a Homepage would be a Single type.</p>',
26
+ },
27
+ cta: {
28
+ title: {
29
+ id: 'app.components.GuidedTour.CTB.create.cta.title',
30
+ defaultMessage: 'Build a Collection type',
31
+ },
32
+ type: 'CLOSE',
33
+ },
34
+ },
35
+ success: {
36
+ title: {
37
+ id: 'app.components.GuidedTour.CTB.success.title',
38
+ defaultMessage: 'Step 1: Completed ✅',
39
+ },
40
+ content: {
41
+ id: 'app.components.GuidedTour.CTB.success.content',
42
+ defaultMessage: '<p>Good going!</p><b>⚡️ What would you like to share with the world?</b>',
43
+ },
44
+ cta: {
45
+ title: {
46
+ id: 'app.components.GuidedTour.create-content',
47
+ defaultMessage: 'Create content',
48
+ },
49
+ type: 'REDIRECT',
50
+ target: '/content-manager',
51
+ },
52
+ },
53
+ },
54
+ contentManager: {
55
+ home: {
56
+ title: {
57
+ id: 'app.components.GuidedTour.home.CM.title',
58
+ defaultMessage: '⚡️ What would you like to share with the world?',
59
+ },
60
+ cta: {
61
+ title: {
62
+ id: 'app.components.GuidedTour.create-content',
63
+ defaultMessage: 'Create content',
64
+ },
65
+ type: 'REDIRECT',
66
+ target: '/content-manager',
67
+ },
68
+ },
69
+ create: {
70
+ title: {
71
+ id: 'app.components.GuidedTour.CM.create.title',
72
+ defaultMessage: '⚡️ Create content',
73
+ },
74
+ content: {
75
+ id: 'app.components.GuidedTour.CM.create.content',
76
+ defaultMessage:
77
+ "<p>Create and manage all the content here in the Content Manager.</p><p>Ex: Taking the Blog website example further, one can write an Article, save and publish it as they like.</p><p>💡 Quick tip - Don't forget to hit publish on the content you create.</p>",
78
+ },
79
+ cta: {
80
+ title: {
81
+ id: 'app.components.GuidedTour.create-content',
82
+ defaultMessage: 'Create content',
83
+ },
84
+ type: 'CLOSE',
85
+ },
86
+ },
87
+ success: {
88
+ title: {
89
+ id: 'app.components.GuidedTour.CM.success.title',
90
+ defaultMessage: 'Step 2: Completed ✅',
91
+ },
92
+ content: {
93
+ id: 'app.components.GuidedTour.CM.success.content',
94
+ defaultMessage: '<p>Awesome, one last step to go!</p><b>🚀 See content in action</b>',
95
+ },
96
+ cta: {
97
+ title: {
98
+ id: 'app.components.GuidedTour.CM.success.cta.title',
99
+ defaultMessage: 'Test the API',
100
+ },
101
+ type: 'REDIRECT',
102
+ target: '/settings/api-tokens',
103
+ },
104
+ },
105
+ },
106
+ apiTokens: {
107
+ home: {
108
+ title: {
109
+ id: 'app.components.GuidedTour.apiTokens.create.title',
110
+ defaultMessage: '🚀 See content in action',
111
+ },
112
+ cta: {
113
+ title: {
114
+ id: 'app.components.GuidedTour.home.apiTokens.cta.title',
115
+ defaultMessage: 'Test the API',
116
+ },
117
+ type: 'REDIRECT',
118
+ target: '/settings/api-tokens',
119
+ },
120
+ },
121
+ create: {
122
+ title: {
123
+ id: 'app.components.GuidedTour.apiTokens.create.title',
124
+ defaultMessage: '🚀 See content in action',
125
+ },
126
+ content: {
127
+ id: 'app.components.GuidedTour.apiTokens.create.content',
128
+ defaultMessage:
129
+ '<p>Generate an authentication token here and retrieve the content you just created.</p>',
130
+ },
131
+ cta: {
132
+ title: {
133
+ id: 'app.components.GuidedTour.apiTokens.create.cta.title',
134
+ defaultMessage: 'Generate an API Token',
135
+ },
136
+ type: 'CLOSE',
137
+ },
138
+ },
139
+ success: {
140
+ title: {
141
+ id: 'app.components.GuidedTour.apiTokens.success.title',
142
+ defaultMessage: 'Step 3: Completed ✅',
143
+ },
144
+ content: {
145
+ id: 'app.components.GuidedTour.apiTokens.success.content',
146
+ defaultMessage:
147
+ "<p>See content in action by making an HTTP request:</p><ul><li><p>To this URL: <light>https://'<'YOUR_DOMAIN'>'/api/'<'YOUR_CT'>'</light></p></li><li><p>With the header: <light>Authorization: bearer '<'YOUR_API_TOKEN'>'</light></p></li></ul><p>For more ways to interact with content, see the <documentationLink>documentation</documentationLink>.</p>",
148
+ },
149
+ },
150
+ },
151
+ };
152
+
153
+ export default layout;
@@ -0,0 +1,50 @@
1
+ /* eslint-disable consistent-return */
2
+ import produce from 'immer';
3
+
4
+ export const initialState = {
5
+ currentStep: null,
6
+ guidedTourState: {
7
+ contentTypeBuilder: {
8
+ create: false,
9
+ success: false,
10
+ },
11
+ contentManager: {
12
+ create: false,
13
+ success: false,
14
+ },
15
+ apiTokens: {
16
+ create: false,
17
+ success: false,
18
+ },
19
+ },
20
+ isGuidedTourVisible: false,
21
+ isSkipped: true,
22
+ };
23
+
24
+ const reducer = (state = initialState, action) =>
25
+ produce(state, draftState => {
26
+ switch (action.type) {
27
+ case 'SET_CURRENT_STEP': {
28
+ draftState.currentStep = action.step;
29
+ break;
30
+ }
31
+ case 'SET_STEP_STATE': {
32
+ const [section, step] = action.currentStep.split('.');
33
+ draftState.guidedTourState[section][step] = action.value;
34
+ break;
35
+ }
36
+ case 'SET_SKIPPED': {
37
+ draftState.isSkipped = action.value;
38
+ break;
39
+ }
40
+ case 'SET_GUIDED_TOUR_VISIBILITY': {
41
+ draftState.isGuidedTourVisible = action.value;
42
+ break;
43
+ }
44
+ default: {
45
+ return draftState;
46
+ }
47
+ }
48
+ });
49
+
50
+ export default reducer;
@@ -0,0 +1,13 @@
1
+ const arePreviousSectionsDone = (sectionName, guidedTourState) => {
2
+ const guidedTourArray = Object.entries(guidedTourState);
3
+
4
+ // Find current section position in the guidedTourArray
5
+ // Get only previous sections based on current section position
6
+ const currentSectionIndex = guidedTourArray.findIndex(([key]) => key === sectionName);
7
+ const previousSections = guidedTourArray.slice(0, currentSectionIndex);
8
+
9
+ // Check if every steps from previous section are done
10
+ return previousSections.every(([, sectionValue]) => Object.values(sectionValue).every(Boolean));
11
+ };
12
+
13
+ export default arePreviousSectionsDone;
@@ -0,0 +1,12 @@
1
+ const arePreviousStepsDone = (step, guidedTourState) => {
2
+ const stepSplit = step.split('.');
3
+ const stepName = stepSplit[1];
4
+ const sectionArray = Object.entries(guidedTourState[stepSplit[0]]);
5
+
6
+ const currentStepIndex = sectionArray.findIndex(([key]) => key === stepName);
7
+ const previousSteps = sectionArray.slice(0, currentStepIndex);
8
+
9
+ return previousSteps.every(([, sectionValue]) => sectionValue);
10
+ };
11
+
12
+ export default arePreviousStepsDone;
@@ -0,0 +1,6 @@
1
+ const isGuidedTourCompleted = guidedTourState =>
2
+ Object.entries(guidedTourState).every(([, section]) =>
3
+ Object.entries(section).every(([, step]) => step)
4
+ );
5
+
6
+ export default isGuidedTourCompleted;
@@ -0,0 +1,34 @@
1
+ export const CURRENT_STEP = 'GUIDED_TOUR_CURRENT_STEP';
2
+ export const COMPLETED_STEPS = 'GUIDED_TOUR_COMPLETED_STEPS';
3
+ export const SKIPPED = 'GUIDED_TOUR_SKIPPED';
4
+ const parse = JSON.parse;
5
+ const stringify = JSON.stringify;
6
+
7
+ const persistStateToLocaleStorage = {
8
+ clear() {
9
+ localStorage.removeItem(CURRENT_STEP);
10
+ localStorage.removeItem(COMPLETED_STEPS);
11
+ },
12
+ addCompletedStep: completedStep => {
13
+ const currentSteps = parse(localStorage.getItem(COMPLETED_STEPS))?.slice() || [];
14
+ const isAlreadyStored = currentSteps.includes(completedStep);
15
+
16
+ if (isAlreadyStored) {
17
+ return;
18
+ }
19
+
20
+ currentSteps.push(completedStep);
21
+ localStorage.setItem(COMPLETED_STEPS, stringify(currentSteps));
22
+ },
23
+ addCurrentStep: currentStep => {
24
+ localStorage.setItem(CURRENT_STEP, stringify(currentStep));
25
+ },
26
+ setSkipped: value => {
27
+ localStorage.setItem(SKIPPED, stringify(value));
28
+ },
29
+ get: item => {
30
+ return parse(localStorage.getItem(item));
31
+ },
32
+ };
33
+
34
+ export default persistStateToLocaleStorage;
@@ -5,6 +5,7 @@ import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
5
5
  import { Provider } from 'react-redux';
6
6
  import { AdminContext, ConfigurationsContext } from '../../contexts';
7
7
  import LanguageProvider from '../LanguageProvider';
8
+ import GuidedTour from '../GuidedTour';
8
9
  import AutoReloadOverlayBlockerProvider from '../AutoReloadOverlayBlockerProvider';
9
10
  import Notifications from '../Notifications';
10
11
  import OverlayBlocker from '../OverlayBlocker';
@@ -58,7 +59,9 @@ const Providers = ({
58
59
  <LanguageProvider messages={messages} localeNames={localeNames}>
59
60
  <AutoReloadOverlayBlockerProvider>
60
61
  <OverlayBlocker>
61
- <Notifications>{children}</Notifications>
62
+ <GuidedTour>
63
+ <Notifications>{children}</Notifications>
64
+ </GuidedTour>
62
65
  </OverlayBlocker>
63
66
  </AutoReloadOverlayBlockerProvider>
64
67
  </LanguageProvider>
@@ -8,6 +8,7 @@ import {
8
8
  useQueryParams,
9
9
  formatComponentData,
10
10
  contentManagementUtilRemoveFieldsFromData,
11
+ useGuidedTour,
11
12
  } from '@strapi/helper-plugin';
12
13
  import { useSelector, useDispatch } from 'react-redux';
13
14
  import PropTypes from 'prop-types';
@@ -34,6 +35,7 @@ import selectCrudReducer from '../../sharedReducers/crudReducer/selectors';
34
35
  // This container is used to handle the CRUD
35
36
  const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }) => {
36
37
  const toggleNotification = useNotification();
38
+ const { setCurrentStep } = useGuidedTour();
37
39
  const { trackUsage } = useTracking();
38
40
  const { push, replace } = useHistory();
39
41
  const [{ rawQuery }] = useQueryParams();
@@ -263,6 +265,8 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
263
265
  message: { id: getTrad('success.record.save') },
264
266
  });
265
267
 
268
+ setCurrentStep('contentManager.success');
269
+
266
270
  dispatch(submitSucceeded(cleanReceivedData(data)));
267
271
  // Enable navigation and remove loaders
268
272
  dispatch(setStatus('resolved'));
@@ -274,7 +278,16 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
274
278
  dispatch(setStatus('resolved'));
275
279
  }
276
280
  },
277
- [cleanReceivedData, displayErrors, replace, slug, dispatch, rawQuery, toggleNotification]
281
+ [
282
+ cleanReceivedData,
283
+ displayErrors,
284
+ replace,
285
+ slug,
286
+ dispatch,
287
+ rawQuery,
288
+ toggleNotification,
289
+ setCurrentStep,
290
+ ]
278
291
  );
279
292
 
280
293
  const onPublish = useCallback(async () => {
@@ -6,6 +6,7 @@ import {
6
6
  formatComponentData,
7
7
  useQueryParams,
8
8
  useNotification,
9
+ useGuidedTour,
9
10
  } from '@strapi/helper-plugin';
10
11
  import { useSelector, useDispatch } from 'react-redux';
11
12
  import PropTypes from 'prop-types';
@@ -29,6 +30,7 @@ import buildQueryString from '../../pages/ListView/utils/buildQueryString';
29
30
  const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
30
31
  const { trackUsage } = useTracking();
31
32
  const { push } = useHistory();
33
+ const { setCurrentStep } = useGuidedTour();
32
34
  const trackUsageRef = useRef(trackUsage);
33
35
  const [isCreatingEntry, setIsCreatingEntry] = useState(true);
34
36
  const [{ query, rawQuery }] = useQueryParams();
@@ -203,6 +205,8 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
203
205
  message: { id: getTrad('success.record.save') },
204
206
  });
205
207
 
208
+ setCurrentStep('contentManager.success');
209
+
206
210
  dispatch(submitSucceeded(cleanReceivedData(data)));
207
211
  setIsCreatingEntry(false);
208
212
 
@@ -215,7 +219,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
215
219
  dispatch(setStatus('resolved'));
216
220
  }
217
221
  },
218
- [cleanReceivedData, displayErrors, slug, dispatch, rawQuery, toggleNotification]
222
+ [cleanReceivedData, displayErrors, slug, dispatch, rawQuery, toggleNotification, setCurrentStep]
219
223
  );
220
224
  const onPublish = useCallback(async () => {
221
225
  try {
@@ -1,7 +1,12 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import { Helmet } from 'react-helmet';
3
3
  import { Switch, Route, useRouteMatch, Redirect, useLocation } from 'react-router-dom';
4
- import { CheckPagePermissions, LoadingIndicatorPage, NotFound } from '@strapi/helper-plugin';
4
+ import {
5
+ CheckPagePermissions,
6
+ LoadingIndicatorPage,
7
+ NotFound,
8
+ useGuidedTour,
9
+ } from '@strapi/helper-plugin';
5
10
  import { Layout, HeaderLayout } from '@strapi/design-system/Layout';
6
11
  import { Main } from '@strapi/design-system/Main';
7
12
  import { useIntl } from 'react-intl';
@@ -28,6 +33,14 @@ const App = () => {
28
33
  );
29
34
  const { pathname } = useLocation();
30
35
  const { formatMessage } = useIntl();
36
+ const { startSection } = useGuidedTour();
37
+ const startSectionRef = useRef(startSection);
38
+
39
+ useEffect(() => {
40
+ if (startSectionRef.current) {
41
+ startSectionRef.current('contentManager');
42
+ }
43
+ }, []);
31
44
 
32
45
  if (status === 'loading') {
33
46
  return (
@@ -15,6 +15,7 @@ import AppLayout from '../../layouts/AppLayout';
15
15
  import { useMenu, useReleaseNotification } from '../../hooks';
16
16
  import Onboarding from './Onboarding';
17
17
  import { createRoute } from '../../utils';
18
+ import GuidedTourModal from '../../components/GuidedTour/Modal';
18
19
 
19
20
  const CM = lazy(() =>
20
21
  import(/* webpackChunkName: "content-manager" */ '../../content-manager/pages/App')
@@ -92,6 +93,7 @@ const Admin = () => {
92
93
  <Route path="" component={NotFoundPage} />
93
94
  </Switch>
94
95
  </Suspense>
96
+ <GuidedTourModal />
95
97
  <Onboarding />
96
98
  </AppLayout>
97
99
  </DndProvider>
@@ -10,7 +10,7 @@ import { Flex } from '@strapi/design-system/Flex';
10
10
  import { Link } from '@strapi/design-system/Link';
11
11
  import { Button } from '@strapi/design-system/Button';
12
12
  import { TextInput } from '@strapi/design-system/TextInput';
13
- import { Checkbox } from '@strapi/design-system/Checkbox';
13
+ // import { Checkbox } from '@strapi/design-system/Checkbox';
14
14
  import { Grid, GridItem } from '@strapi/design-system/Grid';
15
15
  import { Typography } from '@strapi/design-system/Typography';
16
16
  import EyeStriked from '@strapi/icons/EyeStriked';
@@ -30,9 +30,12 @@ import FieldActionWrapper from '../FieldActionWrapper';
30
30
  const CenteredBox = styled(Box)`
31
31
  text-align: center;
32
32
  `;
33
- const A = styled.a`
34
- color: ${({ theme }) => theme.colors.primary600};
35
- `;
33
+
34
+ // Experiment only until the next release
35
+ // To uncomment then
36
+ // const A = styled.a`
37
+ // color: ${({ theme }) => theme.colors.primary600};
38
+ // `;
36
39
 
37
40
  const PasswordInput = styled(TextInput)`
38
41
  ::-ms-reveal {
@@ -271,7 +274,9 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
271
274
  })}
272
275
  type={confirmPasswordShown ? 'text' : 'password'}
273
276
  />
274
- <Checkbox
277
+ {/* Experiment only until the next release
278
+ To uncomment then */}
279
+ {/* <Checkbox
275
280
  onValueChange={checked => {
276
281
  handleChange({ target: { value: checked, name: 'news' } });
277
282
  }}
@@ -304,7 +309,7 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
304
309
  ),
305
310
  }
306
311
  )}
307
- </Checkbox>
312
+ </Checkbox> */}
308
313
  <Button fullWidth size="L" type="submit">
309
314
  {formatMessage({
310
315
  id: 'Auth.form.button.register',