diff --git a/client/package.json b/client/package.json
index 4150bedc6..4e4b99acc 100644
--- a/client/package.json
+++ b/client/package.json
@@ -22,10 +22,11 @@
"storybook-wait-server": "wait-on http://127.0.0.1:6006",
"storybook-test": "test-storybook",
"storybook-compile-and-test": "concurrently -k -s first -n 'BUILD,TEST' -c 'magenta,blue' 'npm run storybook-build && npm run storybook-start-server' 'npm run storybook-wait-server && npm run storybook-test'",
- "generate-api": "npm run generate-api:dataServicesUser && npm run generate-api:namespaceV2 && npm run generate-api:projectV2 && npm run generate-api:searchV2",
+ "generate-api": "npm run generate-api:dataServicesUser && npm run generate-api:namespaceV2 && npm run generate-api:projectV2 && generate-api:platform && npm run generate-api:searchV2",
"generate-api:dataServicesUser": "rtk-query-codegen-openapi src/features/user/dataServicesUser.api/dataServicesUser.api-config.ts",
"generate-api:namespaceV2": "rtk-query-codegen-openapi src/features/projectsV2/api/namespace.api-config.ts",
"generate-api:projectV2": "rtk-query-codegen-openapi src/features/projectsV2/api/projectV2.api-config.ts",
+ "generate-api:platform": "rtk-query-codegen-openapi src/features/platform/api/platform.api-config.ts",
"generate-api:searchV2": "rtk-query-codegen-openapi src/features/searchV2/api/searchV2.api-config.ts"
},
"type": "module",
diff --git a/client/src/components/navbar/AnonymousNavBar.tsx b/client/src/components/navbar/AnonymousNavBar.tsx
index 15b7b8042..7d7b1c604 100644
--- a/client/src/components/navbar/AnonymousNavBar.tsx
+++ b/client/src/components/navbar/AnonymousNavBar.tsx
@@ -16,13 +16,16 @@
* limitations under the License.
*/
+import cx from "classnames";
import { useCallback, useState } from "react";
import { List, Search } from "react-bootstrap-icons";
import { Link } from "react-router-dom";
import { Collapse, Nav, NavItem, Navbar, NavbarToggler } from "reactstrap";
+
+import StatusBanner from "../../features/platform/components/StatusBanner";
import { NavBarWarnings } from "../../landing/NavBarWarnings";
+import type { AppParams } from "../../utils/context/appParams.types";
import { Url } from "../../utils/helpers/url";
-import cx from "classnames";
import { RenkuNavLink } from "../RenkuNavLink";
import {
RenkuToolbarHelpMenu,
@@ -30,7 +33,6 @@ import {
RenkuToolbarNotifications,
} from "./NavBarItems";
import { RENKU_LOGO } from "./navbar.constans";
-import type { AppParams } from "../../utils/context/appParams.types";
interface AnonymousNavBarProps {
model: unknown;
@@ -112,6 +114,7 @@ export default function AnonymousNavBar({
+
>
);
diff --git a/client/src/components/navbar/LoggedInNavBar.tsx b/client/src/components/navbar/LoggedInNavBar.tsx
index 5ab287007..25108965a 100644
--- a/client/src/components/navbar/LoggedInNavBar.tsx
+++ b/client/src/components/navbar/LoggedInNavBar.tsx
@@ -19,10 +19,11 @@
import cx from "classnames";
import { useCallback, useState } from "react";
import { List, Search } from "react-bootstrap-icons";
-import { Link, useLocation } from "react-router-dom";
+import { Link } from "react-router-dom";
import { Collapse, Nav, NavItem, Navbar, NavbarToggler } from "reactstrap";
+import StatusBanner from "../../features/platform/components/StatusBanner";
import { NavBarWarnings } from "../../landing/NavBarWarnings";
-import { StatuspageBanner } from "../../statuspage";
+import { AppParams } from "../../utils/context/appParams.types";
import { Url } from "../../utils/helpers/url";
import { RenkuNavLink } from "../RenkuNavLink";
import {
@@ -37,7 +38,7 @@ import { RENKU_LOGO } from "./navbar.constans";
interface LoggedInNavBarProps {
model: unknown;
notifications: unknown;
- params: unknown;
+ params: AppParams;
}
export default function LoggedInNavBar({
@@ -45,9 +46,7 @@ export default function LoggedInNavBar({
notifications,
params,
}: LoggedInNavBarProps) {
- const location = useLocation();
-
- const uiShortSha = (params as { UI_SHORT_SHA: string }).UI_SHORT_SHA;
+ const uiShortSha = params.UI_SHORT_SHA;
const [isOpen, setIsOpen] = useState(false);
@@ -127,11 +126,7 @@ export default function LoggedInNavBar({
-
+
>
);
diff --git a/client/src/features/admin/AdminPage.tsx b/client/src/features/admin/AdminPage.tsx
index 99f763b45..12c87b674 100644
--- a/client/src/features/admin/AdminPage.tsx
+++ b/client/src/features/admin/AdminPage.tsx
@@ -53,6 +53,7 @@ import AddResourceClassButton from "./AddResourceClassButton";
import AddResourcePoolButton from "./AddResourcePoolButton";
import AddUserToResourcePoolButton from "./AddUserToResourcePoolButton";
import DeleteResourceClassButton from "./DeleteResourceClassButton";
+import IncidentsAndMaintenanceSection from "./IncidentsAndMaintenanceSection";
import SessionEnvironmentsSection from "./SessionEnvironmentsSection";
import UpdateResourceClassButton from "./UpdateResourceClassButton";
import UpdateResourcePoolQuotaButton from "./UpdateResourcePoolQuotaButton";
@@ -66,6 +67,7 @@ export default function AdminPage() {
return (
<>
Admin Panel
+
>
diff --git a/client/src/features/admin/IncidentsAndMaintenanceSection.tsx b/client/src/features/admin/IncidentsAndMaintenanceSection.tsx
new file mode 100644
index 000000000..b4d733b09
--- /dev/null
+++ b/client/src/features/admin/IncidentsAndMaintenanceSection.tsx
@@ -0,0 +1,326 @@
+/*!
+ * Copyright 2024 - Swiss Data Science Center (SDSC)
+ * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
+ * Eidgenössische Technische Hochschule Zürich (ETHZ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+import { skipToken } from "@reduxjs/toolkit/query";
+import cx from "classnames";
+import { useCallback, useContext, useEffect, useState } from "react";
+import {
+ BoxArrowUpRight,
+ CheckCircleFill,
+ XCircleFill,
+ XLg,
+} from "react-bootstrap-icons";
+import { useForm } from "react-hook-form";
+import { Link } from "react-router-dom-v5-compat";
+import {
+ Alert,
+ Button,
+ Card,
+ CardBody,
+ CardHeader,
+ Collapse,
+ Form,
+ Label,
+ Nav,
+ NavItem,
+ TabContent,
+ TabPane,
+} from "reactstrap";
+
+import { Loader } from "../../components/Loader";
+import { RtkOrNotebooksError } from "../../components/errors/RtkErrorAlert";
+import ChevronFlippedIcon from "../../components/icons/ChevronFlippedIcon";
+import LazyRenkuMarkdown from "../../components/markdown/LazyRenkuMarkdown";
+import { Docs } from "../../utils/constants/Docs";
+import AppContext from "../../utils/context/appContext";
+import { DEFAULT_APP_PARAMS } from "../../utils/context/appParams.constants";
+import {
+ useGetPlatformConfigQuery,
+ usePatchPlatformConfigMutation,
+} from "../platform/api/platform.api";
+import { useGetSummaryQuery } from "../platform/statuspage-api/statuspage.api";
+
+export default function IncidentsAndMaintenanceSection() {
+ const { params } = useContext(AppContext);
+ const statusPageId =
+ params?.STATUSPAGE_ID ?? DEFAULT_APP_PARAMS.STATUSPAGE_ID;
+
+ return (
+
+
Incidents And Maintenance
+
+
+
+ Renku documentation about incidents and maintenance
+
+
+
+
+
+
+ );
+}
+
+interface MaintenanceBannerProps {
+ scheduledMaintenances: ScheduledMaintenances;
+ summaryPageUrl: string;
+}
+
+function MaintenanceBanner({
+ scheduledMaintenances,
+ summaryPageUrl,
+}: MaintenanceBannerProps) {
+ const now = useNow();
+
+ // We display:
+ // 1. All ongoing maintenances
+ // 2. If no maintenance is ongoing, we display the next upcoming maintenance.
+ const maintenancesToDisplay = useMemo(() => {
+ const sortedMaintenances = [...scheduledMaintenances].sort((a, b) =>
+ ensureDateTime(a.scheduled_for)
+ .diff(ensureDateTime(b.scheduled_for))
+ .valueOf()
+ );
+ const ongoingMaintenances = sortedMaintenances.filter(
+ (m) =>
+ m.status === "in_progress" ||
+ m.status === "verifying" ||
+ ensureDateTime(m.scheduled_for).diff(now).valueOf() < 0
+ );
+ if (ongoingMaintenances.length > 0) {
+ return ongoingMaintenances;
+ }
+ return sortedMaintenances.slice(0, 1);
+ }, [now, scheduledMaintenances]);
+
+ if (!scheduledMaintenances.length) {
+ return null;
+ }
+
+ return (
+ <>
+ {maintenancesToDisplay.map((maintenance) => (
+
+ ))}
+ >
+ );
+}
+
+interface StatusPageMaintenanceProps {
+ maintenance: ScheduledMaintenance;
+ summaryPageUrl: string;
+}
+
+function StatusPageMaintenance({
+ maintenance,
+ summaryPageUrl,
+}: StatusPageMaintenanceProps) {
+ const { name, incident_updates, scheduled_for, status } = maintenance;
+
+ const color =
+ status === "in_progress" || status === "verifying" ? "warning" : "info";
+
+ const now = useNow();
+
+ const ongoing =
+ status === "in_progress" ||
+ status === "verifying" ||
+ ensureDateTime(scheduled_for).diff(now).valueOf() < 0;
+
+ const caption = ongoing
+ ? "Ongoing maintenance started"
+ : "Maintenance scheduled in";
+
+ const userLogged = useLegacySelector(
+ (state) => state.stateModel.user.logged
+ );
+
+ const location = useLocation();
+ const isDashboard =
+ (userLogged && location.pathname === "/") ||
+ location.pathname === "/v2" ||
+ location.pathname === "/v2/";
+
+ // 1. There is a scheduled maintenance in < 48 hours: show it on all pages except the landing page
+ // 2. There is a scheduled maintenance in < 7 days: show it on the v1 Dashboard and the v2 Dashboard
+ const shouldDisplay = useMemo(
+ () =>
+ ensureDateTime(scheduled_for).diff(now).valueOf() <
+ SOON_MAINTENANCE_CUTOFF.valueOf() ||
+ (isDashboard &&
+ ensureDateTime(scheduled_for).diff(now).valueOf() <
+ MAINTENANCE_CUTOFF.valueOf()),
+ [isDashboard, now, scheduled_for]
+ );
+
+ if (!shouldDisplay) {
+ return null;
+ }
+
+ return (
+
+
+
+
+ {caption}{" "}
+ :{" "}
+ {name}
+
+
+ {summaryPageUrl && (
+
+ For further information, see{" "}
+
+ {summaryPageUrl}
+
+
+ .
+
+ )}
+
+
+ );
+}
diff --git a/client/src/features/platform/components/StatusPageIncidentUpdates.tsx b/client/src/features/platform/components/StatusPageIncidentUpdates.tsx
new file mode 100644
index 000000000..99774c496
--- /dev/null
+++ b/client/src/features/platform/components/StatusPageIncidentUpdates.tsx
@@ -0,0 +1,63 @@
+/*!
+ * Copyright 2024 - Swiss Data Science Center (SDSC)
+ * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
+ * Eidgenössische Technische Hochschule Zürich (ETHZ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+import { startCase } from "lodash-es";
+
+import { toHumanDateTime } from "../../../utils/helpers/DateTimeUtils";
+import type {
+ IncidentUpdate,
+ IncidentUpdates,
+} from "../statuspage-api/statuspage.types";
+
+interface StatusPageIncidentUpdatesProps {
+ incidentUpdates: IncidentUpdates;
+}
+
+export default function StatusPageIncidentUpdates({
+ incidentUpdates,
+}: StatusPageIncidentUpdatesProps) {
+ return (
+ <>
+ {incidentUpdates.map((update) => (
+
+ ))}
+ >
+ );
+}
+
+interface StatusPageIncidentUpdateProps {
+ update: IncidentUpdate;
+}
+
+function StatusPageIncidentUpdate({ update }: StatusPageIncidentUpdateProps) {
+ const { body, display_at, id, status } = update;
+ const title = startCase(status);
+
+ return (
+
+
+ {statusStr}
+
+
+ );
+}
diff --git a/client/src/features/platform/statuspage-api/statuspage-empty.api.ts b/client/src/features/platform/statuspage-api/statuspage-empty.api.ts
new file mode 100644
index 000000000..d7e286093
--- /dev/null
+++ b/client/src/features/platform/statuspage-api/statuspage-empty.api.ts
@@ -0,0 +1,26 @@
+/*!
+ * Copyright 2024 - Swiss Data Science Center (SDSC)
+ * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
+ * Eidgenössische Technische Hochschule Zürich (ETHZ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
+
+// initialize an empty api service that we'll inject endpoints into later as needed
+export const statuspageEmptyApi = createApi({
+ baseQuery: fetchBaseQuery(),
+ endpoints: () => ({}),
+ reducerPath: "statuspageApi",
+});
diff --git a/client/src/features/platform/statuspage-api/statuspage.api.ts b/client/src/features/platform/statuspage-api/statuspage.api.ts
new file mode 100644
index 000000000..6ebaf57e3
--- /dev/null
+++ b/client/src/features/platform/statuspage-api/statuspage.api.ts
@@ -0,0 +1,35 @@
+/*!
+ * Copyright 2024 - Swiss Data Science Center (SDSC)
+ * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
+ * Eidgenössische Technische Hochschule Zürich (ETHZ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+import { statuspageEmptyApi } from "./statuspage-empty.api";
+import { GetSummaryParams, StatusPageSummary } from "./statuspage.types";
+
+export const statuspageApi = statuspageEmptyApi.injectEndpoints({
+ endpoints: (build) => ({
+ getSummary: build.query({
+ query: ({ statusPageId }) => {
+ return {
+ url: `https://${statusPageId}.statuspage.io/api/v2/summary.json`,
+ };
+ },
+ }),
+ }),
+ overrideExisting: false,
+});
+
+export const { useGetSummaryQuery } = statuspageApi;
diff --git a/client/src/features/platform/statuspage-api/statuspage.types.ts b/client/src/features/platform/statuspage-api/statuspage.types.ts
new file mode 100644
index 000000000..bfef7fc70
--- /dev/null
+++ b/client/src/features/platform/statuspage-api/statuspage.types.ts
@@ -0,0 +1,116 @@
+/*!
+ * Copyright 2024 - Swiss Data Science Center (SDSC)
+ * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
+ * Eidgenössische Technische Hochschule Zürich (ETHZ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+export interface StatusPageSummary {
+ page: StatusPageMetadata;
+ components: StatusPageComponents;
+ incidents: Incidents;
+ scheduled_maintenances: ScheduledMaintenances;
+ status: StatusPageOverallStatus;
+}
+
+export interface StatusPageMetadata {
+ id: string;
+ name: string;
+ url: string;
+ time_zone: string;
+ updated_at: string;
+}
+
+export type StatusPageComponents = StatusPageComponent[];
+
+export interface StatusPageComponent {
+ id: string;
+ name: string;
+ status: ComponentStatus;
+ position: number;
+ showcase: boolean;
+ only_show_if_degraded: boolean;
+}
+
+export type ComponentStatus =
+ | "operational"
+ | "degraded_performance"
+ | "partial_outage"
+ | "major_outage"
+ | "under_maintenance";
+
+export type Incidents = Incident[];
+
+export interface Incident {
+ id: string;
+ name: string;
+ status: IncidentStatus;
+ impact: StatusIndicator;
+ incident_updates: IncidentUpdates;
+ components: StatusPageComponents;
+}
+
+export type IncidentStatus =
+ | "investigating"
+ | "identified"
+ | "monitoring"
+ | "resolved";
+
+export type ScheduledMaintenances = ScheduledMaintenance[];
+
+export interface ScheduledMaintenance {
+ id: string;
+ name: string;
+ status: ScheduledMaintenanceStatus;
+ scheduled_for: string;
+ scheduled_until: string;
+ incident_updates: IncidentUpdates;
+}
+
+export type ScheduledMaintenanceStatus =
+ | "scheduled"
+ | "in_progress"
+ | "verifying"
+ | "completed";
+
+export type IncidentUpdates = IncidentUpdate[];
+
+export interface IncidentUpdate {
+ id: string;
+ status: IncidentUpdateStatus;
+ body: string;
+ display_at: string;
+}
+
+export type IncidentUpdateStatus =
+ | "investigating"
+ | "identified"
+ | "monitoring"
+ | "resolved";
+
+export interface StatusPageOverallStatus {
+ indicator: StatusIndicator;
+ description: string;
+}
+
+export type StatusIndicator =
+ | "none"
+ | "maintenance"
+ | "minor"
+ | "major"
+ | "critical";
+
+export interface GetSummaryParams {
+ statusPageId: string;
+}
diff --git a/client/src/features/rootV2/NavbarV2.tsx b/client/src/features/rootV2/NavbarV2.tsx
index 60d7cb6ea..6727733f0 100644
--- a/client/src/features/rootV2/NavbarV2.tsx
+++ b/client/src/features/rootV2/NavbarV2.tsx
@@ -45,6 +45,7 @@ import { Links } from "../../utils/constants/Docs";
import AppContext from "../../utils/context/appContext";
import BackToV1Button from "../projectsV2/shared/BackToV1Button";
import WipBadge from "../projectsV2/shared/WipBadge";
+import StatusBanner from "../platform/components/StatusBanner";
const RENKU_ALPHA_LOGO = "/static/public/img/logo-yellow.svg";
@@ -166,92 +167,99 @@ export default function NavbarV2() {
}
return (
-
-
+
-
-
-
-
- 2.0 Beta
-
-
-
-
-
-
-
-
-
-
+
+
+
+ 2.0 Beta
+
+
+
+
+
+
+
+
+
+
+
+ >
);
}
diff --git a/client/src/help/Help.tsx b/client/src/help/Help.tsx
index e3ca560c5..a57b1e69d 100644
--- a/client/src/help/Help.tsx
+++ b/client/src/help/Help.tsx
@@ -35,11 +35,12 @@ import {
ExternalIconLink,
} from "../components/ExternalLinks";
import RenkuNavLinkV2 from "../components/RenkuNavLinkV2";
-import { StatuspageDisplay, isStatusConfigured } from "../statuspage";
+import { isStatusConfigured } from "../statuspage";
import { Docs, Links, RenkuPythonDocs } from "../utils/constants/Docs";
import AppContext from "../utils/context/appContext";
import { DEFAULT_APP_PARAMS } from "../utils/context/appParams.constants";
+import StatusSummary from "../features/platform/components/StatusSummary";
import HelpRelease from "./HelpRelease";
import PrivacyPolicy from "./PrivacyPolicy";
import TermsOfService from "./TermsOfService";
@@ -197,12 +198,11 @@ function HelpDocumentation() {
}
function HelpContent() {
- const { model } = useContext(AppContext);
return (
} />
} />
- } />
+ } />
} />
} />
} />
diff --git a/client/src/landing/AnonymousHome.tsx b/client/src/landing/AnonymousHome.tsx
index 21dddd169..f116baa57 100644
--- a/client/src/landing/AnonymousHome.tsx
+++ b/client/src/landing/AnonymousHome.tsx
@@ -32,7 +32,6 @@ import { Col, Row } from "reactstrap";
import LazyRenkuMarkdown from "../components/markdown/LazyRenkuMarkdown";
import { stateToSearchString } from "../features/kgSearch";
-import { StatuspageBanner } from "../statuspage";
import AppContext from "../utils/context/appContext";
import { DEFAULT_APP_PARAMS } from "../utils/context/appParams.constants";
import { Url } from "../utils/helpers/url";
@@ -74,16 +73,10 @@ export default function AnonymousHome() {
}
export function HomeHeader(props: AnonymousHomeConfig) {
- const { urlMap } = props;
return (
- args?.refresh ?? Duration.fromObject({ minute: 1 }),
+ [args?.refresh]
+ );
+
+ const [now, setNow] = useState(DateTime.utc());
+
+ const timeoutRef = useRef(null);
+
+ useEffect(() => {
+ timeoutRef.current = window.setInterval(() => {
+ setNow(DateTime.utc());
+ }, refresh.valueOf());
+ return () => {
+ if (timeoutRef.current) {
+ window.clearInterval(timeoutRef.current);
+ }
+ timeoutRef.current = null;
+ };
+ }, [refresh]);
+
+ return now;
+}
diff --git a/client/src/utils/helpers/EnhancedState.ts b/client/src/utils/helpers/EnhancedState.ts
index 20e884f78..3907c1863 100644
--- a/client/src/utils/helpers/EnhancedState.ts
+++ b/client/src/utils/helpers/EnhancedState.ts
@@ -65,6 +65,8 @@ import { versionsApi } from "../../features/versions/versions.api";
import { workflowsApi } from "../../features/workflows/WorkflowsApi";
import { workflowsSlice } from "../../features/workflows/WorkflowsSlice";
import featureFlagsSlice from "../feature-flags/featureFlags.slice";
+import { platformEmptyApi as platformApi } from "../../features/platform/api/platform-empty.api";
+import { statuspageEmptyApi as statuspageApi } from "../../features/platform/statuspage-api/statuspage-empty.api";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const createStore = (
@@ -95,6 +97,7 @@ export const createStore = (
[inactiveKgProjectsApi.reducerPath]: inactiveKgProjectsApi.reducer,
[keycloakUserApi.reducerPath]: keycloakUserApi.reducer,
[kgSearchApi.reducerPath]: kgSearchApi.reducer,
+ [platformApi.reducerPath]: platformApi.reducer,
[projectCloudStorageApi.reducerPath]: projectCloudStorageApi.reducer,
[projectCoreApi.reducerPath]: projectCoreApi.reducer,
[projectGitLabApi.reducerPath]: projectGitLabApi.reducer,
@@ -108,6 +111,7 @@ export const createStore = (
[sessionsApi.reducerPath]: sessionsApi.reducer,
[sessionSidecarApi.reducerPath]: sessionSidecarApi.reducer,
[sessionsV2Api.reducerPath]: sessionsV2Api.reducer,
+ [statuspageApi.reducerPath]: statuspageApi.reducer,
[termsApi.reducerPath]: termsApi.reducer,
[userPreferencesApi.reducerPath]: userPreferencesApi.reducer,
[versionsApi.reducerPath]: versionsApi.reducer,
@@ -132,6 +136,7 @@ export const createStore = (
.concat(inactiveKgProjectsApi.middleware)
.concat(keycloakUserApi.middleware)
.concat(kgSearchApi.middleware)
+ .concat(platformApi.middleware)
.concat(projectCloudStorageApi.middleware)
.concat(projectCoreApi.middleware)
.concat(projectGitLabApi.middleware)
@@ -146,6 +151,7 @@ export const createStore = (
.concat(sessionSidecarApi.middleware)
.concat(sessionSidecarApi.middleware)
.concat(sessionsV2Api.middleware)
+ .concat(statuspageApi.middleware)
.concat(termsApi.middleware)
.concat(userPreferencesApi.middleware)
.concat(versionsApi.middleware)
diff --git a/tests/cypress/e2e/maintenance.spec.ts b/tests/cypress/e2e/maintenance.spec.ts
index 0605b98cb..bec00228d 100644
--- a/tests/cypress/e2e/maintenance.spec.ts
+++ b/tests/cypress/e2e/maintenance.spec.ts
@@ -78,18 +78,7 @@ describe("display the status page", () => {
cy.get(".alert").should("not.exist");
});
- it("Shows the banner on the home page if there is a major incident", () => {
- fixtures
- .config()
- .versions()
- .userNone()
- .getStatuspageInfo({ fixture: "statuspage/statuspage-outage.json" });
- cy.visit("/");
- cy.wait("@getStatuspageInfo");
- cy.get(".alert").contains("RenkuLab is unstable").should("be.visible");
- });
-
- it("Shows the banner everywhere if there is a major incident", () => {
+ it("Shows the banner on the dashboard if there is an incident", () => {
fixtures
.config()
.versions()
@@ -97,30 +86,17 @@ describe("display the status page", () => {
.getStatuspageInfo({ fixture: "statuspage/statuspage-outage.json" });
cy.visit("/");
cy.wait("@getStatuspageInfo");
- cy.get(".alert").contains("RenkuLab is unstable").should("be.visible");
- cy.contains("Search").click();
- cy.get(".alert").contains("RenkuLab is unstable").should("be.visible");
- cy.get(".btn-close").should("not.exist");
+ cy.get(".alert").contains("Ongoing incident").should("be.visible");
});
- it("Shows the banner only on the dashboard if there is a minor incident", () => {
- fixtures
- .config()
- .versions()
- .userTest()
- .getStatuspageInfo({
- overrides: {
- status: {
- indicator: "minor",
- description: "Everything is a little slow, but working",
- },
- },
- });
+ it("Shows the banner everywhere if there is an incident", () => {
+ fixtures.config().versions().userTest().getStatuspageInfo({
+ fixture: "statuspage/statuspage-outage.json",
+ });
cy.visit("/");
cy.wait("@getStatuspageInfo");
- cy.get(".alert").contains("RenkuLab is unstable").should("be.visible");
- cy.get(".btn-close").should("be.visible");
+ cy.get(".alert").contains("Ongoing incident").should("be.visible");
cy.contains("Search").click();
- cy.get(".alert").should("not.exist");
+ cy.get(".alert").contains("Ongoing incident").should("be.visible");
});
});