@akemona-org/strapi-plugin-i18n 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 (147) 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/CMEditViewCopyLocale/index.js +183 -0
  5. package/admin/src/components/CMEditViewCopyLocale/utils/cleanData.js +36 -0
  6. package/admin/src/components/CMEditViewCopyLocale/utils/generateOptions.js +22 -0
  7. package/admin/src/components/CMEditViewCopyLocale/utils/index.js +2 -0
  8. package/admin/src/components/CMEditViewCopyLocale/utils/removePasswordAndRelationsFieldFromData.js +54 -0
  9. package/admin/src/components/CMEditViewCopyLocale/utils/tests/cleanData.test.js +83 -0
  10. package/admin/src/components/CMEditViewCopyLocale/utils/tests/data.js +219 -0
  11. package/admin/src/components/CMEditViewCopyLocale/utils/tests/generateOptions.test.js +79 -0
  12. package/admin/src/components/CMEditViewCopyLocale/utils/tests/removePasswordAndRelationsFieldFromData.test.js +40 -0
  13. package/admin/src/components/CMEditViewInjectedComponents/index.js +58 -0
  14. package/admin/src/components/CMEditViewLocalePicker/Option.js +66 -0
  15. package/admin/src/components/CMEditViewLocalePicker/Wrapper.js +8 -0
  16. package/admin/src/components/CMEditViewLocalePicker/index.js +160 -0
  17. package/admin/src/components/CMEditViewLocalePicker/utils/addStatusColorToLocale.js +24 -0
  18. package/admin/src/components/CMEditViewLocalePicker/utils/createLocalesOption.js +20 -0
  19. package/admin/src/components/CMEditViewLocalePicker/utils/index.js +2 -0
  20. package/admin/src/components/CheckboxConfirmation/Wrapper.js +12 -0
  21. package/admin/src/components/CheckboxConfirmation/index.js +70 -0
  22. package/admin/src/components/DeleteModalAdditionalInfos/index.js +25 -0
  23. package/admin/src/components/LocaleList/index.js +101 -0
  24. package/admin/src/components/LocaleListCell/LocaleListCell.js +90 -0
  25. package/admin/src/components/LocaleListCell/tests/LocaleListCell.test.js +128 -0
  26. package/admin/src/components/LocalePicker/index.js +126 -0
  27. package/admin/src/components/LocaleRow/index.js +77 -0
  28. package/admin/src/components/ModalCreate/AdvancedForm.js +45 -0
  29. package/admin/src/components/ModalCreate/BaseForm.js +103 -0
  30. package/admin/src/components/ModalCreate/index.js +136 -0
  31. package/admin/src/components/ModalDelete/index.js +49 -0
  32. package/admin/src/components/ModalEdit/AdvancedForm.js +51 -0
  33. package/admin/src/components/ModalEdit/BaseForm.js +91 -0
  34. package/admin/src/components/ModalEdit/index.js +122 -0
  35. package/admin/src/components/SettingsModal.js +66 -0
  36. package/admin/src/components/index.js +2 -0
  37. package/admin/src/containers/Initializer.js +31 -0
  38. package/admin/src/containers/SettingsPage/LocaleSettingsPage.js +69 -0
  39. package/admin/src/containers/SettingsPage/index.js +33 -0
  40. package/admin/src/containers/SettingsPage/tests/SettingsPage.test.js +744 -0
  41. package/admin/src/containers/SettingsPage/tests/__snapshots__/SettingsPage.test.js.snap +241 -0
  42. package/admin/src/hooks/constants.js +6 -0
  43. package/admin/src/hooks/reducers.js +63 -0
  44. package/admin/src/hooks/tests/reducers.test.js +203 -0
  45. package/admin/src/hooks/useAddLocale/index.js +60 -0
  46. package/admin/src/hooks/useContentTypePermissions/index.js +16 -0
  47. package/admin/src/hooks/useDefaultLocales/index.js +27 -0
  48. package/admin/src/hooks/useDeleteLocale/index.js +45 -0
  49. package/admin/src/hooks/useEditLocale/index.js +46 -0
  50. package/admin/src/hooks/useHasI18n/index.js +13 -0
  51. package/admin/src/hooks/useLocales/index.js +35 -0
  52. package/admin/src/index.js +169 -0
  53. package/admin/src/middlewares/addCommonFieldsToInitialDataMiddleware.js +83 -0
  54. package/admin/src/middlewares/addLocaleColumnToListViewMiddleware.js +32 -0
  55. package/admin/src/middlewares/addLocaleToCollectionTypesMiddleware.js +25 -0
  56. package/admin/src/middlewares/addLocaleToSingleTypesMiddleware.js +25 -0
  57. package/admin/src/middlewares/extendCMEditViewLayoutMiddleware.js +159 -0
  58. package/admin/src/middlewares/extendCTBAttributeInitialDataMiddleware.js +58 -0
  59. package/admin/src/middlewares/extendCTBInitialDataMiddleware.js +33 -0
  60. package/admin/src/middlewares/index.js +21 -0
  61. package/admin/src/middlewares/localePermissionMiddleware.js +39 -0
  62. package/admin/src/middlewares/tests/addCommonFieldsToInitialDataMiddleware.test.js +97 -0
  63. package/admin/src/middlewares/tests/addLocaleColumnToListViewMiddleware.test.js +68 -0
  64. package/admin/src/middlewares/tests/addLocaleToCollectionTypesMiddleware.test.js +200 -0
  65. package/admin/src/middlewares/tests/addLocaleToSingleTypesMiddleware.test.js +193 -0
  66. package/admin/src/middlewares/tests/extendCMEditViewLayoutMiddleware.test.js +556 -0
  67. package/admin/src/middlewares/tests/extendCTBAttrributeInitialDataMiddleware.test.js +124 -0
  68. package/admin/src/middlewares/tests/extendCTBInitialDataMiddleware.test.js +92 -0
  69. package/admin/src/middlewares/tests/localePermissionMiddleware.test.js +150 -0
  70. package/admin/src/middlewares/utils/addLocaleToLinksSearch.js +56 -0
  71. package/admin/src/middlewares/utils/tests/addLocaleToLinksSearch.test.js +137 -0
  72. package/admin/src/permissions.js +9 -0
  73. package/admin/src/pluginId.js +5 -0
  74. package/admin/src/schemas.js +7 -0
  75. package/admin/src/selectors/selectCollectionTypesRelatedPermissions.js +4 -0
  76. package/admin/src/selectors/selectI18nLocales.js +3 -0
  77. package/admin/src/translations/en.json +60 -0
  78. package/admin/src/translations/fr.json +9 -0
  79. package/admin/src/translations/index.js +11 -0
  80. package/admin/src/translations/zh-Hans.json +60 -0
  81. package/admin/src/utils/getDefaultLocale.js +60 -0
  82. package/admin/src/utils/getInitialLocale.js +14 -0
  83. package/admin/src/utils/getLocaleFromQuery.js +7 -0
  84. package/admin/src/utils/getTrad.js +5 -0
  85. package/admin/src/utils/index.js +2 -0
  86. package/admin/src/utils/localizedFields.js +23 -0
  87. package/admin/src/utils/mutateCTBContentTypeSchema.js +66 -0
  88. package/admin/src/utils/tests/getDefaultLocale.test.js +337 -0
  89. package/admin/src/utils/tests/getInitialLocale.test.js +106 -0
  90. package/admin/src/utils/tests/mutateCTBContentTypeSchema.test.js +205 -0
  91. package/config/functions/bootstrap.js +57 -0
  92. package/config/functions/migrations/__tests__/content-type.test.js +255 -0
  93. package/config/functions/migrations/__tests__/field.test.js +150 -0
  94. package/config/functions/migrations/content-type/disable/index.js +34 -0
  95. package/config/functions/migrations/content-type/disable/migrate-for-bookshelf.js +58 -0
  96. package/config/functions/migrations/content-type/disable/migrate-for-mongoose.js +39 -0
  97. package/config/functions/migrations/content-type/enable/index.js +40 -0
  98. package/config/functions/migrations/content-type/utils/index.js +27 -0
  99. package/config/functions/migrations/field/__tests__/utils.test.js +53 -0
  100. package/config/functions/migrations/field/index.js +37 -0
  101. package/config/functions/migrations/field/migrate-for-bookshelf.js +72 -0
  102. package/config/functions/migrations/field/migrate-for-mongoose.js +24 -0
  103. package/config/functions/migrations/field/migrate.js +55 -0
  104. package/config/functions/migrations/field/utils.js +58 -0
  105. package/config/functions/register.js +46 -0
  106. package/config/policies/validateLocaleCreation.js +68 -0
  107. package/config/routes.json +64 -0
  108. package/constants/__tests__/index.test.js +27 -0
  109. package/constants/index.js +36 -0
  110. package/constants/iso-locales.json +2002 -0
  111. package/controllers/__tests__/content-types.test.js +113 -0
  112. package/controllers/__tests__/iso-locales.test.js +26 -0
  113. package/controllers/__tests__/locales.test.js +308 -0
  114. package/controllers/content-types.js +64 -0
  115. package/controllers/iso-locales.js +11 -0
  116. package/controllers/locales.js +104 -0
  117. package/domain/locale.js +10 -0
  118. package/middlewares/i18n/defaults.json +5 -0
  119. package/middlewares/i18n/index.js +28 -0
  120. package/models/Locale.settings.json +31 -0
  121. package/oas.yml +195 -0
  122. package/package.json +31 -0
  123. package/services/__tests__/__snapshots__/iso-locales.test.js.snap +2006 -0
  124. package/services/__tests__/content-types.test.js +545 -0
  125. package/services/__tests__/core-api.test.js +106 -0
  126. package/services/__tests__/entity-service-decorator.test.js +280 -0
  127. package/services/__tests__/iso-locales.test.js +11 -0
  128. package/services/__tests__/locales.test.js +237 -0
  129. package/services/__tests__/localizations.test.js +187 -0
  130. package/services/__tests__/metrics.test.js +90 -0
  131. package/services/content-types.js +200 -0
  132. package/services/core-api.js +296 -0
  133. package/services/entity-service-decorator.js +155 -0
  134. package/services/iso-locales.js +9 -0
  135. package/services/locales.js +97 -0
  136. package/services/localizations.js +65 -0
  137. package/services/metrics.js +24 -0
  138. package/services/permissions/actions.js +124 -0
  139. package/services/permissions/engine.js +63 -0
  140. package/services/permissions/sections-builder.js +48 -0
  141. package/services/permissions.js +11 -0
  142. package/tests/content-manager/list-relation.test.e2e.js +122 -0
  143. package/tests/graphql.test.e2e.js +120 -0
  144. package/tests/locales.test.e2e.js +414 -0
  145. package/utils/index.js +20 -0
  146. package/validation/content-types.js +30 -0
  147. package/validation/locales.js +39 -0
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Padded, Text } from '@buffetjs/core';
4
+ import { Tooltip } from '@buffetjs/styles';
5
+ import get from 'lodash/get';
6
+ import styled from 'styled-components';
7
+
8
+ const mapToLocaleName = (locales, localeCode) =>
9
+ get(
10
+ locales.find(({ code }) => code === localeCode),
11
+ 'name',
12
+ localeCode
13
+ );
14
+
15
+ const LocaleName = styled.div`
16
+ text-overflow: ellipsis;
17
+ overflow: hidden;
18
+ white-space: nowrap;
19
+ `;
20
+
21
+ const LocaleListCell = ({ locales, localizations, locale: currentLocaleCode, id }) => {
22
+ const allLocalizations = [{ locale: currentLocaleCode }, ...localizations];
23
+ const localizationNames = allLocalizations.map(locale => locale.locale);
24
+ const defaultLocale = locales.find(locale => locale.isDefault);
25
+ const hasDefaultLocale = localizationNames.includes(defaultLocale.code);
26
+
27
+ let localesArray = [];
28
+
29
+ if (hasDefaultLocale) {
30
+ const ctLocalesWithoutDefault = localizationNames.filter(
31
+ locale => locale !== defaultLocale.code
32
+ );
33
+ const ctLocalesNamesWithoutDefault = ctLocalesWithoutDefault.map(locale =>
34
+ mapToLocaleName(locales, locale)
35
+ );
36
+
37
+ ctLocalesNamesWithoutDefault.sort();
38
+
39
+ const ctLocalesNamesWithDefault = [
40
+ `${defaultLocale.name} (default)`,
41
+ ...ctLocalesNamesWithoutDefault,
42
+ ];
43
+
44
+ localesArray = ctLocalesNamesWithDefault;
45
+ } else {
46
+ const ctLocales = localizationNames.map(locale => mapToLocaleName(locales, locale));
47
+ ctLocales.sort();
48
+
49
+ localesArray = ctLocales;
50
+ }
51
+
52
+ const elId = `entry-${id}__locale`;
53
+ const localesNames = localesArray.join(', ');
54
+
55
+ return (
56
+ <div>
57
+ <LocaleName data-for={elId} data-tip={localesNames}>
58
+ {localesNames}
59
+ </LocaleName>
60
+ <Tooltip id={elId} place="bottom" delay={0}>
61
+ {localesArray.map(name => (
62
+ <Padded key={name} top bottom size="xs">
63
+ <Text ellipsis color="white">
64
+ {name}
65
+ </Text>
66
+ </Padded>
67
+ ))}
68
+ </Tooltip>
69
+ </div>
70
+ );
71
+ };
72
+
73
+ LocaleListCell.propTypes = {
74
+ id: PropTypes.number.isRequired,
75
+ localizations: PropTypes.arrayOf(
76
+ PropTypes.shape({
77
+ locale: PropTypes.string.isRequired,
78
+ })
79
+ ).isRequired,
80
+ locales: PropTypes.arrayOf(
81
+ PropTypes.shape({
82
+ name: PropTypes.string.isRequired,
83
+ code: PropTypes.string.isRequired,
84
+ isDefault: PropTypes.bool,
85
+ })
86
+ ).isRequired,
87
+ locale: PropTypes.string.isRequired,
88
+ };
89
+
90
+ export default LocaleListCell;
@@ -0,0 +1,128 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import LocaleListCell from '../LocaleListCell';
4
+
5
+ jest.mock('@buffetjs/styles', () => ({
6
+ Tooltip: () => null,
7
+ }));
8
+
9
+ jest.mock('@buffetjs/core', () => ({
10
+ Padded: props => <div {...props} />,
11
+ Text: props => <p {...props} />,
12
+ }));
13
+
14
+ describe('LocaleListCell', () => {
15
+ it('returns the default locale first, then the others sorted alphabetically', () => {
16
+ const locales = [
17
+ {
18
+ id: 1,
19
+ name: 'English',
20
+ code: 'en',
21
+ created_at: '2021-03-09T14:57:03.016Z',
22
+ updated_at: '2021-03-09T14:57:03.016Z',
23
+ isDefault: false,
24
+ },
25
+ {
26
+ id: 2,
27
+ name: 'French',
28
+ code: 'fr-FR',
29
+ created_at: '2021-03-09T15:03:06.992Z',
30
+ updated_at: '2021-03-17T13:01:03.569Z',
31
+ isDefault: true,
32
+ },
33
+ {
34
+ id: 3,
35
+ name: 'Arabic',
36
+ code: 'ar',
37
+ created_at: '2021-03-09T15:03:06.992Z',
38
+ updated_at: '2021-03-17T13:01:03.569Z',
39
+ isDefault: false,
40
+ },
41
+ ];
42
+
43
+ const locale = 'en';
44
+ const localizations = [{ locale: 'fr-FR' }, { locale: 'ar' }];
45
+
46
+ render(
47
+ <LocaleListCell id={12} locales={locales} locale={locale} localizations={localizations} />
48
+ );
49
+
50
+ expect(screen.getByText('French (default), Arabic, English')).toBeVisible();
51
+ });
52
+
53
+ it('returns the "ar" when there s 2 locales available', () => {
54
+ const locales = [
55
+ {
56
+ id: 1,
57
+ name: 'English',
58
+ code: 'en',
59
+ created_at: '2021-03-09T14:57:03.016Z',
60
+ updated_at: '2021-03-09T14:57:03.016Z',
61
+ isDefault: false,
62
+ },
63
+ {
64
+ id: 2,
65
+ name: 'French',
66
+ code: 'fr-FR',
67
+ created_at: '2021-03-09T15:03:06.992Z',
68
+ updated_at: '2021-03-17T13:01:03.569Z',
69
+ isDefault: true,
70
+ },
71
+ {
72
+ id: 3,
73
+ name: 'Arabic',
74
+ code: 'ar',
75
+ created_at: '2021-03-09T15:03:06.992Z',
76
+ updated_at: '2021-03-17T13:01:03.569Z',
77
+ isDefault: false,
78
+ },
79
+ ];
80
+
81
+ const locale = 'en';
82
+ const localizations = [{ locale: 'ar' }];
83
+
84
+ render(
85
+ <LocaleListCell id={12} locales={locales} locale={locale} localizations={localizations} />
86
+ );
87
+
88
+ expect(screen.getByText('Arabic, English')).toBeVisible();
89
+ });
90
+
91
+ it('returns the "ar" and "en" locales alphabetically sorted', () => {
92
+ const locales = [
93
+ {
94
+ id: 1,
95
+ name: 'English',
96
+ code: 'en',
97
+ created_at: '2021-03-09T14:57:03.016Z',
98
+ updated_at: '2021-03-09T14:57:03.016Z',
99
+ isDefault: false,
100
+ },
101
+ {
102
+ id: 2,
103
+ name: 'French',
104
+ code: 'fr-FR',
105
+ created_at: '2021-03-09T15:03:06.992Z',
106
+ updated_at: '2021-03-17T13:01:03.569Z',
107
+ isDefault: true,
108
+ },
109
+ {
110
+ id: 3,
111
+ name: 'Arabic',
112
+ code: 'ar',
113
+ created_at: '2021-03-09T15:03:06.992Z',
114
+ updated_at: '2021-03-17T13:01:03.569Z',
115
+ isDefault: false,
116
+ },
117
+ ];
118
+
119
+ const locale = 'fr-FR';
120
+ const localizations = [{ locale: 'en' }, { locale: 'ar' }];
121
+
122
+ render(
123
+ <LocaleListCell id={12} locales={locales} locale={locale} localizations={localizations} />
124
+ );
125
+
126
+ expect(screen.getByText('French (default), Arabic, English')).toBeVisible();
127
+ });
128
+ });
@@ -0,0 +1,126 @@
1
+ import React, { useState } from 'react';
2
+ import { useSelector, useDispatch } from 'react-redux';
3
+ import { Picker, Padded, Text, Flex } from '@buffetjs/core';
4
+ import { Carret, useQueryParams } from 'strapi-helper-plugin';
5
+ import { useRouteMatch } from 'react-router-dom';
6
+ import styled from 'styled-components';
7
+ import get from 'lodash/get';
8
+ import useContentTypePermissions from '../../hooks/useContentTypePermissions';
9
+ import useHasI18n from '../../hooks/useHasI18n';
10
+ import selectI18NLocales from '../../selectors/selectI18nLocales';
11
+ import getInitialLocale from '../../utils/getInitialLocale';
12
+
13
+ const List = styled.ul`
14
+ list-style-type: none;
15
+ padding: 3px 0;
16
+ margin: 0;
17
+ `;
18
+
19
+ const ListItem = styled.li`
20
+ margin-top: 0;
21
+ margin-bottom: 0;
22
+ margin-left: -10px;
23
+ margin-right: -10px;
24
+ padding-left: 10px;
25
+ padding-right: 10px;
26
+ height: 36px;
27
+ display: flex;
28
+ justify-content: center;
29
+
30
+ &:hover {
31
+ background: ${props => props.theme.main.colors.mediumGrey};
32
+ }
33
+ `;
34
+
35
+ const EllipsisParagraph = styled(Text)`
36
+ width: ${props => props.width};
37
+ text-overflow: ellipsis;
38
+ overflow: hidden;
39
+ white-space: nowrap;
40
+ text-align: left;
41
+ `;
42
+
43
+ const LocalePicker = () => {
44
+ const dispatch = useDispatch();
45
+ const locales = useSelector(selectI18NLocales);
46
+ const [{ query }, setQuery] = useQueryParams();
47
+ const {
48
+ params: { slug },
49
+ } = useRouteMatch('/plugins/content-manager/collectionType/:slug');
50
+ const isFieldLocalized = useHasI18n();
51
+ const { createPermissions, readPermissions } = useContentTypePermissions(slug);
52
+
53
+ const initialLocale = getInitialLocale(query, locales);
54
+ const [selected, setSelected] = useState(initialLocale);
55
+
56
+ if (!isFieldLocalized) {
57
+ return null;
58
+ }
59
+
60
+ if (!locales || locales.length === 0) {
61
+ return null;
62
+ }
63
+
64
+ const displayedLocales = locales.filter(locale => {
65
+ const canCreate = createPermissions.find(({ properties }) => {
66
+ return get(properties, 'locales', []).includes(locale.code);
67
+ });
68
+ const canRead = readPermissions.find(({ properties }) =>
69
+ get(properties, 'locales', []).includes(locale.code)
70
+ );
71
+
72
+ return canCreate || canRead;
73
+ });
74
+
75
+ return (
76
+ <Picker
77
+ position="right"
78
+ renderButtonContent={isOpen => (
79
+ <Flex>
80
+ <EllipsisParagraph width="20ch">{selected.name}</EllipsisParagraph>
81
+
82
+ <Padded left size="sm">
83
+ <Carret fill={isOpen ? '#007eff' : '#292b2c'} isUp={isOpen} />
84
+ </Padded>
85
+ </Flex>
86
+ )}
87
+ renderSectionContent={onToggle => {
88
+ const handleClick = locale => {
89
+ dispatch({ type: 'ContentManager/RBACManager/RESET_PERMISSIONS' });
90
+ setSelected(locale);
91
+
92
+ setQuery({
93
+ plugins: { ...query.plugins, i18n: { locale: locale.code } },
94
+ });
95
+ onToggle();
96
+ };
97
+
98
+ const hasMultipleLocales = displayedLocales.length > 1;
99
+
100
+ return hasMultipleLocales ? (
101
+ <Padded left right>
102
+ <List>
103
+ {displayedLocales.map(locale => {
104
+ if (locale.id === selected.id) {
105
+ return null;
106
+ }
107
+
108
+ return (
109
+ <ListItem key={locale.id}>
110
+ <button onClick={() => handleClick(locale)} type="button">
111
+ <EllipsisParagraph width="200px">
112
+ {locale.name || locale.code}
113
+ </EllipsisParagraph>
114
+ </button>
115
+ </ListItem>
116
+ );
117
+ })}
118
+ </List>
119
+ </Padded>
120
+ ) : null;
121
+ }}
122
+ />
123
+ );
124
+ };
125
+
126
+ export default LocalePicker;
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { Pencil } from '@buffetjs/icons';
5
+ import { Text, IconLinks } from '@buffetjs/core';
6
+ import { CustomRow } from '@buffetjs/styles';
7
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
+ import { getTrad } from '../../utils';
9
+
10
+ const LocaleSettingsPage = ({ locale, onDelete, onEdit }) => {
11
+ const { formatMessage } = useIntl();
12
+
13
+ const links = [];
14
+
15
+ if (onEdit) {
16
+ links.push({
17
+ icon: (
18
+ <span aria-label={formatMessage({ id: getTrad('Settings.list.actions.edit') })}>
19
+ <Pencil fill="#0e1622" />
20
+ </span>
21
+ ),
22
+ onClick: () => onEdit(locale),
23
+ });
24
+ }
25
+
26
+ if (onDelete && !locale.isDefault) {
27
+ links.push({
28
+ icon: !locale.isDefault ? (
29
+ <span aria-label={formatMessage({ id: getTrad('Settings.list.actions.delete') })}>
30
+ <FontAwesomeIcon icon="trash-alt" />
31
+ </span>
32
+ ) : null,
33
+ onClick: e => {
34
+ e.stopPropagation();
35
+ onDelete(locale);
36
+ },
37
+ });
38
+ }
39
+
40
+ return (
41
+ <CustomRow onClick={() => onEdit(locale)}>
42
+ <td>
43
+ <Text>{locale.code}</Text>
44
+ </td>
45
+ <td>
46
+ <Text fontWeight="regular">{locale.name}</Text>
47
+ </td>
48
+ <td>
49
+ <Text>
50
+ {locale.isDefault
51
+ ? formatMessage({ id: getTrad('Settings.locales.row.default-locale') })
52
+ : null}
53
+ </Text>
54
+ </td>
55
+ <td>
56
+ <IconLinks links={links} />
57
+ </td>
58
+ </CustomRow>
59
+ );
60
+ };
61
+
62
+ LocaleSettingsPage.defaultProps = {
63
+ onDelete: undefined,
64
+ onEdit: undefined,
65
+ };
66
+
67
+ LocaleSettingsPage.propTypes = {
68
+ locale: PropTypes.shape({
69
+ isDefault: PropTypes.bool,
70
+ name: PropTypes.string,
71
+ code: PropTypes.string.isRequired,
72
+ }).isRequired,
73
+ onDelete: PropTypes.func,
74
+ onEdit: PropTypes.func,
75
+ };
76
+
77
+ export default LocaleSettingsPage;
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { Text, Checkbox, Padded } from '@buffetjs/core';
3
+ import { useFormikContext } from 'formik';
4
+ import { useIntl } from 'react-intl';
5
+ import { BaselineAlignment } from 'strapi-helper-plugin';
6
+ import { getTrad } from '../../utils';
7
+
8
+ const AdvancedForm = () => {
9
+ const { values, setFieldValue } = useFormikContext();
10
+ const { formatMessage } = useIntl();
11
+
12
+ return (
13
+ <div>
14
+ <BaselineAlignment top size="2px" />
15
+ <Padded bottom size="sm">
16
+ <Text color="grey" textTransform="uppercase">
17
+ {formatMessage({
18
+ id: getTrad('Settings.locales.modal.advanced.settings'),
19
+ })}
20
+ </Text>
21
+ </Padded>
22
+
23
+ <BaselineAlignment top size="10px" />
24
+ <Checkbox
25
+ id="default-checkbox"
26
+ name="default-checkbox"
27
+ onChange={() => setFieldValue('isDefault', !values.isDefault)}
28
+ message={formatMessage({
29
+ id: getTrad('Settings.locales.modal.advanced.setAsDefault'),
30
+ })}
31
+ someChecked={false}
32
+ value={values.isDefault}
33
+ htmlFor="default-checkbox"
34
+ />
35
+
36
+ <Text color="grey" fontSize="sm">
37
+ {formatMessage({
38
+ id: getTrad('Settings.locales.modal.advanced.setAsDefault.hint'),
39
+ })}
40
+ </Text>
41
+ </div>
42
+ );
43
+ };
44
+
45
+ export default AdvancedForm;
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Label } from '@buffetjs/core';
4
+ import { Inputs } from '@buffetjs/custom';
5
+ import Select, { createFilter } from 'react-select';
6
+ import { Col, Row } from 'reactstrap';
7
+ import { useIntl } from 'react-intl';
8
+ import { useTheme } from 'styled-components';
9
+ import { BaselineAlignment, selectStyles, DropdownIndicator } from 'strapi-helper-plugin';
10
+ import { useFormikContext } from 'formik';
11
+ import { getTrad } from '../../utils';
12
+
13
+ const reactSelectLocaleFilter = createFilter({
14
+ ignoreCase: true,
15
+ ignoreAccents: true,
16
+ matchFrom: 'start',
17
+ });
18
+
19
+ const BaseForm = ({ options, defaultOption }) => {
20
+ const theme = useTheme();
21
+ const { formatMessage } = useIntl();
22
+ const { values, handleChange, setFieldValue } = useFormikContext();
23
+
24
+ const styles = selectStyles(theme);
25
+
26
+ return (
27
+ <Row>
28
+ <Col>
29
+ <span id="locale-code">
30
+ <Label htmlFor="">
31
+ {formatMessage({
32
+ id: getTrad('Settings.locales.modal.locales.label'),
33
+ })}
34
+ </Label>
35
+ </span>
36
+
37
+ <BaselineAlignment top size="5px" />
38
+
39
+ <Select
40
+ aria-labelledby="locale-code"
41
+ options={options}
42
+ defaultValue={defaultOption}
43
+ filterOption={reactSelectLocaleFilter}
44
+ onChange={selection => {
45
+ setFieldValue('displayName', selection.value);
46
+ setFieldValue('code', selection.label);
47
+ }}
48
+ components={{ DropdownIndicator }}
49
+ styles={{
50
+ ...styles,
51
+ control: (base, state) => ({ ...base, ...styles.control(base, state), height: '34px' }),
52
+ indicatorsContainer: (base, state) => ({
53
+ ...base,
54
+ ...styles.indicatorsContainer(base, state),
55
+ height: '32px',
56
+ }),
57
+ }}
58
+ />
59
+ </Col>
60
+
61
+ <Col>
62
+ <BaselineAlignment top size="2px" />
63
+
64
+ <Inputs
65
+ label={formatMessage({
66
+ id: getTrad('Settings.locales.modal.locales.displayName'),
67
+ })}
68
+ name="displayName"
69
+ description={formatMessage({
70
+ id: getTrad('Settings.locales.modal.locales.displayName.description'),
71
+ })}
72
+ type="text"
73
+ value={values.displayName}
74
+ onChange={handleChange}
75
+ validations={{
76
+ max: 50,
77
+ }}
78
+ translatedErrors={{
79
+ max: formatMessage({
80
+ id: getTrad('Settings.locales.modal.locales.displayName.error'),
81
+ }),
82
+ }}
83
+ />
84
+ </Col>
85
+ </Row>
86
+ );
87
+ };
88
+
89
+ BaseForm.defaultProps = {
90
+ defaultOption: undefined,
91
+ };
92
+
93
+ BaseForm.propTypes = {
94
+ options: PropTypes.arrayOf(
95
+ PropTypes.exact({ value: PropTypes.string.isRequired, label: PropTypes.string.isRequired })
96
+ ).isRequired,
97
+ defaultOption: PropTypes.exact({
98
+ value: PropTypes.string.isRequired,
99
+ label: PropTypes.string.isRequired,
100
+ }),
101
+ };
102
+
103
+ export default BaseForm;
@@ -0,0 +1,136 @@
1
+ import React, { useRef } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Modal, ModalFooter, TabPanel, useUser } from 'strapi-helper-plugin';
4
+ import { useIntl } from 'react-intl';
5
+ import { Button } from '@buffetjs/core';
6
+ import { Formik } from 'formik';
7
+ import localeFormSchema from '../../schemas';
8
+ import { getTrad } from '../../utils';
9
+ import SettingsModal from '../SettingsModal';
10
+ import useDefaultLocales from '../../hooks/useDefaultLocales';
11
+ import useAddLocale from '../../hooks/useAddLocale';
12
+ import BaseForm from './BaseForm';
13
+ import AdvancedForm from './AdvancedForm';
14
+
15
+ const ModalCreate = ({ alreadyUsedLocales, onClose, isOpened }) => {
16
+ const { defaultLocales, isLoading } = useDefaultLocales();
17
+ const { isAdding, addLocale } = useAddLocale();
18
+ const { formatMessage } = useIntl();
19
+
20
+ const { fetchUserPermissions } = useUser();
21
+ const shouldUpdatePermissions = useRef(false);
22
+
23
+ if (isLoading) {
24
+ return (
25
+ <div>
26
+ <p>
27
+ {formatMessage({ id: getTrad('Settings.locales.modal.create.defaultLocales.loading') })}
28
+ </p>
29
+ </div>
30
+ );
31
+ }
32
+
33
+ const handleClosed = async () => {
34
+ if (shouldUpdatePermissions.current) {
35
+ await fetchUserPermissions();
36
+ }
37
+
38
+ shouldUpdatePermissions.current = true;
39
+ };
40
+
41
+ const options = (defaultLocales || [])
42
+ .map(locale => ({
43
+ label: locale.code,
44
+ value: locale.name,
45
+ }))
46
+ .filter(({ label }) => {
47
+ const foundLocale = alreadyUsedLocales.find(({ code }) => code === label);
48
+
49
+ return !foundLocale;
50
+ });
51
+
52
+ const defaultOption = options[0];
53
+
54
+ if (!defaultOption) {
55
+ return null;
56
+ }
57
+
58
+ return (
59
+ <Modal isOpen={isOpened} onToggle={onClose} withoverflow="true" onClosed={handleClosed}>
60
+ <Formik
61
+ initialValues={{
62
+ code: defaultOption.label,
63
+ displayName: defaultOption.value,
64
+ isDefault: false,
65
+ }}
66
+ onSubmit={values =>
67
+ addLocale({
68
+ code: values.code,
69
+ name: values.displayName,
70
+ isDefault: values.isDefault,
71
+ })
72
+ .then(() => {
73
+ shouldUpdatePermissions.current = true;
74
+ })
75
+ .then(() => {
76
+ onClose();
77
+ })}
78
+ validationSchema={localeFormSchema}
79
+ >
80
+ {({ handleSubmit, errors }) => (
81
+ <form onSubmit={handleSubmit}>
82
+ <SettingsModal
83
+ title={formatMessage({
84
+ id: getTrad('Settings.locales.modal.title'),
85
+ })}
86
+ breadCrumb={[formatMessage({ id: getTrad('Settings.list.actions.add') })]}
87
+ tabsAriaLabel={formatMessage({
88
+ id: getTrad('Settings.locales.modal.create.tab.label'),
89
+ })}
90
+ tabsId="i18n-settings-tabs-create"
91
+ >
92
+ <TabPanel>
93
+ <BaseForm
94
+ options={options}
95
+ defaultOption={defaultOption}
96
+ alreadyUsedLocales={alreadyUsedLocales}
97
+ />
98
+ </TabPanel>
99
+ <TabPanel>
100
+ <AdvancedForm />
101
+ </TabPanel>
102
+ </SettingsModal>
103
+
104
+ <ModalFooter>
105
+ <section>
106
+ <Button type="button" color="cancel" onClick={onClose}>
107
+ {formatMessage({ id: 'app.components.Button.cancel' })}
108
+ </Button>
109
+ <Button
110
+ color="success"
111
+ type="submit"
112
+ isLoading={isAdding}
113
+ disabled={Object.keys(errors).length > 0}
114
+ >
115
+ {formatMessage({ id: getTrad('Settings.locales.modal.create.confirmation') })}
116
+ </Button>
117
+ </section>
118
+ </ModalFooter>
119
+ </form>
120
+ )}
121
+ </Formik>
122
+ </Modal>
123
+ );
124
+ };
125
+
126
+ ModalCreate.defaultProps = {
127
+ alreadyUsedLocales: [],
128
+ };
129
+
130
+ ModalCreate.propTypes = {
131
+ alreadyUsedLocales: PropTypes.array,
132
+ onClose: PropTypes.func.isRequired,
133
+ isOpened: PropTypes.bool.isRequired,
134
+ };
135
+
136
+ export default ModalCreate;