@strapi/admin 4.2.0-alpha.5 → 4.2.0-alpha.8

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 (71) hide show
  1. package/admin/src/StrapiApp.js +40 -42
  2. package/admin/src/assets/images/icon_made-by-strapi.svg +5 -0
  3. package/admin/src/components/GuidedTour/Modal/components/Modal.js +1 -1
  4. package/admin/src/components/PrivateRoute/index.js +23 -17
  5. package/admin/src/components/Providers/index.js +65 -32
  6. package/admin/src/components/Theme/index.js +11 -12
  7. package/admin/src/components/ThemeToggleProvider/index.js +66 -0
  8. package/admin/src/content-manager/components/SelectWrapper/utils/getSelectStyles.js +3 -1
  9. package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +4 -2
  10. package/admin/src/contexts/ThemeToggle/index.js +5 -0
  11. package/admin/src/contexts/index.js +1 -0
  12. package/admin/src/hooks/index.js +1 -1
  13. package/admin/src/hooks/useFetchInstalledPlugins/index.js +23 -0
  14. package/admin/src/{pages/InstalledPluginsPage → hooks/useFetchInstalledPlugins}/utils/api.js +2 -4
  15. package/admin/src/hooks/useFetchMarketplacePlugins/index.js +23 -0
  16. package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +17 -0
  17. package/admin/src/hooks/useThemeToggle/index.js +10 -0
  18. package/admin/src/pages/Admin/Onboarding/index.js +1 -1
  19. package/admin/src/pages/AuthPage/index.js +22 -5
  20. package/admin/src/pages/HomePage/SocialLinks.js +0 -3
  21. package/admin/src/pages/InstalledPluginsPage/Plugins.js +6 -15
  22. package/admin/src/pages/MarketplacePage/components/EmptyPluginSearch/EmptyPluginGrid.js +27 -0
  23. package/admin/src/pages/MarketplacePage/components/EmptyPluginSearch/index.js +30 -0
  24. package/admin/src/pages/MarketplacePage/components/PluginCard/index.js +186 -0
  25. package/admin/src/pages/MarketplacePage/index.js +199 -107
  26. package/admin/src/pages/MarketplacePage/utils/api.js +9 -0
  27. package/admin/src/pages/ProfilePage/index.js +74 -10
  28. package/admin/src/pages/ProfilePage/utils/api.js +4 -2
  29. package/admin/src/translations/en.json +21 -15
  30. package/build/4362.91a4253f.chunk.js +1 -0
  31. package/build/{6250.836851ca.chunk.js → 6404.3c2d0a81.chunk.js} +1 -1
  32. package/build/90f49a385afb000fb1d4.svg +5 -0
  33. package/build/{Admin-authenticatedApp.a24ebaa0.chunk.js → Admin-authenticatedApp.187bd266.chunk.js} +1 -1
  34. package/build/{Admin_homePage.86604515.chunk.js → Admin_homePage.fd1fc572.chunk.js} +1 -1
  35. package/build/Admin_marketplace.89a0a014.chunk.js +1 -0
  36. package/build/Admin_pluginsPage.97a514db.chunk.js +1 -0
  37. package/build/Admin_profilePage.d7192d06.chunk.js +1 -0
  38. package/build/{Admin_settingsPage.55ec1f30.chunk.js → Admin_settingsPage.a8c7ded5.chunk.js} +1 -1
  39. package/build/admin-users.2740c223.chunk.js +1 -0
  40. package/build/content-manager.f1c46a88.chunk.js +1 -0
  41. package/build/{content-type-builder.de5d18ad.chunk.js → content-type-builder.cda4ba3c.chunk.js} +1 -1
  42. package/build/{email-settings-page.27ee4a98.chunk.js → email-settings-page.40ee2bda.chunk.js} +1 -1
  43. package/build/en-json.73a610d6.chunk.js +1 -0
  44. package/build/index.html +1 -1
  45. package/build/{main.4ea77c5f.js → main.c12537d5.js} +2 -2
  46. package/build/{main.4ea77c5f.js.LICENSE.txt → main.c12537d5.js.LICENSE.txt} +0 -0
  47. package/build/{runtime~main.83b9ee0b.js → runtime~main.e5240b25.js} +1 -1
  48. package/build/users-email-settings-page.5abb9575.chunk.js +1 -0
  49. package/package.json +5 -5
  50. package/server/controllers/admin.js +12 -1
  51. package/.env +0 -0
  52. package/admin/src/hooks/useFetchPluginsFromMarketPlace/index.js +0 -49
  53. package/admin/src/pages/MarketplacePage/MarketplaceBanner/Wrapper.js +0 -28
  54. package/admin/src/pages/MarketplacePage/MarketplaceBanner/index.js +0 -37
  55. package/admin/src/pages/MarketplacePage/PluginCard/Wrapper.js +0 -148
  56. package/admin/src/pages/MarketplacePage/PluginCard/index.js +0 -185
  57. package/admin/src/pages/MarketplacePage/Wrapper.js +0 -5
  58. package/admin/src/pages/MarketplacePage/assets/marketplace-coming-soon.png +0 -0
  59. package/admin/src/themes/colors.js +0 -51
  60. package/admin/src/themes/fontWeights.js +0 -8
  61. package/admin/src/themes/index.js +0 -13
  62. package/admin/src/themes/sizes.js +0 -31
  63. package/build/01a600d9e6e0dea21e33.png +0 -0
  64. package/build/4362.dbe98749.chunk.js +0 -1
  65. package/build/Admin_marketplace.419010d8.chunk.js +0 -1
  66. package/build/Admin_pluginsPage.7d1bd7ce.chunk.js +0 -1
  67. package/build/Admin_profilePage.c497b39d.chunk.js +0 -1
  68. package/build/admin-users.1fda1f27.chunk.js +0 -1
  69. package/build/content-manager.31be1448.chunk.js +0 -1
  70. package/build/en-json.bce44d39.chunk.js +0 -1
  71. package/build/users-email-settings-page.862eb51e.chunk.js +0 -1
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { BrowserRouter } from 'react-router-dom';
3
- import { lightTheme } from '@strapi/design-system/themes';
3
+ import { lightTheme, darkTheme } from '@strapi/design-system/themes';
4
4
  import merge from 'lodash/merge';
