@strapi/plugin-users-permissions 4.0.0-next.9 → 4.0.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 (177) hide show
  1. package/admin/src/components/BoundRoute/getMethodColor.js +41 -0
  2. package/admin/src/components/BoundRoute/index.js +40 -24
  3. package/admin/src/components/FormModal/Input/index.js +121 -0
  4. package/admin/src/components/FormModal/index.js +123 -0
  5. package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +19 -26
  6. package/admin/src/components/Permissions/PermissionRow/SubCategory.js +118 -0
  7. package/admin/src/components/Permissions/PermissionRow/index.js +9 -48
  8. package/admin/src/components/Permissions/index.js +36 -24
  9. package/admin/src/components/Permissions/init.js +1 -6
  10. package/admin/src/components/Policies/index.js +46 -47
  11. package/admin/src/components/UsersPermissions/index.js +29 -26
  12. package/admin/src/components/UsersPermissions/init.js +1 -2
  13. package/admin/src/hooks/useFetchRole/index.js +17 -7
  14. package/admin/src/hooks/useForm/index.js +3 -29
  15. package/admin/src/hooks/useForm/reducer.js +2 -21
  16. package/admin/src/hooks/usePlugins/index.js +12 -21
  17. package/admin/src/hooks/usePlugins/reducer.js +0 -3
  18. package/admin/src/index.js +0 -8
  19. package/admin/src/pages/AdvancedSettings/index.js +203 -193
  20. package/admin/src/pages/AdvancedSettings/utils/api.js +13 -0
  21. package/admin/src/pages/AdvancedSettings/utils/layout.js +96 -0
  22. package/admin/src/pages/AdvancedSettings/utils/schema.js +22 -0
  23. package/admin/src/pages/EmailTemplates/components/EmailForm.js +173 -0
  24. package/admin/src/pages/EmailTemplates/components/EmailTable.js +116 -0
  25. package/admin/src/pages/EmailTemplates/index.js +117 -197
  26. package/admin/src/pages/EmailTemplates/utils/api.js +13 -0
  27. package/admin/src/pages/Providers/index.js +206 -221
  28. package/admin/src/pages/Providers/utils/api.js +21 -0
  29. package/admin/src/pages/Providers/utils/forms.js +168 -126
  30. package/admin/src/pages/Roles/CreatePage/index.js +155 -147
  31. package/admin/src/pages/Roles/EditPage/index.js +162 -134
  32. package/admin/src/pages/Roles/ListPage/components/TableBody.js +96 -0
  33. package/admin/src/pages/Roles/ListPage/index.js +176 -156
  34. package/admin/src/pages/Roles/ListPage/utils/api.js +28 -0
  35. package/admin/src/translations/ar.json +0 -8
  36. package/admin/src/translations/cs.json +0 -8
  37. package/admin/src/translations/de.json +0 -8
  38. package/admin/src/translations/dk.json +0 -8
  39. package/admin/src/translations/en.json +33 -12
  40. package/admin/src/translations/es.json +0 -8
  41. package/admin/src/translations/fr.json +0 -8
  42. package/admin/src/translations/id.json +0 -8
  43. package/admin/src/translations/it.json +0 -8
  44. package/admin/src/translations/ja.json +0 -8
  45. package/admin/src/translations/ko.json +0 -8
  46. package/admin/src/translations/ms.json +0 -8
  47. package/admin/src/translations/nl.json +0 -8
  48. package/admin/src/translations/pl.json +0 -8
  49. package/admin/src/translations/pt-BR.json +0 -8
  50. package/admin/src/translations/pt.json +0 -8
  51. package/admin/src/translations/ru.json +0 -8
  52. package/admin/src/translations/sk.json +0 -8
  53. package/admin/src/translations/sv.json +0 -8
  54. package/admin/src/translations/th.json +0 -8
  55. package/admin/src/translations/tr.json +0 -8
  56. package/admin/src/translations/uk.json +0 -8
  57. package/admin/src/translations/vi.json +0 -8
  58. package/admin/src/translations/zh-Hans.json +5 -14
  59. package/admin/src/translations/zh.json +0 -8
  60. package/admin/src/utils/axiosInstance.js +36 -0
  61. package/admin/src/utils/formatPluginName.js +26 -0
  62. package/admin/src/utils/index.js +1 -0
  63. package/documentation/1.0.0/overrides/users-permissions-Role.json +6 -6
  64. package/documentation/1.0.0/overrides/users-permissions-User.json +7 -7
  65. package/package.json +30 -31
  66. package/server/bootstrap/index.js +19 -21
  67. package/server/config.js +3 -3
  68. package/server/content-types/index.js +3 -3
  69. package/server/content-types/permission/index.js +30 -3
  70. package/server/content-types/role/index.js +47 -3
  71. package/server/content-types/user/index.js +65 -4
  72. package/server/controllers/auth.js +81 -244
  73. package/server/controllers/content-manager-user.js +183 -0
  74. package/server/controllers/index.js +12 -6
  75. package/server/controllers/permissions.js +26 -0
  76. package/server/controllers/role.js +77 -0
  77. package/server/controllers/settings.js +85 -0
  78. package/server/controllers/user.js +118 -44
  79. package/server/controllers/validation/auth.js +29 -0
  80. package/server/controllers/validation/user.js +38 -0
  81. package/server/graphql/index.js +44 -0
  82. package/server/graphql/mutations/auth/email-confirmation.js +39 -0
  83. package/server/graphql/mutations/auth/forgot-password.js +38 -0
  84. package/server/graphql/mutations/auth/login.js +38 -0
  85. package/server/graphql/mutations/auth/register.js +39 -0
  86. package/server/graphql/mutations/auth/reset-password.js +41 -0
  87. package/server/graphql/mutations/crud/role/create-role.js +37 -0
  88. package/server/graphql/mutations/crud/role/delete-role.js +28 -0
  89. package/server/graphql/mutations/crud/role/update-role.js +38 -0
  90. package/server/graphql/mutations/crud/user/create-user.js +48 -0
  91. package/server/graphql/mutations/crud/user/delete-user.js +42 -0
  92. package/server/graphql/mutations/crud/user/update-user.js +49 -0
  93. package/server/graphql/mutations/index.js +42 -0
  94. package/server/graphql/queries/index.js +13 -0
  95. package/server/graphql/queries/me.js +17 -0
  96. package/server/graphql/resolvers-configs.js +37 -0
  97. package/server/graphql/types/create-role-payload.js +11 -0
  98. package/server/graphql/types/delete-role-payload.js +11 -0
  99. package/server/graphql/types/index.js +21 -0
  100. package/server/graphql/types/login-input.js +13 -0
  101. package/server/graphql/types/login-payload.js +12 -0
  102. package/server/graphql/types/me-role.js +14 -0
  103. package/server/graphql/types/me.js +16 -0
  104. package/server/graphql/types/password-payload.js +11 -0
  105. package/server/graphql/types/register-input.js +13 -0
  106. package/server/graphql/types/update-role-payload.js +11 -0
  107. package/server/graphql/utils.js +27 -0
  108. package/server/index.js +21 -0
  109. package/server/middlewares/index.js +2 -2
  110. package/server/{policies → middlewares}/rateLimit.js +3 -7
  111. package/server/register.js +11 -0
  112. package/server/routes/admin/index.js +10 -0
  113. package/server/routes/admin/permissions.js +20 -0
  114. package/server/routes/admin/role.js +79 -0
  115. package/server/routes/admin/settings.js +95 -0
  116. package/server/routes/content-api/auth.js +73 -0
  117. package/server/routes/content-api/index.js +11 -0
  118. package/server/routes/content-api/permissions.js +9 -0
  119. package/server/routes/content-api/role.js +29 -0
  120. package/server/routes/content-api/user.js +61 -0
  121. package/server/routes/index.js +4 -428
  122. package/server/services/index.js +10 -8
  123. package/server/services/jwt.js +9 -17
  124. package/server/services/providers.js +32 -33
  125. package/server/services/role.js +177 -0
  126. package/server/services/user.js +9 -15
  127. package/server/services/users-permissions.js +140 -338
  128. package/server/strategies/users-permissions.js +123 -0
  129. package/server/utils/index.d.ts +2 -0
  130. package/strapi-admin.js +3 -0
  131. package/strapi-server.js +1 -19
  132. package/admin/src/assets/images/logo.svg +0 -1
  133. package/admin/src/components/BaselineAlignement/index.js +0 -33
  134. package/admin/src/components/Bloc/index.js +0 -10
  135. package/admin/src/components/BoundRoute/Components.js +0 -78
  136. package/admin/src/components/ContainerFluid/index.js +0 -13
  137. package/admin/src/components/FormBloc/index.js +0 -61
  138. package/admin/src/components/IntlInput/index.js +0 -38
  139. package/admin/src/components/ListBaselineAlignment/index.js +0 -8
  140. package/admin/src/components/ListRow/Components.js +0 -74
  141. package/admin/src/components/ListRow/index.js +0 -35
  142. package/admin/src/components/ModalForm/Wrapper.js +0 -12
  143. package/admin/src/components/ModalForm/index.js +0 -59
  144. package/admin/src/components/Permissions/ListWrapper.js +0 -9
  145. package/admin/src/components/Permissions/PermissionRow/BaselineAlignment.js +0 -7
  146. package/admin/src/components/Permissions/PermissionRow/RowStyle.js +0 -28
  147. package/admin/src/components/Permissions/PermissionRow/SubCategory/ConditionsButtonWrapper.js +0 -13
  148. package/admin/src/components/Permissions/PermissionRow/SubCategory/PolicyWrapper.js +0 -8
  149. package/admin/src/components/Permissions/PermissionRow/SubCategory/SubCategoryWrapper.js +0 -26
  150. package/admin/src/components/Permissions/PermissionRow/SubCategory/index.js +0 -116
  151. package/admin/src/components/Policies/Components.js +0 -26
  152. package/admin/src/components/PrefixedIcon/index.js +0 -27
  153. package/admin/src/components/Roles/EmptyRole/BaselineAlignment.js +0 -7
  154. package/admin/src/components/Roles/EmptyRole/index.js +0 -27
  155. package/admin/src/components/Roles/RoleListWrapper/index.js +0 -17
  156. package/admin/src/components/Roles/RoleRow/RoleDescription.js +0 -9
  157. package/admin/src/components/Roles/RoleRow/index.js +0 -45
  158. package/admin/src/components/Roles/index.js +0 -3
  159. package/admin/src/components/SizedInput/index.js +0 -24
  160. package/admin/src/pages/AdvancedSettings/reducer.js +0 -65
  161. package/admin/src/pages/AdvancedSettings/utils/form.js +0 -52
  162. package/admin/src/pages/EmailTemplates/CustomTextInput.js +0 -105
  163. package/admin/src/pages/EmailTemplates/Wrapper.js +0 -36
  164. package/admin/src/pages/EmailTemplates/reducer.js +0 -58
  165. package/admin/src/pages/EmailTemplates/utils/forms.js +0 -81
  166. package/admin/src/pages/Roles/ListPage/BaselineAlignment.js +0 -8
  167. package/server/content-types/permission/schema.json +0 -48
  168. package/server/content-types/role/schema.json +0 -46
  169. package/server/content-types/user/schema.json +0 -66
  170. package/server/controllers/user/admin.js +0 -230
  171. package/server/controllers/user/api.js +0 -174
  172. package/server/controllers/users-permissions.js +0 -271
  173. package/server/middlewares/users-permissions.js +0 -44
  174. package/server/policies/index.js +0 -11
  175. package/server/policies/isAuthenticated.js +0 -9
  176. package/server/policies/permissions.js +0 -94
  177. package/server/schema.graphql.js +0 -317
