@strapi/admin 4.2.0-beta.3 → 4.3.0-beta.1

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/ConfigurationsProvider/index.js +51 -0
  2. package/admin/src/components/ConfigurationsProvider/reducer.js +28 -0
  3. package/admin/src/components/LeftMenu/index.js +12 -2
  4. package/admin/src/components/Providers/index.js +8 -4
  5. package/admin/src/components/UnauthenticatedLogo/index.js +4 -2
  6. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +3 -3
  7. package/admin/src/content-manager/components/SelectMany/index.js +2 -4
  8. package/admin/src/content-manager/components/SelectWrapper/index.js +1 -13
  9. package/admin/src/hooks/useFetchMarketplaceProviders/index.js +23 -0
  10. package/admin/src/hooks/useFetchMarketplaceProviders/utils/api.js +11 -0
  11. package/admin/src/pages/App/index.js +7 -2
  12. package/admin/src/pages/MarketplacePage/components/{EmptyPluginSearch/EmptyPluginGrid.js → EmptyNpmPackageSearch/EmptyNpmPackageGrid.js} +1 -1
  13. package/admin/src/pages/MarketplacePage/components/{EmptyPluginSearch → EmptyNpmPackageSearch}/index.js +6 -4
  14. package/admin/src/pages/MarketplacePage/components/{PluginCard → NpmPackageCard}/InstallPluginButton.js +0 -0
  15. package/admin/src/pages/MarketplacePage/components/{PluginCard → NpmPackageCard}/index.js +22 -10
  16. package/admin/src/pages/MarketplacePage/components/NpmPackagesGrid/index.js +42 -0
  17. package/admin/src/pages/MarketplacePage/components/PageHeader/index.js +12 -5
  18. package/admin/src/pages/MarketplacePage/index.js +99 -37
  19. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/Form/index.js +85 -0
  20. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/Form/init.js +13 -0
  21. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/Form/reducer.js +43 -0
  22. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoInput/index.js +118 -0
  23. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoInput/reducer.js +28 -0
  24. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoInput/stepper.js +25 -0
  25. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/AddLogoDialog.js +67 -0
  26. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/FromComputerForm.js +176 -0
  27. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/FromUrlForm.js +86 -0
  28. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/ImageCardAsset.js +51 -0
  29. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/PendingLogoDialog.js +97 -0
  30. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/index.js +85 -0
  31. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/reducer.js +28 -0
  32. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +153 -91
  33. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js +16 -0
  34. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/constants.js +3 -0
  35. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/getFormData.js +17 -0
  36. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/parseFileMetadatas.js +76 -0
  37. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/prefixAllUrls.js +17 -0
  38. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/urlToFile.js +21 -0
  39. package/admin/src/translations/en.json +33 -3
  40. package/admin/src/translations/pl.json +264 -12
  41. package/admin/src/translations/vi.json +17 -17
  42. package/admin/src/tsconfig.json +1 -1
  43. package/build/1820.ca954075.chunk.js +503 -0
  44. package/build/1856.6a94980b.chunk.js +172 -0
  45. package/build/20.9e5a98b6.chunk.js +308 -0
  46. package/build/2077.5456ccd1.chunk.js +194 -0
  47. package/build/3531.231e0eb6.chunk.js +10 -0
  48. package/build/{4073.41ac1235.chunk.js → 4073.e144a91a.chunk.js} +1 -1
  49. package/build/413.d5986568.chunk.js +284 -0
  50. package/build/4715.4588fdf5.chunk.js +385 -0
  51. package/build/{210.014495c1.chunk.js → 472.d43be8bb.chunk.js} +58 -58
  52. package/build/4800.d3ebc81d.chunk.js +1 -0
  53. package/build/5250.24040688.chunk.js +11 -0
  54. package/build/6229.53b33cf0.chunk.js +194 -0
  55. package/build/7418.6db737ce.chunk.js +112 -0
  56. package/build/8773.54a26ded.chunk.js +327 -0
  57. package/build/9066.2847fdff.chunk.js +101 -0
  58. package/build/9262.25aa12a5.chunk.js +1 -0
  59. package/build/9420.ba035f29.chunk.js +508 -0
  60. package/build/Admin-authenticatedApp.52f3c150.chunk.js +80 -0
  61. package/build/{Admin_homePage.f157e33e.chunk.js → Admin_homePage.0ac648e8.chunk.js} +2 -2
  62. package/build/Admin_marketplace.71c66b49.chunk.js +11 -0
  63. package/build/{Admin_profilePage.62c203ad.chunk.js → Admin_profilePage.27191ed2.chunk.js} +1 -1
  64. package/build/Admin_settingsPage.021da806.chunk.js +178 -0
  65. package/build/{admin-edit-roles-page.94e1403b.chunk.js → admin-edit-roles-page.fb374555.chunk.js} +1 -1
  66. package/build/admin-edit-users.a360deaf.chunk.js +10 -0
  67. package/build/admin-users.47d06d24.chunk.js +11 -0
  68. package/build/api-tokens-create-page.698f132d.chunk.js +1 -0
  69. package/build/api-tokens-edit-page.afece2fe.chunk.js +1 -0
  70. package/build/{api-tokens-list-page.340750a6.chunk.js → api-tokens-list-page.46d96dee.chunk.js} +1 -1
  71. package/build/content-manager.afcc3624.chunk.js +1182 -0
  72. package/build/email-settings-page.8871dfe8.chunk.js +103 -0
  73. package/build/en-json.0a5ba154.chunk.js +1 -0
  74. package/build/i18n-settings-page.dfb4eb01.chunk.js +101 -0
  75. package/build/index.html +1 -1
  76. package/build/main.ca3856ed.js +8640 -0
  77. package/build/pl-json.f65302c2.chunk.js +1 -0
  78. package/build/{runtime~main.e7611418.js → runtime~main.48a46dd2.js} +1 -1
  79. package/build/{sso-settings-page.e9034e22.chunk.js → sso-settings-page.dfb0b917.chunk.js} +1 -1
  80. package/build/upload-settings.8d8c672a.chunk.js +101 -0
  81. package/build/upload-translation-de-json.745613c0.chunk.js +1 -0
  82. package/build/upload-translation-dk-json.cb25dcf0.chunk.js +1 -0
  83. package/build/upload-translation-en-json.e78688a0.chunk.js +1 -0
  84. package/build/upload-translation-es-json.1f344b53.chunk.js +1 -0
  85. package/build/upload-translation-fr-json.e21c0c7a.chunk.js +1 -0
  86. package/build/upload-translation-he-json.4ce77b7b.chunk.js +1 -0
  87. package/build/upload-translation-it-json.5ce11e0b.chunk.js +1 -0
  88. package/build/upload-translation-ja-json.22afae44.chunk.js +1 -0
  89. package/build/upload-translation-ko-json.9a2c21cb.chunk.js +1 -0
  90. package/build/upload-translation-ms-json.0605d6da.chunk.js +1 -0
  91. package/build/upload-translation-pl-json.e534b676.chunk.js +1 -0
  92. package/build/upload-translation-pt-BR-json.95686cfb.chunk.js +1 -0
  93. package/build/upload-translation-ru-json.37bd1546.chunk.js +1 -0
  94. package/build/upload-translation-sk-json.b03d4904.chunk.js +1 -0
  95. package/build/upload-translation-th-json.64dd70ce.chunk.js +1 -0
  96. package/build/upload-translation-uk-json.1328cb3e.chunk.js +1 -0
  97. package/build/{upload-translation-zh-Hans-json.c9622577.chunk.js → upload-translation-zh-Hans-json.6832ff81.chunk.js} +1 -1
  98. package/build/upload-translation-zh-json.ee8fba96.chunk.js +1 -0
  99. package/build/upload.5622b777.chunk.js +7 -0
  100. package/build/users-advanced-settings-page.4af9e241.chunk.js +101 -0
  101. package/build/users-email-settings-page.d69c0d87.chunk.js +101 -0
  102. package/build/users-providers-settings-page.fc1fda6d.chunk.js +101 -0
  103. package/build/vi-json.bf3424be.chunk.js +1 -0
  104. package/build/webhook-edit-page.a7ae6e3b.chunk.js +23 -0
  105. package/build/webhook-list-page.057f396c.chunk.js +133 -0
  106. package/index.js +2 -8
  107. package/package.json +10 -9
  108. package/scripts/build.js +3 -1
  109. package/server/config/admin-actions.js +14 -0
  110. package/server/controllers/admin.js +40 -2
  111. package/server/routes/admin.js +28 -1
  112. package/server/services/index.js +1 -0
  113. package/server/services/project-settings.js +173 -0
  114. package/server/utils/index.d.ts +2 -0
  115. package/server/validation/project-settings.js +39 -0
  116. package/utils/create-cache-dir.js +6 -36
  117. package/utils/get-custom-app-config-file.js +7 -2
  118. package/utils/get-plugins-path.js +1 -1
  119. package/admin/src/content-manager/components/SelectWrapper/ClearIndicator.js +0 -18
  120. package/admin/src/content-manager/components/SelectWrapper/DropdownIndicator.js +0 -24
  121. package/admin/src/content-manager/components/SelectWrapper/IconBox.js +0 -20
  122. package/admin/src/content-manager/components/SelectWrapper/IndicatorSeparator.js +0 -3
  123. package/admin/src/content-manager/components/SelectWrapper/utils/getSelectStyles.js +0 -92
  124. package/build/1709.ceed0e18.chunk.js +0 -503
  125. package/build/1856.521a99fd.chunk.js +0 -172
  126. package/build/20.cf744c35.chunk.js +0 -308
  127. package/build/2077.51485bfb.chunk.js +0 -194
  128. package/build/2135.95ee6de1.chunk.js +0 -162
  129. package/build/2524.688d0355.chunk.js +0 -1
  130. package/build/2912.79c2b3c8.chunk.js +0 -253
  131. package/build/4715.77e04177.chunk.js +0 -385
  132. package/build/4761.3eabdf46.chunk.js +0 -101
  133. package/build/6229.a5cca9f2.chunk.js +0 -194
  134. package/build/6281.f10a7e3a.chunk.js +0 -1
  135. package/build/7009.79fce86d.chunk.js +0 -164
  136. package/build/7191.3bde3cbf.chunk.js +0 -335
  137. package/build/7841.f0e7d629.chunk.js +0 -253
  138. package/build/7863.bc7a8f3a.chunk.js +0 -112
  139. package/build/9420.cb0b75e8.chunk.js +0 -508
  140. package/build/Admin-authenticatedApp.4ce8d292.chunk.js +0 -80
  141. package/build/Admin_marketplace.1e3393c9.chunk.js +0 -11
  142. package/build/Admin_settingsPage.924a7816.chunk.js +0 -170
  143. package/build/admin-edit-users.6c2bf718.chunk.js +0 -10
  144. package/build/admin-users.e03db115.chunk.js +0 -11
  145. package/build/api-tokens-create-page.787ab302.chunk.js +0 -1
  146. package/build/api-tokens-edit-page.e4010c0c.chunk.js +0 -1
  147. package/build/content-manager.6cdcfb6e.chunk.js +0 -1204
  148. package/build/email-settings-page.f67d13b2.chunk.js +0 -103
  149. package/build/en-json.3e1a222e.chunk.js +0 -1
  150. package/build/i18n-settings-page.6b67cb75.chunk.js +0 -101
  151. package/build/main.45472ea9.js +0 -8404
  152. package/build/pl-json.94f05d2c.chunk.js +0 -1
  153. package/build/upload-settings.3db55de0.chunk.js +0 -101
  154. package/build/upload-translation-de-json.b642da08.chunk.js +0 -1
  155. package/build/upload-translation-dk-json.fc61df13.chunk.js +0 -1
  156. package/build/upload-translation-en-json.59269508.chunk.js +0 -1
  157. package/build/upload-translation-es-json.8ec935ef.chunk.js +0 -1
  158. package/build/upload-translation-fr-json.eb9b4f84.chunk.js +0 -1
  159. package/build/upload-translation-he-json.c226f2dc.chunk.js +0 -1
  160. package/build/upload-translation-it-json.8e58456e.chunk.js +0 -1
  161. package/build/upload-translation-ja-json.1378a2e7.chunk.js +0 -1
  162. package/build/upload-translation-ko-json.5e06e112.chunk.js +0 -1
  163. package/build/upload-translation-ms-json.dc3bf0d7.chunk.js +0 -1
  164. package/build/upload-translation-pl-json.6071e38c.chunk.js +0 -1
  165. package/build/upload-translation-pt-BR-json.7e8d9550.chunk.js +0 -1
  166. package/build/upload-translation-ru-json.da2529f3.chunk.js +0 -1
  167. package/build/upload-translation-sk-json.bfdf4f09.chunk.js +0 -1
  168. package/build/upload-translation-th-json.6a48b826.chunk.js +0 -1
  169. package/build/upload-translation-uk-json.6fb09148.chunk.js +0 -1
  170. package/build/upload-translation-zh-json.711f804b.chunk.js +0 -1
  171. package/build/upload.070c189b.chunk.js +0 -105
  172. package/build/users-advanced-settings-page.a23cda17.chunk.js +0 -101
  173. package/build/users-email-settings-page.0a096388.chunk.js +0 -1
  174. package/build/users-providers-settings-page.bfe7755a.chunk.js +0 -1
  175. package/build/vi-json.3d14e91e.chunk.js +0 -1
  176. package/build/webhook-edit-page.2fa94db3.chunk.js +0 -23
  177. package/build/webhook-list-page.b594db49.chunk.js +0 -133
