diff --git a/package.json b/package.json index a7402951..625742ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "qryn-view", - "version": "3.2.11", + "version": "3.3.0", "description": "Data Explorer UI for qryn", "repository": { "type": "git", diff --git a/packages/main/package.json b/packages/main/package.json index f0aa75e4..ec2a31eb 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -1,7 +1,7 @@ { "name": "@ui/main", "private": true, - "version": "3.2.11", + "version": "3.3.0", "type": "module", "scripts": { "dev": "VITE_APP_VERSION=$npm_package_version vite", diff --git a/packages/main/qrynui/Table/models/tableModels.tsx b/packages/main/qrynui/Table/models/tableModels.tsx index c376fd57..e3a73564 100644 --- a/packages/main/qrynui/Table/models/tableModels.tsx +++ b/packages/main/qrynui/Table/models/tableModels.tsx @@ -12,7 +12,6 @@ import { RankingInfo, } from "@tanstack/match-sorter-utils"; - export const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { // Rank the item const itemRank = rankItem(row.getValue(columnId), value); @@ -49,24 +48,22 @@ export const defaultColumn: Partial> = { cell: (info: any) => info.getValue(), }; - export const getTableMeta = ( setData: React.Dispatch>, skipAutoResetPageIndex: () => void -) => - ({ - updateData: (rowIndex, columnId, value) => { - // Skip age index reset until after next rerender - skipAutoResetPageIndex(); - setData((old) => - old.map((row, index) => { - if (index !== rowIndex) return row; +): TableMeta => ({ + updateData: (rowIndex, columnId, value) => { + // Skip age index reset until after next rerender + skipAutoResetPageIndex(); + setData((old) => + old.map((row, index) => { + if (index !== rowIndex) return row; - return { - ...old[rowIndex]!, - [columnId]: value, - }; - }) - ); - }, - } as TableMeta); + return { + ...old[rowIndex]!, + [columnId]: value, + }; + }) + ); + }, +}); diff --git a/packages/main/src/components/DataViews/components/Charts/index.tsx b/packages/main/src/components/DataViews/components/Charts/QrynChart.tsx similarity index 77% rename from packages/main/src/components/DataViews/components/Charts/index.tsx rename to packages/main/src/components/DataViews/components/Charts/QrynChart.tsx index b86b94cd..2c31e624 100644 --- a/packages/main/src/components/DataViews/components/Charts/index.tsx +++ b/packages/main/src/components/DataViews/components/Charts/QrynChart.tsx @@ -9,7 +9,7 @@ import "react-flot/flot-override/jquery.flot.resize"; import "react-flot/flot/jquery.flot.stack.min.js"; //React import { useState, useEffect, useRef, useMemo } from "react"; -import { useDispatch} from "react-redux"; +import { useDispatch } from "react-redux"; //Packages import moment from "moment"; import { format } from "date-fns"; @@ -37,14 +37,43 @@ import UseTooltip from "./UseTooltip"; import { useChartOptions, useMatrixData } from "./hooks"; import { FlotChart } from "./FlotChart"; import useTheme from "@ui/theme/useTheme"; - - -export default function QrynChart(props: any): any { +import { type QueryType } from "@ui/store/actions/types"; + +export type ActualQuery = { + id: string; + expr: string; + dataSourceType: string; + queryType: QueryType; + limit: number; + panel: any; + isLogsVolume: boolean; + start: any; + stop: any; +}; + +export type QrynChartProps = { + matrixData?: any; + actualQuery?: ActualQuery; + type?: string; + tWidth?: any; + vHeight?: any; +}; + +export default function QrynChart(props: QrynChartProps) { const { matrixData, actualQuery, type } = props; const { tWidth } = props; - const { expr, dataSourceType, queryType, limit, panel, id, isLogsVolume } = - actualQuery; + const { + expr, + dataSourceType, + queryType, + limit, + panel, + id, + isLogsVolume, + start, + stop, + } = actualQuery; const chartRef = useRef(null); @@ -59,6 +88,7 @@ export default function QrynChart(props: any): any { matrixData, isLogsVolume && type === "stream" ); + const dispatch: any = useDispatch(); const [isSpliced, setIsSpliced] = useState(true); @@ -78,17 +108,9 @@ export default function QrynChart(props: any): any { const [chartOptions, setChartOptions] = useState(chartOpts); const getInitialChartType = useMemo(() => { + if (isLogsVolume && type === "stream") return "bar"; let localType = getTypeFromLocal(); - if (isLogsVolume && type === "stream") { - return "bar"; - } else { - if (localType !== "") { - return localType; - } else { - return "line"; - } - } - + return localType ?? "line" }, [isLogsVolume]); const [chartType, setChartType] = useState(getInitialChartType); @@ -100,7 +122,17 @@ export default function QrynChart(props: any): any { } const chartSeries = setChartTypeSeries(type, barWidth); - const { timeformat, min, max } = formatDateRange(data); + + const startDate = new Date(start).getTime() * 1000; + + const stopDate = new Date(stop).getTime() * 1000; + + const { timeformat, min, max } = formatDateRange( + data, + startDate, + stopDate + ); + return $q.plot( element, data, @@ -113,7 +145,9 @@ export default function QrynChart(props: any): any { function onSetChartType(type: any) { const element = $q(chartRef.current); + const data = isSpliced ? chartData : allData; + const newData = getNewData(data, type); try { @@ -134,9 +168,10 @@ export default function QrynChart(props: any): any { event.preventDefault(); let newData = []; - const lSelected = + const labels_selected = JSON.parse(localStorage.getItem("labelsSelected") || "null") || []; - if (lSelected?.length > 0) { + // if there are labels already selected + if (labels_selected?.length > 0) { let barWidth = 0; if (isLogsVolume && type === "stream") { barWidth = getBarWidth(getTimeSpan(data), tWidth); @@ -147,7 +182,7 @@ export default function QrynChart(props: any): any { barWidth, isLogsVolume && type === "stream" ); - const ids = lSelected?.map((m: any) => m.id); + const ids = labels_selected?.map((m: any) => m.id); const dataMapped = data?.map((series: any) => { if (!ids.includes(series.id)) { return { @@ -170,20 +205,32 @@ export default function QrynChart(props: any): any { }; } }); + newData = dataMapped; + setChartData(() => dataMapped); } else { + + setChartData(() => data); newData = data; } try { + // + // const startDate = (new Date(start).getTime()) * 1000 + + // const stopDate = ( new Date(stop).getTime() ) * 1000 + + const { first, last } = getTimeSpan(newData); + let plot = $q.plot( element, newData, $q.extend(true, {}, chartOptions, { xaxis: { - min: ranges.xaxis.from - 100000, - max: ranges.xaxis.to + 100000, - timeformat: formatDateRange(newData).timeformat, + min: Math.round(ranges.xaxis.from) - 100000, + max: Math.round(ranges.xaxis.to) + 100000, + timeformat: formatDateRange(newData, first, last) + .timeformat, }, }) ); @@ -222,20 +269,33 @@ export default function QrynChart(props: any): any { } } - function onLabelClick(e: any, v: any) { + function onLabelClick(event: any, value: any) { + // actions on label click let newList = []; - const lSelected = + // 1- check for labels selected + const labels_selected = JSON.parse(localStorage.getItem("labelsSelected") || "null") || []; - if (lSelected.some(({ id }: any) => id === v.id)) { - const filtered = lSelected.filter((f: any) => f.id !== v.id); + // check if same label value whas selected + + if (labels_selected.some(({ id }: any) => id === value.id)) { + const filtered = labels_selected.filter( + (filtered: any) => filtered.id !== value.id + ); + // if selected, store on localstorage localStorage.setItem("labelsSelected", JSON.stringify(filtered)); + // set the newList of labels as the filtered newList = filtered; + } else { - newList = lSelected.concat(v); + // if no labels selected, just concat new value and save in localstorage + + newList = labels_selected.concat(value); localStorage.setItem("labelsSelected", JSON.stringify(newList)); } + + if (newList.length > 0) { const ids = newList?.map((m: any) => m.id); @@ -243,7 +303,8 @@ export default function QrynChart(props: any): any { chartType, 0 ); - let dataSelected = e?.map((series: any) => { + + let dataSelected = event?.map((series: any) => { if (!ids.includes(series.id)) { return { ...series, @@ -262,7 +323,15 @@ export default function QrynChart(props: any): any { } }); - const { timeformat, min, max } = formatDateRange(dataSelected); + const startDate = new Date(start).getTime() * 1000; + + const stopDate = new Date(stop).getTime() * 1000; + + const { timeformat, min, max } = formatDateRange( + dataSelected, + startDate, + stopDate + ); let barWidth = 0; if (isLogsVolume && type === "stream") { barWidth = getBarWidth(getTimeSpan(dataSelected), tWidth); @@ -303,7 +372,15 @@ export default function QrynChart(props: any): any { shadowSize: 0, }; }); - const { timeformat, min, max } = formatDateRange(newData); + const startDate = new Date(start).getTime() * 1000; + + const stopDate = new Date(stop).getTime() * 1000; + + const { timeformat, min, max } = formatDateRange( + newData, + startDate, + stopDate + ); let plot = $q.plot( element, @@ -331,14 +408,12 @@ export default function QrynChart(props: any): any { //setChartData(getDataParsed(isSpliced)); setChartData(matrix); localStorage.setItem("labelsSelected", JSON.stringify([])); - }, []); useEffect(() => { setChartOptions(chartOptions); setElement(chartRef.current); drawChartFromData(); - }, [matrixData, isSpliced]); function drawChartFromData() { @@ -352,7 +427,15 @@ export default function QrynChart(props: any): any { barWidth = getBarWidth(getTimeSpan(newData), tWidth); } - const { timeformat, min, max } = formatDateRange(newData); + const startDate = new Date(start).getTime() * 1000; + + const stopDate = new Date(stop).getTime() * 1000; + + const { timeformat, min, max } = formatDateRange( + newData, + startDate, + stopDate + ); let { bars, lines, points, stack } = getSeriesFromChartType( chartType, @@ -409,7 +492,6 @@ export default function QrynChart(props: any): any { if (pointSet.size === 1 && chartType !== "bar") { onSetChartType("bar"); } - return ; } diff --git a/packages/main/src/components/DataViews/components/Charts/consts.ts b/packages/main/src/components/DataViews/components/Charts/consts.ts index 0f731a3b..067f0a46 100644 --- a/packages/main/src/components/DataViews/components/Charts/consts.ts +++ b/packages/main/src/components/DataViews/components/Charts/consts.ts @@ -65,7 +65,7 @@ export const CHART_OPTIONS = (isLogsVolume = false) => ({ // pass the bar width // calculate here the span width -export const CHART_BAR_SERIES = (barWidth=0, isLogsVolume = false) => ({ +export const CHART_BAR_SERIES = (barWidth = 0, isLogsVolume = false) => ({ stack: isLogsVolume, lines: { show: false, diff --git a/packages/main/src/components/DataViews/components/Charts/consts/index.ts b/packages/main/src/components/DataViews/components/Charts/consts/index.ts index d50b5c02..bb36a3bb 100644 --- a/packages/main/src/components/DataViews/components/Charts/consts/index.ts +++ b/packages/main/src/components/DataViews/components/Charts/consts/index.ts @@ -51,7 +51,7 @@ export const CHART_OPTIONS = (isLogsVolume=false)=>({ shadowSize: 0, zero: false, }, - points: { show: true, radius: 1, shadowSize: 0, zero: false }, + points: { show: true, radius: 1, shadowSize: 0, zero: true}, }, markings: { clickable: false, diff --git a/packages/main/src/components/DataViews/components/Charts/helpers/index.tsx b/packages/main/src/components/DataViews/components/Charts/helpers/index.tsx index 6b1758eb..5ee0aca6 100644 --- a/packages/main/src/components/DataViews/components/Charts/helpers/index.tsx +++ b/packages/main/src/components/DataViews/components/Charts/helpers/index.tsx @@ -129,19 +129,25 @@ export const getBarWidth = (tSpan: TimeSpan, width: number) => { return barWidth; }; -export function formatDateRange(data: any) { - const { timeSpan, first, last } = getTimeSpan(data); +export function formatDateRange(data: any, start, stop) { + + + + // first and last should be only when selecting with range + const { timeSpan, } = getTimeSpan(data); const formatted = - timeSpan > 1 + timeSpan > 0 ? "%m/%d %H:%M" : timeSpan > 30 ? "%y/%m/%d %H:%M" : "%H:%M:%S"; return { timeformat: formatted, - min: first, - max: last, + // min: first, + // max: last, + min: start / 1000, + max: stop / 1000, }; } diff --git a/packages/main/src/components/DataViews/views/LogsView.tsx b/packages/main/src/components/DataViews/views/LogsView.tsx index c9b10756..533e3eb4 100644 --- a/packages/main/src/components/DataViews/views/LogsView.tsx +++ b/packages/main/src/components/DataViews/views/LogsView.tsx @@ -5,7 +5,7 @@ import { VectorTable } from "../components/Table/VectorTable/VectorTable"; import { ViewHeader } from "../components/ViewHeader"; import { TabsListq, Tabq, TabPanelq, ViewStyled } from "./styled"; import { localTabsState } from "../helpers"; -import QrynChart from "../components/Charts"; +import QrynChart from "../components/Charts/QrynChart"; import { LogRows } from "../components/Logs/LogRows"; import ReactJson from "@microlink/react-json-view"; import React from "react"; diff --git a/packages/main/src/components/DataViews/views/MatrixView.tsx b/packages/main/src/components/DataViews/views/MatrixView.tsx index c64f398c..454a7f7a 100644 --- a/packages/main/src/components/DataViews/views/MatrixView.tsx +++ b/packages/main/src/components/DataViews/views/MatrixView.tsx @@ -1,4 +1,4 @@ -import QrynChart from "../components/Charts"; +import QrynChart from "../components/Charts/QrynChart"; import { VectorTable } from "../components/Table/VectorTable/VectorTable"; import { ViewHeader } from "../components/ViewHeader"; import { Tabs } from "@mui/base"; diff --git a/packages/main/src/components/DataViews/views/VectorView.tsx b/packages/main/src/components/DataViews/views/VectorView.tsx index e804d4e9..b19e161a 100644 --- a/packages/main/src/components/DataViews/views/VectorView.tsx +++ b/packages/main/src/components/DataViews/views/VectorView.tsx @@ -1,6 +1,6 @@ import { Tabs } from "@mui/base"; import { useMemo, useRef, useState, SyntheticEvent } from "react"; -import QrynChart from "../components/Charts"; +import QrynChart from "../components/Charts/QrynChart"; import { VectorTable } from "../components/Table/VectorTable/VectorTable"; import { ViewHeader } from "../components/ViewHeader"; import { TabsListq, Tabq, TabPanelq, ViewStyled } from "./styled"; diff --git a/packages/main/views/DataSources/ConfirmDialog.tsx b/packages/main/views/DataSources/ConfirmDialog.tsx new file mode 100644 index 00000000..58c3fada --- /dev/null +++ b/packages/main/views/DataSources/ConfirmDialog.tsx @@ -0,0 +1,84 @@ +import * as React from "react"; +import { Button } from "./ui"; +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogContentText from "@mui/material/DialogContentText"; +import DialogTitle from "@mui/material/DialogTitle"; +import DOMPurify from "isomorphic-dompurify"; +import useTheme from "@ui/theme/useTheme"; + +export type ConfirmDialogProps = { + saveDataSource: () => void; + changed: boolean; +}; + +export default function ConfirmDialog({ + saveDataSource, + changed, +}: ConfirmDialogProps) { + const [open, setOpen] = React.useState(false); + const theme = useTheme(); + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleSave = () => { + saveDataSource(); + handleClose(); + }; + + const handleCancel = () => { + handleClose(); + }; + + return ( + +