diff --git a/web/src/App.tsx b/web/src/App.tsx index 06b45ba55d..6d920227f8 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,5 +1,5 @@ /* - * Copyright (c) [2022-2024] SUSE LLC + * Copyright (c) [2022-2025] SUSE LLC * * All Rights Reserved. * @@ -34,12 +34,13 @@ import { useDeprecatedChanges } from "~/queries/storage"; import { useRootUser } from "~/queries/users"; import { ROOT, PRODUCT, USER } from "~/routes/paths"; import { InstallationPhase } from "~/types/status"; -import { isEmpty } from "~/utils"; +import { isEmpty, useLocalStorage } from "~/utils"; /** * Main application component. */ function App() { + const [showWelcomePage] = useLocalStorage("agm-show-welcome-page", true); const location = useLocation(); const { isBusy, phase } = useInstallerStatus({ suspense: true }); const { connected, error } = useInstallerClientStatus(); @@ -55,6 +56,10 @@ function App() { const Content = () => { if (error) return ; + if (showWelcomePage) { + return ; + } + if (phase === InstallationPhase.Install && isBusy) { return ; } diff --git a/web/src/components/core/WelcomePage.tsx b/web/src/components/core/WelcomePage.tsx new file mode 100644 index 0000000000..dad8a37a1e --- /dev/null +++ b/web/src/components/core/WelcomePage.tsx @@ -0,0 +1,98 @@ +/* + * Copyright (c) [2025] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + +import React, { useEffect } from "react"; +import { Form, FormGroup, FormSelectOption, Grid, GridItem } from "@patternfly/react-core"; +import { useNavigate } from "react-router-dom"; +import { Page } from "~/components/core"; +import { useInstallerL10n } from "~/context/installerL10n"; +// import { ROOT as PATHS } from "~/routes/paths"; +import { _ } from "~/i18n"; +import supportedLanguages from "~/languages.json"; +import { Center, Icon } from "../layout"; +import { useLocalStorage } from "~/utils"; +import { ROOT as PATHS } from "~/routes/paths"; + +/** + * A page component to allow setting the installer langauge + */ +function WelcomePage() { + const { language, changeLanguage } = useInstallerL10n(); + const [showWelcomePage, setShowWelcomePage] = useLocalStorage("agm-show-welcome-page"); + const navigate = useNavigate(); + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const formData = new FormData(e.currentTarget); + await changeLanguage(formData.get("language") as string); + setShowWelcomePage(false); + // navigate(PATHS.root, { replace: true }); + }; + + useEffect(() => { + !showWelcomePage && navigate(PATHS.root, { replace: true }); + }); + + return ( + + + + +
+ +
+ + {_("Language")} + + } + > + + +
+
+
+
+
+
+ + + +
+ ); +} + +export default WelcomePage; diff --git a/web/src/components/core/index.ts b/web/src/components/core/index.ts index dd18a31925..959f6b3d47 100644 --- a/web/src/components/core/index.ts +++ b/web/src/components/core/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) [2022-2024] SUSE LLC + * Copyright (c) [2022-2025] SUSE LLC * * All Rights Reserved. * @@ -49,5 +49,6 @@ export { default as TreeTable } from "./TreeTable"; export { default as Link } from "./Link"; export { default as EmptyState } from "./EmptyState"; export { default as InstallerOptions } from "./InstallerOptions"; +export { default as WelcomePage } from "./WelcomePage"; export { default as IssuesDrawer } from "./IssuesDrawer"; export { default as Drawer } from "./Drawer"; diff --git a/web/src/router.tsx b/web/src/router.tsx index 55ec7f0ce2..dc7539efaa 100644 --- a/web/src/router.tsx +++ b/web/src/router.tsx @@ -1,5 +1,5 @@ /* - * Copyright (c) [2024] SUSE LLC + * Copyright (c) [2024-2025] SUSE LLC * * All Rights Reserved. * @@ -25,7 +25,12 @@ import { createHashRouter, Outlet } from "react-router-dom"; import App from "~/App"; import Protected from "~/Protected"; import { FullLayout, PlainLayout } from "~/components/layout"; -import { InstallationFinished, InstallationProgress, LoginPage } from "~/components/core"; +import { + InstallationFinished, + InstallationProgress, + LoginPage, + WelcomePage, +} from "~/components/core"; import { OverviewPage } from "~/components/overview"; import l10nRoutes from "~/routes/l10n"; import networkRoutes from "~/routes/network"; @@ -86,6 +91,10 @@ const protectedRoutes = () => [ ), children: [ + { + path: PATHS.welcomePage, + element: , + }, { path: PATHS.installationProgress, element: , diff --git a/web/src/routes/paths.ts b/web/src/routes/paths.ts index efe67522a4..7093bbb794 100644 --- a/web/src/routes/paths.ts +++ b/web/src/routes/paths.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) [2024] SUSE LLC + * Copyright (c) [2024-2025] SUSE LLC * * All Rights Reserved. * @@ -51,6 +51,7 @@ const ROOT = { installationProgress: "/installation/progress", installationFinished: "/installation/finished", logs: "/api/manager/logs/store", + welcomePage: "/welcome", }; const USER = { @@ -83,6 +84,7 @@ const STORAGE = { }; const SUPPORTIVE_PATHS = [ + ROOT.welcomePage, ROOT.login, PRODUCT.changeProduct, PRODUCT.progress, diff --git a/web/src/utils.ts b/web/src/utils.ts index 81ab64c3f8..4808def554 100644 --- a/web/src/utils.ts +++ b/web/src/utils.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) [2022-2024] SUSE LLC + * Copyright (c) [2022-2025] SUSE LLC * * All Rights Reserved. * @@ -261,7 +261,7 @@ const useCancellablePromise = () => { * @param storageKey * @param fallbackState */ -const useLocalStorage = (storageKey: string, fallbackState) => { +const useLocalStorage = (storageKey: string, fallbackState?) => { const [value, setValue] = useState(JSON.parse(localStorage.getItem(storageKey)) ?? fallbackState); useEffect(() => {