diff --git a/.env b/.env
index 3b2f823..0ba4b92 100644
--- a/.env
+++ b/.env
@@ -1 +1,2 @@
-"VITE_BACKEND_PORT=9000"
+VITE_BACKEND_PORT=9000
+VITE_API_TIMEOUT=5000
diff --git a/package-lock.json b/package-lock.json
index 13b7546..6246795 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,11 +25,14 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cmdk": "^1.0.0",
+ "dotenv": "^16.4.5",
"lucide-react": "^0.371.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-query": "^3.39.3",
"react-router-dom": "^6.16.0",
"react-wrap-balancer": "^1.1.0",
+ "sonner": "^1.4.41",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7"
},
@@ -4341,6 +4344,14 @@
"version": "1.0.2",
"license": "MIT"
},
+ "node_modules/big-integer": {
+ "version": "1.6.52",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
+ "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.2.0",
"license": "MIT",
@@ -4366,6 +4377,21 @@
"node": ">=8"
}
},
+ "node_modules/broadcast-channel": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz",
+ "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==",
+ "dependencies": {
+ "@babel/runtime": "^7.7.2",
+ "detect-node": "^2.1.0",
+ "js-sha3": "0.8.0",
+ "microseconds": "0.2.0",
+ "nano-time": "1.0.0",
+ "oblivious-set": "1.0.0",
+ "rimraf": "3.0.2",
+ "unload": "2.2.0"
+ }
+ },
"node_modules/browserslist": {
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
@@ -4845,6 +4871,11 @@
"node": ">=6"
}
},
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
+ },
"node_modules/detect-node-es": {
"version": "1.1.0",
"license": "MIT"
@@ -4881,6 +4912,17 @@
"node": ">=6.0.0"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.4.748",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.748.tgz",
@@ -7564,6 +7606,11 @@
"jiti": "bin/jiti.js"
}
},
+ "node_modules/js-sha3": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+ "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"license": "MIT"
@@ -7762,6 +7809,15 @@
"node": ">=12"
}
},
+ "node_modules/match-sorter": {
+ "version": "6.3.4",
+ "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz",
+ "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.8",
+ "remove-accents": "0.5.0"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"license": "MIT",
@@ -7780,6 +7836,11 @@
"node": ">=8.6"
}
},
+ "node_modules/microseconds": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz",
+ "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA=="
+ },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -7832,6 +7893,14 @@
"thenify-all": "^1.0.0"
}
},
+ "node_modules/nano-time": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",
+ "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==",
+ "dependencies": {
+ "big-integer": "^1.6.16"
+ }
+ },
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@@ -8012,6 +8081,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/oblivious-set": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz",
+ "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw=="
+ },
"node_modules/once": {
"version": "1.4.0",
"license": "ISC",
@@ -8383,6 +8457,31 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
+ "node_modules/react-query": {
+ "version": "3.39.3",
+ "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",
+ "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "broadcast-channel": "^3.4.1",
+ "match-sorter": "^6.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-refresh": {
"version": "0.14.0",
"dev": true,
@@ -8615,6 +8714,11 @@
"jsesc": "bin/jsesc"
}
},
+ "node_modules/remove-accents": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
+ "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -8669,7 +8773,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dev": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -8846,6 +8949,15 @@
"node": ">=8"
}
},
+ "node_modules/sonner": {
+ "version": "1.4.41",
+ "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.41.tgz",
+ "integrity": "sha512-uG511ggnnsw6gcn/X+YKkWPo5ep9il9wYi3QJxHsYe7yTZ4+cOd1wuodOUmOpFuXL+/RE3R04LczdNCDygTDgQ==",
+ "peerDependencies": {
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
@@ -9361,6 +9473,15 @@
"node": ">=4"
}
},
+ "node_modules/unload": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz",
+ "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==",
+ "dependencies": {
+ "@babel/runtime": "^7.6.2",
+ "detect-node": "^2.0.4"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.0.13",
"dev": true,
diff --git a/package.json b/package.json
index 9eec9b0..77bd14a 100644
--- a/package.json
+++ b/package.json
@@ -28,11 +28,14 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cmdk": "^1.0.0",
+ "dotenv": "^16.4.5",
"lucide-react": "^0.371.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-query": "^3.39.3",
"react-router-dom": "^6.16.0",
"react-wrap-balancer": "^1.1.0",
+ "sonner": "^1.4.41",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7"
},
diff --git a/src/App.tsx b/src/App.tsx
index 654103c..7087140 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -2,13 +2,30 @@ import { RouterProvider } from "react-router-dom";
import { ThemeProvider } from "./contexts/ThemeContext";
import { router } from "./Router";
import { TooltipProvider } from "@/components/ui/tooltip";
+import { toast, Toaster } from "sonner";
+import { QueryClient, QueryClientProvider } from "react-query";
export default function App() {
+ const queryClient = new QueryClient();
+ queryClient.setDefaultOptions({
+ queries: {
+ onError: (error: unknown) => {
+ if (error instanceof Error) {
+ toast.error(`Something went wrong: ${error.message}`);
+ }
+ },
+ staleTime: 10000,
+ refetchOnWindowFocus: "always",
+ },
+ });
return (
-
-
-
+
+
+
+
+
+
);
}
diff --git a/src/Router.tsx b/src/Router.tsx
index 60087d7..c745a92 100644
--- a/src/Router.tsx
+++ b/src/Router.tsx
@@ -6,7 +6,7 @@ import NoMatch from "./modules/fallback/NoMatch";
import ClustersPage from "@/modules/clusters/clusters-list/ClustersPage";
import { appConfig } from "@/config/app";
import { ClusterInfo } from "@/modules/clusters/cluster-information/ClusterInfo";
-const defaultTab = appConfig.sveltosType;
+const defaultTab = appConfig.defaultType;
const defaultPage = appConfig.defaultPage;
export const router = createBrowserRouter(
[
diff --git a/src/api-client/apiClient.ts b/src/api-client/apiClient.ts
index 06f5f17..12ef360 100644
--- a/src/api-client/apiClient.ts
+++ b/src/api-client/apiClient.ts
@@ -1,6 +1,14 @@
import axios from "axios";
+import { toast } from "sonner";
const client = axios.create({
- baseURL: import.meta.env.VITE_SVELTOS_API_BASE_URL,
- timeout: 1000,
+ baseURL: "/api",
+ timeout: import.meta.env.VITE_API_TIMEOUT,
});
+
+client.interceptors.request.use(function (config) {
+ config.headers["Content-Type"] = "application/json";
+ return config;
+});
+
+export default client;
diff --git a/src/components/layouts/Header.tsx b/src/components/layouts/Header.tsx
index c868c3c..bdd5df6 100644
--- a/src/components/layouts/Header.tsx
+++ b/src/components/layouts/Header.tsx
@@ -29,7 +29,7 @@ import {
AccordionTrigger,
} from "@/components/ui/accordion";
import { ModeToggle } from "@/components/mode-toggle";
-import { Badge } from "@/components/ui/badge";
+
export function Header() {
const [open, setOpen] = useState(false);
@@ -99,7 +99,7 @@ export function Header() {
cn(
"text-sm font-medium flex items-center transition-colors hover:text-primary",
isActive
- ? "bg-slate-100 dark:bg-slate-700 p-2 rounded hover:text-main-500 "
+ ? "bg-slate-100 dark:bg-slate-800 p-2 rounded hover:text-main-500 "
: "text-foreground/60 hover:text-primary",
)
}
diff --git a/src/components/ui/PageHeading.tsx b/src/components/ui/PageHeading.tsx
index 0ef4de8..99ffad9 100644
--- a/src/components/ui/PageHeading.tsx
+++ b/src/components/ui/PageHeading.tsx
@@ -2,6 +2,7 @@ import { CardsFilterToolbar } from "@/modules/clusters/clusters-list/components/
interface PageHeadingProps {
title: string;
description: string;
+
}
export const PageHeading = ({ title, description }: PageHeadingProps) => {
return (
@@ -12,10 +13,6 @@ export const PageHeading = ({ title, description }: PageHeadingProps) => {
{title}
{description}
-
- Retry
- {" "}
- or{" "}
(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+
+ const queryClient = useQueryClient();
+ const isFetching = useIsFetching();
+ const handleRefresh = () =>{
+ queryClient.refetchQueries();
+ };
+ return (
+
+ );
+ },
+);
+RefreshButton.displayName = "Button";
+
+export { RefreshButton };
\ No newline at end of file
diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx
new file mode 100644
index 0000000..13219e7
--- /dev/null
+++ b/src/components/ui/alert.tsx
@@ -0,0 +1,59 @@
+import * as React from "react";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const alertVariants = cva(
+ "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
+ {
+ variants: {
+ variant: {
+ default: "bg-background text-foreground",
+ destructive:
+ "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+const Alert = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes & VariantProps
+>(({ className, variant, ...props }, ref) => (
+
+));
+Alert.displayName = "Alert";
+
+const AlertTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+AlertTitle.displayName = "AlertTitle";
+
+const AlertDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+AlertDescription.displayName = "AlertDescription";
+
+export { Alert, AlertTitle, AlertDescription };
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx
index d3d5d60..4d49336 100644
--- a/src/components/ui/badge.tsx
+++ b/src/components/ui/badge.tsx
@@ -8,6 +8,8 @@ const badgeVariants = cva(
{
variants: {
variant: {
+ label:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
default:
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary:
@@ -28,8 +30,10 @@ export interface BadgeProps
VariantProps {}
function Badge({ className, variant, ...props }: BadgeProps) {
+ const isLabelVariant = variant === 'label';
+
return (
-
+
);
}
diff --git a/src/components/ui/emptyData.tsx b/src/components/ui/emptyData.tsx
new file mode 100644
index 0000000..d5094f7
--- /dev/null
+++ b/src/components/ui/emptyData.tsx
@@ -0,0 +1,51 @@
+
+import { CircleOff, FilterX, RefreshCcw } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { useQueryClient } from "react-query";
+import { RefreshButton } from "@/components/ui/RefreshButton";
+
+type EmptyDataProps = {
+ name: string;
+ isFiltered?: boolean;
+ clearFilter?: () => void;
+ refreshKey?: string;
+}
+
+export const EmptyData = ({name,isFiltered,clearFilter,refreshKey}:EmptyDataProps) => {
+ const queryClient = useQueryClient();
+ const handleRefresh = () => {
+
+ queryClient.invalidateQueries()
+
+ }
+ return (
+ <>
+
+
+
+
+
+
+
+ There’s no {name} here
+
+
+ {isFiltered
+ ? "Try changing the filter criteria or clear the filter"
+ : "Try refreshing the page or check back later"}
+
+
+
+ {isFiltered && (
+
+ )}
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/components/ui/errorQuery.tsx b/src/components/ui/errorQuery.tsx
new file mode 100644
index 0000000..f183d74
--- /dev/null
+++ b/src/components/ui/errorQuery.tsx
@@ -0,0 +1,55 @@
+import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
+import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
+
+import { Separator } from "@/components/ui/separator";
+import { Button } from "@/components/ui/button";
+import { AlertTriangleIcon, RefreshCcw } from "lucide-react";
+import { appConfig } from "@/config/app";
+import { useQueryClient } from "react-query";
+import { RefreshButton } from "@/components/ui/RefreshButton";
+
+type ErrorFetchingProps = {
+ name: string;
+ error: unknown;
+ queryKey?: string;
+};
+
+export const ErrorQuery = ({ name, error, queryKey }: ErrorFetchingProps) => {
+
+ const reportError = () => {
+ window.open(appConfig.github.url + "/issues/new", "_blank");
+ };
+
+ return (
+ <>
+
+
+ Error
+
+
+
+ Failed to retrieve {name}, please ensure the backend is running on
+ port {import.meta.env.VITE_BACKEND_PORT}.
+
+
+
+
+ {error instanceof Error && (
+ {error.message}
+ )}
+
+
+
+ >
+ );
+};
diff --git a/src/components/ui/failed-flag.tsx b/src/components/ui/failed-flag.tsx
index 8f3df55..c2c0d47 100644
--- a/src/components/ui/failed-flag.tsx
+++ b/src/components/ui/failed-flag.tsx
@@ -10,7 +10,7 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
-export const FailedFlag = () => {
+export const FailedFlag = ({ msg }: { msg?: string | undefined | null }) => {
return (
<>
@@ -25,6 +25,7 @@ export const FailedFlag = () => {
Unhealthy
+ {msg &&
{msg}
}
diff --git a/src/config/app.ts b/src/config/app.ts
index c3d2244..d103b5e 100644
--- a/src/config/app.ts
+++ b/src/config/app.ts
@@ -1,14 +1,20 @@
-export type clusterType = "SveltosCluster" | "ClusterAPI";
+import {
+ ClusterApiType,
+ ClusterType,
+ SveltosClusterType,
+} from "@/types/cluster";
+import { sveltosClusterValue } from "@/types/cluster.consts";
+
interface AppConfig {
name: string;
github: {
title: string;
url: string;
};
- sveltosType: clusterType;
- clusterAPIType: clusterType;
- defaultType: clusterType;
+ defaultType: ClusterType;
defaultPage: number;
+ defaultSize: number;
+ maxBadges: number;
}
export const appConfig: AppConfig = {
@@ -17,8 +23,8 @@ export const appConfig: AppConfig = {
title: "dashboard",
url: "https://github.com/projectsveltos/dashboard",
},
- sveltosType: "SveltosCluster",
- clusterAPIType: "ClusterAPI",
- defaultType: "SveltosCluster",
+ defaultType: sveltosClusterValue,
defaultPage: 0,
+ defaultSize: 8,
+ maxBadges: 2,
};
diff --git a/src/modules/clusters/clusters-list/ClustersPage.tsx b/src/modules/clusters/clusters-list/ClustersPage.tsx
index 571420b..8bcc506 100644
--- a/src/modules/clusters/clusters-list/ClustersPage.tsx
+++ b/src/modules/clusters/clusters-list/ClustersPage.tsx
@@ -1,162 +1,39 @@
import { PageHeading } from "@/components/ui/PageHeading";
-import { appConfig, clusterType } from "@/config/app";
+import { appConfig } from "@/config/app";
import { useNavigate, useParams } from "react-router-dom";
import { useState } from "react";
+import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { ClusterType } from "@/types/cluster";
+import useClusters from "@/modules/clusters/clusters-list/hooks/useClusters";
+import { clusterTypes } from "@/types/cluster.consts";
import { ClusterList } from "@/modules/clusters/clusters-list/components/ClusterList";
+import { ErrorQuery } from "@/components/ui/errorQuery";
+import { LoadingCards } from "@/modules/clusters/clusters-list/components/LoadingCards";
+
export default function ClustersPage() {
- const dummyClusterData = [
- {
- name: "Cluster 1",
- version: "v1.22.3",
- namespace: "namespace1",
- type: "clusterAPI",
- status: true,
- labels: [
- {
- designation: "env:production",
- color: "red",
- },
- {
- designation: "euw:devp1",
- color: "green",
- },
- ],
- },
- {
- name: "Cluster 2",
- version: "v1.20.7",
- type: "sveltosCluster",
- namespace: "namespace2",
- status: true,
- labels: [
- {
- designation: "ru:devp2",
- color: "amber",
- },
- {
- designation: "env:engi",
- color: "purple",
- },
- ],
- },
- {
- name: "Cluster 2",
- version: "v1.20.7",
- type: "clusterAPI",
- namespace: "namespace2",
- status: false,
- labels: [
- {
- designation: "weu:development",
- color: "yellow",
- },
- {
- designation: "env:eng",
- color: "purple",
- },
- ],
- },
- {
- name: "Cluster 2",
- version: "v1.20.7",
- type: "clusterAPI",
- namespace: "namespace2",
- status: true,
- labels: [
- {
- designation: "env:development",
- color: "yellow",
- },
- {
- designation: "euw:engineering",
- color: "purple",
- },
- ],
- },
- {
- name: "Cluster 2",
- version: "v1.20.7",
- type: "sveltosCluster",
- namespace: "namespace2",
- status: true,
- labels: [
- {
- designation: "env:development",
- color: "yellow",
- },
- {
- designation: "env:eng",
- color: "purple",
- },
- ],
- },
- {
- name: "Cluster 2",
- type: "sveltosCluster",
- version: "v1.20.7",
- namespace: "namespace2",
- status: false,
- labels: [
- {
- designation: "euw:development",
- color: "yellow",
- },
- {
- designation: "euw:engineering",
- color: "purple",
- },
- ],
- },
- {
- name: "Cluster 2",
- version: "v1.20.7",
- type: "clusterAPI",
- namespace: "namespace2",
- status: false,
- labels: [
- {
- designation: "euw:par1",
- color: "amber",
- },
- {
- designation: "euw:par2",
- color: "purple",
- },
- ],
- },
- {
- name: "Cluster 78",
- version: "v1.21.5",
- type: "sveltosCluster",
- namespace: "namespace3",
- status: true,
- labels: [
- {
- designation: "env:staging",
- color: "orange",
- },
- {
- designation: "na:qa",
- color: "red",
- },
- ],
- },
- ];
const navigate = useNavigate();
const defaultTab = appConfig.defaultType;
const defaultPage = appConfig.defaultPage;
const { tab: urlTab, page: urlPage } = useParams();
- const [currentTab, setCurrentTab] = useState(() => {
- return urlTab ? (urlTab as clusterType) : defaultTab;
+ const [currentTab, setCurrentTab] = useState(() => {
+ return urlTab ? (urlTab as ClusterType) : defaultTab;
});
const [currentPage, setCurrentPage] = useState(() => {
return urlPage ? parseInt(urlPage) : defaultPage;
});
-
- const handleTabChange = (value: clusterType) => {
+ const {
+ data = [],
+ isLoading,
+ isError,
+ isSuccess,
+ error,
+ refetch,
+ } = useClusters(currentTab);
+ const handleTabChange = (value: ClusterType) => {
+ setCurrentTab(value);
navigate(`/clusters/${value}/${currentPage}`);
};
@@ -165,15 +42,26 @@ export default function ClustersPage() {
-
+
+
+ {clusterTypes.map((type) => (
+ handleTabChange(type)}
+ >
+ {type}
+
+ ))}
+
+
+ {isLoading && }
+ {isError && }
+ {isSuccess && }
>
);
}
diff --git a/src/modules/clusters/clusters-list/components/CardsFilterToolbar.tsx b/src/modules/clusters/clusters-list/components/CardsFilterToolbar.tsx
index 149d2a0..9b3f97f 100644
--- a/src/modules/clusters/clusters-list/components/CardsFilterToolbar.tsx
+++ b/src/modules/clusters/clusters-list/components/CardsFilterToolbar.tsx
@@ -3,6 +3,7 @@ import { Button } from "@/components/ui/button";
import { Cross2Icon } from "@radix-ui/react-icons";
import { useState } from "react";
import { CardsFacetedFilter } from "@/modules/clusters/clusters-list/components/CardsFacetedFilter";
+import { RefreshButton } from "@/components/ui/RefreshButton";
export const CardsFilterToolbar = () => {
const [isFiltered, setIsFiltered] = useState(false);
@@ -31,6 +32,7 @@ export const CardsFilterToolbar = () => {
{ label: "Inactive", value: "inactive" },
]}
/>
+
{isFiltered && (