diff --git a/frontend/src/app/root/index.tsx b/frontend/src/app/root/index.tsx index a3cb5487c..d88b43af3 100644 --- a/frontend/src/app/root/index.tsx +++ b/frontend/src/app/root/index.tsx @@ -1,5 +1,8 @@ -import { LoadingScreen, Layout, useOdeClient } from "@edifice-ui/react"; +import { LoadingScreen } from "@edifice-ui/react"; import { Outlet } from "react-router-dom"; + +import { Layout, useOdeClient } from "~/utils/context.utils"; + function Root() { console.log("i am in root with outlet"); diff --git a/frontend/src/common/ShareModal/ShareModal.tsx b/frontend/src/common/ShareModal/ShareModal.tsx index a7fd336d8..03cc066d1 100644 --- a/frontend/src/common/ShareModal/ShareModal.tsx +++ b/frontend/src/common/ShareModal/ShareModal.tsx @@ -11,7 +11,6 @@ import { Button, Tooltip, Combobox, - useOdeClient, } from "@edifice-ui/react"; import { UseMutationResult } from "@tanstack/react-query"; import { @@ -32,6 +31,7 @@ import { FOLDER_TYPE } from "~/core/enums/folder-type.enum"; import { Folder } from "~/models/folder.model"; import { useBoardsNavigation } from "~/providers/BoardsNavigationProvider"; import { useFoldersNavigation } from "~/providers/FoldersNavigationProvider"; +import { useOdeClient } from "~/utils/context.utils"; import "./ShareModal.scss"; export type ShareOptions = { diff --git a/frontend/src/common/ShareModal/hooks/useSearch.tsx b/frontend/src/common/ShareModal/hooks/useSearch.tsx index 355b0b745..85554a350 100644 --- a/frontend/src/common/ShareModal/hooks/useSearch.tsx +++ b/frontend/src/common/ShareModal/hooks/useSearch.tsx @@ -1,12 +1,7 @@ import { ChangeEvent, Dispatch, useEffect, useReducer } from "react"; import { Bookmark } from "@edifice-ui/icons"; -import { - OptionListItemType, - useDebounce, - useIsAdml, - useOdeClient, -} from "@edifice-ui/react"; +import { OptionListItemType, useDebounce, useIsAdml } from "@edifice-ui/react"; import { ShareRight, ShareRightAction, @@ -18,6 +13,7 @@ import { useTranslation } from "react-i18next"; import { ShareAction } from "./useShare"; import { ShareOptions } from "../ShareModal"; +import { useOdeClient } from "~/utils/context.utils"; type State = { searchInputValue: string; diff --git a/frontend/src/common/ShareModal/hooks/useShare.ts b/frontend/src/common/ShareModal/hooks/useShare.ts index 15c088082..db2055785 100644 --- a/frontend/src/common/ShareModal/hooks/useShare.ts +++ b/frontend/src/common/ShareModal/hooks/useShare.ts @@ -1,6 +1,6 @@ import { useEffect, useReducer } from "react"; -import { useOdeClient, useUser, useToast } from "@edifice-ui/react"; +import { useUser, useToast } from "@edifice-ui/react"; import { odeServices, PutShareResponse, @@ -15,6 +15,7 @@ import { useDispatch } from "react-redux"; import { ShareOptions, ShareResourceMutation } from "../ShareModal"; import { boardsApi } from "~/services/api/boards.service.ts"; import { foldersApi } from "~/services/api/folders.service.ts"; +import { useOdeClient } from "~/utils/context.utils"; interface UseShareResourceModalProps { /** diff --git a/frontend/src/components/board-item/BoardItem.tsx b/frontend/src/components/board-item/BoardItem.tsx index b5abff2a1..45a694d40 100644 --- a/frontend/src/components/board-item/BoardItem.tsx +++ b/frontend/src/components/board-item/BoardItem.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { Card, useOdeClient, Tooltip } from "@edifice-ui/react"; +import { Card, Tooltip } from "@edifice-ui/react"; import { mdiAccountCircle, mdiCalendarBlank, @@ -19,6 +19,7 @@ import "./BoardItem.scss"; import { LAYOUT_TYPE } from "~/core/enums/layout-type.enum"; import { Board } from "~/models/board.model"; import { useBoardsNavigation } from "~/providers/BoardsNavigationProvider"; +import { useOdeClient } from "~/utils/context.utils"; interface BoardItemProps { board: { diff --git a/frontend/src/components/empty-screen/index.tsx b/frontend/src/components/empty-screen/index.tsx index 7b2b463ad..0942839b8 100644 --- a/frontend/src/components/empty-screen/index.tsx +++ b/frontend/src/components/empty-screen/index.tsx @@ -1,6 +1,8 @@ -import { useOdeClient, useOdeTheme, EmptyScreen } from "@edifice-ui/react"; +import { useOdeTheme, EmptyScreen } from "@edifice-ui/react"; import { useTranslation } from "react-i18next"; +import { useOdeClient } from "~/utils/context.utils"; + export default function EmptyScreenApp(): JSX.Element { const { appCode } = useOdeClient(); const { theme } = useOdeTheme(); diff --git a/frontend/src/components/folder-item/FolderItem.tsx b/frontend/src/components/folder-item/FolderItem.tsx index 1eaa5f905..dd1b2a920 100644 --- a/frontend/src/components/folder-item/FolderItem.tsx +++ b/frontend/src/components/folder-item/FolderItem.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useState } from "react"; -import { Card, useOdeClient } from "@edifice-ui/react"; +import { Card } from "@edifice-ui/react"; import "./FolderItem.scss"; import { mdiFolder, mdiFolderAccount } from "@mdi/js"; import Icon from "@mdi/react"; @@ -12,6 +12,7 @@ import { Board } from "~/models/board.model"; import { Folder } from "~/models/folder.model"; import { useFoldersNavigation } from "~/providers/FoldersNavigationProvider"; import { useMoveBoardsMutation } from "~/services/api/boards.service"; +import { useOdeClient } from "~/utils/context.utils"; import { UserRights } from "~/utils/share.utils"; type FolderListProps = { diff --git a/frontend/src/components/header/Header.tsx b/frontend/src/components/header/Header.tsx index de38ddbf9..976f01790 100644 --- a/frontend/src/components/header/Header.tsx +++ b/frontend/src/components/header/Header.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { AppHeader, Button, useOdeClient } from "@edifice-ui/react"; +import { AppHeader, Button } from "@edifice-ui/react"; import "./Header.scss"; import MenuIcon from "@mui/icons-material/Menu"; import IconButton from "@mui/material/IconButton"; @@ -9,6 +9,7 @@ import { useTranslation } from "react-i18next"; import myimg from "./uni-magneto.png"; import { FOLDER_TYPE } from "~/core/enums/folder-type.enum"; import { useFoldersNavigation } from "~/providers/FoldersNavigationProvider"; +import { useOdeClient } from "~/utils/context.utils"; import { UserRights } from "~/utils/share.utils"; interface HeaderProps { diff --git a/frontend/src/components/magnets-collection/MagnetsCollectionModal.tsx b/frontend/src/components/magnets-collection/MagnetsCollectionModal.tsx index 5353ded6f..3252ff246 100644 --- a/frontend/src/components/magnets-collection/MagnetsCollectionModal.tsx +++ b/frontend/src/components/magnets-collection/MagnetsCollectionModal.tsx @@ -1,6 +1,6 @@ import React, { FunctionComponent, useState } from "react"; -import { Modal, SearchBar, useOdeClient, useToggle } from "@edifice-ui/react"; +import { Modal, SearchBar, useToggle } from "@edifice-ui/react"; import { Switch } from "@mui/material"; import { useSpring } from "@react-spring/web"; import { useTranslation } from "react-i18next"; @@ -14,6 +14,7 @@ import { Card as CardModel, ICardItemResponse } from "~/models/card.model"; import "./MagnetsCollectionModal.scss"; import { SVGProvider } from "~/providers/SVGProvider/index.tsx"; import { useGetAllBoardsQuery } from "~/services/api/boards.service"; +import { useOdeClient } from "~/utils/context.utils"; type props = { isOpen: boolean; diff --git a/frontend/src/components/move-board/MoveBoard.tsx b/frontend/src/components/move-board/MoveBoard.tsx index a3717c662..4e7a0aa1c 100644 --- a/frontend/src/components/move-board/MoveBoard.tsx +++ b/frontend/src/components/move-board/MoveBoard.tsx @@ -1,7 +1,7 @@ import { FunctionComponent, useState } from "react"; // eslint-disable-next-line -import { Button, Modal, TreeView, useOdeClient } from "@edifice-ui/react"; +import { Button, Modal, TreeView } from "@edifice-ui/react"; import { useTranslation } from "react-i18next"; @@ -12,6 +12,7 @@ import { Folder, IFolderResponse } from "~/models/folder.model"; import { useBoardsNavigation } from "~/providers/BoardsNavigationProvider"; import { useFoldersNavigation } from "~/providers/FoldersNavigationProvider"; import { useMoveBoardsMutation } from "~/services/api/boards.service"; +import { useOdeClient } from "~/utils/context.utils"; import { UserRights } from "~/utils/share.utils"; type props = { diff --git a/frontend/src/components/share-modal/ShareModalMagneto.tsx b/frontend/src/components/share-modal/ShareModalMagneto.tsx index fa57531c0..7048d5289 100644 --- a/frontend/src/components/share-modal/ShareModalMagneto.tsx +++ b/frontend/src/components/share-modal/ShareModalMagneto.tsx @@ -1,11 +1,11 @@ import { FunctionComponent } from "react"; import { OdeClientProvider } from "@edifice-ui/react"; -import { useOdeClient } from "@edifice-ui/react"; import { RightStringified } from "edifice-ts-client"; import { ShareModal } from "~/common/ShareModal"; import { RESOURCE_BIG_TYPE } from "~/core/enums/resource-big-type.enum"; +import { useOdeClient } from "~/utils/context.utils"; type props = { isOpen: boolean; diff --git a/frontend/src/components/side-bar/SideBarButtons.tsx b/frontend/src/components/side-bar/SideBarButtons.tsx index d39f717dc..5415c6f3e 100644 --- a/frontend/src/components/side-bar/SideBarButtons.tsx +++ b/frontend/src/components/side-bar/SideBarButtons.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { Button, useOdeClient, useToggle } from "@edifice-ui/react"; +import { Button, useToggle } from "@edifice-ui/react"; import { mdiFolderPlus, mdiStar } from "@mdi/js"; import { Icon } from "@mdi/react"; import { useTranslation } from "react-i18next"; @@ -10,6 +10,7 @@ import { MagnetsCollectionModal } from "../magnets-collection/MagnetsCollectionM import "./SideBar.scss"; import { FOLDER_TYPE } from "~/core/enums/folder-type.enum"; import { useFoldersNavigation } from "~/providers/FoldersNavigationProvider"; +import { useOdeClient } from "~/utils/context.utils"; import { UserRights } from "~/utils/share.utils"; type SideBarButtonsProps = { diff --git a/frontend/src/components/toaster-container/ToasterContainer.tsx b/frontend/src/components/toaster-container/ToasterContainer.tsx index d6ee9008e..5710bd98d 100644 --- a/frontend/src/components/toaster-container/ToasterContainer.tsx +++ b/frontend/src/components/toaster-container/ToasterContainer.tsx @@ -5,7 +5,6 @@ import { ActionBar, isActionAvailable, useToggle, - useOdeClient, checkUserRight, } from "@edifice-ui/react"; import { useTransition, animated } from "@react-spring/web"; @@ -30,6 +29,7 @@ import { useFoldersNavigation } from "~/providers/FoldersNavigationProvider"; import { useDuplicateBoardMutation } from "~/services/api/boards.service"; import { useActions } from "~/services/queries"; import { useUserRightsStore } from "~/stores"; +import { useOdeClient } from "~/utils/context.utils"; export interface ToasterContainerProps { reset: () => void; diff --git a/frontend/src/components/tree-view/TreeViewContainer.tsx b/frontend/src/components/tree-view/TreeViewContainer.tsx index a0a6adb4a..dd515f45a 100644 --- a/frontend/src/components/tree-view/TreeViewContainer.tsx +++ b/frontend/src/components/tree-view/TreeViewContainer.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useState } from "react"; import "./TreeViewContent.scss"; -import { TreeView, useOdeClient } from "@edifice-ui/react"; +import { TreeView } from "@edifice-ui/react"; import { useTranslation } from "react-i18next"; import { useGetFolderTypeData } from "./utils"; @@ -11,6 +11,7 @@ import { Board } from "~/models/board.model"; import { Folder, IFolderResponse } from "~/models/folder.model"; import { useFoldersNavigation } from "~/providers/FoldersNavigationProvider"; import { useMoveBoardsMutation } from "~/services/api/boards.service"; +import { useOdeClient } from "~/utils/context.utils"; import { UserRights } from "~/utils/share.utils"; type TreeViewContainerProps = { diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index d7d429fa8..161ebdf8d 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,6 +1,5 @@ import React from "react"; -import { OdeClientProvider, ThemeProvider } from "@edifice-ui/react"; import { QueryCache, QueryClient, @@ -16,6 +15,7 @@ import { FoldersNavigationProvider } from "./providers/FoldersNavigationProvider import { router } from "./routes"; import { setupStore } from "./store"; import "~/i18n"; +import { OdeClientProvider, ThemeProvider } from "./utils/context.utils"; const rootElement = document.getElementById("root"); const root = createRoot(rootElement!); diff --git a/frontend/src/open/components/Layout.tsx b/frontend/src/open/components/Layout.tsx new file mode 100644 index 000000000..765cdde4b --- /dev/null +++ b/frontend/src/open/components/Layout.tsx @@ -0,0 +1,16 @@ +import { ComponentPropsWithoutRef, type ReactNode } from "react"; + +interface LayoutProps extends ComponentPropsWithoutRef { + /** Main content of an application */ + children: ReactNode; +} + +const Layout = ({ children }: LayoutProps) => { + return ( +
+ {children} +
+ ); +}; + +export default Layout; diff --git a/frontend/src/open/utils/OdeClientProvider.tsx b/frontend/src/open/utils/OdeClientProvider.tsx new file mode 100644 index 000000000..fdb826ed0 --- /dev/null +++ b/frontend/src/open/utils/OdeClientProvider.tsx @@ -0,0 +1,72 @@ +import { createContext, useContext, useEffect, useMemo } from "react"; + +import { OdeClientProps, OdeContextProps } from "@edifice-ui/react"; +import { useTranslation } from "react-i18next"; + +import { confQuery, sessionQuery } from "./fakeData"; + +export const OdeClientContext = createContext(null!); + +export function OdeClientProvider({ children, params }: OdeClientProps) { + const appCode = params.app; + + const { t } = useTranslation(); + const translatedAppCode = t(appCode); + + const init = confQuery?.isSuccess && sessionQuery?.isSuccess; + + useEffect(() => { + const attributes = [ + { + data: "html", + value: sessionQuery?.data?.currentLanguage || "fr", + }, + // #WB-3137 Disable the translation of the content of the page which provoced issues + { + data: "translate", + value: "no", + }, + ]; + + attributes.forEach((attribute) => { + return document + .querySelector("html") + ?.setAttribute(attribute.data, attribute.value as string); + }); + }, [sessionQuery?.data]); + + useEffect(() => { + document.title = `${translatedAppCode}`; + }, [appCode, sessionQuery.data, translatedAppCode]); + + const values = useMemo( + () => ({ + appCode, + applications: confQuery?.data?.applications, + confQuery, + currentApp: confQuery?.data?.currentApp, + currentLanguage: sessionQuery?.data?.currentLanguage, + init, + sessionQuery, + user: sessionQuery?.data?.user, + userDescription: sessionQuery?.data?.userDescription, + userProfile: sessionQuery?.data?.userProfile, + }), + [appCode, confQuery, init, sessionQuery], + ); + + return ( + + {children} + + ); +} + +export function useOdeClient() { + const context = useContext(OdeClientContext); + + if (!context) { + throw new Error(`Cannot be used outside of OdeClientProvider`); + } + return context; +} diff --git a/frontend/src/open/utils/ThemeProvider.tsx b/frontend/src/open/utils/ThemeProvider.tsx new file mode 100644 index 000000000..90caf504f --- /dev/null +++ b/frontend/src/open/utils/ThemeProvider.tsx @@ -0,0 +1,78 @@ +import { + createContext, + type ReactNode, + useMemo, + useContext, + useEffect, +} from "react"; + +import { IOdeTheme } from "edifice-ts-client"; + +import { confQuery } from "./fakeData"; +import { useOdeClient } from "./OdeClientProvider"; + +export interface ThemeProps { + children: ReactNode; +} + +export interface ThemeContextProps { + theme: IOdeTheme | undefined; +} + +export const ThemeContext = createContext(null!); + +export function ThemeProvider({ children }: ThemeProps) { + const { appCode } = useOdeClient(); + + useEffect(() => { + const favicon = document.getElementById("favicon") as HTMLAnchorElement; + favicon.href = + `${confQuery?.data?.theme?.basePath}/img/illustrations/favicon.ico` as string; + const bootstrapVersion = + confQuery?.data?.theme?.bootstrapVersion?.split("-"); + const dataProduct = bootstrapVersion + ? bootstrapVersion[bootstrapVersion.length - 1] + : undefined; + + const attributes = [ + { + data: "data-skin", + value: confQuery?.data?.theme?.skinName, + }, + { + data: "data-theme", + value: confQuery?.data?.theme?.themeName, + }, + { + data: "data-product", + value: dataProduct, + }, + ]; + + attributes.forEach((attribute) => { + return document + .querySelector("html") + ?.setAttribute(attribute.data, attribute.value as string); + }); + }, [confQuery?.data]); + + const values = useMemo( + () => ({ + theme: confQuery?.data?.theme, + }), + [confQuery?.data?.theme], + ); + + return ( + {children} + ); +} + +export function useOdeTheme() { + const context = useContext(ThemeContext); + + if (!context) { + throw new Error(`Cannot be used outside of OdeClientProvider`); + } + return context; +} diff --git a/frontend/src/open/utils/fakeData.ts b/frontend/src/open/utils/fakeData.ts new file mode 100644 index 000000000..c6ee843f5 --- /dev/null +++ b/frontend/src/open/utils/fakeData.ts @@ -0,0 +1,46 @@ +import { IGetConf, IGetSession } from "edifice-ts-client"; + +const sessionQuery: { isSuccess: boolean; data: IGetSession } = { + isSuccess: true, + data: { + user: undefined, + currentLanguage: undefined, + quotaAndUsage: { + quota: 0, + storage: 0, + }, + userDescription: {}, + userProfile: undefined, + bookmarkedApps: [], + }, +}; +const confQuery: { isSuccess: boolean; data: IGetConf } = { + isSuccess: true, + data: { + app: "", + applications: [], + conf: { + dependencies: { + themes: {}, + widgets: {}, + }, + emitWrapper: false, + overriding: [], + }, + currentApp: undefined, + theme: { + basePath: "", + bootstrapPath: "", + bootstrapVersion: "", + is1d: false, + logoutCallback: "", + skin: "", + skinName: "", + skins: [], + themeName: "", + themeUrl: "", + }, + }, +}; + +export { sessionQuery, confQuery }; diff --git a/frontend/src/providers/FoldersNavigationProvider/useInitialCurrentFolder.ts b/frontend/src/providers/FoldersNavigationProvider/useInitialCurrentFolder.ts index 3d01786e6..3239e8a6c 100644 --- a/frontend/src/providers/FoldersNavigationProvider/useInitialCurrentFolder.ts +++ b/frontend/src/providers/FoldersNavigationProvider/useInitialCurrentFolder.ts @@ -1,8 +1,8 @@ -import { useOdeClient } from "@edifice-ui/react"; import { useTranslation } from "react-i18next"; import { FOLDER_TYPE } from "~/core/enums/folder-type.enum"; import { Folder } from "~/models/folder.model"; +import { useOdeClient } from "~/utils/context.utils"; export const useInitialCurrentFolder = () => { const { user } = useOdeClient(); diff --git a/frontend/src/utils/context.utils.ts b/frontend/src/utils/context.utils.ts new file mode 100644 index 000000000..706c3617f --- /dev/null +++ b/frontend/src/utils/context.utils.ts @@ -0,0 +1,28 @@ +import { + Layout as EdificeLayout, + useOdeClient as edificeOdeClient, + OdeClientProvider as EdificeOdeClientProvider, + ThemeProvider as EdificeThemeProvider, +} from "@edifice-ui/react"; + +import { default as OpenLayout } from "~/open/components/Layout"; +import { + useOdeClient as openOdeClient, + OdeClientProvider as OpenOdeClientProvider, +} from "~/open/utils/OdeClientProvider"; +import { ThemeProvider as OpenThemeProvider } from "~/open/utils/ThemeProvider"; + +const isEntNgContext = false; + +const Layout = isEntNgContext ? EdificeLayout : OpenLayout; + +const useOdeClient = () => + isEntNgContext ? edificeOdeClient() : openOdeClient(); + +const OdeClientProvider = isEntNgContext + ? EdificeOdeClientProvider + : OpenOdeClientProvider; + +const ThemeProvider = isEntNgContext ? EdificeThemeProvider : OpenThemeProvider; + +export { Layout, useOdeClient, OdeClientProvider, ThemeProvider };