@akemona-org/strapi-plugin-users-permissions 3.7.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 (143) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +19 -0
  3. package/admin/src/assets/images/logo.svg +1 -0
  4. package/admin/src/components/BaselineAlignement/index.js +33 -0
  5. package/admin/src/components/Bloc/index.js +10 -0
  6. package/admin/src/components/BoundRoute/Components.js +78 -0
  7. package/admin/src/components/BoundRoute/index.js +56 -0
  8. package/admin/src/components/ContainerFluid/index.js +13 -0
  9. package/admin/src/components/FormBloc/index.js +61 -0
  10. package/admin/src/components/IntlInput/index.js +38 -0
  11. package/admin/src/components/ListBaselineAlignment/index.js +8 -0
  12. package/admin/src/components/ListRow/Components.js +74 -0
  13. package/admin/src/components/ListRow/index.js +35 -0
  14. package/admin/src/components/ModalForm/Wrapper.js +12 -0
  15. package/admin/src/components/ModalForm/index.js +59 -0
  16. package/admin/src/components/Permissions/ListWrapper.js +9 -0
  17. package/admin/src/components/Permissions/PermissionRow/BaselineAlignment.js +7 -0
  18. package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +37 -0
  19. package/admin/src/components/Permissions/PermissionRow/RowStyle.js +28 -0
  20. package/admin/src/components/Permissions/PermissionRow/SubCategory/ConditionsButtonWrapper.js +13 -0
  21. package/admin/src/components/Permissions/PermissionRow/SubCategory/PolicyWrapper.js +8 -0
  22. package/admin/src/components/Permissions/PermissionRow/SubCategory/SubCategoryWrapper.js +26 -0
  23. package/admin/src/components/Permissions/PermissionRow/SubCategory/index.js +116 -0
  24. package/admin/src/components/Permissions/PermissionRow/index.js +92 -0
  25. package/admin/src/components/Permissions/index.js +44 -0
  26. package/admin/src/components/Permissions/init.js +14 -0
  27. package/admin/src/components/Permissions/reducer.js +27 -0
  28. package/admin/src/components/Policies/Components.js +26 -0
  29. package/admin/src/components/Policies/index.js +61 -0
  30. package/admin/src/components/PrefixedIcon/index.js +27 -0
  31. package/admin/src/components/Roles/EmptyRole/BaselineAlignment.js +7 -0
  32. package/admin/src/components/Roles/EmptyRole/index.js +27 -0
  33. package/admin/src/components/Roles/RoleListWrapper/index.js +17 -0
  34. package/admin/src/components/Roles/RoleRow/RoleDescription.js +9 -0
  35. package/admin/src/components/Roles/RoleRow/index.js +45 -0
  36. package/admin/src/components/Roles/index.js +3 -0
  37. package/admin/src/components/SizedInput/index.js +24 -0
  38. package/admin/src/components/UsersPermissions/index.js +91 -0
  39. package/admin/src/components/UsersPermissions/init.js +11 -0
  40. package/admin/src/components/UsersPermissions/reducer.js +60 -0
  41. package/admin/src/containers/AdvancedSettings/index.js +218 -0
  42. package/admin/src/containers/AdvancedSettings/reducer.js +65 -0
  43. package/admin/src/containers/AdvancedSettings/utils/form.js +52 -0
  44. package/admin/src/containers/EmailTemplates/CustomTextInput.js +105 -0
  45. package/admin/src/containers/EmailTemplates/Wrapper.js +36 -0
  46. package/admin/src/containers/EmailTemplates/index.js +222 -0
  47. package/admin/src/containers/EmailTemplates/reducer.js +58 -0
  48. package/admin/src/containers/EmailTemplates/utils/forms.js +81 -0
  49. package/admin/src/containers/EmailTemplates/utils/schema.js +25 -0
  50. package/admin/src/containers/Providers/index.js +283 -0
  51. package/admin/src/containers/Providers/reducer.js +54 -0
  52. package/admin/src/containers/Providers/utils/createProvidersArray.js +21 -0
  53. package/admin/src/containers/Providers/utils/forms.js +205 -0
  54. package/admin/src/containers/Roles/CreatePage/index.js +167 -0
  55. package/admin/src/containers/Roles/CreatePage/utils/schema.js +9 -0
  56. package/admin/src/containers/Roles/EditPage/index.js +161 -0
  57. package/admin/src/containers/Roles/EditPage/utils/schema.js +9 -0
  58. package/admin/src/containers/Roles/ListPage/BaselineAlignment.js +8 -0
  59. package/admin/src/containers/Roles/ListPage/index.js +188 -0
  60. package/admin/src/containers/Roles/ProtectedCreatePage/index.js +12 -0
  61. package/admin/src/containers/Roles/ProtectedEditPage/index.js +12 -0
  62. package/admin/src/containers/Roles/ProtectedListPage/index.js +15 -0
  63. package/admin/src/containers/Roles/index.js +35 -0
  64. package/admin/src/contexts/EditPage/index.js +26 -0
  65. package/admin/src/contexts/HomePage/index.js +27 -0
  66. package/admin/src/contexts/UsersPermissionsContext/index.js +17 -0
  67. package/admin/src/hooks/index.js +5 -0
  68. package/admin/src/hooks/useFetchRole/index.js +55 -0
  69. package/admin/src/hooks/useFetchRole/reducer.js +31 -0
  70. package/admin/src/hooks/useForm/index.js +96 -0
  71. package/admin/src/hooks/useForm/reducer.js +59 -0
  72. package/admin/src/hooks/usePlugins/index.js +73 -0
  73. package/admin/src/hooks/usePlugins/init.js +5 -0
  74. package/admin/src/hooks/usePlugins/reducer.js +37 -0
  75. package/admin/src/hooks/useRolesList/index.js +62 -0
  76. package/admin/src/hooks/useRolesList/init.js +5 -0
  77. package/admin/src/hooks/useRolesList/reducer.js +31 -0
  78. package/admin/src/index.js +109 -0
  79. package/admin/src/permissions.js +33 -0
  80. package/admin/src/pluginId.js +5 -0
  81. package/admin/src/translations/ar.json +49 -0
  82. package/admin/src/translations/cs.json +55 -0
  83. package/admin/src/translations/de.json +68 -0
  84. package/admin/src/translations/dk.json +116 -0
  85. package/admin/src/translations/en.json +104 -0
  86. package/admin/src/translations/es.json +70 -0
  87. package/admin/src/translations/fr.json +55 -0
  88. package/admin/src/translations/id.json +69 -0
  89. package/admin/src/translations/index.js +55 -0
  90. package/admin/src/translations/it.json +68 -0
  91. package/admin/src/translations/ja.json +53 -0
  92. package/admin/src/translations/ko.json +55 -0
  93. package/admin/src/translations/ms.json +54 -0
  94. package/admin/src/translations/nl.json +53 -0
  95. package/admin/src/translations/pl.json +55 -0
  96. package/admin/src/translations/pt-BR.json +49 -0
  97. package/admin/src/translations/pt.json +53 -0
  98. package/admin/src/translations/ru.json +68 -0
  99. package/admin/src/translations/sk.json +57 -0
  100. package/admin/src/translations/sv.json +68 -0
  101. package/admin/src/translations/th.json +66 -0
  102. package/admin/src/translations/tr.json +53 -0
  103. package/admin/src/translations/uk.json +54 -0
  104. package/admin/src/translations/vi.json +55 -0
  105. package/admin/src/translations/zh-Hans.json +104 -0
  106. package/admin/src/translations/zh.json +53 -0
  107. package/admin/src/utils/cleanPermissions.js +25 -0
  108. package/admin/src/utils/formatPolicies.js +8 -0
  109. package/admin/src/utils/getRequestURL.js +5 -0
  110. package/admin/src/utils/getTrad.js +5 -0
  111. package/admin/src/utils/index.js +4 -0
  112. package/config/functions/bootstrap.js +234 -0
  113. package/config/layout.js +10 -0
  114. package/config/policies/isAuthenticated.js +9 -0
  115. package/config/policies/permissions.js +93 -0
  116. package/config/policies/rateLimit.js +33 -0
  117. package/config/request.json +6 -0
  118. package/config/routes.json +397 -0
  119. package/config/schema.graphql.js +280 -0
  120. package/config/security.json +5 -0
  121. package/config/users-permissions-actions.js +80 -0
  122. package/controllers/Auth.js +612 -0
  123. package/controllers/User.js +125 -0
  124. package/controllers/UsersPermissions.js +291 -0
  125. package/controllers/user/admin.js +224 -0
  126. package/controllers/user/api.js +173 -0
  127. package/controllers/validation/email-template.js +40 -0
  128. package/documentation/1.0.0/overrides/users-permissions-Role.json +281 -0
  129. package/documentation/1.0.0/overrides/users-permissions-User.json +325 -0
  130. package/middlewares/users-permissions/defaults.json +5 -0
  131. package/middlewares/users-permissions/index.js +40 -0
  132. package/models/Permission.js +7 -0
  133. package/models/Permission.settings.json +43 -0
  134. package/models/Role.js +7 -0
  135. package/models/Role.settings.json +42 -0
  136. package/models/User.config.js +15 -0
  137. package/models/User.js +7 -0
  138. package/models/User.settings.json +62 -0
  139. package/package.json +70 -0
  140. package/services/Jwt.js +65 -0
  141. package/services/Providers.js +596 -0
  142. package/services/User.js +167 -0
  143. package/services/UsersPermissions.js +416 -0
