diff --git a/src/App.tsx b/src/App.tsx
index 7b7f510..8bf7cfe 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,18 +1,21 @@
import React from "react";
-import { ThemeProvider } from "@mui/material";
import { theme } from "./theme";
import { Routes, Route } from "react-router-dom";
import AppHeader from "./components/AppHeader";
import Home from "./routes/Home";
+import ThemeProvider from "@mui/material/styles/ThemeProvider";
+import { GlobalStateProvider } from "./GlobalStateProvider";
function App() {
return (
-
-
- } />
- {/* } /> */}
-
+
+
+
+ } />
+ {/* } /> */}
+
+
);
}
diff --git a/src/GlobalStateProvider.tsx b/src/GlobalStateProvider.tsx
new file mode 100644
index 0000000..09a51c3
--- /dev/null
+++ b/src/GlobalStateProvider.tsx
@@ -0,0 +1,36 @@
+import React, { Dispatch, SetStateAction, createContext, useContext, useState } from "react";
+
+// how to: Typesafe Global State with TypeScript, React & React Context
+// https://jamiehaywood.medium.com/typesafe-global-state-with-typescript-react-react-context-c2df743f3ce
+
+interface GlobalState {
+ searchFilter: string;
+}
+
+const defaultGlobalState: GlobalState = {
+ searchFilter: "",
+};
+
+export const GlobalStateContext = createContext({
+ state: {} as Partial,
+ setState: {} as Dispatch>>,
+});
+
+export const GlobalStateProvider = ({
+ children,
+ value = {} as GlobalState,
+}: {
+ children: React.ReactNode;
+ value?: Partial;
+}) => {
+ const [state, setState] = useState(value);
+ return {children};
+};
+
+export const useGlobalState = () => {
+ const context = useContext(GlobalStateContext);
+ if (!context) {
+ throw new Error("useGlobalState must be used within a GlobalStateContext");
+ }
+ return context;
+};
diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx
index ff9e514..6baad48 100644
--- a/src/components/AppHeader.tsx
+++ b/src/components/AppHeader.tsx
@@ -1,17 +1,57 @@
import React from "react";
-import { Container, Typography } from "@mui/material";
import Stack from "@mui/material/Stack";
import RadarIcon from "@mui/icons-material/Radar";
+import Container from "@mui/material/Container";
+import Typography from "@mui/material/Typography";
+import TextField from "@mui/material/TextField";
+import InputAdornment from "@mui/material/InputAdornment";
+import IconButton from "@mui/material/IconButton";
+import CancelIcon from "@mui/icons-material/Cancel";
+import { useGlobalState } from "../GlobalStateProvider";
export default function AppHeader() {
+ const [searchValue, setSearchValue] = React.useState("");
+ const { setState: setGlobalState } = useGlobalState();
+
+ const handleSearchInputChange = (event: React.ChangeEvent) => {
+ const searchFilter = event.currentTarget.value;
+ setGlobalState({
+ searchFilter,
+ });
+ setSearchValue(event.currentTarget.value);
+ };
+ const handleClearSearchInputClick = () => {
+ setSearchValue("");
+ setGlobalState({
+ searchFilter: "",
+ });
+ };
+
return (
- <>
-
-
-
- pingator
-
-
- >
+
+
+
+ pingator
+
+
+
+
+
+
+ ),
+ }}
+ />
+
+
);
}
diff --git a/src/components/dashboard/ServiceCard.tsx b/src/components/dashboard/ServiceCard.tsx
index 9f02bb2..387f8af 100644
--- a/src/components/dashboard/ServiceCard.tsx
+++ b/src/components/dashboard/ServiceCard.tsx
@@ -5,7 +5,6 @@ import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Stack from "@mui/material/Stack";
import Tooltip from "@mui/material/Tooltip";
-import Chip from "@mui/material/Chip";
import Box from "@mui/material/Box";
import Link from "@mui/material/Link";
import Avatar from "@mui/material/Avatar";
@@ -22,24 +21,6 @@ function normalizeServiceName(name: string): string {
.replace(/\b\w/g, (s) => s.toUpperCase());
}
-interface TenantsStatusTooltipProps {
- tenantsStatus: StatusPerTenant;
-}
-
-function TenantsStatusTooltip({ tenantsStatus }: TenantsStatusTooltipProps) {
- const statusItems = Object.entries(tenantsStatus).map(([key, value]) => {
- console.log(`key: ${key}, value: ${value}`);
- return null;
- });
-
- return (
-
- Tooltip with HTML
- {"And here's"} {"some"} {"amazing content"}. {"It's very engaging. Right?"}
-
- );
-}
-
function getNodeNameElement(node: Node): React.ReactElement {
let statusMessage;
const statusOk = node.health_check_status?.status_ok;
@@ -69,7 +50,11 @@ function TenantsStatus({ statusPerTenant }: TenantsStatusProps) {
for (const [tenantId, statusOk] of Object.entries(statusPerTenant)) {
statuses.push(
-
+
{tenantId}
);
diff --git a/src/components/dashboard/ServiceList.tsx b/src/components/dashboard/ServiceList.tsx
index a4ea8be..a45ecf6 100644
--- a/src/components/dashboard/ServiceList.tsx
+++ b/src/components/dashboard/ServiceList.tsx
@@ -2,19 +2,27 @@ import React from "react";
import { Service } from "../../types";
import Grid from "@mui/material/Unstable_Grid2";
import ServiceCard from "./ServiceCard";
+import { useGlobalState } from "../../GlobalStateProvider";
interface ServiceListProps {
services: Service[];
}
export default function ServiceList({ services }: ServiceListProps) {
- const serviceItems = services.map((service) => {
- return (
-
-
-
- );
- });
+ const { state } = useGlobalState();
+
+ const serviceItems = services
+ .filter(
+ (service) =>
+ !state.searchFilter || service.name.toLocaleLowerCase().includes(state.searchFilter.toLocaleLowerCase())
+ )
+ .map((service) => {
+ return (
+
+
+
+ );
+ });
return (
diff --git a/src/scss/_app-header.scss b/src/scss/_app-header.scss
index b885748..1aa23c4 100644
--- a/src/scss/_app-header.scss
+++ b/src/scss/_app-header.scss
@@ -20,4 +20,9 @@
}
}
}
+
+ .search-input {
+ background-color: whitesmoke;
+ margin-left: auto;
+ }
}
diff --git a/src/theme.ts b/src/theme.ts
index a517970..fad1fb2 100644
--- a/src/theme.ts
+++ b/src/theme.ts
@@ -1,4 +1,4 @@
-import { createTheme } from "@mui/material";
+import createTheme from "@mui/material/styles/createTheme";
const PRIMARY_COLOR = "#0d6efd";
const SECONDARY_COLOR = "#6c757d";