5
5
  import pick from 'lodash/pick';
6
6
  import isFunction from 'lodash/isFunction';
@@ -13,7 +13,6 @@ import App from './pages/App';
13
13
  import AuthLogo from './assets/images/logo_strapi_auth_v4.png';
14
14
  import MenuLogo from './assets/images/logo_strapi_menu.png';
15
15
  import Providers from './components/Providers';
16
- import Theme from './components/Theme';
17
16
  import languageNativeNames from './translations/languageNativeNames';
18
17
  import {
19
18
  INJECT_COLUMN_IN_TABLE,
@@ -34,7 +33,7 @@ class StrapiApp {
34
33
  locales: ['en'],
35
34
  menuLogo: MenuLogo,
36
35
  notifications: { releases: true },
37
- theme: lightTheme,
36
+ themes: { light: lightTheme, dark: darkTheme },
38
37
  translations: {},
39
38
  tutorials: true,
40
39
  };
@@ -226,7 +225,7 @@ class StrapiApp {
226
225
  }
227
226
 
228
227
  if (this.customConfigurations?.theme) {
229
- this.configurations.theme = merge(this.configurations.theme, this.customConfigurations.theme);
228
+ merge(this.configurations.themes.light, this.customConfigurations.theme);
230
229
  }
231
230
 
232
231
  if (this.customConfigurations?.notifications?.releases !== undefined) {
@@ -427,44 +426,43 @@ class StrapiApp {
427
426
  } = this.library;
428
427
 
429
428
  return (
430
- <Theme theme={this.configurations.theme}>
431
- <Providers
432
- authLogo={this.configurations.authLogo}
433
- components={components}
434
- fields={fields}
435
- localeNames={localeNames}
436
- getAdminInjectedComponents={this.getAdminInjectedComponents}
437
- getPlugin={this.getPlugin}
438
- messages={this.configurations.translations}
439
- menu={this.menu}
440
- menuLogo={this.configurations.menuLogo}
441
- plugins={this.plugins}
442
- runHookParallel={this.runHookParallel}
443
- runHookWaterfall={(name, initialValue, async = false) => {
444
- return this.runHookWaterfall(name, initialValue, async, store);
445
- }}
446
- runHookSeries={this.runHookSeries}
447
- settings={this.settings}
448
- showTutorials={this.configurations.tutorials}
449
- showReleaseNotification={this.configurations.notifications.releases}
450
- store={store}
451
- >
452
- <>
453
- <Helmet
454
- link={[
455
- {
456
- rel: 'icon',
457
- type: 'image/png',
458
- href: this.configurations.head.favicon,
459
- },
460
- ]}
461
- />
462
- <BrowserRouter basename={basename}>
463
- <App store={store} />
464
- </BrowserRouter>
465
- </>
466
- </Providers>
467
- </Theme>
429
+ <Providers
430
+ authLogo={this.configurations.authLogo}
431
+ components={components}
432
+ fields={fields}
433
+ localeNames={localeNames}
434
+ getAdminInjectedComponents={this.getAdminInjectedComponents}
435
+ getPlugin={this.getPlugin}
436
+ messages={this.configurations.translations}
437
+ menu={this.menu}
438
+ menuLogo={this.configurations.menuLogo}
439
+ plugins={this.plugins}
440
+ runHookParallel={this.runHookParallel}
441
+ runHookWaterfall={(name, initialValue, async = false) => {
442
+ return this.runHookWaterfall(name, initialValue, async, store);
443
+ }}
444
+ runHookSeries={this.runHookSeries}
445
+ themes={this.configurations.themes}
446
+ settings={this.settings}
447
+ showTutorials={this.configurations.tutorials}
448
+ showReleaseNotification={this.configurations.notifications.releases}
449
+ store={store}
450
+ >
451
+ <>
452
+ <Helmet
453
+ link={[
454
+ {
455
+ rel: 'icon',
456
+ type: 'image/png',
457
+ href: this.configurations.head.favicon,
458
+ },
459
+ ]}
460
+ />
461
+ <BrowserRouter basename={basename}>
462
+ <App store={store} />
463
+ </BrowserRouter>
464
+ </>
465
+ </Providers>
468
466
  );
469
467
  }
470
468
  }
