From 9d87272fe879f8f2c98344ed5a904440e48e00e9 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Tue, 19 Apr 2022 11:38:30 +0200 Subject: [PATCH 01/14] fix font styles and sizes --- src/components/DataView/styled/index.js | 170 ++--- .../LabelBrowser/components/styled/index.js | 162 ++--- src/components/MainView.js | 62 +- .../components/AbsoluteSelector.js | 302 ++++----- .../daterangepicker/components/Ranges.js | 118 ++-- src/components/StatusBar/index.js | 66 +- src/components/StatusBar/styled/index.js | 266 ++++---- src/plugins/queryhistory/styled/index.js | 622 +++++++++--------- src/plugins/settingsmenu/Menu.js | 188 +++--- src/plugins/settingsmenu/styled/index.js | 19 + src/scss/modules/date-range-picker.scss | 51 +- src/scss/modules/log-view.scss | 2 +- src/scss/modules/status-bar.scss | 301 ++++----- src/scss/modules/valuesList.scss | 393 +++++------ src/theme/styles/Input.js | 22 +- 15 files changed, 1384 insertions(+), 1360 deletions(-) create mode 100644 src/plugins/settingsmenu/styled/index.js diff --git a/src/components/DataView/styled/index.js b/src/components/DataView/styled/index.js index 61b37034..7ed2ed64 100644 --- a/src/components/DataView/styled/index.js +++ b/src/components/DataView/styled/index.js @@ -1,85 +1,85 @@ -import styled from "@emotion/styled"; -import { THEME_COLORS } from '../theme/theme'; -import { CircularProgress } from "@mui/material"; -import darkTheme from "../../../theme/dark"; - -const theme = darkTheme; - -export const DataViewStyled = styled.div` - background: ${theme.viewBg}; - margin: 6px 8px; - overflow-y: scroll; - overflow-x: hidden; - position: relative; - flex: 1; - border-radius: 3px; - //padding: 0.5rem; - - &::-webkit-scrollbar { - width: 10px; - } - - &::-webkit-scrollbar-thumb { - border-radius: 10px; - background: ${theme.scrollbarThumb}; - } -`; - -export const EmptyViewContainer = styled.div` - color: white; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 175px; - font-size: 1em; - color: ${theme.textOff}; - text-align: center; -`; - -export const DataViewCont = styled.div` - display: flex; - min-height: min-content; - flex-direction: column; -`; - -export const LogRow = styled.div` - padding: 0.3rem; - color: white; - font-size: 13px; - cursor: pointer; - margin-bottom: 4px; - padding-left: 0.5rem; - margin-left: 0.25rem; - transition: 0.2s all; - &:hover { - background: black; - } - - p { - display: inline-block; - } - - border-left: 4px solid ${(props) => props.rowColor}; -`; - -export const RowLogContent = styled.span` - font-size: 0.95em; - font-family: monospace; - color: ${theme.textWhite}; - line-height: 1.5; -`; - -export const RowTimestamp = styled.span` - position: relative; - color: ${theme.textColor}; - margin-right: 0.25rem; -`; - -export const Loader = styled(CircularProgress)` - position: absolute; - top: 50px; - left: 0; - right: 0; - margin: auto; -`; +import styled from "@emotion/styled"; +import { THEME_COLORS } from '../theme/theme'; +import { CircularProgress } from "@mui/material"; +import darkTheme from "../../../theme/dark"; + +const theme = darkTheme; + +export const DataViewStyled = styled.div` + background: ${theme.viewBg}; + margin: 6px 8px; + overflow-y: scroll; + overflow-x: hidden; + position: relative; + flex: 1; + border-radius: 3px; + //padding: 0.5rem; + + &::-webkit-scrollbar { + width: 10px; + } + + &::-webkit-scrollbar-thumb { + border-radius: 10px; + background: ${theme.scrollbarThumb}; + } +`; + +export const EmptyViewContainer = styled.div` + color: white; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 175px; + font-size: 1em; + color: ${theme.textOff}; + text-align: center; +`; + +export const DataViewCont = styled.div` + display: flex; + min-height: min-content; + flex-direction: column; +`; + +export const LogRow = styled.div` + padding: 0.3rem; + color: white; + font-size: 12px; + cursor: pointer; + margin-bottom: 4px; + padding-left: 0.5rem; + margin-left: 0.25rem; + transition: 0.2s all; + &:hover { + background: black; + } + + p { + display: inline-block; + } + + border-left: 4px solid ${(props) => props.rowColor}; +`; + +export const RowLogContent = styled.span` + font-size: 12px; + font-family: monospace; + color: ${theme.textWhite}; + line-height: 1.5; +`; + +export const RowTimestamp = styled.span` + position: relative; + color: ${theme.textColor}; + margin-right: 0.25rem; +`; + +export const Loader = styled(CircularProgress)` + position: absolute; + top: 50px; + left: 0; + right: 0; + margin: auto; +`; diff --git a/src/components/LabelBrowser/components/styled/index.js b/src/components/LabelBrowser/components/styled/index.js index 78d4d5ce..6587ea6f 100644 --- a/src/components/LabelBrowser/components/styled/index.js +++ b/src/components/LabelBrowser/components/styled/index.js @@ -1,80 +1,82 @@ -import styled from "@emotion/styled"; - -import HistoryIcon from "@mui/icons-material/History"; -import darkTheme from "../../../../theme/dark"; -import { BtnSmall } from "../../../../theme/styles/Button"; - -// get theme from main state - -const theme = darkTheme; - -export const HistoryIconStyled = styled(HistoryIcon)` - color: ${(props) => props.color}; -`; -export const HistoryButtonStyled = styled(BtnSmall)` - background: none; - color: ${theme.buttonText}; - margin-left: 5px; - background: ${theme.buttonHover}; - span { - margin-left: 5px; - } - @media screen and (max-width: 864px) { - display: ${(props) => (props.isMobile ? "flex" : "none")}; - } -`; - -export const ShowLabelsBtn = styled(BtnSmall)` - background: ${(props) => - props.browserActive ? theme.buttonDefault : theme.buttonHover}; - text-overflow: ellipsis; - transition: 0.25s all; - justify-content: flex-start; - color: ${theme.buttonText}; - &:hover { - background: ${theme.buttonHover}; - } - @media screen and (max-width: 864px) { - display: ${(props) => (props.isMobile ? "flex" : "none")}; - - margin: 0; - } -`; - -export const QueryBarContainer = styled.div` - display: flex; - padding: 3px 6px; - margin: 5px 0px; - margin-left: 0px; - background: ${theme.widgetContainer}; - flex-wrap: wrap; - border-radius: 3px; -`; -export const ShowLogsBtn = styled(BtnSmall)` - background: ${theme.primaryDark}; - color: ${theme.buttonText}; - margin-left: 5px; - transition: 0.25s all; - justify-content: center; - &:hover { - background: ${theme.primaryLight}; - } - &:disabled { - background: ${theme.buttonDefault}; - cursor: not-allowed; - } - @media screen and (max-width: 864px) { - display: ${(props) => (props.isMobile ? "flex" : "none")}; - - margin: 0; - } -`; - -export const MobileTopQueryMenu = styled.div` - display: none; - @media screen and (max-width: 864px) { - display: flex; - justify-content: space-between; - margin: 10px; - } -`; +import styled from "@emotion/styled"; + +import HistoryIcon from "@mui/icons-material/History"; +import darkTheme from "../../../../theme/dark"; +import { BtnSmall } from "../../../../theme/styles/Button"; + +// get theme from main state + +const theme = darkTheme; + +export const HistoryIconStyled = styled(HistoryIcon)` + color: ${(props) => props.color}; + height:18px; + width:18px; +`; +export const HistoryButtonStyled = styled(BtnSmall)` + background: none; + color: ${theme.buttonText}; + margin-left: 5px; + background: ${theme.buttonHover}; + span { + margin-left: 5px; + } + @media screen and (max-width: 864px) { + display: ${(props) => (props.isMobile ? "flex" : "none")}; + } +`; + +export const ShowLabelsBtn = styled(BtnSmall)` + background: ${(props) => + props.browserActive ? theme.buttonDefault : theme.buttonHover}; + text-overflow: ellipsis; + transition: 0.25s all; + justify-content: flex-start; + color: ${theme.buttonText}; + &:hover { + background: ${theme.buttonHover}; + } + @media screen and (max-width: 864px) { + display: ${(props) => (props.isMobile ? "flex" : "none")}; + + margin: 0; + } +`; + +export const QueryBarContainer = styled.div` + display: flex; + padding: 3px 6px; + margin: 5px 0px; + margin-left: 0px; + background: ${theme.widgetContainer}; + flex-wrap: wrap; + border-radius: 3px; +`; +export const ShowLogsBtn = styled(BtnSmall)` + background: ${theme.primaryDark}; + color: ${theme.buttonText}; + margin-left: 10px; + transition: 0.25s all; + justify-content: center; + &:hover { + background: ${theme.primaryLight}; + } + &:disabled { + background: ${theme.buttonDefault}; + cursor: not-allowed; + } + @media screen and (max-width: 864px) { + display: ${(props) => (props.isMobile ? "flex" : "none")}; + + margin: 0; + } +`; + +export const MobileTopQueryMenu = styled.div` + display: none; + @media screen and (max-width: 864px) { + display: flex; + justify-content: space-between; + margin: 10px; + } +`; diff --git a/src/components/MainView.js b/src/components/MainView.js index 4f3eceed..04245c2c 100644 --- a/src/components/MainView.js +++ b/src/components/MainView.js @@ -1,31 +1,31 @@ -import { Notification } from "../plugins/notifications"; - -import DataView from "./DataView/DataView"; -import StatusBar from "./StatusBar"; -import { UpdateStateFromQueryParams } from "../helpers/UpdateStateFromQueryParams"; -import LabelBrowser from "./LabelBrowser"; -import SettingsDialog from "../plugins/settingsdialog/SettingsDialog"; -import { useSelector } from 'react-redux'; - -export default function MainView() { - UpdateStateFromQueryParams(); - const settingsDialogOpen = useSelector( store => store.settingsDialogOpen) - return ( -
- - - - - - - - - - - - -
- ); -} +import { Notification } from "../plugins/notifications"; + +import DataView from "./DataView/DataView"; +import StatusBar from "./StatusBar"; +import { UpdateStateFromQueryParams } from "../helpers/UpdateStateFromQueryParams"; +import LabelBrowser from "./LabelBrowser"; +import SettingsDialog from "../plugins/settingsdialog/SettingsDialog"; +import { useSelector } from 'react-redux'; + +export default function MainView() { + UpdateStateFromQueryParams(); + const settingsDialogOpen = useSelector( store => store.settingsDialogOpen) + return ( +
+ + + + + + + + + + + + +
+ ); +} diff --git a/src/components/StatusBar/components/daterangepicker/components/AbsoluteSelector.js b/src/components/StatusBar/components/daterangepicker/components/AbsoluteSelector.js index 4a9e3065..7dc57f7f 100644 --- a/src/components/StatusBar/components/daterangepicker/components/AbsoluteSelector.js +++ b/src/components/StatusBar/components/daterangepicker/components/AbsoluteSelector.js @@ -1,151 +1,151 @@ -import styled from "@emotion/styled"; -import DateRangeIcon from "@mui/icons-material/DateRange"; -import darkTheme from "../../../../../theme/dark"; -import { BtnSmall } from "../../../../../theme/styles/Button"; -const theme = darkTheme; - -const SelectorsContainer = styled.div` - display: ${(props) => (props.isDisplay ? "flex" : "none")}; - flex-direction: column; - margin: 20px; - margin-top: 30px; - .time-selectors { - display: ${(props) => (props.isDisplay ? "flex" : "none")}; - flex-direction: column; - margin-bottom: 15px; - .label { - font-size: 0.85em; - color: ${theme.inputLabelColor}; - width: 50px; - margin-left: 5px; - margin-bottom: 5px; - white-space: nowrap; - } - .input-group { - display: flex; - margin-bottom: 20px; - .date-time-ranged { - font-size:14px; - width: 170px; - line-height: 20px; - border:1px solid ${theme.buttonHover}; - color: ${theme.textColor}; - margin: 0; - padding:0px 8px; - margin-right: 5px; - &:focus{ - border:1px solid ${theme.buttonDefault}; - } - } - } - } -`; - -const AbsoluteSubmitButton = styled(BtnSmall)` - color: ${theme.buttonText}; - background: ${theme.primaryDark}; - padding: 6px; - justify-content: center; - margin-top:10px; - margin-bottom: 10px; - cursor: pointer; - &:hover { - background: ${theme.primaryLight}; - } -`; - -const CalendarBtn = styled(BtnSmall)` - - color: ${theme.buttonText}; - background: ${theme.buttonDefault}; - padding:8px; - &:hover { - background: ${theme.buttonHover}; - color: ${theme.textColor}; - } -`; - -export default function AbsoluteSelector({ - getEditedStartDate, - getEditedEndDate, - handleStart, - handleStop, - onTimeRangeSet, - setCalendarOpen, - isFullCalendar, - setStopCalendar, - setStartCalendar, - startCalendarOpen, - stopCalendarOpen, - isHorizontal, - styles, - isMobile, -}) { - function handleStartOpen() { - if (isFullCalendar) { - setCalendarOpen((open) => (open ? false : true)); - } else { - setCalendarOpen((open) => - open && startCalendarOpen ? false : true - ); - setStopCalendar(() => false); - setStartCalendar((open) => (open ? false : true)); - } - } - - function handleStopOpen() { - if (isFullCalendar) { - setCalendarOpen((open) => (open ? false : true)); - } else { - setCalendarOpen((open) => - open && stopCalendarOpen ? false : true - ); - setStartCalendar(() => false); - setStopCalendar((open) => (open ? false : true)); - } - } - - return ( - - -
- {"From"} -
- handleStart(e, false)} - onBlur={(e) => handleStart(e, true)} - /> - - - -
-
- -
- {"To"} -
- handleStop(e, false)} - onBlur={(e) => handleStop(e, true)} - /> - - - -
-
- { - onTimeRangeSet(e); - }} - > - {"Apply Time Range"} - -
- ); -} +import styled from "@emotion/styled"; +import DateRangeIcon from "@mui/icons-material/DateRange"; +import darkTheme from "../../../../../theme/dark"; +import { BtnSmall } from "../../../../../theme/styles/Button"; +const theme = darkTheme; + +const SelectorsContainer = styled.div` + display: ${(props) => (props.isDisplay ? "flex" : "none")}; + flex-direction: column; + margin: 20px; + margin-top: 30px; + .time-selectors { + display: ${(props) => (props.isDisplay ? "flex" : "none")}; + flex-direction: column; + margin-bottom: 15px; + .label { + font-size: 12px; + color: ${theme.inputLabelColor}; + width: 50px; + margin-left: 5px; + margin-bottom: 5px; + white-space: nowrap; + } + .input-group { + display: flex; + margin-bottom: 20px; + .date-time-ranged { + font-size:14px; + width: 170px; + line-height: 20px; + border:1px solid ${theme.buttonHover}; + color: ${theme.textColor}; + margin: 0; + padding:0px 8px; + margin-right: 5px; + &:focus{ + border:1px solid ${theme.buttonDefault}; + } + } + } + } +`; + +const AbsoluteSubmitButton = styled(BtnSmall)` + color: ${theme.buttonText}; + background: ${theme.primaryDark}; + padding: 6px; + justify-content: center; + margin-top:10px; + margin-bottom: 10px; + cursor: pointer; + &:hover { + background: ${theme.primaryLight}; + } +`; + +const CalendarBtn = styled(BtnSmall)` + + color: ${theme.buttonText}; + background: ${theme.buttonDefault}; + padding:8px; + &:hover { + background: ${theme.buttonHover}; + color: ${theme.textColor}; + } +`; + +export default function AbsoluteSelector({ + getEditedStartDate, + getEditedEndDate, + handleStart, + handleStop, + onTimeRangeSet, + setCalendarOpen, + isFullCalendar, + setStopCalendar, + setStartCalendar, + startCalendarOpen, + stopCalendarOpen, + isHorizontal, + styles, + isMobile, +}) { + function handleStartOpen() { + if (isFullCalendar) { + setCalendarOpen((open) => (open ? false : true)); + } else { + setCalendarOpen((open) => + open && startCalendarOpen ? false : true + ); + setStopCalendar(() => false); + setStartCalendar((open) => (open ? false : true)); + } + } + + function handleStopOpen() { + if (isFullCalendar) { + setCalendarOpen((open) => (open ? false : true)); + } else { + setCalendarOpen((open) => + open && stopCalendarOpen ? false : true + ); + setStartCalendar(() => false); + setStopCalendar((open) => (open ? false : true)); + } + } + + return ( + + +
+ {"From"} +
+ handleStart(e, false)} + onBlur={(e) => handleStart(e, true)} + /> + + + +
+
+ +
+ {"To"} +
+ handleStop(e, false)} + onBlur={(e) => handleStop(e, true)} + /> + + + +
+
+ { + onTimeRangeSet(e); + }} + > + {"Apply Time Range"} + +
+ ); +} diff --git a/src/components/StatusBar/components/daterangepicker/components/Ranges.js b/src/components/StatusBar/components/daterangepicker/components/Ranges.js index 6e55dec6..4edc5066 100644 --- a/src/components/StatusBar/components/daterangepicker/components/Ranges.js +++ b/src/components/StatusBar/components/daterangepicker/components/Ranges.js @@ -1,59 +1,59 @@ -import styled from "@emotion/styled"; -import { isSameRange } from "../utils"; - -const StyledList = styled.div` - display: flex; - flex-direction: column; - padding: 10px; - max-height: 250px; - overflow-y: auto; - flex: 1; - margin-top: 10px; - margin-bottom: 10px; - padding: 5px; - button { - background: none; - text-align: left; - border: none; - padding: 10px; - line-height: 1.5; - border-radius: 3px; - font-size: 0.85em; - cursor: pointer; - &:hover { - background: #11111155; - } - } -`; - -const Ranges = (props) => { - return ( - - {props.ranges.map((range, idx) => ( - - ))} - - ); -}; - -export default Ranges; +import styled from "@emotion/styled"; +import { isSameRange } from "../utils"; + +const StyledList = styled.div` + display: flex; + flex-direction: column; + padding: 10px; + max-height: 250px; + overflow-y: auto; + flex: 1; + margin-top: 10px; + margin-bottom: 10px; + padding: 5px; + button { + background: none; + text-align: left; + border: none; + padding: 10px; + line-height: 1.5; + border-radius: 3px; + font-size: 12px; + cursor: pointer; + &:hover { + background: #11111155; + } + } +`; + +const Ranges = (props) => { + return ( + + {props.ranges.map((range, idx) => ( + + ))} + + ); +}; + +export default Ranges; diff --git a/src/components/StatusBar/index.js b/src/components/StatusBar/index.js index 6ada0aa1..915f2ba5 100644 --- a/src/components/StatusBar/index.js +++ b/src/components/StatusBar/index.js @@ -1,33 +1,33 @@ -import { ApiSelector } from "./components/apiselector/ApiSelector"; -import { StatusBarSelectors } from "./components/statusbarselectors/StatusBarSelectors"; -import Logo from "./assets/cloki-logo.png"; -import { StatusBarCont, StatusCont } from "./styled"; -import ClokiMenu from "../../plugins/settingsmenu/Menu"; -import { useSelector } from "react-redux"; - - -export default function StatusBar() { -const isEmbed = useSelector(store => store.isEmbed) - return ( !isEmbed && ( - -
- {"cLoki - -
- - - - - -
- - - ) - - ); -} +import { ApiSelector } from "./components/apiselector/ApiSelector"; +import { StatusBarSelectors } from "./components/statusbarselectors/StatusBarSelectors"; +import Logo from "./assets/cloki-logo.png"; +import { StatusBarCont, StatusCont } from "./styled"; +import ClokiMenu from "../../plugins/settingsmenu/Menu"; +import { useSelector } from "react-redux"; + + +export default function StatusBar() { +const isEmbed = useSelector(store => store.isEmbed) + return ( !isEmbed && ( + +
+ {"cLoki + +
+ + + + + +
+ + + ) + + ); +} diff --git a/src/components/StatusBar/styled/index.js b/src/components/StatusBar/styled/index.js index 7660b6c4..75459380 100644 --- a/src/components/StatusBar/styled/index.js +++ b/src/components/StatusBar/styled/index.js @@ -1,133 +1,133 @@ -import styled from "@emotion/styled"; -import darkTheme from "../../../theme/dark"; -import { BtnSmall } from "../../../theme/styles/Button"; -import { InputSmall } from "../../../theme/styles/Input"; -const theme = darkTheme; -export const MenuButton = styled(BtnSmall)` - background: none; - border: none; - display: flex; - height: 26px; - color: ${(props) => - props.isActive ? theme.inputTextFocus : theme.textColor}; - cursor: pointer; -`; - -export const StatusBarCont = styled.div` - display: flex; - justify-content: space-between; - padding: 5px 10px; -`; - -export const StatusCont = styled.div` - display: flex; - align-items: center; - .selector { - margin-left: 10px; - .label { - flex: 1; - color: ${theme.inputLabelColor}; - padding: 4px 8px; - font-size: 0.85em; - text-transform: uppercase; - background: ${theme.inputLabelBg}; - border-radius: 4px; - white-space: nowrap; - } - } - input { - color: ${theme.textColor}; - background: ${theme.inputBg}; - border: none; - outline: none; - padding: 4px 8px; - font-size: 1em; - border-radius: 3px; - line-height: 1.5; - margin: 0px 5px; - &:focus { - color: orange; - } - &.limit { - width: 50px; - } - - &.date-time-range { - width: 120px; - } - } -`; -export const ApiSelectorStyled = styled.div` - display: flex; - align-items: center; - margin-left: 20px; - display: flex; - align-items: center; - transition: 0.2s all; - height: 26px; - - .selector { - margin-left: 10px; - .label { - flex: 1; - color: ${theme.inputLabelColor}; - padding: 4px 8px; - font-size: 0.85em; - text-transform: uppercase; - background: ${theme.inputLabelBg}; - border-radius: 4px; - white-space: nowrap; - } - } - & div { - display: flex; - align-items: center; - } - @media screen and (max-width: 850px) { - display: none; - } -`; - -export const ApiSelectorButton = styled(BtnSmall)` - background: ${theme.buttonDefault}; - color: ${theme.textColor}; - text-overflow: ellipsis; - transition: 0.2s all; - &:hover { - background: ${theme.buttonHover}; - } -`; -export const ApiSelectorInput = styled(InputSmall)` - color: ${theme.textColor}; - background: ${theme.inputBg}; - &:focus { - color: orange; - } -`; -export const UrlCopyButton = styled(BtnSmall)` - background: ${theme.buttonDefault}; - color: ${({ isActive }) => (isActive ? "orange" : theme.textColor)}; - cursor: ${({ isActive }) => (isActive ? "pointer" : "not-allowed")}; - text-overflow: ellipsis; - transition: 0.2s all; - span { - margin-left: 4px; - color: ${theme.textColor}; - } - &:hover { - background: ${theme.buttonHover}; - } -`; - -export const DatePickerButton = styled(BtnSmall)` - background: ${theme.buttonDefault}; - color: ${theme.textColor}; - height: 26px; - margin-left: 10px; - span { - margin-left: 5px; - } - &:hover { - color: orange; - } -`; +import styled from "@emotion/styled"; +import darkTheme from "../../../theme/dark"; +import { BtnSmall } from "../../../theme/styles/Button"; +import { InputSmall } from "../../../theme/styles/Input"; +const theme = darkTheme; +export const MenuButton = styled(BtnSmall)` + background: none; + border: none; + display: flex; + height: 26px; + color: ${(props) => + props.isActive ? theme.inputTextFocus : theme.textColor}; + cursor: pointer; +`; + +export const StatusBarCont = styled.div` + display: flex; + justify-content: space-between; + padding: 0px 7px; +`; + +export const StatusCont = styled.div` + display: flex; + align-items: center; + .selector { + margin-left: 10px; + .label { + flex: 1; + color: ${theme.inputLabelColor}; + padding: 4px 8px; + font-size: 12px; + text-transform: uppercase; + background: ${theme.inputLabelBg}; + border-radius: 4px; + white-space: nowrap; + } + } + input { + color: ${theme.textColor}; + background: ${theme.inputBg}; + border: none; + outline: none; + padding: 4px 8px; + font-size: 1em; + border-radius: 3px; + line-height: 1.5; + margin: 0px 5px; + &:focus { + color: orange; + } + &.limit { + width: 50px; + } + + &.date-time-range { + width: 120px; + } + } +`; +export const ApiSelectorStyled = styled.div` + display: flex; + align-items: center; + margin-left: 20px; + display: flex; + align-items: center; + transition: 0.2s all; + height: 26px; + + .selector { + margin-left: 10px; + .label { + flex: 1; + color: ${theme.inputLabelColor}; + padding: 4px 8px; + font-size: 12px; + text-transform: uppercase; + background: ${theme.inputLabelBg}; + border-radius: 4px; + white-space: nowrap; + } + } + & div { + display: flex; + align-items: center; + } + @media screen and (max-width: 850px) { + display: none; + } +`; + +export const ApiSelectorButton = styled(BtnSmall)` + background: ${theme.buttonDefault}; + color: ${theme.textColor}; + text-overflow: ellipsis; + transition: 0.2s all; + &:hover { + background: ${theme.buttonHover}; + } +`; +export const ApiSelectorInput = styled(InputSmall)` + color: ${theme.textColor}; + background: ${theme.inputBg}; + &:focus { + color: orange; + } +`; +export const UrlCopyButton = styled(BtnSmall)` + background: ${theme.buttonDefault}; + color: ${({ isActive }) => (isActive ? "orange" : theme.textColor)}; + cursor: ${({ isActive }) => (isActive ? "pointer" : "not-allowed")}; + text-overflow: ellipsis; + transition: 0.2s all; + span { + margin-left: 4px; + color: ${theme.textColor}; + } + &:hover { + background: ${theme.buttonHover}; + } +`; + +export const DatePickerButton = styled(BtnSmall)` + background: ${theme.buttonDefault}; + color: ${theme.textColor}; + height: 26px; + margin-left: 10px; + span { + margin-left: 5px; + } + &:hover { + color: orange; + } +`; diff --git a/src/plugins/queryhistory/styled/index.js b/src/plugins/queryhistory/styled/index.js index c32db5de..71ba58b6 100644 --- a/src/plugins/queryhistory/styled/index.js +++ b/src/plugins/queryhistory/styled/index.js @@ -1,311 +1,311 @@ -import styled from "@emotion/styled"; -import { buttonUnstyledClasses } from "@mui/base/ButtonUnstyled"; -import TabUnstyled, { tabUnstyledClasses } from "@mui/base/TabUnstyled"; -import DisplaySettingsIcon from "@mui/icons-material/DisplaySettings"; -import HistoryIcon from "@mui/icons-material/History"; -import SearchIcon from "@mui/icons-material/Search"; -import TabsListUnstyled from "@mui/base/TabsListUnstyled"; -import TabPanelUnstyled from "@mui/base/TabPanelUnstyled"; -import StarBorderIcon from "@mui/icons-material/StarBorder"; -import LinkIcon from "@mui/icons-material/Link"; -import { createTheme } from "@mui/material"; -import darkTheme from "../../../theme/dark"; - -const dTheme = darkTheme; -export const Tab = styled(TabUnstyled)` - color: ${dTheme.buttonText}; - cursor: pointer; - font-size: 13px; - background-color: transparent; - padding: 6px 10px; - border: none; - border-radius: 3px 3px 0px 0px; - display: flex; - justify-content: center; - align-items: center; - border-bottom: 1px solid transparent; - transition: 0.2s all; - - &:hover { - background-color: ${dTheme.buttonHover}; - } - - &:focus { - color: #aaa; - border-radius: 3px 3px 0px 0px; - - outline-offset: 2px; - } - - &.${tabUnstyledClasses.selected} { - border-bottom: 1px solid ${dTheme.primaryDark}; - } - - &.${buttonUnstyledClasses.disabled} { - opacity: 0.5; - cursor: not-allowed; - } - @media screen and (max-width: 360px) { - span { - display: none; - } - padding: 5px 20px; - } -`; - -export const TabHistoryIcon = styled(HistoryIcon)` - height: 16px; - width: 16px; - margin-right: 3px; -`; - -export const TabHistoryStarIcon = styled(StarBorderIcon)` - height: 16px; - width: 16px; - margin-right: 3px; -`; -export const TabHistorySettingIcon = styled(DisplaySettingsIcon)` - height: 16px; - width: 16px; - margin-right: 3px; -`; - -export const TabHistoryLinkIcon = styled(LinkIcon)` - height: 16px; - width: 16px; - margin-right: 3px; -`; - -export const TabHistorySearchIcon = styled(SearchIcon)` - height: 21px; - width: 16px; - padding: 0px 3px; - border-radius: 3px 0px 0px 3px; - background: ${dTheme.inputBg}; -`; - -export const TabHeaderContainer = styled.div` - padding: 0px 15px; - font-size: 13px; - display: flex; - align-items: center; - justify-content: space-between; - background: #7b7b7b1f; - height: 37px; -`; -export const TabPanel = styled(TabPanelUnstyled)` - width: 100%; -`; - -export const TabsList = styled(TabsListUnstyled)` - min-width: 320px; - border-bottom: 4px solid ${dTheme.inputBg}; - display: flex; - align-items: center; - align-content: space-between; -`; - -export const EmptyHistory = styled.div` - display: flex; - align-items: center; - justify-content: center; - color: ${dTheme.buttonText}; - font-size: 14px; - flex: 1; - padding: 20px; - height: 50%; -`; - -export const QueryHistoryContainer = styled.div` - height: 250px; - overflow-y: auto; - &.starredCont { - height: 210px; - } - &::-webkit-scrollbar { - width: 10px; - background: black; - } - - -`; - -export const HistoryButton = styled.button` - padding: 3px 6px; - background: ${dTheme.buttonDefault}; - border-radius: 3px; - border: none; - color: ${dTheme.buttonText}; - display: flex; - align-items: center; - justify-content: center; - margin: 0px 6px; - cursor: pointer; - min-height: 20px; -`; - -export const SettingItemContainer = styled.div` - height: 100px; - width: 240px; - display: flex; - flex-direction: column; - justify-content: space-between; - padding: 20px; - background: ${dTheme.viewBg}; - margin: 10px; - border-radius: 3px; - & div { - font-size: 15px; - color: orange; - line-height: 1.5; - } - & small { - font-size: 12px; - color: ${dTheme.buttonText}; - line-height: 1.5; - margin-bottom:10px; - } -`; -export const SubmitButton = styled(HistoryButton)` - background: ${dTheme.primaryDark}; - color: ${dTheme.buttonText}; - white-space: nowrap; - .open-icon { - display: none; - } - .open-text { - display: flex; - } - @media screen and (max-width: 864px) { - .open-icon { - display: flex; - } - .open-text { - display: none; - } - } -`; - -export const ClearHistoryButton = styled(HistoryButton)` - font-weight: bold; - padding: 10px 20px; - background: ${dTheme.primaryDark}; - margin: 0; - width: 100%; - white-space: nowrap; -`; -export const StyledCloseButton = styled(HistoryButton)` - background: none; - color: ${dTheme.buttonText}; - position: absolute; - right: 0; -`; - -export const DialogCancelButton = styled(HistoryButton)` - background: ${dTheme.buttonDefault}; - padding: 8px 16px; -`; -export const DialogConfirmButton = styled(HistoryButton)` - background: ${dTheme.primaryDark}; - padding: 8px 16px; -`; - -export const FilterInput = styled.input` - color: ${dTheme.buttonText}; - background: ${dTheme.inputBg}; - border: none; - height: 21px; - margin: 0px 10px 0px 0px; - padding: 0px; - font-size: 13px; - border-radius: 0px 3px 3px 0px; - font-family: monospace; - font-size: 12px; - &:focus { - outline: none; - color: ${dTheme.inputTextFocus}; - } -`; -export const RowData = styled.span` - flex: 1; - font-family: "monospace"; - font-size: "13px"; - color: ${dTheme.buttonText}; - white-space: nowrap; - padding: 4px 0px; - overflow: hidden; - text-overflow: ellipsis; -`; - -export const LinkParams = styled.div` - display: flex; - flex: 1; - justify-content: space-between; - .open-button { - display: none; - } - .inline-params { - align-items: center; - display: ${(props) => (props.open ? "none" : "grid")}; - flex: 1; - grid-template-columns: 1fr 0.25fr 0.25fr auto; - margin-right: 5px; - } - - .open-button { - display: flex; - color: ${dTheme.buttonText}; - background: none; - border: none; - } - .block-params { - display: ${(props) => (props.open ? "flex" : "none")}; - flex-direction: column; - flex: 1; - p { - display: flex; - align-items: center; - justify-content: space-between; - flex: 1; - border-bottom: 1px solid ${dTheme.buttonDefault}; - margin-bottom: 4px; - padding-bottom: 2px; - span { - margin-left: 3px; - } - } - } - @media screen and (max-width: 864px) { - .inline-params { - display: none; - } - } -`; - -export const HistoryRow = styled.div` - padding: 5px 0px; - padding-left: 10px; - background:#7b7b7b1f; - margin: 5px; - border-radius: 3px; - font-size: 13px; - display: flex; - justify-content: space-between; - align-items: center; - //height: 30px; -`; -export const TimeSpan = styled.div` - @media screen and (max-width: 1370px) { - display: none; - } -`; - -export const theme = createTheme({ - palette: { - mode: "dark", - primary: { - main: dTheme.buttonText, - background: dTheme.buttonHover, - }, - }, -}); +import styled from "@emotion/styled"; +import { buttonUnstyledClasses } from "@mui/base/ButtonUnstyled"; +import TabUnstyled, { tabUnstyledClasses } from "@mui/base/TabUnstyled"; +import DisplaySettingsIcon from "@mui/icons-material/DisplaySettings"; +import HistoryIcon from "@mui/icons-material/History"; +import SearchIcon from "@mui/icons-material/Search"; +import TabsListUnstyled from "@mui/base/TabsListUnstyled"; +import TabPanelUnstyled from "@mui/base/TabPanelUnstyled"; +import StarBorderIcon from "@mui/icons-material/StarBorder"; +import LinkIcon from "@mui/icons-material/Link"; +import { createTheme } from "@mui/material"; +import darkTheme from "../../../theme/dark"; + +const dTheme = darkTheme; +export const Tab = styled(TabUnstyled)` + color: ${dTheme.buttonText}; + cursor: pointer; + font-size: 13px; + background-color: transparent; + padding: 6px 10px; + border: none; + border-radius: 3px 3px 0px 0px; + display: flex; + justify-content: center; + align-items: center; + border-bottom: 1px solid transparent; + transition: 0.2s all; + + &:hover { + background-color: ${dTheme.buttonHover}; + } + + &:focus { + color: #aaa; + border-radius: 3px 3px 0px 0px; + + outline-offset: 2px; + } + + &.${tabUnstyledClasses.selected} { + border-bottom: 1px solid ${dTheme.primaryDark}; + } + + &.${buttonUnstyledClasses.disabled} { + opacity: 0.5; + cursor: not-allowed; + } + @media screen and (max-width: 360px) { + span { + display: none; + } + padding: 5px 20px; + } +`; + +export const TabHistoryIcon = styled(HistoryIcon)` + height: 16px; + width: 16px; + margin-right: 3px; +`; + +export const TabHistoryStarIcon = styled(StarBorderIcon)` + height: 16px; + width: 16px; + margin-right: 3px; +`; +export const TabHistorySettingIcon = styled(DisplaySettingsIcon)` + height: 16px; + width: 16px; + margin-right: 3px; +`; + +export const TabHistoryLinkIcon = styled(LinkIcon)` + height: 16px; + width: 16px; + margin-right: 3px; +`; + +export const TabHistorySearchIcon = styled(SearchIcon)` + height: 21px; + width: 16px; + padding: 0px 3px; + border-radius: 3px 0px 0px 3px; + background: ${dTheme.inputBg}; +`; + +export const TabHeaderContainer = styled.div` + padding: 0px 15px; + font-size: 13px; + display: flex; + align-items: center; + justify-content: space-between; + background: #7b7b7b1f; + height: 37px; +`; +export const TabPanel = styled(TabPanelUnstyled)` + width: 100%; +`; + +export const TabsList = styled(TabsListUnstyled)` + min-width: 320px; + border-bottom: 4px solid ${dTheme.inputBg}; + display: flex; + align-items: center; + align-content: space-between; +`; + +export const EmptyHistory = styled.div` + display: flex; + align-items: center; + justify-content: center; + color: ${dTheme.buttonText}; + font-size: 14px; + flex: 1; + padding: 20px; + height: 50%; +`; + +export const QueryHistoryContainer = styled.div` + height: 250px; + overflow-y: auto; + &.starredCont { + height: 210px; + } + &::-webkit-scrollbar { + width: 10px; + background: black; + } + + +`; + +export const HistoryButton = styled.button` + padding: 3px 6px; + background: ${dTheme.buttonDefault}; + border-radius: 3px; + border: none; + color: ${dTheme.buttonText}; + display: flex; + align-items: center; + justify-content: center; + margin: 0px 6px; + cursor: pointer; + min-height: 20px; +`; + +export const SettingItemContainer = styled.div` + height: 100px; + width: 240px; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 20px; + background: ${dTheme.viewBg}; + margin: 10px; + border-radius: 3px; + & div { + font-size: 15px; + color: orange; + line-height: 1.5; + } + & small { + font-size: 12px; + color: ${dTheme.buttonText}; + line-height: 1.5; + margin-bottom:10px; + } +`; +export const SubmitButton = styled(HistoryButton)` + background: ${dTheme.primaryDark}; + color: ${dTheme.buttonText}; + white-space: nowrap; + .open-icon { + display: none; + } + .open-text { + display: flex; + } + @media screen and (max-width: 864px) { + .open-icon { + display: flex; + } + .open-text { + display: none; + } + } +`; + +export const ClearHistoryButton = styled(HistoryButton)` + font-weight: bold; + padding: 10px 20px; + background: ${dTheme.primaryDark}; + margin: 0; + width: 100%; + white-space: nowrap; +`; +export const StyledCloseButton = styled(HistoryButton)` + background: none; + color: ${dTheme.buttonText}; + position: absolute; + right: 0; +`; + +export const DialogCancelButton = styled(HistoryButton)` + background: ${dTheme.buttonDefault}; + padding: 8px 16px; +`; +export const DialogConfirmButton = styled(HistoryButton)` + background: ${dTheme.primaryDark}; + padding: 8px 16px; +`; + +export const FilterInput = styled.input` + color: ${dTheme.buttonText}; + background: ${dTheme.inputBg}; + border: none; + height: 21px; + margin: 0px 10px 0px 0px; + padding: 0px; + font-size: 13px; + border-radius: 0px 3px 3px 0px; + font-family: monospace; + font-size: 12px; + &:focus { + outline: none; + color: ${dTheme.inputTextFocus}; + } +`; +export const RowData = styled.span` + flex: 1; + font-family: monospace; + font-size: "13px"; + color: ${dTheme.buttonText}; + white-space: nowrap; + padding: 4px 0px; + overflow: hidden; + text-overflow: ellipsis; +`; + +export const LinkParams = styled.div` + display: flex; + flex: 1; + justify-content: space-between; + .open-button { + display: none; + } + .inline-params { + align-items: center; + display: ${(props) => (props.open ? "none" : "grid")}; + flex: 1; + grid-template-columns: 1fr 0.25fr 0.25fr auto; + margin-right: 5px; + } + + .open-button { + display: flex; + color: ${dTheme.buttonText}; + background: none; + border: none; + } + .block-params { + display: ${(props) => (props.open ? "flex" : "none")}; + flex-direction: column; + flex: 1; + p { + display: flex; + align-items: center; + justify-content: space-between; + flex: 1; + border-bottom: 1px solid ${dTheme.buttonDefault}; + margin-bottom: 4px; + padding-bottom: 2px; + span { + margin-left: 3px; + } + } + } + @media screen and (max-width: 864px) { + .inline-params { + display: none; + } + } +`; + +export const HistoryRow = styled.div` + padding: 5px 0px; + padding-left: 10px; + background:#7b7b7b1f; + margin: 5px; + border-radius: 3px; + font-size: 13px; + display: flex; + justify-content: space-between; + align-items: center; + //height: 30px; +`; +export const TimeSpan = styled.div` + @media screen and (max-width: 1370px) { + display: none; + } +`; + +export const theme = createTheme({ + palette: { + mode: "dark", + primary: { + main: dTheme.buttonText, + background: dTheme.buttonHover, + }, + }, +}); diff --git a/src/plugins/settingsmenu/Menu.js b/src/plugins/settingsmenu/Menu.js index f1b3d4dc..7c97ff5c 100644 --- a/src/plugins/settingsmenu/Menu.js +++ b/src/plugins/settingsmenu/Menu.js @@ -1,93 +1,95 @@ -import * as React from "react"; -import Button from "@mui/material/Button"; -import Menu from "@mui/material/Menu"; -import MenuItem from "@mui/material/MenuItem"; -import DisplaySettingsIcon from "@mui/icons-material/DisplaySettings"; -import { useDispatch } from "react-redux"; - -import CopyButton from "./CopyButton/CopyButton"; -import { styled } from "@mui/material/styles"; -import MenuIcon from "@mui/icons-material/Menu"; -import setSettingsDialogOpen from "../../actions/setSettingsDialogOpen"; - -const StyledMenu = styled((props) => ( - -))(({ theme }) => ({ - "& .MuiPaper-root": { - borderRadius: 6, - marginTop: theme.spacing(1), - minWidth: 180, - color: "#ddd", - backgroundColor: "#333", - "& .MuiMenu-list": { - padding: "4px 0", - }, - "& .MuiMenuItem-root": { - "& .MuiSvgIcon-root": { - fontSize: 18, - color: theme.palette.text.secondary, - marginRight: theme.spacing(1.5), - }, - "&:active": { - backgroundColor: "#222", - }, - }, - }, -})); - -export default function ClokiMenu() { - const dispatch = useDispatch(); - const [anchorEl, setAnchorEl] = React.useState(null); - const open = Boolean(anchorEl); - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; - const handleSettingsOpen = () => { - dispatch(setSettingsDialogOpen(true)); - - handleClose(); - }; - return ( -
- - - - - Query - Settings - - -
- ); -} +import * as React from "react"; +import Button from "@mui/material/Button"; +import Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import DisplaySettingsIcon from "@mui/icons-material/DisplaySettings"; +import { useDispatch } from "react-redux"; + +import CopyButton from "./CopyButton/CopyButton"; +import { styled } from "@mui/material/styles"; +import MenuIcon from "@mui/icons-material/Menu"; +import setSettingsDialogOpen from "../../actions/setSettingsDialogOpen"; +import { MenuButton } from "./styled"; + +const StyledMenu = styled((props) => ( + +))(({ theme }) => ({ + "& .MuiPaper-root": { + borderRadius: 6, + marginTop: theme.spacing(1), + minWidth: 180, + color: "#ddd", + backgroundColor: "#333", + "& .MuiMenu-list": { + padding: "4px 0", + }, + "& .MuiMenuItem-root": { + fontSize:12, + "& .MuiSvgIcon-root": { + fontSize: 12, + color: theme.palette.text.secondary, + marginRight: theme.spacing(1.5), + }, + "&:active": { + backgroundColor: "#222", + }, + }, + }, +})); + +export default function ClokiMenu() { + const dispatch = useDispatch(); + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleSettingsOpen = () => { + dispatch(setSettingsDialogOpen(true)); + + handleClose(); + }; + return ( +
+ + + + + + + Query + Settings + + +
+ ); +} diff --git a/src/plugins/settingsmenu/styled/index.js b/src/plugins/settingsmenu/styled/index.js new file mode 100644 index 00000000..3cdf8ae5 --- /dev/null +++ b/src/plugins/settingsmenu/styled/index.js @@ -0,0 +1,19 @@ +import styled from "@emotion/styled"; +import darkTheme from "../../../theme/dark"; +const theme = darkTheme; + +export const MenuButton = styled.button` + border: none; + background: ${theme.buttonDefault}; + color: ${theme.buttonText}; + padding: 3px 12px; + border-radius: 3px; + font-size: 12px; + cursor: pointer; + user-select: none; + line-height: 20px; + display: flex; + align-items: center; + margin-left: 10px; + height: 26px; +`; diff --git a/src/scss/modules/date-range-picker.scss b/src/scss/modules/date-range-picker.scss index 367014fc..798d1221 100644 --- a/src/scss/modules/date-range-picker.scss +++ b/src/scss/modules/date-range-picker.scss @@ -1,27 +1,26 @@ -.date-time-selector { - svg { - font-size: 1.15em; - margin-right: 3px; - } - .tooltip { - background-color: red; - display: flex; - align-items: center; - justify-items: center; - } -} -@media screen and (max-width: 1200px) { - .date-time-selector { - font-size: 1em; - padding: 8px; - color:#ddd; - line-height: 1.5; - margin-right:10px; - svg { - margin-right: 0; - } - span { - display: none; - } - } +.date-time-selector { + svg { + font-size: 1.15em; + margin-right: 3px; + } + .tooltip { + background-color: red; + display: flex; + align-items: center; + justify-items: center; + } +} +@media screen and (max-width: 1200px) { + .date-time-selector { + font-size: 1em; + padding: 8px; + color:#ddd; + line-height: 1.5; + svg { + margin-right: 0; + } + span { + display: none; + } + } } \ No newline at end of file diff --git a/src/scss/modules/log-view.scss b/src/scss/modules/log-view.scss index 70284e45..3fa5962b 100644 --- a/src/scss/modules/log-view.scss +++ b/src/scss/modules/log-view.scss @@ -77,7 +77,7 @@ max-width: 450px; .value-tags { font-family: monospace; - font-size: 0.95em; + font-size: 12px; display: flex; margin: 2px; padding: 2px; diff --git a/src/scss/modules/status-bar.scss b/src/scss/modules/status-bar.scss index a7e9bd12..fcb76732 100644 --- a/src/scss/modules/status-bar.scss +++ b/src/scss/modules/status-bar.scss @@ -1,150 +1,151 @@ -.status-bar { - display: flex; - justify-content: space-between; - padding: 5px 10px; -} -.status-options { - display: flex; - align-items: center; -} -.status-selectors { - display: flex; - align-items: center; - .selector { - margin-left: 10px; - .label { - flex: 1; - color: #bfbfbf; - white-space: nowrap;; - text-transform: uppercase; - border-radius: 3px; - } - } - & div { - display: flex; - align-items: center; - } - - @media screen and (max-width: 565px) { - display: none; - } -} -.date-selector { - input { - color: orange; - background: #121212; - border: none; - margin: 3px; - padding: 3px 6px; - font-size: 13px; - border-radius: 3px; - &.limit { - width: 50px; - } - - &.date-time-range { - width: 120px; - color: orange; - background: #121212; - } - } -} - -.logo { - border-radius: 90px; -} - -.api-url-selector { - margin-left: 20px; - display: flex; - align-items: center; - transition: 0.2s all; - input { - color: orange; - background: #121212; - border: none; - margin: 3px; - padding: 3px 6px; - font-size: 13px; - border-radius: 3px; - - } - button { - display: flex; - align-items: center; - height: 20px; - font-size: 18px; - border: none; - margin: 3px; - padding: 5px 8px; - border-radius: 3px; - background: #7b7b7b3b; - color: #d1d1d1; - font-size: 0.9em; - cursor: pointer; - align-items: center; - white-space: nowrap; - text-overflow: ellipsis; - transition: 0.2s all; - &:hover { - background: #9e9e9e3b; - } - } -} - -.logo-section { - display: flex; - align-items: center; - flex: 1; -} - -.url-copy { - display: flex; - align-items: center; - height: 20px; - font-size: 18px; - border: none; - margin: 3px; - padding: 5px 8px; - border-radius: 3px; - background: #7b7b7b3b; - // color: #d1d1d1; - color: orange; - font-size: 0.9em; - cursor: pointer; - align-items: center; - white-space: nowrap; - text-overflow: ellipsis; - transition: 0.2s all; - span { - margin-left: 4px; - text-transform: uppercase; - font-size: 0.85em; - color: #d1d1d1; - } - &:hover { - background: #9e9e9e3b; - } - &:disabled { - color: #7b7b7b; - cursor: not-allowed; - } -} -@media screen and (max-width: 950px) { - .url-copy { - span { - display: none; - } - } -} -.copied-warning { - transition: 0.2s all; - color: orange; - font-size: 0.8em; - text-transform: uppercase; - margin-right: 10px; -} -.submit-checkbox { - color: red; - background-color: green; -} +.status-bar { + display: flex; + justify-content: space-between; + padding: 0px 7px; +} +.status-options { + display: flex; + align-items: center; +} +.status-selectors { + display: flex; + align-items: center; + .selector { + margin-left: 10px; + .label { + flex: 1; + color: #bfbfbf; + white-space: nowrap;; + text-transform: uppercase; + border-radius: 3px; + font-size: 12px; + } + } + & div { + display: flex; + align-items: center; + } + + @media screen and (max-width: 565px) { + display: none; + } +} +.date-selector { + input { + color: orange; + background: #121212; + border: none; + margin: 3px; + padding: 3px 6px; + font-size: 13px; + border-radius: 3px; + &.limit { + width: 50px; + } + + &.date-time-range { + width: 120px; + color: orange; + background: #121212; + } + } +} + +.logo { +margin-left: 10px; +} + +.api-url-selector { + margin-left: 20px; + display: flex; + align-items: center; + transition: 0.2s all; + input { + color: orange; + background: #121212; + border: none; + margin: 3px; + padding: 3px 6px; + font-size: 13px; + border-radius: 3px; + + } + button { + display: flex; + align-items: center; + height: 20px; + font-size: 18px; + border: none; + margin: 3px; + padding: 5px 8px; + border-radius: 3px; + background: #7b7b7b3b; + color: #d1d1d1; + font-size: 0.9em; + cursor: pointer; + align-items: center; + white-space: nowrap; + text-overflow: ellipsis; + transition: 0.2s all; + &:hover { + background: #9e9e9e3b; + } + } +} + +.logo-section { + display: flex; + align-items: center; + flex: 1; +} + +.url-copy { + display: flex; + align-items: center; + height: 20px; + font-size: 18px; + border: none; + margin: 3px; + padding: 5px 8px; + border-radius: 3px; + background: #7b7b7b3b; + // color: #d1d1d1; + color: orange; + font-size: 0.9em; + cursor: pointer; + align-items: center; + white-space: nowrap; + text-overflow: ellipsis; + transition: 0.2s all; + span { + margin-left: 4px; + text-transform: uppercase; + font-size: 12px; + color: #d1d1d1; + } + &:hover { + background: #9e9e9e3b; + } + &:disabled { + color: #7b7b7b; + cursor: not-allowed; + } +} +@media screen and (max-width: 950px) { + .url-copy { + span { + display: none; + } + } +} +.copied-warning { + transition: 0.2s all; + color: orange; + font-size: 0.8em; + text-transform: uppercase; + margin-right: 10px; +} +.submit-checkbox { + color: red; + background-color: green; +} diff --git a/src/scss/modules/valuesList.scss b/src/scss/modules/valuesList.scss index 7aa9ab4b..897ef552 100644 --- a/src/scss/modules/valuesList.scss +++ b/src/scss/modules/valuesList.scss @@ -1,197 +1,198 @@ -.valuesList { - transition:.2s all; - display: flex; - flex-wrap: wrap; - flex: 1; - flex-direction: column; - font-size: 13px; - - background: #262626; - margin: 5px 0px; - border-radius: 4px; - transition: 0.2s all; - .valuelist-title { - display: flex; - flex-direction: column; - flex: 1; - justify-content: space-between; - color: white; - font-size: 1em; - flex: 1; - margin: 10px; - - } - .valuelist-content { - padding: 5px; - display: flex; - color: white; - font-size: 14px; - align-items: center; - flex-wrap: wrap; - max-height: 500px; - overflow: auto; - small { - margin: 5px; - padding: 4px 8px; - border-radius: 3px; - background: #5454543b; - line-height: 1.5; - color: #d1d1d1; - font-size: 1em; - cursor: pointer; - align-items: center; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - max-width: 23ch; - transition: 1s all; - &:hover { - background: #ffffff3a; - - } - } - } - .valuelist-filter { - display: flex; - flex: 1; - flex-direction: column; - - - .valuelist-filter-title { - font-size: 0.75em; - } - input { - color: white; - background: $black-quoise; - padding: 6px 3px; - border-radius: 4px; - flex: 1; - margin: 0px 3px; - outline: none; - border: none; - } - } - &::-webkit-scrollbar { - width: 10px; - } - - &::-webkit-scrollbar-thumb { - border-radius: 10px; - background: $grey-light; - } -} -.values-container { - max-width: 100%; - .values-container-column { - display: flex; - max-width: 100%; - .values-column { - margin: 5px; - border-radius: 3px; - background: #323131; - height: auto; - min-height: 100px; - min-width: 175px; - flex:1; - .column { - max-height: 350px; - &::-webkit-scrollbar { - width: 10px; - color: #58585898; - background: #ffffff1f; - } - &::-webkit-scrollbar-thumb { - border-radius: 10px; - background: $grey-light; - } - } - .values-column-title { - background: #403f3f; - border-left: 1px solid #ffa60056; - padding: 8px; - font-size: 1em; - border-radius: 4px 4px 0px 0px; - border-bottom: 2px solid #2e2e2e; - color:white; - display: flex; - flex: 1; - transition: .2s all; - - justify-content: space-between; - - .close-column { - align-self: flex-end; - justify-self: end; - color:orange; - cursor:pointer; - - padding:2px; - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - color:#c9c5c5; - - font-size: 10px; - &:hover { - - background:$dark-quoise-hover; - - color:black; - - } - } - } - } - } -} -.legend-container { - margin:3px; - padding:3px; - .legend-title { - font-size: 13px; - font-weight: bold; - } - .legend-text { - font-size: 11px; - font-family: monospace; - } -} - -.refresh-button { - display: flex; - align-items: center; - margin: 3px; - padding: 5px 8px; - border:none; - height: 22px; - font-size: 18px; - border-radius: 3px; - background: #b1b1b13b; - color: #d1d1d1; - font-size: 0.9em; - cursor: pointer; - align-items: center; - white-space: nowrap; - text-overflow: ellipsis; - transition: 0.2s all; - &.haveToRefresh { - background: $dark-quoise-hover; - } - &:hover { - background: #ffffff3a; - } -} - -.label-error { - display:flex; - align-items: center; - color:orangered; - border:1px solid orangered; - padding:3px 6px; - border-radius:3px; - width:250px; - justify-content: center; - position:absolute; - right: 20px; +.valuesList { + transition:.2s all; + display: flex; + flex-wrap: wrap; + flex: 1; + flex-direction: column; + font-size: 13px; + + background: #262626; + margin: 5px 0px; + border-radius: 4px; + transition: 0.2s all; + .valuelist-title { + display: flex; + flex-direction: column; + flex: 1; + justify-content: space-between; + color: white; + font-size: 1em; + flex: 1; + margin: 10px; + + } + .valuelist-content { + padding: 5px; + display: flex; + color: white; + font-size: 14px; + align-items: center; + flex-wrap: wrap; + max-height: 500px; + overflow: auto; + small { + margin: 5px; + padding: 4px 8px; + border-radius: 3px; + background: #5454543b; + line-height: 1.5; + color: #d1d1d1; + font-size: 12px; + cursor: pointer; + align-items: center; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + max-width: 23ch; + transition: 1s all; + &:hover { + background: #ffffff3a; + + } + } + } + .valuelist-filter { + display: flex; + flex: 1; + flex-direction: column; + + + .valuelist-filter-title { + font-size: 0.75em; + } + input { + color: white; + background: $black-quoise; + padding: 6px 3px; + border-radius: 4px; + flex: 1; + margin: 0px 3px; + outline: none; + border: none; + } + } + &::-webkit-scrollbar { + width: 10px; + } + + &::-webkit-scrollbar-thumb { + border-radius: 10px; + background: $grey-light; + } +} +.values-container { + max-width: 100%; + .values-container-column { + display: flex; + max-width: 100%; + .values-column { + margin: 5px; + border-radius: 3px; + background: #323131; + height: auto; + min-height: 100px; + min-width: 175px; + flex:1; + .column { + max-height: 350px; + &::-webkit-scrollbar { + width: 10px; + color: #58585898; + background: #ffffff1f; + } + &::-webkit-scrollbar-thumb { + border-radius: 10px; + background: $grey-light; + } + } + .values-column-title { + background: #403f3f; + border-left: 1px solid #ffa60056; + padding: 8px; + font-size: 1em; + border-radius: 4px 4px 0px 0px; + border-bottom: 2px solid #2e2e2e; + color:white; + display: flex; + flex: 1; + transition: .2s all; + + justify-content: space-between; + + .close-column { + align-self: flex-end; + justify-self: end; + color:orange; + cursor:pointer; + + padding:2px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + color:#c9c5c5; + + font-size: 10px; + &:hover { + + background:$dark-quoise-hover; + + color:black; + + } + } + } + } + } +} +.legend-container { + margin:3px; + padding:3px; + line-height: 1.5; + .legend-title { + font-size: 12px; + font-weight: bold; + } + .legend-text { + font-size: 12px; + font-family: monospace; + } +} + +.refresh-button { + display: flex; + align-items: center; + margin: 3px; + padding: 5px 8px; + border:none; + height: 22px; + font-size: 18px; + border-radius: 3px; + background: #b1b1b13b; + color: #d1d1d1; + font-size: 0.9em; + cursor: pointer; + align-items: center; + white-space: nowrap; + text-overflow: ellipsis; + transition: 0.2s all; + &.haveToRefresh { + background: $dark-quoise-hover; + } + &:hover { + background: #ffffff3a; + } +} + +.label-error { + display:flex; + align-items: center; + color:orangered; + border:1px solid orangered; + padding:3px 6px; + border-radius:3px; + width:250px; + justify-content: center; + position:absolute; + right: 20px; } \ No newline at end of file diff --git a/src/theme/styles/Input.js b/src/theme/styles/Input.js index 8f7b79bc..a8eee843 100644 --- a/src/theme/styles/Input.js +++ b/src/theme/styles/Input.js @@ -1,11 +1,11 @@ -import styled from "@emotion/styled" - -export const InputSmall = styled.input` -padding:3px 12px; -line-height: 20px; -border: 1px solid; -outline:none; -border:none; -font-size: .9em; -border-radius:3px; -` \ No newline at end of file +import styled from "@emotion/styled"; + +export const InputSmall = styled.input` + padding: 3px 12px; + line-height: 20px; + border: 1px solid; + outline: none; + border: none; + font-size: 12px; + border-radius: 3px; +`; From 70039ee793b5a3554d43fbbdaca0372575396231 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Tue, 19 Apr 2022 19:53:35 +0200 Subject: [PATCH 02/14] fix: labels at initial state --- src/components/LabelBrowser/QueryBar.js | 374 ++++++----- src/components/LabelBrowser/ValuesList.js | 444 ++++++------- .../LabelBrowser/helpers/querybuilder.js | 79 +-- .../helpers/useLabelsFromQuery.js | 16 + .../components/apiselector/ApiSelector.js | 172 ++--- src/helpers/UpdateStateFromQueryParams.js | 617 +++++++++--------- src/hooks/useLabels.js | 47 ++ 7 files changed, 921 insertions(+), 828 deletions(-) create mode 100644 src/components/LabelBrowser/helpers/useLabelsFromQuery.js create mode 100644 src/hooks/useLabels.js diff --git a/src/components/LabelBrowser/QueryBar.js b/src/components/LabelBrowser/QueryBar.js index c6f34596..f2f2daa9 100644 --- a/src/components/LabelBrowser/QueryBar.js +++ b/src/components/LabelBrowser/QueryBar.js @@ -1,179 +1,195 @@ -import React, { useState, useEffect } from "react"; -import { useSelector, useDispatch } from "react-redux"; -import { setIsSubmit, setQuery } from "../../actions"; -import loadLogs from "../../actions/loadLogs"; -import setLoading from "../../actions/setLoading"; -import { setLabelsBrowserOpen } from "../../actions/setLabelsBrowserOpen"; -import localService from "../../services/localService"; -import setQueryHistory from "../../actions/setQueryHistory"; - -import setHistoryOpen from "../../actions/setHistoryOpen"; -import localUrl from "../../services/localUrl"; -import setLinksHistory from "../../actions/setLinksHistory"; -import QueryEditor from "../../plugins/queryeditor"; - -import loadLabels from "../../actions/LoadLabels"; -import { decodeQuery } from "../../helpers/UpdateStateFromQueryParams"; -import { css } from "@emotion/css"; -import { MobileTopQueryMenu, QueryBarContainer } from "./components/styled"; -import HistoryButton from "./components/HistoryButton/HistoryButton"; -import ShowLabelsButton from "./components/ShowLabelsButton/ShowLabelsButton"; -import ShowLogsButton from "./components/ShowLogsButton/ShowLogsButton"; -import queryInit from "./helpers/queryInit"; -import onQueryValid from "./helpers/onQueryValid"; -import debugLog from "./helpers/debugLog"; - -export const QueryBar = () => { - const dispatch = useDispatch(); - const historyService = localService().historyStore(); - const labelsBrowserOpen = useSelector((store) => store.labelsBrowserOpen); - const debug = useSelector((store) => store.debugMode); - const query = useSelector((store) => store.query); - const apiUrl = useSelector((store) => store.apiUrl); - const isSubmit = useSelector((store) => store.isSubmit); - const historyOpen = useSelector((store) => store.historyOpen); - const isEmbed = useSelector( ( store ) => store.isEmbed) - - const [queryInput, setQueryInput] = useState(query); - const [queryValid, setQueryValid] = useState(false); - const [queryValue, setQueryValue] = useState(queryInit(query)); - - const queryHistory = useSelector((store) => store.queryHistory); - const saveUrl = localUrl(); - useEffect(() => { - const dLog = debugLog(query); - debug && dLog.logicQueryBar(); - - if (onQueryValid(query && isSubmit === "true")) { - debug && dLog.queryBarDispatch(); - - dispatch(setLoading(true)); - dispatch(loadLogs()); - - setTimeout(() => { - dispatch(setIsSubmit(false)); - }, 200); - } else if (!onQueryValid(query) && isSubmit === "true") { - - dispatch(setIsSubmit(false)); - } - }, []); - - useEffect(() => { - setQueryInput(query); - setQueryValue([{ children: [{ text: query }] }]); - setQueryValid(onQueryValid(query)); - }, [query]); - - const onValueDisplay = (e) => { - e.preventDefault(); - const isOpen = labelsBrowserOpen ? false : true; - if (isOpen) dispatch(loadLabels(apiUrl)); - dispatch(setLabelsBrowserOpen(isOpen)); - }; - - function handleQueryChange(e) { - setQueryValue(e); - - const multiline = e.map((text) => text.children[0].text).join("\n"); - dispatch(setQuery(multiline)); - } - - const handleInputKeyDown = (e) => { - if (e.code === "Enter" && e.ctrlKey) { - dispatch(setLoading(true)); - onSubmit(e); - } - }; - - const onSubmit = (e) => { - e.preventDefault(); - - dispatch(setQuery(queryInput)); - - if (onQueryValid(queryInput)) { - try { - const historyUpdated = historyService.add({ - data: queryInput, - url: window.location.hash, - }); - dispatch(setQueryHistory(historyUpdated)); - dispatch(setLabelsBrowserOpen(false)); - decodeQuery(query, apiUrl); - dispatch(setLoading(true)); - dispatch(loadLogs()); - const storedUrl = saveUrl.add({ - data: window.location.href, - description: "From Query Submit", - }); - dispatch(setLinksHistory(storedUrl)); - } catch (e) { - console.log(e); - } - } else { - console.log("Please make a log query", query); - } - }; - function handleHistoryClick(e) { - dispatch(setHistoryOpen(!historyOpen)); - } - return ( - !isEmbed && ( -
- -
- - -
- - -
- - - - - - - - -
- - ) - - ); -}; +import React, { useState, useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { setIsSubmit, setQuery } from "../../actions"; +import loadLogs from "../../actions/loadLogs"; +import setLoading from "../../actions/setLoading"; +import { setLabelsBrowserOpen } from "../../actions/setLabelsBrowserOpen"; +import localService from "../../services/localService"; +import setQueryHistory from "../../actions/setQueryHistory"; + +import setHistoryOpen from "../../actions/setHistoryOpen"; +import localUrl from "../../services/localUrl"; +import setLinksHistory from "../../actions/setLinksHistory"; +import QueryEditor from "../../plugins/queryeditor"; + +import loadLabels from "../../actions/loadLabels"; +import { decodeQuery } from "../../helpers/UpdateStateFromQueryParams"; +import { css } from "@emotion/css"; +import { MobileTopQueryMenu, QueryBarContainer } from "./components/styled"; +import HistoryButton from "./components/HistoryButton/HistoryButton"; +import ShowLabelsButton from "./components/ShowLabelsButton/ShowLabelsButton"; +import ShowLogsButton from "./components/ShowLogsButton/ShowLogsButton"; +import queryInit from "./helpers/queryInit"; +import onQueryValid from "./helpers/onQueryValid"; +import debugLog from "./helpers/debugLog"; +import {sendLabels} from "../../hooks/useLabels"; + +export const QueryBar = () => { + const dispatch = useDispatch(); + const historyService = localService().historyStore(); + const labelsBrowserOpen = useSelector((store) => store.labelsBrowserOpen); + const debug = useSelector((store) => store.debugMode); + const query = useSelector((store) => store.query); + const apiUrl = useSelector((store) => store.apiUrl); + const isSubmit = useSelector((store) => store.isSubmit); + const historyOpen = useSelector((store) => store.historyOpen); + const isEmbed = useSelector( ( store ) => store.isEmbed) + const [queryInput, setQueryInput] = useState(query); + const [queryValid, setQueryValid] = useState(false); + const [queryValue, setQueryValue] = useState(queryInit(query)); + const labels = useSelector( store => store.labels) + const queryHistory = useSelector((store) => store.queryHistory); + const saveUrl = localUrl(); + useEffect(() => { + const dLog = debugLog(query); + debug && dLog.logicQueryBar(); + // labels first load from api url + const labels = sendLabels(apiUrl) + + if (onQueryValid(query && isSubmit === "true")) { + debug && dLog.queryBarDispatch(); + return labels.then( data => { + decodeQuery(query,apiUrl,data) + + }) + dispatch(setLoading(true)); + dispatch(loadLogs()); + + setTimeout(() => { + dispatch(setIsSubmit(false)); + + + + }, 200); + } else if (!onQueryValid(query) && isSubmit === "true") { + + dispatch(setIsSubmit(false)); + } + + return labels.then( data => { + decodeQuery(query,apiUrl,data) + + }) + + }, []); + + useEffect(() => { + setQueryInput(query); + setQueryValue([{ children: [{ text: query }] }]); + setQueryValid(onQueryValid(query)); + }, [query]); + + const onValueDisplay = (e) => { + e.preventDefault(); + const isOpen = labelsBrowserOpen ? false : true; + if (isOpen) dispatch(loadLabels(apiUrl)); + dispatch(setLabelsBrowserOpen(isOpen)); + }; + + function handleQueryChange(e) { + setQueryValue(e); + + const multiline = e.map((text) => text.children[0].text).join("\n"); + dispatch(setQuery(multiline)); + } + + const handleInputKeyDown = (e) => { + if (e.code === "Enter" && e.ctrlKey) { + dispatch(setLoading(true)); + onSubmit(e); + } + }; + + const onSubmit = (e) => { + e.preventDefault(); + + dispatch(setQuery(queryInput)); + + if (onQueryValid(queryInput)) { + try { + const historyUpdated = historyService.add({ + data: queryInput, + url: window.location.hash, + }); + dispatch(setQueryHistory(historyUpdated)); + dispatch(setLabelsBrowserOpen(false)); + decodeQuery(queryInput,apiUrl,labels) + + + dispatch(setLoading(true)); + dispatch(loadLogs()); + const storedUrl = saveUrl.add({ + data: window.location.href, + description: "From Query Submit", + }); + dispatch(setLinksHistory(storedUrl)); + } catch (e) { + console.log(e); + } + } else { + console.log("Please make a log query", query); + } + }; + function handleHistoryClick(e) { + dispatch(setHistoryOpen(!historyOpen)); + } + return ( + !isEmbed && ( +
+ +
+ + +
+ + +
+ + + + + + + + +
+ + ) + + ); +}; diff --git a/src/components/LabelBrowser/ValuesList.js b/src/components/LabelBrowser/ValuesList.js index 03842023..44a53067 100644 --- a/src/components/LabelBrowser/ValuesList.js +++ b/src/components/LabelBrowser/ValuesList.js @@ -1,222 +1,222 @@ -import React, { useState, useEffect } from "react"; -import { Legend } from "./Legend"; -import { useSelector, useDispatch } from "react-redux"; -import RefreshIcon from "@mui/icons-material/Refresh"; -import loadLabels from "../../actions/LoadLabels"; -import { queryBuilder } from "./helpers/querybuilder"; -import { setQuery } from "../../actions"; -import loadLabelValues from "../../actions/loadLabelValues"; - -import Tooltip from "@mui/material/Tooltip"; -import store from "../../store/store"; -import styled from "@emotion/styled"; - -const ErrorContainer = styled.div` - padding: 20px; - display: flex; - align-items: center; - justify-content: center; -`; - -const LabelErrorStyled = styled.div` - padding: 10px; - color: orangered; - border: 1px solid orangered; - border-radius: 3px; - font-size: 1em; -`; - -export const LabelsFetchError = () => { - const labelError = useSelector((store) => store.apiErrors); - - return ( - - {labelError !== "" && ( - - {labelError} - - )} - - ); -}; - -export const ValuesList = (props) => { - const [labelsSelected, setLabelsSelected] = useState([]); - const labels = useSelector((state) => { - const selected = state.labels.filter((f) => f.selected); - if (JSON.stringify(selected) !== JSON.stringify(labelsSelected)) { - setLabelsSelected(selected); - } - return state.labels; - }); - - const [labelList, setLabelList] = useState(labels); - - const dispatch = useDispatch(); - const debug = useSelector((store) => store.debug); - const apiUrl = useSelector((store) => store.apiUrl); - //if(debug) console.log('🚧 LOGIC/LabelBrowser/ValuesList', apiUrl) - const labelsBrowserOpen = useSelector((store) => store.labelsBrowserOpen); - - const CLEAR = "clear"; - - useEffect(() => { - dispatch(loadLabels(apiUrl)); - // setLabelList(labels) - }, [apiUrl]); - - useEffect(() => { - setLabelList(labels); // LABELS - }, [labels]); - - const handleRefresh = (e) => { - e.preventDefault(); - dispatch(loadLabels(apiUrl)); - }; - - const onLabelOpen = (e, value) => { - e.preventDefault(); - value.selected = !value.selected; - // setLabel(value); - const selected = labelList.filter((f) => f.selected); - setLabelsSelected(selected); - - // setFilteredPlaceholder(value); - - const query = queryBuilder(labelList); - dispatch(setQuery(query)); - - //this.setState({ ...this.state, query }); - - dispatch(loadLabelValues(value, labelList, apiUrl)); - // loads label values into labelList - }; - - const onLabelValueClick = (e, value) => { - e.preventDefault(); - value.selected = !value.selected; - value.inverted = false; - onLabelValueChange(); - }; - - const selectedList = () => { - return labelsSelected.length > 0; - }; - const onLabelValueChange = () => { - const query = queryBuilder(labels); - dispatch(setQuery(query)); - }; - - const styleValue = (value) => { - if (value.selected) { - return { - borderColor: "#11abab", - color: "#11abab", - }; - } else return {}; - }; - return ( - labelsBrowserOpen && ( -
-
- {labelList.length > 0 ? ( -
- - -
- - {labelList && - labelList?.map((value, key) => ( - - onLabelOpen(e, value) - } - > - {value.name} - - ))} -
-
- ) : ( - - )} - - {selectedList() && ( -
-
- {labelsSelected.map((labelSelected, skey) => ( -
-
- - {labelSelected.name} ( - {labelSelected.values.length}) - - - onLabelOpen( - e, - labelSelected - ) - } - > - {CLEAR} - -
-
- {labelSelected?.values?.map( - (value, key) => ( - - - onLabelValueClick( - e, - value - ) - } - > - {value.name} - - - ) - )} -
-
- ))} -
-
- )} -
-
- ) - ); -}; +import React, { useState, useEffect } from "react"; +import { Legend } from "./Legend"; +import { useSelector, useDispatch } from "react-redux"; +import RefreshIcon from "@mui/icons-material/Refresh"; +import loadLabels from "../../actions/loadLabels"; +import { queryBuilder } from "./helpers/querybuilder"; +import { setQuery } from "../../actions"; +import loadLabelValues from "../../actions/loadLabelValues"; + +import Tooltip from "@mui/material/Tooltip"; +import store from "../../store/store"; +import styled from "@emotion/styled"; + +const ErrorContainer = styled.div` + padding: 20px; + display: flex; + align-items: center; + justify-content: center; +`; + +const LabelErrorStyled = styled.div` + padding: 10px; + color: orangered; + border: 1px solid orangered; + border-radius: 3px; + font-size: 1em; +`; + +export const LabelsFetchError = () => { + const labelError = useSelector((store) => store.apiErrors); + + return ( + + {labelError !== "" && ( + + {labelError} + + )} + + ); +}; + +export const ValuesList = (props) => { + const [labelsSelected, setLabelsSelected] = useState([]); + const labels = useSelector((state) => { + const selected = state.labels.filter((f) => f.selected); + if (JSON.stringify(selected) !== JSON.stringify(labelsSelected)) { + setLabelsSelected(selected); + } + return state.labels; + }); + + const [labelList, setLabelList] = useState(labels); + + const dispatch = useDispatch(); + const debug = useSelector((store) => store.debug); + const apiUrl = useSelector((store) => store.apiUrl); + //if(debug) console.log('🚧 LOGIC/LabelBrowser/ValuesList', apiUrl) + const labelsBrowserOpen = useSelector((store) => store.labelsBrowserOpen); + + const CLEAR = "clear"; + + useEffect(() => { + dispatch(loadLabels(apiUrl)); + // setLabelList(labels) + }, [apiUrl]); + + useEffect(() => { + setLabelList(labels); // LABELS + }, [labels]); + + const handleRefresh = (e) => { + e.preventDefault(); + dispatch(loadLabels(apiUrl)); + }; + + const onLabelOpen = (e, value) => { + e.preventDefault(); + value.selected = !value.selected; + // setLabel(value); + const selected = labelList.filter((f) => f.selected); + setLabelsSelected(selected); + + // setFilteredPlaceholder(value); + + const query = queryBuilder(labelList); + dispatch(setQuery(query)); + + //this.setState({ ...this.state, query }); + + dispatch(loadLabelValues(value, labelList, apiUrl)); + // loads label values into labelList + }; + + const onLabelValueClick = (e, value) => { + e.preventDefault(); + value.selected = !value.selected; + value.inverted = false; + onLabelValueChange(); + }; + + const selectedList = () => { + return labelsSelected.length > 0; + }; + const onLabelValueChange = () => { + const query = queryBuilder(labels); + dispatch(setQuery(query)); + }; + + const styleValue = (value) => { + if (value.selected) { + return { + borderColor: "#11abab", + color: "#11abab", + }; + } else return {}; + }; + return ( + labelsBrowserOpen && ( +
+
+ {labelList.length > 0 ? ( +
+ + +
+ + {labelList && + labelList?.map((value, key) => ( + + onLabelOpen(e, value) + } + > + {value.name} + + ))} +
+
+ ) : ( + + )} + + {selectedList() && ( +
+
+ {labelsSelected.map((labelSelected, skey) => ( +
+
+ + {labelSelected.name} ( + {labelSelected.values.length}) + + + onLabelOpen( + e, + labelSelected + ) + } + > + {CLEAR} + +
+
+ {labelSelected?.values?.map( + (value, key) => ( + + + onLabelValueClick( + e, + value + ) + } + > + {value.name} + + + ) + )} +
+
+ ))} +
+
+ )} +
+
+ ) + ); +}; diff --git a/src/components/LabelBrowser/helpers/querybuilder.js b/src/components/LabelBrowser/helpers/querybuilder.js index 5af1c501..6116f211 100644 --- a/src/components/LabelBrowser/helpers/querybuilder.js +++ b/src/components/LabelBrowser/helpers/querybuilder.js @@ -1,38 +1,41 @@ -import { setQuery } from "../../../actions"; -import store from "../../../store/store"; - -export function queryBuilder(labels) { - const actualQuery = store.getState().query - const preTags = actualQuery.split("{")[0] - const postTags = actualQuery.split("}")[1] - const selectedLabels = []; - for (const label of labels) { - if (label.selected && label.values && label.values.length > 0) { - const selectedValues = label.values - .filter((value) => value.selected && !value.inverted) - .map((value) => value.name); - const invertedSelectedValues = label.values - .filter((value) => value.selected && value.inverted) - .map((value) => value.name); - - if (selectedValues.length > 1) { - selectedLabels.push( - `${label.name}=~"${selectedValues.join("|")}"` - ); - } else if (selectedValues.length === 1) { - selectedLabels.push(`${label.name}="${selectedValues[0]}"`); - } - invertedSelectedValues.forEach(value => { - selectedLabels.push(`${label.name}!="${value}"`) - }); - - } - } - return [preTags,"{", selectedLabels.join(","), "}",postTags].join(""); -} -export function queryBuilderWithLabels() { - const labels = store.getState().labels; - - const query = queryBuilder(labels) - store.dispatch(setQuery(query)); -} +import { setQuery } from "../../../actions"; +import store from "../../../store/store"; + +export function queryBuilder(labels) { + const actualLabels = store.getState().labels + const actualQuery = store.getState().query + // console.log(actualLabels) + const preTags = actualQuery.split("{")[0] + const postTags = actualQuery.split("}")[1] + const selectedLabels = []; + for (const label of labels) { + if (label.selected && label.values && label.values.length > 0) { + const selectedValues = label.values + .filter((value) => value.selected && !value.inverted) + .map((value) => value.name); + const invertedSelectedValues = label.values + .filter((value) => value.selected && value.inverted) + .map((value) => value.name); + + if (selectedValues.length > 1) { + selectedLabels.push( + `${label.name}=~"${selectedValues.join("|")}"` + ); + } else if (selectedValues.length === 1) { + selectedLabels.push(`${label.name}="${selectedValues[0]}"`); + } + invertedSelectedValues.forEach(value => { + selectedLabels.push(`${label.name}!="${value}"`) + }); + + } + } + return [preTags,"{", selectedLabels.join(","), "}",postTags].join(""); +} + +export function queryBuilderWithLabels() { + const labels = store.getState().labels; + + const query = queryBuilder(labels) + store.dispatch(setQuery(query)); +} diff --git a/src/components/LabelBrowser/helpers/useLabelsFromQuery.js b/src/components/LabelBrowser/helpers/useLabelsFromQuery.js new file mode 100644 index 00000000..dc77a8fa --- /dev/null +++ b/src/components/LabelBrowser/helpers/useLabelsFromQuery.js @@ -0,0 +1,16 @@ +import { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; + +export default function useLabelsFromQuery() { + const actualQuery = useSelector((store) => store.query); + const [labels, setLabels] = useState([]); + + function getLabels(query) { + return query.split(/[{}]/); + } +useEffect(()=>{ +setLabels(getLabels(actualQuery)) +},[actualQuery]) + + return labels; +} diff --git a/src/components/StatusBar/components/apiselector/ApiSelector.js b/src/components/StatusBar/components/apiselector/ApiSelector.js index c8045cf4..582a1da0 100644 --- a/src/components/StatusBar/components/apiselector/ApiSelector.js +++ b/src/components/StatusBar/components/apiselector/ApiSelector.js @@ -1,86 +1,86 @@ -import { useDispatch, useSelector } from "react-redux"; -import { useState, useEffect } from "react"; -import loadLabels from "../../../../actions/LoadLabels"; -import { setLabelsBrowserOpen } from "../../../../actions/setLabelsBrowserOpen"; -import setMatrixData from "../../../../actions/setMatrixData"; -import loadLogs from "../../../../actions/loadLogs"; -import { setApiUrl } from "../../../../actions"; -import LinkIcon from "@mui/icons-material/Link"; -import setLogs from "../../../../actions/setLogs"; -import { - ApiSelectorButton, - ApiSelectorInput, - ApiSelectorStyled, -} from "../../styled"; - -export function ApiSelector() { - const apiUrl = useSelector((store) => store.apiUrl); - const apiError = useSelector((store) => store.apiErrors); - const [editedUrl, setEditedUrl] = useState(apiUrl); - const query = useSelector((store) => store.query); - const [apiSelectorOpen, setApiSelectorOpen] = useState(false); - const dispatch = useDispatch(); - const API_URL = "API URL"; - useEffect(() => { - setEditedUrl(apiUrl); - }, []); - - useEffect(() => { - setEditedUrl(apiUrl); - }, [apiUrl]); - - useEffect(() => { - if (apiError.length > 0) { - setApiSelectorOpen(true); - dispatch(setLogs([])); - dispatch(setMatrixData([])); - } else { - setApiSelectorOpen(false); - dispatch(loadLogs()); - } - }, [apiError]); - - const handleApiUrlOpen = (e = null) => { - e?.preventDefault(); - apiSelectorOpen ? setApiSelectorOpen(false) : setApiSelectorOpen(true); - }; - - const handleIntputChange = (e) => { - e.preventDefault(); - setEditedUrl(e.target.value); - }; - const onUrlSubmit = (e) => { - dispatch(setApiUrl(editedUrl)); - dispatch(loadLabels(editedUrl)); - if (query?.length > 3) { - dispatch(setLabelsBrowserOpen(false)); - } - }; - - return ( - - - - - ● - - - {apiSelectorOpen ? ( -
- {API_URL} - - - {"save"} - -
- ) : null} -
- ); -} +import { useDispatch, useSelector } from "react-redux"; +import { useState, useEffect } from "react"; +import loadLabels from "../../../../actions/loadLabels"; +import { setLabelsBrowserOpen } from "../../../../actions/setLabelsBrowserOpen"; +import setMatrixData from "../../../../actions/setMatrixData"; +import loadLogs from "../../../../actions/loadLogs"; +import { setApiUrl } from "../../../../actions"; +import LinkIcon from "@mui/icons-material/Link"; +import setLogs from "../../../../actions/setLogs"; +import { + ApiSelectorButton, + ApiSelectorInput, + ApiSelectorStyled, +} from "../../styled"; + +export function ApiSelector() { + const apiUrl = useSelector((store) => store.apiUrl); + const apiError = useSelector((store) => store.apiErrors); + const [editedUrl, setEditedUrl] = useState(apiUrl); + const query = useSelector((store) => store.query); + const [apiSelectorOpen, setApiSelectorOpen] = useState(false); + const dispatch = useDispatch(); + const API_URL = "API URL"; + useEffect(() => { + setEditedUrl(apiUrl); + }, []); + + useEffect(() => { + setEditedUrl(apiUrl); + }, [apiUrl]); + + useEffect(() => { + if (apiError.length > 0) { + setApiSelectorOpen(true); + dispatch(setLogs([])); + dispatch(setMatrixData([])); + } else { + setApiSelectorOpen(false); + dispatch(loadLogs()); + } + }, [apiError]); + + const handleApiUrlOpen = (e = null) => { + e?.preventDefault(); + apiSelectorOpen ? setApiSelectorOpen(false) : setApiSelectorOpen(true); + }; + + const handleIntputChange = (e) => { + e.preventDefault(); + setEditedUrl(e.target.value); + }; + const onUrlSubmit = (e) => { + dispatch(setApiUrl(editedUrl)); + dispatch(loadLabels(editedUrl)); + if (query?.length > 3) { + dispatch(setLabelsBrowserOpen(false)); + } + }; + + return ( + + + + + ● + + + {apiSelectorOpen ? ( +
+ {API_URL} + + + {"save"} + +
+ ) : null} +
+ ); +} diff --git a/src/helpers/UpdateStateFromQueryParams.js b/src/helpers/UpdateStateFromQueryParams.js index 91067275..14e23c2d 100644 --- a/src/helpers/UpdateStateFromQueryParams.js +++ b/src/helpers/UpdateStateFromQueryParams.js @@ -1,303 +1,314 @@ -import * as moment from "moment"; -import { useEffect } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { useLocation } from "react-router-dom"; -import { - setApiUrl, - setIsSubmit, - setLabels, - setQuery, - setQueryLimit, - setQueryStep, - setStartTime, - setStopTime, - -} from "../actions"; - - -import loadLabels from "../actions/LoadLabels"; -import loadLabelValues from "../actions/loadLabelValues"; -import setFromTime from "../actions/setFromTime"; -import setIsEmbed from "../actions/setIsEmbed"; -import { setLabelsBrowserOpen } from "../actions/setLabelsBrowserOpen"; -import setToTime from "../actions/setToTime"; -import { setUrlLocation } from "../actions/setUrlLocation"; -import { setUrlQueryParams } from "../actions/setUrlQueryParams"; -import { environment } from "../environment/env.dev"; -import store from "../store/store"; -export function UpdateStateFromQueryParams() { - const { hash } = useLocation(); - - const dispatch = useDispatch(); - const urlQueryParams = useSelector((store) => store.urlQueryParams); - const start = useSelector((store) => store.start); - const stop = useSelector((store) => store.stop); - const limit = useSelector((store) => store.limit); - const from = useSelector((store) => store.from); - const to = useSelector((store)=> store.to); - const step = useSelector((store) => store.step); - const apiUrl = useSelector((store) => store.apiUrl); - const isSubmit = useSelector((store) => store.isSubmit); - const isEmbed = useSelector((store) => store.isEmbed); - const query = useSelector((store) => store.query); - - const STORE_KEYS = { - apiUrl, - query, - start, - limit, - step, - end: stop, - from, - to, - isSubmit, - isEmbed, - }; - - const STORE_ACTIONS = { - apiUrl: setApiUrl, - query: setQuery, - start: setStartTime, - limit: setQueryLimit, - step: setQueryStep, - end: setStopTime, - from: setFromTime, - to: setToTime, - isSubmit: setIsSubmit, - isEmbed: setIsEmbed, - - }; - - const STRING_VALUES = ["limit", "step", "apiUrl"]; - - const QUERY_VALUE = "query"; - - const TIME_VALUES = ["start", "end",]; - - const BOOLEAN_VALUES = ["isSubmit", "isEmbed"]; - - const encodeTs = (ts) => { - return ts.getTime() + "000000"; - }; - - useEffect(() => { - const urlFromHash = new URLSearchParams(hash.replace("#", "")); - - // !if there is some params set them first on UI - - if (hash.length > 0) { - const startParams = urlQueryParams; - - for (let [key, value] of urlFromHash.entries()) { - startParams[key] = value; - } - if (Object.keys(startParams).length > 0) { - dispatch(setUrlQueryParams({ ...urlQueryParams, startParams })); - - dispatch(setUrlLocation(hash)); - Object.keys(startParams).forEach((param) => { - if ( - STRING_VALUES.includes(param) && - startParams[param] !== "" - ) { - dispatch(STORE_ACTIONS[param](startParams[param])); - } else if ( - QUERY_VALUE === param && - startParams[param] !== "" - ) { - const parsedQuery = decodeURIComponent( - startParams[param] - ); - dispatch(STORE_ACTIONS[param](parsedQuery)); - } else if ( - TIME_VALUES.includes(param) && - startParams[param] !== "" - ) { - const croppedTime = startParams[param] / 1000000; - const paramDate = new Date( - moment(croppedTime).format( - "YYYY-MM-DDTHH:mm:ss.SSSZ" - ) - ); - dispatch(STORE_ACTIONS[param](paramDate)); - } else if ( - BOOLEAN_VALUES.includes(param) && - typeof param === "boolean" - ) { - dispatch(STORE_ACTIONS[param](startParams[param])); - } - if (QUERY_VALUE === param) { - } - }); - decodeQuery(decodeURIComponent(startParams.query), apiUrl); - dispatch(setLabelsBrowserOpen(false)); - } - } else { - dispatch(setApiUrl(environment.apiUrl)); - - const allParams = STRING_VALUES.concat(TIME_VALUES); - allParams.push(QUERY_VALUE); - allParams.push(BOOLEAN_VALUES); - allParams.forEach((param) => { - if (STRING_VALUES.includes(param, "PARAM")) { - urlFromHash.set(param, STORE_KEYS[param].toString()); - } else if (TIME_VALUES.includes(param)) { - const time_value = STORE_KEYS[param]?.getTime() * 1000000; - - urlFromHash.set(param, time_value.toString()); - } else if (QUERY_VALUE === param) { - const parsed = encodeURIComponent( - STORE_KEYS[param] - ).toString(); - urlFromHash.set(param, parsed.toString()); - } else if ( - BOOLEAN_VALUES === param && - typeof param === "boolean" - ) { - urlFromHash.set(param, param.toString()); - } - }); - const newQuery = STORE_KEYS[query]; - decodeQuery(newQuery, apiUrl); - - window.location.hash = urlFromHash; - } - }, []); - - useEffect(() => { - if (hash.length > 0) { - const paramsFromHash = new URLSearchParams(hash.replace("#", "")); - let previousParams = {}; - for (let [key, value] of paramsFromHash.entries()) { - previousParams[key] = value; - } - - Object.keys(STORE_KEYS).forEach((store_key) => { - if ( - STRING_VALUES.includes(store_key) && - previousParams[store_key] !== STORE_KEYS[store_key] - ) { - const updated = STORE_KEYS[store_key].toString().trim(); - paramsFromHash.set(store_key, updated); - } else if ( - QUERY_VALUE === store_key && - previousParams[store_key] !== - encodeURIComponent(STORE_KEYS[store_key]) - ) { - const queryUpdated = encodeURIComponent( - STORE_KEYS[store_key].toString() - ); - paramsFromHash.set(store_key, queryUpdated); - } else if ( - TIME_VALUES.includes(store_key) && - previousParams[store_key] !== - encodeTs(STORE_KEYS[store_key]) - ) { - const encodedTs = encodeTs(STORE_KEYS[store_key]); - paramsFromHash.set(store_key, encodedTs); - } else if ( - BOOLEAN_VALUES === store_key && - previousParams[store_key] !== STORE_KEYS[store_key] - ) { - paramsFromHash.set(store_key, STORE_KEYS[store_key]); - } - }); - window.location.hash = paramsFromHash; - } - }, [STORE_KEYS]); -} - -export async function decodeQuery(query, apiUrl) { - await store.dispatch(loadLabels(apiUrl)); - const queryArr = query - ?.match(/[^{\}]+(?=})/g, "$1") - ?.map((m) => m.split(",")) - ?.flat(); - const labelsFromQuery = []; - queryArr?.forEach((label) => { - const regexQuery = label.match(/([^{}=,~!]+)/gm); - if (!regexQuery) { - return; - } - if (label.includes("!=")) { - const labelObj = { - name: regexQuery[0], - values: [], - }; - const valueObj = { - name: regexQuery[1]?.replaceAll('"', ""), - selected: true, - inverted: true, - }; - labelObj.values.push(valueObj); - labelsFromQuery.push(labelObj); - } else if (label.includes("=~")) { - const values = regexQuery[1]?.split("|"); - const labelObj = { - name: regexQuery[0], - values: [], - }; - values.forEach((value) => { - const valueObj = { - name: value?.replaceAll('"', ""), - selected: true, - inverted: false, - }; - labelObj.values.push(valueObj); - }); - labelsFromQuery.push(labelObj); - } else { - const labelObj = { - name: regexQuery[0], - values: [], - }; - const valueObj = { - name: regexQuery[1]?.replaceAll('"', ""), - selected: true, - inverted: false, - }; - labelObj.values.push(valueObj); - labelsFromQuery.push(labelObj); - } - }); - const newLabels = store.getState().labels; - newLabels?.forEach((label) => { - if (label.selected && label.values > 0) { - label.selected = false; - label.values.forEach((value) => { - if (value.selected) { - value.selected = false; - } - }); - } - }); - - if (labelsFromQuery.length > 0) { - labelsFromQuery.forEach(async (label) => { - const cleanLabel = newLabels?.find( - (item) => item?.name === label?.name - ); - if (!cleanLabel) { - return; - } - await store.dispatch( - loadLabelValues(cleanLabel, newLabels, apiUrl) - ); - const labelsWithValues = store.getState().labels; - const labelWithValues = labelsWithValues.find( - (item) => item?.name === label?.name - ); - let values = labelWithValues.values; - values = label.values.concat(values); - values = values - .sort((a, b) => a.name.localeCompare(b.name)) - .filter((value, index, arr) => { - return value.name !== arr[index - 1]?.name; - }) - .filter((value) => !!value); - labelWithValues.values = values; - labelWithValues.selected = true; - store.dispatch(setLabels(labelsWithValues)); - }); - } -} +import * as moment from "moment"; +import { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useLocation } from "react-router-dom"; +import { + setApiUrl, + setIsSubmit, + setLabels, + setQuery, + setQueryLimit, + setQueryStep, + setStartTime, + setStopTime, + +} from "../actions"; + +import loadLabels from "../actions/loadLabels" + +import loadLabelValues from "../actions/loadLabelValues"; +import setFromTime from "../actions/setFromTime"; +import setIsEmbed from "../actions/setIsEmbed"; +import { setLabelsBrowserOpen } from "../actions/setLabelsBrowserOpen"; +import setToTime from "../actions/setToTime"; +import { setUrlLocation } from "../actions/setUrlLocation"; +import { setUrlQueryParams } from "../actions/setUrlQueryParams"; +import { environment } from "../environment/env.dev"; +import store from "../store/store"; +export function UpdateStateFromQueryParams() { + const { hash } = useLocation(); + + const dispatch = useDispatch(); + const urlQueryParams = useSelector((store) => store.urlQueryParams); + const start = useSelector((store) => store.start); + const stop = useSelector((store) => store.stop); + const limit = useSelector((store) => store.limit); + const from = useSelector((store) => store.from); + const to = useSelector((store)=> store.to); + const step = useSelector((store) => store.step); + const labels = useSelector((store) => store.labels) + const apiUrl = useSelector((store) => store.apiUrl); + const isSubmit = useSelector((store) => store.isSubmit); + const isEmbed = useSelector((store) => store.isEmbed); + const query = useSelector((store) => store.query); + + const STORE_KEYS = { + apiUrl, + query, + start, + limit, + step, + end: stop, + from, + to, + isSubmit, + isEmbed, + }; + + const STORE_ACTIONS = { + apiUrl: setApiUrl, + query: setQuery, + start: setStartTime, + limit: setQueryLimit, + step: setQueryStep, + end: setStopTime, + from: setFromTime, + to: setToTime, + isSubmit: setIsSubmit, + isEmbed: setIsEmbed, + + }; + + const STRING_VALUES = ["limit", "step", "apiUrl"]; + + const QUERY_VALUE = "query"; + + const TIME_VALUES = ["start", "end",]; + + const BOOLEAN_VALUES = ["isSubmit", "isEmbed"]; + + const encodeTs = (ts) => { + return ts.getTime() + "000000"; + }; + + useEffect(() => { + const urlFromHash = new URLSearchParams(hash.replace("#", "")); + + // !if there is some params set them first on UI + + if (hash.length > 0) { + const startParams = urlQueryParams; + + for (let [key, value] of urlFromHash.entries()) { + startParams[key] = value; + } + if (Object.keys(startParams).length > 0) { + dispatch(setUrlQueryParams({ ...urlQueryParams, startParams })); + + dispatch(setUrlLocation(hash)); + Object.keys(startParams).forEach((param) => { + if ( + STRING_VALUES.includes(param) && + startParams[param] !== "" + ) { + + dispatch(STORE_ACTIONS[param](startParams[param])); + if(param === 'apiUrl') { + dispatch(loadLabels(startParams[param])) + } + } else if ( + QUERY_VALUE === param && + startParams[param] !== "" + ) { + const parsedQuery = decodeURIComponent( + startParams[param] + ); + dispatch(STORE_ACTIONS[param](parsedQuery)); + } else if ( + TIME_VALUES.includes(param) && + startParams[param] !== "" + ) { + const croppedTime = startParams[param] / 1000000; + const paramDate = new Date( + moment(croppedTime).format( + "YYYY-MM-DDTHH:mm:ss.SSSZ" + ) + ); + dispatch(STORE_ACTIONS[param](paramDate)); + } else if ( + BOOLEAN_VALUES.includes(param) && + typeof param === "boolean" + ) { + dispatch(STORE_ACTIONS[param](startParams[param])); + } + if (QUERY_VALUE === param) { + } + }); + + decodeQuery(decodeURIComponent(startParams.query),apiUrl,labels); + dispatch(setLabelsBrowserOpen(false)); + + } + } else { + dispatch(setApiUrl(environment.apiUrl)); + + const allParams = STRING_VALUES.concat(TIME_VALUES); + allParams.push(QUERY_VALUE); + allParams.push(BOOLEAN_VALUES); + allParams.forEach((param) => { + if (STRING_VALUES.includes(param, "PARAM")) { + urlFromHash.set(param, STORE_KEYS[param].toString()); + } else if (TIME_VALUES.includes(param)) { + const time_value = STORE_KEYS[param]?.getTime() * 1000000; + + urlFromHash.set(param, time_value.toString()); + } else if (QUERY_VALUE === param) { + const parsed = encodeURIComponent( + STORE_KEYS[param] + ).toString(); + urlFromHash.set(param, parsed.toString()); + } else if ( + BOOLEAN_VALUES === param && + typeof param === "boolean" + ) { + urlFromHash.set(param, param.toString()); + } + }); + const newQuery = STORE_KEYS[query]; + + decodeQuery(newQuery, apiUrl, labels); + + window.location.hash = urlFromHash; + } + }, []); + + useEffect(() => { + if (hash.length > 0) { + const paramsFromHash = new URLSearchParams(hash.replace("#", "")); + let previousParams = {}; + for (let [key, value] of paramsFromHash.entries()) { + previousParams[key] = value; + } + + Object.keys(STORE_KEYS).forEach((store_key) => { + if ( + STRING_VALUES.includes(store_key) && + previousParams[store_key] !== STORE_KEYS[store_key] + ) { + const updated = STORE_KEYS[store_key].toString().trim(); + paramsFromHash.set(store_key, updated); + } else if ( + QUERY_VALUE === store_key && + previousParams[store_key] !== + encodeURIComponent(STORE_KEYS[store_key]) + ) { + const queryUpdated = encodeURIComponent( + STORE_KEYS[store_key].toString() + ); + paramsFromHash.set(store_key, queryUpdated); + } else if ( + TIME_VALUES.includes(store_key) && + previousParams[store_key] !== + encodeTs(STORE_KEYS[store_key]) + ) { + const encodedTs = encodeTs(STORE_KEYS[store_key]); + paramsFromHash.set(store_key, encodedTs); + } else if ( + BOOLEAN_VALUES === store_key && + previousParams[store_key] !== STORE_KEYS[store_key] + ) { + paramsFromHash.set(store_key, STORE_KEYS[store_key]); + } + }); + window.location.hash = paramsFromHash; + } + }, [STORE_KEYS]); +} + +export function decodeQuery(query, apiUrl, labels=[]) { + + const queryArr = query + ?.match(/[^{\}]+(?=})/g, "$1") + ?.map((m) => m.split(",")) + ?.flat(); + const labelsFromQuery = []; + queryArr?.forEach((label) => { + const regexQuery = label.match(/([^{}=,~!]+)/gm); + if (!regexQuery) { + return; + } + if (label.includes("!=")) { + const labelObj = { + name: regexQuery[0], + values: [], + }; + const valueObj = { + name: regexQuery[1]?.replaceAll('"', ""), + selected: true, + inverted: true, + }; + labelObj.values.push(valueObj); + labelsFromQuery.push(labelObj); + } else if (label.includes("=~")) { + const values = regexQuery[1]?.split("|"); + const labelObj = { + name: regexQuery[0], + values: [], + }; + values.forEach((value) => { + const valueObj = { + name: value?.replaceAll('"', ""), + selected: true, + inverted: false, + }; + labelObj.values.push(valueObj); + }); + labelsFromQuery.push(labelObj); + } else { + const labelObj = { + name: regexQuery[0], + values: [], + }; + const valueObj = { + name: regexQuery[1]?.replaceAll('"', ""), + selected: true, + inverted: false, + }; + labelObj.values.push(valueObj); + labelsFromQuery.push(labelObj); + + } + }); + + const newLabels = labels + + newLabels?.forEach((label) => { + if (label.selected && label.values > 0) { + label.selected = false; + label.values.forEach((value) => { + if (value.selected) { + value.selected = false; + } + }); + } + }); + + if (labelsFromQuery.length > 0) { + labelsFromQuery.forEach(async (label) => { + const cleanLabel = newLabels?.find( + (item) => item?.name === label?.name + ); + if (!cleanLabel) { + return; + } + await store.dispatch( + loadLabelValues(cleanLabel, newLabels, apiUrl) + ); + const labelsWithValues = store.getState().labels; + const labelWithValues = labelsWithValues.find( + (item) => item?.name === label?.name + ); + let values = labelWithValues.values; + values = label.values.concat(values); + values = values + .sort((a, b) => a.name.localeCompare(b.name)) + .filter((value, index, arr) => { + return value.name !== arr[index - 1]?.name; + }) + .filter((value) => !!value); + labelWithValues.values = values; + labelWithValues.selected = true; + store.dispatch(setLabels(labelsWithValues)); + }); + } +} diff --git a/src/hooks/useLabels.js b/src/hooks/useLabels.js new file mode 100644 index 00000000..acee6c05 --- /dev/null +++ b/src/hooks/useLabels.js @@ -0,0 +1,47 @@ + +import axios from "axios"; +export const sendLabels = async (apiUrl) => { + const origin = window.location.origin; + const url = apiUrl; + const headers = { + "Access-Control-Allow-Origin": origin, + "Access-Control-Allow-Headers": [ + "Access-Control-Request-Headers", + "Content-Type", + ], + "Content-Type": "application/json", + }; + + const options = { + method: "GET", + headers: headers, + mode: "cors", + }; + + const res = await axios + .get(`${url.trim()}/loki/api/v1/labels`, options) + .then((response) => { + if (response) { + if (response?.data?.data === []) console.log("no labels found"); + if (response?.data?.data?.length > 0) { + const labels = response?.data?.data.sort().map((label) => ({ + name: label, + selected: false, + values: [], + })); + + + return labels || [] + + } + } else { + + return [] + } + }) + .catch((e) => console.log(e)); + +return res + +}; + From 79917158fcc55dba51ed55c57ac220cb79996c68 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Wed, 20 Apr 2022 21:15:32 +0200 Subject: [PATCH 03/14] fix labels blocking --- src/actions/LoadLabels.js | 7 +- src/actions/errorHandler.js | 94 +-- src/actions/loadLabelValues.js | 138 ++-- src/actions/loadLogs.js | 273 ++++---- src/components/DataView/ValueTags.js | 2 +- src/components/LabelBrowser/QueryBar.js | 122 ++-- src/components/LabelBrowser/ValuesList.js | 26 +- .../ShowLabelsButton/ShowLabelsButton.js | 52 +- .../LabelBrowser/components/styled/index.js | 5 + src/components/LabelBrowser/index.js | 28 +- .../components/apiselector/ApiSelector.js | 7 +- .../components/daterangepicker/index.js | 578 ++++++++-------- src/helpers/UpdateStateFromQueryParams.js | 3 +- src/helpers/error.interceptor.js | 54 +- src/plugins/charts/index.js | 654 +++++++++--------- src/store/createInitialState.js | 136 ++-- 16 files changed, 1111 insertions(+), 1068 deletions(-) diff --git a/src/actions/LoadLabels.js b/src/actions/LoadLabels.js index 7f5a1c1b..f786c533 100644 --- a/src/actions/LoadLabels.js +++ b/src/actions/LoadLabels.js @@ -29,7 +29,6 @@ export default function loadLabels(apiUrl) { ?.then((response) => { if (response) { if (response?.data?.data === []) - console.log("no labels found"); if (response?.data?.data?.length > 0) { const labels = response?.data?.data .sort() @@ -44,16 +43,16 @@ export default function loadLabels(apiUrl) { } else { dispatch(setLoading(false)); dispatch( - setApiError("API Not Found, Please adjust API URL") + setApiError("") ); - dispatch(setLabelsBrowserOpen(true)); dispatch(setLabels([])); } }) .catch((error) => { console.log(error); dispatch(setLoading(false)); - const { message, status } = errorHandler(url, error); + const { message, status } = errorHandler(url, error,'labels'); + dispatch(setApiError(`Status: ${status}, ${message}`)); dispatch(setLabels([])); }); diff --git a/src/actions/errorHandler.js b/src/actions/errorHandler.js index dc040567..d4af4162 100644 --- a/src/actions/errorHandler.js +++ b/src/actions/errorHandler.js @@ -1,41 +1,53 @@ -export const errorHandler = (url, error) => { - const { request, response } = error; - - if (response?.statusText) { - const status = response?.status; - return { - message: "API " + response.statusText + ", Please adjust API URL", - status, - }; - } else if (!url.includes(window.location.protocol)) { - return { - message: "Mixed Content Error, your View should be over same protocol as your API", - status: 500, - }; - } else if (request) { - if(error.stack.includes('Network Error')) { - return { - message: "Invalid API URL, please adjust API URL",status:500 - } - } - return { - message: "server time out", - status: response?.status, - }; - } else if (error.stack.includes("Invalid URL")) { - return { - message: "Invalid API URL, please adjust API URL", - stauts: response?.status, - }; - } else if (error?.response?.status === 404) { - return { - message: "Invalid API URL, please adjust API URL", - status: response?.status, - }; - } else { - return { - message: "something went wrong with request", - status: response?.status, - }; - } -}; +export const errorHandler = (url, error, type) => { + const { request, response } = error; + console.log(url) + console.log(error) + if (response?.statusText) { + const status = response?.status; + + return { + message: "API " + response.statusText + ", Please adjust API URL", + status, + type + }; + } else if (url && !url.includes(window.location.protocol)) { + + return { + message: "Mixed Content Error, your View should be over same protocol as your API", + status: 500, + type + }; + } else if (request) { + if(error.stack.includes('Network Error')) { + return { + message: "Invalid API URL, please adjust API URL", + status:500 + } + } + return { + message: "server time out", + status: response?.status, + type + }; + } else if (error.stack.includes("Invalid URL")) { + return { + message: "Invalid API URL, please adjust API URL", + stauts: response?.status, + type + }; + } else if (error?.response?.status === 404) { + return { + message: "Invalid API URL, please adjust API URL", + status: response?.status, + type + }; + } else { + if(type==='labels') return; + + return { + message: "something went wrong with request", + status: response?.status, + type + }; + } +}; diff --git a/src/actions/loadLabelValues.js b/src/actions/loadLabelValues.js index 486e7a03..00e8ec25 100644 --- a/src/actions/loadLabelValues.js +++ b/src/actions/loadLabelValues.js @@ -1,69 +1,69 @@ -import axios from "axios"; -import { errorHandler } from "./errorHandler"; -import { setApiError } from "./setApiError"; -import { setLabels } from "./setLabels"; -import setLabelValues from "./setLabelValues"; -import setLoading from "./setLoading"; - - -export default function loadLabelValues(label, labelList, apiUrl) { - if (!label || (label?.length <= 0 && label.lsList.length <= 0)) { - return () => {}; - }; - - const url = apiUrl; - - const origin = window.location.origin - - const headers = { - "Access-Control-Allow-Origin": origin, - "Access-Control-Allow-Headers": ["Access-Control-Request-Headers", "Content-Type"], - "Content-Type": "application/json", - } - - const options = { - method: "GET", - headers: headers, - mode: "cors", - - }; - - return async (dispatch) => { - dispatch(setLoading(true)) - - await axios.get(`${url}/loki/api/v1/label/${label.name}/values`, options) - ?.then(response => { - if (response?.data?.data) { - const values = response?.data?.data?.map?.((value) => ({ - name: value, - selected: false, - inverted: false - })); - - const lsList = [...labelList]; - lsList.forEach((l) => { - if (l?.name === label?.name) { - l.values = [...values]; - } - }); - dispatch(setLabels(lsList)) - } else if(!response) { - dispatch(setApiError('URL NOT FOUND')) - dispatch(setLabelValues([])) - } - - dispatch(setLoading(false)); - dispatch(setApiError('')) - dispatch(setLabelValues(response?.data?.data)); - - }).catch(error => { - dispatch(setLoading(false)) - const { message } = errorHandler(url, error) - dispatch(setApiError(message || 'API NOT FOUND')) - dispatch(setLabelValues([])) - console.err(error) - }) - } - - -} +import axios from "axios"; +import { errorHandler } from "./errorHandler"; +import { setApiError } from "./setApiError"; +import { setLabels } from "./setLabels"; +import setLabelValues from "./setLabelValues"; +import setLoading from "./setLoading"; + + +export default function loadLabelValues(label, labelList, apiUrl) { + if (!label || (label?.length <= 0 && label.lsList.length <= 0)) { + return () => {}; + }; + + const url = apiUrl; + + const origin = window.location.origin + + const headers = { + "Access-Control-Allow-Origin": origin, + "Access-Control-Allow-Headers": ["Access-Control-Request-Headers", "Content-Type"], + "Content-Type": "application/json", + } + + const options = { + method: "GET", + headers: headers, + mode: "cors", + + }; + + return async (dispatch) => { + dispatch(setLoading(true)) + + await axios.get(`${url}/loki/api/v1/label/${label.name}/values`, options) + ?.then(response => { + if (response?.data?.data) { + const values = response?.data?.data?.map?.((value) => ({ + name: value, + selected: false, + inverted: false + })); + + const lsList = [...labelList]; + lsList.forEach((l) => { + if (l?.name === label?.name) { + l.values = [...values]; + } + }); + dispatch(setLabels(lsList)) + } else if(!response) { + dispatch(setApiError('URL NOT FOUND')) + dispatch(setLabelValues([])) + } + + dispatch(setLoading(false)); + dispatch(setApiError('')) + dispatch(setLabelValues(response?.data?.data)); + + }).catch(error => { + dispatch(setLoading(false)) + const { message } = errorHandler(url, error,'lavelValues') + dispatch(setApiError(message || 'API NOT FOUND')) + dispatch(setLabelValues([])) + console.error(error) + }) + } + + +} diff --git a/src/actions/loadLogs.js b/src/actions/loadLogs.js index fb8efc37..8ba83cf3 100644 --- a/src/actions/loadLogs.js +++ b/src/actions/loadLogs.js @@ -1,135 +1,138 @@ -import axios from "axios"; -import setLogs from "./setLogs"; -import setLoading from "./setLoading"; -import store from "../store/store"; -import setMatrixData from "./setMatrixData"; -import { nanoid } from "nanoid"; -import { setStartTime, setStopTime } from "./"; -import { findRangeByLabel } from "../components/StatusBar/components/daterangepicker/utils"; - -export default function loadLogs() { - const localStore = store.getState(); - const { - query, - limit, - step, - apiUrl, - label: rangeLabel, - from, - to, - } = localStore; - let { start: startTs, stop: stopTs } = localStore; - - function getTimeParsed(time) { - return time.getTime() + "000000"; - } - - const parsedStart = getTimeParsed(startTs); - const parsedStop = getTimeParsed(stopTs); - - const parsedTime = - "&start=" + (from || parsedStart) + "&end=" + (to || parsedStop); - - if (findRangeByLabel(rangeLabel)) { - ({ dateStart: startTs, dateEnd: stopTs } = - findRangeByLabel(rangeLabel)); - } - - store.dispatch(setStartTime(startTs)); - store.dispatch(setStopTime(stopTs)); - - const origin = window.location.origin; - const url = apiUrl; - - const queryStep = `&step=${step || 120}`; - - const encodedQuery = `${encodeURIComponent(query)}`; - - const getUrl = `${url}/loki/api/v1/query_range?query=${encodedQuery}&limit=${limit}${parsedTime}${queryStep}`; - - const options = { - method: "GET", - headers: { - "Content-Type": "application/javascript", - "Access-Control-Allow-Origin": origin, - }, - }; - - const fromNanoSec = (ts) => parseInt(ts / 1000000); - const toMiliseC = (ts) => parseInt(ts * 1000); - const getTimestamp = (type) => (ts) => - type === "streams" - ? fromNanoSec(ts) - : type === "matrix" - ? toMiliseC(ts) - : ts; - const mapStreams = (streams, messages, type) => { - streams.forEach((stream) => { - stream.values.forEach((log, i) => { - let [ts, text] = log; - messages.push({ - type, - timestamp: getTimestamp(type)(ts), - text, - tags: - type === "streams" - ? stream.stream - : type === "matrix" - ? stream.metric - : {}, - showTs: true, - showLabels: false, - id: nanoid(), - }); - }); - }); - }; - - //const mapMatrix - return async function (dispatch) { - dispatch(setLoading(true)); - dispatch(setLogs([])); - dispatch(setMatrixData([])); - - await axios - .get(getUrl, options) - ?.then((response) => { - if (response?.data?.data) { - let messages = []; - const result = response?.data?.data?.result; // array - const type = response?.data?.data?.resultType; - if (type === "streams") { - mapStreams(result, messages, type); - dispatch(setMatrixData([])); - const messSorted = messages?.sort((a, b) => - a.timestamp < b.timestamp ? 1 : -1 - ); - if (messSorted) { - dispatch(setLogs(messSorted || [])); - - dispatch(setLoading(false)); - } - } - - if (type === "matrix") { - const idResult = - result?.map((m) => ({ ...m, id: nanoid() })) || []; - dispatch(setMatrixData(idResult || [])); - dispatch(setLoading(false)); - } - dispatch(setLoading(false)); - } else { - dispatch(setLogs([])); - dispatch(setMatrixData([])); - dispatch(setLoading(false)); - } - dispatch(setLoading(false)); - }) - .catch((error) => { - dispatch(setLogs([])); - dispatch(setMatrixData([])); - dispatch(setLoading(false)); - console.log(error); - }); - }; -} +import axios from "axios"; +import setLogs from "./setLogs"; +import setLoading from "./setLoading"; +import store from "../store/store"; +import setMatrixData from "./setMatrixData"; +import { nanoid } from "nanoid"; +import { setStartTime, setStopTime } from "./"; +import { findRangeByLabel } from "../components/StatusBar/components/daterangepicker/utils"; +import { createAlert } from "./createAlert"; +import { errorHandler } from "./errorHandler"; + +export default function loadLogs() { + const localStore = store.getState(); + const { + query, + limit, + step, + apiUrl, + label: rangeLabel, + from, + to, + } = localStore; + let { start: startTs, stop: stopTs } = localStore; + + function getTimeParsed(time) { + return time.getTime() + "000000"; + } + + const parsedStart = getTimeParsed(startTs); + const parsedStop = getTimeParsed(stopTs); + + const parsedTime = + "&start=" + (from || parsedStart) + "&end=" + (to || parsedStop); + + if (findRangeByLabel(rangeLabel)) { + ({ dateStart: startTs, dateEnd: stopTs } = + findRangeByLabel(rangeLabel)); + } + + store.dispatch(setStartTime(startTs)); + store.dispatch(setStopTime(stopTs)); + + const origin = window.location.origin; + const url = apiUrl; + + const queryStep = `&step=${step || 120}`; + + const encodedQuery = `${encodeURIComponent(query)}`; + + const getUrl = `${url}/loki/api/v1/query_range?query=${encodedQuery}&limit=${limit}${parsedTime}${queryStep}`; + + const options = { + method: "GET", + headers: { + "Content-Type": "application/javascript", + "Access-Control-Allow-Origin": origin, + }, + }; + + const fromNanoSec = (ts) => parseInt(ts / 1000000); + const toMiliseC = (ts) => parseInt(ts * 1000); + const getTimestamp = (type) => (ts) => + type === "streams" + ? fromNanoSec(ts) + : type === "matrix" + ? toMiliseC(ts) + : ts; + const mapStreams = (streams, messages, type) => { + streams.forEach((stream) => { + stream.values.forEach((log, i) => { + let [ts, text] = log; + messages.push({ + type, + timestamp: getTimestamp(type)(ts), + text, + tags: + type === "streams" + ? stream.stream + : type === "matrix" + ? stream.metric + : {}, + showTs: true, + showLabels: false, + id: nanoid(), + }); + }); + }); + }; + + //const mapMatrix + return async function (dispatch) { + dispatch(setLoading(true)); + dispatch(setLogs([])); + dispatch(setMatrixData([])); + + await axios + .get(getUrl, options) + ?.then((response) => { + if (response?.data?.data) { + let messages = []; + const result = response?.data?.data?.result; // array + const type = response?.data?.data?.resultType; + if (type === "streams") { + mapStreams(result, messages, type); + dispatch(setMatrixData([])); + const messSorted = messages?.sort((a, b) => + a.timestamp < b.timestamp ? 1 : -1 + ); + if (messSorted) { + dispatch(setLogs(messSorted || [])); + + dispatch(setLoading(false)); + } + } + + if (type === "matrix") { + const idResult = + result?.map((m) => ({ ...m, id: nanoid() })) || []; + dispatch(setMatrixData(idResult || [])); + dispatch(setLoading(false)); + } + dispatch(setLoading(false)); + } else { + dispatch(setLogs([])); + dispatch(setMatrixData([])); + dispatch(setLoading(false)); + } + dispatch(setLoading(false)); + }) + .catch((error) => { + dispatch(setLogs([])); + dispatch(setMatrixData([])); + dispatch(setLoading(false)); + const handler = errorHandler(url,error,type) + console.log(handler) + }); + }; +} diff --git a/src/components/DataView/ValueTags.js b/src/components/DataView/ValueTags.js index 3b1d871e..d79a2608 100644 --- a/src/components/DataView/ValueTags.js +++ b/src/components/DataView/ValueTags.js @@ -29,7 +29,7 @@ export default function ValueTags({ tags }) { const updatedLabels = store.getState().labels; const updatedLabel = updatedLabels.find( (label) => label.name === key - ); + ); const labelValue = updatedLabel.values.find( (tag) => tag.name === value ); diff --git a/src/components/LabelBrowser/QueryBar.js b/src/components/LabelBrowser/QueryBar.js index f2f2daa9..8396f6b2 100644 --- a/src/components/LabelBrowser/QueryBar.js +++ b/src/components/LabelBrowser/QueryBar.js @@ -22,7 +22,7 @@ import ShowLogsButton from "./components/ShowLogsButton/ShowLogsButton"; import queryInit from "./helpers/queryInit"; import onQueryValid from "./helpers/onQueryValid"; import debugLog from "./helpers/debugLog"; -import {sendLabels} from "../../hooks/useLabels"; +import { sendLabels } from "../../hooks/useLabels"; export const QueryBar = () => { const dispatch = useDispatch(); @@ -33,44 +33,31 @@ export const QueryBar = () => { const apiUrl = useSelector((store) => store.apiUrl); const isSubmit = useSelector((store) => store.isSubmit); const historyOpen = useSelector((store) => store.historyOpen); - const isEmbed = useSelector( ( store ) => store.isEmbed) + const isEmbed = useSelector((store) => store.isEmbed) const [queryInput, setQueryInput] = useState(query); const [queryValid, setQueryValid] = useState(false); const [queryValue, setQueryValue] = useState(queryInit(query)); - const labels = useSelector( store => store.labels) + const labels = useSelector(store => store.labels) const queryHistory = useSelector((store) => store.queryHistory); const saveUrl = localUrl(); useEffect(() => { const dLog = debugLog(query); debug && dLog.logicQueryBar(); - // labels first load from api url - const labels = sendLabels(apiUrl) - if (onQueryValid(query && isSubmit === "true")) { + const labels = sendLabels(apiUrl) + if (isEmbed) dispatch(loadLogs()) + if (query.length > 0) { debug && dLog.queryBarDispatch(); - return labels.then( data => { - decodeQuery(query,apiUrl,data) - - }) dispatch(setLoading(true)); - dispatch(loadLogs()); - setTimeout(() => { - dispatch(setIsSubmit(false)); - - - }, 200); - } else if (!onQueryValid(query) && isSubmit === "true") { - - dispatch(setIsSubmit(false)); + return labels.then(data => { + decodeQuery(query, apiUrl, data) + + }) } - return labels.then( data => { - decodeQuery(query,apiUrl,data) - }) - }, []); useEffect(() => { @@ -82,7 +69,11 @@ export const QueryBar = () => { const onValueDisplay = (e) => { e.preventDefault(); const isOpen = labelsBrowserOpen ? false : true; - if (isOpen) dispatch(loadLabels(apiUrl)); + if (isOpen) { + dispatch(loadLabels(apiUrl)); + + } + dispatch(setLabelsBrowserOpen(isOpen)); }; @@ -113,10 +104,11 @@ export const QueryBar = () => { }); dispatch(setQueryHistory(historyUpdated)); dispatch(setLabelsBrowserOpen(false)); - decodeQuery(queryInput,apiUrl,labels) - + decodeQuery(queryInput, apiUrl, labels) + dispatch(setLoading(true)); + dispatch(loadLogs()); const storedUrl = saveUrl.add({ data: window.location.href, @@ -136,58 +128,58 @@ export const QueryBar = () => { return ( !isEmbed && (
- -
+ +
+ > + + +
+ + +
+ + + + -
- - -
- - - - - - - - -
+ + + ) diff --git a/src/components/LabelBrowser/ValuesList.js b/src/components/LabelBrowser/ValuesList.js index 44a53067..8f955657 100644 --- a/src/components/LabelBrowser/ValuesList.js +++ b/src/components/LabelBrowser/ValuesList.js @@ -53,7 +53,7 @@ export const ValuesList = (props) => { const [labelList, setLabelList] = useState(labels); const dispatch = useDispatch(); - const debug = useSelector((store) => store.debug); + const debug = useSelector((store) => store.debugMode); const apiUrl = useSelector((store) => store.apiUrl); //if(debug) console.log('🚧 LOGIC/LabelBrowser/ValuesList', apiUrl) const labelsBrowserOpen = useSelector((store) => store.labelsBrowserOpen); @@ -62,7 +62,6 @@ export const ValuesList = (props) => { useEffect(() => { dispatch(loadLabels(apiUrl)); - // setLabelList(labels) }, [apiUrl]); useEffect(() => { @@ -115,6 +114,9 @@ export const ValuesList = (props) => { }; } else return {}; }; + const isString = (value) => { + return typeof value === 'string' + } return ( labelsBrowserOpen && (
@@ -139,7 +141,7 @@ export const ValuesList = (props) => { {labelList && labelList?.map((value, key) => ( { {selectedList() && (
- {labelsSelected.map((labelSelected, skey) => ( + { labelsSelected.map((labelSelected, skey) => (
+ {labelSelected.name} ( {labelSelected.values.length}) @@ -183,13 +186,17 @@ export const ValuesList = (props) => { "valuelist-content column" } > + {labelSelected?.values?.map( - (value, key) => ( - ( + + isString(value.name) ? ( + + { ) } > - {value.name} + + {value.name} + ):(unknown) + ) )}
diff --git a/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js b/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js index e4d3a03a..94959393 100644 --- a/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js +++ b/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js @@ -1,24 +1,30 @@ -import { ShowLabelsBtn } from "../styled"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; - - - -export default function ShowLabelsButton({ onValueDisplay, labelsBrowserOpen, isMobile }) { - const LOG_BROWSER = "Labels"; - - return ( - - {labelsBrowserOpen ? ( - - ) : ( - - )}{" "} - {LOG_BROWSER} - - ); +import { ShowLabelsBtn } from "../styled"; +import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; +import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; +import { useSelector } from "react-redux"; + + +export default function ShowLabelsButton({ onValueDisplay, labelsBrowserOpen, isMobile }) { + const LOG_BROWSER = "Labels"; + const labels = useSelector( store => store.labels) + return ( + + 0 ? 'Show / Hide Labels' : 'Labels Not Available' } + onClick={onValueDisplay} + browserActive={labelsBrowserOpen} + isMobile={isMobile} + disabled={labels?.length < 1} + + + > + {labelsBrowserOpen ? ( + + ) : ( + + )}{" "} + {LOG_BROWSER} + + + ); } \ No newline at end of file diff --git a/src/components/LabelBrowser/components/styled/index.js b/src/components/LabelBrowser/components/styled/index.js index 6587ea6f..c9218a7f 100644 --- a/src/components/LabelBrowser/components/styled/index.js +++ b/src/components/LabelBrowser/components/styled/index.js @@ -36,6 +36,11 @@ export const ShowLabelsBtn = styled(BtnSmall)` &:hover { background: ${theme.buttonHover}; } + &:disabled { + cursor:auto; + color:${theme.buttonDefault}; + background:${theme.buttonInactive}; + } @media screen and (max-width: 864px) { display: ${(props) => (props.isMobile ? "flex" : "none")}; diff --git a/src/components/LabelBrowser/index.js b/src/components/LabelBrowser/index.js index 02cd8c87..91ff6f7d 100644 --- a/src/components/LabelBrowser/index.js +++ b/src/components/LabelBrowser/index.js @@ -1,12 +1,16 @@ -import { QueryBar } from "./QueryBar"; -import { ValuesList } from "./ValuesList"; - -export default function LabelBrowser() { - return ( -
- - - -
- ); -} +import { QueryBar } from "./QueryBar"; +import { ValuesList } from "./ValuesList"; +import { useSelector } from "react-redux"; +export default function LabelBrowser() { + const labelsBrowserOpen = useSelector(store => store.labelsBrowserOpen) + + return ( +
+ + {labelsBrowserOpen && ( + + )} + +
+ ); +} diff --git a/src/components/StatusBar/components/apiselector/ApiSelector.js b/src/components/StatusBar/components/apiselector/ApiSelector.js index 582a1da0..ffa81155 100644 --- a/src/components/StatusBar/components/apiselector/ApiSelector.js +++ b/src/components/StatusBar/components/apiselector/ApiSelector.js @@ -27,6 +27,7 @@ export function ApiSelector() { useEffect(() => { setEditedUrl(apiUrl); + dispatch(loadLabels(apiUrl)) }, [apiUrl]); useEffect(() => { @@ -51,10 +52,8 @@ export function ApiSelector() { }; const onUrlSubmit = (e) => { dispatch(setApiUrl(editedUrl)); - dispatch(loadLabels(editedUrl)); - if (query?.length > 3) { - dispatch(setLabelsBrowserOpen(false)); - } + dispatch(setLabelsBrowserOpen(false)); + }; return ( diff --git a/src/components/StatusBar/components/daterangepicker/index.js b/src/components/StatusBar/components/daterangepicker/index.js index 137da478..c1e97947 100644 --- a/src/components/StatusBar/components/daterangepicker/index.js +++ b/src/components/StatusBar/components/daterangepicker/index.js @@ -1,289 +1,289 @@ -import React, { useState, useEffect } from "react"; - -import { - addMonths, - isSameDay, - isWithinInterval, - isAfter, - isBefore, - isDate, - isSameMonth, - addYears, - max, - min, - format, - isValid, -} from "date-fns"; - -import Nav from "./components/Nav"; -import { - findRangeByLabel, - getDefaultRanges, - getValidatedMonths, - parseOptionalDate, -} from "./utils"; -import { DATE_TIME_RANGE, MARKERS } from "./consts"; -import { theme } from "./components/styles"; -import { ThemeProvider } from "@emotion/react"; -import { - setRangeOpen, - setStartTime, - setTimeRangeLabel, - setStopTime, -} from "../../../../actions"; - -import { useSelector, useDispatch } from "react-redux"; -import useOutsideRef from "./hooks/useOutsideRef"; - -import store from "../../../../store/store"; - -import AccessTimeOutlinedIcon from "@mui/icons-material/AccessTimeOutlined"; -import { Tooltip } from "@mui/material"; -import loadLogs from "../../../../actions/loadLogs"; -import { setLabelsBrowserOpen } from "../../../../actions/setLabelsBrowserOpen"; - - - -import TimeLabel from "./components/TimeLabel"; -import { DatePickerButton } from "../../styled"; - - -export function DateRangePickerMain(props) { - const today = Date.now(); - const { isOpen, minDate, maxDate } = props; - - const startTs = useSelector((store) => store.start); - const stopTs = useSelector((store) => store.stop); - const initialDateRange = () => { - try { - const ls = JSON.parse(localStorage.getItem(DATE_TIME_RANGE)); - if (ls?.label !== "" && typeof ls.label !== "undefined") { - const range = findRangeByLabel(ls?.label); - ls.dateStart = range.dateStart; - ls.dateEnd = range.dateEnd; - } else { - ls.dateStart = new Date(ls.dateStart); - ls.dateEnd = new Date(ls.dateEnd); - } - return ls; - } catch (e) { - if (isDate(startTs) && isDate(stopTs)) { - return { dateStart: startTs, dateEnd: stopTs }; - } - } - }; - - const minDateValid = parseOptionalDate(minDate, addYears(today, -10)); - const maxDateValid = parseOptionalDate(maxDate, addYears(today, 10)); - const [intialFirstMonth, initialSecondMonth] = getValidatedMonths( - initialDateRange() || {}, - minDateValid, - maxDateValid - ); - const [dateRange, setDateRange] = useState({ ...initialDateRange() }); - const [hoverDay, setHoverDay] = useState(); - const [firstMonth, setFirstMonth] = useState(intialFirstMonth || today); - const [secondMonth, setSecondMonth] = useState( - initialSecondMonth || addMonths(firstMonth, 1) - ); - const [timeLabel, setTimeLabel] = useState(""); - const dispatch = useDispatch(); - - const rangeOpen = useSelector((store) => store.rangeOpen); - const range = useSelector((store) => ({ - dateStart: store.start, - dateEnd: store.stop, - label: store.label, - })); - - useEffect(() => { - setTimeLabel(range.label); - }, [range]); - - const { dateStart, dateEnd } = dateRange; - - const { ref } = useOutsideRef(true); - - - const setFirstMonthValidated = (date) => { - if (isBefore(date, secondMonth)) { - setFirstMonth(date); - } - }; - - const setSecondMonthValidated = (date) => { - if (isAfter(date, firstMonth)) { - setSecondMonth(date); - } - }; - - const setDateRangeValidated = (range) => { - let { label, dateStart: newStart, dateEnd: newEnd } = range; - if (newStart && newEnd) { - range.label = label; - range.dateStart = newStart = max([newStart, minDateValid]); - range.dateEnd = newEnd = min([newEnd, maxDateValid]); - setDateRange(range); - saveDateRange(range); - onChange(range); - setFirstMonth(newStart); - setSecondMonth( - isSameMonth(newStart, newEnd) ? addMonths(newStart, 1) : newEnd - ); - } - }; - const saveDateRange = (range) => { - localStorage.setItem(DATE_TIME_RANGE, JSON.stringify(range)); - }; - const onDayClick = (day) => { - if (dateStart && !dateEnd && !isBefore(day, dateStart)) { - const newRange = { dateStart, dateEnd: day }; - onChange(newRange); - saveDateRange(newRange); - setDateRange(newRange); - dispatch(setTimeRangeLabel("")); - onClose(); - } else { - setDateRange({ dateStart: day, dateEnd: undefined }); - } - setHoverDay(day); - }; - - const onMonthNavigate = (marker, action) => { - if (marker === MARKERS.FIRST_MONTH) { - const firstNew = addMonths(firstMonth, action); - if (isBefore(firstNew, secondMonth)) setFirstMonth(firstNew); - } else { - const secondNew = addMonths(secondMonth, action); - if (isBefore(firstMonth, secondNew)) setSecondMonth(secondNew); - } - }; - - const onDayHover = (date) => { - if (dateStart && !dateEnd) { - if (!hoverDay || !isSameDay(date, hoverDay)) { - setHoverDay(date); - } - } - }; - const onClose = (e = null) => { - const { query } = store.getState(); - e?.preventDefault(); - if (onQueryValid(query)) { - dispatch(setLabelsBrowserOpen(false)); - dispatch(loadLogs()); - } else { - console.log("Please make a log query", query); - } - dispatch(setRangeOpen(false)); - isOpen(e); - }; - const onQueryValid = (query) => { - return query !== "{" && query !== "}" && query !== "{}" && query !== ""; // TODO: make a proper query validation - }; - // helpers - const inHoverRange = (day) => { - return ( - dateStart && - !dateEnd && - hoverDay && - isAfter(hoverDay, dateStart) && - isWithinInterval(day, { - start: dateStart, - end: hoverDay, - }) - ); - }; - - - const helpers = { - inHoverRange, - }; - - const handlers = { - onDayClick, - onDayHover, - onMonthNavigate, - }; - - function onChange({ dateStart, dateEnd, label }) { - const isStart = isDate(dateStart); - const isEnd = isDate(dateEnd); - const isLabel = typeof label !== "undefined"; - if (isStart) dispatch(setStartTime(dateStart)); - if (isEnd) dispatch(setStopTime(dateEnd)); - if (isLabel) dispatch(setTimeRangeLabel(label)); - } - - const openButtonHandler = (e) => { - e.preventDefault(); - if (rangeOpen === true) { - onClose(e); - - } else { - dispatch(setRangeOpen(true)); - - } - }; - - return ( -
- : ""} - > - - - - - {timeLabel - ? timeLabel - : (isValid(dateRange.dateStart) - ? format( - dateRange.dateStart, - "yyyy/MM/dd HH:mm:ss" - ) - : dateRange.dateStart) + - "-" + - (isValid(dateRange.dateEnd) - ? format( - dateRange.dateEnd, - "yyyy/MM/dd HH:mm:ss" - ) - : typeof dateRange.dateEnd !== "undefined" - ? dateRange.dateEnd - : "")} - - - - {rangeOpen ? ( -
- -
- ) : null} -
- ); -} -export const DateRangePicker = DateRangePickerMain; - -//shouldnt be at same div!! - - +import React, { useState, useEffect } from "react"; + +import { + addMonths, + isSameDay, + isWithinInterval, + isAfter, + isBefore, + isDate, + isSameMonth, + addYears, + max, + min, + format, + isValid, +} from "date-fns"; + +import Nav from "./components/Nav"; +import { + findRangeByLabel, + getDefaultRanges, + getValidatedMonths, + parseOptionalDate, +} from "./utils"; +import { DATE_TIME_RANGE, MARKERS } from "./consts"; +import { theme } from "./components/styles"; +import { ThemeProvider } from "@emotion/react"; +import { + setRangeOpen, + setStartTime, + setTimeRangeLabel, + setStopTime, +} from "../../../../actions"; + +import { useSelector, useDispatch } from "react-redux"; +import useOutsideRef from "./hooks/useOutsideRef"; + +import store from "../../../../store/store"; + +import AccessTimeOutlinedIcon from "@mui/icons-material/AccessTimeOutlined"; +import { Tooltip } from "@mui/material"; +import loadLogs from "../../../../actions/loadLogs"; +import { setLabelsBrowserOpen } from "../../../../actions/setLabelsBrowserOpen"; + + + +import TimeLabel from "./components/TimeLabel"; +import { DatePickerButton } from "../../styled"; + + +export function DateRangePickerMain(props) { + const today = Date.now(); + const { isOpen, minDate, maxDate } = props; + + const startTs = useSelector((store) => store.start); + const stopTs = useSelector((store) => store.stop); + const initialDateRange = () => { + try { + const ls = JSON.parse(localStorage.getItem(DATE_TIME_RANGE)); + if (ls?.label !== "" && typeof ls.label !== "undefined") { + const range = findRangeByLabel(ls?.label); + ls.dateStart = range.dateStart; + ls.dateEnd = range.dateEnd; + } else { + ls.dateStart = new Date(ls.dateStart); + ls.dateEnd = new Date(ls.dateEnd); + } + return ls; + } catch (e) { + if (isDate(startTs) && isDate(stopTs)) { + return { dateStart: startTs, dateEnd: stopTs }; + } + } + }; + + const minDateValid = parseOptionalDate(minDate, addYears(today, -10)); + const maxDateValid = parseOptionalDate(maxDate, addYears(today, 10)); + const [intialFirstMonth, initialSecondMonth] = getValidatedMonths( + initialDateRange() || {}, + minDateValid, + maxDateValid + ); + const [dateRange, setDateRange] = useState({ ...initialDateRange() }); + const [hoverDay, setHoverDay] = useState(); + const [firstMonth, setFirstMonth] = useState(intialFirstMonth || today); + const [secondMonth, setSecondMonth] = useState( + initialSecondMonth || addMonths(firstMonth, 1) + ); + const [timeLabel, setTimeLabel] = useState(""); + const dispatch = useDispatch(); + + const rangeOpen = useSelector((store) => store.rangeOpen); + const range = useSelector((store) => ({ + dateStart: store.start, + dateEnd: store.stop, + label: store.label, + })); + + useEffect(() => { + setTimeLabel(range.label); + }, [range]); + + const { dateStart, dateEnd } = dateRange; + + const { ref } = useOutsideRef(true); + + + const setFirstMonthValidated = (date) => { + if (isBefore(date, secondMonth)) { + setFirstMonth(date); + } + }; + + const setSecondMonthValidated = (date) => { + if (isAfter(date, firstMonth)) { + setSecondMonth(date); + } + }; + + const setDateRangeValidated = (range) => { + let { label, dateStart: newStart, dateEnd: newEnd } = range; + if (newStart && newEnd) { + range.label = label; + range.dateStart = newStart = max([newStart, minDateValid]); + range.dateEnd = newEnd = min([newEnd, maxDateValid]); + setDateRange(range); + saveDateRange(range); + onChange(range); + setFirstMonth(newStart); + setSecondMonth( + isSameMonth(newStart, newEnd) ? addMonths(newStart, 1) : newEnd + ); + } + }; + const saveDateRange = (range) => { + localStorage.setItem(DATE_TIME_RANGE, JSON.stringify(range)); + }; + const onDayClick = (day) => { + if (dateStart && !dateEnd && !isBefore(day, dateStart)) { + const newRange = { dateStart, dateEnd: day }; + onChange(newRange); + saveDateRange(newRange); + setDateRange(newRange); + dispatch(setTimeRangeLabel("")); + onClose(); + } else { + setDateRange({ dateStart: day, dateEnd: undefined }); + } + setHoverDay(day); + }; + + const onMonthNavigate = (marker, action) => { + if (marker === MARKERS.FIRST_MONTH) { + const firstNew = addMonths(firstMonth, action); + if (isBefore(firstNew, secondMonth)) setFirstMonth(firstNew); + } else { + const secondNew = addMonths(secondMonth, action); + if (isBefore(firstMonth, secondNew)) setSecondMonth(secondNew); + } + }; + + const onDayHover = (date) => { + if (dateStart && !dateEnd) { + if (!hoverDay || !isSameDay(date, hoverDay)) { + setHoverDay(date); + } + } + }; + const onClose = (e = null) => { + const { query } = store.getState(); + e?.preventDefault(); + if (query.length > 0) { + dispatch(setLabelsBrowserOpen(false)); + dispatch(loadLogs()); + } else { + console.log("Please make a log query", query); + } + dispatch(setRangeOpen(false)); + isOpen(e); + }; + const onQueryValid = (query) => { + return query !== "{" && query !== "}" && query !== "{}" && query !== ""; // TODO: make a proper query validation + }; + // helpers + const inHoverRange = (day) => { + return ( + dateStart && + !dateEnd && + hoverDay && + isAfter(hoverDay, dateStart) && + isWithinInterval(day, { + start: dateStart, + end: hoverDay, + }) + ); + }; + + + const helpers = { + inHoverRange, + }; + + const handlers = { + onDayClick, + onDayHover, + onMonthNavigate, + }; + + function onChange({ dateStart, dateEnd, label }) { + const isStart = isDate(dateStart); + const isEnd = isDate(dateEnd); + const isLabel = typeof label !== "undefined"; + if (isStart) dispatch(setStartTime(dateStart)); + if (isEnd) dispatch(setStopTime(dateEnd)); + if (isLabel) dispatch(setTimeRangeLabel(label)); + } + + const openButtonHandler = (e) => { + e.preventDefault(); + if (rangeOpen === true) { + onClose(e); + + } else { + dispatch(setRangeOpen(true)); + + } + }; + + return ( +
+ : ""} + > + + + + + {timeLabel + ? timeLabel + : (isValid(dateRange.dateStart) + ? format( + dateRange.dateStart, + "yyyy/MM/dd HH:mm:ss" + ) + : dateRange.dateStart) + + "-" + + (isValid(dateRange.dateEnd) + ? format( + dateRange.dateEnd, + "yyyy/MM/dd HH:mm:ss" + ) + : typeof dateRange.dateEnd !== "undefined" + ? dateRange.dateEnd + : "")} + + + + {rangeOpen ? ( +
+ +
+ ) : null} +
+ ); +} +export const DateRangePicker = DateRangePickerMain; + +//shouldnt be at same div!! + + diff --git a/src/helpers/UpdateStateFromQueryParams.js b/src/helpers/UpdateStateFromQueryParams.js index 14e23c2d..5b9bc83e 100644 --- a/src/helpers/UpdateStateFromQueryParams.js +++ b/src/helpers/UpdateStateFromQueryParams.js @@ -104,7 +104,8 @@ export function UpdateStateFromQueryParams() { dispatch(STORE_ACTIONS[param](startParams[param])); if(param === 'apiUrl') { - dispatch(loadLabels(startParams[param])) + + // dispatch(loadLabels(startParams[param])) } } else if ( QUERY_VALUE === param && diff --git a/src/helpers/error.interceptor.js b/src/helpers/error.interceptor.js index 621ab2d5..d95113ab 100644 --- a/src/helpers/error.interceptor.js +++ b/src/helpers/error.interceptor.js @@ -1,21 +1,33 @@ -import store from '../store/store' -import { errorHandler, setApiError } from "../actions/"; -const errorInterceptor = (axiosInstance) => { - axiosInstance.interceptors.response.use( - (response) => { - //Response Successful - return response; - }, - (error) => { - if (error?.response?.status === 401) { - //Unauthorized - //redirect to Login - } else { - const url = error?.response?.config?.url || "" - const {message,status} = errorHandler(url, error) - store.dispatch(setApiError(message || status + 'Error')) - } - } - ); -}; -export default errorInterceptor; +import store from '../store/store' +import { errorHandler, setApiError,createAlert } from "../actions/"; + +const errorInterceptor = (axiosInstance) => { + axiosInstance.interceptors.response.use( + (response) => { + //Response Successful + return response; + }, + (error) => { + console.log("ERROR", error) + + if (error?.response?.status === 401) { + //Unauthorized + //redirect to Login + } else { + const url = error?.response?.config?.url || "" + console.log(error.response) + const { message, status, type } = errorHandler(url, error, 'intercepted') + + console.log(message,status,type) + // console.log(errorHandler(url, error)) + // store.dispatch(setApiError(message || status + 'Error')) + + store.dispatch(createAlert({ + type:"error", + message: (message || status + 'Error') + })) + } + } + ); +}; +export default errorInterceptor; diff --git a/src/plugins/charts/index.js b/src/plugins/charts/index.js index 47f773d8..44bcf4bb 100644 --- a/src/plugins/charts/index.js +++ b/src/plugins/charts/index.js @@ -1,327 +1,327 @@ -import "./jquery-loader"; -import ReactFlot from "react-flot"; -import "react-flot/flot/jquery.flot.time.min"; - -import "react-flot/flot/jquery.flot.selection.min"; - -import "react-flot/flot/jquery.flot.crosshair.min"; - -import loadLogs from "../../actions/loadLogs"; -import { useDispatch } from "react-redux"; -import { setStartTime, setStopTime, setTimeRangeLabel } from "../../actions"; -import * as moment from "moment"; -import { useState, useEffect, useRef } from "react"; -import { format } from "date-fns"; -import ChartTools from "./ChartTools"; -import ChartLabelsList from "./ChartLabelList"; -import { - getTypeFromLocal, - formatTs, - getSeriesFromChartType, - setChartTypeSeries, - setTypeToLocal, - formatDateRange, - formatLabel, - getNewData, -} from "./helpers"; -import UseTooltip from "./UseTooltip"; -import { CHART_OPTIONS } from "./consts"; - -export default function ClokiChart({ matrixData }) { - const chartRef = useRef(null); - - const $q = window.jQuery; - $q.fn.UseTooltip = UseTooltip; - - const dispatch = useDispatch(); - const [isSpliced, setIsSpliced] = useState(true); - const [chartData, setChartData] = useState(getDataParsed(isSpliced)); - const [allData] = useState(getDataParsed(false)); - const [labels, setLabels] = useState([]); - const [element, setElement] = useState(chartRef.current); - - const [chartOptions, setChartOptions] = useState(CHART_OPTIONS); - - const [chartType, setChartType] = useState(getTypeFromLocal() || "line"); - - function getDataParsed(spliced) { - const parsed = [...matrixData].map((m) => ({ - data: formatTs(m.values), - label: formatLabel(m.metric), - isVisible: true, - id: m.id, - })); - - if (spliced) { - const splicedData = parsed.splice(0, 20); - return splicedData; - } else { - return parsed; - } - } - - /** - * - * Set chart types - */ - - function plotChartData(data, type, element) { - const chartSeries = setChartTypeSeries(type); - const { timeformat, min, max } = formatDateRange(data); - return $q.plot( - element, - data, - $q.extend(true, {}, chartOptions, { - ...chartSeries, - xaxis: { timeformat, min, max, }, - }) - ); - } - - function onSetChartType(type) { - const element = $q(chartRef.current); - const data = isSpliced ? chartData : allData; - const newData = getNewData(data, type); - - try { - let plot = plotChartData(newData, type, element); - const colorLabels = plot.getData(); - setLabels(colorLabels); - $q(chartRef.current).UseTooltip(plot); - setChartType(type); - setTypeToLocal(type); - } catch (e) { - console.log(data, e); - } - } - - function setRanges(event, ranges) { - const element = $q(chartRef.current); - const data = isSpliced ? chartData : allData; - event.preventDefault(); - - let newData = []; - const lSelected = - JSON.parse(localStorage.getItem("labelsSelected")) || []; - if (lSelected.length > 0) { - const { lines, bars, points } = getSeriesFromChartType(chartType); - const ids = lSelected.map((m) => m.id); - const dataMapped = data.map((series) => { - if (!ids.includes(series.id)) { - return { - ...series, - lines: { ...series.lines, show: false }, - bars: { ...series.bars, show: false }, - points: { ...series.points, show: false }, - isVisible: false, - }; - } else { - return { - ...series, - bars, - lines, - points, - isVisible: true, - }; - } - }); - newData = dataMapped; - } else { - newData = data; - } - - try { - let plot = $q.plot( - element, - newData, - $q.extend(true, {}, chartOptions, { - xaxis: { - min: ranges.xaxis.from - 100000, - max: ranges.xaxis.to + 100000, - timeformat: formatDateRange(newData).timerange, - - }, - }) - ); - $q(chartRef.current).UseTooltip(plot); - setTimeout(() => { - const fromTime = ranges.xaxis.from; - const toTime = ranges.xaxis.to; - - const fromTs = new Date( - moment(parseInt(fromTime)).format( - "YYYY-MM-DDTHH:mm:ss.SSSZ" - ) - ); - const toTs = new Date( - moment(parseInt(toTime)).format("YYYY-MM-DDTHH:mm:ss.SSSZ") - ); - const fromLabel = format(fromTs, "yyyy/MM/dd HH:mm:ss"); - const toLabel = format(toTs, "yyyy/MM/dd HH:mm:ss"); - - const timeRangeLabel = `${fromLabel}-${toLabel}`; - dispatch(setStopTime(toTs)); - dispatch(setStartTime(fromTs)); - - dispatch(setTimeRangeLabel(timeRangeLabel)); - dispatch(loadLogs()); - }, 400); - } catch (e) { - console.log("error on chart redraw", e); - } - } - - /** - * - *Isolate Series clicking label - */ - - function onLabelClick(e, v) { - let newList = []; - const lSelected = - JSON.parse(localStorage.getItem("labelsSelected")) || []; - - if (lSelected.some(({ id }) => id === v.id)) { - const filtered = lSelected.filter((f) => f.id !== v.id); - localStorage.setItem("labelsSelected", JSON.stringify(filtered)); - newList = filtered; - } else { - newList = lSelected.concat(v); - localStorage.setItem("labelsSelected", JSON.stringify(newList)); - } - - if (newList.length > 0) { - const ids = newList.map((m) => m.id); - const { lines, bars, points } = getSeriesFromChartType(chartType); - let dataSelected = e.map((series) => { - if (!ids.includes(series.id)) { - return { - ...series, - lines: { ...series.lines, show: false }, - bars: { ...series.bars, show: false }, - points: { ...series.points, show: false }, - }; - } else { - return { - ...series, - bars, - lines, - points, - }; - } - }); - const { timeformat, min, max } = formatDateRange(dataSelected); - - let plot = $q.plot( - element, - dataSelected, - - $q.extend(true, {}, chartOptions, { - series: getSeriesFromChartType(chartType), - xaxis: { timeformat, min, max, }, - }) - ); - - const colorLabels = plot.getData(); - setLabels(colorLabels); - $q(chartRef.current).UseTooltip(plot); - } else { - const data = isSpliced ? chartData : allData; - const { lines, bars, points } = getSeriesFromChartType(chartType); - const newData = data.map((series) => { - return { - ...series, - bars, - lines, - points, - isVisible: true, - }; - }); - const { timeformat, min, max } = formatDateRange(newData); - - let plot = $q.plot( - element, - newData, - $q.extend(true, {}, chartOptions, { - series: getSeriesFromChartType(chartType), - xaxis: { timeformat, min, max, }, - }) - ); - - const colorLabels = plot.getData(); - setLabels(colorLabels); - $q(chartRef.current).UseTooltip(plot); - } - } - - useEffect(() => { - setElement(chartRef.current); - setLabels(chartData.map(({ label }) => label)); - $q(chartRef.current).bind("plotselected", setRanges); - setChartData(getDataParsed(isSpliced)); - localStorage.setItem("labelsSelected", JSON.stringify([])); - }, []); - - useEffect(() => { - setChartOptions(chartOptions); - setElement(chartRef.current); - drawChartFromData(); - }, [matrixData, isSpliced]); - - function drawChartFromData() { - const data = isSpliced ? chartData : allData; - const element = $q(chartRef.current); - let newData = getNewData(data); - try { - const { timeformat, min, max } = formatDateRange(newData); - let plot = $q.plot( - element, - newData, - $q.extend(true, {}, chartOptions, { - series: getSeriesFromChartType(chartType), - xaxis: { timeformat, min, max, }, - }) - ); - - const colorLabels = plot.getData(); - setLabels(colorLabels); - $q(chartRef.current).UseTooltip(plot); - } catch (e) { - console.log(e); - } - } - - const handleNoLimitData = (e) => { - setIsSpliced(false); - }; - - const handleLimitData = (e) => { - setIsSpliced(true); - }; - - return ( -
- - -
- - -
- ); -} +import "./jquery-loader"; +import ReactFlot from "react-flot"; +import "react-flot/flot/jquery.flot.time.min"; + +import "react-flot/flot/jquery.flot.selection.min"; + +import "react-flot/flot/jquery.flot.crosshair.min"; + +import loadLogs from "../../actions/loadLogs"; +import { useDispatch } from "react-redux"; +import { setStartTime, setStopTime, setTimeRangeLabel } from "../../actions"; +import * as moment from "moment"; +import { useState, useEffect, useRef } from "react"; +import { format } from "date-fns"; +import ChartTools from "./ChartTools"; +import ChartLabelsList from "./ChartLabelList"; +import { + getTypeFromLocal, + formatTs, + getSeriesFromChartType, + setChartTypeSeries, + setTypeToLocal, + formatDateRange, + formatLabel, + getNewData, +} from "./helpers"; +import UseTooltip from "./UseTooltip"; +import { CHART_OPTIONS } from "./consts"; + +export default function ClokiChart({ matrixData }) { + const chartRef = useRef(null); + + const $q = window.jQuery; + $q.fn.UseTooltip = UseTooltip; + + const dispatch = useDispatch(); + const [isSpliced, setIsSpliced] = useState(true); + const [chartData, setChartData] = useState(getDataParsed(isSpliced)); + const [allData] = useState(getDataParsed(false)); + const [labels, setLabels] = useState([]); + const [element, setElement] = useState(chartRef.current); + + const [chartOptions, setChartOptions] = useState(CHART_OPTIONS); + + const [chartType, setChartType] = useState(getTypeFromLocal() || "line"); + + function getDataParsed(spliced) { + const parsed = [...matrixData].map((m) => ({ + data: formatTs(m.values), + label: formatLabel(m.metric), + isVisible: true, + id: m.id, + })); + + if (spliced) { + const splicedData = parsed.splice(0, 20); + return splicedData; + } else { + return parsed; + } + } + + /** + * + * Set chart types + */ + + function plotChartData(data, type, element) { + const chartSeries = setChartTypeSeries(type); + const { timeformat, min, max } = formatDateRange(data); + return $q.plot( + element, + data, + $q.extend(true, {}, chartOptions, { + ...chartSeries, + xaxis: { timeformat, min, max, }, + }) + ); + } + + function onSetChartType(type) { + const element = $q(chartRef.current); + const data = isSpliced ? chartData : allData; + const newData = getNewData(data, type); + + try { + let plot = plotChartData(newData, type, element); + const colorLabels = plot.getData(); + setLabels(colorLabels); + $q(chartRef.current).UseTooltip(plot); + setChartType(type); + setTypeToLocal(type); + } catch (e) { + console.log(data, e); + } + } + + function setRanges(event, ranges) { + const element = $q(chartRef.current); + const data = isSpliced ? chartData : allData; + event.preventDefault(); + + let newData = []; + const lSelected = + JSON.parse(localStorage.getItem("labelsSelected")) || []; + if (lSelected.length > 0) { + const { lines, bars, points } = getSeriesFromChartType(chartType); + const ids = lSelected.map((m) => m.id); + const dataMapped = data.map((series) => { + if (!ids.includes(series.id)) { + return { + ...series, + lines: { ...series.lines, show: false }, + bars: { ...series.bars, show: false }, + points: { ...series.points, show: false }, + isVisible: false, + }; + } else { + return { + ...series, + bars, + lines, + points, + isVisible: true, + }; + } + }); + newData = dataMapped; + } else { + newData = data; + } + + try { + let plot = $q.plot( + element, + newData, + $q.extend(true, {}, chartOptions, { + xaxis: { + min: ranges.xaxis.from - 100000, + max: ranges.xaxis.to + 100000, + timeformat: formatDateRange(newData).timerange, + + }, + }) + ); + $q(chartRef.current).UseTooltip(plot); + setTimeout(() => { + const fromTime = ranges.xaxis.from; + const toTime = ranges.xaxis.to; + + const fromTs = new Date( + moment(parseInt(fromTime)).format( + "YYYY-MM-DDTHH:mm:ss.SSSZ" + ) + ); + const toTs = new Date( + moment(parseInt(toTime)).format("YYYY-MM-DDTHH:mm:ss.SSSZ") + ); + const fromLabel = format(fromTs, "yyyy/MM/dd HH:mm:ss"); + const toLabel = format(toTs, "yyyy/MM/dd HH:mm:ss"); + + const timeRangeLabel = `${fromLabel}-${toLabel}`; + dispatch(setStopTime(toTs)); + dispatch(setStartTime(fromTs)); + + dispatch(setTimeRangeLabel(timeRangeLabel)); + dispatch(loadLogs()); + }, 400); + } catch (e) { + console.log("error on chart redraw", e); + } + } + + /** + * + *Isolate Series clicking label + */ + + function onLabelClick(e, v) { + let newList = []; + const lSelected = + JSON.parse(localStorage.getItem("labelsSelected")) || []; + + if (lSelected.some(({ id }) => id === v.id)) { + const filtered = lSelected.filter((f) => f.id !== v.id); + localStorage.setItem("labelsSelected", JSON.stringify(filtered)); + newList = filtered; + } else { + newList = lSelected.concat(v); + localStorage.setItem("labelsSelected", JSON.stringify(newList)); + } + + if (newList.length > 0) { + const ids = newList.map((m) => m.id); + const { lines, bars, points } = getSeriesFromChartType(chartType); + let dataSelected = e.map((series) => { + if (!ids.includes(series.id)) { + return { + ...series, + lines: { ...series.lines, show: false }, + bars: { ...series.bars, show: false }, + points: { ...series.points, show: false }, + }; + } else { + return { + ...series, + bars, + lines, + points, + }; + } + }); + const { timeformat, min, max } = formatDateRange(dataSelected); + + let plot = $q.plot( + element, + dataSelected, + + $q.extend(true, {}, chartOptions, { + series: getSeriesFromChartType(chartType), + xaxis: { timeformat, min, max, }, + }) + ); + + const colorLabels = plot.getData(); + setLabels(colorLabels); + $q(chartRef.current).UseTooltip(plot); + } else { + const data = isSpliced ? chartData : allData; + const { lines, bars, points } = getSeriesFromChartType(chartType); + const newData = data.map((series) => { + return { + ...series, + bars, + lines, + points, + isVisible: true, + }; + }); + const { timeformat, min, max } = formatDateRange(newData); + + let plot = $q.plot( + element, + newData, + $q.extend(true, {}, chartOptions, { + series: getSeriesFromChartType(chartType), + xaxis: { timeformat, min, max, }, + }) + ); + + const colorLabels = plot.getData(); + setLabels(colorLabels); + $q(chartRef.current).UseTooltip(plot); + } + } + + useEffect(() => { + setElement(chartRef.current); + setLabels(chartData.map(({ label }) => label)); + $q(chartRef.current).bind("plotselected", setRanges); + setChartData(getDataParsed(isSpliced)); + localStorage.setItem("labelsSelected", JSON.stringify([])); + }, []); + + useEffect(() => { + setChartOptions(chartOptions); + setElement(chartRef.current); + drawChartFromData(); + }, [matrixData, isSpliced]); + + function drawChartFromData() { + const data = isSpliced ? chartData : allData; + const element = $q(chartRef.current); + let newData = getNewData(data); + try { + const { timeformat, min, max } = formatDateRange(newData); + let plot = $q.plot( + element, + newData, + $q.extend(true, {}, chartOptions, { + series: getSeriesFromChartType(chartType), + xaxis: { timeformat, min, max, }, + }) + ); + + const colorLabels = plot.getData(); + setLabels(colorLabels); + $q(chartRef.current).UseTooltip(plot); + } catch (e) { + console.log(e); + } + } + + const handleNoLimitData = (e) => { + setIsSpliced(false); + }; + + const handleLimitData = (e) => { + setIsSpliced(true); + }; + + return ( +
+ + +
+ + +
+ ); +} diff --git a/src/store/createInitialState.js b/src/store/createInitialState.js index 17d6a5bc..effce3f3 100644 --- a/src/store/createInitialState.js +++ b/src/store/createInitialState.js @@ -1,68 +1,68 @@ -import * as moment from "moment"; -import { environment } from "../environment/env.dev"; -import stateFromQueryParams from "../helpers/stateFromQueryParams"; -import localService from "../services/localService"; -import localUrl from "../services/localUrl"; -const debugLocal = () => { - let isDebug = JSON.parse(localStorage.getItem("isDebug")); - if (!isDebug) { - localStorage.setItem("isDebug", JSON.stringify({ isActive: false })); - isDebug = {isActive:false} - } - - return isDebug; -}; - -export default function initialState() { - const urlState = stateFromQueryParams(); - const historyService = localService().historyStore(); - const linkService = localUrl(); - const state = { - debugMode: debugLocal().isActive|| false, - labels: [], - labelValues: [], - queryHistory: historyService.getAll() || [], - linksHistory: linkService.getAll() || [], - timeRange: [], - query: urlState.query || "", - logs: [], - matrixData: [], - loading: false, - start: - urlState.start || - new Date( - moment(Date.now()) - .subtract(5, "minutes") - .format("YYYY-MM-DDTHH:mm:ss.SSSZ") - ), - stop: - urlState.end || - new Date(moment(Date.now()).format("YYYY-MM-DDTHH:mm:ss.SSSZ")), - from: urlState.from || null, - to: urlState.to || null, - label: urlState.label || "Last 5 minutes", - messages: [], - limitLoad: false, - limit: urlState.limit || 100, - step: urlState.step || 100, - rangeOpen: false, - labelsBrowserOpen: true, - settingsMenuOpen: false, - timePickerOpen: false, - settingsDialogOpen: false, - historyOpen: false, - apiErrors: "", - urlQueryParams: urlState || {}, - urlLocation: "", - apiUrl: urlState.apiUrl || environment.apiUrl || "", - isSubmit: urlState.isSubmit || false, - isEmbed: urlState.isEmbed || false, - chartType: "line", - notifications: [], - theme: "darkTheme", - }; - const debug = state.debugMode; - if (debug) console.log("🚧 LOGIC/ INITIAL STATE ::: ", state); - - return state; -} +import * as moment from "moment"; +import { environment } from "../environment/env.dev"; +import stateFromQueryParams from "../helpers/stateFromQueryParams"; +import localService from "../services/localService"; +import localUrl from "../services/localUrl"; +const debugLocal = () => { + let isDebug = JSON.parse(localStorage.getItem("isDebug")); + if (!isDebug) { + localStorage.setItem("isDebug", JSON.stringify({ isActive: false })); + isDebug = {isActive:false} + } + + return isDebug; +}; + +export default function initialState() { + const urlState = stateFromQueryParams(); + const historyService = localService().historyStore(); + const linkService = localUrl(); + const state = { + debugMode: debugLocal().isActive|| false, + labels: [], + labelValues: [], + queryHistory: historyService.getAll() || [], + linksHistory: linkService.getAll() || [], + timeRange: [], + query: urlState.query || "", + logs: [], + matrixData: [], + loading: false, + start: + urlState.start || + new Date( + moment(Date.now()) + .subtract(5, "minutes") + .format("YYYY-MM-DDTHH:mm:ss.SSSZ") + ), + stop: + urlState.end || + new Date(moment(Date.now()).format("YYYY-MM-DDTHH:mm:ss.SSSZ")), + from: urlState.from || null, + to: urlState.to || null, + label: urlState.label || "Last 5 minutes", + messages: [], + limitLoad: false, + limit: urlState.limit || 100, + step: urlState.step || 100, + rangeOpen: false, + labelsBrowserOpen: false, + settingsMenuOpen: false, + timePickerOpen: false, + settingsDialogOpen: false, + historyOpen: false, + apiErrors: "", + urlQueryParams: urlState || {}, + urlLocation: "", + apiUrl: urlState.apiUrl || environment.apiUrl || "", + isSubmit: urlState.isSubmit || false, + isEmbed: urlState.isEmbed || false, + chartType: "line", + notifications: [], + theme: "darkTheme", + }; + const debug = state.debugMode; + if (debug) console.log("🚧 LOGIC/ INITIAL STATE ::: ", state); + + return state; +} From 8f1dedf3c189bc1981c1f697f0cab36f2ed3aa82 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 16:39:43 +0200 Subject: [PATCH 04/14] fix alerts handler --- src/actions/LoadLabels.js | 4 - src/actions/errorHandler.js | 48 ++++-- src/actions/loadLogs.js | 25 ++-- src/actions/setApiWarning.js | 8 + src/components/LabelBrowser/QueryBar.js | 13 +- src/components/LabelBrowser/ValuesList.js | 65 ++++---- .../ShowLabelsButton/ShowLabelsButton.js | 7 +- .../components/apiselector/ApiSelector.js | 2 + src/helpers/error.interceptor.js | 61 +++++--- src/store/createInitialState.js | 8 +- src/store/reducer.js | 140 +++++++++--------- 11 files changed, 208 insertions(+), 173 deletions(-) create mode 100644 src/actions/setApiWarning.js diff --git a/src/actions/LoadLabels.js b/src/actions/LoadLabels.js index f786c533..04be61fb 100644 --- a/src/actions/LoadLabels.js +++ b/src/actions/LoadLabels.js @@ -2,8 +2,6 @@ import axios from "axios"; import { setLabels } from "./setLabels"; import setLoading from "./setLoading"; import { setApiError } from "./setApiError"; -import { errorHandler } from "./errorHandler"; -import { setLabelsBrowserOpen } from "./setLabelsBrowserOpen"; export default function loadLabels(apiUrl) { const origin = window.location.origin; @@ -51,8 +49,6 @@ export default function loadLabels(apiUrl) { .catch((error) => { console.log(error); dispatch(setLoading(false)); - const { message, status } = errorHandler(url, error,'labels'); - dispatch(setApiError(`Status: ${status}, ${message}`)); dispatch(setLabels([])); }); diff --git a/src/actions/errorHandler.js b/src/actions/errorHandler.js index d4af4162..2da155d8 100644 --- a/src/actions/errorHandler.js +++ b/src/actions/errorHandler.js @@ -1,53 +1,69 @@ -export const errorHandler = (url, error, type) => { +export const errorHandler = (error) => { + + const LABELS_URL = "/loki/api/v1/labels"; + const QUERY_URL = "/loki/api/v1/query_range"; + const { request, response } = error; - console.log(url) - console.log(error) + const url = error?.response?.request?.responseURL + + + let type = () => { + switch(url) { + case url?.includes(LABELS_URL): + return 'labels'; + case url?.includes(QUERY_URL): + return 'query' + default: return 'labels' + } + } + if (response?.statusText) { const status = response?.status; - + return { message: "API " + response.statusText + ", Please adjust API URL", status, - type + type: type() }; } else if (url && !url.includes(window.location.protocol)) { - + return { message: "Mixed Content Error, your View should be over same protocol as your API", status: 500, - type + type : type() }; } else if (request) { - if(error.stack.includes('Network Error')) { + if (error.stack.includes('Network Error')) { return { message: "Invalid API URL, please adjust API URL", - status:500 + status: 500, + type: type() } } return { message: "server time out", status: response?.status, - type + type: type() }; - } else if (error.stack.includes("Invalid URL")) { + } else if (error?.stack?.includes("Invalid URL")) { return { message: "Invalid API URL, please adjust API URL", stauts: response?.status, - type + type: type() }; } else if (error?.response?.status === 404) { return { message: "Invalid API URL, please adjust API URL", status: response?.status, - type + type: type() }; } else { - if(type==='labels') return; - + if (type === 'labels') return; + return { message: "something went wrong with request", status: response?.status, - type + type: type() }; } }; diff --git a/src/actions/loadLogs.js b/src/actions/loadLogs.js index 8ba83cf3..da20d9d4 100644 --- a/src/actions/loadLogs.js +++ b/src/actions/loadLogs.js @@ -29,8 +29,7 @@ export default function loadLogs() { const parsedStart = getTimeParsed(startTs); const parsedStop = getTimeParsed(stopTs); - const parsedTime = - "&start=" + (from || parsedStart) + "&end=" + (to || parsedStop); + const parsedTime = "&start=" + (from || parsedStart) + "&end=" + (to || parsedStop); if (findRangeByLabel(rangeLabel)) { ({ dateStart: startTs, dateEnd: stopTs } = @@ -39,14 +38,10 @@ export default function loadLogs() { store.dispatch(setStartTime(startTs)); store.dispatch(setStopTime(stopTs)); - const origin = window.location.origin; const url = apiUrl; - const queryStep = `&step=${step || 120}`; - const encodedQuery = `${encodeURIComponent(query)}`; - const getUrl = `${url}/loki/api/v1/query_range?query=${encodedQuery}&limit=${limit}${parsedTime}${queryStep}`; const options = { @@ -63,8 +58,8 @@ export default function loadLogs() { type === "streams" ? fromNanoSec(ts) : type === "matrix" - ? toMiliseC(ts) - : ts; + ? toMiliseC(ts) + : ts; const mapStreams = (streams, messages, type) => { streams.forEach((stream) => { stream.values.forEach((log, i) => { @@ -77,8 +72,8 @@ export default function loadLogs() { type === "streams" ? stream.stream : type === "matrix" - ? stream.metric - : {}, + ? stream.metric + : {}, showTs: true, showLabels: false, id: nanoid(), @@ -121,18 +116,20 @@ export default function loadLogs() { } dispatch(setLoading(false)); } else { - dispatch(setLogs([])); - dispatch(setMatrixData([])); + dispatch(setLogs([])); + dispatch(setMatrixData([])); dispatch(setLoading(false)); } dispatch(setLoading(false)); }) .catch((error) => { + dispatch(setLogs([])); dispatch(setMatrixData([])); + dispatch(setLoading(false)); - const handler = errorHandler(url,error,type) - console.log(handler) + const handler = errorHandler(url, error, type) + }); }; } diff --git a/src/actions/setApiWarning.js b/src/actions/setApiWarning.js new file mode 100644 index 00000000..d82f2361 --- /dev/null +++ b/src/actions/setApiWarning.js @@ -0,0 +1,8 @@ +export default function setApiWarning(apiWarning){ + return function(dispatch){ + dispatch({ + type:'SET_API_WARNING', + apiWarning + }) + } +} \ No newline at end of file diff --git a/src/components/LabelBrowser/QueryBar.js b/src/components/LabelBrowser/QueryBar.js index 8396f6b2..d16a08a7 100644 --- a/src/components/LabelBrowser/QueryBar.js +++ b/src/components/LabelBrowser/QueryBar.js @@ -31,9 +31,9 @@ export const QueryBar = () => { const debug = useSelector((store) => store.debugMode); const query = useSelector((store) => store.query); const apiUrl = useSelector((store) => store.apiUrl); - const isSubmit = useSelector((store) => store.isSubmit); const historyOpen = useSelector((store) => store.historyOpen); const isEmbed = useSelector((store) => store.isEmbed) + const apiWarning = useSelector((store) => store.apiWarning) const [queryInput, setQueryInput] = useState(query); const [queryValid, setQueryValid] = useState(false); const [queryValue, setQueryValue] = useState(queryInit(query)); @@ -43,21 +43,17 @@ export const QueryBar = () => { useEffect(() => { const dLog = debugLog(query); debug && dLog.logicQueryBar(); - const labels = sendLabels(apiUrl) if (isEmbed) dispatch(loadLogs()) if (query.length > 0) { debug && dLog.queryBarDispatch(); dispatch(setLoading(true)); - - return labels.then(data => { decodeQuery(query, apiUrl, data) }) } - }, []); useEffect(() => { @@ -71,7 +67,7 @@ export const QueryBar = () => { const isOpen = labelsBrowserOpen ? false : true; if (isOpen) { dispatch(loadLabels(apiUrl)); - + } dispatch(setLabelsBrowserOpen(isOpen)); @@ -93,9 +89,7 @@ export const QueryBar = () => { const onSubmit = (e) => { e.preventDefault(); - dispatch(setQuery(queryInput)); - if (onQueryValid(queryInput)) { try { const historyUpdated = historyService.add({ @@ -105,10 +99,7 @@ export const QueryBar = () => { dispatch(setQueryHistory(historyUpdated)); dispatch(setLabelsBrowserOpen(false)); decodeQuery(queryInput, apiUrl, labels) - - dispatch(setLoading(true)); - dispatch(loadLogs()); const storedUrl = saveUrl.add({ data: window.location.href, diff --git a/src/components/LabelBrowser/ValuesList.js b/src/components/LabelBrowser/ValuesList.js index 8f955657..620f85d4 100644 --- a/src/components/LabelBrowser/ValuesList.js +++ b/src/components/LabelBrowser/ValuesList.js @@ -55,11 +55,9 @@ export const ValuesList = (props) => { const dispatch = useDispatch(); const debug = useSelector((store) => store.debugMode); const apiUrl = useSelector((store) => store.apiUrl); - //if(debug) console.log('🚧 LOGIC/LabelBrowser/ValuesList', apiUrl) + if (debug) console.log('🚧 LOGIC/LabelBrowser/ValuesList', apiUrl) const labelsBrowserOpen = useSelector((store) => store.labelsBrowserOpen); - const CLEAR = "clear"; - useEffect(() => { dispatch(loadLabels(apiUrl)); }, [apiUrl]); @@ -76,12 +74,9 @@ export const ValuesList = (props) => { const onLabelOpen = (e, value) => { e.preventDefault(); value.selected = !value.selected; - // setLabel(value); const selected = labelList.filter((f) => f.selected); setLabelsSelected(selected); - // setFilteredPlaceholder(value); - const query = queryBuilder(labelList); dispatch(setQuery(query)); @@ -161,11 +156,11 @@ export const ValuesList = (props) => { {selectedList() && (
- { labelsSelected.map((labelSelected, skey) => ( + {labelsSelected.map((labelSelected, skey) => (
- + {labelSelected.name} ( {labelSelected.values.length}) @@ -186,37 +181,37 @@ export const ValuesList = (props) => { "valuelist-content column" } > - + {labelSelected?.values?.map( - ( value, key) => ( + (value, key) => ( - isString(value.name) ? ( + isString(value.name) ? ( - - - onLabelValueClick( - e, - value - ) - } + title={value?.name || ''} + key={key} + placement="bottom" > - - {value.name} - - - ):(unknown) - + + + onLabelValueClick( + e, + value + ) + } + > + + {value.name} + + + ) : (unknown) + ) )}
diff --git a/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js b/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js index 94959393..940d686b 100644 --- a/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js +++ b/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js @@ -6,17 +6,16 @@ import { useSelector } from "react-redux"; export default function ShowLabelsButton({ onValueDisplay, labelsBrowserOpen, isMobile }) { const LOG_BROWSER = "Labels"; - const labels = useSelector( store => store.labels) + const labels = useSelector(store => store.labels) return ( 0 ? 'Show / Hide Labels' : 'Labels Not Available' } + title={labels?.length > 0 ? 'Show / Hide Labels' : 'Labels Not Available'} onClick={onValueDisplay} browserActive={labelsBrowserOpen} isMobile={isMobile} disabled={labels?.length < 1} - > {labelsBrowserOpen ? ( @@ -25,6 +24,6 @@ export default function ShowLabelsButton({ onValueDisplay, labelsBrowserOpen, is )}{" "} {LOG_BROWSER} - + ); } \ No newline at end of file diff --git a/src/components/StatusBar/components/apiselector/ApiSelector.js b/src/components/StatusBar/components/apiselector/ApiSelector.js index ffa81155..15f69807 100644 --- a/src/components/StatusBar/components/apiselector/ApiSelector.js +++ b/src/components/StatusBar/components/apiselector/ApiSelector.js @@ -52,6 +52,8 @@ export function ApiSelector() { }; const onUrlSubmit = (e) => { dispatch(setApiUrl(editedUrl)); + setEditedUrl(editedUrl); + dispatch(loadLabels(editedUrl)) dispatch(setLabelsBrowserOpen(false)); }; diff --git a/src/helpers/error.interceptor.js b/src/helpers/error.interceptor.js index d95113ab..4260a075 100644 --- a/src/helpers/error.interceptor.js +++ b/src/helpers/error.interceptor.js @@ -1,32 +1,57 @@ import store from '../store/store' -import { errorHandler, setApiError,createAlert } from "../actions/"; +import { errorHandler, setApiError, createAlert } from "../actions/"; +import setApiWarning from '../actions/setApiWarning'; const errorInterceptor = (axiosInstance) => { axiosInstance.interceptors.response.use( (response) => { - //Response Successful + return response; }, + (error) => { - console.log("ERROR", error) + + if (error.response) { + const handler = errorHandler(error) + if (error?.response?.status === 401) { - if (error?.response?.status === 401) { - //Unauthorized - //redirect to Login + } + if (handler.status === 500 && handler.type === 'labels') { + + } + else { + store.dispatch(createAlert({ + type: "error", + message: (handler.message + " for " + handler.type || status + handler.type + 'Error') + })) + + } } else { - const url = error?.response?.config?.url || "" - console.log(error.response) - const { message, status, type } = errorHandler(url, error, 'intercepted') - - console.log(message,status,type) - // console.log(errorHandler(url, error)) - // store.dispatch(setApiError(message || status + 'Error')) - - store.dispatch(createAlert({ - type:"error", - message: (message || status + 'Error') - })) + + const error_parsed = JSON.parse(JSON.stringify(error)); + const networkError = { + url: error_parsed.config.url, + message: error_parsed.message, + name: error_parsed.name + } + + store.dispatch(setApiWarning({ type: 'labels', message: 'Labels not available', })) + const{url,message,name} = networkError + + const apiWarning = store.getState().apiWarning + if(apiWarning && url.includes('query') ) { + apiWarning.num ++ + store.dispatch(createAlert({ + type:'error', + message: `API not found, please adjust API URL` + })) + } + } + + + + } ); }; diff --git a/src/store/createInitialState.js b/src/store/createInitialState.js index effce3f3..8e21481d 100644 --- a/src/store/createInitialState.js +++ b/src/store/createInitialState.js @@ -3,6 +3,7 @@ import { environment } from "../environment/env.dev"; import stateFromQueryParams from "../helpers/stateFromQueryParams"; import localService from "../services/localService"; import localUrl from "../services/localUrl"; + const debugLocal = () => { let isDebug = JSON.parse(localStorage.getItem("isDebug")); if (!isDebug) { @@ -17,6 +18,8 @@ export default function initialState() { const urlState = stateFromQueryParams(); const historyService = localService().historyStore(); const linkService = localUrl(); + const lastQuery = linkService.getAll()?.[0]?.params + const state = { debugMode: debugLocal().isActive|| false, labels: [], @@ -24,7 +27,7 @@ export default function initialState() { queryHistory: historyService.getAll() || [], linksHistory: linkService.getAll() || [], timeRange: [], - query: urlState.query || "", + query: urlState.query || decodeURIComponent(lastQuery.query) || "", logs: [], matrixData: [], loading: false, @@ -54,7 +57,8 @@ export default function initialState() { apiErrors: "", urlQueryParams: urlState || {}, urlLocation: "", - apiUrl: urlState.apiUrl || environment.apiUrl || "", + apiUrl: urlState.apiUrl || environment.apiUrl || "", + apiWarning:{}, isSubmit: urlState.isSubmit || false, isEmbed: urlState.isEmbed || false, chartType: "line", diff --git a/src/store/reducer.js b/src/store/reducer.js index 783cdbd2..81a7b2d3 100644 --- a/src/store/reducer.js +++ b/src/store/reducer.js @@ -1,69 +1,71 @@ -const reducer = (state, action) => { - switch (action.type) { - case "SET_LABELS": - return { ...state, labels: action.labels }; - case "SET_LOADING": - return { ...state, loading: action.loading }; - case "SET_LOGS": - return { ...state, logs: action.logs }; - case "SET_LABEL_VALUES": - return { ...state, labelValues: action.labelValues }; - case "SET_START_TIME": - return { ...state, start: action.start }; - case "SET_STOP_TIME": - return { ...state, stop: action.stop }; - case "SET_FROM_TIME": - return { ...state, from: action.from }; - case "SET_TO_TIME": - return { ...state, to: action.to }; - case "SET_TIME_RANGE_LABEL": - return { ...state, label: action.label }; - case "SET_QUERY_LIMIT": - return { ...state, limit: action.limit }; - case "SET_RANGE_OPEN": - return { ...state, rangeOpen: action.rangeOpen }; - case "SET_BROWSER_OPEN": - return { ...state, labelsBrowserOpen: action.labelsBrowserOpen }; - case "SET_SETTINGS_MENU_OPEN": - return { ...state, settingsMenuOpen: action.settingsMenuOpen }; - case "SET_TIME_PICKER_OPEN": - return { ...state, timePickerOpen: action.timePickerOpen }; - case "SET_SETTINGS_DIALOG_OPEN": - return { ...state, settingsDialogOpen: action.settingsDialogOpen }; - case "SET_QUERY": - return { ...state, query: action.query }; - case "SET_QUERY_STEP": - return { ...state, step: action.step }; - case "SET_API_URL": - return { ...state, apiUrl: action.apiUrl }; - case "SET_API_ERRORS": - return { ...state, apiErrors: action.apiErrors }; - case "SET_URL_QUERY_PARAMS": - return { ...state, urlQueryParams: action.urlQueryParams }; - case "SET_URL_LOCATION": - return { ...state, urlLocation: action.urlLocation }; - case "SET_IS_SUBMIT": - return { ...state, isSubmit: action.isSubmit }; - case "SET_IS_EMBED": - return { ...state, isEmbed: action.isEmbed }; - case "SET_MATRIX_DATA": - return { ...state, matrixData: action.matrixData }; - case "SET_CHART_TYPE": - return { ...state, chartType: action.setChartType }; - case "SET_QUERY_HISTORY": - return { ...state, queryHistory: action.queryHistory }; - case "SET_LINKS_HISTORY": - return { ...state, linksHistory: action.linksHistory }; - case "SET_HISTORY_OPEN": - return { ...state, historyOpen: action.historyOpen }; - case "ADD_NOTIFICATION": - return { ...state, notifications: action.payload }; - case "REMOVE_NOTIFICATION": - return { ...state, notifications: action.payload }; - case "SET_DEBUG_MODE": - return { ...state, debugMode: action.debugMode }; - default: - return { ...state }; - } -}; -export default reducer; +const reducer = (state, action) => { + switch (action.type) { + case "SET_LABELS": + return { ...state, labels: action.labels }; + case "SET_LOADING": + return { ...state, loading: action.loading }; + case "SET_LOGS": + return { ...state, logs: action.logs }; + case "SET_LABEL_VALUES": + return { ...state, labelValues: action.labelValues }; + case "SET_START_TIME": + return { ...state, start: action.start }; + case "SET_STOP_TIME": + return { ...state, stop: action.stop }; + case "SET_FROM_TIME": + return { ...state, from: action.from }; + case "SET_TO_TIME": + return { ...state, to: action.to }; + case "SET_TIME_RANGE_LABEL": + return { ...state, label: action.label }; + case "SET_QUERY_LIMIT": + return { ...state, limit: action.limit }; + case "SET_RANGE_OPEN": + return { ...state, rangeOpen: action.rangeOpen }; + case "SET_BROWSER_OPEN": + return { ...state, labelsBrowserOpen: action.labelsBrowserOpen }; + case "SET_SETTINGS_MENU_OPEN": + return { ...state, settingsMenuOpen: action.settingsMenuOpen }; + case "SET_TIME_PICKER_OPEN": + return { ...state, timePickerOpen: action.timePickerOpen }; + case "SET_SETTINGS_DIALOG_OPEN": + return { ...state, settingsDialogOpen: action.settingsDialogOpen }; + case "SET_QUERY": + return { ...state, query: action.query }; + case "SET_QUERY_STEP": + return { ...state, step: action.step }; + case "SET_API_URL": + return { ...state, apiUrl: action.apiUrl }; + case "SET_API_ERRORS": + return { ...state, apiErrors: action.apiErrors }; + case "SET_URL_QUERY_PARAMS": + return { ...state, urlQueryParams: action.urlQueryParams }; + case "SET_URL_LOCATION": + return { ...state, urlLocation: action.urlLocation }; + case "SET_IS_SUBMIT": + return { ...state, isSubmit: action.isSubmit }; + case "SET_IS_EMBED": + return { ...state, isEmbed: action.isEmbed }; + case "SET_MATRIX_DATA": + return { ...state, matrixData: action.matrixData }; + case "SET_CHART_TYPE": + return { ...state, chartType: action.setChartType }; + case "SET_QUERY_HISTORY": + return { ...state, queryHistory: action.queryHistory }; + case "SET_LINKS_HISTORY": + return { ...state, linksHistory: action.linksHistory }; + case "SET_HISTORY_OPEN": + return { ...state, historyOpen: action.historyOpen }; + case "ADD_NOTIFICATION": + return { ...state, notifications: action.payload }; + case "REMOVE_NOTIFICATION": + return { ...state, notifications: action.payload }; + case "SET_DEBUG_MODE": + return { ...state, debugMode: action.debugMode }; + case "SET_API_WARNING": + return { ...state, apiWarning: action.apiWarning}; + default: + return { ...state }; + } +}; +export default reducer; From 3d5a0fbaf5f3f8560f1b22622a1420850735c0e3 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 17:30:00 +0200 Subject: [PATCH 05/14] fix alert repetition --- src/helpers/error.interceptor.js | 47 +++-- src/plugins/queryeditor/index.js | 326 +++++++++++++++---------------- src/store/createInitialState.js | 2 +- 3 files changed, 196 insertions(+), 179 deletions(-) diff --git a/src/helpers/error.interceptor.js b/src/helpers/error.interceptor.js index 4260a075..9760c4b7 100644 --- a/src/helpers/error.interceptor.js +++ b/src/helpers/error.interceptor.js @@ -12,19 +12,40 @@ const errorInterceptor = (axiosInstance) => { (error) => { if (error.response) { + const handler = errorHandler(error) + if (error?.response?.status === 401) { - - } - if (handler.status === 500 && handler.type === 'labels') { + + } + else if (handler.status === 500 && handler.type === 'labels') { + + if (store.getState().notifications.length < 1 && store.getState().debugMode === true) { + store.dispatch(createAlert({ + type: "error", + message: (handler.message + " for " + handler.type || status + handler.type + 'Error') + })) + } + } + + else if (handler.status === 404 && handler.type === 'labels') { + + if (store.getState().notifications.length < 1) { + store.dispatch(createAlert({ + type: "error", + message: (handler.message || status + handler.type + 'Error') + })) + } } else { - store.dispatch(createAlert({ - type: "error", - message: (handler.message + " for " + handler.type || status + handler.type + 'Error') - })) + if (store.getState().notifications.length < 1) { + store.dispatch(createAlert({ + type: "error", + message: (handler.message + " for " + handler.type || status + handler.type + 'Error') + })) + } } } else { @@ -36,22 +57,18 @@ const errorInterceptor = (axiosInstance) => { } store.dispatch(setApiWarning({ type: 'labels', message: 'Labels not available', })) - const{url,message,name} = networkError + const { url, message, name } = networkError const apiWarning = store.getState().apiWarning - if(apiWarning && url.includes('query') ) { - apiWarning.num ++ + if (apiWarning && url.includes('query')) { + apiWarning.num++ store.dispatch(createAlert({ - type:'error', + type: 'error', message: `API not found, please adjust API URL` })) } } - - - - } ); }; diff --git a/src/plugins/queryeditor/index.js b/src/plugins/queryeditor/index.js index 8e077548..729c88f4 100644 --- a/src/plugins/queryeditor/index.js +++ b/src/plugins/queryeditor/index.js @@ -1,163 +1,163 @@ -import styled from "@emotion/styled"; -import { css } from "@emotion/css"; -import React, { useCallback, useState, useMemo, useEffect } from "react"; - -import { createEditor, Text } from "slate"; -import { Slate, Editable, withReact } from "slate-react"; -import { withHistory } from "slate-history"; -import Prism from "prismjs"; -import "prismjs/components/prism-promql"; -import "prismjs/components/prism-sql"; -import darkTheme from "../../theme/dark"; -const theme = darkTheme; -const CustomEditor = styled(Editable)` - flex: 1; - background: ${theme.inputBg}; - color: ${theme.textColor}; - padding: 4px 8px; - font-size: 1em; - font-family: monospace; - margin: 0px 5px; - border-radius: 3px; - line-height: 1.5; - line-break: anywhere; -`; - -const QueryBar = styled.div` - display: flex; - align-items: center; - flex: 1; - max-width: 100%; -`; - -function Leaf({ attributes, children, leaf }) { - return ( - - {children} - - ); -} - -export default function QueryEditor({ onQueryChange, value, onKeyDown }) { - const renderLeaf = useCallback((props) => , []); - - const editor = useMemo(() => withHistory(withReact(createEditor())), []); - // Keep track of state for the value of the editor. - - const [language] = useState("sql"); - - const decorate = useCallback( - ([node, path]) => { - const ranges = []; - if (!Text.isText(node) || node.length < 1) { - return ranges; - } - const tokens = Prism.tokenize(node.text, Prism.languages[language]); - let start = 0; - for (const token of tokens) { - const length = getLength(token); - const end = start + length; - - if (typeof token !== "string") { - ranges.push({ - [token.type]: true, - anchor: { path, offset: start }, - focus: { path, offset: end }, - }); - } - start = end; - } - return ranges; - }, - [language] - ); - - function getLength(token) { - if (typeof token === "string") { - return token.length; - } else if (typeof token.content === "string") { - return token.content.length; - } else { - return token.content.reduce((l, t) => l + getLength(t), 0); - } - } - - const [editorValue, setEditorValue] = useState(value); - - useEffect(() => { - setEditorValue(value); - }, []); - useEffect(() => { - setEditorValue(value); - editor.children = value; - }, [value]); - return ( - - {/* */} - - - - - ); -} +import styled from "@emotion/styled"; +import { css } from "@emotion/css"; +import React, { useCallback, useState, useMemo, useEffect } from "react"; + +import { createEditor, Text } from "slate"; +import { Slate, Editable, withReact } from "slate-react"; +import { withHistory } from "slate-history"; +import Prism from "prismjs"; +import "prismjs/components/prism-promql"; +import "prismjs/components/prism-sql"; +import darkTheme from "../../theme/dark"; +const theme = darkTheme; +const CustomEditor = styled(Editable)` + flex: 1; + background: ${theme.inputBg}; + color: ${theme.textColor}; + padding: 4px 8px; + font-size: 1em; + font-family: monospace; + margin: 0px 5px; + border-radius: 3px; + line-height: 1.5; + line-break: anywhere; +`; + +const QueryBar = styled.div` + display: flex; + align-items: center; + flex: 1; + max-width: 100%; +`; + +function Leaf({ attributes, children, leaf }) { + return ( + + {children} + + ); +} + +export default function QueryEditor({ onQueryChange, value, onKeyDown }) { + const renderLeaf = useCallback((props) => , []); + + const editor = useMemo(() => withHistory(withReact(createEditor())), []); + // Keep track of state for the value of the editor. + + const [language] = useState("sql"); + + const decorate = useCallback( + ([node, path]) => { + const ranges = []; + if (!Text.isText(node) || node.length < 1) { + return ranges; + } + const tokens = Prism.tokenize(node.text, Prism.languages[language]); + let start = 0; + for (const token of tokens) { + const length = getLength(token); + const end = start + length; + + if (typeof token !== "string") { + ranges.push({ + [token.type]: true, + anchor: { path, offset: start }, + focus: { path, offset: end }, + }); + } + start = end; + } + return ranges; + }, + [language] + ); + + function getLength(token) { + if (typeof token === "string") { + return token.length; + } else if (typeof token.content === "string") { + return token.content.length; + } else { + return token.content.reduce((l, t) => l + getLength(t), 0); + } + } + + const [editorValue, setEditorValue] = useState(value); + + useEffect(() => { + setEditorValue(value); + }, []); + useEffect(() => { + setEditorValue(value); + editor.children = value; + }, [value]); + return ( + + {/* */} + + + + + ); +} diff --git a/src/store/createInitialState.js b/src/store/createInitialState.js index 8e21481d..89341cab 100644 --- a/src/store/createInitialState.js +++ b/src/store/createInitialState.js @@ -27,7 +27,7 @@ export default function initialState() { queryHistory: historyService.getAll() || [], linksHistory: linkService.getAll() || [], timeRange: [], - query: urlState.query || decodeURIComponent(lastQuery.query) || "", + query: urlState.query || "", logs: [], matrixData: [], loading: false, From 6f9b1981ac06937134dac314a3056db86aba1cdc Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 18:12:35 +0200 Subject: [PATCH 06/14] rm disaled --- src/actions/LoadLabels.js | 2 ++ .../components/ShowLabelsButton/ShowLabelsButton.js | 7 ++++--- .../StatusBar/components/apiselector/ApiSelector.js | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/actions/LoadLabels.js b/src/actions/LoadLabels.js index 04be61fb..262579bf 100644 --- a/src/actions/LoadLabels.js +++ b/src/actions/LoadLabels.js @@ -36,6 +36,7 @@ export default function loadLabels(apiUrl) { values: [], })); dispatch(setLabels(labels || [])); + console.log(labels) dispatch(setApiError("")); } } else { @@ -44,6 +45,7 @@ export default function loadLabels(apiUrl) { setApiError("") ); dispatch(setLabels([])); + } }) .catch((error) => { diff --git a/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js b/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js index 940d686b..7851497f 100644 --- a/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js +++ b/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js @@ -3,10 +3,11 @@ import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; import { useSelector } from "react-redux"; - export default function ShowLabelsButton({ onValueDisplay, labelsBrowserOpen, isMobile }) { + const LOG_BROWSER = "Labels"; const labels = useSelector(store => store.labels) + return ( {labelsBrowserOpen ? ( @@ -26,4 +27,4 @@ export default function ShowLabelsButton({ onValueDisplay, labelsBrowserOpen, is ); -} \ No newline at end of file +} diff --git a/src/components/StatusBar/components/apiselector/ApiSelector.js b/src/components/StatusBar/components/apiselector/ApiSelector.js index 15f69807..34651159 100644 --- a/src/components/StatusBar/components/apiselector/ApiSelector.js +++ b/src/components/StatusBar/components/apiselector/ApiSelector.js @@ -17,7 +17,7 @@ export function ApiSelector() { const apiUrl = useSelector((store) => store.apiUrl); const apiError = useSelector((store) => store.apiErrors); const [editedUrl, setEditedUrl] = useState(apiUrl); - const query = useSelector((store) => store.query); + const [apiSelectorOpen, setApiSelectorOpen] = useState(false); const dispatch = useDispatch(); const API_URL = "API URL"; From d04f13bb004662041ab48af8ffe999b9d319550a Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 18:13:29 +0200 Subject: [PATCH 07/14] rm log --- src/actions/LoadLabels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/LoadLabels.js b/src/actions/LoadLabels.js index 262579bf..d580bde1 100644 --- a/src/actions/LoadLabels.js +++ b/src/actions/LoadLabels.js @@ -36,7 +36,7 @@ export default function loadLabels(apiUrl) { values: [], })); dispatch(setLabels(labels || [])); - console.log(labels) + dispatch(setApiError("")); } } else { From b47460a419fed493bba3ceb21bd509f53a9f6e4f Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 18:17:15 +0200 Subject: [PATCH 08/14] fix file name --- src/actions/index.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index 5fefe495..43b883f7 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -1,19 +1,19 @@ -export * from "./setStartTime"; -export * from "./setStopTime"; -export * from "./setQueryLimit"; -export * from "./setQueryStep"; -export * from "./setRangeOpen"; -export * from "./setTimeRangeLabel"; -export * from "./setApiUrl"; -export * from "./setQuery"; -export * from "./setIsSubmit"; -export * from "./setMatrixData"; -export * from "./setQueryHistory"; -export * from "./setHistoryOpen"; -export * from "./setApiError"; -export * from "./errorHandler"; -export * from "./setLabels"; -export * from "./createAlert"; -export * from "./removeAlert"; -export * from "./setFromTime"; -export * from "./setToTime"; \ No newline at end of file +export * from "./setStartTime"; +export * from "./setStopTime"; +export * from "./setQueryLimit"; +export * from "./setQueryStep"; +export * from "./setRangeOpen"; +export * from "./setTimeRangeLabel"; +export * from "./setApiUrl"; +export * from "./setQuery"; +export * from "./setIsSubmit"; +export * from "./setMatrixData"; +export * from "./setQueryHistory"; +export * from "./setHistoryOpen"; +export * from "./setApiError"; +export * from "./errorHandler"; +export * from "./setLabels"; +export * from "./createAlert"; +export * from "./removeAlert"; +export * from "./setFromTime"; +export * from "./setToTime"; From 56824997c7e5b9f966333acfa55bdb770cad7b4b Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 18:24:37 +0200 Subject: [PATCH 09/14] fix file not used --- src/helpers/UpdateStateFromQueryParams.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/helpers/UpdateStateFromQueryParams.js b/src/helpers/UpdateStateFromQueryParams.js index 5b9bc83e..db404fac 100644 --- a/src/helpers/UpdateStateFromQueryParams.js +++ b/src/helpers/UpdateStateFromQueryParams.js @@ -14,7 +14,6 @@ import { } from "../actions"; -import loadLabels from "../actions/loadLabels" import loadLabelValues from "../actions/loadLabelValues"; import setFromTime from "../actions/setFromTime"; @@ -105,7 +104,7 @@ export function UpdateStateFromQueryParams() { dispatch(STORE_ACTIONS[param](startParams[param])); if(param === 'apiUrl') { - // dispatch(loadLabels(startParams[param])) + } } else if ( QUERY_VALUE === param && From 10e270d6f29cafc6da73e5e43b54d05b1153814f Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 18:32:56 +0200 Subject: [PATCH 10/14] fix file imports --- src/components/LabelBrowser/QueryBar.js | 2 +- src/components/LabelBrowser/ValuesList.js | 3 ++- src/components/StatusBar/components/apiselector/ApiSelector.js | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/LabelBrowser/QueryBar.js b/src/components/LabelBrowser/QueryBar.js index d16a08a7..7975ec59 100644 --- a/src/components/LabelBrowser/QueryBar.js +++ b/src/components/LabelBrowser/QueryBar.js @@ -12,7 +12,6 @@ import localUrl from "../../services/localUrl"; import setLinksHistory from "../../actions/setLinksHistory"; import QueryEditor from "../../plugins/queryeditor"; -import loadLabels from "../../actions/loadLabels"; import { decodeQuery } from "../../helpers/UpdateStateFromQueryParams"; import { css } from "@emotion/css"; import { MobileTopQueryMenu, QueryBarContainer } from "./components/styled"; @@ -23,6 +22,7 @@ import queryInit from "./helpers/queryInit"; import onQueryValid from "./helpers/onQueryValid"; import debugLog from "./helpers/debugLog"; import { sendLabels } from "../../hooks/useLabels"; +import loadLabels from "../../actions/loadLabels"; export const QueryBar = () => { const dispatch = useDispatch(); diff --git a/src/components/LabelBrowser/ValuesList.js b/src/components/LabelBrowser/ValuesList.js index 620f85d4..bbd80359 100644 --- a/src/components/LabelBrowser/ValuesList.js +++ b/src/components/LabelBrowser/ValuesList.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import { Legend } from "./Legend"; import { useSelector, useDispatch } from "react-redux"; import RefreshIcon from "@mui/icons-material/Refresh"; -import loadLabels from "../../actions/loadLabels"; + import { queryBuilder } from "./helpers/querybuilder"; import { setQuery } from "../../actions"; import loadLabelValues from "../../actions/loadLabelValues"; @@ -10,6 +10,7 @@ import loadLabelValues from "../../actions/loadLabelValues"; import Tooltip from "@mui/material/Tooltip"; import store from "../../store/store"; import styled from "@emotion/styled"; +import loadLabels from "../../actions/loadLabels"; const ErrorContainer = styled.div` padding: 20px; diff --git a/src/components/StatusBar/components/apiselector/ApiSelector.js b/src/components/StatusBar/components/apiselector/ApiSelector.js index 34651159..9b56455b 100644 --- a/src/components/StatusBar/components/apiselector/ApiSelector.js +++ b/src/components/StatusBar/components/apiselector/ApiSelector.js @@ -1,6 +1,6 @@ import { useDispatch, useSelector } from "react-redux"; import { useState, useEffect } from "react"; -import loadLabels from "../../../../actions/loadLabels"; + import { setLabelsBrowserOpen } from "../../../../actions/setLabelsBrowserOpen"; import setMatrixData from "../../../../actions/setMatrixData"; import loadLogs from "../../../../actions/loadLogs"; @@ -12,6 +12,7 @@ import { ApiSelectorInput, ApiSelectorStyled, } from "../../styled"; +import loadLabels from "../../../../actions/loadLabels"; export function ApiSelector() { const apiUrl = useSelector((store) => store.apiUrl); From 3de2d45dec5a7d6c0d0092c5dd845660393b9724 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 18:42:52 +0200 Subject: [PATCH 11/14] rm wrong naming --- src/actions/LoadLabels.js | 58 --------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 src/actions/LoadLabels.js diff --git a/src/actions/LoadLabels.js b/src/actions/LoadLabels.js deleted file mode 100644 index d580bde1..00000000 --- a/src/actions/LoadLabels.js +++ /dev/null @@ -1,58 +0,0 @@ -import axios from "axios"; -import { setLabels } from "./setLabels"; -import setLoading from "./setLoading"; -import { setApiError } from "./setApiError"; - -export default function loadLabels(apiUrl) { - const origin = window.location.origin; - const url = apiUrl; - const headers = { - "Access-Control-Allow-Origin": origin, - "Access-Control-Allow-Headers": [ - "Access-Control-Request-Headers", - "Content-Type", - ], - "Content-Type": "application/json", - }; - - const options = { - method: "GET", - headers: headers, - mode: "cors", - }; - - return function (dispatch) { - axios - .get(`${url.trim()}/loki/api/v1/labels`, options) - ?.then((response) => { - if (response) { - if (response?.data?.data === []) - if (response?.data?.data?.length > 0) { - const labels = response?.data?.data - .sort() - .map((label) => ({ - name: label, - selected: false, - values: [], - })); - dispatch(setLabels(labels || [])); - - dispatch(setApiError("")); - } - } else { - dispatch(setLoading(false)); - dispatch( - setApiError("") - ); - dispatch(setLabels([])); - - } - }) - .catch((error) => { - console.log(error); - dispatch(setLoading(false)); - dispatch(setApiError(`Status: ${status}, ${message}`)); - dispatch(setLabels([])); - }); - }; -} From 972da2e931a5b3eff88d81c3c9c49c8d6e8d6fcf Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 18:43:30 +0200 Subject: [PATCH 12/14] fix file name --- src/actions/loadLabels.js | 58 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/actions/loadLabels.js diff --git a/src/actions/loadLabels.js b/src/actions/loadLabels.js new file mode 100644 index 00000000..cc886295 --- /dev/null +++ b/src/actions/loadLabels.js @@ -0,0 +1,58 @@ +import axios from "axios"; +import { setLabels } from "./setLabels"; +import setLoading from "./setLoading"; +import { setApiError } from "./setApiError"; + +export default function loadLabels(apiUrl) { + const origin = window.location.origin; + const url = apiUrl; + const headers = { + "Access-Control-Allow-Origin": origin, + "Access-Control-Allow-Headers": [ + "Access-Control-Request-Headers", + "Content-Type", + ], + "Content-Type": "application/json", + }; + + const options = { + method: "GET", + headers: headers, + mode: "cors", + }; + + return function (dispatch) { + axios + .get(`${url.trim()}/loki/api/v1/labels`, options) + ?.then((response) => { + if (response) { + if (response?.data?.data === []) + if (response?.data?.data?.length > 0) { + const labels = response?.data?.data + .sort() + .map((label) => ({ + name: label, + selected: false, + values: [], + })); + dispatch(setLabels(labels || [])); + + dispatch(setApiError("")); + } + } else { + dispatch(setLoading(false)); + dispatch( + setApiError("") + ); + dispatch(setLabels([])); + + } + }) + .catch((error) => { + console.log(error); + dispatch(setLoading(false)); + dispatch(setApiError(`Status: ${status}, ${message}`)); + dispatch(setLabels([])); + }); + }; +} From 1f458c49bd4a2cc755ea9c3ee53515400d34adc4 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 18:54:04 +0200 Subject: [PATCH 13/14] rm unused --- src/actions/loadLabels.js | 1 - src/helpers/error.interceptor.js | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/actions/loadLabels.js b/src/actions/loadLabels.js index cc886295..764f8484 100644 --- a/src/actions/loadLabels.js +++ b/src/actions/loadLabels.js @@ -51,7 +51,6 @@ export default function loadLabels(apiUrl) { .catch((error) => { console.log(error); dispatch(setLoading(false)); - dispatch(setApiError(`Status: ${status}, ${message}`)); dispatch(setLabels([])); }); }; diff --git a/src/helpers/error.interceptor.js b/src/helpers/error.interceptor.js index 9760c4b7..1a4cbf46 100644 --- a/src/helpers/error.interceptor.js +++ b/src/helpers/error.interceptor.js @@ -1,5 +1,5 @@ import store from '../store/store' -import { errorHandler, setApiError, createAlert } from "../actions/"; +import { errorHandler, createAlert } from "../actions/"; import setApiWarning from '../actions/setApiWarning'; const errorInterceptor = (axiosInstance) => { @@ -23,7 +23,7 @@ const errorInterceptor = (axiosInstance) => { if (store.getState().notifications.length < 1 && store.getState().debugMode === true) { store.dispatch(createAlert({ type: "error", - message: (handler.message + " for " + handler.type || status + handler.type + 'Error') + message: (handler.message + " for " + handler.type || handler.status + handler.type + 'Error') })) } } @@ -33,7 +33,7 @@ const errorInterceptor = (axiosInstance) => { if (store.getState().notifications.length < 1) { store.dispatch(createAlert({ type: "error", - message: (handler.message || status + handler.type + 'Error') + message: (handler.message || handler.status + handler.type + 'Error') })) } @@ -43,7 +43,7 @@ const errorInterceptor = (axiosInstance) => { if (store.getState().notifications.length < 1) { store.dispatch(createAlert({ type: "error", - message: (handler.message + " for " + handler.type || status + handler.type + 'Error') + message: (handler.message + " for " + handler.type || handler.status + handler.type + 'Error') })) } } @@ -57,7 +57,7 @@ const errorInterceptor = (axiosInstance) => { } store.dispatch(setApiWarning({ type: 'labels', message: 'Labels not available', })) - const { url, message, name } = networkError + const { url } = networkError const apiWarning = store.getState().apiWarning if (apiWarning && url.includes('query')) { From 8df4d03ff6db056b7dfa0a0d8671305e22c18e12 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Thu, 21 Apr 2022 19:05:38 +0200 Subject: [PATCH 14/14] cleanup --- src/actions/loadLogs.js | 5 +- src/components/LabelBrowser/QueryBar.js | 3 +- src/components/LabelBrowser/ValuesList.js | 1 - .../LabelBrowser/helpers/querybuilder.js | 2 - .../components/daterangepicker/defaults.js | 104 ++++---- .../components/daterangepicker/index.js | 5 +- .../timepickerbutton/TimePickerButton.js | 41 ++-- src/plugins/charts/UseTooltip.js | 228 +++++++++--------- src/plugins/settingsmenu/Menu.js | 1 - src/store/createInitialState.js | 1 - 10 files changed, 188 insertions(+), 203 deletions(-) diff --git a/src/actions/loadLogs.js b/src/actions/loadLogs.js index da20d9d4..dda20106 100644 --- a/src/actions/loadLogs.js +++ b/src/actions/loadLogs.js @@ -6,8 +6,7 @@ import setMatrixData from "./setMatrixData"; import { nanoid } from "nanoid"; import { setStartTime, setStopTime } from "./"; import { findRangeByLabel } from "../components/StatusBar/components/daterangepicker/utils"; -import { createAlert } from "./createAlert"; -import { errorHandler } from "./errorHandler"; + export default function loadLogs() { const localStore = store.getState(); @@ -128,7 +127,7 @@ export default function loadLogs() { dispatch(setMatrixData([])); dispatch(setLoading(false)); - const handler = errorHandler(url, error, type) + }); }; diff --git a/src/components/LabelBrowser/QueryBar.js b/src/components/LabelBrowser/QueryBar.js index 7975ec59..51242be7 100644 --- a/src/components/LabelBrowser/QueryBar.js +++ b/src/components/LabelBrowser/QueryBar.js @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { setIsSubmit, setQuery } from "../../actions"; +import { setQuery } from "../../actions"; import loadLogs from "../../actions/loadLogs"; import setLoading from "../../actions/setLoading"; import { setLabelsBrowserOpen } from "../../actions/setLabelsBrowserOpen"; @@ -33,7 +33,6 @@ export const QueryBar = () => { const apiUrl = useSelector((store) => store.apiUrl); const historyOpen = useSelector((store) => store.historyOpen); const isEmbed = useSelector((store) => store.isEmbed) - const apiWarning = useSelector((store) => store.apiWarning) const [queryInput, setQueryInput] = useState(query); const [queryValid, setQueryValid] = useState(false); const [queryValue, setQueryValue] = useState(queryInit(query)); diff --git a/src/components/LabelBrowser/ValuesList.js b/src/components/LabelBrowser/ValuesList.js index bbd80359..9b6f9bbf 100644 --- a/src/components/LabelBrowser/ValuesList.js +++ b/src/components/LabelBrowser/ValuesList.js @@ -8,7 +8,6 @@ import { setQuery } from "../../actions"; import loadLabelValues from "../../actions/loadLabelValues"; import Tooltip from "@mui/material/Tooltip"; -import store from "../../store/store"; import styled from "@emotion/styled"; import loadLabels from "../../actions/loadLabels"; diff --git a/src/components/LabelBrowser/helpers/querybuilder.js b/src/components/LabelBrowser/helpers/querybuilder.js index 6116f211..9e2a4e98 100644 --- a/src/components/LabelBrowser/helpers/querybuilder.js +++ b/src/components/LabelBrowser/helpers/querybuilder.js @@ -2,9 +2,7 @@ import { setQuery } from "../../../actions"; import store from "../../../store/store"; export function queryBuilder(labels) { - const actualLabels = store.getState().labels const actualQuery = store.getState().query - // console.log(actualLabels) const preTags = actualQuery.split("{")[0] const postTags = actualQuery.split("}")[1] const selectedLabels = []; diff --git a/src/components/StatusBar/components/daterangepicker/defaults.js b/src/components/StatusBar/components/daterangepicker/defaults.js index 9138cefb..c7a965a3 100644 --- a/src/components/StatusBar/components/daterangepicker/defaults.js +++ b/src/components/StatusBar/components/daterangepicker/defaults.js @@ -1,55 +1,51 @@ -import { - addDays, - startOfWeek, - endOfWeek, - addWeeks, - startOfMonth, - endOfMonth, - addMonths, - addMinutes, - addHours, - addSeconds, - startOfDay, - endOfDay, - getHours -} from "date-fns"; - -const getDefaultRanges = (date) => [ - { - label: "Today", - dateStart: startOfDay, - dateEnd: endOfDay - }, - { - label: "Yesterday", - dateStart: addDays(date, -1), - dateEnd: addDays(date, -1) - }, - { - label: "This Week", - dateStart: startOfWeek(date), - dateEnd: endOfWeek(date) - }, - { - label: "Last Week", - dateStart: startOfWeek(addWeeks(date, -1)), - dateEnd: endOfWeek(addWeeks(date, -1)) - }, - { - label: "Last 7 Days", - dateStart: addWeeks(date, -1), - dateEnd: date - }, - { - label: "This Month", - dateStart: startOfMonth(date), - dateEnd: endOfMonth(date) - }, - { - label: "Last Month", - dateStart: startOfMonth(addMonths(date, -1)), - dateEnd: endOfMonth(addMonths(date, -1)) - } -]; - +import { + addDays, + startOfWeek, + endOfWeek, + addWeeks, + startOfMonth, + endOfMonth, + addMonths, + startOfDay, + endOfDay, +} from "date-fns"; + +const getDefaultRanges = (date) => [ + { + label: "Today", + dateStart: startOfDay, + dateEnd: endOfDay + }, + { + label: "Yesterday", + dateStart: addDays(date, -1), + dateEnd: addDays(date, -1) + }, + { + label: "This Week", + dateStart: startOfWeek(date), + dateEnd: endOfWeek(date) + }, + { + label: "Last Week", + dateStart: startOfWeek(addWeeks(date, -1)), + dateEnd: endOfWeek(addWeeks(date, -1)) + }, + { + label: "Last 7 Days", + dateStart: addWeeks(date, -1), + dateEnd: date + }, + { + label: "This Month", + dateStart: startOfMonth(date), + dateEnd: endOfMonth(date) + }, + { + label: "Last Month", + dateStart: startOfMonth(addMonths(date, -1)), + dateEnd: endOfMonth(addMonths(date, -1)) + } +]; + export const defaultRanges = getDefaultRanges(new Date()); \ No newline at end of file diff --git a/src/components/StatusBar/components/daterangepicker/index.js b/src/components/StatusBar/components/daterangepicker/index.js index c1e97947..b684b014 100644 --- a/src/components/StatusBar/components/daterangepicker/index.js +++ b/src/components/StatusBar/components/daterangepicker/index.js @@ -178,10 +178,7 @@ export function DateRangePickerMain(props) { dispatch(setRangeOpen(false)); isOpen(e); }; - const onQueryValid = (query) => { - return query !== "{" && query !== "}" && query !== "{}" && query !== ""; // TODO: make a proper query validation - }; - // helpers + const inHoverRange = (day) => { return ( dateStart && diff --git a/src/components/StatusBar/components/timepickerbutton/TimePickerButton.js b/src/components/StatusBar/components/timepickerbutton/TimePickerButton.js index 665fb03f..2ffe6f52 100644 --- a/src/components/StatusBar/components/timepickerbutton/TimePickerButton.js +++ b/src/components/StatusBar/components/timepickerbutton/TimePickerButton.js @@ -1,21 +1,20 @@ -import { TimePickerButtonStyled } from "./styled"; -import { useDispatch, useSelector } from "react-redux"; -import setTimePickerOpen from "./actions/setTimePickerOpen"; -import AccessTimeIcon from "@mui/icons-material/AccessTime"; -import { setRangeOpen } from "../../../../actions"; - -export default function TimePickerButton() { - const dispatch = useDispatch(); - const timePickerOpen = useSelector((store) => store.rangeOpen); - - function openTimePicker() { - const shouldOpen = timePickerOpen ? false : true; - - dispatch(setRangeOpen(shouldOpen)); - } - return ( - - - - ); -} +import { TimePickerButtonStyled } from "./styled"; +import { useDispatch, useSelector } from "react-redux"; +import AccessTimeIcon from "@mui/icons-material/AccessTime"; +import { setRangeOpen } from "../../../../actions"; + +export default function TimePickerButton() { + const dispatch = useDispatch(); + const timePickerOpen = useSelector((store) => store.rangeOpen); + + function openTimePicker() { + const shouldOpen = timePickerOpen ? false : true; + + dispatch(setRangeOpen(shouldOpen)); + } + return ( + + + + ); +} diff --git a/src/plugins/charts/UseTooltip.js b/src/plugins/charts/UseTooltip.js index 284a4760..4f9ae024 100644 --- a/src/plugins/charts/UseTooltip.js +++ b/src/plugins/charts/UseTooltip.js @@ -1,114 +1,114 @@ -import * as moment from "moment"; -import { - highlightItems, - isFloat, - isLAbelSelected, - makeTolltipItems, -} from "./helpers"; - -const $q = window.jQuery; - -export default function UseTooltip(plot) { - let previousPoint = null; - $q("#tooltip").remove(); - previousPoint = null; - $q(this).bind("plothover", function (event, pos, item) { - let labels = ``; - plot.unhighlight(); - if (item) { - let plotData = plot.getData(); - const [plotTime, _] = item.datapoint; - const selectedPlots = JSON.parse( - localStorage.getItem("labelsSelected") - ); - const itemValue = isFloat(parseFloat(item.datapoint[1])) - ? parseFloat(item.datapoint[1]).toFixed(3) - : item.datapoint[1]; - - const isSelectedPlots = selectedPlots.length > 0; - const labelsList = []; - for (let i = 0; i < plotData.length; i++) { - const plotIsVisible = isSelectedPlots - ? isLAbelSelected(plotData[i]) - : true; - const plotTimes = plotData[i].data - .map((d) => d) - .map((e) => e[0]); - const plotPoints = plotData[i].data.map((d) => d); - - if (plotTimes.includes(plotTime) && plotIsVisible) { - const plotIndex = plotTimes.indexOf(plotTime); - - const [_, value] = plotPoints.find( - ([time, _]) => time === plotTime - ); - labelsList.push({ - color: plotData[i].color, - label: plotData[i].label, - value: value, - plot, - plotIndex, - item, - i, - }); - } - } - - highlightItems(labelsList); - const labelsFormatted = makeTolltipItems(labelsList); - if (previousPoint !== item.datapoint) { - previousPoint = item.datapoint; - $q("#tooltip").remove(); - const tooltipTemplate = ` -
-

${moment(item.datapoint[0]).format( - "YYYY-MM-DDTHH:mm:ss.SSSZ" - )}

- -

- Value: ${itemValue}

-
-
- ${labelsFormatted} -
- `; - const labelLength = item.series.label.length; - showTooltip( - item.pageX, - item.pageY, - tooltipTemplate, - labelLength - ); - } - } else { - $q("#tooltip").remove(); - previousPoint = null; - } - }); -} - -function showTooltip(x, y, contents, length) { - let wWidth = window.innerWidth; - let posX = x + 20; - if (x * 2 > wWidth) { - posX = x - length * 8; - } - - // use ref for tooltip as made with chart! - - $q(`
` + contents + `
`) - .css({ - position: "absolute", - display: "none", - top: y, - left: posX, - padding: "6px", - "font-size": "12px", - size: "10", - "border-radius": "6px 6px 6px 6px", - "background-color": "#333", - color: "#aaa", - }) - .appendTo("body") - .fadeIn(125); -} +import * as moment from "moment"; +import { + highlightItems, + isFloat, + isLAbelSelected, + makeTolltipItems, +} from "./helpers"; + +const $q = window.jQuery; + +export default function UseTooltip(plot) { + let previousPoint = null; + $q("#tooltip").remove(); + previousPoint = null; + $q(this).bind("plothover", function (event, pos, item) { + + plot.unhighlight(); + if (item) { + let plotData = plot.getData(); + const [plotTime, _] = item.datapoint; + const selectedPlots = JSON.parse( + localStorage.getItem("labelsSelected") + ); + const itemValue = isFloat(parseFloat(item.datapoint[1])) + ? parseFloat(item.datapoint[1]).toFixed(3) + : item.datapoint[1]; + + const isSelectedPlots = selectedPlots.length > 0; + const labelsList = []; + for (let i = 0; i < plotData.length; i++) { + const plotIsVisible = isSelectedPlots + ? isLAbelSelected(plotData[i]) + : true; + const plotTimes = plotData[i].data + .map((d) => d) + .map((e) => e[0]); + const plotPoints = plotData[i].data.map((d) => d); + + if (plotTimes.includes(plotTime) && plotIsVisible) { + const plotIndex = plotTimes.indexOf(plotTime); + + const [_, value] = plotPoints.find( + ([time, _]) => time === plotTime + ); + labelsList.push({ + color: plotData[i].color, + label: plotData[i].label, + value: value, + plot, + plotIndex, + item, + i, + }); + } + } + + highlightItems(labelsList); + const labelsFormatted = makeTolltipItems(labelsList); + if (previousPoint !== item.datapoint) { + previousPoint = item.datapoint; + $q("#tooltip").remove(); + const tooltipTemplate = ` +
+

${moment(item.datapoint[0]).format( + "YYYY-MM-DDTHH:mm:ss.SSSZ" + )}

+ +

+ Value: ${itemValue}

+
+
+ ${labelsFormatted} +
+ `; + const labelLength = item.series.label.length; + showTooltip( + item.pageX, + item.pageY, + tooltipTemplate, + labelLength + ); + } + } else { + $q("#tooltip").remove(); + previousPoint = null; + } + }); +} + +function showTooltip(x, y, contents, length) { + let wWidth = window.innerWidth; + let posX = x + 20; + if (x * 2 > wWidth) { + posX = x - length * 8; + } + + // use ref for tooltip as made with chart! + + $q(`
` + contents + `
`) + .css({ + position: "absolute", + display: "none", + top: y, + left: posX, + padding: "6px", + "font-size": "12px", + size: "10", + "border-radius": "6px 6px 6px 6px", + "background-color": "#333", + color: "#aaa", + }) + .appendTo("body") + .fadeIn(125); +} diff --git a/src/plugins/settingsmenu/Menu.js b/src/plugins/settingsmenu/Menu.js index 7c97ff5c..de317285 100644 --- a/src/plugins/settingsmenu/Menu.js +++ b/src/plugins/settingsmenu/Menu.js @@ -1,5 +1,4 @@ import * as React from "react"; -import Button from "@mui/material/Button"; import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; import DisplaySettingsIcon from "@mui/icons-material/DisplaySettings"; diff --git a/src/store/createInitialState.js b/src/store/createInitialState.js index 89341cab..ce366e75 100644 --- a/src/store/createInitialState.js +++ b/src/store/createInitialState.js @@ -18,7 +18,6 @@ export default function initialState() { const urlState = stateFromQueryParams(); const historyService = localService().historyStore(); const linkService = localUrl(); - const lastQuery = linkService.getAll()?.[0]?.params const state = { debugMode: debugLocal().isActive|| false,