@@ -0,0 +1,173 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { Form, GenericInput } from '@strapi/helper-plugin';
5
+ import { Formik } from 'formik';
6
+ import {
7
+ ModalLayout,
8
+ ModalHeader,
9
+ ModalFooter,
10
+ ModalBody,
11
+ } from '@strapi/design-system/ModalLayout';
12
+ import { Grid, GridItem } from '@strapi/design-system/Grid';
13
+ import { Button } from '@strapi/design-system/Button';
14
+ import { Breadcrumbs, Crumb } from '@strapi/design-system/Breadcrumbs';
15
+ import { Textarea } from '@strapi/design-system/Textarea';
16
+ import { getTrad } from '../../../utils';
17
+ import schema from '../utils/schema';
18
+
19
+ const EmailForm = ({ template, onToggle, onSubmit }) => {
20
+ const { formatMessage } = useIntl();
21
+
22
+ return (
23
+ <ModalLayout
24
+ onClose={onToggle}
25
+ labelledBy={`${formatMessage({
26
+ id: getTrad('PopUpForm.header.edit.email-templates'),
27
+ defaultMessage: 'Edit email template',
28
+ })}, ${formatMessage({ id: getTrad(template.display), defaultMessage: template.display })}`}
29
+ >
30
+ <ModalHeader>
31
+ <Breadcrumbs
32
+ label={`${formatMessage({
33
+ id: getTrad('PopUpForm.header.edit.email-templates'),
34
+ defaultMessage: 'Edit email template',
35
+ })}, ${formatMessage({
36
+ id: getTrad(template.display),
37
+ defaultMessage: template.display,
38
+ })}`}
39
+ >
40
+ <Crumb>
41
+ {formatMessage({
42
+ id: getTrad('PopUpForm.header.edit.email-templates'),
43
+ defaultMessage: 'Edit email template',
44
+ })}
45
+ </Crumb>
46
+ <Crumb>
47
+ {formatMessage({ id: getTrad(template.display), defaultMessage: template.display })}
48
+ </Crumb>
49
+ </Breadcrumbs>
50
+ </ModalHeader>
51
+ <Formik
52
+ onSubmit={onSubmit}
53
+ initialValues={template}
54
+ validateOnChange={false}
55
+ validationSchema={schema}
56
+ enableReinitialize
57
+ >
58
+ {({ errors, values, handleChange, isSubmitting }) => {
59
+ return (
60
+ <Form>
61
+ <ModalBody>
62
+ <Grid gap={5}>
63
+ <GridItem col={6} s={12}>
64
+ <GenericInput
65
+ intlLabel={{
66
+ id: getTrad('PopUpForm.Email.options.from.name.label'),
67
+ defaultMessage: 'Shipper name',
68
+ }}
69
+ name="options.from.name"
70
+ onChange={handleChange}
71
+ value={values.options.from.name}
72
+ error={errors?.options?.from?.name}
73
+ type="text"
74
+ />
75
+ </GridItem>
76
+ <GridItem col={6} s={12}>
77
+ <GenericInput
78
+ intlLabel={{
79
+ id: getTrad('PopUpForm.Email.options.from.email.label'),
80
+ defaultMessage: 'Shipper email',
81
+ }}
82
+ name="options.from.email"
83
+ onChange={handleChange}
84
+ value={values.options.from.email}
85
+ error={errors?.options?.from?.email}
86
+ type="text"
87
+ />
88
+ </GridItem>
89
+ <GridItem col={6} s={12}>
90
+ <GenericInput
91
+ intlLabel={{
92
+ id: getTrad('PopUpForm.Email.options.response_email.label'),
93
+ defaultMessage: 'Response email',
94
+ }}
95
+ name="options.response_email"
96
+ onChange={handleChange}
97
+ value={values.options.response_email}
98
+ error={errors?.options?.response_email}
99
+ type="text"
100
+ />
101
+ </GridItem>
102
+ <GridItem col={6} s={12}>
103
+ <GenericInput
104
+ intlLabel={{
105
+ id: getTrad('PopUpForm.Email.options.object.label'),
106
+ defaultMessage: 'Subject',
107
+ }}
108
+ name="options.object"
109
+ onChange={handleChange}
110
+ value={values.options.object}
111
+ error={errors?.options?.object}
112
+ type="text"
113
+ />
114
+ </GridItem>
115
+ <GridItem col={12} s={12}>
116
+ <Textarea
117
+ label={formatMessage({
118
+ id: getTrad('PopUpForm.Email.options.message.label'),
119
+ defaultMessage: 'Message',
120
+ })}
121
+ name="options.message"
122
+ onChange={handleChange}
123
+ value={values.options.message}
124
+ error={
125
+ errors?.options?.message &&
126
+ formatMessage({
127
+ id: errors.options.message,
128
+ defaultMessage: errors.options.message,
129
+ })
130
+ }
131
+ />
132
+ </GridItem>
133
+ </Grid>
134
+ </ModalBody>
135
+ <ModalFooter
136
+ startActions={
137
+ <Button onClick={onToggle} variant="tertiary">
138
+ Cancel
139
+ </Button>
140
+ }
141
+ endActions={
142
+ <Button loading={isSubmitting} type="submit">
143
+ Finish
144
+ </Button>
145
+ }
146
+ />
147
+ </Form>
148
+ );
149
+ }}
150
+ </Formik>
151
+ </ModalLayout>
152
+ );
153
+ };
154
+
155
+ EmailForm.propTypes = {
156
+ template: PropTypes.shape({
157
+ display: PropTypes.string,
158
+ icon: PropTypes.string,
159
+ options: PropTypes.shape({
160
+ from: PropTypes.shape({
161
+ name: PropTypes.string,
162
+ email: PropTypes.string,
163
+ }),
164
+ message: PropTypes.string,
165
+ object: PropTypes.string,
166
+ response_email: PropTypes.string,
167
+ }),
168
+ }).isRequired,
169
+ onSubmit: PropTypes.func.isRequired,
170
+ onToggle: PropTypes.func.isRequired,
171
+ };
172
+
173
+ export default EmailForm;
@@ -0,0 +1,116 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { Table, Thead, Tbody, Tr, Td, Th } from '@strapi/design-system/Table';
5
+ import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
6
+ import { Typography } from '@strapi/design-system/Typography';
7
+ import { IconButton } from '@strapi/design-system/IconButton';
8
+ import Pencil from '@strapi/icons/Pencil';
9
+ import Reload from '@strapi/icons/Refresh';
10
+ import { onRowClick, stopPropagation } from '@strapi/helper-plugin';
11
+ import Check from '@strapi/icons/Check';
12
+ import { getTrad } from '../../../utils';
13
+
14
+ const EmailTable = ({ canUpdate, onEditClick }) => {
15
+ const { formatMessage } = useIntl();
16
+
17
+ return (
18
+ <Table colCount={3} rowCount={3}>
19
+ <Thead>
20
+ <Tr>
21
+ <Th width="1%">
22
+ <VisuallyHidden>
23
+ {formatMessage({
24
+ id: getTrad('Email.template.table.icon.label'),
25
+ defaultMessage: 'icon',
26
+ })}
27
+ </VisuallyHidden>
28
+ </Th>
29
+ <Th>
30
+ <Typography variant="sigma" textColor="neutral600">
31
+ {formatMessage({
32
+ id: getTrad('Email.template.table.name.label'),
33
+ defaultMessage: 'name',
34
+ })}
35
+ </Typography>
36
+ </Th>
37
+ <Th width="1%">
38
+ <VisuallyHidden>
39
+ {formatMessage({
40
+ id: getTrad('Email.template.table.action.label'),
41
+ defaultMessage: 'action',
42
+ })}
43
+ </VisuallyHidden>
44
+ </Th>
45
+ </Tr>
46
+ </Thead>
47
+ <Tbody>
48
+ <Tr {...onRowClick({ fn: () => onEditClick('reset_password') })}>
49
+ <Td>
50
+ <Reload
51
+ aria-label={formatMessage({
52
+ id: getTrad('Email.template.reset_password'),
53
+ defaultMessage: 'Reset password',
54
+ })}
55
+ />
56
+ </Td>
57
+ <Td>
58
+ <Typography>
59
+ {formatMessage({
60
+ id: getTrad('Email.template.reset_password'),
61
+ defaultMessage: 'Reset password',
62
+ })}
63
+ </Typography>
64
+ </Td>
65
+ <Td {...stopPropagation}>
66
+ <IconButton
67
+ onClick={() => onEditClick('reset_password')}
68
+ label={formatMessage({
69
+ id: getTrad('Email.template.form.edit.label'),
70
+ defaultMessage: 'Edit a template',
71
+ })}
72
+ noBorder
73
+ icon={canUpdate && <Pencil />}
74
+ />
75
+ </Td>
76
+ </Tr>
77
+ <Tr {...onRowClick({ fn: () => onEditClick('email_confirmation') })}>
78
+ <Td>
79
+ <Check
80
+ aria-label={formatMessage({
81
+ id: getTrad('Email.template.email_confirmation'),
82
+ defaultMessage: 'Email address confirmation',
83
+ })}
84
+ />
85
+ </Td>
86
+ <Td>
87
+ <Typography>
88
+ {formatMessage({
89
+ id: getTrad('Email.template.email_confirmation'),
90
+ defaultMessage: 'Email address confirmation',
91
+ })}
92
+ </Typography>
93
+ </Td>
94
+ <Td {...stopPropagation}>
95
+ <IconButton
96
+ onClick={() => onEditClick('email_confirmation')}
97
+ label={formatMessage({
98
+ id: getTrad('Email.template.form.edit.label'),
99
+ defaultMessage: 'Edit a template',
100
+ })}
101
+ noBorder
102
+ icon={canUpdate && <Pencil />}
103
+ />
104
+ </Td>
105
+ </Tr>
106
+ </Tbody>
107
+ </Table>
108
+ );
109
+ };
110
+
111
+ EmailTable.propTypes = {
112
+ canUpdate: PropTypes.bool.isRequired,
113
+ onEditClick: PropTypes.func.isRequired,
114
+ };
115
+
116
+ export default EmailTable;
@@ -1,27 +1,24 @@
1
- import React, { useCallback, useMemo, useRef, useState } from 'react';
1
+ import React, { useMemo, useRef, useState } from 'react';
2
+ import { useQuery, useMutation, useQueryClient } from 'react-query';
2
3
  import { useIntl } from 'react-intl';
