diff --git a/package.json b/package.json index 18f4bbe..3ec7207 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,6 @@ "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-free": "^6.4.0", - "@mui/icons-material": "^5.14.0", - "@mui/material": "^5.14.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -16,6 +14,7 @@ "@types/node": "^20.2.5", "@types/react": "^18.2.7", "@types/react-dom": "^18.2.4", + "ansi-to-react": "^6.1.6", "axios": "^1.4.0", "chart.js": "^4.3.0", "formik": "^2.4.1", @@ -26,6 +25,7 @@ "moment": "^2.29.4", "react": "^18.2.0", "react-datepicker": "^4.12.0", + "react-datetime-picker": "^5.5.1", "react-dom": "^18.2.0", "react-form-stepper": "^2.0.3", "react-fullscreen-loading": "^0.0.4", diff --git a/src/App.css b/src/App.css index 74b5e05..2c871ed 100644 --- a/src/App.css +++ b/src/App.css @@ -1,38 +1,3 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } +body .dark-mode { + --mdb-body-color: #fff !important; } diff --git a/src/App.js b/src/App.js index 212daac..bffba16 100644 --- a/src/App.js +++ b/src/App.js @@ -6,20 +6,29 @@ import { useNavigate, } from "react-router-dom"; import { Toaster } from "react-hot-toast"; - +import "./App.css"; import { SidebarComponent } from "./components/sidebar"; import { MDBCol, MDBRow } from "mdb-react-ui-kit"; -import { useContext, useMemo, useState, useEffect } from "react"; +import { useEffect, useMemo, useState } from "react"; import { AppContext } from "./provider/contextProvider"; -import { useAuthContext } from "./provider/authProvider"; import RequireAuth from "./hoc/requireAuth"; import Loader from "./components/fullscreenLoader"; import { useStore } from "./store"; -import { SuccessScreen, Dashboard, Login, Add } from "./pages"; +import { SuccessScreen, Dashboard, Login, Add, Overview } from "./pages"; import { history } from "./utils/history"; -import { useAuth } from "./hooks/useAuth"; -import { AuthContext } from "./provider/authProvider"; import useWindowSize from "./hooks/useWindow"; +import Uciapi from "./pages/monitoring/uci-api"; +import Inbound from "./pages/monitoring/inbound"; +import Orchestrator from "./pages/monitoring/orchestrator"; +import BroadcastTransformer from "./pages/monitoring/broadcast-transformer"; +import Outbound from "./pages/monitoring/outbound"; +import Transformer from "./pages/monitoring/transformer"; +import UCIAPIlogs from "./pages/monitoring/logs/uci-api"; +import InboundLogs from "./pages/monitoring/logs/inbound"; +import OrchestratorLogs from "./pages/monitoring/logs/orchestrator"; +import TransformerLogs from "./pages/monitoring/logs/transformer"; +import BroadcastTransformerLogs from "./pages/monitoring/logs/broadcast-transformer"; +import OutboundLogs from "./pages/monitoring/logs/outbound"; function App() { const [isLoading, setIsLoading] = useState(false); @@ -57,20 +66,21 @@ function App() { const user = useMemo(() => store.user, [store.user]); return ( -
+
<> {user && ( + {" "} )} - + {user ? ( } /> @@ -101,6 +111,126 @@ function App() { } /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> @@ -133,4 +263,4 @@ function App() { ); } -export default App; +export default App; \ No newline at end of file diff --git a/src/api/downloadErrLog.ts b/src/api/downloadErrLog.ts new file mode 100644 index 0000000..a5edc06 --- /dev/null +++ b/src/api/downloadErrLog.ts @@ -0,0 +1,17 @@ +import axios from "axios"; +import { downloadErrLog } from "./urls"; +import { getDefaultHeaders } from "./utils"; + +export const downloadErrLogData = ( + service_name: string, + date:string +) => { + const url = downloadErrLog(service_name,date); + const config = { + headers: { + ...getDefaultHeaders() + }, + }; + + return axios.get(url, config); +}; diff --git a/src/api/downloadLog.ts b/src/api/downloadLog.ts new file mode 100644 index 0000000..0e23401 --- /dev/null +++ b/src/api/downloadLog.ts @@ -0,0 +1,17 @@ +import axios from "axios"; +import { downloadLog } from "./urls"; +import { getDefaultHeaders } from "./utils"; + +export const downloadLogData = ( + service_name: string, + date:string +) => { + const url = downloadLog(service_name,date); + const config = { + headers: { + ...getDefaultHeaders() + }, + }; + + return axios.get(url, config); +}; diff --git a/src/api/fetchOverview.ts b/src/api/fetchOverview.ts new file mode 100644 index 0000000..7b36a05 --- /dev/null +++ b/src/api/fetchOverview.ts @@ -0,0 +1,16 @@ +import axios from "axios"; +import { fetchOverview } from "./urls"; +import { getDefaultHeaders } from "./utils"; + +export const fetchOverviewData = ( + file: string +) => { + const url = fetchOverview(file); + const config = { + headers: { + ...getDefaultHeaders() + }, + }; + + return axios.get(url, config); +}; diff --git a/src/api/fetchRealtimeData.ts b/src/api/fetchRealtimeData.ts new file mode 100644 index 0000000..1b40e90 --- /dev/null +++ b/src/api/fetchRealtimeData.ts @@ -0,0 +1,16 @@ +import axios from "axios"; +import { fetchRealtimeData } from "./urls"; +import { getDefaultHeaders } from "./utils"; + +export const fetchRealtime = ( + file: string +) => { + const url = fetchRealtimeData(file); + const config = { + headers: { + ...getDefaultHeaders() + }, + }; + + return axios.get(url, config); +}; diff --git a/src/api/fetchService.ts b/src/api/fetchService.ts new file mode 100644 index 0000000..08b025e --- /dev/null +++ b/src/api/fetchService.ts @@ -0,0 +1,18 @@ +import axios from "axios"; +import { fetchService } from "./urls"; +import { getDefaultHeaders } from "./utils"; + +export const fetchServiceData = ( + service_name: string, + lines: number, + date: string +) => { + const url = fetchService(service_name, lines, date); + const config = { + headers: { + ...getDefaultHeaders() + }, + }; + + return axios.get(url, config); +}; diff --git a/src/api/getFiles.ts b/src/api/getFiles.ts new file mode 100644 index 0000000..3c6dcff --- /dev/null +++ b/src/api/getFiles.ts @@ -0,0 +1,12 @@ +import axios from "axios"; +import { getFiles } from "./urls"; +import { getDefaultHeaders } from "./utils"; + +export const getFilesData = () => { + const config = { + headers: { + ...getDefaultHeaders(), + }, + }; + return axios.get(getFiles, config); +}; diff --git a/src/api/stopRealtimeData.ts b/src/api/stopRealtimeData.ts new file mode 100644 index 0000000..a90972d --- /dev/null +++ b/src/api/stopRealtimeData.ts @@ -0,0 +1,13 @@ +import axios from "axios"; +import { stopRealtimeData } from "./urls"; +import { getDefaultHeaders } from "./utils"; + +export const stopRealtime = () => { + const config = { + headers: { + ...getDefaultHeaders(), + }, + }; + + return axios.post(stopRealtimeData,'', config); +}; diff --git a/src/api/triggerRealtime.ts b/src/api/triggerRealtime.ts new file mode 100644 index 0000000..00f8e82 --- /dev/null +++ b/src/api/triggerRealtime.ts @@ -0,0 +1,12 @@ +import axios from "axios"; +import { triggerRealtimeData } from "./urls"; +import { getDefaultHeaders } from "./utils"; + +export const triggerRealtimeDataRes = () => { + const config = { + headers: { + ...getDefaultHeaders(), + }, + }; + return axios.post(triggerRealtimeData, "", config); +}; diff --git a/src/api/urls.ts b/src/api/urls.ts index dd0125d..051a98c 100644 --- a/src/api/urls.ts +++ b/src/api/urls.ts @@ -20,4 +20,20 @@ export const addLogicUrl = `${process.env.REACT_APP_UCI_BASE_URL}/admin/conversa export const getBotByIdUrl =(id:string)=> `${process.env.REACT_APP_UCI_BASE_URL}/admin/bot/${id}`; +export const fetchService =(service_name:string,lines:number,date:string)=> `${process.env.REACT_APP_UCI_BASE_URL}/admin/monitoring/logs/${service_name}?lines=${lines}&date=${date}`; + +export const triggerRealtimeData =`${process.env.REACT_APP_UCI_BASE_URL}/admin/monitoring/realtime/start`; + +export const fetchRealtimeData =(file:string)=>`${process.env.REACT_APP_UCI_BASE_URL}/admin/monitoring/realtime?date=${file}`; + +export const stopRealtimeData =`${process.env.REACT_APP_UCI_BASE_URL}/admin/monitoring/realtime/stop`; + +export const getFiles =`${process.env.REACT_APP_UCI_BASE_URL}/admin/monitoring/realtime/available`; + +export const fetchOverview =(file:string)=>`${process.env.REACT_APP_UCI_BASE_URL}/admin/monitoring/overview?date=${file}`; + +export const downloadLog =(service_name:string,date:string)=>`${process.env.REACT_APP_UCI_BASE_URL}/admin/monitoring/logs/${service_name}/download/log?date=${date}`; + +export const downloadErrLog =(service_name:string,date:string)=>`${process.env.REACT_APP_UCI_BASE_URL}/admin/monitoring/logs/${service_name}/download/error?date=${date}`; + export const getSegmentCountUrl = (segment:string | number) => `${process.env.REACT_APP_nl_url}/segments/${segment}/mentors/count` \ No newline at end of file diff --git a/src/components/icons/Dashboard.tsx b/src/components/icons/Dashboard.tsx index 43d4064..5e6483a 100644 --- a/src/components/icons/Dashboard.tsx +++ b/src/components/icons/Dashboard.tsx @@ -1,10 +1,10 @@ import React from 'react' //@ts-ignore import dashboard from '../../assets/images/dashboard.png'; -const DashobardIcon = () => { +const DashboardIcon = () => { return ( dashboard ) } -export default DashobardIcon \ No newline at end of file +export default DashboardIcon \ No newline at end of file diff --git a/src/components/sidebar/Badge.tsx b/src/components/sidebar/Badge.tsx index 4ed04db..b31266c 100644 --- a/src/components/sidebar/Badge.tsx +++ b/src/components/sidebar/Badge.tsx @@ -8,7 +8,7 @@ interface BadgeProps extends React.HTMLAttributes { } const StyledBadge = styled.div` - min-width: 18px; + min-width: 28px; min-height: 18px; display: flex; align-items: center; diff --git a/src/components/sidebar/Menu.tsx b/src/components/sidebar/Menu.tsx new file mode 100644 index 0000000..8e9040b --- /dev/null +++ b/src/components/sidebar/Menu.tsx @@ -0,0 +1,73 @@ +export const monitoringItemsConfig = [ + { + label: "Overview", + link: "/monitoring/overview", + }, + { + label: "Kafka Topics", + link: "/monitoring/kafka-topics", + }, + { + label: "UCI-API", + link: "/monitoring/uci-api", + }, + { + label: "Inbound", + link: "/monitoring/inbound", + }, + { + label: "Orchestrator", + link: "/monitoring/orchestrator", + }, + { + label: "Transformer", + link: "/monitoring/transformer", + }, + { + label: "Broadcast-Transformer", + link: "/monitoring/broadcast-transformer", + }, + { + label: "Outbound", + link: "/monitoring/outbound", + }, + { + label: "Transport-Socket", + link: "/monitoring/transport-socket", + } +]; + +export const logsItemsConfig = [ + { + label: "Kafka Topics", + link: "/monitoring/logs/kafka-topics", + }, + { + label: "UCI-API", + link: "/monitoring/logs/uci-api", + }, + { + label: "Inbound", + link: "/monitoring/logs/inbound", + }, + { + label: "Orchestrator", + link: "/monitoring/logs/orchestrator", + }, + { + label: "Transformer", + link: "/monitoring/logs/transformer", + }, + { + label: "Broadcast-Transformer", + link: "/monitoring/logs/broadcast-transformer", + }, + { + label: "Outbound", + link: "/monitoring/logs/outbound", + }, + { + label: "Transport-Socket", + link: "/monitoring/logs/transport-socket", + } +]; diff --git a/src/components/sidebar/SidebarHeader.tsx b/src/components/sidebar/SidebarHeader.tsx index 5d191d0..6350df1 100644 --- a/src/components/sidebar/SidebarHeader.tsx +++ b/src/components/sidebar/SidebarHeader.tsx @@ -1,13 +1,13 @@ import styled from "@emotion/styled"; import React from "react"; import { Typography } from "./Typography"; -import DehazeOutlinedIcon from '@mui/icons-material/DehazeOutlined'; -import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'; +import { MDBIcon } from "mdb-react-ui-kit"; + interface SidebarHeaderProps extends React.HTMLAttributes { children?: React.ReactNode; rtl: boolean; - collapsed:any; - handleCollapse:any + collapsed: boolean; + handleCollapse: Function; } const StyledSidebarHeader = styled.div` @@ -68,15 +68,27 @@ export const SidebarHeader: React.FC = ({
- {!collapsed?:} + {!collapsed ? ( + + ) : ( + + )}
- - + UCI Admin Dashboard -
); diff --git a/src/components/sidebar/index.tsx b/src/components/sidebar/index.tsx index 1d683b5..47df9e5 100644 --- a/src/components/sidebar/index.tsx +++ b/src/components/sidebar/index.tsx @@ -7,19 +7,20 @@ import { SubMenu, } from "react-pro-sidebar"; import { Link, useNavigate } from "react-router-dom"; -import React, { useCallback } from "react"; +import React, { useCallback, useState } from "react"; import { Switch } from "./Switch"; // import { PackageBadges } from './PackageBadges'; // import { Typography } from './Typography'; import { SidebarFooter } from "./SidebarFooter"; import { SidebarHeader } from "./SidebarHeader"; -import DashboardIcon from "../icons/Dashboard"; +import DashobardIcon from "../icons/Dashboard"; import AddIcon from "../icons/AddIcon"; import LogoutIcon from "../icons/LogoutIcon"; import ThemeIcon from "../icons/ThemeIcon"; import { useStore } from "../../store"; -import InsightsIcon from "@mui/icons-material/Insights"; -import CodeIcon from "@mui/icons-material/Code"; +import { MDBIcon } from "mdb-react-ui-kit"; +import { logsItemsConfig, monitoringItemsConfig } from "./Menu"; +import "./style.css"; interface SidebarProps extends React.HTMLAttributes { children?: React.ReactNode; @@ -48,6 +49,17 @@ const themes = { color: "#9fb6cf", }, }, + submenu: { + menuContent: "#000", + icon: "#0098e5", + hover: { + backgroundColor: "#c5e4ff", + color: "#44596e", + }, + disabled: { + color: "#9fb6cf", + }, + }, }, dark: { sidebar: { @@ -85,11 +97,11 @@ export const SidebarComponent: React.FC = ({ }) => { const store: any = useStore(); // const [collapsed, setCollapsed] = React.useState(false); - const [toggled, setToggled] = React.useState(false); - const [broken, setBroken] = React.useState(false); - const [rtl, setRtl] = React.useState(false); - const [hasImage, setHasImage] = React.useState(true); - const [theme, setTheme] = React.useState(store?.theme); + const [toggled, setToggled] = useState(false); + const [broken, setBroken] = useState(false); + const [rtl, setRtl] = useState(false); + const [hasImage, setHasImage] = useState(true); + const [theme, setTheme] = useState(store?.theme); // handle on RTL change event const handleRTLChange = (e: React.ChangeEvent) => { @@ -123,14 +135,15 @@ export const SidebarComponent: React.FC = ({ SubMenuExpandIcon: { color: "#b6b7b9", }, - subMenuContent: ({ level }) => ({ + subMenuContent: ({ level, active }) => ({ backgroundColor: level === 0 ? hexToRgba( - themes[theme].menu.menuContent, - hasImage && !collapsed ? 0.4 : 1 + themes[theme].sidebar.backgroundColor, + hasImage && !collapsed ? 0.8 : 1 ) - : "transparent", + : "#0b2948", + ...(active && { backgroundColor: "black" }), }), button: { [`&.${menuClasses.disabled}`]: { @@ -143,6 +156,13 @@ export const SidebarComponent: React.FC = ({ ), color: themes[theme].menu.hover.color, }, + "&:active": { + backgroundColor: "#fff", + }, + // [`&.active`]: { + // backgroundColor: "#fff", + // color: "#b6c8d9", + // }, }, label: ({ open }) => ({ fontWeight: open ? 600 : undefined, @@ -214,7 +234,7 @@ export const SidebarComponent: React.FC = ({ label="Charts" icon={} > - } component={}> + } component={}> Dashboard } component={}>Add Bot @@ -242,76 +262,40 @@ export const SidebarComponent: React.FC = ({ label="Dark theme" /> - } component={}> + } + active + component={} + > Dashboard - } component={}> + } + component={} + > Add Bot - }> - }> - Overview - - }> - Kafka Topics - - }> - UCI-API - - }> - Inbound - - }> - Orchestrator - - }> - Transformer - - } - > - Broadcast-Transformer - - }> - Outbound - - } - > - Transport-Socket - - }> - }> - UCI-API - - }> - Inbound - - } - > - Orchestrator - - } - > - Transformer - - - } - > - Broadcast-Transformer - - }> - Outbound - + } + > + {monitoringItemsConfig.map((item) => ( } + key={item.label} + component={} > - Transport-Socket + {item.label} + ))} + }> + {logsItemsConfig.map((item) => ( + } + > + {item.label} + + ))} diff --git a/src/components/sidebar/style.css b/src/components/sidebar/style.css new file mode 100644 index 0000000..8dd74d7 --- /dev/null +++ b/src/components/sidebar/style.css @@ -0,0 +1,15 @@ +/* This is for menu item */ +.pro-menu-item a.active { + color: aliceblue !important; /* put any color you want */ + font-weight: 500; +} + +/* This is for submenu item */ +.nav-member .react-slidedown .pro-menu-item a.active { + color: #003642; /* put any color you want */ + font-weight: 500; +} + +.ps-menu-button a:active { + background-color: aliceblue !important; +} diff --git a/src/components/visualisation/bar.tsx b/src/components/visualisation/bar.tsx index 636ed39..27797db 100644 --- a/src/components/visualisation/bar.tsx +++ b/src/components/visualisation/bar.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useRef } from "react"; -// import Chart from "chart.js"; +import { useEffect, useRef } from "react"; import Chart from 'chart.js/auto'; -export const BarChart = ({ data }) => { +const BarChart = ({ data}) => { const chartRef = useRef(null); useEffect(() => { @@ -33,3 +32,4 @@ export const BarChart = ({ data }) => { return ; }; +export default BarChart; diff --git a/src/components/visualisation/index.tsx b/src/components/visualisation/index.tsx index 3e07c2a..b40ea06 100644 --- a/src/components/visualisation/index.tsx +++ b/src/components/visualisation/index.tsx @@ -1 +1,3 @@ -export * from './bar' \ No newline at end of file +export * from './bar' +export * from './line' +export * from './pie' \ No newline at end of file diff --git a/src/components/visualisation/line.tsx b/src/components/visualisation/line.tsx new file mode 100644 index 0000000..f8b0d2a --- /dev/null +++ b/src/components/visualisation/line.tsx @@ -0,0 +1,35 @@ +import { useEffect, useRef } from "react"; +import Chart from 'chart.js/auto'; + +const LineChart = ({ data }) => { + const chartRef = useRef(null); + + useEffect(() => { + const ctx = chartRef.current.getContext("2d"); + const chart = new Chart(ctx, { + type: "line", + data: data, + options: { + responsive: true, + scales: { + x: { + grid: { + display: false, + }, + }, + y: { + beginAtZero: true + }, + }, + }, + }); + + return () => { + chart.destroy(); + }; + }, [data]); + + return ; +}; + +export default LineChart; diff --git a/src/components/visualisation/pie.tsx b/src/components/visualisation/pie.tsx new file mode 100644 index 0000000..4ebb394 --- /dev/null +++ b/src/components/visualisation/pie.tsx @@ -0,0 +1,70 @@ +import React, { useEffect, useRef } from "react"; +import Chart from "chart.js/auto"; +import useWindowSize from "../../hooks/useWindow"; + +const generateRandomColor = () => { + const letters = "0123456789ABCDEF"; + let color = "#"; + for (let i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; +}; + +const PieChart = ({ data }) => { + const chartRef = useRef(null); + const { height } = useWindowSize(); + + useEffect(() => { + const dynamicColors = data?.labels?.map(() => generateRandomColor()); + + const ctx = chartRef.current.getContext("2d"); + const chart = new Chart(ctx, { + type: "pie", + data: { + labels: data?.labels, + datasets: [ + { + data: data?.datasets[0].data, + backgroundColor: dynamicColors, + }, + ], + }, + options: { + responsive: true, + plugins: { + tooltip: { + callbacks: { + label: (context) => { + const label = data?.labels[context.dataIndex]; + const value = data?.datasets[0].data[context.dataIndex]; + const total = data?.datasets[0].data.reduce((acc, curr) => acc + curr); + const percentage = ((value / total) * 100).toFixed(2); + return `${label}: ${value} (${percentage}%)`; + }, + }, + }, + }, + }, + }); + + return () => { + chart.destroy(); + }; + }, [data]); + + const chartContainerStyle = { + display: "flex", + justifyContent: "center", + alignItems: "center", + maxHeight: `${height - 100}px`, + }; + + return ( +
+ +
+ ); +}; + +export default PieChart; diff --git a/src/hooks/useWindow.ts b/src/hooks/useWindow.ts index 5d79395..f3fd8fa 100644 --- a/src/hooks/useWindow.ts +++ b/src/hooks/useWindow.ts @@ -16,7 +16,6 @@ const useWindowSize = () => { window.addEventListener("resize", handleResize); - // Cleanup the event listener on component unmount return () => { window.removeEventListener("resize", handleResize); }; diff --git a/src/index.css b/src/index.css index a67c038..c61aa09 100644 --- a/src/index.css +++ b/src/index.css @@ -25,6 +25,5 @@ code { .form-outline .form-control { background-color: white !important; } -.form-label { -} \ No newline at end of file + diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index a3e57a0..8ab3c43 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -30,6 +30,7 @@ export const Dashboard = () => { .then((res) => { store?.stopLoading(); setBotList(res?.data?.result?.data); + console.log(botList); setTotalRecords(res?.data?.result?.totalCount || 0); if(searchText.length >0 ){ setPage(1); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index edaba61..e116b75 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -3,4 +3,9 @@ export * from './dashboard'; export * from './login'; export * from './success'; export * from './monitoring/overview'; -export * from './monitoring/kafka'; \ No newline at end of file +export * from './monitoring/inbound'; +export * from './monitoring/broadcast-transformer'; +export * from './monitoring/orchestrator'; +export * from './monitoring/outbound'; +export * from './monitoring/transformer'; +export * from './monitoring/uci-api'; \ No newline at end of file diff --git a/src/pages/login/index.css b/src/pages/login/index.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/pages/monitoring/broadcast-transformer/index.tsx b/src/pages/monitoring/broadcast-transformer/index.tsx new file mode 100644 index 0000000..6e04a46 --- /dev/null +++ b/src/pages/monitoring/broadcast-transformer/index.tsx @@ -0,0 +1,319 @@ +import { + MDBBtn, + MDBCol, + MDBDropdown, + MDBDropdownItem, + MDBDropdownMenu, + MDBDropdownToggle, + MDBRow, +} from "mdb-react-ui-kit"; +import BarChart from "../../../components/visualisation/bar"; +import React, { useEffect, useState } from "react"; +import { useStore } from "../../../store"; +import { getBots } from "../../../api/getBots"; +import { toast } from "react-hot-toast"; +import "./style.css"; +import PieChart from "../../../components/visualisation/pie"; +import LineChart from "../../../components/visualisation/line"; +// import { formatDate, reverseFormatDate } from "../../../utils/functions"; +import { fetchRealtime } from "../../../api/fetchRealtimeData"; +import { getFilesData } from "../../../api/getFiles"; +import { convertToShortDate, formatDate, reverseFormatDate } from "../../../utils/functions"; + +interface AutocompleteItem { + value: string; +} + +interface BroadcastTransformerProps extends React.HTMLAttributes { + theme: string; +} + +export const BroadcastTransformer: React.FC = ({ theme }) => { + const [selectedChart, setSelectedChart] = useState("barchart"); + const [dropdownLabel, setDropdownLabel] = useState("Select Chart"); + const [searchText, setSearchText] = useState(""); + // const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + // AutocompleteItem[] + // >([]); + const [autocompleteSuggestionsFile, setAutocompleteSuggestionsFile] = + useState([]); + const [botList, setBotList] = useState([]); + const store: any = useStore(); + + const [selected, setSelected] = useState(""); + const [dropdown, setDropdown] = useState(true); + + const [BroadcastTransformerData, setBroadcastTransformerData] = useState([]); + + const [FileName, setFileName] = useState(""); + + const func = async () => { + if (localStorage.getItem("file")) { + const file = localStorage.getItem("file"); + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setBroadcastTransformerData(res["Broadcast Transformer".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } else if (FileName !== "") { + const file = reverseFormatDate(FileName); + localStorage.setItem("file", file); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setBroadcastTransformerData(res["Broadcast Transformer".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } + fetchBotData(); + }; + + useEffect(() => { + func(); + // fetchBotData(); + }, [FileName]); + + const dataBar = { + labels: Object.keys(BroadcastTransformerData), + datasets: [ + { + label: "Broadcast Transformer", + data: Object.values(BroadcastTransformerData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const dataPie = { + labels: Object.keys(BroadcastTransformerData), + datasets: [ + { + label: "Broadcast Transformer", + data: Object.values(BroadcastTransformerData).map(Number), + }, + ], + }; + + const dataLine = { + labels: Object.keys(BroadcastTransformerData), + datasets: [ + { + label: "Broadcast Transformer", + data: Object.values(BroadcastTransformerData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const handleChartChange = (value, label) => { + setSelectedChart(value); + setDropdownLabel(label); + }; + + let chartComponent; + if (selectedChart === "barchart") { + chartComponent = ; + } else if (selectedChart === "piechart") { + // chartComponent = ; + chartComponent = ; + } else if (selectedChart === "linechart") { + chartComponent = ; + } + + const fetchBotData = async () => { + store?.startLoading(); + const data = searchText.length > 0 ? { name: searchText } : {}; + try { + const res = await getBots(data); + store?.stopLoading(); + setBotList(res?.data?.result?.data.map((bot) => bot.name)); + } catch (err) { + store?.stopLoading(); + toast.error(err.message); + } + }; + + // const handleSearchInputChange = ( + // event: React.ChangeEvent + // ) => { + // const { value } = event.target; + // setSearchText(value); + + // const lowercasedValue = value.toLowerCase(); + // const suggestions: AutocompleteItem[] = botList.map((name) => ({ + // value: name, + // })); + + // setAutocompleteSuggestions( + // suggestions.filter((item) => + // item.value.toLowerCase().includes(lowercasedValue) + // ) + // ); + // }; + + // const handleSearchSubmit = (event: React.FormEvent) => { + // event.preventDefault(); + // }; + + const handleSearchSubmitFile = async ( + event: React.FormEvent + ) => { + event.preventDefault(); + setDropdown(false); + setFileName(selected); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const file = reverseFormatDate(selected); + localStorage.setItem("file", file); + + func(); + }; + + const handleSearchInputChangeFile = async ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSelected(value); + + const lowercasedValue = value.toLowerCase(); + const apiEndpoint = getFilesData(); + let data = []; + + try { + const response = await apiEndpoint; + data = response.data.result; + } catch (error) { + console.error("Error toggling:", error); + } + + const suggestions: AutocompleteItem[] = data.map((name) => ({ + value: formatDate(name), + })); + + setAutocompleteSuggestionsFile( + suggestions.filter((item) => + item.value.toLowerCase().includes(lowercasedValue) + ) + ); + setDropdown(true); + }; + + const handleAutocompleteItemClick = (value) => { + setSelected(value); + setDropdown(false); + }; + + return ( +
+ + + + + {dropdownLabel} + + + handleChartChange("barchart", "Bar Chart")} + style={{ cursor: "pointer" }} + > + Bar Chart + + handleChartChange("piechart", "Pie Chart")} + style={{ cursor: "pointer" }} + > + Pie Chart + + handleChartChange("linechart", "Line Chart")} + style={{ cursor: "pointer" }} + > + Line Chart + + + + + {/* +
+ + + Search + +
+ {autocompleteSuggestions.length > 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • setSearchText(item.value)}> + {item.value} +
  • + ))} +
+ )} +
*/} + +
+ + + Search + +
+ {dropdown && autocompleteSuggestionsFile.length > 0 && ( +
    + {autocompleteSuggestionsFile.map((item) => ( +
  • handleAutocompleteItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+
+ {chartComponent} +
+ ); +}; + +export default BroadcastTransformer; diff --git a/src/pages/monitoring/broadcast-transformer/style.css b/src/pages/monitoring/broadcast-transformer/style.css new file mode 100644 index 0000000..dc6b6c1 --- /dev/null +++ b/src/pages/monitoring/broadcast-transformer/style.css @@ -0,0 +1,25 @@ +/* Add a higher z-index to the autocomplete dropdown */ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + max-width: inherit; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.dark-dropdown { + color: black ; +} \ No newline at end of file diff --git a/src/pages/monitoring/dummy.json b/src/pages/monitoring/dummy.json deleted file mode 100644 index 9b4ad95..0000000 --- a/src/pages/monitoring/dummy.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "Kafka Topics": { - "broadcast-transformer": "7", - "com.odk.transformer": "17", - "generic-transformer": "0", - "notification-outbound": "35", - "outbound": "17", - "inbound-processed": "24", - "process-outbound": "52", - "telemetry": "14", - "outbound-processed": "0" - }, - "UCI API": { - "ResolveUser": 1 - }, - "Inbound": { - "Data inserted in Cassandra": 1, - "Kafka topic push": 1 - }, - "Orchestrator": { - "Topic consumed from Kafka": 5, - "Notification pushed to Kafka": 5 - }, - "Broadcast Transformer": { - "Broadcast Processed": 5, - "Kafka Push to outbound": 5 - }, - "Outbound": { - "Saved data in cache": 5, - "Notification inserted in Cassandra": 5 - } - } - \ No newline at end of file diff --git a/src/pages/monitoring/inbound/index.tsx b/src/pages/monitoring/inbound/index.tsx new file mode 100644 index 0000000..de82c96 --- /dev/null +++ b/src/pages/monitoring/inbound/index.tsx @@ -0,0 +1,318 @@ +import { + MDBBtn, + MDBCol, + MDBDropdown, + MDBDropdownItem, + MDBDropdownMenu, + MDBDropdownToggle, + MDBRow, +} from "mdb-react-ui-kit"; +import BarChart from "../../../components/visualisation/bar"; +import React, { useEffect, useState } from "react"; +import { useStore } from "../../../store"; +import { getBots } from "../../../api/getBots"; +import { toast } from "react-hot-toast"; +import "./style.css"; +import PieChart from "../../../components/visualisation/pie"; +import LineChart from "../../../components/visualisation/line"; +// import { formatDate, reverseFormatDate } from "../../../utils/functions"; +import { fetchRealtime } from "../../../api/fetchRealtimeData"; +import { getFilesData } from "../../../api/getFiles"; +import { convertToShortDate, formatDate, reverseFormatDate } from "../../../utils/functions"; + +interface AutocompleteItem { + value: string; +} + +interface InboundProps extends React.HTMLAttributes { + theme: string; +} + +export const Inbound: React.FC = ({ theme }) => { + const [selectedChart, setSelectedChart] = useState("barchart"); + const [dropdownLabel, setDropdownLabel] = useState("Select Chart"); + const [searchText, setSearchText] = useState(""); + // const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + // AutocompleteItem[] + // >([]); + const [autocompleteSuggestionsFile, setAutocompleteSuggestionsFile] = + useState([]); + const [botList, setBotList] = useState([]); + const store: any = useStore(); + + const [selected, setSelected] = useState(""); + const [dropdown, setDropdown] = useState(true); + + const [InboundData, setInboundData] = useState([]); + + const [FileName, setFileName] = useState(""); + + const func = async () => { + if (localStorage.getItem("file")) { + const file = localStorage.getItem("file"); + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setInboundData(res["Inbound".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } else if (FileName !== "") { + const file = reverseFormatDate(FileName); + localStorage.setItem("file", file); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setInboundData(res["Inbound".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } + fetchBotData(); + }; + + useEffect(() => { + func(); + // fetchBotData(); + }, [FileName]); + + const dataBar = { + labels: Object.keys(InboundData), + datasets: [ + { + label: "Inbound", + data: Object.values(InboundData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const dataPie = { + labels: Object.keys(InboundData), + datasets: [ + { + label: "Inbound", + data: Object.values(InboundData).map(Number), + }, + ], + }; + + const dataLine = { + labels: Object.keys(InboundData), + datasets: [ + { + label: "Inbound", + data: Object.values(InboundData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const handleChartChange = (value, label) => { + setSelectedChart(value); + setDropdownLabel(label); + }; + + let chartComponent; + if (selectedChart === "barchart") { + chartComponent = ; + } else if (selectedChart === "piechart") { + // chartComponent = ; + chartComponent = ; + } else if (selectedChart === "linechart") { + chartComponent = ; + } + + const fetchBotData = async () => { + store?.startLoading(); + const data = searchText.length > 0 ? { name: searchText } : {}; + try { + const res = await getBots(data); + store?.stopLoading(); + setBotList(res?.data?.result?.data.map((bot) => bot.name)); + } catch (err) { + store?.stopLoading(); + toast.error(err.message); + } + }; + + // const handleSearchInputChange = ( + // event: React.ChangeEvent + // ) => { + // const { value } = event.target; + // setSearchText(value); + + // const lowercasedValue = value.toLowerCase(); + // const suggestions: AutocompleteItem[] = botList.map((name) => ({ + // value: name, + // })); + + // setAutocompleteSuggestions( + // suggestions.filter((item) => + // item.value.toLowerCase().includes(lowercasedValue) + // ) + // ); + // }; + + // const handleSearchSubmit = (event: React.FormEvent) => { + // event.preventDefault(); + // }; + + const handleSearchSubmitFile = async ( + event: React.FormEvent + ) => { + event.preventDefault(); + setFileName(selected); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const file = reverseFormatDate(selected); + localStorage.setItem("file", file); + + func(); + }; + + const handleSearchInputChangeFile = async ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSelected(value); + + const lowercasedValue = value.toLowerCase(); + const apiEndpoint = getFilesData(); + let data = []; + + try { + const response = await apiEndpoint; + data = response.data.result; + } catch (error) { + console.error("Error toggling:", error); + } + + const suggestions: AutocompleteItem[] = data.map((name) => ({ + value: formatDate(name), + })); + + setAutocompleteSuggestionsFile( + suggestions.filter((item) => + item.value.toLowerCase().includes(lowercasedValue) + ) + ); + setDropdown(true); + }; + + const handleAutocompleteItemClick = (value) => { + setSelected(value); + setDropdown(false); + }; + + return ( +
+ + + + + {dropdownLabel} + + + handleChartChange("barchart", "Bar Chart")} + style={{ cursor: "pointer" }} + > + Bar Chart + + handleChartChange("piechart", "Pie Chart")} + style={{ cursor: "pointer" }} + > + Pie Chart + + handleChartChange("linechart", "Line Chart")} + style={{ cursor: "pointer" }} + > + Line Chart + + + + + {/* +
+ + + Search + +
+ {autocompleteSuggestions.length > 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • setSearchText(item.value)}> + {item.value} +
  • + ))} +
+ )} +
*/} + +
+ + + Search + +
+ {dropdown && autocompleteSuggestionsFile.length > 0 && ( +
    + {autocompleteSuggestionsFile.map((item) => ( +
  • handleAutocompleteItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+
+ {chartComponent} +
+ ); +}; + +export default Inbound; diff --git a/src/pages/monitoring/inbound/style.css b/src/pages/monitoring/inbound/style.css new file mode 100644 index 0000000..dc6b6c1 --- /dev/null +++ b/src/pages/monitoring/inbound/style.css @@ -0,0 +1,25 @@ +/* Add a higher z-index to the autocomplete dropdown */ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + max-width: inherit; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.dark-dropdown { + color: black ; +} \ No newline at end of file diff --git a/src/pages/monitoring/kafka/index.tsx b/src/pages/monitoring/kafka/index.tsx deleted file mode 100644 index 557c9e7..0000000 --- a/src/pages/monitoring/kafka/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import {BarChart} from "../../../components"; -import jsonData from '../dummy.json'; - -const kafkaTopicsData = jsonData["Kafka Topics"]; - -const data = { - labels: Object.keys(kafkaTopicsData), - datasets: [ - { - label: "Kafka Topics", - data: Object.values(kafkaTopicsData).map(Number), - backgroundColor: "rgba(75, 192, 192, 0.6)", - }, - ], -}; - -const Kafka = () => { - return ; -}; - -export default Kafka; \ No newline at end of file diff --git a/src/pages/monitoring/logs/broadcast-transformer/index.tsx b/src/pages/monitoring/logs/broadcast-transformer/index.tsx new file mode 100644 index 0000000..368c703 --- /dev/null +++ b/src/pages/monitoring/logs/broadcast-transformer/index.tsx @@ -0,0 +1,400 @@ +import React, { useEffect, useState } from "react"; +import { + MDBBtn, + MDBBtnGroup, + MDBCol, + MDBContainer, + MDBRow, +} from "mdb-react-ui-kit"; +import "./style.css"; +import Ansi from "ansi-to-react"; +import { useStore } from "../../../../store"; +import toast from "react-hot-toast"; +import { getFilesData } from "../../../../api/getFiles"; +import { downloadLogData } from "../../../../api/downloadLog"; +import { downloadErrLogData } from "../../../../api/downloadErrLog"; +import { fetchServiceData } from "../../../../api/fetchService"; +import { MDBIcon } from "mdb-react-ui-kit"; +import DateTimePicker from "react-datetime-picker"; +import "react-datetime-picker/dist/DateTimePicker.css"; +import "react-calendar/dist/Calendar.css"; +import "react-clock/dist/Clock.css"; + +interface DropdownItem { + value: number; +} + +interface BroadcastTransformerLogsProps + extends React.HTMLAttributes { + theme: string; +} + +export const BroadcastTransformerLogs: React.FC< + BroadcastTransformerLogsProps +> = ({ theme }) => { + const store: any = useStore(); + const [searchText, setSearchText] = useState(0); + const [filterText, setFilterText] = useState(""); + const [filter, setFilter] = useState(false); + const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + DropdownItem[] + >([]); + const [showDropdown, setShowDropdown] = useState(false); + const [messageLines, setMessageLines] = useState([]); + const [searchPerformed, setSearchPerformed] = useState(false); + const [activeButton, setActiveButton] = useState("normal"); + const [flag, setFlag] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const maxLines = process.env.REACT_APP_MAX_LINES + ? parseInt(process.env.REACT_APP_MAX_LINES) + : 5000; + + const [logdate, setLogDate] = useState(new Date()); + const [date, setDate] = useState(""); + + const [endDate, setEndDate] = useState(new Date()); + const [endDateFormatted, setEndDateFormatted] = useState(""); + + function formatDateTo_dd_mm_yyyy(inputDate) { + const date = new Date(inputDate); + + const day = String(date.getDate()).padStart(2, "0"); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const year = date.getFullYear(); + + return `${day}_${month}_${year}`; + } + + const handleDateChange = (date: Date | null) => { + setLogDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setDate(formattedDate); + } else { + setDate(""); + } + }; + + const handleEndDateChange = (date: Date | null) => { + setEndDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setEndDateFormatted(formattedDate); + } else { + setEndDateFormatted(""); + } + }; + + const fetchMessageData = async () => { + setFlag(false); + setIsLoading(true); + try { + let logData = ""; + let responseType = activeButton === "normal" ? "logs" : "error"; + const response = await fetchServiceData( + "broadcast-transformer", + searchText, + date + ); + logData = response.data.result[responseType]; + const lines = logData.split("\n"); + setMessageLines(lines); + setFlag(true); + } catch (error) { + console.error("Error reading the file:", error); + } + setIsLoading(false); + }; + + const handleSearchInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSearchText(Number(value)); + + const data = [10, 100, 1000, 5000]; + const uniqueData = Array.from(new Set(data)); + + const suggestions: DropdownItem[] = uniqueData.map((val) => ({ + value: val, + })); + + setAutocompleteSuggestions(suggestions.filter((item) => item.value)); + setShowDropdown(true); + }; + + const handleFilterInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setFilterText(value); + setFilter(true); + }; + + const handleSearchSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setSearchPerformed(true); + setShowDropdown(false); + fetchMessageData(); + }; + + const handleFilterSubmit = (event: React.FormEvent) => { + event.preventDefault(); + fetchMessageData(); + }; + + const handleDropdownItemClick = (value: number) => { + setSearchText(value); + setShowDropdown(false); + }; + + const handleClick = (buttonName) => { + setActiveButton(buttonName); + fetchMessageData(); + }; + + const handleDownload = async (activeButton, date) => { + let response; + if (activeButton === "normal") { + response = await downloadLogData("broadcast-transformer", date); + } else { + response = await downloadErrLogData("broadcast-transformer", date); + } + const responseData = response.data; + console.log(responseData); + + const blob = new Blob([responseData], { type: "text/plain" }); + + const url = URL.createObjectURL(blob); + + const tempAnchor = document.createElement("a"); + tempAnchor.href = url; + const serviceName = "broadcast_transformer"; + const filename = `${serviceName}_${date}.txt`; + tempAnchor.download = filename; + document.body.appendChild(tempAnchor); + tempAnchor.click(); + + document.body.removeChild(tempAnchor); + + URL.revokeObjectURL(url); + }; + + useEffect(() => { + store?.startLoading(); + getFilesData() + .then((res) => { + store?.stopLoading(); + }) + .catch((err) => { + store?.stopLoading(); + toast.error(err.message); + }); + + if (activeButton) { + fetchMessageData(); + } + + if (maxLines <= searchText) { + handleDownload(activeButton, date); + } + }, [activeButton, date, maxLines, searchText]); + + return ( + +
+ + + + + + + + + + + + + + +
+ + + Search + +
+ {showDropdown && + autocompleteSuggestions.length > 0 && + searchText !== 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • handleDropdownItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+ +
+ + + Filter + +
+
+
+
+
+ {flag && searchPerformed && ( + + handleClick("normal")} + style={{ + backgroundColor: + activeButton === "normal" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Normal Logs + + handleClick("err")} + style={{ + backgroundColor: activeButton === "err" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Error + + + )} + {flag && searchPerformed && !isLoading && messageLines.length === 0 && ( +

+ No data available for selected date +

+ )} + + {!searchPerformed && ( +

Please select number of lines

+ )} + {searchPerformed && ( + + handleDownload(activeButton, date)} + style={{ + backgroundColor: "#007BFF", + borderColor: "#007BFF", + }} + > + Download + + + )} + {searchPerformed && maxLines > searchText && ( +
+ {searchPerformed && ( +
+ {!filter && + activeButton === "normal" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "normal" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} + {!filter && + activeButton === "err" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "err" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} +
+ )} +
+ )} + {searchPerformed && maxLines <= searchText && ( +
+

+ Too large to display! +

+
+ )} +
+ ); +}; + +export default BroadcastTransformerLogs; diff --git a/src/pages/monitoring/logs/broadcast-transformer/style.css b/src/pages/monitoring/logs/broadcast-transformer/style.css new file mode 100644 index 0000000..499654f --- /dev/null +++ b/src/pages/monitoring/logs/broadcast-transformer/style.css @@ -0,0 +1,59 @@ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + min-width: 10%; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.highlight { + background-color: yellow; +} + +.date-picker-container { + z-index: 9999 !important; + position: relative; +} + +.date-picker { + padding: 5px; + width: 120px; + cursor: pointer; +} + +.light-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: #333; +} + +.dark-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: white; +} + +.dark-date-picker .react-datetime-picker__button svg, +.dark-date-picker .react-datetime-picker__button:after, +.dark-date-picker .react-datetime-picker__button:before { + background-color: white !important; +} diff --git a/src/pages/monitoring/logs/inbound/index.tsx b/src/pages/monitoring/logs/inbound/index.tsx new file mode 100644 index 0000000..d5752ff --- /dev/null +++ b/src/pages/monitoring/logs/inbound/index.tsx @@ -0,0 +1,362 @@ +import React, { useEffect, useState } from "react"; +import { + MDBBtn, + MDBBtnGroup, + MDBCol, + MDBContainer, + MDBRow, +} from "mdb-react-ui-kit"; +import "./style.css"; +import Ansi from "ansi-to-react"; +import { useStore } from "../../../../store"; +import toast from "react-hot-toast"; +import { getFilesData } from "../../../../api/getFiles"; +import { downloadLogData } from "../../../../api/downloadLog"; +import { downloadErrLogData } from "../../../../api/downloadErrLog"; +import { fetchServiceData } from "../../../../api/fetchService"; +import { MDBIcon } from "mdb-react-ui-kit"; +import DateTimePicker from "react-datetime-picker"; +import "react-datetime-picker/dist/DateTimePicker.css"; +import "react-calendar/dist/Calendar.css"; +import "react-clock/dist/Clock.css"; + +interface DropdownItem { + value: number; +} + +interface InboundLogsProps extends React.HTMLAttributes { + theme: string; +} + +export const InboundLogs: React.FC = ({ theme }) => { + const store: any = useStore(); + const [searchText, setSearchText] = useState(0); + const [filterText, setFilterText] = useState(""); + const [filter, setFilter] = useState(false); + const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + DropdownItem[] + >([]); + const [showDropdown, setShowDropdown] = useState(false); + const [messageLines, setMessageLines] = useState([]); + const [searchPerformed, setSearchPerformed] = useState(false); + const [activeButton, setActiveButton] = useState("normal"); + const [flag, setFlag] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const maxLines = process.env.REACT_APP_MAX_LINES + ? parseInt(process.env.REACT_APP_MAX_LINES) + : 5000; + + const [logdate, setLogDate] = useState(new Date()); + const [date, setDate] = useState(""); + + const [endDate, setEndDate] = useState(new Date()); + const [endDateFormatted, setEndDateFormatted] = useState(""); + + function formatDateTo_dd_mm_yyyy(inputDate) { + const date = new Date(inputDate); + + const day = String(date.getDate()).padStart(2, "0"); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const year = date.getFullYear(); + + return `${day}_${month}_${year}`; + } + + const handleDateChange = (date: Date | null) => { + setLogDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setDate(formattedDate); + } else { + setDate(""); + } + }; + + const handleEndDateChange = (date: Date | null) => { + setEndDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setEndDateFormatted(formattedDate); + } else { + setEndDateFormatted(""); + } + }; + + const fetchMessageData = async () => { + setFlag(false); + setIsLoading(true); + try { + let logData = ""; + let responseType = activeButton === "normal" ? "logs" : "error"; + const response = await fetchServiceData("inbound", searchText, date); + logData = response.data.result[responseType]; + const lines = logData.split("\n"); + setMessageLines(lines); + setFlag(true); + } catch (error) { + console.error("Error reading the file:", error); + } + setIsLoading(false); + }; + + + const handleSearchInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSearchText(Number(value)); + + const data = [10, 100, 1000, 5000]; + const uniqueData = Array.from(new Set(data)); + + const suggestions: DropdownItem[] = uniqueData.map((val) => ({ + value: val, + })); + + setAutocompleteSuggestions(suggestions.filter((item) => item.value)); + setShowDropdown(true); + }; + + const handleFilterInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setFilterText(value); + setFilter(true); + }; + + const handleSearchSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setSearchPerformed(true); + setShowDropdown(false); + fetchMessageData(); + }; + + const handleFilterSubmit = (event: React.FormEvent) => { + event.preventDefault(); + fetchMessageData(); + }; + + const handleDropdownItemClick = (value: number) => { + setSearchText(value); + setShowDropdown(false); + }; + + const handleClick = (buttonName) => { + setActiveButton(buttonName); + fetchMessageData(); + }; + + const handleDownload = async (activeButton,date) => { + let response; + if (activeButton === "normal") { + response = await downloadLogData("inbound", date); + } else { + response = await downloadErrLogData("inbound", date); + } + const responseData = response.data; + console.log(responseData); + + const blob = new Blob([responseData], { type: "text/plain" }); + + const url = URL.createObjectURL(blob); + + const tempAnchor = document.createElement("a"); + tempAnchor.href = url; + const serviceName = "inbound"; + const filename = `${serviceName}_${date}.txt`; + tempAnchor.download = filename; + document.body.appendChild(tempAnchor); + tempAnchor.click(); + + document.body.removeChild(tempAnchor); + + URL.revokeObjectURL(url); + }; + + useEffect(() => { + store?.startLoading(); + getFilesData() + .then((res) => { + store?.stopLoading(); + }) + .catch((err) => { + store?.stopLoading(); + toast.error(err.message); + }); + + if (activeButton) { + fetchMessageData(); + } + + if (maxLines <= searchText) { + handleDownload(activeButton, date); + } + }, [activeButton, date,maxLines,searchText]); + + + return ( + +
+ + + + + + + + + + + + + + +
+ + + Search + +
+ {showDropdown && + autocompleteSuggestions.length > 0 && + searchText !== 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • handleDropdownItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+ +
+ + + Filter + +
+
+
+
+
+ {flag && searchPerformed && !isLoading && messageLines.length === 0 && ( +

+ No data available for selected date +

+ )} + + {!searchPerformed && ( +

Please select number of lines

+ )} + {searchPerformed && ( + + handleDownload(activeButton,date)} + style={{ + backgroundColor: "#007BFF", + borderColor: "#007BFF", + }} + > + Download + + + )} + {searchPerformed && maxLines > searchText && ( +
+ {searchPerformed && ( +
+ {!filter && + activeButton === "normal" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "normal" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} + {!filter && + activeButton === "err" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "err" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} +
+ )} +
+ )} + {searchPerformed && maxLines <= searchText && ( +
+

+ Too large to display! +

+
+ )} +
+ ); +}; + +export default InboundLogs; diff --git a/src/pages/monitoring/logs/inbound/style.css b/src/pages/monitoring/logs/inbound/style.css new file mode 100644 index 0000000..499654f --- /dev/null +++ b/src/pages/monitoring/logs/inbound/style.css @@ -0,0 +1,59 @@ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + min-width: 10%; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.highlight { + background-color: yellow; +} + +.date-picker-container { + z-index: 9999 !important; + position: relative; +} + +.date-picker { + padding: 5px; + width: 120px; + cursor: pointer; +} + +.light-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: #333; +} + +.dark-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: white; +} + +.dark-date-picker .react-datetime-picker__button svg, +.dark-date-picker .react-datetime-picker__button:after, +.dark-date-picker .react-datetime-picker__button:before { + background-color: white !important; +} diff --git a/src/pages/monitoring/logs/orchestrator/index.tsx b/src/pages/monitoring/logs/orchestrator/index.tsx new file mode 100644 index 0000000..91612a4 --- /dev/null +++ b/src/pages/monitoring/logs/orchestrator/index.tsx @@ -0,0 +1,396 @@ +import React, { useEffect, useState } from "react"; +import { + MDBBtn, + MDBBtnGroup, + MDBCol, + MDBContainer, + MDBRow, +} from "mdb-react-ui-kit"; +import "./style.css"; +import Ansi from "ansi-to-react"; +import { useStore } from "../../../../store"; +import toast from "react-hot-toast"; +import { getFilesData } from "../../../../api/getFiles"; +import { downloadLogData } from "../../../../api/downloadLog"; +import { downloadErrLogData } from "../../../../api/downloadErrLog"; +import { fetchServiceData } from "../../../../api/fetchService"; +import { MDBIcon } from "mdb-react-ui-kit"; +import DateTimePicker from "react-datetime-picker"; +import "react-datetime-picker/dist/DateTimePicker.css"; +import "react-calendar/dist/Calendar.css"; +import "react-clock/dist/Clock.css"; + +interface DropdownItem { + value: number; +} + +interface OrchestratorLogsProps extends React.HTMLAttributes { + theme: string; +} + +export const OrchestratorLogs: React.FC = ({ + theme, +}) => { + const store: any = useStore(); + const [searchText, setSearchText] = useState(0); + const [filterText, setFilterText] = useState(""); + const [filter, setFilter] = useState(false); + const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + DropdownItem[] + >([]); + const [showDropdown, setShowDropdown] = useState(false); + const [messageLines, setMessageLines] = useState([]); + const [searchPerformed, setSearchPerformed] = useState(false); + const [activeButton, setActiveButton] = useState("normal"); + const [flag, setFlag] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const maxLines = process.env.REACT_APP_MAX_LINES + ? parseInt(process.env.REACT_APP_MAX_LINES) + : 5000; + + const [logdate, setLogDate] = useState(new Date()); + const [date, setDate] = useState(""); + + const [endDate, setEndDate] = useState(new Date()); + const [endDateFormatted, setEndDateFormatted] = useState(""); + + function formatDateTo_dd_mm_yyyy(inputDate) { + const date = new Date(inputDate); + + const day = String(date.getDate()).padStart(2, "0"); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const year = date.getFullYear(); + + return `${day}_${month}_${year}`; + } + + const handleDateChange = (date: Date | null) => { + setLogDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setDate(formattedDate); + } else { + setDate(""); + } + }; + + const handleEndDateChange = (date: Date | null) => { + setEndDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setEndDateFormatted(formattedDate); + } else { + setEndDateFormatted(""); + } + }; + + const fetchMessageData = async () => { + setFlag(false); + setIsLoading(true); + try { + let logData = ""; + let responseType = activeButton === "normal" ? "logs" : "error"; + const response = await fetchServiceData("orchestrator", searchText, date); + logData = response.data.result[responseType]; + const lines = logData.split("\n"); + setMessageLines(lines); + setFlag(true); + } catch (error) { + console.error("Error reading the file:", error); + } + setIsLoading(false); + }; + + const handleSearchInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSearchText(Number(value)); + + const data = [10, 100, 1000, 5000]; + const uniqueData = Array.from(new Set(data)); + + const suggestions: DropdownItem[] = uniqueData.map((val) => ({ + value: val, + })); + + setAutocompleteSuggestions(suggestions.filter((item) => item.value)); + setShowDropdown(true); + }; + + const handleFilterInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setFilterText(value); + setFilter(true); + }; + + const handleSearchSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setSearchPerformed(true); + setShowDropdown(false); + fetchMessageData(); + }; + + const handleFilterSubmit = (event: React.FormEvent) => { + event.preventDefault(); + fetchMessageData(); + }; + + const handleDropdownItemClick = (value: number) => { + setSearchText(value); + setShowDropdown(false); + }; + + const handleClick = (buttonName) => { + setActiveButton(buttonName); + fetchMessageData(); + }; + + const handleDownload = async (activeButton, date) => { + let response; + if (activeButton === "normal") { + response = await downloadLogData("orchestrator", date); + } else { + response = await downloadErrLogData("orchestrator", date); + } + const responseData = response.data; + console.log(responseData); + + const blob = new Blob([responseData], { type: "text/plain" }); + + const url = URL.createObjectURL(blob); + + const tempAnchor = document.createElement("a"); + tempAnchor.href = url; + const serviceName = "orchestrator"; + const filename = `${serviceName}_${date}.txt`; + tempAnchor.download = filename; + document.body.appendChild(tempAnchor); + tempAnchor.click(); + + document.body.removeChild(tempAnchor); + + URL.revokeObjectURL(url); + }; + + useEffect(() => { + store?.startLoading(); + getFilesData() + .then((res) => { + store?.stopLoading(); + }) + .catch((err) => { + store?.stopLoading(); + toast.error(err.message); + }); + + if (activeButton) { + fetchMessageData(); + } + + if (maxLines <= searchText) { + handleDownload(activeButton, date); + } + }, [activeButton, date, maxLines, searchText]); + + return ( + +
+ + + + + + + + + + + + + + + +
+ + + Search + +
+ {showDropdown && + autocompleteSuggestions.length > 0 && + searchText !== 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • handleDropdownItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+ +
+ + + Filter + +
+
+
+
+
+ {flag && searchPerformed && ( + + handleClick("normal")} + style={{ + backgroundColor: + activeButton === "normal" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Normal Logs + + handleClick("err")} + style={{ + backgroundColor: activeButton === "err" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Error + + + )} + {flag && searchPerformed && !isLoading && messageLines.length === 0 && ( +

+ No data available for selected date +

+ )} + + {!searchPerformed && ( +

Please select number of lines

+ )} + {searchPerformed && ( + + handleDownload(activeButton, date)} + style={{ + backgroundColor: "#007BFF", + borderColor: "#007BFF", + }} + > + Download + + + )} + {searchPerformed && maxLines > searchText && ( +
+ {searchPerformed && ( +
+ {!filter && + activeButton === "normal" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "normal" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} + {!filter && + activeButton === "err" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "err" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} +
+ )} +
+ )} + {searchPerformed && maxLines <= searchText && ( +
+

+ Too large to display! +

+
+ )} +
+ ); +}; + +export default OrchestratorLogs; diff --git a/src/pages/monitoring/logs/orchestrator/style.css b/src/pages/monitoring/logs/orchestrator/style.css new file mode 100644 index 0000000..499654f --- /dev/null +++ b/src/pages/monitoring/logs/orchestrator/style.css @@ -0,0 +1,59 @@ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + min-width: 10%; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.highlight { + background-color: yellow; +} + +.date-picker-container { + z-index: 9999 !important; + position: relative; +} + +.date-picker { + padding: 5px; + width: 120px; + cursor: pointer; +} + +.light-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: #333; +} + +.dark-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: white; +} + +.dark-date-picker .react-datetime-picker__button svg, +.dark-date-picker .react-datetime-picker__button:after, +.dark-date-picker .react-datetime-picker__button:before { + background-color: white !important; +} diff --git a/src/pages/monitoring/logs/outbound/index.tsx b/src/pages/monitoring/logs/outbound/index.tsx new file mode 100644 index 0000000..f0fc989 --- /dev/null +++ b/src/pages/monitoring/logs/outbound/index.tsx @@ -0,0 +1,393 @@ +import React, { useEffect, useState } from "react"; +import { + MDBBtn, + MDBBtnGroup, + MDBCol, + MDBContainer, + MDBRow, +} from "mdb-react-ui-kit"; +import "./style.css"; +import Ansi from "ansi-to-react"; +import { useStore } from "../../../../store"; +import toast from "react-hot-toast"; +import { getFilesData } from "../../../../api/getFiles"; +import { downloadLogData } from "../../../../api/downloadLog"; +import { downloadErrLogData } from "../../../../api/downloadErrLog"; +import { fetchServiceData } from "../../../../api/fetchService"; +import { MDBIcon } from "mdb-react-ui-kit"; +import DateTimePicker from "react-datetime-picker"; +import "react-datetime-picker/dist/DateTimePicker.css"; +import "react-calendar/dist/Calendar.css"; +import "react-clock/dist/Clock.css"; + +interface DropdownItem { + value: number; +} + +interface OutboundLogsProps extends React.HTMLAttributes { + theme: string; +} + +export const OutboundLogs: React.FC = ({ theme }) => { + const store: any = useStore(); + const [searchText, setSearchText] = useState(0); + const [filterText, setFilterText] = useState(""); + const [filter, setFilter] = useState(false); + const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + DropdownItem[] + >([]); + const [showDropdown, setShowDropdown] = useState(false); + const [messageLines, setMessageLines] = useState([]); + const [searchPerformed, setSearchPerformed] = useState(false); + const [activeButton, setActiveButton] = useState("normal"); + const [flag, setFlag] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const maxLines = process.env.REACT_APP_MAX_LINES + ? parseInt(process.env.REACT_APP_MAX_LINES) + : 5000; + + const [logdate, setLogDate] = useState(new Date()); + const [date, setDate] = useState(""); + + const [endDate, setEndDate] = useState(new Date()); + const [endDateFormatted, setEndDateFormatted] = useState(""); + + function formatDateTo_dd_mm_yyyy(inputDate) { + const date = new Date(inputDate); + + const day = String(date.getDate()).padStart(2, "0"); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const year = date.getFullYear(); + + return `${day}_${month}_${year}`; + } + + const handleDateChange = (date: Date | null) => { + setLogDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setDate(formattedDate); + } else { + setDate(""); + } + }; + + const handleEndDateChange = (date: Date | null) => { + setEndDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setEndDateFormatted(formattedDate); + } else { + setEndDateFormatted(""); + } + }; + + const fetchMessageData = async () => { + setFlag(false); + setIsLoading(true); + try { + let logData = ""; + let responseType = activeButton === "normal" ? "logs" : "error"; + const response = await fetchServiceData("outbound", searchText, date); + logData = response.data.result[responseType]; + const lines = logData.split("\n"); + setMessageLines(lines); + setFlag(true); + } catch (error) { + console.error("Error reading the file:", error); + } + setIsLoading(false); + }; + + const handleSearchInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSearchText(Number(value)); + + const data = [10, 100, 1000, 5000]; + const uniqueData = Array.from(new Set(data)); + + const suggestions: DropdownItem[] = uniqueData.map((val) => ({ + value: val, + })); + + setAutocompleteSuggestions(suggestions.filter((item) => item.value)); + setShowDropdown(true); + }; + + const handleFilterInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setFilterText(value); + setFilter(true); + }; + + const handleSearchSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setSearchPerformed(true); + setShowDropdown(false); + fetchMessageData(); + }; + + const handleFilterSubmit = (event: React.FormEvent) => { + event.preventDefault(); + fetchMessageData(); + }; + + const handleDropdownItemClick = (value: number) => { + setSearchText(value); + setShowDropdown(false); + }; + + const handleClick = (buttonName) => { + setActiveButton(buttonName); + fetchMessageData(); + }; + + const handleDownload = async (activeButton, date) => { + let response; + if (activeButton === "normal") { + response = await downloadLogData("outbound", date); + } else { + response = await downloadErrLogData("outbound", date); + } + const responseData = response.data; + console.log(responseData); + + const blob = new Blob([responseData], { type: "text/plain" }); + + const url = URL.createObjectURL(blob); + + const tempAnchor = document.createElement("a"); + tempAnchor.href = url; + const serviceName = "outbound"; + const filename = `${serviceName}_${date}.txt`; + tempAnchor.download = filename; + document.body.appendChild(tempAnchor); + tempAnchor.click(); + + document.body.removeChild(tempAnchor); + + URL.revokeObjectURL(url); + }; + + useEffect(() => { + store?.startLoading(); + getFilesData() + .then((res) => { + store?.stopLoading(); + }) + .catch((err) => { + store?.stopLoading(); + toast.error(err.message); + }); + + if (activeButton) { + fetchMessageData(); + } + + if (maxLines <= searchText) { + handleDownload(activeButton, date); + } + }, [activeButton, date, maxLines, searchText]); + + return ( + +
+ + + + + + + + + + + + + + +
+ + + Search + +
+ {showDropdown && + autocompleteSuggestions.length > 0 && + searchText !== 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • handleDropdownItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+ +
+ + + Filter + +
+
+
+
+
+ {flag && searchPerformed && ( + + handleClick("normal")} + style={{ + backgroundColor: + activeButton === "normal" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Normal Logs + + handleClick("err")} + style={{ + backgroundColor: activeButton === "err" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Error + + + )} + {flag && searchPerformed && !isLoading && messageLines.length === 0 && ( +

+ No data available for selected date +

+ )} + + {!searchPerformed && ( +

Please select number of lines

+ )} + {searchPerformed && ( + + handleDownload(activeButton, date)} + style={{ + backgroundColor: "#007BFF", + borderColor: "#007BFF", + }} + > + Download + + + )} + {searchPerformed && maxLines > searchText && ( +
+ {searchPerformed && ( +
+ {!filter && + activeButton === "normal" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "normal" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} + {!filter && + activeButton === "err" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "err" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} +
+ )} +
+ )} + {searchPerformed && maxLines <= searchText && ( +
+

+ Too large to display! +

+
+ )} +
+ ); +}; + +export default OutboundLogs; diff --git a/src/pages/monitoring/logs/outbound/style.css b/src/pages/monitoring/logs/outbound/style.css new file mode 100644 index 0000000..499654f --- /dev/null +++ b/src/pages/monitoring/logs/outbound/style.css @@ -0,0 +1,59 @@ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + min-width: 10%; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.highlight { + background-color: yellow; +} + +.date-picker-container { + z-index: 9999 !important; + position: relative; +} + +.date-picker { + padding: 5px; + width: 120px; + cursor: pointer; +} + +.light-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: #333; +} + +.dark-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: white; +} + +.dark-date-picker .react-datetime-picker__button svg, +.dark-date-picker .react-datetime-picker__button:after, +.dark-date-picker .react-datetime-picker__button:before { + background-color: white !important; +} diff --git a/src/pages/monitoring/logs/transformer/index.tsx b/src/pages/monitoring/logs/transformer/index.tsx new file mode 100644 index 0000000..0ecf531 --- /dev/null +++ b/src/pages/monitoring/logs/transformer/index.tsx @@ -0,0 +1,393 @@ +import React, { useEffect, useState } from "react"; +import { + MDBBtn, + MDBBtnGroup, + MDBCol, + MDBContainer, + MDBRow, +} from "mdb-react-ui-kit"; +import "./style.css"; +import Ansi from "ansi-to-react"; +import { useStore } from "../../../../store"; +import toast from "react-hot-toast"; +import { getFilesData } from "../../../../api/getFiles"; +import { downloadLogData } from "../../../../api/downloadLog"; +import { downloadErrLogData } from "../../../../api/downloadErrLog"; +import { fetchServiceData } from "../../../../api/fetchService"; +import { MDBIcon } from "mdb-react-ui-kit"; +import DateTimePicker from "react-datetime-picker"; +import "react-datetime-picker/dist/DateTimePicker.css"; +import "react-calendar/dist/Calendar.css"; +import "react-clock/dist/Clock.css"; + +interface DropdownItem { + value: number; +} + +interface TransformerLogsProps extends React.HTMLAttributes { + theme: string; +} + +export const TransformerLogs: React.FC = ({ theme }) => { + const store: any = useStore(); + const [searchText, setSearchText] = useState(0); + const [filterText, setFilterText] = useState(""); + const [filter, setFilter] = useState(false); + const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + DropdownItem[] + >([]); + const [showDropdown, setShowDropdown] = useState(false); + const [messageLines, setMessageLines] = useState([]); + const [searchPerformed, setSearchPerformed] = useState(false); + const [activeButton, setActiveButton] = useState("normal"); + const [flag, setFlag] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const maxLines = process.env.REACT_APP_MAX_LINES + ? parseInt(process.env.REACT_APP_MAX_LINES) + : 5000; + + const [logdate, setLogDate] = useState(new Date()); + const [date, setDate] = useState(""); + + const [endDate, setEndDate] = useState(new Date()); + const [endDateFormatted, setEndDateFormatted] = useState(""); + + function formatDateTo_dd_mm_yyyy(inputDate) { + const date = new Date(inputDate); + + const day = String(date.getDate()).padStart(2, "0"); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const year = date.getFullYear(); + + return `${day}_${month}_${year}`; + } + + const handleDateChange = (date: Date | null) => { + setLogDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setDate(formattedDate); + } else { + setDate(""); + } + }; + + const handleEndDateChange = (date: Date | null) => { + setEndDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setEndDateFormatted(formattedDate); + } else { + setEndDateFormatted(""); + } + }; + + const fetchMessageData = async () => { + setFlag(false); + setIsLoading(true); + try { + let logData = ""; + let responseType = activeButton === "normal" ? "logs" : "error"; + const response = await fetchServiceData("transformer", searchText, date); + logData = response.data.result[responseType]; + const lines = logData.split("\n"); + setMessageLines(lines); + setFlag(true); + } catch (error) { + console.error("Error reading the file:", error); + } + setIsLoading(false); + }; + + const handleSearchInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSearchText(Number(value)); + + const data = [10, 100, 1000, 5000]; + const uniqueData = Array.from(new Set(data)); + + const suggestions: DropdownItem[] = uniqueData.map((val) => ({ + value: val, + })); + + setAutocompleteSuggestions(suggestions.filter((item) => item.value)); + setShowDropdown(true); + }; + + const handleFilterInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setFilterText(value); + setFilter(true); + }; + + const handleSearchSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setSearchPerformed(true); + setShowDropdown(false); + fetchMessageData(); + }; + + const handleFilterSubmit = (event: React.FormEvent) => { + event.preventDefault(); + fetchMessageData(); + }; + + const handleDropdownItemClick = (value: number) => { + setSearchText(value); + setShowDropdown(false); + }; + + const handleClick = (buttonName) => { + setActiveButton(buttonName); + fetchMessageData(); + }; + + const handleDownload = async (activeButton, date) => { + let response; + if (activeButton === "normal") { + response = await downloadLogData("transformer", date); + } else { + response = await downloadErrLogData("transformer", date); + } + const responseData = response.data; + console.log(responseData); + + const blob = new Blob([responseData], { type: "text/plain" }); + + const url = URL.createObjectURL(blob); + + const tempAnchor = document.createElement("a"); + tempAnchor.href = url; + const serviceName = "transformer"; + const filename = `${serviceName}_${date}.txt`; + tempAnchor.download = filename; + document.body.appendChild(tempAnchor); + tempAnchor.click(); + + document.body.removeChild(tempAnchor); + + URL.revokeObjectURL(url); + }; + + useEffect(() => { + store?.startLoading(); + getFilesData() + .then((res) => { + store?.stopLoading(); + }) + .catch((err) => { + store?.stopLoading(); + toast.error(err.message); + }); + + if (activeButton) { + fetchMessageData(); + } + + if (maxLines <= searchText) { + handleDownload(activeButton, date); + } + }, [activeButton, date, maxLines, searchText]); + + return ( + +
+ + + + + + + + + + + + + + +
+ + + Search + +
+ {showDropdown && + autocompleteSuggestions.length > 0 && + searchText !== 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • handleDropdownItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+ +
+ + + Filter + +
+
+
+
+
+ {flag && searchPerformed && ( + + handleClick("normal")} + style={{ + backgroundColor: + activeButton === "normal" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Normal Logs + + handleClick("err")} + style={{ + backgroundColor: activeButton === "err" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Error + + + )} + {flag && searchPerformed && !isLoading && messageLines.length === 0 && ( +

+ No data available for selected date +

+ )} + + {!searchPerformed && ( +

Please select number of lines

+ )} + {searchPerformed && ( + + handleDownload(activeButton, date)} + style={{ + backgroundColor: "#007BFF", + borderColor: "#007BFF", + }} + > + Download + + + )} + {searchPerformed && maxLines > searchText && ( +
+ {searchPerformed && ( +
+ {!filter && + activeButton === "normal" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "normal" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} + {!filter && + activeButton === "err" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "err" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} +
+ )} +
+ )} + {searchPerformed && maxLines <= searchText && ( +
+

+ Too large to display! +

+
+ )} +
+ ); +}; + +export default TransformerLogs; diff --git a/src/pages/monitoring/logs/transformer/style.css b/src/pages/monitoring/logs/transformer/style.css new file mode 100644 index 0000000..499654f --- /dev/null +++ b/src/pages/monitoring/logs/transformer/style.css @@ -0,0 +1,59 @@ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + min-width: 10%; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.highlight { + background-color: yellow; +} + +.date-picker-container { + z-index: 9999 !important; + position: relative; +} + +.date-picker { + padding: 5px; + width: 120px; + cursor: pointer; +} + +.light-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: #333; +} + +.dark-icon { + position: absolute; + top: 50%; + left:-20px; + transform: translateY(-50%); + font-size: 18px; + color: white; +} + +.dark-date-picker .react-datetime-picker__button svg, +.dark-date-picker .react-datetime-picker__button:after, +.dark-date-picker .react-datetime-picker__button:before { + background-color: white !important; +} diff --git a/src/pages/monitoring/logs/uci-api/index.tsx b/src/pages/monitoring/logs/uci-api/index.tsx new file mode 100644 index 0000000..6c0d354 --- /dev/null +++ b/src/pages/monitoring/logs/uci-api/index.tsx @@ -0,0 +1,397 @@ +import React, { useEffect, useState } from "react"; +import { + MDBBtn, + MDBBtnGroup, + MDBCol, + MDBContainer, + MDBRow, +} from "mdb-react-ui-kit"; +import "./style.css"; +import Ansi from "ansi-to-react"; +import { useStore } from "../../../../store"; +import toast from "react-hot-toast"; +import { getFilesData } from "../../../../api/getFiles"; +import { downloadLogData } from "../../../../api/downloadLog"; +import { downloadErrLogData } from "../../../../api/downloadErrLog"; +import { fetchServiceData } from "../../../../api/fetchService"; +import { MDBIcon } from "mdb-react-ui-kit"; +import DateTimePicker from "react-datetime-picker"; +import "react-datetime-picker/dist/DateTimePicker.css"; +import "react-calendar/dist/Calendar.css"; +import "react-clock/dist/Clock.css"; + +interface DropdownItem { + value: number; +} + +interface UCIAPIlogsProps extends React.HTMLAttributes { + theme: string; +} + +export const UCIAPIlogs: React.FC = ({ theme }) => { + const store: any = useStore(); + const [searchText, setSearchText] = useState(0); + const [filterText, setFilterText] = useState(""); + const [filter, setFilter] = useState(false); + const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + DropdownItem[] + >([]); + const [showDropdown, setShowDropdown] = useState(false); + const [messageLines, setMessageLines] = useState([]); + const [searchPerformed, setSearchPerformed] = useState(false); + const [activeButton, setActiveButton] = useState("normal"); + const [flag, setFlag] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const maxLines = process.env.REACT_APP_MAX_LINES + ? parseInt(process.env.REACT_APP_MAX_LINES) + : 5000; + + const [logdate, setLogDate] = useState(new Date()); + const [date, setDate] = useState(""); + + const [endDate, setEndDate] = useState(new Date()); + const [endDateFormatted, setEndDateFormatted] = useState(""); + + function formatDateTo_dd_mm_yyyy(inputDate) { + const date = new Date(inputDate); + + const day = String(date.getDate()).padStart(2, "0"); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const year = date.getFullYear(); + + return `${day}_${month}_${year}`; + } + + const handleDateChange = (date: Date | null) => { + setLogDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setDate(formattedDate); + } else { + setDate(""); + } + }; + + const handleEndDateChange = (date: Date | null) => { + setEndDate(date); + if (date) { + // Format date as needed + let formattedDate = formatDateTo_dd_mm_yyyy(date); + setEndDateFormatted(formattedDate); + } else { + setEndDateFormatted(""); + } + }; + + const fetchMessageData = async () => { + setFlag(false); + setIsLoading(true); + try { + let logData = ""; + let responseType = activeButton === "normal" ? "logs" : "error"; + console.log(date) + const response = await fetchServiceData("uci-apis", searchText, date); + logData = response.data.result[responseType]; + const lines = logData.split("\n"); + setMessageLines(lines); + setFlag(true); + } catch (error) { + console.error("Error reading the file:", error); + } + setIsLoading(false); + }; + + const handleSearchInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSearchText(Number(value)); + + const data = [10, 100, 1000, 5000]; + const uniqueData = Array.from(new Set(data)); + + const suggestions: DropdownItem[] = uniqueData.map((val) => ({ + value: val, + })); + + setAutocompleteSuggestions(suggestions.filter((item) => item.value)); + setShowDropdown(!value); + }; + + const handleFilterInputChange = ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setFilterText(value); + setFilter(true); + }; + + const handleSearchSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setSearchPerformed(true); + setShowDropdown(false); + fetchMessageData(); + }; + + const handleFilterSubmit = (event: React.FormEvent) => { + event.preventDefault(); + fetchMessageData(); + }; + + const handleDropdownItemClick = (value: number) => { + setSearchText(value); + setShowDropdown(false); + }; + + const handleClick = (buttonName) => { + setActiveButton(buttonName); + setShowDropdown(false); + fetchMessageData(); + }; + + const handleDownload = async (activeButton, date) => { + let response; + if (activeButton === "normal") { + response = await downloadLogData("uci-apis", date); + } else { + response = await downloadErrLogData("uci-apis", date); + } + const responseData = response.data; + + const blob = new Blob([responseData], { type: "text/plain" }); + + const url = URL.createObjectURL(blob); + + const tempAnchor = document.createElement("a"); + tempAnchor.href = url; + const serviceName = "uci_api"; + const filename = `${serviceName}_${date}.txt`; + tempAnchor.download = filename; + document.body.appendChild(tempAnchor); + tempAnchor.click(); + + document.body.removeChild(tempAnchor); + + URL.revokeObjectURL(url); + }; + + useEffect(() => { + store?.startLoading(); + getFilesData() + .then((res) => { + store?.stopLoading(); + }) + .catch((err) => { + store?.stopLoading(); + toast.error(err.message); + }); + + if (activeButton) { + fetchMessageData(); + } + + if (maxLines <= searchText) { + handleDownload(activeButton, date); + } + }, [activeButton, date, maxLines, searchText]); + + return ( + +
+ + + + + + + + + + + + + + +
+ + + Search + +
+ {showDropdown && + autocompleteSuggestions.length > 0 && + searchText !== 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • handleDropdownItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+ +
+ + + Filter + +
+
+
+
+
+ {flag && searchPerformed && ( + + handleClick("normal")} + style={{ + backgroundColor: + activeButton === "normal" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Normal Logs + + handleClick("err")} + style={{ + backgroundColor: activeButton === "err" ? "#007BFF" : "#B0C4DE", + borderColor: "#007BFF", + }} + > + Error + + + )} + {flag && searchPerformed && !isLoading && messageLines.length === 0 && ( +

+ No data available for selected date +

+ )} + + {!searchPerformed && ( +

Please select number of lines

+ )} + {searchPerformed && ( + + handleDownload(activeButton, date)} + style={{ + backgroundColor: "#007BFF", + borderColor: "#007BFF", + }} + > + Download + + + )} + {searchPerformed && maxLines > searchText && ( +
+ {searchPerformed && ( +
+ {!filter && + activeButton === "normal" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "normal" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} + {!filter && + activeButton === "err" && + messageLines.slice(0, searchText).map((line, index) => ( +

+ {line} +

+ ))} + {filter && + activeButton === "err" && + messageLines + .slice(0, searchText) + .filter((line) => line.includes(filterText)) + .map((line, index) => ( +

+ {line} +

+ ))} +
+ )} +
+ )} + {searchPerformed && maxLines <= searchText && ( +
+

+ Too large to display! +

+
+ )} +
+ ); +}; + +export default UCIAPIlogs; diff --git a/src/pages/monitoring/logs/uci-api/style.css b/src/pages/monitoring/logs/uci-api/style.css new file mode 100644 index 0000000..e10503f --- /dev/null +++ b/src/pages/monitoring/logs/uci-api/style.css @@ -0,0 +1,59 @@ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + min-width: 5%; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.highlight { + background-color: yellow; +} + +.date-picker-container { + z-index: 9999 !important; + position: relative; +} + +.date-picker { + padding: 5px; + width: 500px; + cursor: pointer; +} + +.light-icon { + position: absolute; + top: 50%; + left: -20px; + transform: translateY(-50%); + font-size: 18px; + color: #333; +} + +.dark-icon { + position: absolute; + top: 50%; + left: -20px; + transform: translateY(-50%); + font-size: 18px; + color: white; +} + +.dark-date-picker .react-datetime-picker__button svg, +.dark-date-picker .react-datetime-picker__button:after, +.dark-date-picker .react-datetime-picker__button:before { + background-color: white !important; +} diff --git a/src/pages/monitoring/orchestrator/index.tsx b/src/pages/monitoring/orchestrator/index.tsx new file mode 100644 index 0000000..9c13611 --- /dev/null +++ b/src/pages/monitoring/orchestrator/index.tsx @@ -0,0 +1,319 @@ +import { + MDBBtn, + MDBCol, + MDBDropdown, + MDBDropdownItem, + MDBDropdownMenu, + MDBDropdownToggle, + MDBRow, +} from "mdb-react-ui-kit"; +import BarChart from "../../../components/visualisation/bar"; +import React, { useEffect, useState } from "react"; +import { useStore } from "../../../store"; +import { getBots } from "../../../api/getBots"; +import { toast } from "react-hot-toast"; +import "./style.css"; +import PieChart from "../../../components/visualisation/pie"; +import LineChart from "../../../components/visualisation/line"; +// import { formatDate, reverseFormatDate } from "../../../utils/functions"; +import { fetchRealtime } from "../../../api/fetchRealtimeData"; +import { getFilesData } from "../../../api/getFiles"; +import { convertToShortDate, formatDate, reverseFormatDate } from "../../../utils/functions"; + +interface AutocompleteItem { + value: string; +} + +interface OrchestratorProps extends React.HTMLAttributes { + theme: string; +} + +export const Orchestrator: React.FC = ({ theme }) => { + const [selectedChart, setSelectedChart] = useState("barchart"); + const [dropdownLabel, setDropdownLabel] = useState("Select Chart"); + const [searchText, setSearchText] = useState(""); + // const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + // AutocompleteItem[] + // >([]); + const [autocompleteSuggestionsFile, setAutocompleteSuggestionsFile] = + useState([]); + const [botList, setBotList] = useState([]); + const store: any = useStore(); + + const [selected, setSelected] = useState(""); + const [dropdown, setDropdown] = useState(true); + + const [OrchestratorData, setOrchestratorData] = useState([]); + + const [FileName, setFileName] = useState(""); + + const func = async () => { + if (localStorage.getItem("file")) { + const file = localStorage.getItem("file"); + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setOrchestratorData(res["Orchestrator".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } else if (FileName !== "") { + const file = reverseFormatDate(FileName); + localStorage.setItem("file", file); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setOrchestratorData(res["Orchestrator".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } + fetchBotData(); + }; + + useEffect(() => { + func(); + // fetchBotData(); + }, [FileName]); + + const dataBar = { + labels: Object.keys(OrchestratorData), + datasets: [ + { + label: "Orchestrator", + data: Object.values(OrchestratorData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const dataPie = { + labels: Object.keys(OrchestratorData), + datasets: [ + { + label: "Orchestrator", + data: Object.values(OrchestratorData).map(Number), + }, + ], + }; + + const dataLine = { + labels: Object.keys(OrchestratorData), + datasets: [ + { + label: "Orchestrator", + data: Object.values(OrchestratorData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const handleChartChange = (value, label) => { + setSelectedChart(value); + setDropdownLabel(label); + }; + + let chartComponent; + if (selectedChart === "barchart") { + chartComponent = ; + } else if (selectedChart === "piechart") { + // chartComponent = ; + chartComponent = ; + } else if (selectedChart === "linechart") { + chartComponent = ; + } + + const fetchBotData = async () => { + store?.startLoading(); + const data = searchText.length > 0 ? { name: searchText } : {}; + try { + const res = await getBots(data); + store?.stopLoading(); + setBotList(res?.data?.result?.data.map((bot) => bot.name)); + } catch (err) { + store?.stopLoading(); + toast.error(err.message); + } + }; + + // const handleSearchInputChange = ( + // event: React.ChangeEvent + // ) => { + // const { value } = event.target; + // setSearchText(value); + + // const lowercasedValue = value.toLowerCase(); + // const suggestions: AutocompleteItem[] = botList.map((name) => ({ + // value: name, + // })); + + // setAutocompleteSuggestions( + // suggestions.filter((item) => + // item.value.toLowerCase().includes(lowercasedValue) + // ) + // ); + // }; + + // const handleSearchSubmit = (event: React.FormEvent) => { + // event.preventDefault(); + // }; + + const handleSearchSubmitFile = async ( + event: React.FormEvent + ) => { + event.preventDefault(); + setDropdown(false); + setFileName(selected); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const file = reverseFormatDate(selected); + localStorage.setItem("file", file); + + func(); + }; + + const handleSearchInputChangeFile = async ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSelected(value); + + const lowercasedValue = value.toLowerCase(); + const apiEndpoint = getFilesData(); + let data = []; + + try { + const response = await apiEndpoint; + data = response.data.result; + } catch (error) { + console.error("Error toggling:", error); + } + + const suggestions: AutocompleteItem[] = data.map((name) => ({ + value: formatDate(name), + })); + + setAutocompleteSuggestionsFile( + suggestions.filter((item) => + item.value.toLowerCase().includes(lowercasedValue) + ) + ); + setDropdown(true); + }; + + const handleAutocompleteItemClick = (value) => { + setSelected(value); + setDropdown(false); + }; + + return ( +
+ + + + + {dropdownLabel} + + + handleChartChange("barchart", "Bar Chart")} + style={{ cursor: "pointer" }} + > + Bar Chart + + handleChartChange("piechart", "Pie Chart")} + style={{ cursor: "pointer" }} + > + Pie Chart + + handleChartChange("linechart", "Line Chart")} + style={{ cursor: "pointer" }} + > + Line Chart + + + + + {/* +
+ + + Search + +
+ {autocompleteSuggestions.length > 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • setSearchText(item.value)}> + {item.value} +
  • + ))} +
+ )} +
*/} + +
+ + + Search + +
+ {dropdown && autocompleteSuggestionsFile.length > 0 && ( +
    + {autocompleteSuggestionsFile.map((item) => ( +
  • handleAutocompleteItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+
+ {chartComponent} +
+ ); +}; + +export default Orchestrator; diff --git a/src/pages/monitoring/orchestrator/style.css b/src/pages/monitoring/orchestrator/style.css new file mode 100644 index 0000000..7b4f6f8 --- /dev/null +++ b/src/pages/monitoring/orchestrator/style.css @@ -0,0 +1,26 @@ +/* Add a higher z-index to the autocomplete dropdown */ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + max-width: inherit; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.dark-dropdown { + color: black ; +} + diff --git a/src/pages/monitoring/outbound/index.tsx b/src/pages/monitoring/outbound/index.tsx new file mode 100644 index 0000000..36c710b --- /dev/null +++ b/src/pages/monitoring/outbound/index.tsx @@ -0,0 +1,316 @@ +import { + MDBBtn, + MDBCol, + MDBDropdown, + MDBDropdownItem, + MDBDropdownMenu, + MDBDropdownToggle, + MDBRow, +} from "mdb-react-ui-kit"; +import BarChart from "../../../components/visualisation/bar"; +import React, { useEffect, useState } from "react"; +import { useStore } from "../../../store"; +import { getBots } from "../../../api/getBots"; +import { toast } from "react-hot-toast"; +import "./style.css"; +import PieChart from "../../../components/visualisation/pie"; +import LineChart from "../../../components/visualisation/line"; +import { fetchRealtime } from "../../../api/fetchRealtimeData"; +import { getFilesData } from "../../../api/getFiles"; +import { convertToShortDate, formatDate, reverseFormatDate } from "../../../utils/functions"; + +interface AutocompleteItem { + value: string; +} + +interface OutboundProps extends React.HTMLAttributes { + theme: string; +} + +export const Outbound: React.FC = ({ theme }) => { + const [selectedChart, setSelectedChart] = useState("barchart"); + const [dropdownLabel, setDropdownLabel] = useState("Select Chart"); + const [searchText, setSearchText] = useState(""); + // const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + // AutocompleteItem[] + // >([]); + const [autocompleteSuggestionsFile, setAutocompleteSuggestionsFile] = + useState([]); + const [botList, setBotList] = useState([]); + const store: any = useStore(); + + const [selected, setSelected] = useState(""); + const [dropdown, setDropdown] = useState(true); + + const [OutboundData, setOutboundData] = useState([]); + + const [FileName, setFileName] = useState(""); + + const func = async () => { + if (localStorage.getItem("file")) { + const file = localStorage.getItem("file"); + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setOutboundData(res["Outbound".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } else if (FileName !== "") { + const file = reverseFormatDate(FileName); + localStorage.setItem("file", file); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setOutboundData(res["Outbound".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } + fetchBotData(); + }; + + useEffect(() => { + func(); + // fetchBotData(); + }, [FileName]); + + const dataBar = { + labels: Object.keys(OutboundData), + datasets: [ + { + label: "Outbound", + data: Object.values(OutboundData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const dataPie = { + labels: Object.keys(OutboundData), + datasets: [ + { + label: "Outbound", + data: Object.values(OutboundData).map(Number), + }, + ], + }; + + const dataLine = { + labels: Object.keys(OutboundData), + datasets: [ + { + label: "Outbound", + data: Object.values(OutboundData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const handleChartChange = (value, label) => { + setSelectedChart(value); + setDropdownLabel(label); + }; + + let chartComponent; + if (selectedChart === "barchart") { + chartComponent = ; + } else if (selectedChart === "piechart") { + // chartComponent = ; + chartComponent = ; + } else if (selectedChart === "linechart") { + chartComponent = ; + } + + const fetchBotData = async () => { + store?.startLoading(); + const data = searchText.length > 0 ? { name: searchText } : {}; + try { + const res = await getBots(data); + store?.stopLoading(); + setBotList(res?.data?.result?.data.map((bot) => bot.name)); + } catch (err) { + store?.stopLoading(); + toast.error(err.message); + } + }; + + // const handleSearchInputChange = ( + // event: React.ChangeEvent + // ) => { + // const { value } = event.target; + // setSearchText(value); + + // const lowercasedValue = value.toLowerCase(); + // const suggestions: AutocompleteItem[] = botList.map((name) => ({ + // value: name, + // })); + + // setAutocompleteSuggestions( + // suggestions.filter((item) => + // item.value.toLowerCase().includes(lowercasedValue) + // ) + // ); + // }; + + // const handleSearchSubmit = (event: React.FormEvent) => { + // event.preventDefault(); + // }; + const handleSearchSubmitFile = async ( + event: React.FormEvent + ) => { + event.preventDefault(); + setFileName(selected); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const file = reverseFormatDate(selected); + localStorage.setItem("file", file); + + func(); + }; + + const handleSearchInputChangeFile = async ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSelected(value); + + const lowercasedValue = value.toLowerCase(); + const apiEndpoint = getFilesData(); + let data = []; + + try { + const response = await apiEndpoint; + data = response.data.result; + } catch (error) { + console.error("Error toggling:", error); + } + + const suggestions: AutocompleteItem[] = data.map((name) => ({ + value: formatDate(name), + })); + + setAutocompleteSuggestionsFile( + suggestions.filter((item) => + item.value.toLowerCase().includes(lowercasedValue) + ) + ); + setDropdown(true); + }; + + const handleAutocompleteItemClick = (value) => { + setSelected(value); + setDropdown(false); + }; + + return ( +
+ + + + + {dropdownLabel} + + + handleChartChange("barchart", "Bar Chart")} + style={{ cursor: "pointer" }} + > + Bar Chart + + handleChartChange("piechart", "Pie Chart")} + style={{ cursor: "pointer" }} + > + Pie Chart + + handleChartChange("linechart", "Line Chart")} + style={{ cursor: "pointer" }} + > + Line Chart + + + + + {/* +
+ + + Search + +
+ {autocompleteSuggestions.length > 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • setSearchText(item.value)}> + {item.value} +
  • + ))} +
+ )} +
*/} + +
+ + + Search + +
+ {dropdown && autocompleteSuggestionsFile.length > 0 && ( +
    + {autocompleteSuggestionsFile.map((item) => ( +
  • handleAutocompleteItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+
+ {chartComponent} +
+ ); +}; + +export default Outbound; diff --git a/src/pages/monitoring/outbound/style.css b/src/pages/monitoring/outbound/style.css new file mode 100644 index 0000000..dc6b6c1 --- /dev/null +++ b/src/pages/monitoring/outbound/style.css @@ -0,0 +1,25 @@ +/* Add a higher z-index to the autocomplete dropdown */ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + max-width: inherit; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.dark-dropdown { + color: black ; +} \ No newline at end of file diff --git a/src/pages/monitoring/overview/card.tsx b/src/pages/monitoring/overview/card.tsx new file mode 100644 index 0000000..954e857 --- /dev/null +++ b/src/pages/monitoring/overview/card.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import { + MDBCard, + MDBCardBody, + MDBCol, + MDBRow, +} from "mdb-react-ui-kit"; +import { formatNumberWithCommas } from "../../../utils/functions"; + +const OverviewComponent = ({ theme, jsonData }) => { + const data = jsonData; + + const renderDataCards = (data: { [key: string]: number }) => { + return Object.entries(data).map(([key, value]) => ( + + + +
{formatNumberWithCommas(value)}
+

{key}

+
+
+
+ )); + }; + + return ( +
+ + {Object.entries(data).map(([category, categoryData]) => ( + + {categoryData && + renderDataCards(categoryData as { [key: string]: number })} + + ))} + +
+ ); +}; + +export default OverviewComponent; diff --git a/src/pages/monitoring/overview/index.tsx b/src/pages/monitoring/overview/index.tsx index 3559055..762b0fb 100644 --- a/src/pages/monitoring/overview/index.tsx +++ b/src/pages/monitoring/overview/index.tsx @@ -1,78 +1,217 @@ -import React, { useEffect } from "react"; -import { MDBCard, MDBCardBody } from "mdb-react-ui-kit"; +import { + MDBCard, + MDBCardBody, + MDBCol, + MDBRow, + MDBBtn, + MDBSwitch, +} from "mdb-react-ui-kit"; import "./style.css"; -import { useStore } from "../../../store"; +import { useEffect, useState } from "react"; +import { triggerRealtimeDataRes } from "../../../api/triggerRealtime"; +import { stopRealtime } from "../../../api/stopRealtimeData"; +import { getFilesData } from "../../../api/getFiles"; +import { fetchOverviewData } from "../../../api/fetchOverview"; +import { + convertToShortDate, + formatDate, + reverseFormatDate, +} from "../../../utils/functions"; +import OverviewComponent from "./card"; -type Theme = "light" | "dark"; +interface OverviewHeaderProps extends React.HTMLAttributes { + theme: string; +} -export const Overview = () => { - const store: any = useStore(); - const [theme, setTheme] = React.useState(store?.theme); +interface AutocompleteItem { + value: string; +} + +export const Overview: React.FC = ({ theme }) => { + const [isRealtimeEnabled, setIsRealtimeEnabled] = useState(false); + const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + AutocompleteItem[] + >([]); + const [selected, setSelected] = useState(""); + const [dropdown, setDropdown] = useState(true); + const [flag, setFlag] = useState(false); + const [OverviewData, setOverviewData] = useState({}); + + const handleSearchInputChange = async ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSelected(value); + setFlag(false); + + const lowercasedValue = value.toLowerCase(); + const apiEndpoint = getFilesData(); + let data = []; + + try { + const response = await apiEndpoint; + data = response.data.result; + } catch (error) { + console.error("Error toggling:", error); + } + + const suggestions: AutocompleteItem[] = data.map((name) => ({ + value: formatDate(name), + })); + + setAutocompleteSuggestions( + suggestions.filter((item) => + item.value.toLowerCase().includes(lowercasedValue) + ) + ); + setDropdown(true); + }; + + function isEmpty(value) { + if (Array.isArray(value)) { + return value.length === 0; + } else if (typeof value === "object") { + return isObjectEmpty(value); + } else { + return !value; + } + } + function isObjectEmpty(obj) { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + if (typeof obj[key] === "object" && !isObjectEmpty(obj[key])) { + return false; + } + if (typeof obj[key] !== "object" || !isEmpty(obj[key])) { + return false; + } + } + } + return true; + } + const handleSearchSubmit = async ( + event: React.FormEvent + ) => { + event.preventDefault(); + setDropdown(false); + setFlag(true); + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const file = reverseFormatDate(selected); + localStorage.setItem("file", file); + + const f="56_22_20_12_08_2023"; + + // const apiEndpoint = fetchOverviewData(file); + const apiEndpoint = fetchOverviewData(f); + let res; + + try { + const response = await apiEndpoint; + res = response.data.result; + + const parsedData = JSON.parse(res); + + setOverviewData(parsedData); + } catch (error) { + console.error("Error toggling:", error); + } + setDropdown(false); + }; + + const handleAutocompleteItemClick = (value) => { + setSelected(value); + setDropdown(false); + }; + + const handleToggleChange = async () => { + setIsRealtimeEnabled(!isRealtimeEnabled); + const apiEndpoint = isRealtimeEnabled + ? triggerRealtimeDataRes() + : stopRealtime(); + + try { + const response = await apiEndpoint; + console.log("Toggle API Response:", response); + } catch (error) { + console.error("Error toggling:", error); + } + }; + + async function fetchData() { + if (localStorage.getItem("file")) { + const file = localStorage.getItem("file"); + const apiEndpoint = fetchOverviewData(file); + let res; + const response = await apiEndpoint; + res = response.data.result; + + const parsedData = JSON.parse(res); + + setOverviewData(parsedData); + } + } useEffect(() => { - setTheme(store?.theme); - }, [store?.theme]); + fetchData(); + console.log(!selected || localStorage.getItem("file") == null); + }, []); return (
-
-
- - -
80
-

No. of Notifications sent

-
-
-
-
- - -
65
-

- No. of Notifications received by users -

-
-
-
-
- - -
50
-

No. of Notifications opened by users

-
-
-
-
-
-
- - -
80
-

No. of Notifications sent

-
-
-
-
- - -
65
-

- No. of Notifications received by users -

-
-
-
-
- - -
50
-

No. of Notifications opened by users

-
-
-
-
+ + + +
+ + + Submit + +
+ {dropdown && autocompleteSuggestions.length > 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • handleAutocompleteItemClick(item.value)} + className={`${theme}-dropdown`} + > + {item.value} +
  • + ))} +
+ )} +
+
+ + Toggle Realtime   + + +
+ {(selected || localStorage.getItem("file") != null || flag) && + isObjectEmpty(OverviewData) &&

No data available

} + {(selected || localStorage.getItem("file") != null) && ( + + )} + {!selected && localStorage.getItem("file") == null && ( +

Please enter a date

+ )}
); }; - -// export default Overview; diff --git a/src/pages/monitoring/overview/style.css b/src/pages/monitoring/overview/style.css index e88ee7f..d269be9 100644 --- a/src/pages/monitoring/overview/style.css +++ b/src/pages/monitoring/overview/style.css @@ -1,15 +1,18 @@ .card-title { - font-size: 50px; - font-weight: bold; - text-align: center; - } - - .card-text { - text-align: center; - } - - .dark-card { - background-color: black; - color: black; - } - \ No newline at end of file + font-size: 50px; + font-weight: bold; + text-align: center; +} + +.card-text { + text-align: center; +} + +.dark-card { + background-color: black; + color: black; +} + +.dark-dropdown { + color: black ; +} \ No newline at end of file diff --git a/src/pages/monitoring/transformer/index.tsx b/src/pages/monitoring/transformer/index.tsx new file mode 100644 index 0000000..ee0c9d1 --- /dev/null +++ b/src/pages/monitoring/transformer/index.tsx @@ -0,0 +1,317 @@ +import { + MDBBtn, + MDBCol, + MDBDropdown, + MDBDropdownItem, + MDBDropdownMenu, + MDBDropdownToggle, + MDBRow, +} from "mdb-react-ui-kit"; +import BarChart from "../../../components/visualisation/bar"; +import React, { useEffect, useState } from "react"; +import { useStore } from "../../../store"; +import { getBots } from "../../../api/getBots"; +import { toast } from "react-hot-toast"; +import "./style.css"; +import PieChart from "../../../components/visualisation/pie"; +import LineChart from "../../../components/visualisation/line"; +// import { formatDate, reverseFormatDate } from "../../../utils/functions"; +import { fetchRealtime } from "../../../api/fetchRealtimeData"; +import { getFilesData } from "../../../api/getFiles"; +import { convertToShortDate, formatDate, reverseFormatDate } from "../../../utils/functions"; + +interface AutocompleteItem { + value: string; +} + +interface TransformerProps extends React.HTMLAttributes { + theme: string; +} + +export const Transformer: React.FC = ({ theme }) => { + const [selectedChart, setSelectedChart] = useState("barchart"); + const [dropdownLabel, setDropdownLabel] = useState("Select Chart"); + const [searchText, setSearchText] = useState(""); + // const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + // AutocompleteItem[] + // >([]); + const [autocompleteSuggestionsFile, setAutocompleteSuggestionsFile] = + useState([]); + const [botList, setBotList] = useState([]); + const store: any = useStore(); + + const [selected, setSelected] = useState(""); + const [dropdown, setDropdown] = useState(true); + + const [TransformerData, setTransformerData] = useState([]); + + const [FileName, setFileName] = useState(""); + + const func = async () => { + if (localStorage.getItem("file")) { + const file = localStorage.getItem("file"); + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setTransformerData(res["Transformer".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } else if (FileName !== "") { + const file = reverseFormatDate(FileName); + localStorage.setItem("file", file); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setTransformerData(res["Transformer".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } + fetchBotData(); + }; + + useEffect(() => { + func(); + // fetchBotData(); + }, [FileName]); + + const dataBar = { + labels: Object.keys(TransformerData), + datasets: [ + { + label: "Transformer", + data: Object.values(TransformerData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const dataPie = { + labels: Object.keys(TransformerData), + datasets: [ + { + label: "Transformer", + data: Object.values(TransformerData).map(Number), + }, + ], + }; + + const dataLine = { + labels: Object.keys(TransformerData), + datasets: [ + { + label: "Transformer", + data: Object.values(TransformerData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const handleChartChange = (value, label) => { + setSelectedChart(value); + setDropdownLabel(label); + }; + + let chartComponent; + if (selectedChart === "barchart") { + chartComponent = ; + } else if (selectedChart === "piechart") { + // chartComponent = ; + chartComponent = ; + } else if (selectedChart === "linechart") { + chartComponent = ; + } + + const fetchBotData = async () => { + store?.startLoading(); + const data = searchText.length > 0 ? { name: searchText } : {}; + try { + const res = await getBots(data); + store?.stopLoading(); + setBotList(res?.data?.result?.data.map((bot) => bot.name)); + } catch (err) { + store?.stopLoading(); + toast.error(err.message); + } + }; + + // const handleSearchInputChange = ( + // event: React.ChangeEvent + // ) => { + // const { value } = event.target; + // setSearchText(value); + + // const lowercasedValue = value.toLowerCase(); + // const suggestions: AutocompleteItem[] = botList.map((name) => ({ + // value: name, + // })); + + // setAutocompleteSuggestions( + // suggestions.filter((item) => + // item.value.toLowerCase().includes(lowercasedValue) + // ) + // ); + // }; + + // const handleSearchSubmit = (event: React.FormEvent) => { + // event.preventDefault(); + // }; + + const handleSearchSubmitFile = async ( + event: React.FormEvent + ) => { + event.preventDefault(); + setFileName(selected); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const file = reverseFormatDate(selected); + localStorage.setItem("file", file); + + func(); + }; + + const handleSearchInputChangeFile = async ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSelected(value); + + const lowercasedValue = value.toLowerCase(); + const apiEndpoint = getFilesData(); + let data = []; + + try { + const response = await apiEndpoint; + data = response.data.result; + } catch (error) { + console.error("Error toggling:", error); + } + + const suggestions: AutocompleteItem[] = data.map((name) => ({ + value: formatDate(name), + })); + + setAutocompleteSuggestionsFile( + suggestions.filter((item) => + item.value.toLowerCase().includes(lowercasedValue) + ) + ); + setDropdown(true); + }; + + const handleAutocompleteItemClick = (value) => { + setSelected(value); + setDropdown(false); + }; + return ( +
+ + + + + {dropdownLabel} + + + handleChartChange("barchart", "Bar Chart")} + style={{ cursor: "pointer" }} + > + Bar Chart + + handleChartChange("piechart", "Pie Chart")} + style={{ cursor: "pointer" }} + > + Pie Chart + + handleChartChange("linechart", "Line Chart")} + style={{ cursor: "pointer" }} + > + Line Chart + + + + + {/* +
+ + + Search + +
+ {autocompleteSuggestions.length > 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • setSearchText(item.value)}> + {item.value} +
  • + ))} +
+ )} +
*/} + +
+ + + Search + +
+ {dropdown && autocompleteSuggestionsFile.length > 0 && ( +
    + {autocompleteSuggestionsFile.map((item) => ( +
  • handleAutocompleteItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+
+ {chartComponent} +
+ ); +}; + +export default Transformer; diff --git a/src/pages/monitoring/transformer/style.css b/src/pages/monitoring/transformer/style.css new file mode 100644 index 0000000..5859a92 --- /dev/null +++ b/src/pages/monitoring/transformer/style.css @@ -0,0 +1,25 @@ +/* Add a higher z-index to the autocomplete dropdown */ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + max-width: inherit; + overflow-y: auto; + } + + .autocomplete-dropdown li { + cursor: pointer; + padding: 8px; + } + + .autocomplete-dropdown li:hover { + background-color: #f0f0f0; + } + + .dark-dropdown { + color: black ; + } \ No newline at end of file diff --git a/src/pages/monitoring/uci-api/index.tsx b/src/pages/monitoring/uci-api/index.tsx new file mode 100644 index 0000000..f375d5e --- /dev/null +++ b/src/pages/monitoring/uci-api/index.tsx @@ -0,0 +1,327 @@ +import { + MDBBtn, + MDBCol, + MDBDropdown, + MDBDropdownItem, + MDBDropdownMenu, + MDBDropdownToggle, + MDBRow, +} from "mdb-react-ui-kit"; +import BarChart from "../../../components/visualisation/bar"; +import React, { useEffect, useState } from "react"; +import { useStore } from "../../../store"; +import { getBots } from "../../../api/getBots"; +import { toast } from "react-hot-toast"; +import "./style.css"; +import PieChart from "../../../components/visualisation/pie"; +import LineChart from "../../../components/visualisation/line"; +// import { formatDate, reverseFormatDate } from "../../../utils/functions"; +import { fetchRealtime } from "../../../api/fetchRealtimeData"; +import { getFilesData } from "../../../api/getFiles"; +import { + convertToShortDate, + formatDate, + reverseFormatDate, +} from "../../../utils/functions"; + +interface AutocompleteItem { + value: string; +} + +interface UCIAPIProps extends React.HTMLAttributes { + theme: string; +} + +export const UCIAPI: React.FC = ({ theme }) => { + const [selectedChart, setSelectedChart] = useState("barchart"); + const [dropdownLabel, setDropdownLabel] = useState("Select Chart"); + const [searchText, setSearchText] = useState(""); + // const [autocompleteSuggestions, setAutocompleteSuggestions] = useState< + // AutocompleteItem[] + // >([]); + const [autocompleteSuggestionsFile, setAutocompleteSuggestionsFile] = + useState([]); + const [botList, setBotList] = useState([]); + const store: any = useStore(); + + const [selected, setSelected] = useState(""); + const [dropdown, setDropdown] = useState(true); + + const [uciApiData, setUciApiData] = useState([]); + + const [FileName, setFileName] = useState(""); + + const func = async () => { + if (localStorage.getItem("file")) { + const file = localStorage.getItem("file"); + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setUciApiData(res["uci-apis".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } else if (FileName !== "") { + const file = reverseFormatDate(FileName); + localStorage.setItem("file", file); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const apiEndpoint = fetchRealtime(file); + try { + const response = await apiEndpoint; + const res = JSON.parse(response.data.result); + setUciApiData(res["uci-apis".trim()] || {}); + } catch (error) { + console.error("Error toggling:", error); + } + } + fetchBotData(); + }; + + useEffect(() => { + func(); + // fetchBotData(); + }, [FileName]); + + const dataBar = { + labels: Object.keys(uciApiData), + datasets: [ + { + label: "UCI APIS", + data: Object.values(uciApiData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const dataPie = { + labels: Object.keys(uciApiData), + datasets: [ + { + label: "UCI APIS", + data: Object.values(uciApiData).map(Number), + }, + ], + }; + + const dataLine = { + labels: Object.keys(uciApiData), + datasets: [ + { + label: "UCI APIS", + data: Object.values(uciApiData).map(Number), + backgroundColor: "rgba(75, 192, 192, 0.6)", + }, + ], + }; + + const handleChartChange = (value, label) => { + setSelectedChart(value); + setDropdownLabel(label); + }; + + let chartComponent; + if (selectedChart === "barchart") { + chartComponent = ; + } else if (selectedChart === "piechart") { + // chartComponent = ; + chartComponent = ; + } else if (selectedChart === "linechart") { + chartComponent = ; + } + + const fetchBotData = async () => { + store?.startLoading(); + const data = searchText.length > 0 ? { name: searchText } : {}; + try { + const res = await getBots(data); + store?.stopLoading(); + setBotList(res?.data?.result?.data.map((bot) => bot.name)); + } catch (err) { + store?.stopLoading(); + toast.error(err.message); + } + }; + + // const handleSearchInputChange = ( + // event: React.ChangeEvent + // ) => { + // const { value } = event.target; + // setSearchText(value); + + // const lowercasedValue = value.toLowerCase(); + // const suggestions: AutocompleteItem[] = botList.map((name) => ({ + // value: name, + // })); + + // setAutocompleteSuggestions( + // suggestions.filter((item) => + // item.value.toLowerCase().includes(lowercasedValue) + // ) + // ); + // }; + + // const handleSearchSubmit = (event: React.FormEvent) => { + // event.preventDefault(); + // }; + + const handleSearchSubmitFile = async ( + event: React.FormEvent + ) => { + event.preventDefault(); + + const shortDate = convertToShortDate(selected); + localStorage.setItem("shortDate", shortDate); + localStorage.setItem("data_time", selected); + + const file = reverseFormatDate(selected); + localStorage.setItem("file", file); + + func(); + setDropdown(false); + }; + + const handleSearchInputChangeFile = async ( + event: React.ChangeEvent + ) => { + const { value } = event.target; + setSelected(value); + setFileName(selected); + + const lowercasedValue = value.toLowerCase(); + const apiEndpoint = getFilesData(); + let data = []; + + try { + const response = await apiEndpoint; + data = response.data.result; + } catch (error) { + console.error("Error toggling:", error); + } + + const suggestions: AutocompleteItem[] = data.map((name) => ({ + value: formatDate(name), + })); + + setAutocompleteSuggestionsFile( + suggestions.filter((item) => + item.value.toLowerCase().includes(lowercasedValue) + ) + ); + setDropdown(true); + }; + + const handleAutocompleteItemClick = (value) => { + setSelected(value); + setDropdown(false); + }; + + return ( +
+ + + + + {dropdownLabel} + + + handleChartChange("barchart", "Bar Chart")} + style={{ cursor: "pointer" }} + > + Bar Chart + + handleChartChange("piechart", "Pie Chart")} + style={{ cursor: "pointer" }} + > + Pie Chart + + handleChartChange("linechart", "Line Chart")} + style={{ cursor: "pointer" }} + > + Line Chart + + + + + {/* +
+ + + Search + +
+ {autocompleteSuggestions.length > 0 && ( +
    + {autocompleteSuggestions.map((item) => ( +
  • setSearchText(item.value)}> + {item.value} +
  • + ))} +
+ )} +
*/} + +
+ + + Search + +
+ {dropdown && autocompleteSuggestionsFile.length > 0 && ( +
    + {autocompleteSuggestionsFile.map((item) => ( +
  • handleAutocompleteItemClick(item.value)} + > + {item.value} +
  • + ))} +
+ )} +
+
+ {chartComponent} +
+ ); +}; + +export default UCIAPI; diff --git a/src/pages/monitoring/uci-api/style.css b/src/pages/monitoring/uci-api/style.css new file mode 100644 index 0000000..2af15c3 --- /dev/null +++ b/src/pages/monitoring/uci-api/style.css @@ -0,0 +1,25 @@ +/* Add a higher z-index to the autocomplete dropdown */ +.autocomplete-dropdown { + position: absolute; + z-index: 100; + background-color: #fff; + border: 1px solid #ccc; + padding: 5px; + list-style: none; + max-height: 150px; + max-width: inherit; + overflow-y: auto; +} + +.autocomplete-dropdown li { + cursor: pointer; + padding: 8px; +} + +.autocomplete-dropdown li:hover { + background-color: #f0f0f0; +} + +.dark-dropdown { + color: black !important; +} \ No newline at end of file diff --git a/src/utils/functions.ts b/src/utils/functions.ts new file mode 100644 index 0000000..24a20ff --- /dev/null +++ b/src/utils/functions.ts @@ -0,0 +1,94 @@ +export function formatDate(input) { + const [seconds, minutes, hours, day, month, year] = input + .split("_") + .map(Number); + + const months = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ]; + + const formattedDate = new Date(year, month - 1, day, hours, minutes, seconds); + + const dayNumber = formattedDate.getDate(); + const monthName = months[formattedDate.getMonth()]; + const yearNumber = formattedDate.getFullYear(); + const hoursString = formattedDate.getHours().toString().padStart(2, "0"); + const minutesString = formattedDate.getMinutes().toString().padStart(2, "0"); + const secondsString = formattedDate.getSeconds().toString().padStart(2, "0"); + + const ordinalDay = + dayNumber + + (dayNumber === 1 || dayNumber === 21 || dayNumber === 31 + ? "st" + : dayNumber === 2 || dayNumber === 22 + ? "nd" + : dayNumber === 3 || dayNumber === 23 + ? "rd" + : "th"); + + return `${ordinalDay} ${monthName}, ${yearNumber} ${hoursString}:${minutesString}:${secondsString}`; +} + +export function convertToShortDate(input) { + var cleanedDate = input.replace(/(\d+)(st|nd|rd|th)/, "$1"); + + var dateObject = new Date(cleanedDate); + + var day = String(dateObject.getDate()).padStart(2, "0"); + var month = String(dateObject.getMonth() + 1).padStart(2, "0"); + var year = dateObject.getFullYear(); + var convertedFormat = day + "_" + month + "_" + year; + + return convertedFormat; +} + +export function reverseFormatDate(formattedDate) { + const parts = formattedDate.match(/(\d+)(?:st|nd|rd|th)\s+(\w+),\s+(\d+)\s+(\d+:\d+:\d+)/); + + if (!parts) { + return "Invalid date format"; + } + + const dayNumber = parts[1].padStart(2, "0"); + const monthName = parts[2]; + const yearNumber = parts[3]; + const time = parts[4]; + + const months = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ]; + + const month = (months.indexOf(monthName) + 1).toString().padStart(2, "0"); + + const [hours, minutes, seconds] = time.split(":").map(Number); + + const reversedFormat = `${seconds.toString().padStart(2, "0")}_${minutes.toString().padStart(2, "0")}_${hours.toString().padStart(2, "0")}_${dayNumber}_${month}_${yearNumber}`; + + return reversedFormat; +} + +export const formatNumberWithCommas = (number) => { + return number.toLocaleString("en-US"); +}; \ No newline at end of file