@@ -0,0 +1,5 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect x="3" y="3" width="18" height="18" rx="4" fill="#4945FF"/>
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M15.8075 7.625H9.03058V11.1792H12.2533C12.4977 11.1792 12.6958 11.3773 12.6958 11.6216V14.8444H16.25V8.06746C16.25 7.82309 16.0519 7.625 15.8075 7.625Z" fill="white"/>
4
+ <path opacity="0.4" fill-rule="evenodd" clip-rule="evenodd" d="M9.0308 7.625V11.1792H6.01073C5.81364 11.1792 5.71494 10.9409 5.8543 10.8015L9.0308 7.625ZM13.0735 18.0209C12.9342 18.1603 12.6959 18.0616 12.6959 17.8645V14.8444H16.25L13.0735 18.0209ZM12.4746 11.1792H9.03058V14.4019C9.03058 14.6463 9.22868 14.8444 9.47304 14.8444H12.6958V11.4004C12.6958 11.2782 12.5968 11.1792 12.4746 11.1792Z" fill="#DAD9FF"/>
5
+ </svg>
@@ -17,7 +17,7 @@ const ModalWrapper = styled(Flex)`
17
17
  z-index: 4;
18
18
  inset: 0;
19
19
  /* this is theme.colors.neutral800 with opacity */
20
- background: ${({ theme }) => `${theme.colors.neutral800}33`};
20
+ background: ${({ theme }) => `${theme.colors.neutral800}1F`};
21
21
  `;
22
22
 