3
- import { Header, List } from '@buffetjs/custom';
4
- import { Pencil } from '@buffetjs/icons';
5
- import { get } from 'lodash';
6
4
  import {
7
5
  SettingsPageTitle,
8
- SizedInput,
9
6
  useTracking,
10
- request,
11
- getYupInnerErrors,
12
7
  useNotification,
13
8
  useOverlayBlocker,
14
9
  CheckPagePermissions,
10
+ useRBAC,
11
+ useFocusWhenNavigate,
12
+ LoadingIndicatorPage,
15
13
  } from '@strapi/helper-plugin';
16
- import { Row } from 'reactstrap';
14
+ import { useNotifyAT } from '@strapi/design-system/LiveRegions';
15
+ import { Main } from '@strapi/design-system/Main';
16
+ import { ContentLayout, HeaderLayout } from '@strapi/design-system/Layout';
17
17
  import pluginPermissions from '../../permissions';
18
- import { useForm } from '../../hooks';
19
- import ListBaselineAlignment from '../../components/ListBaselineAlignment';
20
- import ListRow from '../../components/ListRow';
21
- import ModalForm from '../../components/ModalForm';
22
- import { getRequestURL, getTrad } from '../../utils';
23
- import forms from './utils/forms';
24
- import schema from './utils/schema';
18
+ import { getTrad } from '../../utils';
19
+ import { fetchData, putEmailTemplate } from './utils/api';
20
+ import EmailTable from './components/EmailTable';
21
+ import EmailForm from './components/EmailForm';
25
22
 
