diff --git a/public/favicon.ico b/public/favicon.ico index 6f357a0..222b983 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/src/components/App/app-top-bar.tsx b/src/components/App/app-top-bar.tsx new file mode 100644 index 0000000..dcf1557 --- /dev/null +++ b/src/components/App/app-top-bar.tsx @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { FunctionComponent, useEffect, useState } from 'react'; +import { capitalize, useTheme } from '@mui/material'; +import { logout, TopBar } from '@gridsuite/commons-ui'; +import { useParameterState } from '../parameters'; +import { + APP_NAME, + PARAM_LANGUAGE, + PARAM_THEME, +} from '../../utils/config-params'; +import { useNavigate } from 'react-router-dom'; +import { useDispatch } from 'react-redux'; +import { AppsMetadataSrv, MetadataJson, StudySrv } from '../../services'; +import { ReactComponent as GridAdminLogoLight } from '../../images/GridAdmin_logo_light.svg'; +import { ReactComponent as GridAdminLogoDark } from '../../images/GridAdmin_logo_dark.svg'; +import AppPackage from '../../../package.json'; +import { AppState } from '../../redux/reducer'; + +export type AppTopBarProps = { + user?: AppState['user']; + userManager: { + instance: unknown | null; + error: string | null; + }; +}; + +const AppTopBar: FunctionComponent = (props) => { + const navigate = useNavigate(); + const theme = useTheme(); + const dispatch = useDispatch(); + + const [appsAndUrls, setAppsAndUrls] = useState([]); + + const [themeLocal, handleChangeTheme] = useParameterState(PARAM_THEME); + const [languageLocal, handleChangeLanguage] = + useParameterState(PARAM_LANGUAGE); + + useEffect(() => { + if (props.user !== null) { + AppsMetadataSrv.fetchAppsAndUrls().then((res) => { + setAppsAndUrls(res); + }); + } + }, [props.user]); + + return ( + + ) : ( + + ) + } + appVersion={AppPackage.version} + appLicense={AppPackage.license} + onLogoutClick={() => logout(dispatch, props.userManager.instance)} + onLogoClick={() => navigate('/', { replace: true })} + user={props.user} + appsAndUrls={appsAndUrls} + globalVersionPromise={() => + AppsMetadataSrv.fetchVersion().then((res) => res?.deployVersion) + } + additionalModulesPromise={StudySrv.getServersInfos} + onThemeClick={handleChangeTheme} + theme={themeLocal} + onLanguageClick={handleChangeLanguage} + language={languageLocal} + /> + ); +}; +export default AppTopBar; diff --git a/src/components/app-wrapper.tsx b/src/components/App/app-wrapper.tsx similarity index 73% rename from src/components/app-wrapper.tsx rename to src/components/App/app-wrapper.tsx index 30a17b3..3b64c33 100644 --- a/src/components/app-wrapper.tsx +++ b/src/components/App/app-wrapper.tsx @@ -6,18 +6,21 @@ */ import App from './app'; -import { FunctionComponent } from 'react'; -import { CssBaseline } from '@mui/material'; +import { FunctionComponent, useMemo } from 'react'; +import { CssBaseline, responsiveFontSizes, ThemeOptions } from '@mui/material'; import { createTheme, StyledEngineProvider, Theme, ThemeProvider, } from '@mui/material/styles'; +import { enUS as MuiCoreEnUS, frFR as MuiCoreFrFR } from '@mui/material/locale'; import { card_error_boundary_en, card_error_boundary_fr, CardErrorBoundary, + LANG_ENGLISH, + LANG_FRENCH, LIGHT_THEME, login_en, login_fr, @@ -28,17 +31,15 @@ import { import { IntlProvider } from 'react-intl'; import { BrowserRouter } from 'react-router-dom'; import { Provider, useSelector } from 'react-redux'; -import { SupportedLanguages } from '../utils/language'; -import messages_en from '../translations/en.json'; -import messages_fr from '../translations/fr.json'; -import messages_plugins_en from '../plugins/translations/en.json'; -import messages_plugins_fr from '../plugins/translations/fr.json'; -import { store } from '../redux/store'; -import { PARAM_THEME } from '../utils/config-params'; +import { SupportedLanguages } from '../../utils/language'; +import messages_en from '../../translations/en.json'; +import messages_fr from '../../translations/fr.json'; +import { store } from '../../redux/store'; +import { PARAM_THEME } from '../../utils/config-params'; import { IntlConfig } from 'react-intl/src/types'; -import { AppState } from '../redux/reducer'; +import { AppState } from '../../redux/reducer'; -const lightTheme: Theme = createTheme({ +const lightTheme: ThemeOptions = { palette: { mode: 'light', }, @@ -62,9 +63,9 @@ const lightTheme: Theme = createTheme({ color: 'blue', }, mapboxStyle: 'mapbox://styles/mapbox/light-v9', -}); +}; -const darkTheme: Theme = createTheme({ +const darkTheme: ThemeOptions = { palette: { mode: 'dark', }, @@ -88,14 +89,15 @@ const darkTheme: Theme = createTheme({ color: 'green', }, mapboxStyle: 'mapbox://styles/mapbox/dark-v9', -}); +}; -const getMuiTheme = (theme: string): Theme => { - if (theme === LIGHT_THEME) { - return lightTheme; - } else { - return darkTheme; - } +const getMuiTheme = (theme: unknown, locale: SupportedLanguages): Theme => { + return responsiveFontSizes( + createTheme( + theme === LIGHT_THEME ? lightTheme : darkTheme, + locale === LANG_FRENCH ? MuiCoreFrFR : MuiCoreEnUS // MUI core translations + ) + ); }; const messages: Record = { @@ -104,14 +106,12 @@ const messages: Record = { ...login_en, ...top_bar_en, ...card_error_boundary_en, - ...messages_plugins_en, // keep it at the end to allow translation overwriting }, fr: { ...messages_fr, ...login_fr, ...top_bar_fr, ...card_error_boundary_fr, - ...messages_plugins_fr, // keep it at the end to allow translation overwriting }, }; @@ -122,14 +122,19 @@ const AppWrapperWithRedux: FunctionComponent = () => { (state: AppState) => state.computedLanguage ); const theme = useSelector((state: AppState) => state[PARAM_THEME]); + const themeCompiled = useMemo( + () => getMuiTheme(theme, computedLanguage), + [computedLanguage, theme] + ); return ( - + diff --git a/src/components/app.test.tsx b/src/components/App/app.test.tsx similarity index 97% rename from src/components/app.test.tsx rename to src/components/App/app.test.tsx index be4ffd1..950285f 100644 --- a/src/components/app.test.tsx +++ b/src/components/App/app.test.tsx @@ -7,7 +7,7 @@ import { IntlProvider } from 'react-intl'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import App from './app'; -import { store } from '../redux/store'; +import { store } from '../../redux/store'; import { createTheme, StyledEngineProvider, diff --git a/src/components/app.tsx b/src/components/App/app.tsx similarity index 96% rename from src/components/app.tsx rename to src/components/App/app.tsx index 36db3a6..856f8a7 100644 --- a/src/components/app.tsx +++ b/src/components/App/app.tsx @@ -28,25 +28,25 @@ import { selectComputedLanguage, selectLanguage, selectTheme, -} from '../redux/actions'; -import { AppState } from '../redux/reducer'; +} from '../../redux/actions'; +import { AppState } from '../../redux/reducer'; import { AppsMetadataSrv, ConfigNotif, ConfigParameters, ConfigSrv, UserAdminSrv, -} from '../services'; +} from '../../services'; import { APP_NAME, COMMON_APP_NAME, PARAM_LANGUAGE, PARAM_THEME, -} from '../utils/config-params'; -import { getComputedLanguage } from '../utils/language'; +} from '../../utils/config-params'; +import { getComputedLanguage } from '../../utils/language'; import AppTopBar, { AppTopBarProps } from './app-top-bar'; import ReconnectingWebSocket from 'reconnecting-websocket'; -import { getErrorMessage } from '../utils/error'; +import { getErrorMessage } from '../../utils/error'; const App: FunctionComponent = () => { const { snackError } = useSnackMessage(); @@ -171,7 +171,7 @@ const App: FunctionComponent = () => { }) ); - ConfigSrv.fetchConfigParameters(APP_NAME.toLowerCase()) + ConfigSrv.fetchConfigParameters(APP_NAME) .then((params) => updateParams(params)) .catch((error) => snackError({ diff --git a/src/components/App/index.ts b/src/components/App/index.ts new file mode 100644 index 0000000..05cce66 --- /dev/null +++ b/src/components/App/index.ts @@ -0,0 +1 @@ +export { default as AppWrapper } from './app-wrapper'; diff --git a/src/components/app-top-bar.tsx b/src/components/app-top-bar.tsx deleted file mode 100644 index ae49f77..0000000 --- a/src/components/app-top-bar.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2021, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { FunctionComponent, useCallback, useEffect, useState } from 'react'; -import { LIGHT_THEME, logout, TopBar } from '@gridsuite/commons-ui'; -import Parameters, { useParameterState } from './parameters'; -import { APP_NAME, PARAM_LANGUAGE, PARAM_THEME } from '../utils/config-params'; -import { useDispatch, useSelector } from 'react-redux'; -import { AppsMetadataSrv, MetadataJson, StudySrv } from '../services'; -import { useNavigate } from 'react-router-dom'; -import { ReactComponent as PowsyblLogo } from '../images/powsybl_logo.svg'; -import AppPackage from '../../package.json'; -import { AppState } from '../redux/reducer'; - -export type AppTopBarProps = { - user?: AppState['user']; - userManager: { - instance: unknown | null; - error: string | null; - }; -}; - -const AppTopBar: FunctionComponent = (props) => { - const navigate = useNavigate(); - - const dispatch = useDispatch(); - - const [appsAndUrls, setAppsAndUrls] = useState([]); - - const theme = useSelector((state: AppState) => state[PARAM_THEME]); - - const [themeLocal, handleChangeTheme] = useParameterState(PARAM_THEME); - - const [languageLocal, handleChangeLanguage] = - useParameterState(PARAM_LANGUAGE); - - const [showParameters, setShowParameters] = useState(false); - const displayParameters = useCallback(() => setShowParameters(true), []); - const hideParameters = useCallback(() => setShowParameters(false), []); - - useEffect(() => { - if (props.user !== null) { - AppsMetadataSrv.fetchAppsAndUrls().then((res) => { - setAppsAndUrls(res); - }); - } - }, [props.user]); - - return ( - <> - //GridAdminLogoLight - ) : ( - //GridAdminLogoDark - ) - } - appVersion={AppPackage.version} - appLicense={AppPackage.license} - onParametersClick={displayParameters} - onLogoutClick={() => - logout(dispatch, props.userManager.instance) - } - onLogoClick={() => navigate('/', { replace: true })} - user={props.user} - appsAndUrls={appsAndUrls} - globalVersionPromise={() => - AppsMetadataSrv.fetchVersion().then( - (res) => res?.deployVersion - ) - } - additionalModulesPromise={StudySrv.getServersInfos} - onThemeClick={handleChangeTheme} - theme={themeLocal} - onLanguageClick={handleChangeLanguage} - language={languageLocal} - /> - - - ); -}; -export default AppTopBar; diff --git a/src/components/parameters.tsx b/src/components/parameters.tsx index e756b24..bf13895 100644 --- a/src/components/parameters.tsx +++ b/src/components/parameters.tsx @@ -5,49 +5,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { - FunctionComponent, - PropsWithChildren, - ReactElement, - useCallback, - useEffect, - useState, -} from 'react'; -import { FormattedMessage } from 'react-intl'; +import { useCallback, useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; -import { - Box, - Button, - Container, - Dialog, - DialogContent, - DialogTitle, - Grid, - Tab, - Tabs, - Typography, - TypographyTypeMap, -} from '@mui/material'; -import { CSSObject, Theme } from '@emotion/react'; import { ConfigSrv } from '../services'; import { useSnackMessage } from '@gridsuite/commons-ui'; import { AppState, AppStateKey } from '../redux/reducer'; -const styles = { - title: (theme: Theme): CSSObject => ({ - padding: theme.spacing(2), - }), - grid: (theme: Theme): CSSObject => ({ - padding: theme.spacing(2), - }), - controlItem: { - justifyContent: 'flex-end', - } as CSSObject, - button: { - marginBottom: '30px', - } as CSSObject, -}; - export function useParameterState( paramName: K ): [AppState[K], (value: AppState[K]) => void] { @@ -76,85 +39,3 @@ export function useParameterState( return [paramLocalState, handleChangeParamLocalState]; } - -function GUITab(): ReactElement { - return ; -} - -type TabPanelProps = PropsWithChildren< - TypographyTypeMap<{ index: number; value: number }, 'div'>['props'] ->; -function TabPanel({ - children, - value, - index, - ...typoProps -}: TabPanelProps): ReactElement { - return ( - - ); -} - -export type ParametersProps = PropsWithChildren<{ - showParameters: boolean; - hideParameters: () => void; -}>; -const Parameters: FunctionComponent = (props) => { - const [tabIndex, setTabIndex] = useState(0); - - return ( - - - - - - - - - setTabIndex(newValue)} - aria-label="parameters" - > - } /> - - - - - - - - - - - - - ); -}; - -export default Parameters; diff --git a/src/images/GridAdmin_logo_dark.svg b/src/images/GridAdmin_logo_dark.svg new file mode 100644 index 0000000..26be3e5 --- /dev/null +++ b/src/images/GridAdmin_logo_dark.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/images/GridAdmin_logo_light.svg b/src/images/GridAdmin_logo_light.svg new file mode 100644 index 0000000..e69b1bb --- /dev/null +++ b/src/images/GridAdmin_logo_light.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/src/images/powsybl_logo.svg b/src/images/powsybl_logo.svg deleted file mode 100644 index 93c9724..0000000 --- a/src/images/powsybl_logo.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/index.tsx b/src/index.tsx index 0c6c8f8..698e4ad 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -10,7 +10,7 @@ import 'typeface-roboto'; import React from 'react'; import { createRoot } from 'react-dom/client'; import './index.css'; -import AppWrapper from './components/app-wrapper'; +import { AppWrapper } from './components/App'; const container = document.getElementById('root'); if (container) { diff --git a/src/module-commons-ui.d.ts b/src/module-commons-ui.d.ts index 0c0aa8c..1d78d56 100644 --- a/src/module-commons-ui.d.ts +++ b/src/module-commons-ui.d.ts @@ -1,4 +1,2 @@ //TODO: remove when commons-ui will include typescript definitions -declare module '@gridsuite/commons-ui' /*{ - export = typeof import('@gridsuite/commons-ui'); -}*/; +declare module '@gridsuite/commons-ui'; diff --git a/src/plugins/README.md b/src/plugins/README.md deleted file mode 100644 index fd2ae97..0000000 --- a/src/plugins/README.md +++ /dev/null @@ -1,52 +0,0 @@ - -# How to add plugins - -Add a plugin component or object in the corresponding group represented as folders. - -```ts -// plugins/myPluginGroup/myNewPlugin.ts -const MyNewPlugin = { - ... -}; -export default MyNewPlugin; -``` - -Edit `index.ts` to export your new plugin in the corresponding group -```ts -import MyNewPlugin from './myPluginGroup/myNewPlugin'; -... -export const MyPluginGroupPlugins = [ - { - id: 'MyNewPlugin', // must be unique in a group - Component: MyNewPlugin, - }, -]; -``` - -Defining and adding a group of plugins needs to add some code in the target component - -```tsx -import { FunctionComponent } from 'react'; -// Plugins -import { MyPluginGroupPlugins } from '../plugins'; -//... - -const MyPluggableComponent: FunctionComponent = () => { - //... - return ( - <> - {/*...*/} - {MyPluginGroupPlugins.map((plugin) => { - return ; - })} - {/*...*/} - - ); -}; -``` - -# How to overwrite translations - -Add your private translations to the following files to complete or overwrite existing translations -* `src/plugins/translations/en.json` -* `src/plugins/translations/fr.json` diff --git a/src/plugins/index.ts b/src/plugins/index.ts deleted file mode 100644 index fe78b97..0000000 --- a/src/plugins/index.ts +++ /dev/null @@ -1 +0,0 @@ -// This file will contain exported plugin collections diff --git a/src/plugins/translations/en.json b/src/plugins/translations/en.json deleted file mode 100644 index 2c63c08..0000000 --- a/src/plugins/translations/en.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/src/plugins/translations/fr.json b/src/plugins/translations/fr.json deleted file mode 100644 index 2c63c08..0000000 --- a/src/plugins/translations/fr.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/src/services/config-notification.ts b/src/services/config-notification.ts index 1da21e7..48bf662 100644 --- a/src/services/config-notification.ts +++ b/src/services/config-notification.ts @@ -12,7 +12,7 @@ import { getUrlWithToken, getWsBase } from '../utils/api-ws'; const PREFIX_CONFIG_NOTIFICATION_WS = `${process.env.REACT_APP_WS_GATEWAY}/config-notification`; export function connectNotificationsWsUpdateConfig(): ReconnectingWebSocket { - const webSocketUrl = `${getWsBase()}${PREFIX_CONFIG_NOTIFICATION_WS}/notify?appName=${APP_NAME.toLowerCase()}`; + const webSocketUrl = `${getWsBase()}${PREFIX_CONFIG_NOTIFICATION_WS}/notify?appName=${APP_NAME}`; const reconnectingWebSocket = new ReconnectingWebSocket( () => getUrlWithToken(webSocketUrl), undefined, diff --git a/src/utils/config-params.ts b/src/utils/config-params.ts index cba6391..6fd3b22 100644 --- a/src/utils/config-params.ts +++ b/src/utils/config-params.ts @@ -8,7 +8,7 @@ import { LiteralUnion } from 'type-fest'; export const COMMON_APP_NAME = 'common'; -export const APP_NAME = 'Admin'; +export const APP_NAME = 'admin'; export const PARAM_THEME = 'theme'; export const PARAM_LANGUAGE = 'language';