@@ -0,0 +1,222 @@
1
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { Header, List } from '@buffetjs/custom';
4
+ import { Pencil } from '@buffetjs/icons';
5
+ import { get } from 'lodash';
6
+ import {
7
+ SettingsPageTitle,
8
+ SizedInput,
9
+ useGlobalContext,
10
+ request,
11
+ getYupInnerErrors,
12
+ } from 'strapi-helper-plugin';
13
+ import { Row } from 'reactstrap';
14
+ import pluginPermissions from '../../permissions';
15
+ import { useForm } from '../../hooks';
16
+ import ListBaselineAlignment from '../../components/ListBaselineAlignment';
17
+ import ListRow from '../../components/ListRow';
18
+ import ModalForm from '../../components/ModalForm';
19
+ import { getRequestURL, getTrad } from '../../utils';
20
+ import forms from './utils/forms';
21
+ import schema from './utils/schema';
22
+
23
+ const EmailTemplatesPage = () => {
24
+ const { formatMessage } = useIntl();
25
+ const { emitEvent } = useGlobalContext();
26
+ const emitEventRef = useRef(emitEvent);
27
+ const buttonSubmitRef = useRef(null);
28
+ const pageTitle = formatMessage({ id: getTrad('HeaderNav.link.emailTemplates') });
29
+ const updatePermissions = useMemo(() => {
30
+ return { update: pluginPermissions.updateEmailTemplates };
31
+ }, []);
32
+ const [isOpen, setIsOpen] = useState(false);
33
+ const [isSubmiting, setIsSubmiting] = useState(false);
34
+ const [showForm, setShowForm] = useState(false);
35
+ const [templateToEdit, setTemplateToEdit] = useState(null);
36
+
37
+ const {
38
+ allowedActions: { canUpdate },
39
+ dispatchResetForm,
40
+ dispatchSetFormErrors,
41
+ dispatchSubmitSucceeded,
42
+ formErrors,
43
+ handleChange,
44
+ isLoading,
45
+ isLoadingForPermissions,
46
+ modifiedData,
47
+ } = useForm('email-templates', updatePermissions);
48
+
49
+ const emailTemplates = useMemo(() => {
50
+ return Object.keys(modifiedData).reduce((acc, current) => {
51
+ const { display, icon } = modifiedData[current];
52
+
53
+ acc.push({
54
+ id: current,
55
+ name: formatMessage({ id: getTrad(display) }),
56
+ icon: ['fas', icon],
57
+ });
58
+
59
+ return acc;
60
+ }, []);
61
+ }, [modifiedData, formatMessage]);
62
+
63
+ const listTitle = useMemo(() => {
64
+ const count = emailTemplates.length;
65
+
66
+ return formatMessage(
67
+ {
68
+ id: getTrad(`List.title.emailTemplates.${count > 1 ? 'plural' : 'singular'}`),
69
+ },
70
+ { number: count }
71
+ );
72
+ }, [emailTemplates.length, formatMessage]);
73
+
74
+ const handleClosed = useCallback(() => {
75
+ setTemplateToEdit(null);
76
+ setShowForm(false);
77
+ dispatchResetForm();
78
+ }, [dispatchResetForm]);
79
+
80
+ const handleToggle = useCallback(() => {
81
+ setIsOpen(prev => !prev);
82
+ }, []);
83
+
84
+ const handleClickEdit = useCallback(
85
+ template => {
86
+ setTemplateToEdit(template);
87
+ handleToggle();
88
+ },
89
+ [handleToggle]
90
+ );
91
+
92
+ const handleSubmit = useCallback(
93
+ async e => {
94
+ e.preventDefault();
95
+
96
+ let errors = {};
97
+
98
+ try {
99
+ setIsSubmiting(true);
100
+ await schema.validate(modifiedData[templateToEdit.id], { abortEarly: false });
101
+
102
+ strapi.lockAppWithOverlay();
103
+
104
+ try {
105
+ emitEventRef.current('willEditEmailTemplates');
106
+
107
+ await request(getRequestURL('email-templates'), {
108
+ method: 'PUT',
109
+ body: { 'email-templates': modifiedData },
110
+ });
111
+
112
+ emitEventRef.current('didEditEmailTemplates');
113
+
114
+ strapi.notification.toggle({
115
+ type: 'success',
116
+ message: { id: getTrad('notification.success.submit') },
117
+ });
118
+
119
+ dispatchSubmitSucceeded();
120
+
121
+ handleToggle();
122
+ } catch (err) {
123
+ console.error(err);
124
+
125
+ strapi.notification.toggle({
126
+ type: 'warning',
127
+ message: { id: 'notification.error' },
128
+ });
129
+ }
130
+ } catch (err) {
131
+ errors = getYupInnerErrors(err);
132
+ } finally {
133
+ setIsSubmiting(false);
134
+ strapi.unlockApp();
135
+ }
136
+
137
+ dispatchSetFormErrors(errors);
138
+ },
139
+ [dispatchSetFormErrors, dispatchSubmitSucceeded, modifiedData, templateToEdit, handleToggle]
140
+ );
141
+
142
+ const handleClick = useCallback(() => {
143
+ buttonSubmitRef.current.click();
144
+ }, []);
145
+
146
+ const handleOpened = useCallback(() => {
147
+ setShowForm(true);
148
+ }, []);
149
+
150
+ return (
151
+ <>
152
+ <SettingsPageTitle name={pageTitle} />
153
+ <div>
154
+ <Header title={{ label: pageTitle }} isLoading={isLoadingForPermissions || isLoading} />
155
+ <ListBaselineAlignment />
156
+ <List
157
+ title={listTitle}
158
+ items={emailTemplates}
159
+ isLoading={isLoadingForPermissions || isLoading}
160
+ customRowComponent={template => (
161
+ <ListRow
162
+ {...template}
163
+ onClick={() => {
164
+ if (canUpdate) {
165
+ handleClickEdit(template);
166
+ }
167
+ }}
168
+ links={[
169
+ {
170
+ icon: canUpdate ? <Pencil fill="#0e1622" /> : null,
171
+ onClick: e => {
172
+ e.stopPropagation();
173
+ handleClickEdit(template);
174
+ },
175
+ },
176
+ ]}
177
+ />
178
+ )}
179
+ />
180
+ </div>
181
+ <ModalForm
182
+ isOpen={isOpen}
183
+ onOpened={handleOpened}
184
+ onToggle={handleToggle}
185
+ onClosed={handleClosed}
186
+ headerBreadcrumbs={[
187
+ getTrad('PopUpForm.header.edit.email-templates'),
188
+ get(templateToEdit, 'name', ''),
189
+ ]}
190
+ onClick={handleClick}
191
+ onCancel={handleToggle}
192
+ isLoading={isSubmiting}
193
+ >
194
+ {showForm && (
195
+ <form onSubmit={handleSubmit}>
196
+ <Row>
197
+ {forms.map(input => {
198
+ const id = get(templateToEdit, 'id');
199
+
200
+ return (
201
+ <SizedInput
202
+ key={input.name}
203
+ {...input}
204
+ error={formErrors[input.name]}
205
+ name={`${id}.${input.name}`}
206
+ onChange={handleChange}
207
+ value={get(modifiedData, [id, ...input.name.split('.')], '')}
208
+ />
209
+ );
210
+ })}
211
+ </Row>
212
+ <button type="submit" style={{ display: 'none' }} ref={buttonSubmitRef}>
213
+ hidden button to use the native form event
214
+ </button>
215
+ </form>
216
+ )}
217
+ </ModalForm>
218
+ </>
219
+ );
220
+ };
221
+
222
+ export default EmailTemplatesPage;
@@ -0,0 +1,58 @@
1
+ import produce from 'immer';
2
+ import { set } from 'lodash';
3
+
4
+ const initialState = {
5
+ formErrors: {},
6
+ isLoading: true,
7
+ initialData: {},
8
+ modifiedData: {},
9
+ };
10
+
11
+ const reducer = (state, action) =>
12
+ // eslint-disable-next-line consistent-return
13
+ produce(state, draftState => {
14
+ switch (action.type) {
15
+ case 'GET_DATA': {
16
+ draftState.isLoading = true;
17
+ draftState.initialData = {};
18
+ draftState.modifiedData = {};
19
+
20
+ break;
21
+ }
22
+
23
+ case 'GET_DATA_SUCCEEDED': {
24
+ draftState.isLoading = false;
25
+ draftState.initialData = action.data;
26
+ draftState.modifiedData = action.data;
27
+
28
+ break;
29
+ }
30
+ case 'GET_DATA_ERROR': {
31
+ draftState.isLoading = true;
32
+ break;
33
+ }
34
+ case 'ON_CHANGE': {
35
+ set(draftState, ['modifiedData', ...action.keys.split('.')], action.value);
36
+ break;
37
+ }
38
+ case 'ON_SUBMIT_SUCCEEDED': {
39
+ draftState.initialData = state.modifiedData;
40
+ break;
41
+ }
42
+ case 'RESET_FORM': {
43
+ draftState.modifiedData = state.initialData;
44
+ draftState.formErrors = {};
45
+ break;
46
+ }
47
+ case 'SET_ERRORS': {
48
+ draftState.formErrors = action.errors;
49
+ break;
50
+ }
51
+ default: {
52
+ return draftState;
53
+ }
54
+ }
55
+ });
56
+
57
+ export default reducer;
58
+ export { initialState };
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+ import { FormattedMessage } from 'react-intl';
3
+ import { getTrad } from '../../../utils';
4
+ import CustomTextInput from '../CustomTextInput';
5
+
6
+ const forms = [
7
+ {
8
+ autoFocus: true,
9
+ label: getTrad('PopUpForm.Email.options.from.name.label'),
10
+ name: 'options.from.name',
11
+ type: 'text',
12
+ placeholder: getTrad('PopUpForm.Email.options.from.name.placeholder'),
13
+ size: { xs: 6 },
14
+ validations: {
15
+ required: true,
16
+ },
17
+ },
18
+ {
19
+ autoFocus: false,
20
+ label: getTrad('PopUpForm.Email.options.from.email.label'),
21
+ name: 'options.from.email',
22
+ type: 'email',
23
+ placeholder: getTrad('PopUpForm.Email.options.from.email.placeholder'),
24
+ size: { xs: 6 },
25
+ validations: {
26
+ required: true,
27
+ },
28
+ },
29
+ {
30
+ autoFocus: false,
31
+ label: getTrad('PopUpForm.Email.options.response_email.label'),
32
+ name: 'options.response_email',
33
+ type: 'email',
34
+ placeholder: getTrad('PopUpForm.Email.options.response_email.placeholder'),
35
+ size: { xs: 6 },
36
+ validations: {
37
+ required: true,
38
+ },
39
+ },
40
+ {
41
+ autoFocus: false,
42
+ label: getTrad('PopUpForm.Email.options.object.label'),
43
+ name: 'options.object',
44
+ type: 'customText',
45
+ placeholder: getTrad('PopUpForm.Email.options.object.placeholder'),
46
+ customInputs: { customText: CustomTextInput },
47
+ descriptione: () => (
48
+ <FormattedMessage
49
+ id={getTrad('PopUpForm.Email.email_templates.inputDescription')}
50
+ values={{
51
+ link: (
52
+ <a
53
+ href="https://strapi.akemona.com/documentation/developer-docs/latest/development/plugins/users-permissions.html#templating-emails"
54
+ target="_blank"
55
+ rel="noopener noreferrer"
56
+ >
57
+ <FormattedMessage id={getTrad('PopUpForm.Email.link.documentation')} />
58
+ </a>
59
+ ),
60
+ }}
61
+ />
62
+ ),
63
+ size: { xs: 6 },
64
+ validations: {
65
+ required: true,
66
+ },
67
+ },
68
+ {
69
+ autoFocus: false,
70
+ label: getTrad('PopUpForm.Email.options.message.label'),
71
+ name: 'options.message',
72
+ type: 'textarea',
73
+ style: { height: '15.6rem' },
74
+ size: { xs: 12 },
75
+ validations: {
76
+ required: true,
77
+ },
78
+ },
79
+ ];
80
+
81
+ export default forms;
@@ -0,0 +1,25 @@
1
+ import * as yup from 'yup';
2
+ import { translatedErrors } from 'strapi-helper-plugin';
3
+
4
+ const schema = yup.object().shape({
5
+ options: yup
6
+ .object()
7
+ .shape({
8
+ from: yup
9
+ .object()
10
+ .shape({
11
+ name: yup.string().required(translatedErrors.required),
12
+ email: yup
13
+ .string()
14
+ .email(translatedErrors.email)
15
+ .required(translatedErrors.required),
16
+ })
17
+ .required(),
18
+ response_email: yup.string().email(translatedErrors.email),
19
+ object: yup.string().required(translatedErrors.required),
20
+ message: yup.string().required(translatedErrors.required),
21
+ })
22
+ .required(translatedErrors.required),
23
+ });
24
+
25
+ export default schema;
@@ -0,0 +1,283 @@
1
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { Header, List } from '@buffetjs/custom';
4
+ import { Text } from '@buffetjs/core';
5
+ import { Pencil } from '@buffetjs/icons';
6
+ import {
7
+ SettingsPageTitle,
8
+ SizedInput,
9
+ useGlobalContext,
10
+ getYupInnerErrors,
11
+ request,
12
+ } from 'strapi-helper-plugin';
13
+ import { get, upperFirst, has } from 'lodash';
14
+ import { Row } from 'reactstrap';
15
+ import pluginPermissions from '../../permissions';
16
+ import { useForm } from '../../hooks';
17
+ import { getRequestURL, getTrad } from '../../utils';
18
+ import ListBaselineAlignment from '../../components/ListBaselineAlignment';
19
+ import ListRow from '../../components/ListRow';
20
+ import ModalForm from '../../components/ModalForm';
21
+ import createProvidersArray from './utils/createProvidersArray';
22
+ import forms from './utils/forms';
23
+
24
+ const ProvidersPage = () => {
25
+ const { formatMessage } = useIntl();
26
+ const { emitEvent } = useGlobalContext();
27
+ const emitEventRef = useRef(emitEvent);
28
+ const [isOpen, setIsOpen] = useState(false);
29
+ const [isSubmiting, setIsSubmiting] = useState(false);
30
+ const buttonSubmitRef = useRef(null);
31
+ const [showForm, setShowForm] = useState(false);
32
+ const [providerToEditName, setProviderToEditName] = useState(null);
33
+
34
+ const updatePermissions = useMemo(() => {
35
+ return { update: pluginPermissions.updateProviders };
36
+ }, []);
37
+
38
+ const {
39
+ allowedActions: { canUpdate },
40
+ dispatchResetForm,
41
+ dispatchSetFormErrors,
42
+ dispatchSubmitSucceeded,
43
+ formErrors,
44
+ handleChange,
45
+ isLoading,
46
+ isLoadingForPermissions,
47
+ modifiedData,
48
+ } = useForm('providers', updatePermissions);
49
+
50
+ const providers = useMemo(() => createProvidersArray(modifiedData), [modifiedData]);
51
+ const enabledProvidersCount = useMemo(
52
+ () => providers.filter(provider => provider.enabled).length,
53
+ [providers]
54
+ );
55
+ const isProviderWithSubdomain = useMemo(() => {
56
+ if (!providerToEditName) {
57
+ return false;
58
+ }
59
+
60
+ const providerToEdit = providers.find(obj => obj.name === providerToEditName);
61
+
62
+ return has(providerToEdit, 'subdomain');
63
+ }, [providers, providerToEditName]);
64
+ const disabledProvidersCount = useMemo(() => {
65
+ return providers.length - enabledProvidersCount;
66
+ }, [providers, enabledProvidersCount]);
67
+
68
+ const listTitle = useMemo(() => {
69
+ const enabledMessage = formatMessage(
70
+ {
71
+ id: getTrad(
72
+ `List.title.providers.enabled.${enabledProvidersCount > 1 ? 'plural' : 'singular'}`
73
+ ),
74
+ },
75
+ { number: enabledProvidersCount }
76
+ );
77
+ const disabledMessage = formatMessage(
78
+ {
79
+ id: getTrad(
80
+ `List.title.providers.disabled.${disabledProvidersCount > 1 ? 'plural' : 'singular'}`
81
+ ),
82
+ },
83
+ { number: disabledProvidersCount }
84
+ );
85
+
86
+ return `${enabledMessage} ${disabledMessage}`;
87
+ }, [formatMessage, enabledProvidersCount, disabledProvidersCount]);
88
+
89
+ const pageTitle = formatMessage({ id: getTrad('HeaderNav.link.providers') });
90
+
91
+ const formToRender = useMemo(() => {
92
+ if (providerToEditName === 'email') {
93
+ return forms.email;
94
+ }
95
+
96
+ if (isProviderWithSubdomain) {
97
+ return forms.providersWithSubdomain;
98
+ }
99
+
100
+ return forms.providers;
101
+ }, [providerToEditName, isProviderWithSubdomain]);
102
+
103
+ const handleClick = useCallback(() => {
104
+ buttonSubmitRef.current.click();
105
+ }, []);
106
+
107
+ const handleToggle = useCallback(() => {
108
+ setIsOpen(prev => !prev);
109
+ }, []);
110
+
111
+ const handleClickEdit = useCallback(
112
+ provider => {
113
+ if (canUpdate) {
114
+ setProviderToEditName(provider.name);
115
+ handleToggle();
116
+ }
117
+ },
118
+ [canUpdate, handleToggle]
119
+ );
120
+
121
+ const handleClosed = useCallback(() => {
122
+ setProviderToEditName(null);
123
+ setShowForm(false);
124
+ dispatchResetForm();
125
+ }, [dispatchResetForm]);
126
+
127
+ const handleOpened = useCallback(() => {
128
+ setShowForm(true);
129
+ }, []);
130
+
131
+ const handleSubmit = useCallback(
132
+ async e => {
133
+ e.preventDefault();
134
+ const { schema } = formToRender;
135
+ let errors = {};
136
+
137
+ setIsSubmiting(true);
138
+
139
+ try {
140
+ await schema.validate(modifiedData[providerToEditName], { abortEarly: false });
141
+ strapi.lockAppWithOverlay();
142
+
143
+ try {
144
+ emitEventRef.current('willEditAuthenticationProvider');
145
+
146
+ await request(getRequestURL('providers'), {
147
+ method: 'PUT',
148
+ body: { providers: modifiedData },
149
+ });
150
+
151
+ emitEventRef.current('didEditAuthenticationProvider');
152
+
153
+ strapi.notification.toggle({
154
+ type: 'success',
155
+ message: { id: getTrad('notification.success.submit') },
156
+ });
157
+
158
+ dispatchSubmitSucceeded();
159
+
160
+ handleToggle();
161
+ } catch (err) {
162
+ console.error(err);
163
+ strapi.notification.toggle({
164
+ type: 'warning',
165
+ message: { id: 'notification.error' },
166
+ });
167
+ }
168
+ } catch (err) {
169
+ console.error(err);
170
+ errors = getYupInnerErrors(err);
171
+ console.log(errors);
172
+ }
173
+
174
+ dispatchSetFormErrors(errors);
175
+
176
+ setIsSubmiting(false);
177
+ strapi.unlockApp();
178
+ },
179
+ [
180
+ dispatchSetFormErrors,
181
+ dispatchSubmitSucceeded,
182
+ formToRender,
183
+ handleToggle,
184
+ modifiedData,
185
+ providerToEditName,
186
+ ]
187
+ );
188
+
189
+ return (
190
+ <>
191
+ <SettingsPageTitle name={pageTitle} />
192
+ <div>
193
+ <Header title={{ label: pageTitle }} isLoading={isLoadingForPermissions || isLoading} />
194
+ <ListBaselineAlignment />
195
+ <List
196
+ title={listTitle}
197
+ items={providers}
198
+ isLoading={isLoadingForPermissions || isLoading}
199
+ customRowComponent={provider => (
200
+ <ListRow
201
+ {...provider}
202
+ onClick={() => handleClickEdit(provider)}
203
+ links={[
204
+ {
205
+ icon: canUpdate ? <Pencil fill="#0e1622" /> : null,
206
+ onClick: e => {
207
+ e.stopPropagation();
208
+ handleClickEdit(provider);
209
+ },
210
+ },
211
+ ]}
212
+ >
213
+ <td key="enabled">
214
+ <Text
215
+ fontWeight="semiBold"
216
+ lineHeight="18px"
217
+ color={provider.enabled ? 'green' : 'lightOrange'}
218
+ >
219
+ {provider.enabled
220
+ ? formatMessage({
221
+ id: getTrad('List.row.provider.enabled'),
222
+ defaultMessage: 'Enabled',
223
+ })
224
+ : formatMessage({
225
+ id: getTrad('List.row.provider.disabled'),
226
+ defaultMessage: 'Disabled',
227
+ })}
228
+ </Text>
229
+ </td>
230
+ </ListRow>
231
+ )}
232
+ />
233
+ </div>
234
+ <ModalForm
235
+ isOpen={isOpen}
236
+ onClick={handleClick}
237
+ onCancel={handleToggle}
238
+ isLoading={isSubmiting}
239
+ onOpened={handleOpened}
240
+ onClosed={handleClosed}
241
+ onToggle={handleToggle}
242
+ headerBreadcrumbs={[
243
+ getTrad('PopUpForm.header.edit.providers'),
244
+ upperFirst(providerToEditName),
245
+ ]}
246
+ >
247
+ {showForm && (
248
+ <form onSubmit={handleSubmit}>
249
+ <Row>
250
+ {formToRender.form.map(input => {
251
+ const label = input.label.params
252
+ ? { ...input.label, params: { provider: upperFirst(providerToEditName) } }
253
+ : input.label;
254
+
255
+ const value =
256
+ input.name === 'noName'
257
+ ? `${strapi.backendURL}/connect/${providerToEditName}/callback`
258
+ : get(modifiedData, [providerToEditName, ...input.name.split('.')], '');
259
+
260
+ return (
261
+ <SizedInput
262
+ key={input.name}
263
+ {...input}
264
+ label={label}
265
+ error={formErrors[input.name]}
266
+ name={`${providerToEditName}.${input.name}`}
267
+ onChange={handleChange}
268
+ value={value}
269
+ />
270
+ );
271
+ })}
272
+ </Row>
273
+ <button type="submit" style={{ display: 'none' }} ref={buttonSubmitRef}>
274
+ hidden button to use the native form event
275
+ </button>
276
+ </form>
277
+ )}
278
+ </ModalForm>
279
+ </>
280
+ );
281
+ };
282
+
283
+ export default ProvidersPage;