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 };