From e6f1970f29fd24f837f0ad48b1e4736025965f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riley=20B=C4=85kowska?= <6470178+RiledUpCrow@users.noreply.github.com> Date: Tue, 4 Apr 2023 11:05:31 +0200 Subject: [PATCH] feat: importing themes (#1403) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle AdminJS themes with @adminjs/themes --------- Co-authored-by: Riley Bąkowska <6470178+RiledUpCrow@users.noreply.github.com> Co-authored-by: Arian Sobczak --- package.json | 5 +- src/adminjs-options.interface.ts | 130 ++++---- src/adminjs.ts | 17 + .../utils/options-parser/options-parser.ts | 42 +-- .../utils/view-helpers/view-helpers.ts | 24 +- src/current-admin.interface.ts | 4 + src/frontend/bundle-entry.jsx | 47 +-- src/frontend/components/actions/list.tsx | 2 +- .../app/action-header/styled-back-button.tsx | 6 +- src/frontend/components/app/app-loader.tsx | 8 + src/frontend/components/app/breadcrumbs.tsx | 3 + .../components/app/default-dashboard.tsx | 2 +- src/frontend/components/app/drawer-portal.tsx | 4 +- src/frontend/components/app/index.ts | 10 +- src/frontend/components/app/notice.tsx | 2 +- .../components/app/sidebar/sidebar.tsx | 48 ++- src/frontend/components/app/top-bar.tsx | 26 +- src/frontend/components/login/index.tsx | 56 +++- .../property-type/default-type/edit.tsx | 8 +- src/frontend/components/routes/resource.tsx | 2 +- .../components/routes/utils/wrapper.tsx | 8 +- src/frontend/hoc/allow-override.tsx | 2 +- src/frontend/hoc/index.ts | 1 + src/frontend/hoc/with-no-ssr.tsx | 17 +- src/frontend/hooks/use-history-listen.ts | 4 +- src/frontend/layout-template.tsx | 13 +- src/frontend/login-template.tsx | 5 +- src/frontend/store/actions/add-notice.ts | 8 +- src/frontend/store/actions/index.ts | 7 +- .../store/actions/initialize-assets.ts | 2 +- .../store/actions/initialize-branding.ts | 6 +- .../store/actions/initialize-dashboard.ts | 6 +- .../store/actions/initialize-paths.ts | 8 +- .../store/actions/initialize-theme.ts | 13 + .../store/actions/initialize-versions.ts | 4 +- src/frontend/store/actions/modal.ts | 20 +- src/frontend/store/actions/route-changed.ts | 10 +- .../store/actions/set-current-admin.ts | 8 +- .../store/actions/set-drawer-preroute.ts | 12 +- src/frontend/store/index.ts | 4 +- src/frontend/store/initialize-store.ts | 66 ++-- src/frontend/store/pages-to-store.ts | 11 - src/frontend/store/reducers/assetsReducer.ts | 17 + .../store/reducers/brandingReducer.ts | 17 + .../store/reducers/dashboardReducer.ts | 20 ++ src/frontend/store/reducers/drawerReducer.ts | 23 ++ src/frontend/store/reducers/index.ts | 15 +- src/frontend/store/reducers/localesReducer.ts | 21 ++ src/frontend/store/reducers/modal.ts | 24 -- src/frontend/store/reducers/modalReducer.ts | 26 ++ src/frontend/store/reducers/noticesReducer.ts | 46 +++ src/frontend/store/reducers/pagesReducer.ts | 19 ++ src/frontend/store/reducers/pathsReducer.ts | 24 ++ .../store/reducers/resourcesReducer.ts | 19 ++ src/frontend/store/reducers/routerReducer.ts | 30 ++ src/frontend/store/reducers/sessionReducer.ts | 19 ++ src/frontend/store/reducers/themeReducer.ts | 19 ++ .../store/reducers/versionsReducer.ts | 20 ++ src/frontend/store/store.ts | 306 +++--------------- src/frontend/store/utils/pages-to-store.ts | 10 + src/frontend/utils/adminjs.i18n.ts | 6 + src/utils/theme-bundler.ts | 9 + tsconfig.json | 1 - vendor-types/global.ts | 1 + yarn.lock | 9 +- 65 files changed, 800 insertions(+), 582 deletions(-) create mode 100644 src/frontend/components/app/app-loader.tsx create mode 100644 src/frontend/store/actions/initialize-theme.ts delete mode 100644 src/frontend/store/pages-to-store.ts create mode 100644 src/frontend/store/reducers/assetsReducer.ts create mode 100644 src/frontend/store/reducers/brandingReducer.ts create mode 100644 src/frontend/store/reducers/dashboardReducer.ts create mode 100644 src/frontend/store/reducers/drawerReducer.ts create mode 100644 src/frontend/store/reducers/localesReducer.ts delete mode 100644 src/frontend/store/reducers/modal.ts create mode 100644 src/frontend/store/reducers/modalReducer.ts create mode 100644 src/frontend/store/reducers/noticesReducer.ts create mode 100644 src/frontend/store/reducers/pagesReducer.ts create mode 100644 src/frontend/store/reducers/pathsReducer.ts create mode 100644 src/frontend/store/reducers/resourcesReducer.ts create mode 100644 src/frontend/store/reducers/routerReducer.ts create mode 100644 src/frontend/store/reducers/sessionReducer.ts create mode 100644 src/frontend/store/reducers/themeReducer.ts create mode 100644 src/frontend/store/reducers/versionsReducer.ts create mode 100644 src/frontend/store/utils/pages-to-store.ts create mode 100644 src/utils/theme-bundler.ts diff --git a/package.json b/package.json index 0aba54d3c..26c07bf67 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,9 @@ "types": "./index.d.ts", "import": "./index.js", "require": "./index.js" + }, + "./bundler": { + "import": "./lib/backend/bundler/config.js" } }, "scripts": { @@ -84,7 +87,7 @@ }, "homepage": "https://github.com/SoftwareBrothers/adminjs#readme", "dependencies": { - "@adminjs/design-system": "^4.0.0-beta-v4.7", + "@adminjs/design-system": "^4.0.0-beta-v4.8", "@babel/core": "^7.21.0", "@babel/parser": "^7.21.0", "@babel/plugin-syntax-import-assertions": "^7.20.0", diff --git a/src/adminjs-options.interface.ts b/src/adminjs-options.interface.ts index 3d4e967c3..8fe314fab 100644 --- a/src/adminjs-options.interface.ts +++ b/src/adminjs-options.interface.ts @@ -184,57 +184,41 @@ export interface AdminJSOptions { */ env?: Record; - /* cspell: disable */ - /** - * Translation file. Change it in order to: + * Translations + * + * Change it in order to: * - localize admin panel * - change any arbitrary text in the UI * * This is the example for changing name of a couple of resources along with some - * properties to Polish + * properties to Polish. You can also use this technic to change any text even in english. + * So to change button label of a "new action" from default "Create new" to "Create new Comment" + * only for Comment resource, place it in action section. * * ```javascript * { - * ... - * locale: { - * language: 'pl', - * translations: { - * labels: { - * Comments: 'Komentarze', - * } - * resources: { - * Comments: { - * properties: { - * name: 'Nazwa Komentarza', - * content: 'Zawartość', + * locale: { + * translations: { + * pl: { + * labels: { + * Comments: "Komentarze", + * }, + * resources: { + * Comments: { + * properties: { + * name: "Nazwa Komentarza", + * content: "Zawartość", + * }, + * actions: { + * new: 'Create new Comment' * } - * } - * } - * } - * } - * } - * ``` - * - * As I mentioned you can use this technic to change any text even in english. - * So to change button label for a "new action" from default "Create new" to "Create new Comment" - * only for Comment resource you can do: - * - * ```javascript - * { - * ... - * locale: { - * translations: { - * resources: { - * Comments: { - * actions: { - * new: 'Create new Comment', - * } - * } - * } - * } - * } - * } + * } + * } + * } + * } + * } + *} * ``` * * Check out the [i18n tutorial]{@tutorial i18n} to see how @@ -251,6 +235,25 @@ export interface AdminJSOptions { * Additional settings. */ settings?: Partial; + + /** + * List of available themes, for example exports of the `@adminjs/themes` npm package. + */ + availableThemes?: ThemeConfig[]; + + /** + * ID of the default theme. If not provided, the first theme from the `availableThemes` + * list will be used. + */ + defaultTheme?: string; +} + +export type ThemeConfig = { + id: string, + name: string, + overrides: Partial; + bundlePath?: string; + stylePath?: string; } export type AdminJSSettings = { @@ -282,7 +285,7 @@ export type Assets = { * Mapping of core scripts in case you want to version your assets */ coreScripts?: CoreScripts; -} +}; /** * @alias AssetsFunction @@ -292,7 +295,7 @@ export type Assets = { * @description * Function returning {@link Assets} */ -export type AssetsFunction = (admin?: CurrentAdmin) => Assets | Promise +export type AssetsFunction = (admin?: CurrentAdmin) => Assets | Promise; /** * Version Props @@ -309,12 +312,12 @@ export type VersionSettings = { * You can pass here your current API version. */ app?: string; -} +}; export type VersionProps = { admin?: string; app?: string; -} +}; /** * Branding Options @@ -358,7 +361,7 @@ export type BrandingOptions = { * URL to a favicon */ favicon?: string; -} +}; /** * Branding Options Function @@ -370,8 +373,8 @@ export type BrandingOptions = { * @returns {BrandingOptions | Promise} */ export type BrandingOptionsFunction = ( - admin?: CurrentAdmin -) => BrandingOptions | Promise + admin?: CurrentAdmin, +) => BrandingOptions | Promise; /** * Object describing regular page in AdminJS @@ -393,7 +396,7 @@ export type AdminPage = { * Page icon */ icon?: string; -} +}; /** * Object describing map of regular pages in AdminJS @@ -401,7 +404,7 @@ export type AdminPage = { * @alias AdminPages * @memberof AdminJSOptions */ -export type AdminPages = Record +export type AdminPages = Record; /** * Default way of passing Options with a Resource @@ -412,7 +415,7 @@ export type ResourceWithOptions = { resource: any; options: ResourceOptions; features?: Array; -} +}; /** * Function taking {@link ResourceOptions} and merging it with all other options @@ -430,8 +433,8 @@ export type FeatureType = ( /** * Options returned by the feature added before */ - options: ResourceOptions -) => ResourceOptions + options: ResourceOptions, +) => ResourceOptions; /** * Function which is invoked when user enters given AdminPage @@ -439,11 +442,7 @@ export type FeatureType = ( * @alias PageHandler * @memberof AdminJSOptions */ -export type PageHandler = ( - request: any, - response: any, - context: PageContext, -) => Promise +export type PageHandler = (request: any, response: any, context: PageContext) => Promise; /** * Bundle options @@ -462,17 +461,20 @@ export type BundlerOptions = { * The file path to babel config file or json object of babel config. */ babelConfig?: BabelConfig | string; -} +}; export interface AdminJSOptionsWithDefault extends AdminJSOptions { rootPath: string; logoutPath: string; loginPath: string; databases?: Array; - resources?: Array; + resources?: Array< + | BaseResource + | { + resource: BaseResource; + options: ResourceOptions; + } + >; dashboard: { handler?: PageHandler; component?: string; diff --git a/src/adminjs.ts b/src/adminjs.ts index ca7c6d00f..a85676ee6 100644 --- a/src/adminjs.ts +++ b/src/adminjs.ts @@ -19,8 +19,10 @@ import { ListActionResponse } from './backend/actions/list/list-action.js' import { defaultLocale, Locale } from './locale/index.js' import { TranslateFunctions } from './utils/translate-functions.factory.js' import { relativeFilePathResolver } from './utils/file-resolver.js' +import { Router } from './backend/utils/index.js' import { ComponentLoader } from './backend/utils/component-loader.js' import { OverridableComponent } from './frontend/index.js' +import { bundlePath, stylePath } from './utils/theme-bundler.js' const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8')) @@ -123,6 +125,8 @@ class AdminJS { const resourcesFactory = new ResourcesFactory(this, global.RegisteredAdapters || []) this.resources = resourcesFactory.buildResources({ databases, resources }) + + this.addThemeAssets() } /** @@ -299,6 +303,19 @@ class AdminJS { return name } + addThemeAssets() { + this.options.availableThemes?.forEach((theme) => { + Router.assets.push({ + path: `/frontend/assets/themes/${theme.id}/theme.bundle.js`, + src: theme.bundlePath ?? bundlePath(theme.id), + }) + Router.assets.push({ + path: `/frontend/assets/themes/${theme.id}/style.css`, + src: theme.stylePath ?? stylePath(theme.id), + }) + }) + } + private static __unsafe_componentIndex = 0 public static __unsafe_staticComponentLoader = new ComponentLoader() diff --git a/src/backend/utils/options-parser/options-parser.ts b/src/backend/utils/options-parser/options-parser.ts index a7804dc8d..b5364c52e 100644 --- a/src/backend/utils/options-parser/options-parser.ts +++ b/src/backend/utils/options-parser/options-parser.ts @@ -1,28 +1,24 @@ import merge from 'lodash/merge.js' -import AdminJS from '../../../adminjs.js' import { AdminJSOptions, Assets, BrandingOptions } from '../../../adminjs-options.interface.js' +import AdminJS from '../../../adminjs.js' import { CurrentAdmin } from '../../../current-admin.interface.js' +import { ThemeInState } from '../../../index.js' +import { Locale, defaultLocale } from '../../../locale/index.js' import ViewHelpers from '../view-helpers/view-helpers.js' -import { defaultLocale, Locale } from '../../../locale/index.js' const defaultBranding: AdminJSOptions['branding'] = { companyName: 'Company', withMadeWithLove: true, } -const defaultAssets = { +const defaultAssets: Assets = { styles: [], scripts: [], } -export const getAssets = async ( - admin: AdminJS, - currentAdmin?: CurrentAdmin, -): Promise => { +export const getAssets = async (admin: AdminJS, currentAdmin?: CurrentAdmin): Promise => { const { assets } = admin.options || {} - const computed = typeof assets === 'function' - ? await assets(currentAdmin) - : assets + const computed = typeof assets === 'function' ? await assets(currentAdmin) : assets return merge({}, defaultAssets, computed) } @@ -36,9 +32,7 @@ export const getBranding = async ( const h = new ViewHelpers(admin) const defaultLogo = h.assetPath('logo.svg') - const computed = typeof branding === 'function' - ? await branding(currentAdmin) - : branding + const computed = typeof branding === 'function' ? await branding(currentAdmin) : branding const merged = merge({}, defaultBranding, computed) // checking for undefined because logo can also be `false` or `null` @@ -47,18 +41,26 @@ export const getBranding = async ( return merged } -export const getLocales = async ( - admin: AdminJS, - currentAdmin?: CurrentAdmin, -): Promise => { +export const getLocales = async (admin: AdminJS, currentAdmin?: CurrentAdmin): Promise => { const { locale } = admin.options || {} - const computed = typeof locale === 'function' - ? await locale(currentAdmin) - : locale + const computed = typeof locale === 'function' ? await locale(currentAdmin) : locale return merge({}, defaultLocale, computed) } +export const getTheme = async ( + admin: AdminJS, + currentAdmin?: CurrentAdmin, +): Promise => { + const { availableThemes, defaultTheme } = admin.options + let themeId = defaultTheme ?? availableThemes?.[0].id + if (currentAdmin?.theme?.length) { + themeId = currentAdmin?.theme + } + const theme = availableThemes?.find(({ id }) => id === themeId) + return theme ? { ...theme, availableThemes } : null +} + export const getFaviconFromBranding = (branding: BrandingOptions): string => { if (branding.favicon) { const { favicon } = branding diff --git a/src/backend/utils/view-helpers/view-helpers.ts b/src/backend/utils/view-helpers/view-helpers.ts index 9caf75d7a..1cb4bbfd4 100644 --- a/src/backend/utils/view-helpers/view-helpers.ts +++ b/src/backend/utils/view-helpers/view-helpers.ts @@ -1,5 +1,7 @@ import { AdminJSOptions, Assets } from '../../../adminjs-options.interface.js' -import { Paths } from '../../../frontend/store/store.js' +import type { PathsInState } from '../../../index.js' + +type Paths = PathsInState let globalAny: any = {} @@ -20,15 +22,15 @@ export type ActionParams = { /** * Unique Resource ID */ - resourceId: string; + resourceId: string /** * Action name */ - actionName: string; + actionName: string /** * Optional query string: ?.... */ - search? : string; + search?: string } /** @@ -41,7 +43,7 @@ export type RecordActionParams = ActionParams & { /** * Record ID */ - recordId: string; + recordId: string } /** @@ -54,7 +56,7 @@ export type BulkActionParams = ActionParams & { /** * Array of Records ID */ - recordIds?: Array; + recordIds?: Array } /** @@ -85,7 +87,7 @@ export class ViewHelpers { } static getPaths(options?: AdminJSOptions): Paths { - return options || (globalAny.REDUX_STATE?.paths) + return options || globalAny.REDUX_STATE?.paths } /** @@ -101,7 +103,9 @@ export class ViewHelpers { const replace = new RegExp(`${separator}{1,}`, 'g') let { rootPath } = this.options - if (!rootPath.startsWith(separator)) { rootPath = `${separator}${rootPath}` } + if (!rootPath.startsWith(separator)) { + rootPath = `${separator}${rootPath}` + } const parts = [rootPath, ...paths] return `${parts.join(separator).replace(replace, separator)}${search}` @@ -248,9 +252,7 @@ export class ViewHelpers { * @return {string} */ bulkActionUrl({ resourceId, recordIds, actionName, search }: BulkActionParams): string { - const url = this.urlBuilder([ - 'resources', resourceId, 'bulk', actionName, - ]) + const url = this.urlBuilder(['resources', resourceId, 'bulk', actionName]) if (recordIds && recordIds.length) { const query = new URLSearchParams(search) query.set('recordIds', recordIds.join(',')) diff --git a/src/current-admin.interface.ts b/src/current-admin.interface.ts index fa5541ceb..bcb836e88 100644 --- a/src/current-admin.interface.ts +++ b/src/current-admin.interface.ts @@ -27,6 +27,10 @@ export type CurrentAdmin = { * Id of your admin user */ id?: string; + /** + * Optional ID of theme to use + */ + theme?: string; /** * Also you can put as many other fields to it as you like. */ diff --git a/src/frontend/bundle-entry.jsx b/src/frontend/bundle-entry.jsx index e8b0305fc..75d0a7fd1 100644 --- a/src/frontend/bundle-entry.jsx +++ b/src/frontend/bundle-entry.jsx @@ -1,20 +1,21 @@ +import { ThemeProvider } from '@adminjs/design-system/styled-components' import React, { Suspense } from 'react' import { I18nextProvider } from 'react-i18next' import { Provider } from 'react-redux' import { BrowserRouter } from 'react-router-dom' -import { ThemeProvider } from '@adminjs/design-system/styled-components' import ViewHelpers from '../backend/utils/view-helpers/view-helpers.js' import { flat } from '../utils/flat/index.js' import * as AppComponents from './components/app/index.js' import App from './components/application.js' -import BasePropertyComponent, { CleanPropertyComponent } from './components/property-type/index.js' +import { AppLoader } from './components/index.js' import Login from './components/login/index.js' +import BasePropertyComponent, { CleanPropertyComponent } from './components/property-type/index.js' import withNotice from './hoc/with-notice.js' import * as Hooks from './hooks/index.js' import createStore from './store/store.js' -import ApiClient from './utils/api-client.js' import initTranslations from './utils/adminjs.i18n.js' +import ApiClient from './utils/api-client.js' const env = { NODE_ENV: process.env.NODE_ENV || 'development', @@ -26,32 +27,32 @@ const { locale } = store.getState() const { i18n } = initTranslations(locale) const Application = ( - - - - - + + + + + }> - - - - - + + + + + ) const loginAppProps = window.__APP_STATE__ ?? {} const LoginApplication = ( - - - - - + + + + + }> - - - - - + + + + + ) // eslint-disable-next-line no-undef diff --git a/src/frontend/components/actions/list.tsx b/src/frontend/components/actions/list.tsx index 94c31e146..f85dc670d 100644 --- a/src/frontend/components/actions/list.tsx +++ b/src/frontend/components/actions/list.tsx @@ -58,7 +58,7 @@ const List: React.FC = ({ resource, setTag }) => { const contentTag = getActionElementCss(resource.id, 'list', 'table-wrapper') return ( - + )`${ButtonCSS}` @@ -20,8 +20,8 @@ export type StyledBackButtonProps = { const StyledBackButton: React.FC = (props) => { const { showInDrawer } = props - const { previousRoute } = useSelector((state) => state.drawer) - const { from = {} } = useSelector((state) => state.router) + const { previousRoute } = useSelector((state) => state.drawer) + const { from = {} } = useSelector((state) => state.router) const cssCloseIcon = showInDrawer ? 'ChevronRight' : 'ChevronLeft' const backLink = useMemo(() => { diff --git a/src/frontend/components/app/app-loader.tsx b/src/frontend/components/app/app-loader.tsx new file mode 100644 index 000000000..b17b8af72 --- /dev/null +++ b/src/frontend/components/app/app-loader.tsx @@ -0,0 +1,8 @@ +import { Box, Loader } from '@adminjs/design-system' +import React, { FC } from 'react' + +export const AppLoader: FC = () => ( + + + +) diff --git a/src/frontend/components/app/breadcrumbs.tsx b/src/frontend/components/app/breadcrumbs.tsx index 245c87b00..bda862e6b 100644 --- a/src/frontend/components/app/breadcrumbs.tsx +++ b/src/frontend/components/app/breadcrumbs.tsx @@ -18,6 +18,9 @@ export const BreadcrumbLink: any = styled(Link)` &:hover { color: ${({ theme }): string => theme.colors.primary100}; + &:after { + color: ${({ theme }): string => theme.colors.grey60}; + } } &:after { diff --git a/src/frontend/components/app/default-dashboard.tsx b/src/frontend/components/app/default-dashboard.tsx index d9577dca5..ab7ca0832 100644 --- a/src/frontend/components/app/default-dashboard.tsx +++ b/src/frontend/components/app/default-dashboard.tsx @@ -101,7 +101,7 @@ const Card = styled(Box)` ` Card.defaultProps = { - variant: 'white', + variant: 'container', boxShadow: 'card', } diff --git a/src/frontend/components/app/drawer-portal.tsx b/src/frontend/components/app/drawer-portal.tsx index f25aca620..db19ade19 100644 --- a/src/frontend/components/app/drawer-portal.tsx +++ b/src/frontend/components/app/drawer-portal.tsx @@ -5,7 +5,7 @@ import { createRoot } from 'react-dom/client' import { Drawer, DEFAULT_DRAWER_WIDTH } from '@adminjs/design-system' import { ThemeProvider } from '@adminjs/design-system/styled-components' -import { ReduxState, RouterProps } from '../../store/index.js' +import { ReduxState, RouterInState } from '../../store/index.js' import { setDrawerPreRoute } from '../../store/actions/set-drawer-preroute.js' /** @@ -73,7 +73,7 @@ const getOrCreatePortalContainer = (id: string) => { */ export const DrawerPortal: React.FC = ({ children, width }) => { const [drawerElement, setDrawerElement] = useState(document.getElementById(DRAWER_PORTAL_ID)) - const { to = null } = useSelector((state) => state.router) + const { to = null } = useSelector((state) => state.router) const dispatch = useDispatch() const handleDrawerMount = () => { diff --git a/src/frontend/components/app/index.ts b/src/frontend/components/app/index.ts index 556d6e304..1e9e9eed2 100644 --- a/src/frontend/components/app/index.ts +++ b/src/frontend/components/app/index.ts @@ -1,19 +1,19 @@ -export * from './records-table/index.js' -export * from './sidebar/index.js' export * from './action-button/index.js' export * from './action-header/index.js' +export * from './admin-modal.js' +export * from './app-loader.js' export * from './base-action-component.js' export * from './breadcrumbs.js' export * from './default-dashboard.js' -export * from './error-boundary.js' export * from './drawer-portal.js' export * from './error-boundary.js' export * from './error-message.js' export * from './filter-drawer.js' export * from './logged-in.js' export * from './notice.js' -export { default as SortLink } from './sort-link.js' +export * from './records-table/index.js' +export * from './sidebar/index.js' export * from './sort-link.js' +export { default as SortLink } from './sort-link.js' export * from './top-bar.js' export * from './version.js' -export * from './admin-modal.js' diff --git a/src/frontend/components/app/notice.tsx b/src/frontend/components/app/notice.tsx index b9cb4ef79..c27b31658 100644 --- a/src/frontend/components/app/notice.tsx +++ b/src/frontend/components/app/notice.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react' import { connect } from 'react-redux' import { MessageBox } from '@adminjs/design-system' -import { NoticeMessageInState, ReduxState } from '../../store/store.js' +import { NoticeMessageInState, ReduxState } from '../../store/index.js' import { dropNotice } from '../../store/actions/drop-notice.js' import { setNoticeProgress } from '../../store/actions/set-notice-progress.js' import allowOverride from '../../hoc/allow-override.js' diff --git a/src/frontend/components/app/sidebar/sidebar.tsx b/src/frontend/components/app/sidebar/sidebar.tsx index b375034f4..9981eeb71 100644 --- a/src/frontend/components/app/sidebar/sidebar.tsx +++ b/src/frontend/components/app/sidebar/sidebar.tsx @@ -1,30 +1,34 @@ +import { Box, BoxProps, cssClass } from '@adminjs/design-system' +import { styled } from '@adminjs/design-system/styled-components' import React from 'react' import { useSelector } from 'react-redux' -import { Box, cssClass, themeGet } from '@adminjs/design-system' -import { styled } from '@adminjs/design-system/styled-components' -import { BrandingOptions } from '../../../../adminjs-options.interface.js' -import { ResourceJSON, PageJSON } from '../../../interfaces/index.js' -import SidebarBranding from './sidebar-branding.js' -import SidebarPages from './sidebar-pages.js' +import allowOverride from '../../../hoc/allow-override.js' import { ReduxState } from '../../../store/store.js' +import SidebarBranding from './sidebar-branding.js' import SidebarFooter from './sidebar-footer.js' +import SidebarPages from './sidebar-pages.js' import SidebarResourceSection from './sidebar-resource-section.js' -import allowOverride from '../../../hoc/allow-override.js' type Props = { - isVisible: boolean; -}; + isVisible: boolean +} -const StyledSidebar = styled(Box)` - transition: left 0.3s; +const StyledSidebar = styled(Box)` top: 0; bottom: 0; flex-shrink: 0; overflow-y: auto; + width: ${({ theme }) => theme.sizes.sidebarWidth}; + border-right: ${({ theme }) => theme.borders.default}; + display: flex; + flex-direction: column; + z-index: 50; + background: ${({ theme }) => theme.colors.sidebar}; + transition: left 300ms ease-in; &.hidden { - left: -${themeGet('sizes', 'sidebarWidth')}; + left: -${({ theme }) => theme.sizes.sidebarWidth}; } &.visible { left: 0; @@ -32,26 +36,14 @@ const StyledSidebar = styled(Box)` ` StyledSidebar.defaultProps = { - position: ['absolute', 'absolute', 'absolute', 'absolute', 'inherit'], - width: 'sidebarWidth', - borderRight: 'default', - display: 'flex', - flexDirection: 'column', - zIndex: 50, - bg: 'white', + position: ['absolute', 'absolute', 'absolute', 'absolute', 'initial'], } const SidebarOriginal: React.FC = (props) => { const { isVisible } = props - const [branding, resources, pages]: [ - BrandingOptions, - ResourceJSON[], - PageJSON[] - ] = useSelector((state: ReduxState) => [ - state.branding, - state.resources, - state.pages, - ]) + const branding = useSelector((state: ReduxState) => state.branding) + const resources = useSelector((state: ReduxState) => state.resources) + const pages = useSelector((state: ReduxState) => state.pages) return ( diff --git a/src/frontend/components/app/top-bar.tsx b/src/frontend/components/app/top-bar.tsx index 5ae2165ab..76613effb 100644 --- a/src/frontend/components/app/top-bar.tsx +++ b/src/frontend/components/app/top-bar.tsx @@ -1,20 +1,18 @@ +import { Box, BoxProps, Icon, cssClass, themeGet } from '@adminjs/design-system' +import { styled } from '@adminjs/design-system/styled-components' import React from 'react' import { useSelector } from 'react-redux' -import { cssClass, Box, Icon, themeGet } from '@adminjs/design-system' -import { styled } from '@adminjs/design-system/styled-components' import allowOverride from '../../hoc/allow-override.js' +import { ReduxState } from '../../store/store.js' +import LanguageSelect from './language-select/language-select.js' import LoggedIn from './logged-in.js' import Version from './version.js' -import { ReduxState, Paths } from '../../store/store.js' -import { CurrentAdmin } from '../../../current-admin.interface.js' -import { VersionProps } from '../../../adminjs-options.interface.js' -import LanguageSelect from './language-select/language-select.js' -const NavBar = styled(Box)` +const NavBar = styled(Box)` height: ${({ theme }) => theme.sizes.navbarHeight}; border-bottom: ${themeGet('borders', 'default')}; - background: ${({ theme }) => theme.colors.white}; + background: ${({ theme }) => theme.colors.container}; display: flex; flex-direction: row; flex-shrink: 0; @@ -30,13 +28,9 @@ type Props = { const TopBar: React.FC = (props) => { const { toggleSidebar } = props - const [session, paths, versions] = useSelector( - (state: ReduxState): [CurrentAdmin | null, Paths, VersionProps] => [ - state.session, - state.paths, - state.versions, - ], - ) + const session = useSelector((state: ReduxState) => state.session) + const paths = useSelector((state: ReduxState) => state.paths) + const versions = useSelector((state: ReduxState) => state.versions) return ( @@ -58,4 +52,4 @@ const TopBar: React.FC = (props) => { const OverridableTopbar = allowOverride(TopBar, 'TopBar') -export { OverridableTopbar as default, OverridableTopbar as TopBar } +export { OverridableTopbar as TopBar, OverridableTopbar as default } diff --git a/src/frontend/components/login/index.tsx b/src/frontend/components/login/index.tsx index 91a5f4b63..e4f10f489 100644 --- a/src/frontend/components/login/index.tsx +++ b/src/frontend/components/login/index.tsx @@ -1,14 +1,24 @@ import { - Box, Button, FormGroup, H2, H5, Illustration, - Input, Label, MadeWithLove, MessageBox, Text, BoxProps, + Box, + BoxProps, + Button, + FormGroup, + H2, + H5, + Illustration, + Input, + Label, + MadeWithLove, + MessageBox, + Text, } from '@adminjs/design-system' import { styled } from '@adminjs/design-system/styled-components' import React from 'react' import { useSelector } from 'react-redux' +import { allowOverride } from '../../hoc/allow-override.js' import { useTranslation } from '../../hooks/index.js' import { ReduxState } from '../../store/store.js' -import { allowOverride } from '../../hoc/allow-override.js' const Wrapper = styled(Box)` align-items: center; @@ -22,9 +32,22 @@ const StyledLogo = styled.img` margin: ${({ theme }) => theme.space.md} 0; ` +const IllustrationsWrapper = styled(Box)` + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + & svg [stroke='#3B3552'] { + stroke: rgba(255, 255, 255, 0.5); + } + & svg [fill='#3040D6'] { + fill: rgba(255, 255, 255, 1); + } +` + export type LoginProps = { - message?: string; - action: string; + message?: string + action: string } export const Login: React.FC = (props) => { @@ -48,7 +71,7 @@ export const Login: React.FC = (props) => { {translateComponent('Login.welcomeMessage')} - + @@ -58,7 +81,7 @@ export const Login: React.FC = (props) => { - + = (props) => { >
{branding.logo ? ( - - ) : branding.companyName} + + ) : ( + branding.companyName + )}
{message && ( = (props) => { /> - +
- {branding.withMadeWithLove ? () : null} + {branding.withMadeWithLove ? ( + + + + ) : null} ) } diff --git a/src/frontend/components/property-type/default-type/edit.tsx b/src/frontend/components/property-type/default-type/edit.tsx index 8c2aa5723..7647ef3f4 100644 --- a/src/frontend/components/property-type/default-type/edit.tsx +++ b/src/frontend/components/property-type/default-type/edit.tsx @@ -6,6 +6,7 @@ import { EditPropertyProps } from '../base-property-props.js' import { recordPropertyIsEqual } from '../record-property-is-equal.js' import { PropertyLabel } from '../utils/property-label/index.js' import allowOverride from '../../../hoc/allow-override.js' +import { useTranslation } from '../../../hooks/index.js' type CombinedProps = EditPropertyProps @@ -24,16 +25,19 @@ const Edit: FC = (props) => { const SelectEdit: FC = (props) => { const { record, property, onChange } = props + const { translateProperty } = useTranslation() if (!property.availableValues) { return null } const propValue = record.params?.[property.path] ?? '' - const selected = property.availableValues.find((av) => av.value === propValue) + // eslint-disable-next-line max-len + const availableValues = property.availableValues.map((v) => ({ ...v, label: translateProperty(v.label) })) + const selected = availableValues.find((av) => av.value === propValue) return (