23
23
  const Modal = ({ onClose, onSkip, children, hideSkip }) => {
@@ -8,28 +8,34 @@
8
8
  */
9
9
 
10
10
  import React, { memo } from 'react';
11
- import { Redirect, Route } from 'react-router-dom';
11
+ import { Redirect, Route, useLocation } from 'react-router-dom';
12
12
  import PropTypes from 'prop-types';
13
13
  import { auth } from '@strapi/helper-plugin';
14
14
 
15
15
  /* eslint-disable react/jsx-curly-newline */
16
16
 
17
- const PrivateRoute = ({ component: Component, path, ...rest }) => (
18
- <Route
19
- path={path}
20
- render={props =>
21
- auth.getToken() !== null ? (
22
- <Component {...rest} {...props} />
23
- ) : (
24
- <Redirect
25
- to={{
26
- pathname: '/auth/login',
27
- }}
28
- />
29
- )
30
- }
31
- />
32
- );
17
+ const PrivateRoute = ({ component: Component, path, ...rest }) => {
18
+ const { pathname, search } = useLocation();
19
+
20
+ return (
21
+ <Route
22
+ path={path}
23
+ render={props =>
24
+ auth.getToken() !== null ? (
25
+ <Component {...rest} {...props} />
26
+ ) : (
27
+ <Redirect
28
+ to={{
29
+ pathname: '/auth/login',
30
+ search:
31
+ pathname !== '/' && `?redirectTo=${encodeURIComponent(`${pathname}${search}`)}`,
32
+ }}
33
+ />
34
+ )
35
+ }
36
+ />
37
+ );
38
+ };
33
39
 
34
40
  PrivateRoute.propTypes = {
35
41
  component: PropTypes.any.isRequired,
@@ -9,6 +9,8 @@ import GuidedTour from '../GuidedTour';
9
9
  import AutoReloadOverlayBlockerProvider from '../AutoReloadOverlayBlockerProvider';
10
10
  import Notifications from '../Notifications';
11
11
  import OverlayBlocker from '../OverlayBlocker';
12
+ import ThemeToggleProvider from '../ThemeToggleProvider';
13
+ import Theme from '../Theme';
12
14
 
13
15
  const queryClient = new QueryClient({
14
16
  defaultOptions: {
@@ -36,41 +38,45 @@ const Providers = ({
36
38
  settings,
37
39
  showReleaseNotification,
38
40
  showTutorials,
39
-
40
41
  store,
42
+ themes,
41
43
  }) => {
42
44
  return (
43
- <QueryClientProvider client={queryClient}>
44
- <Provider store={store}>
45
- <AdminContext.Provider value={{ getAdminInjectedComponents }}>
46
- <ConfigurationsContext.Provider
47
- value={{ authLogo, menuLogo, showReleaseNotification, showTutorials }}
48
- >
49
- <StrapiAppProvider
50
- getPlugin={getPlugin}
51
- menu={menu}
52
- plugins={plugins}
53
- runHookParallel={runHookParallel}
54
- runHookWaterfall={runHookWaterfall}
55
- runHookSeries={runHookSeries}
56
- settings={settings}
57
- >
58
- <LibraryProvider components={components} fields={fields}>
59
- <LanguageProvider messages={messages} localeNames={localeNames}>
60
- <AutoReloadOverlayBlockerProvider>
61
- <OverlayBlocker>
62
- <GuidedTour>
63
- <Notifications>{children}</Notifications>
64
- </GuidedTour>
65
- </OverlayBlocker>
66
- </AutoReloadOverlayBlockerProvider>
67
- </LanguageProvider>
68
- </LibraryProvider>
69
- </StrapiAppProvider>
70
- </ConfigurationsContext.Provider>
71
- </AdminContext.Provider>
72
- </Provider>
73
- </QueryClientProvider>
45
+ <ThemeToggleProvider themes={themes}>
46
+ <Theme>
47
+ <QueryClientProvider client={queryClient}>
48
+ <Provider store={store}>
49
+ <AdminContext.Provider value={{ getAdminInjectedComponents }}>
50
+ <ConfigurationsContext.Provider
51
+ value={{ authLogo, menuLogo, showReleaseNotification, showTutorials }}
52
+ >
53
+ <StrapiAppProvider
54
+ getPlugin={getPlugin}
55
+ menu={menu}
56
+ plugins={plugins}
57
+ runHookParallel={runHookParallel}
58
+ runHookWaterfall={runHookWaterfall}
59
+ runHookSeries={runHookSeries}
60
+ settings={settings}
61
+ >
62
+ <LibraryProvider components={components} fields={fields}>
63
+ <LanguageProvider messages={messages} localeNames={localeNames}>
64
+ <AutoReloadOverlayBlockerProvider>
65
+ <OverlayBlocker>
66
+ <GuidedTour>
67
+ <Notifications>{children}</Notifications>
68
+ </GuidedTour>
69
+ </OverlayBlocker>
70
+ </AutoReloadOverlayBlockerProvider>
71
+ </LanguageProvider>
72
+ </LibraryProvider>
73
+ </StrapiAppProvider>
74
+ </ConfigurationsContext.Provider>
75
+ </AdminContext.Provider>
76
+ </Provider>
77
+ </QueryClientProvider>
78
+ </Theme>
79
+ </ThemeToggleProvider>
74
80
  );
75
81
  };
76
82
 
@@ -104,6 +110,33 @@ Providers.propTypes = {
104
110
  showReleaseNotification: PropTypes.bool.isRequired,
105
111
  showTutorials: PropTypes.bool.isRequired,
106
112
  store: PropTypes.object.isRequired,
113
+ themes: PropTypes.shape({
114
+ light: PropTypes.shape({
115
+ colors: PropTypes.object.isRequired,
116
+ shadows: PropTypes.object.isRequired,
117
+ sizes: PropTypes.object.isRequired,
118
+ zIndices: PropTypes.array.isRequired,
119
+ spaces: PropTypes.array.isRequired,
120
+ borderRadius: PropTypes.string.isRequired,
121
+ mediaQueries: PropTypes.object.isRequired,
122
+ fontSizes: PropTypes.array.isRequired,
123
+ lineHeights: PropTypes.array.isRequired,
124
+ fontWeights: PropTypes.object.isRequired,
125
+ }).isRequired,
126
+ dark: PropTypes.shape({
127
+ colors: PropTypes.object.isRequired,
128
+ shadows: PropTypes.object.isRequired,
129
+ sizes: PropTypes.object.isRequired,
130
+ zIndices: PropTypes.array.isRequired,
131
+ spaces: PropTypes.array.isRequired,
132
+ borderRadius: PropTypes.string.isRequired,
133
+ mediaQueries: PropTypes.object.isRequired,
134
+ fontSizes: PropTypes.array.isRequired,
135
+ lineHeights: PropTypes.array.isRequired,
136
+ fontWeights: PropTypes.object.isRequired,
137
+ }).isRequired,
138
+ custom: PropTypes.object,
139
+ }).isRequired,
107
140
  };
108
141
 
109
142
  export default Providers;
@@ -1,23 +1,22 @@
1
1
  import React from 'react';
2
2
  import { ThemeProvider } from '@strapi/design-system/ThemeProvider';
3
3
  import PropTypes from 'prop-types';
4
- import { lightTheme } from '@strapi/design-system/themes';
4
+ import { useThemeToggle } from '../../hooks';
5
5
  import GlobalStyle from '../GlobalStyle';
6
6
 
7
- const Theme = ({ children, theme }) => (
8
- <ThemeProvider theme={theme}>
9
- {children}
10
- <GlobalStyle />
11
- </ThemeProvider>
12
- );
7
+ const Theme = ({ children }) => {
8
+ const { currentTheme, themes } = useThemeToggle();
13
9
 
14
- Theme.propTypes = {
15
- children: PropTypes.element.isRequired,
16
- theme: PropTypes.object,
10
+ return (
11
+ <ThemeProvider theme={themes[currentTheme] || themes.light}>
12
+ {children}
13
+ <GlobalStyle />
14
+ </ThemeProvider>
15
+ );
17
16
  };
18
17
 
19
- Theme.defaultProps = {
20
- theme: lightTheme,
18
+ Theme.propTypes = {
19
+ children: PropTypes.element.isRequired,
21
20
  };
22
21
 
23
22
  export default Theme;
@@ -0,0 +1,66 @@
1
+ /**
2
+ *
3
+ * ThemeToggleProvider
4
+ *
5
+ */
6
+
7
+ import React, { useState } from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import { ThemeToggleContext } from '../../contexts';
10
+
11
+ const THEME_KEY = 'STRAPI_THEME';
12
+
13
+ const getDefaultTheme = () => {
14
+ const browserTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
15
+ const persistedTheme = localStorage.getItem(THEME_KEY);
16
+
17
+ return persistedTheme || browserTheme;
18
+ };
19
+
20
+ const ThemeToggleProvider = ({ children, themes }) => {
21
+ const [currentTheme, setCurrentTheme] = useState(getDefaultTheme());
22
+
23
+ const handleChangeTheme = nextTheme => {
24
+ setCurrentTheme(nextTheme);
25
+ localStorage.setItem(THEME_KEY, nextTheme);
26
+ };
27
+
28
+ return (
29
+ <ThemeToggleContext.Provider value={{ currentTheme, onChangeTheme: handleChangeTheme, themes }}>
30
+ {children}
31
+ </ThemeToggleContext.Provider>
32
+ );
33
+ };
34
+
35
+ ThemeToggleProvider.propTypes = {
36
+ children: PropTypes.node.isRequired,
37
+ themes: PropTypes.shape({
38
+ light: PropTypes.shape({
39
+ colors: PropTypes.object.isRequired,
40
+ shadows: PropTypes.object.isRequired,
41
+ sizes: PropTypes.object.isRequired,
42
+ zIndices: PropTypes.array.isRequired,
43
+ spaces: PropTypes.array.isRequired,
44
+ borderRadius: PropTypes.string.isRequired,
45
+ mediaQueries: PropTypes.object.isRequired,
46
+ fontSizes: PropTypes.array.isRequired,
47
+ lineHeights: PropTypes.array.isRequired,
48
+ fontWeights: PropTypes.object.isRequired,
49
+ }).isRequired,
50
+ dark: PropTypes.shape({
51
+ colors: PropTypes.object.isRequired,
52
+ shadows: PropTypes.object.isRequired,
53
+ sizes: PropTypes.object.isRequired,
54
+ zIndices: PropTypes.array.isRequired,
55
+ spaces: PropTypes.array.isRequired,
56
+ borderRadius: PropTypes.string.isRequired,
57
+ mediaQueries: PropTypes.object.isRequired,
58
+ fontSizes: PropTypes.array.isRequired,
59
+ lineHeights: PropTypes.array.isRequired,
60
+ fontWeights: PropTypes.object.isRequired,
61
+ }).isRequired,
62
+ custom: PropTypes.object,
63
+ }).isRequired,
64
+ };
65
+
66
+ export default ThemeToggleProvider;
@@ -49,6 +49,8 @@ const getSelectStyles = theme => {
49
49
  ...base,
50
50
  width: '100%',
51
51
  marginTop: theme.spaces[1],
52
+ backgroundColor: theme.colors.neutral0,
53
+ color: theme.colors.neutral800,
52
54
  borderRadius: '4px !important',
53
55
  borderTopLeftRadius: '4px !important',
54
56
  borderTopRightRadius: '4px !important',
@@ -76,7 +78,7 @@ const getSelectStyles = theme => {
76
78
  return { ...base, lineHeight: theme.spaces[5], backgroundColor, borderRadius: 4 };
77
79
  },
78
80
  placeholder: base => ({ ...base, marginLeft: 0 }),
79
- singleValue: base => ({ ...base, marginLeft: 0 }),
81
+ singleValue: base => ({ ...base, marginLeft: 0, color: theme.colors.neutral800 }),
80
82
  valueContainer: base => ({
81
83
  ...base,
82
84
  padding: 0,
@@ -29,7 +29,8 @@ export const EditorStylesContainer = styled.div`
29
29
 
30
30
  .CodeMirror-scrollbar-filler,
31
31
  .CodeMirror-gutter-filler {
32
- background-color: white; /* The little square between H and V scrollbars */
32
+ /* The little square between H and V scrollbars */
33
+ background-color: ${({ theme }) => `${theme.colors.neutral0}`};
33
34
  }
34
35
 
35
36
  /* GUTTER */
@@ -158,7 +159,7 @@ export const EditorStylesContainer = styled.div`
158
159
  .CodeMirror {
159
160
  position: relative;
160
161
  overflow: hidden;
161
- background: white;
162
+ background: ${({ theme }) => `${theme.colors.neutral0}`};
162
163
  }
163
164
 
164
165
  .CodeMirror-scroll {
@@ -292,6 +293,7 @@ export const EditorStylesContainer = styled.div`
292
293
  .CodeMirror-cursor {
293
294
  position: absolute;
294
295
  pointer-events: none;
296
+ border-color: ${({ theme }) => `${theme.colors.neutral800}`};
295
297
  }
296
298
  .CodeMirror-measure pre {
297
299
  position: static;
@@ -0,0 +1,5 @@
1
+ import { createContext } from 'react';
2
+
3
+ const ThemeToggleContext = createContext({});
4
+
5
+ export default ThemeToggleContext;
@@ -1,3 +1,4 @@
1
1
  export { default as AdminContext } from './Admin';
2
2
  export { default as ConfigurationsContext } from './Configurations';
3
3
  export { default as PermissionsDataManagerContext } from './PermisssionsDataManagerContext';
4
+ export { default as ThemeToggleContext } from './ThemeToggle';
@@ -1,7 +1,6 @@
1
1
  export { default as useConfigurations } from './useConfigurations';
2
2
  export { default as useModels } from './useModels';
3
3
  export { default as useFetchPermissionsLayout } from './useFetchPermissionsLayout';
4
- export { default as useFetchPluginsFromMarketPlace } from './useFetchPluginsFromMarketPlace';
5
4
  export { default as useFetchRole } from './useFetchRole';
6
5
  export { default as useMenu } from './useMenu';
7
6
  export { default as useRolesList } from './useRolesList';
@@ -9,3 +8,4 @@ export { default as useSettingsMenu } from './useSettingsMenu';
9
8
  export { default as useSettingsForm } from './useSettingsForm';
10
9
  export { default as usePermissionsDataManager } from './usePermissionsDataManager';
11
10
  export { default as useReleaseNotification } from './useReleaseNotification';
11
+ export { default as useThemeToggle } from './useThemeToggle';
@@ -0,0 +1,23 @@
1
+ import { useQuery } from 'react-query';
2
+ import { useNotification } from '@strapi/helper-plugin';
3
+ import { fetchInstalledPlugins } from './utils/api';
4
+
5
+ const useFetchInstalledPlugins = notifyLoad => {
6
+ const toggleNotification = useNotification();
7
+
8
+ return useQuery('list-installed-plugins', () => fetchInstalledPlugins(), {
9
+ onSuccess: () => {
10
+ if (notifyLoad) {
11
+ notifyLoad();
12
+ }
13
+ },
14
+ onError: () => {
15
+ toggleNotification({
16
+ type: 'warning',
17
+ message: { id: 'notification.error', defaultMessage: 'An error occured' },
18
+ });
19
+ },
20
+ });
21
+ };
22
+
23
+ export default useFetchInstalledPlugins;
@@ -1,11 +1,9 @@
1
1
  import { axiosInstance } from '../../../core/utils';
2
2
 
3
- const fetchPlugins = async notify => {
3
+ const fetchInstalledPlugins = async () => {
4
4
  const { data } = await axiosInstance.get('/admin/plugins');
5
5
 
6
- notify();
7
-
8
6
  return data;
9
7
  };
10
8
 
11
- export { fetchPlugins };
9
+ export { fetchInstalledPlugins };
@@ -0,0 +1,23 @@
1
+ import { useQuery } from 'react-query';
2
+ import { useNotification } from '@strapi/helper-plugin';
3
+ import { fetchMarketplacePlugins } from './utils/api';
4
+
5
+ const useFetchMarketplacePlugins = notifyLoad => {
6
+ const toggleNotification = useNotification();
7
+
8
+ return useQuery('list-marketplace-plugins', () => fetchMarketplacePlugins(), {
9
+ onSuccess: () => {
10
+ if (notifyLoad) {
11
+ notifyLoad();
12
+ }
13
+ },
14
+ onError: () => {
15
+ toggleNotification({
16
+ type: 'warning',
17
+ message: { id: 'notification.error', defaultMessage: 'An error occured' },
18
+ });
19
+ },
20
+ });
21
+ };
22
+
23
+ export default useFetchMarketplacePlugins;
@@ -0,0 +1,17 @@
1
+ import axios from 'axios';
2
+
3
+ const MARKETPLACE_API_URL = 'https://market-api.strapi.io';
4
+
5
+ const fetchMarketplacePlugins = async () => {
6
+ const { data: response } = await axios.get(`${MARKETPLACE_API_URL}/plugins`);
7
+
8
+ // Only keep v4 plugins
9
+ const filteredResponse = {
10
+ ...response,
11
+ data: response.data.filter(plugin => plugin.attributes.strapiCompatibility === 'v4'),
12
+ };
13
+
14
+ return filteredResponse;
15
+ };
16
+
17
+ export { fetchMarketplacePlugins };
@@ -0,0 +1,10 @@
1
+ import { useContext } from 'react';
2
+ import { ThemeToggleContext } from '../../contexts';
3
+
4
+ const useThemeToggle = () => {
5
+ const context = useContext(ThemeToggleContext);
6
+
7
+ return context;
8
+ };
9
+
10
+ export default useThemeToggle;
@@ -21,7 +21,7 @@ const Button = styled.button`
21
21
  box-shadow: ${({ theme }) => theme.shadows.tableShadow};
22
22
  border-radius: 50%;
23
23
  svg {
24
- color: ${({ theme }) => theme.colors.neutral0};
24
+ color: ${({ theme }) => theme.colors.buttonNeutral0};
25
25
  }
26
26
  `;
27
27
 
@@ -14,7 +14,10 @@ import init from './init';
14
14
  import { initialState, reducer } from './reducer';
15
15
 
16
16
  const AuthPage = ({ hasAdmin, setHasAdmin }) => {
17
- const { push } = useHistory();
17
+ const {
18
+ push,
19
+ location: { search },
20
+ } = useHistory();
18
21
  const { changeLocale } = useLocalesProvider();
19
22
  const { setSkipped } = useGuidedTour();
20
23
  const { trackUsage } = useTracking();
@@ -119,7 +122,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
119
122
  auth.setToken(token, body.rememberMe);
120
123
  auth.setUserInfo(user, body.rememberMe);
121
124
 
122
- push('/');
125
+ redirectToPreviousLocation();
123
126
  } catch (err) {
124
127
  if (err.response) {
125
128
  const errorMessage = get(
@@ -190,8 +193,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
190
193
  return;
191
194
  }
192
195
 
193
- // Redirect to the homePage
194
- push('/');
196
+ redirectToPreviousLocation();
195
197
  } catch (err) {
196
198
  trackUsage('didNotCreateFirstAdmin');
197
199
 
@@ -239,6 +241,12 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
239
241
  }
240
242
  };
241
243
 
244
+ const redirectToPreviousLocation = () => {
245
+ const locationBeforeAuthenticated = decodeURIComponent(query.get('redirectTo'));
246
+ const redirectUrl = locationBeforeAuthenticated || '/';
247
+ push(redirectUrl);
248
+ };
249
+
242
250
  // Redirect the user to the login page if
243
251
  // the endpoint does not exist or
244
252
  // there is already an admin user oo
@@ -249,7 +257,16 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
249
257
 
250
258
  // Redirect the user to the register-admin if it is the first user
251
259
  if (!hasAdmin && authType !== 'register-admin') {
252
- return <Redirect to="/auth/register-admin" />;
260
+ return (
261
+ <Redirect
262
+ to={{
263
+ pathname: '/auth/register-admin',
264
+ // Forward the `?redirectTo` from /auth/login
265
+ // /abc => /auth/login?redirectTo=%2Fabc => /auth/register-admin?redirectTo=%2Fabc
266
+ search,
267
+ }}
268
+ />
269
+ );
253
270
  }
254
271
 
255
272
  return (
@@ -30,9 +30,6 @@ const StyledReddit = styled(Reddit)`
30
30
  > path:first-child {
31
31
  fill: #ff4500;
32
32
  }
33
- > path:last-child {
34
- fill: ${({ theme }) => theme.colors.neutral0};
35
- }
36
33
  `;
37
34
  const StyledStrapi = styled(Strapi)`
38
35
  > path:first-child {