diff --git a/package-lock.json b/package-lock.json
index 67374e96..c24d5b02 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
"env-cmd": "^10.1.0",
"jquery": "^3.6.0",
"lodash": "^4.17.21",
+ "memoize-one": "^6.0.0",
"moment": "^2.29.2",
"nanoid": "^3.3.1",
"prismjs": "^1.27.0",
@@ -13790,6 +13791,11 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/memoize-one": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+ },
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -30295,6 +30301,11 @@
"fs-monkey": "1.0.3"
}
},
+ "memoize-one": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+ },
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
diff --git a/package.json b/package.json
index baf037fd..6290d7b0 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"env-cmd": "^10.1.0",
"jquery": "^3.6.0",
"lodash": "^4.17.21",
+ "memoize-one": "^6.0.0",
"moment": "^2.29.2",
"nanoid": "^3.3.1",
"prismjs": "^1.27.0",
diff --git a/src/actions/index.js b/src/actions/index.js
index bf067bae..2380f243 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -6,6 +6,8 @@ export * from "./setRangeOpen";
export * from "./setTimeRangeLabel";
export * from "./setApiUrl";
export * from "./setQuery";
+export * from "./setQueryTime";
+export * from "./setQueryType";
export * from "./setIsSubmit";
export * from "./setMatrixData";
export * from "./setQueryHistory";
@@ -18,3 +20,4 @@ export * from "./removeAlert";
export * from "./setFromTime";
export * from "./setToTime";
export * from "./setTheme";
+export * from "./setIsEmptyView";
\ No newline at end of file
diff --git a/src/actions/loadLabels.js b/src/actions/loadLabels.js
index 8649f907..72ef9049 100644
--- a/src/actions/loadLabels.js
+++ b/src/actions/loadLabels.js
@@ -4,6 +4,7 @@ import setLoading from "./setLoading";
import { setApiError } from "./setApiError";
import { createAlert } from "./createAlert";
+
export default function loadLabels(apiUrl) {
const origin = window.location.origin;
const url = apiUrl;
@@ -36,6 +37,7 @@ export default function loadLabels(apiUrl) {
values: [],
}));
if (labels) {
+ console.log("loding labels from loadLabels")
dispatch(setLabels(labels || []));
dispatch(setApiError(""));
dispatch(setLoading(false));
@@ -48,6 +50,7 @@ export default function loadLabels(apiUrl) {
dispatch(setApiError(""));
dispatch(setLabels([]));
+
}
})
.catch((error) => {
diff --git a/src/actions/loadLogs.js b/src/actions/loadLogs.js
index 58051e05..c706df31 100644
--- a/src/actions/loadLogs.js
+++ b/src/actions/loadLogs.js
@@ -6,7 +6,55 @@ import setMatrixData from "./setMatrixData";
import { nanoid } from "nanoid";
import { setStartTime, setStopTime } from "./";
import { findRangeByLabel } from "../components/StatusBar/components/daterangepicker/utils";
+import { setQueryTime } from "./setQueryTime";
+import setIsEmptyView from "./setIsEmptyView";
+
+// import adjustedStep from "../components/QueryTypeBar/helpers";
+const debugMode = store.getState().debugMode
+export async function getAsyncResponse(
+ cb //: callback dispatch function
+) {
+ return await cb;
+}
+
+export function sortMessagesByTimestamp(
+ messages //:array sort by timestamp
+) {
+ const startTime = performance.now()
+ const mess = messages?.sort((a, b) => (a.timestamp < b.timestamp ? 1 : -1));
+ const duration = performance.now() - startTime;
+ if(debugMode) console.log( "🚧 loadLogs / sorting logs took: ",duration," ms")
+ return mess
+
+}
+export function fromNanoSec(
+ ts // :timestamp
+) {
+ return parseInt(ts / 1000000);
+}
+
+export function mapStreams (streams) {
+ const startTime = performance.now()
+ let messages = []
+
+ streams.forEach((stream) => {
+ stream.values.forEach(([ts,text], i) => {
+ messages.push({
+ type:'stream',
+ timestamp:fromNanoSec(ts),
+ text,
+ tags: stream.stream || {},
+ showTs: true,
+ showLabels: false,
+ id: nanoid(),
+ });
+ });
+ });
+ const duration = performance.now() - startTime;
+ if(debugMode) console.log( "🚧 loadLogs / mapping logs took: ",duration," ms")
+ return messages
+};
export default function loadLogs() {
const localStore = store.getState();
@@ -18,37 +66,40 @@ export default function loadLogs() {
label: rangeLabel,
from,
to,
+ debugMode,
} = localStore;
let { start: startTs, stop: stopTs } = localStore;
- function adjustForTimezone(date){
-
- var timeOffsetInMS = date.getTimezoneOffset() * 60000;
- date.setTime(date.getTime() + timeOffsetInMS);
- return date
- }
-
function getTimeParsed(time) {
return time.getTime() + "000000";
}
- const timeZone = new Date().getTimezoneOffset()
+ const time = localStore.time || new Date().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));
+ ({ dateStart: startTs, dateEnd: stopTs } = findRangeByLabel(
+ rangeLabel
+ ));
+
}
store.dispatch(setStartTime(startTs));
store.dispatch(setStopTime(stopTs));
-
+ const queryType = store.getState().queryType;
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 queryUrl = `${url}/loki/api/v1`;
+
+ const rangeEP = `${queryUrl}/query_range?query=${encodedQuery}&limit=${limit}${parsedTime}${queryStep}`;
+ const instantEP = `${queryUrl}/query?query=${encodedQuery}&limit=${limit}&time=${time}`;
+
+ const endpoint = { instant: instantEP, range: rangeEP };
const options = {
method: "GET",
@@ -58,84 +109,95 @@ export default function loadLogs() {
},
};
- 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(setIsEmptyView(false));
dispatch(setLogs([]));
dispatch(setMatrixData([]));
await axios
- .get(getUrl, options)
+ .get(endpoint[queryType], options)
?.then((response) => {
+ if (response?.data?.streams?.length === 0) {
+ if (debugMode)
+ console.log(
+ "🚧 loadLogs / getting no data from streams"
+ );
+ dispatch(setIsEmptyView(true));
+ }
if (response?.data?.data) {
let messages = [];
- const result = response?.data?.data?.result; // array
+ const result = response?.data?.data?.result;
const type = response?.data?.data?.resultType;
if (type === "streams") {
- mapStreams(result, messages, type);
+ messages = mapStreams(result);
dispatch(setMatrixData([]));
- const messSorted = messages?.sort((a, b) =>
- a.timestamp < b.timestamp ? 1 : -1
- );
+ const messSorted = sortMessagesByTimestamp(messages)
if (messSorted) {
- dispatch(setLogs(messSorted || []));
-
- dispatch(setLoading(false));
+ try {
+ getAsyncResponse(
+ dispatch(setLogs(messSorted || []))
+ ).then(() => {
+ if (messSorted.length === 0) {
+ if (debugMode)
+ console.log(
+ "🚧 loadLogs / getting no messages sorted"
+ );
+ dispatch(setIsEmptyView(true));
+ }
+ dispatch(setIsEmptyView(false));
+ dispatch(setLoading(false));
+ });
+ if (queryType === "instant") {
+ store.dispatch(setQueryTime(time));
+ }
+ } catch (e) {
+ console.log(e);
+ }
}
}
if (type === "matrix") {
- const idResult =
- result?.map((m) => ({ ...m, id: nanoid() })) || [];
- dispatch(setMatrixData(idResult || []));
- dispatch(setLoading(false));
+ try {
+ const idResult =
+ result?.map((m) => ({ ...m, id: nanoid() })) ||
+ [];
+
+ getAsyncResponse(
+ dispatch(setMatrixData(idResult || []))
+ ).then(() => {
+ dispatch(setLoading(false));
+ if (idResult.length === 0) {
+ if (debugMode)
+ console.log(
+ "🚧 loadLogs / getting no data from matrix"
+ );
+ dispatch(setIsEmptyView(true));
+ }
+ dispatch(setIsEmptyView(false));
+ });
+ } catch (e) {
+ if (debugMode)
+ console.log(
+ "🚧 loadLogs / getting an error from rendering matrix type streams"
+ );
+ console.log(e);
+ }
}
- // dispatch(setLoading(false));
} else {
dispatch(setLogs([]));
dispatch(setMatrixData([]));
- dispatch(setLoading(false));
+
+ //
}
- // dispatch(setLoading(false));
})
.catch((error) => {
-
dispatch(setLogs([]));
dispatch(setMatrixData([]));
-
dispatch(setLoading(false));
-
-
+ if (debugMode)
+ console.log("getting an error from response: ", error);
+ dispatch(setIsEmptyView(true));
});
};
}
diff --git a/src/actions/setFromTime.js b/src/actions/setFromTime.js
index adfec8dc..1ecccc78 100644
--- a/src/actions/setFromTime.js
+++ b/src/actions/setFromTime.js
@@ -1,7 +1,8 @@
-export default function setFromTime(toTime){
- return function (dispatch){
- dispatch({
- type:"SET_FROM_TIME"
- })
- }
+export default function setFromTime(toTime){
+ return function (dispatch){
+ dispatch({
+ type:"SET_FROM_TIME",
+ toTime
+ })
+ }
}
\ No newline at end of file
diff --git a/src/actions/setIsEmptyView.js b/src/actions/setIsEmptyView.js
new file mode 100644
index 00000000..0b2c461e
--- /dev/null
+++ b/src/actions/setIsEmptyView.js
@@ -0,0 +1,8 @@
+const setIsEmptyView = (isEmptyView) => (dispatch) => {
+ dispatch({
+ type: 'SET_IS_EMPTY_VIEW',
+ isEmptyView,
+ })
+}
+
+export default setIsEmptyView;
\ No newline at end of file
diff --git a/src/actions/setLabelValues.js b/src/actions/setLabelValues.js
index 4e82dd8d..98ab8da2 100644
--- a/src/actions/setLabelValues.js
+++ b/src/actions/setLabelValues.js
@@ -1,7 +1,7 @@
-const setLabelValues = (labelValues) => (dispatch) => {
- dispatch({
- type: 'SET_LABEL_VALUES',
- labelValues
- });
-}
-export default setLabelValues;
+const setLabelValues = (labelValues) => (dispatch) => {
+ dispatch({
+ type: 'SET_LABEL_VALUES',
+ labelValues
+ });
+}
+export default setLabelValues;
diff --git a/src/actions/setLabels.js b/src/actions/setLabels.js
index 0c8a8437..c97659f3 100644
--- a/src/actions/setLabels.js
+++ b/src/actions/setLabels.js
@@ -1,6 +1,6 @@
-export const setLabels = (labels) => (dispatch) => {
- dispatch({
- type: 'SET_LABELS',
- labels: labels
- });
-};
+export const setLabels = (labels) => (dispatch) => {
+ dispatch({
+ type: 'SET_LABELS',
+ labels: labels
+ });
+};
diff --git a/src/actions/setQueryLimit.js b/src/actions/setQueryLimit.js
index e8af27d4..b05e042e 100644
--- a/src/actions/setQueryLimit.js
+++ b/src/actions/setQueryLimit.js
@@ -1,6 +1,6 @@
-export const setQueryLimit = (limit) => (dispatch) => {
- dispatch({
- type: 'SET_QUERY_LIMIT',
- limit
- });
-}
+export const setQueryLimit = (limit) => (dispatch) => {
+ dispatch({
+ type: 'SET_QUERY_LIMIT',
+ limit
+ });
+}
diff --git a/src/actions/setQueryTime.js b/src/actions/setQueryTime.js
new file mode 100644
index 00000000..7f77f45a
--- /dev/null
+++ b/src/actions/setQueryTime.js
@@ -0,0 +1,6 @@
+export const setQueryTime = (time) => (dispatch)=>{
+ dispatch({
+ type: 'SET_QUERY_TIME',
+ time
+ })
+}
diff --git a/src/actions/setQueryType.js b/src/actions/setQueryType.js
new file mode 100644
index 00000000..22b71896
--- /dev/null
+++ b/src/actions/setQueryType.js
@@ -0,0 +1,6 @@
+export const setQueryType = (queryType) => (dispatch) => {
+ dispatch({
+ type: "SET_QUERY_TYPE",
+ queryType,
+ });
+};
diff --git a/src/components/DataView/DataView.js b/src/components/DataView/DataView.js
index 85ef1e44..12e05cb0 100644
--- a/src/components/DataView/DataView.js
+++ b/src/components/DataView/DataView.js
@@ -1,28 +1,30 @@
import React, { Component } from "react";
import { connect } from "react-redux";
-
import { DataViewCont, DataViewStyled, Loader } from "./styled";
-
import ClokiChart from "../../plugins/charts";
import QueryHistory from "../../plugins/queryhistory";
-import LogsRow from "./LogsRow";
import EmptyView from "./EmptyView";
import { ThemeProvider } from "@emotion/react";
import { themes } from "../../theme/themes";
+import { LogRows } from "./LogRows";
class DataView extends Component {
constructor(props) {
super(props);
+ const { messages } = props || [];
this.state = {
limit: props.limit || 100,
- messages: props.messages || [],
+ messages,
matrixData: props.matrixData || [],
loading: false,
theme: props.theme,
+ isEmptyView: props.isEmptyView || false,
};
}
+
getMatrixForChart = () => {
return this.props.matrixData;
};
+
getLimit = () => {
return this.props.limit;
};
@@ -32,28 +34,21 @@ class DataView extends Component {
- {this.props.messages.length > 0 &&
- this.getMatrixForChart().length < 1
- ? this.props.messages.map((message, key) => (
-
- ))
- : null}
-
+ {this.props.messages.length > 0 && (
+
+ )}
{this.getMatrixForChart().length > 0 ? (
) : null}
- {this.props.messages.length < 1 &&
- this.getMatrixForChart().length < 1 &&
- !this.props.loading && }
-
+
{this.props.loading && }
+
+ {this.props.isEmptyView && }
+
+
@@ -64,12 +59,11 @@ class DataView extends Component {
const mapStateToProps = (state) => {
return {
messages: state.logs,
- start: state.start,
- stop: state.stop,
limit: state.limit,
loading: state.loading,
matrixData: state.matrixData,
theme: state.theme,
+ isEmptyView: state.isEmptyView,
};
};
diff --git a/src/components/DataView/EmptyView.js b/src/components/DataView/EmptyView.js
index 0d91c751..45d79f93 100644
--- a/src/components/DataView/EmptyView.js
+++ b/src/components/DataView/EmptyView.js
@@ -1,17 +1,24 @@
import { ThemeProvider } from "@emotion/react";
+
import { useSelector } from "react-redux";
import { themes } from "../../theme/themes";
import { EmptyViewContainer } from "./styled";
export default function EmptyView() {
- const theme = useSelector( store => store.theme)
+ const theme = useSelector((store) => store.theme);
+ const loading = useSelector((store) => store.loading);
+
+
return (
-
-
- {
- "Please adjust search parameters and click on ‘Show Logs’ button"
- }
-
-
- );
+ !loading && (
+
+
+ {
+ "Please adjust search parameters and click on ‘Show Logs’ button"
+ }
+
+
+ )
+ )
+
}
diff --git a/src/components/DataView/LogRows/index.js b/src/components/DataView/LogRows/index.js
new file mode 100644
index 00000000..05e8e586
--- /dev/null
+++ b/src/components/DataView/LogRows/index.js
@@ -0,0 +1,80 @@
+import memoize from "memoize-one";
+import { PureComponent } from "react";
+import { formatDate, getRowColor } from "../helpers";
+import { LogRow, RowLogContent, RowTimestamp } from "../styled";
+import ValueTags from "../ValueTags";
+
+function Row({ toggleItemActive, index, log }) {
+
+ return (
+ {
+ toggleItemActive(index);
+ }}
+ >
+
+ {formatDate(log.timestamp)}
+ {log.text}
+
+
+ {log.showLabels && (
+
+
+
+ )}
+
+ )
+
+}
+
+const createItemData = memoize((items, toggleItemActive) => ({
+ items,
+ toggleItemActive,
+}));
+
+function Logs({ items, toggleItemActive }) {
+ const itemData = createItemData(items, toggleItemActive);
+ return (
+ itemData &&
+ itemData.items.map((log, key) => (
+
+ ))
+ );
+}
+
+export class LogRows extends PureComponent {
+ constructor(props) {
+ super(props);
+ const { messages } = props || [];
+
+ this.state = {
+ messages,
+ };
+ }
+
+ toggleItemActive = (index) =>
+ this.setState((prevState) => {
+ const message = prevState.messages[index];
+ const messages = prevState.messages.concat();
+ messages[index] = {
+ ...message,
+ showLabels: !message.showLabels,
+ };
+ return { messages };
+ });
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/src/components/DataView/LogsRow.js b/src/components/DataView/LogsRow.js
deleted file mode 100644
index 2db79750..00000000
--- a/src/components/DataView/LogsRow.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { formatDate, getRowColor, toggleActiveStyles } from "./helpers";
-import { LogRow, RowLogContent, RowTimestamp } from "./styled";
-import ValueTags from "./ValueTags";
-import { useSelector, useDispatch, useStore } from "react-redux";
-import setLogs from "../../actions/setLogs";
-import { ThemeProvider } from "@emotion/react";
-import { themes } from "../../theme/themes";
-
-export default function LogsRow({ message }) {
- const dispatch = useDispatch();
- const messages = useSelector((store) => store.logs);
-
- const theme = useStore().getState().theme;
- function toggleTagsActive(idx) {
- let arrCopy = [...messages];
- arrCopy.forEach((entry) => {
- if (entry.id === idx) {
- entry.showLabels = entry.showLabels ? false : true;
- }
- });
- dispatch(setLogs(arrCopy));
- }
-
- return (
-
- {
- toggleTagsActive(message.id);
- }}
- >
-
- {formatDate(message.timestamp)}
- {message.text}
-
- {message.tags && (
-
-
-
- )}
-
-
- );
-}
diff --git a/src/components/DataView/ValueTags.js b/src/components/DataView/ValueTags.js
index 3eb244e1..5bbe0ef2 100644
--- a/src/components/DataView/ValueTags.js
+++ b/src/components/DataView/ValueTags.js
@@ -7,23 +7,38 @@ import { ZoomIn, ZoomOut } from "@mui/icons-material/";
import { useSelector } from "react-redux";
import { themes } from "../../theme/themes";
import { ThemeProvider } from "@emotion/react";
-import styled from '@emotion/styled'
+import styled from "@emotion/styled";
const ValueTagsStyled = styled.div`
- color: ${props => props.theme.textPrimary};
- flex:1;
+ color: ${(props) => props.theme.textPrimary};
+ flex: 1;
+ z-index: 10000;
+ display: flex;
&:hover {
- background: ${props => props.theme.widgetContainer};
+ background: ${(props) => props.theme.widgetContainer};
}
-`
+`;
+
export default function ValueTags({ tags }) {
const theme = useSelector((store) => store.theme);
const isEmbed = useSelector((store) => store.isEmbed);
+ const query = useSelector((store) => store.query);
async function addLabel(e, key, value, isInverted = false) {
e.preventDefault();
e.stopPropagation();
const { labels, apiUrl } = store.getState();
const label = labels.find((label) => label.name === key);
+ const symb = isInverted ? "!=" : "=";
+ const isAlreadySelected = query.includes(`${key}="${value}"`);
+ const isAlreadyInverted = query.includes(`${key}!="${value}"`);
+
+ if (
+ (isAlreadyInverted && isInverted) ||
+ (isAlreadySelected && !isInverted)
+ ) {
+ return;
+ }
+
if (label) {
const labelValue = label.values.find((tag) => tag.name === value);
if (labelValue?.selected && labelValue.inverted === isInverted) {
@@ -35,12 +50,13 @@ export default function ValueTags({ tags }) {
labelValue.inverted = !labelValue.inverted && isInverted;
label.selected = label.values.some((value) => value.selected);
store.dispatch(setLabels(labels));
+
} else {
await store.dispatch(loadLabelValues(label, labels, apiUrl));
const updatedLabels = store.getState().labels;
const updatedLabel = updatedLabels.find(
(label) => label.name === key
- );
+ );
const labelValue = updatedLabel.values.find(
(tag) => tag.name === value
);
@@ -50,44 +66,61 @@ export default function ValueTags({ tags }) {
updatedLabel.selected = updatedLabel.values.some(
(value) => value.selected
);
+ console.log('labels set if no labelvalue')
store.dispatch(setLabels(updatedLabels));
}
queryBuilderWithLabels();
+ store.dispatch(loadLogs());
+ } else {
+ queryBuilderWithLabels(true, [`${key}${symb}"${value}"`]);
store.dispatch(loadLogs());
}
}
return (
-
{Object.entries(tags).map(([key, value], k) => (
-
- {!isEmbed && (
- <>
-
addLabel(e, key, value)}
- className={"icon"}
- >
-
-
-
addLabel(e, key, value, true)}
- className={"icon"}
- >
-
-
- >
- )}
+
+ {!isEmbed && (
+ <>
+ addLabel(e, key, value)}
+ className={"icon"}
+ >
+
+
+
+ addLabel(e, key, value, true)
+ }
+ className={"icon"}
+ >
+
+
+ >
+ )}
- {key}
- {value}
-
+
{key}
+
{value}
+
))}
diff --git a/src/components/DataView/helpers/index.js b/src/components/DataView/helpers/index.js
index d70e87f4..a67f2107 100644
--- a/src/components/DataView/helpers/index.js
+++ b/src/components/DataView/helpers/index.js
@@ -15,6 +15,7 @@ export function getRowColor(tags) {
}
export function toggleActiveStyles(idx) {
+ console.log(idx, 'toggled')
return idx.showLabels
? "value-tags-container labelsActive"
: "value-tags-container labelsInactive";
diff --git a/src/components/LabelBrowser/QueryBar.js b/src/components/LabelBrowser/QueryBar.js
index 55bea3f5..a9090697 100644
--- a/src/components/LabelBrowser/QueryBar.js
+++ b/src/components/LabelBrowser/QueryBar.js
@@ -25,6 +25,7 @@ import debugLog from "./helpers/debugLog";
import { ThemeProvider } from "@emotion/react";
import { themes } from "../../theme/themes";
import { sendLabels } from "../../hooks/useLabels";
+import QueryTypeBar from "../QueryTypeBar";
export const QueryBar = () => {
const dispatch = useDispatch();
@@ -66,9 +67,6 @@ export const QueryBar = () => {
const onValueDisplay = (e) => {
e.preventDefault();
const isOpen = labelsBrowserOpen ? false : true;
- if (isOpen) {
- dispatch(loadLabels(apiUrl));
- }
dispatch(setLabelsBrowserOpen(isOpen));
};
@@ -85,7 +83,6 @@ export const QueryBar = () => {
onSubmit(e);
}
};
-
const onSubmit = (e) => {
e.preventDefault();
@@ -171,6 +168,7 @@ export const QueryBar = () => {
isMobile={false}
/>
+
)
diff --git a/src/components/LabelBrowser/ValuesList.js b/src/components/LabelBrowser/ValuesList.js
index cd5ac796..ee0aacbd 100644
--- a/src/components/LabelBrowser/ValuesList.js
+++ b/src/components/LabelBrowser/ValuesList.js
@@ -30,9 +30,9 @@ const ValuesListStyled = styled.div`
background: ${(props) => props.theme.widgetContainer};
.valuelist-content {
small {
- color: ${(props) => props.theme.textColor} !important;
+ color: ${(props) => props.theme.textColor};
background: ${(props) => props.theme.buttonDefault} !important;
- border: 1px solid ${(props) => props.theme.buttonBorder} !important;
+ border: 1px solid ${(props) => props.theme.buttonBorder};
margin: 5px;
padding: 4px 8px;
border-radius: 3px;
@@ -104,20 +104,17 @@ 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);
+
const labelsBrowserOpen = useSelector((store) => store.labelsBrowserOpen);
const CLEAR = "clear";
- useEffect(() => {
- dispatch(loadLabels(apiUrl));
- }, [apiUrl]);
-
useEffect(() => {
setLabelList(labels); // LABELS
}, [labels]);
const handleRefresh = (e) => {
e.preventDefault();
+ console.log("event handled")
dispatch(loadLabels(apiUrl));
};
diff --git a/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js b/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js
index fb47c0fe..18d775dc 100644
--- a/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js
+++ b/src/components/LabelBrowser/components/ShowLabelsButton/ShowLabelsButton.js
@@ -26,9 +26,9 @@ export default function ShowLabelsButton({
isMobile={isMobile}
>
{labelsBrowserOpen ? (
-
+
) : (
-
+
)}{" "}
{LOG_BROWSER}
diff --git a/src/components/LabelBrowser/components/styled/index.js b/src/components/LabelBrowser/components/styled/index.js
index 2436553e..31255b81 100644
--- a/src/components/LabelBrowser/components/styled/index.js
+++ b/src/components/LabelBrowser/components/styled/index.js
@@ -29,6 +29,7 @@ export const ShowLabelsBtn = styled(BtnSmall)`
transition: 0.25s all;
justify-content: flex-start;
color: ${({ theme }) => theme.textColor};
+ height: 28px;
&:hover {
background: ${({ theme }) => theme.buttonHover};
}
@@ -42,7 +43,7 @@ export const ShowLabelsBtn = styled(BtnSmall)`
export const QueryBarContainer = styled.div`
display: flex;
padding: 3px 6px;
- margin: 5px 0px;
+ margin-top:5px;
margin-left: 0px;
background: ${({ theme }) => theme.widgetContainer};
flex-wrap: wrap;
@@ -62,6 +63,7 @@ export const ShowLogsBtn = styled(BtnSmall)`
background: ${(props) => props.theme.buttonDefault};
border:1px solid ${(props)=>props.theme.buttonBorder};
cursor: not-allowed;
+ color: ${props => props.theme.textColor};
}
@media screen and (max-width: 864px) {
display: ${(props) => (props.isMobile ? "flex" : "none")};
diff --git a/src/components/LabelBrowser/helpers/querybuilder.js b/src/components/LabelBrowser/helpers/querybuilder.js
index 7de52e05..219e7e1f 100644
--- a/src/components/LabelBrowser/helpers/querybuilder.js
+++ b/src/components/LabelBrowser/helpers/querybuilder.js
@@ -1,11 +1,42 @@
import { setQuery } from "../../../actions";
import store from "../../../store/store";
-export function queryBuilder(labels) {
+export const PIPE_PARSE = [
+ {
+ label: "json",
+ },
+ {
+ label: "regexp",
+ text: 'regexp ""',
+ },
+ {
+ label: "logfmt",
+ },
+ {
+ label: "pattern",
+ },
+];
+
+const pipeParseOpts = ["json", "regexp", "logfmt", "pattern", "~", "="];
+
+export function queryBuilder(labels, hasPipe = false, pipeLabels = []) {
const actualQuery = store.getState().query;
const preTags = actualQuery.split("{")[0];
- const postTags = actualQuery.split("}")[1];
+ let postTags = "";
+
+ if (hasPipe) {
+ postTags = actualQuery.split("}")[1];
+ const expParse = actualQuery.split(/[|]/);
+ if (pipeParseOpts.some((s) => expParse[1].includes(s))) {
+ const pipeTags = ` | ${pipeLabels}`;
+ postTags = postTags.toString().concat(pipeTags.toString());
+ }
+ } else {
+ postTags = actualQuery.split("}")[1];
+ }
+
const selectedLabels = [];
+
for (const label of labels) {
if (label.selected && label.values && label.values.length > 0) {
const selectedValues = label.values
@@ -27,12 +58,14 @@ export function queryBuilder(labels) {
});
}
}
+
return [preTags, "{", selectedLabels.join(","), "}", postTags].join("");
}
-export function queryBuilderWithLabels() {
+export function queryBuilderWithLabels(hasPipe = false, pipeLabels = []) {
const labels = store.getState().labels;
- const query = queryBuilder(labels);
+ const query = queryBuilder(labels, hasPipe, pipeLabels);
+
store.dispatch(setQuery(query));
}
diff --git a/src/components/QueryTypeBar/actions/setQueryResolution.js b/src/components/QueryTypeBar/actions/setQueryResolution.js
new file mode 100644
index 00000000..8eea64f5
--- /dev/null
+++ b/src/components/QueryTypeBar/actions/setQueryResolution.js
@@ -0,0 +1,6 @@
+export const setQueryResolution = (queryResolution) => (dispatch) => {
+ dispatch({
+ type: "SET_QUERY_RESOLUTION",
+ queryResolution,
+ });
+};
diff --git a/src/components/QueryTypeBar/components/QueryLimit.js b/src/components/QueryTypeBar/components/QueryLimit.js
new file mode 100644
index 00000000..14d1aad7
--- /dev/null
+++ b/src/components/QueryTypeBar/components/QueryLimit.js
@@ -0,0 +1,53 @@
+import styled from "@emotion/styled";
+import { useEffect, useState } from "react";
+import { useSelector, useDispatch } from "react-redux";
+import { setQueryLimit } from "../../../actions";
+
+const InputGroup = styled.div`
+ display: flex;
+ margin-right: 10px;
+`;
+
+const Label = styled.div`
+ color: ${(props) => props.theme.textColor};
+ background: ${(props) => props.theme.buttonInactive};
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ padding: 0px 8px;
+`;
+
+const Input = styled.input`
+ flex: 1;
+ background: ${(props) => props.theme.inputBg};
+ color: ${(props) => props.theme.textColor};
+ border: 1px solid ${(props) => props.theme.buttonBorder};
+ border-radius: 3px;
+ max-width: 60px;
+ padding-left: 8px;
+`;
+
+export default function QueryLimit() {
+ const dispatch = useDispatch();
+ const limit = useSelector((store) => store.limit);
+ const [editedValue, setEditedValue] = useState(limit);
+
+ useEffect(() => {
+ setEditedValue(limit);
+ }, [limit, setEditedValue]);
+
+ function onLimitChange(e) {
+ dispatch(setQueryLimit(e.target.value));
+ }
+ return (
+
+
+
+
+ );
+}
diff --git a/src/components/QueryTypeBar/components/QueryResolution.js b/src/components/QueryTypeBar/components/QueryResolution.js
new file mode 100644
index 00000000..cd55d805
--- /dev/null
+++ b/src/components/QueryTypeBar/components/QueryResolution.js
@@ -0,0 +1,63 @@
+import styled from "@emotion/styled";
+import { useState } from "react";
+import { useDispatch } from "react-redux";
+import { setQueryResolution } from "../actions/setQueryResolution";
+import { DEFAULT_RESOLUTION, RESOLUTION_OPTIONS } from "../helpers";
+
+const Label = styled.div`
+ color: ${(props) => props.theme.textColor};
+ background: ${(props) => props.theme.buttonInactive};
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ padding: 0px 8px;
+`;
+
+const ResSelect = styled.select`
+ cursor: pointer;
+
+ position: relative;
+ font-size: 14px;
+ color: ${(props) => props.theme.textColor};
+ background: ${(props) => props.theme.inputBg};
+ border: 1px solid ${(props) => props.theme.buttonBorder};
+ border-radius: 3px;
+ padding: 4px 8px;
+ line-height: 20px;
+ flex: 1;
+ max-width: 70px;
+ &::-webkit-scrollbar {
+ width: 5px;
+ background: ${(props) => props.theme.inputBg};
+ }
+
+ &::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ background: ${(props) => props.theme.scrollbarThumb};
+ }
+`;
+
+export default function QueryResolution() {
+ const dispatch = useDispatch();
+ const [resValue, setResValue] = useState(DEFAULT_RESOLUTION.value);
+
+ function handleResChange(e) {
+ setResValue(parseInt(e.target.value));
+ dispatch(setQueryResolution(parseInt(e.target.value)));
+ }
+
+ return (
+ <>
+
+
+ {RESOLUTION_OPTIONS.map((res, idx) => (
+
+ ))}
+
+ >
+ );
+}
diff --git a/src/components/QueryTypeBar/components/QueryTypeSwitch.js b/src/components/QueryTypeBar/components/QueryTypeSwitch.js
new file mode 100644
index 00000000..1abf5249
--- /dev/null
+++ b/src/components/QueryTypeBar/components/QueryTypeSwitch.js
@@ -0,0 +1,87 @@
+import styled from "@emotion/styled";
+import { useState } from "react";
+const Label = styled.div`
+color:${props => props.theme.textColor};
+background: ${props => props.theme.buttonInactive};
+display:flex;
+align-items: center;
+justify-content: center;
+font-size: 12px;
+padding:0px 8px;
+`;
+const QuerySwitchCont = styled.div`
+
+ display: flex;
+ align-items: center;
+ font-size: 12px;
+ background-color: ${(props) => props.theme.buttonInactive};
+
+ border: 1px solid ${(props) => props.theme.buttonBorder};
+ color: ${(props) => props.theme.textColor};
+ border-radius: 3px;
+ margin-right:10px;
+`;
+
+const QuerySwitchBtn = styled.div`
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ background: ${(props) =>
+ props.selected ? props.theme.buttonDefault : props.theme.buttonInactive};
+ border-left: ${(props) =>
+ props.position === "last"
+ ? `1px solid ${props.theme.buttonBorder}`
+ : "none" };
+ border-right: ${(props) =>
+ props.position === "first"
+ ? `1px solid ${props.theme.buttonBorder}`
+ : "none" };
+ border-radius: ${({ position }) =>
+ position === "first"
+ ? "3px 0px 0px 3px"
+ : position === "last"
+ ? "0px 3px 3px 0px"
+ : "0px"};
+ flex: 1;
+ height: 90%;
+
+ padding: 0px 12px;
+ font-size: 12px;
+ line-height: 20px;
+`;
+const getBtnPos = (key, arr) => {
+ const arrLen = arr.length;
+ return key === 0 ? "first" : key === arrLen - 1 ? "last" : "center";
+};
+
+export default function QueryTypeSwitch(props) {
+ const { options, defaultActive, onChange } = props;
+
+ const [activeBtn, setActiveBtn] = useState(defaultActive);
+
+ function setButtonValue(value) {
+ setActiveBtn(value);
+ onChange(value)
+ }
+
+ return (<>
+
+
+
+
+
+ {options &&
+ options.map((value, key, arr) => (
+ setButtonValue(value.value)}
+ >
+ {value.label}
+
+ ))}
+
+ >
+ );
+}
diff --git a/src/components/QueryTypeBar/helpers/index.js b/src/components/QueryTypeBar/helpers/index.js
new file mode 100644
index 00000000..f96808c9
--- /dev/null
+++ b/src/components/QueryTypeBar/helpers/index.js
@@ -0,0 +1,72 @@
+import { map } from "lodash";
+
+function getTime(date) {
+ return Math.ceil(date.valueOf() * 1e6);
+}
+
+function adjustInterval(
+ dynamicInterval, // number
+ resolution, // number
+ range // number time range
+) {
+ let safeInterval = range / 1100;
+ if (safeInterval > 1) {
+ safeInterval = Math.ceil(safeInterval);
+ }
+ return Math.max(resolution * dynamicInterval, safeInterval);
+}
+
+const MILLISECOND = 1;
+const SECOND = 1000 * MILLISECOND;
+const MINUTE = 60 * SECOND;
+const HOUR = 60 * MINUTE;
+const DAY = 24 * HOUR;
+
+function getIntervalInfo(timespanMs) {
+ let intervalMs = timespanMs;
+
+ let interval = "";
+ if (timespanMs < SECOND * 5) {
+ intervalMs = MILLISECOND;
+ interval = "1ms";
+ } else if (intervalMs > HOUR) {
+ intervalMs = DAY;
+ interval = "1d";
+ } else if (intervalMs > MINUTE) {
+ intervalMs = HOUR;
+ interval = "1h";
+ } else if (intervalMs > SECOND) {
+ intervalMs = MINUTE;
+ interval = "1m";
+ } else {
+ intervalMs = SECOND;
+ interval = "1s";
+ }
+ console.log(intervalMs)
+ return { interval, intervalMs };
+}
+
+export const DEFAULT_RESOLUTION = {
+ value: 1,
+ label: "1/1",
+};
+
+export const RESOLUTION_OPTIONS = [DEFAULT_RESOLUTION].concat(
+ map([2, 3, 4, 5, 10], (value) => ({
+ value,
+ label: "1/" + value,
+ }))
+);
+
+
+export default function adjustedStep(target, options) {
+
+ const resolution = target.resolution || DEFAULT_RESOLUTION.value;
+ const startNs = getTime(options.range.from, false);
+ const endNs = getTime(options.range.to, true);
+ const rangeMs = Math.ceil((endNs - startNs) / 1e6);
+ const { intervalMs } = getIntervalInfo(rangeMs);
+ const intl = adjustInterval(intervalMs || 1000, resolution, rangeMs);
+ console.log(intl)
+ return Math.ceil(intl) / 1000;
+}
diff --git a/src/components/QueryTypeBar/index.js b/src/components/QueryTypeBar/index.js
new file mode 100644
index 00000000..f283d7bd
--- /dev/null
+++ b/src/components/QueryTypeBar/index.js
@@ -0,0 +1,49 @@
+import { ThemeProvider } from "@emotion/react";
+import styled from "@emotion/styled";
+import { useState, useEffect } from "react";
+import { useSelector, useDispatch } from "react-redux";
+import { setQueryType } from "../../actions";
+import { themes } from "../../theme/themes";
+import QueryLimit from "./components/QueryLimit";
+import QueryTypeSwitch from "./components/QueryTypeSwitch";
+
+const QueryTypeCont = styled.div`
+ display: flex;
+ padding: 4px;
+ background: ${(props) => props.theme.widgetContainer};
+ color: ${(props) => props.color};
+ height: 26px;
+`;
+
+export default function QueryTypeBar() {
+ const dispatch = useDispatch();
+ const theme = useSelector((store) => store.theme);
+ const queryType = useSelector((store) => store.queryType);
+ const [queryTypeSwitch, setQueryTypeSwitch] = useState(queryType);
+
+ useEffect(() => {
+ setQueryTypeSwitch(queryType);
+ }, [queryType, setQueryTypeSwitch]);
+
+ const SWITCH_OPTIONS = [
+ { value: "range", label: "Range" },
+ { value: "instant", label: "Instant" },
+ ];
+
+ function onSwitchChange(e) {
+ dispatch(setQueryType(e));
+ }
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/StatusBar/components/apiselector/ApiSelector.js b/src/components/StatusBar/components/apiselector/ApiSelector.js
index 0215a753..4e1322d9 100644
--- a/src/components/StatusBar/components/apiselector/ApiSelector.js
+++ b/src/components/StatusBar/components/apiselector/ApiSelector.js
@@ -28,7 +28,7 @@ export function ApiSelector() {
useEffect(() => {
setEditedUrl(apiUrl);
-
+ console.log("labels loaded from apiURL")
dispatch(loadLabels(apiUrl));
}, [apiUrl]);
@@ -57,7 +57,7 @@ export function ApiSelector() {
dispatch(setApiUrl(editedUrl));
setEditedUrl(editedUrl);
-
+ console.log("set from onurlsubmit")
dispatch(loadLabels(editedUrl));
dispatch(setLabelsBrowserOpen(false));
diff --git a/src/components/Switch/Switch.js b/src/components/Switch/Switch.js
new file mode 100644
index 00000000..6901c9b6
--- /dev/null
+++ b/src/components/Switch/Switch.js
@@ -0,0 +1,87 @@
+import styled from "@emotion/styled";
+import { useState } from "react";
+const Label = styled.div`
+ color: ${(props) => props.theme.textColor};
+ background: ${(props) => props.theme.buttonInactive};
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ padding: 0px 8px;
+`;
+const SwitchCont = styled.div`
+
+ display: flex;
+ align-items: center;
+ font-size: 12px;
+ background-color: ${(props) => props.theme.buttonInactive};
+
+ border: 1px solid ${(props) => props.theme.buttonBorder};
+ color: ${(props) => props.theme.textColor};
+ border-radius: 3px;
+ margin-right: 10px;
+`;
+
+const SwitchBtn = styled.div`
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ background: ${(props) =>
+ props.selected
+ ? props.theme.buttonDefault
+ : props.theme.buttonInactive};
+ border-left: ${(props) =>
+ props.position === "last"
+ ? `1px solid ${props.theme.buttonBorder}`
+ : "none"};
+ border-right: ${(props) =>
+ props.position === "first"
+ ? `1px solid ${props.theme.buttonBorder}`
+ : "none"};
+ border-radius: ${({ position }) =>
+ position === "first"
+ ? "3px 0px 0px 3px"
+ : position === "last"
+ ? "0px 3px 3px 0px"
+ : "0px"};
+ flex: 1;
+ height: 90%;
+
+ padding: 0px 12px;
+ font-size: 12px;
+ line-height: 20px;
+`;
+const getBtnPos = (key, arr) => {
+ const arrLen = arr.length;
+ return key === 0 ? "first" : key === arrLen - 1 ? "last" : "center";
+};
+
+export default function QueryTypeSwitch(props) {
+ const { options, defaultActive, onChange, label } = props;
+
+ const [activeBtn, setActiveBtn] = useState(defaultActive);
+
+ function setButtonValue(value) {
+ setActiveBtn(value);
+ onChange(value);
+ }
+
+ return (
+ <>
+
+
+ {options &&
+ options.map((value, key, arr) => (
+ setButtonValue(value.value)}
+ >
+ {value.label}
+
+ ))}
+
+ >
+ );
+}
diff --git a/src/helpers/UpdateStateFromQueryParams.js b/src/helpers/UpdateStateFromQueryParams.js
index d4e7a731..7e7ce589 100644
--- a/src/helpers/UpdateStateFromQueryParams.js
+++ b/src/helpers/UpdateStateFromQueryParams.js
@@ -2,20 +2,22 @@ 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,
+ setQueryTime,
setQueryStep,
+ setQueryType,
setStartTime,
setStopTime,
setTheme,
-
} from "../actions";
-import loadLabels from "../actions/loadLabels"
+import loadLabels from "../actions/loadLabels";
import loadLabelValues from "../actions/loadLabelValues";
import setFromTime from "../actions/setFromTime";
import setIsEmbed from "../actions/setIsEmbed";
@@ -26,8 +28,8 @@ import { setUrlQueryParams } from "../actions/setUrlQueryParams";
import { environment } from "../environment/env.dev";
import store from "../store/store";
export function UpdateStateFromQueryParams() {
-
- const isLightTheme = window.matchMedia('(prefers-color-scheme: light)').matches;
+ const isLightTheme = window.matchMedia("(prefers-color-scheme: light)")
+ .matches;
const { hash } = useLocation();
const dispatch = useDispatch();
@@ -36,48 +38,63 @@ export function UpdateStateFromQueryParams() {
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 to = useSelector((store) => store.to);
const step = useSelector((store) => store.step);
- const labels = useSelector((store) => store.labels)
+ 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 theme = useSelector((store) => isLightTheme ? 'light' : store.theme)
+ const queryType = useSelector((store) => store.queryType);
+ const time = useSelector((store) => store.time);
+ const theme = useSelector((store) =>
+ isLightTheme ? "light" : store.theme
+ );
const STORE_KEYS = {
apiUrl,
query,
+ queryType,
start,
limit,
step,
end: stop,
from,
to,
+ time,
isSubmit,
isEmbed,
- theme
+ theme,
};
const STORE_ACTIONS = {
apiUrl: setApiUrl,
query: setQuery,
+ queryType: setQueryType,
start: setStartTime,
limit: setQueryLimit,
step: setQueryStep,
end: setStopTime,
from: setFromTime,
to: setToTime,
+ time: setQueryTime,
isSubmit: setIsSubmit,
isEmbed: setIsEmbed,
- theme: setTheme
+ theme: setTheme,
};
- const STRING_VALUES = ["limit", "step", "apiUrl", "theme"];
+ const STRING_VALUES = [
+ "limit",
+ "step",
+ "apiUrl",
+ "theme",
+ "queryType",
+ "time",
+ ];
const QUERY_VALUE = "query";
- const TIME_VALUES = ["start", "end",];
+ const TIME_VALUES = ["start", "end"];
const BOOLEAN_VALUES = ["isSubmit", "isEmbed"];
@@ -106,10 +123,6 @@ export function UpdateStateFromQueryParams() {
startParams[param] !== ""
) {
dispatch(STORE_ACTIONS[param](startParams[param]));
-
- if(param === 'apiUrl') {
- dispatch(loadLabels(startParams[param]))
- }
} else if (
QUERY_VALUE === param &&
startParams[param] !== ""
@@ -117,6 +130,7 @@ export function UpdateStateFromQueryParams() {
const parsedQuery = decodeURIComponent(
startParams[param]
);
+
dispatch(STORE_ACTIONS[param](parsedQuery));
} else if (
TIME_VALUES.includes(param) &&
@@ -139,12 +153,14 @@ export function UpdateStateFromQueryParams() {
}
});
- decodeQuery(decodeURIComponent(startParams.query),apiUrl,labels);
+ 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);
@@ -174,7 +190,6 @@ export function UpdateStateFromQueryParams() {
window.location.hash = urlFromHash;
}
-
}, []);
useEffect(() => {
@@ -219,8 +234,7 @@ export function UpdateStateFromQueryParams() {
}, [STORE_KEYS]);
}
-export function decodeQuery(query, apiUrl, labels=[]) {
-
+export function decodeQuery(query, apiUrl, labels = []) {
const queryArr = query
?.match(/[^{\}]+(?=})/g, "$1")
?.map((m) => m.split(","))
@@ -270,11 +284,10 @@ export function decodeQuery(query, apiUrl, labels=[]) {
};
labelObj.values.push(valueObj);
labelsFromQuery.push(labelObj);
-
}
});
- const newLabels = labels
+ const newLabels = labels;
newLabels?.forEach((label) => {
if (label.selected && label.values > 0) {
@@ -298,10 +311,12 @@ export function decodeQuery(query, apiUrl, labels=[]) {
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
diff --git a/src/plugins/charts/index.js b/src/plugins/charts/index.js
index 44bcf4bb..789aafda 100644
--- a/src/plugins/charts/index.js
+++ b/src/plugins/charts/index.js
@@ -1,11 +1,8 @@
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";
@@ -60,11 +57,6 @@ export default function ClokiChart({ matrixData }) {
}
}
- /**
- *
- * Set chart types
- */
-
function plotChartData(data, type, element) {
const chartSeries = setChartTypeSeries(type);
const { timeformat, min, max } = formatDateRange(data);
@@ -73,7 +65,7 @@ export default function ClokiChart({ matrixData }) {
data,
$q.extend(true, {}, chartOptions, {
...chartSeries,
- xaxis: { timeformat, min, max, },
+ xaxis: { timeformat, min, max },
})
);
}
@@ -139,10 +131,10 @@ export default function ClokiChart({ matrixData }) {
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;
@@ -160,10 +152,12 @@ export default function ClokiChart({ matrixData }) {
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) {
@@ -171,11 +165,6 @@ export default function ClokiChart({ matrixData }) {
}
}
- /**
- *
- *Isolate Series clicking label
- */
-
function onLabelClick(e, v) {
let newList = [];
const lSelected =
@@ -218,7 +207,7 @@ export default function ClokiChart({ matrixData }) {
$q.extend(true, {}, chartOptions, {
series: getSeriesFromChartType(chartType),
- xaxis: { timeformat, min, max, },
+ xaxis: { timeformat, min, max },
})
);
@@ -244,7 +233,7 @@ export default function ClokiChart({ matrixData }) {
newData,
$q.extend(true, {}, chartOptions, {
series: getSeriesFromChartType(chartType),
- xaxis: { timeformat, min, max, },
+ xaxis: { timeformat, min, max },
})
);
@@ -279,7 +268,7 @@ export default function ClokiChart({ matrixData }) {
newData,
$q.extend(true, {}, chartOptions, {
series: getSeriesFromChartType(chartType),
- xaxis: { timeformat, min, max, },
+ xaxis: { timeformat, min, max },
})
);
@@ -300,7 +289,7 @@ export default function ClokiChart({ matrixData }) {
};
return (
-
+
diff --git a/src/plugins/queryhistory/index.js b/src/plugins/queryhistory/index.js
index 7605b3f2..09161321 100644
--- a/src/plugins/queryhistory/index.js
+++ b/src/plugins/queryhistory/index.js
@@ -766,6 +766,7 @@ const QueryHistory = (props) => {
style={{ maxHeight: "250px" }}
open={historyOpen}
variant={"persistent"}
+ theme={themes[theme]}
>
Embed View
diff --git a/src/store/createInitialState.js b/src/store/createInitialState.js
index 46f5c51f..50783d80 100644
--- a/src/store/createInitialState.js
+++ b/src/store/createInitialState.js
@@ -25,9 +25,11 @@ export default function initialState() {
linksHistory: linkService.getAll() || [],
timeRange: [],
query: urlState.query || "",
+ queryType: urlState.queryType || 'range',
logs: [],
matrixData: [],
loading: false,
+ queryResolution: 1,
start:
urlState.start ||
new Date(
@@ -35,6 +37,7 @@ export default function initialState() {
.subtract(5, "minutes")
.format("YYYY-MM-DDTHH:mm:ss.SSSZ")
),
+ time: urlState.time || '',
stop:
urlState.end ||
new Date(moment(Date.now()).format("YYYY-MM-DDTHH:mm:ss.SSSZ")),
@@ -60,6 +63,7 @@ export default function initialState() {
chartType: "line",
notifications: [],
theme: urlState.theme || "dark",
+ isEmptyView: false,
};
const debug = state.debugMode;
if (debug) console.log("🚧 LOGIC/ INITIAL STATE ::: ", state);
diff --git a/src/store/reducer.js b/src/store/reducer.js
index eba0a35c..50c922cc 100644
--- a/src/store/reducer.js
+++ b/src/store/reducer.js
@@ -40,6 +40,8 @@ const reducer = (state, action) => {
return { ...state, apiErrors: action.apiErrors };
case "SET_URL_QUERY_PARAMS":
return { ...state, urlQueryParams: action.urlQueryParams };
+ case "SET_QUERY_TYPE":
+ return { ...state, queryType: action.queryType };
case "SET_URL_LOCATION":
return { ...state, urlLocation: action.urlLocation };
case "SET_IS_SUBMIT":
@@ -62,8 +64,14 @@ const reducer = (state, action) => {
return { ...state, notifications: action.payload };
case "SET_DEBUG_MODE":
return { ...state, debugMode: action.debugMode };
- case "SET_THEME":
- return {...state, theme: action.theme};
+ case "SET_THEME":
+ return { ...state, theme: action.theme };
+ case "SET_QUERY_TIME":
+ return { ...state, time: action.time };
+ case "SET_QUERY_RESOLUTION":
+ return {...state, queryResolution: action.queryResolution};
+ case "SET_IS_EMPTY_VIEW":
+ return {...state, isEmptyView: action.isEmptyView};
default:
return { ...state };
}
diff --git a/src/theme/light.js b/src/theme/light.js
index aeb08a9d..a839b939 100644
--- a/src/theme/light.js
+++ b/src/theme/light.js
@@ -74,7 +74,7 @@ const widgetTitle = lightgrey.lg300;
const widgetTitleBorder = lightgrey.lg700;
const buttonDefault = white.w100;
const buttonHover = white.w700;
-const buttonInactive = black.b400;
+const buttonInactive = lightgrey.lg10;
const buttonBorder = lightgrey.lg100;
const buttonText = white.w200;
const inputTextFocus = orange.or100;