@@ -12,7 +12,6 @@ import {
12
12
  useNotification,
13
13
  useAppInfos,
14
14
  } from '@strapi/helper-plugin';
15
- import { Grid, GridItem } from '@strapi/design-system/Grid';
16
15
  import { Layout, ContentLayout } from '@strapi/design-system/Layout';
17
16
  import { Main } from '@strapi/design-system/Main';
18
17
  import { Searchbar } from '@strapi/design-system/Searchbar';
@@ -20,20 +19,22 @@ import { Box } from '@strapi/design-system/Box';
20
19
  import { useNotifyAT } from '@strapi/design-system/LiveRegions';
21
20
  import { Typography } from '@strapi/design-system/Typography';
22
21
  import { Flex } from '@strapi/design-system/Flex';
22
+ import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs';
23
23
 
24
- import PluginCard from './components/PluginCard';
25
- import { EmptyPluginSearch } from './components/EmptyPluginSearch';
24
+ import EmptyNpmPackageSearch from './components/EmptyNpmPackageSearch';
26
25
  import PageHeader from './components/PageHeader';
27
26
  import { fetchAppInformation } from './utils/api';
28
27
  import useFetchInstalledPlugins from '../../hooks/useFetchInstalledPlugins';
28
+ import useFetchMarketplaceProviders from '../../hooks/useFetchMarketplaceProviders';
29
29
  import useFetchMarketplacePlugins from '../../hooks/useFetchMarketplacePlugins';
