From b1f7d8196a1b8bbe0888b2487d581d45ce8940d3 Mon Sep 17 00:00:00 2001 From: Eden Kirin Date: Sun, 28 Jan 2024 10:35:01 +0100 Subject: [PATCH] Global search filter --- src/App.tsx | 15 +++--- src/GlobalStateProvider.tsx | 36 +++++++++++++++ src/components/AppHeader.tsx | 58 ++++++++++++++++++++---- src/components/dashboard/ServiceCard.tsx | 25 ++-------- src/components/dashboard/ServiceList.tsx | 22 ++++++--- src/scss/_app-header.scss | 5 ++ src/theme.ts | 2 +- 7 files changed, 120 insertions(+), 43 deletions(-) create mode 100644 src/GlobalStateProvider.tsx 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";