diff --git a/src/components/tab.jsx b/src/components/tab.jsx
new file mode 100644
index 00000000..912051a3
--- /dev/null
+++ b/src/components/tab.jsx
@@ -0,0 +1,25 @@
+import { useContext } from "react";
+import classNames from "classnames";
+
+import { SettingsContext } from "utils/contexts/settings";
+import { TabContext } from "utils/contexts/tab";
+
+export default function Tab({ tab }) {
+ const { settings } = useContext(SettingsContext);
+ const { activeTab, setActiveTab } = useContext(TabContext);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx
index e99303ab..7b93b005 100644
--- a/src/pages/_app.jsx
+++ b/src/pages/_app.jsx
@@ -11,6 +11,7 @@ import nextI18nextConfig from "../../next-i18next.config";
import { ColorProvider } from "utils/contexts/color";
import { ThemeProvider } from "utils/contexts/theme";
import { SettingsProvider } from "utils/contexts/settings";
+import { TabProvider } from "utils/contexts/tab";
function MyApp({ Component, pageProps }) {
return (
@@ -26,7 +27,9 @@ function MyApp({ Component, pageProps }) {
-
+
+
+
diff --git a/src/pages/index.jsx b/src/pages/index.jsx
index 65426862..8a8e0fc4 100644
--- a/src/pages/index.jsx
+++ b/src/pages/index.jsx
@@ -7,7 +7,9 @@ import { useTranslation } from "next-i18next";
import { useEffect, useContext, useState, useMemo } from "react";
import { BiError } from "react-icons/bi";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
+import { useRouter } from "next/router";
+import Tab from "components/tab";
import FileContent from "components/filecontent";
import ServicesGroup from "components/services/group";
import BookmarksGroup from "components/bookmarks/group";
@@ -19,6 +21,7 @@ import { getSettings } from "utils/config/config";
import { ColorContext } from "utils/contexts/color";
import { ThemeContext } from "utils/contexts/theme";
import { SettingsContext } from "utils/contexts/settings";
+import { TabContext } from "utils/contexts/tab";
import { bookmarksResponse, servicesResponse, widgetsResponse } from "utils/config/api-response";
import ErrorBoundary from "components/errorboundry";
import themes from "utils/styles/themes";
@@ -169,6 +172,8 @@ function Home({ initialSettings }) {
const { theme, setTheme } = useContext(ThemeContext);
const { color, setColor } = useContext(ColorContext);
const { settings, setSettings } = useContext(SettingsContext);
+ const { activeTab, setActiveTab } = useContext(TabContext);
+ const { asPath } = useRouter();
useEffect(() => {
setSettings(initialSettings);
@@ -231,18 +236,52 @@ function Home({ initialSettings }) {
}
})
- const servicesAndBookmarksGroups = useMemo(() => {
- const layoutGroups = settings.layout ? Object.keys(settings.layout).map(
- (groupName) => services?.find(g => g.name === groupName) ?? bookmarks?.find(b => b.name === groupName)
- ).filter(g => g) : [];
+ const tabs = useMemo( () => {
+ if (!settings.layout) return [];
- const serviceGroups = services?.filter(group => settings.layout?.[group.name] === undefined);
- const bookmarkGroups = bookmarks.filter(group => settings.layout?.[group.name] === undefined);
+ return [
+ ...new Set(
+ Object.keys(settings.layout).map(
+ (groupName) => settings.layout[groupName]?.tab
+ ).filter(group => group)
+ )
+ ]
+ }, [settings.layout]);
+
+ if (!activeTab) {
+ const initialTab = decodeURI(asPath.substring(asPath.indexOf("#") + 1));
+ if (initialTab !== '/') {
+ setActiveTab(initialTab)
+ } else {
+ setActiveTab(tabs['0'] ?? false)
+ }
+ }
+
+ const servicesAndBookmarksGroups = useMemo(() => {
+ const tabGroupFilter = g => g && [activeTab, undefined].includes(settings.layout?.[g.name]?.tab);
+ const undefinedGroupFilter = g => settings.layout?.[g.name] === undefined;
+
+ const layoutGroups = Object.keys(settings.layout ?? {}).map(
+ (groupName) => services?.find(g => g.name === groupName) ?? bookmarks?.find(b => b.name === groupName)
+ ).filter(tabGroupFilter);
+
+ if (!settings.layout || !layoutGroups) {
+ // wait for settings to populate, otherwise all the widgets will be requested initially even if we are on a single tab
+ return ;
+ }
+
+ const serviceGroups = services?.filter(tabGroupFilter).filter(undefinedGroupFilter);
+ const bookmarkGroups = bookmarks.filter(tabGroupFilter).filter(undefinedGroupFilter);
return <>
+ {tabs.length > 0 && }
{layoutGroups.length > 0 &&
{layoutGroups.map((group) => (
- group.services ?
+ group.services ?
(}
>
}, [
+ tabs,
+ activeTab,
services,
bookmarks,
settings.layout,
diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js
index 023c89e3..63c5c34c 100644
--- a/src/utils/config/service-helpers.js
+++ b/src/utils/config/service-helpers.js
@@ -286,6 +286,7 @@ export async function servicesFromKubernetes() {
export function cleanServiceGroups(groups) {
return groups.map((serviceGroup) => ({
name: serviceGroup.name,
+ tabs: serviceGroup?.tabs,
services: serviceGroup.services.map((service) => {
const cleanedService = { ...service };
if (cleanedService.showStats !== undefined) cleanedService.showStats = JSON.parse(cleanedService.showStats);
diff --git a/src/utils/contexts/tab.jsx b/src/utils/contexts/tab.jsx
new file mode 100644
index 00000000..8cd5d520
--- /dev/null
+++ b/src/utils/contexts/tab.jsx
@@ -0,0 +1,15 @@
+import { createContext, useState, useMemo } from "react";
+
+export const TabContext = createContext();
+
+export function TabProvider({ initialTab, children }) {
+ const [activeTab, setActiveTab] = useState(false);
+
+ if (initialTab) {
+ setActiveTab(initialTab);
+ }
+
+ const value = useMemo(() => ({ activeTab, setActiveTab }), [activeTab]);
+
+ return {children};
+}