From 4e02bafa6a3659a58c118b8eab1609c4059acd1b Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Fri, 28 Nov 2025 15:06:03 +0000 Subject: [PATCH] - added status grid items - add react modal pkg --- package.json | 2 + .../dashboard/components/SystemHealth.tsx | 33 ++++++++++--- .../statusGridItem/StatusGridItem.tsx | 43 +++++++++++++++++ .../systemHealthModal/SystemHealthModal.tsx | 48 +++++++++++++++++++ src/main.tsx | 3 ++ src/types/types.ts | 8 ++++ src/ui/ModalComponent.tsx | 22 +++++++++ yarn.lock | 45 ++++++++++++++++- 8 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 src/features/dashboard/components/statusGridItem/StatusGridItem.tsx create mode 100644 src/features/dashboard/components/systemHealthModal/SystemHealthModal.tsx create mode 100644 src/ui/ModalComponent.tsx diff --git a/package.json b/package.json index f1f0622..83f6739 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "react": "^19.2.0", "react-dom": "^19.2.0", "react-konva": "^19.2.0", + "react-modal": "^3.16.3", "react-tabs": "^6.1.0", "react-use-websocket": "3.0.0", "sonner": "^2.0.7" @@ -35,6 +36,7 @@ "@types/node": "^24.10.1", "@types/react": "^19.2.5", "@types/react-dom": "^19.2.3", + "@types/react-modal": "^3.16.3", "@vitejs/plugin-react": "^5.1.1", "autoprefixer": "^10.4.22", "eslint": "^9.39.1", diff --git a/src/features/dashboard/components/SystemHealth.tsx b/src/features/dashboard/components/SystemHealth.tsx index 664ac0b..4055e7a 100644 --- a/src/features/dashboard/components/SystemHealth.tsx +++ b/src/features/dashboard/components/SystemHealth.tsx @@ -1,5 +1,5 @@ import type { SystemHealthStatus } from "../../../types/types"; -import Badge from "../../../ui/Badge"; +import StatusGridItem from "./statusGridItem/StatusGridItem"; type SystemHealthProps = { startTime: string; @@ -13,7 +13,28 @@ type SystemHealthProps = { const SystemHealth = ({ startTime, uptime, statuses, isLoading, isError, dateUpdatedAt }: SystemHealthProps) => { const updatedDate = dateUpdatedAt ? new Date(dateUpdatedAt).toLocaleString() : null; - // console.log(statuses); + const statusCategories = statuses?.reduce>( + (acc, cur) => { + if (cur?.groupID === "ChannelA") acc?.channelA?.push(cur); + if (cur?.groupID === "ChannelB") acc?.channelB?.push(cur); + if (cur?.groupID === "ChannelC") acc?.channelC?.push(cur); + if (cur?.groupID === "Default") acc?.default?.push(cur); + return acc; + }, + { + channelA: [], + channelB: [], + channelC: [], + default: [], + }, + ); + + const convertObjtoArray = (obj: Record) => { + if (!obj) return; + const statusCategoryArray = Object.entries(obj); + return statusCategoryArray; + }; + const statusCategoryArray = convertObjtoArray(statusCategories); if (isError) { return Error loading system health.; @@ -31,11 +52,9 @@ const SystemHealth = ({ startTime, uptime, statuses, isLoading, isError, dateUpd

Up Time

{uptime} -
- {statuses?.map((status: SystemHealthStatus) => ( -
- {status.id} -
+
+ {statusCategoryArray?.map((status) => ( + ))}
diff --git a/src/features/dashboard/components/statusGridItem/StatusGridItem.tsx b/src/features/dashboard/components/statusGridItem/StatusGridItem.tsx new file mode 100644 index 0000000..05318b5 --- /dev/null +++ b/src/features/dashboard/components/statusGridItem/StatusGridItem.tsx @@ -0,0 +1,43 @@ +import { useState } from "react"; +import type { SystemHealthStatus } from "../../../../types/types"; +import StatusIndicators from "../../../../ui/StatusIndicators"; +import { capitalize } from "../../../../utils/utils"; +import SystemHealthModal from "../systemHealthModal/SystemHealthModal"; + +type StatusGridItemProps = { + title: string; + statusCategory: SystemHealthStatus[]; +}; + +const StatusGridItem = ({ title, statusCategory }: StatusGridItemProps) => { + const [isOpen, setIsOpen] = useState(false); + const isAllGood = statusCategory.every((status) => status.tags.includes("RUNNING")); + + const handleClick = () => { + setIsOpen(false); + }; + + return ( + <> +
setIsOpen(true)} + > +

+ {isAllGood ? : } + {capitalize(title)} +

+

{isAllGood ? "All systems running" : "Some systems down"}

+
+ + + ); +}; + +export default StatusGridItem; diff --git a/src/features/dashboard/components/systemHealthModal/SystemHealthModal.tsx b/src/features/dashboard/components/systemHealthModal/SystemHealthModal.tsx new file mode 100644 index 0000000..fb43cb2 --- /dev/null +++ b/src/features/dashboard/components/systemHealthModal/SystemHealthModal.tsx @@ -0,0 +1,48 @@ +import type { SystemHealthStatus } from "../../../../types/types"; +import Badge from "../../../../ui/Badge"; +import ModalComponent from "../../../../ui/ModalComponent"; +import StatusIndicators from "../../../../ui/StatusIndicators"; +import { capitalize } from "../../../../utils/utils"; + +type SystemHealthModalProps = { + isSystemHealthModalOpen: boolean; + handleClose: () => void; + statusCategory: SystemHealthStatus[]; + title: string; + isAllGood: boolean; +}; + +const SystemHealthModal = ({ + isSystemHealthModalOpen, + handleClose, + statusCategory, + title, + isAllGood, +}: SystemHealthModalProps) => { + return ( + +
+
+

+ {isAllGood ? : } + {capitalize(title)} +

+

{isAllGood ? "All systems running" : "Some systems down"}

+
+ +
+ {statusCategory?.map((status: SystemHealthStatus) => ( +
+ {status.id} +
+ ))} +
+
+
+ ); +}; + +export default SystemHealthModal; diff --git a/src/main.tsx b/src/main.tsx index d0ee7d9..90b08ec 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,9 +4,12 @@ import { RouterProvider, createRouter } from "@tanstack/react-router"; import { routeTree } from "./routeTree.gen"; // generated by plugin import { AppProviders } from "./app/providers/AppProviders"; import "./index.css"; +import Modal from "react-modal"; const router = createRouter({ routeTree }); +Modal.setAppElement("#root"); + declare module "@tanstack/react-router" { interface Register { router: typeof router; diff --git a/src/types/types.ts b/src/types/types.ts index d59e1ad..814decb 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -20,6 +20,14 @@ export type Region = { export type SystemHealthStatus = { id: string; tags: string[]; + groupID: string; +}; + +export type StatusGroups = { + channelA: SystemHealthStatus[]; + channelB: SystemHealthStatus[]; + channelC: SystemHealthStatus[]; + default: SystemHealthStatus[]; }; export type BearerTypeFields = { diff --git a/src/ui/ModalComponent.tsx b/src/ui/ModalComponent.tsx new file mode 100644 index 0000000..6a1684d --- /dev/null +++ b/src/ui/ModalComponent.tsx @@ -0,0 +1,22 @@ +import Modal from "react-modal"; + +type ModalComponentProps = { + isModalOpen: boolean; + children: React.ReactNode; + close: () => void; +}; + +const ModalComponent = ({ isModalOpen, children, close }: ModalComponentProps) => { + return ( + + {children} + + ); +}; + +export default ModalComponent; diff --git a/yarn.lock b/yarn.lock index ceebd90..612afcf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1018,6 +1018,13 @@ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== +"@types/react-modal@^3.16.3": + version "3.16.3" + resolved "https://registry.yarnpkg.com/@types/react-modal/-/react-modal-3.16.3.tgz#250f32c07f1de28e2bcf9c3e84b56adaa6897013" + integrity sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg== + dependencies: + "@types/react" "*" + "@types/react-reconciler@^0.28.9": version "0.28.9" resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.28.9.tgz#d24b4864c384e770c83275b3fe73fba00269c83b" @@ -1028,6 +1035,13 @@ resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.32.3.tgz#eb4b346f367f29f07628032934d30a4f3f9eaba7" integrity sha512-cMi5ZrLG7UtbL7LTK6hq9w/EZIRk4Mf1Z5qHoI+qBh7/WkYkFXQ7gOto2yfUvPzF5ERMAhaXS5eTQ2SAnHjLzA== +"@types/react@*": + version "19.2.7" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.7.tgz#84e62c0f23e8e4e5ac2cadcea1ffeacccae7f62f" + integrity sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg== + dependencies: + csstype "^3.2.2" + "@types/react@^19.2.5": version "19.2.6" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.6.tgz#d27db1ff45012d53980f5589fda925278e1249ca" @@ -1540,6 +1554,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +exenv@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1934,7 +1953,7 @@ lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -2104,7 +2123,7 @@ prettier@^3.5.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== -prop-types@^15.5.0: +prop-types@^15.5.0, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -2150,6 +2169,21 @@ react-konva@^19.2.0: react-reconciler "0.33.0" scheduler "0.27.0" +react-lifecycles-compat@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-modal@^3.16.3: + version "3.16.3" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.3.tgz#c412d41915782e3c261253435d01468e2439b11b" + integrity sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw== + dependencies: + exenv "^1.2.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.0" + warning "^4.0.3" + react-reconciler@0.33.0: version "0.33.0" resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.33.0.tgz#9dd20208d45baa5b0b4701781f858236657f15e1" @@ -2446,6 +2480,13 @@ vite@^7.1.7, vite@^7.2.4: optionalDependencies: fsevents "~2.3.3" +warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + webpack-virtual-modules@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8"