diff --git a/web/src/client/index.js b/web/src/client/index.js index 0d8529643d..8488197714 100644 --- a/web/src/client/index.js +++ b/web/src/client/index.js @@ -67,7 +67,7 @@ import { HTTPClient } from "./http"; * @typedef {(issues: Issues) => void} IssuesHandler */ -const createIssuesList = (product, software, storage, users) => { +const createIssuesList = (product = [], software = [], storage = [], users = []) => { const list = { product, storage, software, users }; list.isEmpty = !Object.values(list).some(v => v.length > 0); return list; @@ -155,4 +155,4 @@ const createDefaultClient = async () => { return createClient(httpUrl); }; -export { createClient, createDefaultClient, phase }; +export { createClient, createDefaultClient, phase, createIssuesList }; diff --git a/web/src/components/core/IssuesHint.jsx b/web/src/components/core/IssuesHint.jsx new file mode 100644 index 0000000000..20c52c444d --- /dev/null +++ b/web/src/components/core/IssuesHint.jsx @@ -0,0 +1,43 @@ +/* + * Copyright (c) [2023] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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 from "react"; +import { Hint, HintBody, List, ListItem, Stack } from "@patternfly/react-core"; +import { _ } from "~/i18n"; + +export default function IssuesHint({ issues }) { + if (issues === undefined || issues.length === 0) return; + + return ( + + + +

+ {_("Please, pay attention to the following tasks:")} +

+ + {issues.map((i, idx) => {i.description})} + +
+
+
+ ); +} diff --git a/web/src/components/core/IssuesHint.test.jsx b/web/src/components/core/IssuesHint.test.jsx new file mode 100644 index 0000000000..62c48ddfe2 --- /dev/null +++ b/web/src/components/core/IssuesHint.test.jsx @@ -0,0 +1,35 @@ +/* + * Copyright (c) [2022-2023] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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 from "react"; +import { screen } from "@testing-library/react"; +import { plainRender } from "~/test-utils"; +import { IssuesHint } from "~/components/core"; + +it("renders a list of issues", () => { + const issue = { + description: "You need to create a user", + source: "config", + severity: "error" + }; + plainRender(); + expect(screen.getByText(issue.description)).toBeInTheDocument(); +}); diff --git a/web/src/components/core/index.js b/web/src/components/core/index.js index 4c337505ab..a02328a437 100644 --- a/web/src/components/core/index.js +++ b/web/src/components/core/index.js @@ -34,6 +34,7 @@ export { default as InstallationFinished } from "./InstallationFinished"; export { default as InstallationProgress } from "./InstallationProgress"; export { default as InstallButton } from "./InstallButton"; export { default as IssuesDialog } from "./IssuesDialog"; +export { default as IssuesHint } from "./IssuesHint"; export { default as SectionSkeleton } from "./SectionSkeleton"; export { default as ListSearch } from "./ListSearch"; export { default as LoginPage } from "./LoginPage"; diff --git a/web/src/components/overview/OverviewPage.jsx b/web/src/components/overview/OverviewPage.jsx index 876a2a109e..ee82516948 100644 --- a/web/src/components/overview/OverviewPage.jsx +++ b/web/src/components/overview/OverviewPage.jsx @@ -114,7 +114,7 @@ export default function OverviewPage() { - + {issues.isEmpty ? : } diff --git a/web/src/components/users/UsersPage.jsx b/web/src/components/users/UsersPage.jsx index d78944dd21..1fffd6f0b3 100644 --- a/web/src/components/users/UsersPage.jsx +++ b/web/src/components/users/UsersPage.jsx @@ -22,11 +22,14 @@ import React from "react"; import { _ } from "~/i18n"; -import { CardField, Page } from "~/components/core"; +import { CardField, IssuesHint, Page } from "~/components/core"; import { FirstUser, RootAuthMethods } from "~/components/users"; -import { Card, CardBody, Grid, GridItem, Stack } from "@patternfly/react-core"; +import { CardBody, Grid, GridItem, Stack } from "@patternfly/react-core"; +import { useIssues } from "~/context/issues"; export default function UsersPage() { + const { users: issues } = useIssues(); + return ( <> @@ -35,6 +38,9 @@ export default function UsersPage() { + + + diff --git a/web/src/context/app.jsx b/web/src/context/app.jsx index 761ebd8385..d92e9d662e 100644 --- a/web/src/context/app.jsx +++ b/web/src/context/app.jsx @@ -26,6 +26,7 @@ import { InstallerClientProvider } from "./installer"; import { InstallerL10nProvider } from "./installerL10n"; import { L10nProvider } from "./l10n"; import { ProductProvider } from "./product"; +import { IssuesProvider } from "./issues"; /** * Combines all application providers. @@ -39,7 +40,9 @@ function AppProviders({ children }) { - {children} + + {children} + diff --git a/web/src/context/issues.jsx b/web/src/context/issues.jsx new file mode 100644 index 0000000000..84c215b0fd --- /dev/null +++ b/web/src/context/issues.jsx @@ -0,0 +1,70 @@ +/* + * Copyright (c) [2024] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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, { useContext, useEffect, useState } from "react"; +import { useCancellablePromise } from "~/utils"; +import { useInstallerClient } from "./installer"; +import { createIssuesList } from "~/client"; + +/** + * @typedef {import ("~/client").Issues} Issues list + */ + +const IssuesContext = React.createContext({}); + +function IssuesProvider({ children }) { + const [issues, setIssues] = useState(createIssuesList()); + const { cancellablePromise } = useCancellablePromise(); + const client = useInstallerClient(); + + useEffect(() => { + const loadIssues = async () => { + const issues = await cancellablePromise(client.issues()); + setIssues(issues); + }; + + if (client) { + loadIssues(); + } + }, [client, cancellablePromise, setIssues]); + + useEffect(() => { + if (!client) return; + + return client.onIssuesChange((updated) => { + setIssues({ ...issues, ...updated }); + }); + }, [client, issues, setIssues]); + + return {children}; +} + +function useIssues() { + const context = useContext(IssuesContext); + + if (!context) { + throw new Error("useIssues must be used within an IssuesProvider"); + } + + return context; +} + +export { IssuesProvider, useIssues };