26
23
  const ProtectedEmailTemplatesPage = () => (
27
24
  <CheckPagePermissions permissions={pluginPermissions.readEmailTemplates}>
@@ -32,210 +29,133 @@ const ProtectedEmailTemplatesPage = () => (
32
29
  const EmailTemplatesPage = () => {
33
30
  const { formatMessage } = useIntl();
34
31
  const { trackUsage } = useTracking();
32
+ const { notifyStatus } = useNotifyAT();
35
33
  const toggleNotification = useNotification();
36
34
  const { lockApp, unlockApp } = useOverlayBlocker();
37
35
  const trackUsageRef = useRef(trackUsage);
38
- const buttonSubmitRef = useRef(null);
39
- const pageTitle = formatMessage({ id: getTrad('HeaderNav.link.emailTemplates') });
36
+ const queryClient = useQueryClient();
37
+ useFocusWhenNavigate();
38
+
39
+ const [isModalOpen, setIsModalOpen] = useState(false);
40
+ const [templateToEdit, setTemplateToEdit] = useState(null);
41
+
40
42
  const updatePermissions = useMemo(() => {
41
43
  return { update: pluginPermissions.updateEmailTemplates };
42
44
  }, []);
43
- const [isOpen, setIsOpen] = useState(false);
44
- const [isSubmiting, setIsSubmiting] = useState(false);
45
- const [showForm, setShowForm] = useState(false);
46
- const [templateToEdit, setTemplateToEdit] = useState(null);
47
45
 
48
46
  const {
47
+ isLoading: isLoadingForPermissions,
49
48
  allowedActions: { canUpdate },
50
- dispatchResetForm,
51
- dispatchSetFormErrors,
52
- dispatchSubmitSucceeded,
53
- formErrors,
54
- handleChange,
55
- isLoading,
56
- isLoadingForPermissions,
57
- modifiedData,
58
- } = useForm('email-templates', updatePermissions);
59
-
60
- const emailTemplates = useMemo(() => {
61
- return Object.keys(modifiedData).reduce((acc, current) => {
62
- const { display, icon } = modifiedData[current];
63
-
64
- acc.push({
65
- id: current,
66
- name: formatMessage({ id: getTrad(display) }),
67
- icon: ['fas', icon],
49
+ } = useRBAC(updatePermissions);
50
+
51
+ const { status: isLoadingData, data } = useQuery('email-templates', () => fetchData(), {
52
+ onSuccess: () => {
53
+ notifyStatus(
54
+ formatMessage({
55
+ id: getTrad('Email.template.data.loaded'),
56
+ defaultMessage: 'Email templates has been loaded',
57
+ })
58
+ );
59
+ },
60
+ onError: () => {
61
+ toggleNotification({
62
+ type: 'warning',
63
+ message: { id: 'notification.error', defaultMessage: 'An error occured' },
68
64
  });
69
-
70
- return acc;
71
- }, []);
72
- }, [modifiedData, formatMessage]);
73
-
74
- const listTitle = useMemo(() => {
75
- const count = emailTemplates.length;
76
-
77
- return formatMessage(
78
- {
79
- id: getTrad(`List.title.emailTemplates.${count > 1 ? 'plural' : 'singular'}`),
80
- },
81
- { number: count }
82
- );
83
- }, [emailTemplates.length, formatMessage]);
84
-
85
- const handleClosed = useCallback(() => {
86
- setTemplateToEdit(null);
87
- setShowForm(false);
88
- dispatchResetForm();
89
- }, [dispatchResetForm]);
90
-
91
- const handleToggle = useCallback(() => {
92
- setIsOpen(prev => !prev);
93
- }, []);
94
-
95
- const handleClickEdit = useCallback(
96
- template => {
97
- setTemplateToEdit(template);
98
- handleToggle();
99
65
  },
100
- [handleToggle]
101
- );
102
-
103
- const handleSubmit = useCallback(
104
- async e => {
105
- e.preventDefault();
106
-
107
- let errors = {};
66
+ });
108
67
 
109
- try {
110
- setIsSubmiting(true);
111
- await schema.validate(modifiedData[templateToEdit.id], { abortEarly: false });
68
+ const isLoading = isLoadingForPermissions || isLoadingData !== 'success';
112
69
 
113
- lockApp();
70
+ const handleToggle = () => {
71
+ setIsModalOpen(prev => !prev);
72
+ };
114
73
 
115
- try {
116
- trackUsageRef.current('willEditEmailTemplates');
74
+ const handleEditClick = template => {
75
+ setTemplateToEdit(template);
76
+ handleToggle();
77
+ };
117
78
 
118
- await request(getRequestURL('email-templates'), {
119
- method: 'PUT',
120
- body: { 'email-templates': modifiedData },
121
- });
79
+ const submitMutation = useMutation(body => putEmailTemplate({ 'email-templates': body }), {
80
+ onSuccess: async () => {
81
+ await queryClient.invalidateQueries('email-templates');
122
82
 
123
- trackUsageRef.current('didEditEmailTemplates');
124
-
125
- toggleNotification({
126
- type: 'success',
127
- message: { id: getTrad('notification.success.submit') },
128
- });
129
-
130
- dispatchSubmitSucceeded();
131
-
132
- handleToggle();
133
- } catch (err) {
134
- console.error(err);
83
+ toggleNotification({
84
+ type: 'success',
85
+ message: { id: 'notification.success.saved', defaultMessage: 'Saved' },
86
+ });
135
87
 
136
- toggleNotification({
137
- type: 'warning',
138
- message: { id: 'notification.error' },
139
- });
140
- }
141
- } catch (err) {
142
- errors = getYupInnerErrors(err);
143
- } finally {
144
- setIsSubmiting(false);
145
- unlockApp();
146
- }
88
+ trackUsageRef.current('didEditEmailTemplates');
147
89
 
148
- dispatchSetFormErrors(errors);
90
+ unlockApp();
91
+ handleToggle();
149
92
  },
150
- [
151
- dispatchSetFormErrors,
152
- dispatchSubmitSucceeded,
153
- modifiedData,
154
- templateToEdit,
155
- handleToggle,
156
- toggleNotification,
157
- lockApp,
158
- unlockApp,
159
- ]
160
- );
161
-
162
- const handleClick = useCallback(() => {
163
- buttonSubmitRef.current.click();
164
- }, []);
165
-
166
- const handleOpened = useCallback(() => {
167
- setShowForm(true);
168
- }, []);
93
+ onError: () => {
94
+ toggleNotification({
95
+ type: 'warning',
96
+ message: { id: 'notification.error', defaultMessage: 'An error occured' },
97
+ });
98
+ unlockApp();
99
+ },
100
+ refetchActive: true,
101
+ });
102
+ const { isLoading: isSubmittingForm } = submitMutation;
103
+
104
+ const handleSubmit = body => {
105
+ lockApp();
106
+ trackUsageRef.current('willEditEmailTemplates');
107
+
108
+ const editedTemplates = { ...data, [templateToEdit]: body };
109
+ submitMutation.mutate(editedTemplates);
110
+ };
111
+
112
+ if (isLoading) {
113
+ return (
114
+ <Main aria-busy="true">
115
+ <SettingsPageTitle
116
+ name={formatMessage({
117
+ id: getTrad('HeaderNav.link.emailTemplates'),
118
+ defaultMessage: 'Email templates',
119
+ })}
120
+ />
121
+ <HeaderLayout
122
+ title={formatMessage({
123
+ id: getTrad('HeaderNav.link.emailTemplates'),
124
+ defaultMessage: 'Email templates',
125
+ })}
126
+ />
127
+ <ContentLayout>
128
+ <LoadingIndicatorPage />
129
+ </ContentLayout>
130
+ </Main>
131
+ );
132
+ }
169
133
 
170
134
  return (
171
- <>
172
- <SettingsPageTitle name={pageTitle} />
173
- <div>
174
- <Header title={{ label: pageTitle }} isLoading={isLoadingForPermissions || isLoading} />
175
- <ListBaselineAlignment />
176
- <List
177
- title={listTitle}
178
- items={emailTemplates}
179
- isLoading={isLoadingForPermissions || isLoading}
180
- customRowComponent={template => (
181
- <ListRow
182
- {...template}
183
- onClick={() => {
184
- if (canUpdate) {
185
- handleClickEdit(template);
186
- }
187
- }}
188
- links={[
189
- {
190
- icon: canUpdate ? <Pencil fill="#0e1622" /> : null,
191
- onClick: e => {
192
- e.stopPropagation();
193
- handleClickEdit(template);
194
- },
195
- },
196
- ]}
197
- />
198
- )}
199
- />
200
- </div>
201
- <ModalForm
202
- isOpen={isOpen}
203
- onOpened={handleOpened}
204
- onToggle={handleToggle}
205
- onClosed={handleClosed}
206
- headerBreadcrumbs={[
207
- getTrad('PopUpForm.header.edit.email-templates'),
208
- get(templateToEdit, 'name', ''),
209
- ]}
210
- onClick={handleClick}
211
- onCancel={handleToggle}
212
- isLoading={isSubmiting}
213
- >
214
- {showForm && (
215
- <form onSubmit={handleSubmit}>
216
- <Row>
217
- {forms.map(input => {
218
- const id = get(templateToEdit, 'id');
219
-
220
- return (
221
- <SizedInput
222
- key={input.name}
223
- {...input}
224
- error={formErrors[input.name]}
225
- name={`${id}.${input.name}`}
226
- onChange={handleChange}
227
- value={get(modifiedData, [id, ...input.name.split('.')], '')}
228
- />
229
- );
230
- })}
231
- </Row>
232
- <button type="submit" style={{ display: 'none' }} ref={buttonSubmitRef}>
233
- hidden button to use the native form event
234
- </button>
235
- </form>
135
+ <Main aria-busy={isSubmittingForm}>
136
+ <SettingsPageTitle
137
+ name={formatMessage({
138
+ id: getTrad('HeaderNav.link.emailTemplates'),
139
+ defaultMessage: 'Email templates',
140
+ })}
141
+ />
142
+ <HeaderLayout
143
+ title={formatMessage({
144
+ id: getTrad('HeaderNav.link.emailTemplates'),
145
+ defaultMessage: 'Email templates',
146
+ })}
147
+ />
148
+ <ContentLayout>
149
+ <EmailTable onEditClick={handleEditClick} canUpdate={canUpdate} />
150
+ {isModalOpen && (
151
+ <EmailForm
152
+ template={data[templateToEdit]}
153
+ onToggle={handleToggle}
154
+ onSubmit={handleSubmit}
155
+ />
236
156
  )}
237
- </ModalForm>
238
- </>
157
+ </ContentLayout>
158
+ </Main>
239
159
  );
240
160
  };
241
161
 
@@ -0,0 +1,13 @@
1
+ import { axiosInstance, getRequestURL } from '../../../utils';
2
+
3
+ const fetchData = async () => {
4
+ const { data } = await axiosInstance.get(getRequestURL('email-templates'));
5
+
6
+ return data;
7
+ };
8
+
9
+ const putEmailTemplate = body => {
10
+ return axiosInstance.put(getRequestURL('email-templates'), body);
11
+ };
12
+
13
+ export { fetchData, putEmailTemplate };