30
30
  import adminPermissions from '../../permissions';
31
31
  import offlineCloud from '../../assets/images/icon_offline-cloud.svg';
32
32
  import useNavigatorOnLine from '../../hooks/useNavigatorOnLine';
33
33
  import MissingPluginBanner from './components/MissingPluginBanner';
34
+ import NpmPackagesGrid from './components/NpmPackagesGrid';
34
35
 
35
- const matchSearch = (plugins, search) => {
36
- return matchSorter(plugins, search, {
36
+ const matchSearch = (npmPackages, search) => {
37
+ return matchSorter(npmPackages, search, {
37
38
  keys: [
38
39
  {
39
40
  threshold: matchSorter.rankings.WORD_STARTS_WITH,
@@ -51,6 +52,7 @@ const MarketPlacePage = () => {
51
52
  const trackUsageRef = useRef(trackUsage);
52
53
  const toggleNotification = useNotification();
53
54
  const [searchQuery, setSearchQuery] = useState('');
55
+ const [npmPackageType, setNpmPackageType] = useState('plugin');
54
56
  const { autoReload: isInDevelopmentMode } = useAppInfos();
55
57
  const isOnline = useNavigatorOnLine();
56
58
 
@@ -78,6 +80,11 @@ const MarketPlacePage = () => {
78
80
  data: marketplacePluginsResponse,
79
81
  } = useFetchMarketplacePlugins(notifyMarketplaceLoad);
80
82
 
83
+ const {
84
+ status: marketplaceProvidersStatus,
85
+ data: marketplaceProvidersResponse,
86
+ } = useFetchMarketplaceProviders(notifyMarketplaceLoad);
87
+
81
88
  const {
82
89
  status: installedPluginsStatus,
83
90
  data: installedPluginsResponse,
@@ -96,13 +103,19 @@ const MarketPlacePage = () => {
96
103
  }
97
104
  );
98
105
 
99
- const isLoading = [marketplacePluginsStatus, installedPluginsStatus, appInfoStatus].includes(
100
- 'loading'
101
- );
106
+ const isLoading = [
107
+ marketplacePluginsStatus,
108
+ marketplaceProvidersStatus,
109
+ installedPluginsStatus,
110
+ appInfoStatus,
111
+ ].includes('loading');
102
112
 
103
- const hasFailed = [marketplacePluginsStatus, installedPluginsStatus, appInfoStatus].includes(
104
- 'error'
105
- );
113
+ const hasFailed = [
114
+ marketplacePluginsStatus,
115
+ marketplaceProvidersStatus,
116
+ installedPluginsStatus,
117
+ appInfoStatus,
118
+ ].includes('error');
106
119
 
107
120
  useEffect(() => {
108
121
  trackUsageRef.current('didGoToMarketplace');
@@ -179,7 +192,23 @@ const MarketPlacePage = () => {
179
192
  );
180
193
  }
181
194
 
182
- const searchResults = matchSearch(marketplacePluginsResponse.data, searchQuery);
195
+ // Search for plugins and providers that match the search query
196
+ const pluginSearchResults = matchSearch(marketplacePluginsResponse.data, searchQuery);
197
+ const providerSearchResults = matchSearch(marketplaceProvidersResponse.data, searchQuery);
198
+ const emptySearchMessage = formatMessage(
199
+ {
200
+ id: 'admin.pages.MarketPlacePage.search.empty',
201
+ defaultMessage: 'No result for "{target}"',
202
+ },
203
+ { target: searchQuery }
204
+ );
205
+
206
+ const handleTabChange = selected => {
207
+ const packageType = selected === 0 ? 'plugin' : 'provider';
208
+ setNpmPackageType(packageType);
209
+ };
210
+
211
+ // Check if plugins are installed already
183
212
  const installedPluginNames = installedPluginsResponse.plugins.map(plugin => plugin.packageName);
184
213
 
185
214
  return (
@@ -191,7 +220,7 @@ const MarketPlacePage = () => {
191
220
  defaultMessage: 'Marketplace - Plugins',
192
221
  })}
193
222
  />
194
- <PageHeader isOnline={isOnline} />
223
+ <PageHeader isOnline={isOnline} npmPackageType={npmPackageType} />
195
224
  <ContentLayout>
196
225
  <Box width="25%" paddingBottom={4}>
197
226
  <Searchbar
@@ -201,43 +230,76 @@ const MarketPlacePage = () => {
201
230
  onChange={e => setSearchQuery(e.target.value)}
202
231
  clearLabel={formatMessage({
203
232
  id: 'admin.pages.MarketPlacePage.search.clear',
204
- defaultMessage: 'Clear the plugin search',
233
+ defaultMessage: 'Clear the search',
205
234
  })}
206
235
  placeholder={formatMessage({
207
236
  id: 'admin.pages.MarketPlacePage.search.placeholder',
208
- defaultMessage: 'Search for a plugin',
237
+ defaultMessage: 'Search',
209
238
  })}
210
239
  >
211
240
  {formatMessage({
212
241
  id: 'admin.pages.MarketPlacePage.search.placeholder',
213
- defaultMessage: 'Search for a plugin',
242
+ defaultMessage: 'Search',
214
243
  })}
215
244
  </Searchbar>
216
245
  </Box>
217
- {searchQuery.length > 0 && !searchResults.length ? (
218
- <EmptyPluginSearch
219
- content={formatMessage(
220
- {
221
- id: 'admin.pages.MarketPlacePage.search.empty',
222
- defaultMessage: 'No result for "{target}"',
223
- },
224
- { target: searchQuery }
225
- )}
226
- />
227
- ) : (
228
- <Grid gap={4}>
229
- {searchResults.map(plugin => (
230
- <GridItem col={4} s={6} xs={12} style={{ height: '100%' }} key={plugin.id}>
231
- <PluginCard
232
- plugin={plugin}
233
- installedPluginNames={installedPluginNames}
246
+ <TabGroup
247
+ label={formatMessage({
248
+ id: 'admin.pages.MarketPlacePage.tab-group.label',
249
+ defaultMessage: 'Plugins and Providers for Strapi',
250
+ })}
251
+ id="tabs"
252
+ variant="simple"
253
+ onTabChange={handleTabChange}
254
+ >
255
+ <Box paddingBottom={4}>
256
+ <Tabs>
257
+ <Tab>
258
+ {formatMessage({
259
+ id: 'admin.pages.MarketPlacePage.plugins',
260
+ defaultMessage: 'Plugins',
261
+ })}{' '}
262
+ ({pluginSearchResults.length})
263
+ </Tab>
264
+ <Tab>
265
+ {formatMessage({
266
+ id: 'admin.pages.MarketPlacePage.providers',
267
+ defaultMessage: 'Providers',
268
+ })}{' '}
269
+ ({providerSearchResults.length})
270
+ </Tab>
271
+ </Tabs>
272
+ </Box>
273
+ <TabPanels>
274
+ {/* Plugins panel */}
275
+ <TabPanel>
276
+ {searchQuery.length > 0 && !pluginSearchResults.length ? (
277
+ <EmptyNpmPackageSearch content={emptySearchMessage} />
278
+ ) : (
279
+ <NpmPackagesGrid
280
+ npmPackages={pluginSearchResults}
281
+ installedPackageNames={installedPluginNames}
282
+ useYarn={appInfoResponse.data.useYarn}
283
+ isInDevelopmentMode={isInDevelopmentMode}
284
+ npmPackageType="plugin"
285
+ />
286
+ )}
287
+ </TabPanel>
288
+ {/* Providers panel */}
289
+ <TabPanel>
290
+ {searchQuery.length > 0 && !providerSearchResults.length ? (
291
+ <EmptyNpmPackageSearch content={emptySearchMessage} />
292
+ ) : (
293
+ <NpmPackagesGrid
294
+ npmPackages={providerSearchResults}
234
295
  useYarn={appInfoResponse.data.useYarn}
235
296
  isInDevelopmentMode={isInDevelopmentMode}
297
+ npmPackageType="provider"
236
298
  />
237
- </GridItem>
238
- ))}
239
- </Grid>
240
- )}
299
+ )}
300
+ </TabPanel>
301
+ </TabPanels>
302
+ </TabGroup>
241
303
  <Box paddingTop={7}>
242
304
  <MissingPluginBanner />
243
305
  </Box>
@@ -0,0 +1,85 @@
1
+ import React, { useReducer, forwardRef, useImperativeHandle } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { useTracking } from '@strapi/helper-plugin';
5
+ import { Grid, GridItem } from '@strapi/design-system/Grid';
6
+ import { Box } from '@strapi/design-system/Box';
7
+ import { Typography } from '@strapi/design-system/Typography';
8
+ import LogoInput from '../LogoInput';
9
+ import { useConfigurations } from '../../../../../../hooks';
10
+ import reducer, { initialState } from './reducer';
11
+ import init from './init';
12
+
13
+ const Form = forwardRef(({ projectSettingsStored }, ref) => {
14
+ const { formatMessage } = useIntl();
15
+ const { trackUsage } = useTracking();
16
+ const {
17
+ logos: { menu },
18
+ } = useConfigurations();
19
+ const [{ menuLogo }, dispatch] = useReducer(reducer, initialState, () =>
20
+ init(initialState, projectSettingsStored)
21
+ );
22
+
23
+ const handleChangeMenuLogo = asset => {
24
+ dispatch({
25
+ type: 'SET_CUSTOM_MENU_LOGO',
26
+ value: asset,
27
+ });
28
+ };
29
+
30
+ const handleResetMenuLogo = () => {
31
+ trackUsage('didClickResetLogo');
32
+
33
+ dispatch({
34
+ type: 'RESET_CUSTOM_MENU_LOGO',
35
+ });
36
+ };
37
+
38
+ useImperativeHandle(ref, () => ({
39
+ getValues: () => ({ menuLogo: menuLogo.submit }),
40
+ }));
41
+
42
+ return (
43
+ <Box
44
+ hasRadius
45
+ background="neutral0"
46
+ shadow="tableShadow"
47
+ paddingTop={6}
48
+ paddingBottom={6}
49
+ paddingRight={7}
50
+ paddingLeft={7}
51
+ >
52
+ <Typography variant="delta" as="h3">
53
+ {formatMessage({
54
+ id: 'Settings.application.customization',
55
+ defaultMessage: 'Customization',
56
+ })}
57
+ </Typography>
58
+ <Grid paddingTop={4}>
59
+ <GridItem col={6} s={12}>
60
+ <LogoInput
61
+ onChangeLogo={handleChangeMenuLogo}
62
+ customLogo={menuLogo.display}
63
+ defaultLogo={menu.default}
64
+ onResetMenuLogo={handleResetMenuLogo}
65
+ />
66
+ </GridItem>
67
+ </Grid>
68
+ </Box>
69
+ );
70
+ });
71
+
72
+ Form.defaultProps = {
73
+ projectSettingsStored: null,
74
+ };
75
+
76
+ Form.propTypes = {
77
+ projectSettingsStored: PropTypes.shape({
78
+ menuLogo: PropTypes.shape({
79
+ url: PropTypes.string,
80
+ name: PropTypes.string,
81
+ }),
82
+ }),
83
+ };
84
+
85
+ export default Form;
@@ -0,0 +1,13 @@
1
+ import merge from 'lodash/merge';
2
+
3
+ const init = (initialState, projectSettingsStored) => {
4
+ const copyInitialState = merge(initialState, {
5
+ menuLogo: {
6
+ display: projectSettingsStored.menuLogo,
7
+ },
8
+ });
9
+
10
+ return copyInitialState;
11
+ };
12
+
13
+ export default init;
@@ -0,0 +1,43 @@
1
+ /* eslint-disable consistent-return */
2
+ /*
3
+ *
4
+ * ApplicationInfosPage Form reducer
5
+ *
6
+ */
7
+
8
+ import produce from 'immer';
9
+
10
+ const initialState = {
11
+ menuLogo: {
12
+ display: null,
13
+ submit: {
14
+ rawFile: null,
15
+ isReset: false,
16
+ },
17
+ },
18
+ };
19
+
20
+ const reducer = (state = initialState, action) =>
21
+ produce(state, draftState => {
22
+ switch (action.type) {
23
+ case 'SET_CUSTOM_MENU_LOGO': {
24
+ draftState.menuLogo.display = action.value;
25
+ draftState.menuLogo.submit.rawFile = action.value.rawFile;
26
+ break;
27
+ }
28
+ case 'RESET_CUSTOM_MENU_LOGO': {
29
+ draftState.menuLogo.display = null;
30
+ draftState.menuLogo.submit = {
31
+ rawFile: null,
32
+ isReset: true,
33
+ };
34
+ break;
35
+ }
36
+ default: {
37
+ return draftState;
38
+ }
39
+ }
40
+ });
41
+
42
+ export default reducer;
43
+ export { initialState };
@@ -0,0 +1,118 @@
1
+ import React, { useReducer } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import PropTypes from 'prop-types';
4
+ import { CarouselInput, CarouselSlide, CarouselActions } from '@strapi/design-system/CarouselInput';
5
+ import { IconButton } from '@strapi/design-system/IconButton';
6
+ import { Box } from '@strapi/design-system/Box';
7
+ import Plus from '@strapi/icons/Plus';
8
+ import Refresh from '@strapi/icons/Refresh';
9
+ import reducer, { initialState } from './reducer';
10
+ import LogoModalStepper from '../LogoModalStepper';
11
+ import { SIZE, DIMENSION } from '../../utils/constants';
12
+ import stepper from './stepper';
13
+
14
+ const LogoInput = ({ customLogo, defaultLogo, onChangeLogo, onResetMenuLogo }) => {
15
+ const [{ currentStep }, dispatch] = useReducer(reducer, initialState);
16
+ const { Component, next, prev, modalTitle } = stepper[currentStep] || {};
17
+ const { formatMessage } = useIntl();
18
+
19
+ const goTo = to => {
20
+ dispatch({
21
+ type: 'GO_TO',
22
+ to,
23
+ });
24
+ };
25
+
26
+ return (
27
+ <>
28
+ <CarouselInput
29
+ label={formatMessage({
30
+ id: 'Settings.application.customization.carousel.title',
31
+ defaultMessage: 'Logo',
32
+ })}
33
+ selectedSlide={0}
34
+ hint={formatMessage(
35
+ {
36
+ id: 'Settings.application.customization.carousel-hint',
37
+ defaultMessage:
38
+ 'Change the admin panel logo (Max dimension: {dimension}x{dimension}, Max file size: {size}KB)',
39
+ },
40
+ { size: SIZE, dimension: DIMENSION }
41
+ )}
42
+ // Carousel is used here for a single media,
43
+ // we don't need previous and next labels but these props are required
44
+ previousLabel=""
45
+ nextLabel=""
46
+ onNext={() => {}}
47
+ onPrevious={() => {}}
48
+ secondaryLabel={customLogo?.name || 'logo.png'}
49
+ actions={
50
+ <CarouselActions>
51
+ <IconButton
52
+ onClick={() => goTo(customLogo ? 'pending' : 'upload')}
53
+ label={formatMessage({
54
+ id: 'Settings.application.customization.carousel.change-action',
55
+ defaultMessage: 'Change logo',
56
+ })}
57
+ icon={<Plus />}
58
+ />
59
+ {customLogo && (
60
+ <IconButton
61
+ onClick={onResetMenuLogo}
62
+ label={formatMessage({
63
+ id: 'Settings.application.customization.carousel.reset-action',
64
+ defaultMessage: 'Reset logo',
65
+ })}
66
+ icon={<Refresh />}
67
+ />
68
+ )}
69
+ </CarouselActions>
70
+ }
71
+ >
72
+ <CarouselSlide
73
+ label={formatMessage({
74
+ id: 'Settings.application.customization.carousel-slide.label',
75
+ defaultMessage: 'Logo slide',
76
+ })}
77
+ >
78
+ <Box
79
+ maxHeight="40%"
80
+ maxWidth="40%"
81
+ as="img"
82
+ src={customLogo?.url || defaultLogo}
83
+ alt={formatMessage({
84
+ id: 'Settings.application.customization.carousel.title',
85
+ defaultMessage: 'Logo',
86
+ })}
87
+ />
88
+ </CarouselSlide>
89
+ </CarouselInput>
90
+ <LogoModalStepper
91
+ Component={Component}
92
+ currentStep={currentStep}
93
+ onChangeLogo={onChangeLogo}
94
+ customLogo={customLogo}
95
+ goTo={goTo}
96
+ next={next}
97
+ prev={prev}
98
+ modalTitle={modalTitle}
99
+ />
100
+ </>
101
+ );
102
+ };
103
+
104
+ LogoInput.defaultProps = {
105
+ customLogo: null,
106
+ };
107
+
108
+ LogoInput.propTypes = {
109
+ customLogo: PropTypes.shape({
110
+ url: PropTypes.string,
111
+ name: PropTypes.string,
112
+ }),
113
+ defaultLogo: PropTypes.string.isRequired,
114
+ onChangeLogo: PropTypes.func.isRequired,
115
+ onResetMenuLogo: PropTypes.func.isRequired,
116
+ };
117
+
118
+ export default LogoInput;
@@ -0,0 +1,28 @@
1
+ /* eslint-disable consistent-return */
2
+ /*
3
+ *
4
+ * LogoInput reducer
5
+ *
6
+ */
7
+
8
+ import produce from 'immer';
9
+
10
+ const initialState = {
11
+ currentStep: undefined,
12
+ };
13
+
14
+ const reducer = (state = initialState, action) =>
15
+ produce(state, draftState => {
16
+ switch (action.type) {
17
+ case 'GO_TO': {
18
+ draftState.currentStep = action.to;
19
+ break;
20
+ }
21
+ default: {
22
+ return draftState;
23
+ }
24
+ }
25
+ });
26
+
27
+ export default reducer;
28
+ export { initialState };
@@ -0,0 +1,25 @@
1
+ import AddLogoDialog from '../LogoModalStepper/AddLogoDialog';
2
+ import PendingLogoDialog from '../LogoModalStepper/PendingLogoDialog';
3
+
4
+ const stepper = {
5
+ upload: {
6
+ Component: AddLogoDialog,
7
+ modalTitle: {
8
+ id: 'Settings.application.customization.modal.upload',
9
+ defaultMessage: 'Upload logo',
10
+ },
11
+ next: 'pending',
12
+ prev: null,
13
+ },
14
+ pending: {
15
+ Component: PendingLogoDialog,
16
+ modalTitle: {
17
+ id: 'Settings.application.customization.modal.pending',
18
+ defaultMessage: 'Pending logo',
19
+ },
20
+ next: null,
21
+ prev: 'upload',
22
+ },
23
+ };
24
+
25
+ export default stepper;
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import PropTypes from 'prop-types';
4
+ import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs';
5
+ import { Box } from '@strapi/design-system/Box';
6
+ import { Divider } from '@strapi/design-system/Divider';
7
+ import FromComputerForm from './FromComputerForm';
8
+ import FromUrlForm from './FromUrlForm';
9
+
10
+ const AddLogoDialog = ({ setLocalImage, goTo, next, onClose }) => {
11
+ const { formatMessage } = useIntl();
12
+
13
+ return (
14
+ <TabGroup
15
+ label={formatMessage({
16
+ id: 'Settings.application.customization.modal.tab.label',
17
+ defaultMessage: 'How do you want to upload your assets?',
18
+ })}
19
+ variant="simple"
20
+ >
21
+ <Box paddingLeft={8} paddingRight={8}>
22
+ <Tabs>
23
+ <Tab>
24
+ {formatMessage({
25
+ id: 'Settings.application.customization.modal.upload.from-computer',
26
+ defaultMessage: 'From computer',
27
+ })}
28
+ </Tab>
29
+ <Tab>
30
+ {formatMessage({
31
+ id: 'Settings.application.customization.modal.upload.from-url',
32
+ defaultMessage: 'From url',
33
+ })}
34
+ </Tab>
35
+ </Tabs>
36
+
37
+ <Divider />
38
+ </Box>
39
+ <TabPanels>
40
+ <TabPanel>
41
+ <FromComputerForm
42
+ onClose={onClose}
43
+ setLocalImage={setLocalImage}
44
+ goTo={goTo}
45
+ next={next}
46
+ />
47
+ </TabPanel>
48
+ <TabPanel>
49
+ <FromUrlForm onClose={onClose} setLocalImage={setLocalImage} goTo={goTo} next={next} />
50
+ </TabPanel>
51
+ </TabPanels>
52
+ </TabGroup>
53
+ );
54
+ };
55
+
56
+ AddLogoDialog.defaultProps = {
57
+ next: null,
58
+ };
59
+
60
+ AddLogoDialog.propTypes = {
61
+ goTo: PropTypes.func.isRequired,
62
+ next: PropTypes.string,
63
+ onClose: PropTypes.func.isRequired,
64
+ setLocalImage: PropTypes.func.isRequired,
65
+ };
66
+
67
+ export default AddLogoDialog;