From d4ea0b7313a9194828403b9043c883ddb39e9ff2 Mon Sep 17 00:00:00 2001 From: ameen4455 Date: Wed, 31 Aug 2022 09:52:52 +0530 Subject: [PATCH 1/2] Add field fix --- src/components/Checkbox/index.js | 17 ++ src/components/Layout/Navbar.js | 2 +- src/components/SideDialog/index.js | 87 +++----- src/page/Dashboard/DateRangeSeletor.js | 103 +++++----- src/page/Dashboard/FieldBox.js | 43 ++++ src/page/Dashboard/index.js | 274 +++++++++++++------------ src/utils/api/constants.js | 2 + src/utils/api/logstreams.js | 16 +- src/utils/api/query.js | 56 ++++- 9 files changed, 348 insertions(+), 252 deletions(-) create mode 100644 src/components/Checkbox/index.js create mode 100644 src/page/Dashboard/FieldBox.js diff --git a/src/components/Checkbox/index.js b/src/components/Checkbox/index.js new file mode 100644 index 00000000..cb00358b --- /dev/null +++ b/src/components/Checkbox/index.js @@ -0,0 +1,17 @@ +const Checkbox = ({ name, selected, onClick }) => { + return ( +
+ +
+ ); +}; + +export default Checkbox; diff --git a/src/components/Layout/Navbar.js b/src/components/Layout/Navbar.js index bca4068e..89f7048f 100644 --- a/src/components/Layout/Navbar.js +++ b/src/components/Layout/Navbar.js @@ -15,7 +15,7 @@ const Navbar = ({setSidebarOpen}) => { return ( <> -
+
- - { - if (moment(fromInput, FORMAT).isValid) - setFromInput(moment(fromInput, FORMAT).format(FORMAT)); - }} - onChange={(e) => { - setFromInput(e.target.value); - }} - className="custom-focus custom-input text-xs" - type="text" - /> - {/*
- + + { + if (moment(fromInput, FORMAT).isValid) + setFromInput(moment(fromInput, FORMAT).format(FORMAT)); + }} + onChange={(e) => { + setFromInput(e.target.value); + }} + className="custom-focus custom-input text-xs" + type="text" /> -
*/} +
- - { - if (moment(toInput, FORMAT).isValid) - setToInput(moment(toInput, FORMAT).format(FORMAT)); - }} - onChange={(e) => { - setToInput(e.target.value); - }} - className="custom-input custom-focus text-xs" - type="text" - /> - {/*
- + + { + if (moment(toInput, FORMAT).isValid) + setToInput(moment(toInput, FORMAT).format(FORMAT)); + }} + onChange={(e) => { + setToInput(e.target.value); + }} + className="custom-input custom-focus text-xs" + type="text" /> -
*/} +
- +
{!checkValidDate() && (
diff --git a/src/page/Dashboard/FieldBox.js b/src/page/Dashboard/FieldBox.js new file mode 100644 index 00000000..63bd7556 --- /dev/null +++ b/src/page/Dashboard/FieldBox.js @@ -0,0 +1,43 @@ +import Checkbox from "../../components/Checkbox"; + +const Field = ({ + logStreamSchema, + selectedLogSchema, + setSelectedLogSchema, +}) => { + return ( +
+
+

Columns

+
+
+ {logStreamSchema?.data?.data?.fields?.map((key, index) => ( + + e.target.checked + ? setSelectedLogSchema( + logStreamSchema.data.data.fields + .map((x) => x.name) + .filter( + (schema) => + selectedLogSchema.includes(schema) || + schema === key.name, + ), + ) + : setSelectedLogSchema( + selectedLogSchema.filter( + (clickedName) => clickedName !== key.name, + ), + ) + } + selected={selectedLogSchema?.includes(key.name)} + /> + ))} +
+
+ ); +}; + +export default Field; diff --git a/src/page/Dashboard/index.js b/src/page/Dashboard/index.js index 5cfe31d3..e76336e1 100644 --- a/src/page/Dashboard/index.js +++ b/src/page/Dashboard/index.js @@ -1,5 +1,5 @@ import moment from "moment"; -import { useState, Fragment } from "react"; +import { useState, Fragment, useRef, useCallback, useEffect } from "react"; import Layout from "../../components/Layout"; import SideDialog from "../../components/SideDialog"; import { Listbox, Transition } from "@headlessui/react"; @@ -9,9 +9,13 @@ import { Combobox } from "@headlessui/react"; import { CheckIcon, XCircleIcon, SelectorIcon } from "@heroicons/react/solid"; import BeatLoader from "react-spinners/BeatLoader"; import { Menu } from "@headlessui/react"; -import { useGetLogStream, useQueryLogs } from "../../utils/api"; +import { + useGetLogStream, + useGetLogStreamSchema, + useQueryLogs, +} from "../../utils/api"; import "./index.css"; -import Picker from "./DateTimeRangePicker"; +import Field from "./FieldBox"; import Calendar from "./DateRangeSeletor"; const override = { @@ -28,6 +32,7 @@ function hasSubArray(master, sub) { )(0), ); } + const Dashboard = () => { const getCurrentTime = () => { let now = new Date(); @@ -45,6 +50,7 @@ const Dashboard = () => { return moment(start); }; + const getRange = () => { return { // "Live tracking": [moment(start), moment(end)], @@ -80,7 +86,8 @@ const Dashboard = () => { const [searchSelected, setSearchSelected] = useState({}); const [interval, setInterval] = useState(null); const [range, setRange] = useState(0); - const [dateRangeValues, setDateRangeValues] = useState(getRange); + const [selectedLogSchema, setSelectedLogSchema] = useState([]); + // const [dateRangeValues, setDateRangeValues] = useState(getRange); const [startTime, setStartTime] = useState( getCurrentTime().subtract(10, "minutes"), // .utcOffset("+00:00") @@ -135,38 +142,64 @@ const Dashboard = () => { const logStream = useGetLogStream({ retry: false, - staleTime: 60 * 1000, refetchOnWindowFocus: false, + onSuccess: (data) => { + setSelectedLogStream(data.data[0]); + }, + }); + + const logStreamSchema = useGetLogStreamSchema(selectedLogStream?.name, { + retry: false, + enabled: !!(selectedLogStream?.name != null), + refetchOnWindowFocus: false, + onSuccess: (data) => { + const allFields = data.data.fields.map((field) => { + return field.name; + }); + setSelectedLogSchema([...allFields]); + }, }); - if ( - logStream.isSuccess && - logStream?.data?.data.length && - !selectedLogStream - ) { - setSelectedLogStream(logStream.data.data[0]); - } const logQueries = useQueryLogs( selectedLogStream?.name, moment(startTime).utcOffset("+00:00").format("YYYY-MM-DDTHH:mm:ssZ"), moment(endTime).utcOffset("+00:00").format("YYYY-MM-DDTHH:mm:ssZ"), + selectedLogSchema, () => { if (range < 7) { const rangeVal = getRange(); - setDateRangeValues(rangeVal); setStartTime(rangeVal[rangeArr[range]][0]); setEndTime(rangeVal[rangeArr[range]][1]); } }, { retry: false, - enabled: !!(selectedLogStream?.name != null), + enabled: !!(selectedLogSchema?.length !== 0), refetchOnWindowFocus: false, refetchInterval: interval === null || range === 7 ? false : interval * 1000, }, ); + const { fetchNextPage } = logQueries; + + useEffect(() => { + let fetching = false; + const handleScroll = async (e) => { + const { scrollHeight, scrollTop, clientHeight } = + e.target.scrollingElement; + if (!fetching && scrollHeight - scrollTop <= clientHeight * 1.2) { + fetching = true; + await fetchNextPage(); + fetching = false; + } + }; + document.addEventListener("scroll", handleScroll); + return () => { + document.removeEventListener("scroll", handleScroll); + }; + }, [fetchNextPage]); + const timeZoneChange = (e) => { setTimeZone(e.target.value); }; @@ -197,11 +230,12 @@ const Dashboard = () => { <> 0 && logQueries?.data?.data[0]?.labels + logQueries?.data?.data?.length > 0 && + logQueries?.data?.data[0]?.labels } > -
-
+
+
@@ -444,7 +478,7 @@ const Dashboard = () => { } > {refreshInterval.map((interval) => ( - + {({ active, selected }) => (
setInterval(interval.value)} @@ -552,123 +586,95 @@ const Dashboard = () => {
*/} -
-
-
- - - - - - - - - - {logQueries.fetchStatus !== "idle" && - (!logQueries.data || - !logQueries.data?.data || - logQueries.data?.data?.length === 0) ? ( - - - - - +
+
+
+ +
+
+
+
-
Time
- - -
- Log - - Tags - - Edit -
- -
+ + + {selectedLogSchema?.map((name) => ( + + ))} - - ) : ( - - {logQueries?.data?.data?.map && - logQueries?.data?.data?.map( - (data, index) => - hasSubArray( - data.labels?.split(","), - labelSelected, - ) && ( - { - setOpen(true); - setClickedRow(data); - }} - className="cursor-pointer hover:bg-slate-100 hover:shadow" - key={index} - > - - - - - - ), - )} - - )} -
+ {name} +
- {timeZone === "UTC" || timeZone === "GMT" - ? moment - .utc(data.time) - .format("DD/MM/YYYY, HH:mm") - : moment(data.time) - .utcOffset("+05:30") - .format("DD/MM/YYYY, HH:mm")} - - {data.log} - - {data.labels - ?.split(",") - .filter((tag, index) => index <= 2) - .map((tag, index) => ( -
- {tag} -
- ))} -
- {data.labels - ?.split(",") - .filter((tag, index) => index <= 1) - .map((tag, index) => ( -
- {tag} -
- ))} -
+ + {logQueries.isLoading && + (!logQueries.data || + !logQueries.data?.data || + logQueries.data?.data?.length === 0) ? ( + + + + + + + + ) : ( + + {logQueries?.data?.pages?.map && + logQueries.data.pages.map( + (page) => + page?.data?.map && + page?.data?.map( + (data, index) => + hasSubArray( + data.labels?.split(","), + labelSelected, + ) && ( + { + setOpen(true); + setClickedRow(data); + }} + className="cursor-pointer hover:bg-slate-100 hover:shadow" + key={index} + > + {selectedLogSchema.map((schema) => ( + + {data[schema] || ""} + + ))} + + ), + ), + )} + + + + + + + )} + +
{logStream.isError || !logStream?.data?.data.length ? ( diff --git a/src/utils/api/constants.js b/src/utils/api/constants.js index 9140c9b4..4a47c059 100644 --- a/src/utils/api/constants.js +++ b/src/utils/api/constants.js @@ -1,5 +1,7 @@ export const LOG_STREAMS = "log_stream"; export const LOG_STREAMS_URL = "api/v1/logstream"; +export const LOG_STREAMS_SCHEMA = "logstream_schema"; + export const QUERY = "query"; export const QUERY_URL = "api/v1/query"; diff --git a/src/utils/api/logstreams.js b/src/utils/api/logstreams.js index 04ce6948..6e2f9afa 100644 --- a/src/utils/api/logstreams.js +++ b/src/utils/api/logstreams.js @@ -1,9 +1,21 @@ import { useQuery } from "@tanstack/react-query"; import { get } from "./index"; -import { LOG_STREAMS, LOG_STREAMS_URL } from "./constants"; +import { + LOG_STREAMS, + LOG_STREAMS_URL, +} from "./constants"; const getLogStream = async () => { return await get(LOG_STREAMS_URL); }; -export const useGetLogStream = (option = {}) => useQuery([LOG_STREAMS], getLogStream, option); \ No newline at end of file +export const useGetLogStream = (option = {}) => + useQuery([LOG_STREAMS], getLogStream, {...option}); + + +const getLogStreamSchema = async (streamName) => { + return await get(`${LOG_STREAMS_URL}/${streamName}/schema`); +}; + +export const useGetLogStreamSchema = (streamName, option = {}) => + useQuery([streamName], () => getLogStreamSchema(streamName), option); \ No newline at end of file diff --git a/src/utils/api/query.js b/src/utils/api/query.js index 658b7f5a..01be6263 100644 --- a/src/utils/api/query.js +++ b/src/utils/api/query.js @@ -1,12 +1,32 @@ -import { useQuery } from "@tanstack/react-query"; +import { useInfiniteQuery } from "@tanstack/react-query"; import { post } from "./index"; import { QUERY_URL, QUERY } from "./constants"; -const queryLogs = async (streamName, startTime, endTime, signal) => { +const queryLogs = async ( + streamName, + startTime, + endTime, + signal, + pageParam, + selectedLogSchema, +) => { + let dateStream = null; + + for (let index in selectedLogSchema) { + if ( + selectedLogSchema[index].includes("date") || + selectedLogSchema[index].includes("time") + ) { + dateStream = selectedLogSchema[index]; + } + } + return await post( QUERY_URL, { - query: `select * from ${streamName}`, + query: `select * from ${streamName} ${ + dateStream !== null ? `order by ${dateStream}` : "" + } limit 10 offset ${pageParam * 10}`, startTime: startTime, endTime: endTime, }, @@ -14,12 +34,32 @@ const queryLogs = async (streamName, startTime, endTime, signal) => { ); }; -export const useQueryLogs = (streamName, startTime, endTime, fn, option = {}) => - useQuery( +export const useQueryLogs = ( + streamName, + startTime, + endTime, + selectedLogSchema, + fn, + option = {}, +) => + useInfiniteQuery( [QUERY, streamName, startTime, endTime], - async ({ signal }) => { + async ({ signal, pageParam = 1 }) => { await fn(); - return await queryLogs(streamName, startTime, endTime, signal); + return await queryLogs( + streamName, + startTime, + endTime, + signal, + pageParam, + selectedLogSchema, + ); + }, + { + ...option, + getNextPageParam: (lastPage, allPages) => { + const nextPage = allPages.length + 1; + return lastPage.data.length !== 0 ? nextPage : undefined; + }, }, - option, ); From 4bfabd8dd36f2876c2e8ffa268f0c8610437a1d6 Mon Sep 17 00:00:00 2001 From: ameen4455 Date: Wed, 31 Aug 2022 23:34:35 +0530 Subject: [PATCH 2/2] Fix tags and metadata --- src/components/SideDialog/index.js | 39 ++++---- src/page/Dashboard/FieldBox.js | 48 +++++----- src/page/Dashboard/index.js | 148 ++++++++++++++++++----------- src/utils/api/query.js | 2 +- 4 files changed, 139 insertions(+), 98 deletions(-) diff --git a/src/components/SideDialog/index.js b/src/components/SideDialog/index.js index 120faeae..e1af30fe 100644 --- a/src/components/SideDialog/index.js +++ b/src/components/SideDialog/index.js @@ -13,6 +13,13 @@ function isJsonString(str) { return true; } +const removeMeta = (data) => { + let res = { ...data }; + delete res.p_tags; + delete res.p_metadata; + return { ...res }; +}; + export default function SideDialog({ open, setOpen, data }) { const [log, setLog] = useState({}); @@ -33,12 +40,12 @@ export default function SideDialog({ open, setOpen, data }) { // ) // ); - useEffect(() => { - if (Object.keys(data).length !== 0) { - setLog(JSON.parse(`${data?.log}`)); - } else { - } - }, [data]); + // useEffect(() => { + // if (Object.keys(data).length !== 0) { + // setLog(JSON.parse(`${data?.log}`)); + // } else { + // } + // }, [data]); return ( @@ -103,15 +110,15 @@ export default function SideDialog({ open, setOpen, data }) {
{/* Replace with your content */}
- {/*
- {data.labels?.split(",").map((tag, index) => ( +
+ {data.p_tags?.split(",").map((tag, index) => (
{tag}
))}
-
*/} +
{/*
@@ -123,19 +130,13 @@ export default function SideDialog({ open, setOpen, data }) {
*/}
- {Object.keys(data).map((field) => ( + {data.p_metadata?.split(",").map((field) => (
- {field} + {field.split("=")[0]}
- {isJsonString(data[field]) - ? JSON.stringify( - JSON.parse(data[field]), - null, - 2, - ) - : data[field]} + {field.split("=")[1]}
))} @@ -154,7 +155,7 @@ export default function SideDialog({ open, setOpen, data }) {
-                                  {JSON.stringify(data, null, 2)}
+                                  {JSON.stringify(removeMeta(data), null, 2)}
                                 
{/*
                                   {data.log}
diff --git a/src/page/Dashboard/FieldBox.js b/src/page/Dashboard/FieldBox.js
index 63bd7556..fa2c21d6 100644
--- a/src/page/Dashboard/FieldBox.js
+++ b/src/page/Dashboard/FieldBox.js
@@ -11,30 +11,32 @@ const Field = ({
         

Columns

- {logStreamSchema?.data?.data?.fields?.map((key, index) => ( - - e.target.checked - ? setSelectedLogSchema( - logStreamSchema.data.data.fields - .map((x) => x.name) - .filter( - (schema) => - selectedLogSchema.includes(schema) || - schema === key.name, + {logStreamSchema?.data?.data?.fields + ?.filter((field) => field.name !== "p_metadata" && field.name !== "p_tags") + .map((key, index) => ( + + e.target.checked + ? setSelectedLogSchema( + logStreamSchema.data.data.fields + .map((x) => x.name) + .filter( + (schema) => + selectedLogSchema.includes(schema) || + schema === key.name, + ), + ) + : setSelectedLogSchema( + selectedLogSchema.filter( + (clickedName) => clickedName !== key.name, ), - ) - : setSelectedLogSchema( - selectedLogSchema.filter( - (clickedName) => clickedName !== key.name, - ), - ) - } - selected={selectedLogSchema?.includes(key.name)} - /> - ))} + ) + } + selected={selectedLogSchema?.includes(key.name)} + /> + ))}
); diff --git a/src/page/Dashboard/index.js b/src/page/Dashboard/index.js index e76336e1..7758fdb1 100644 --- a/src/page/Dashboard/index.js +++ b/src/page/Dashboard/index.js @@ -25,6 +25,8 @@ const override = { }; function hasSubArray(master, sub) { + master.sort(); + sub.sort(); return sub.every( ( (i) => (v) => @@ -87,6 +89,8 @@ const Dashboard = () => { const [interval, setInterval] = useState(null); const [range, setRange] = useState(0); const [selectedLogSchema, setSelectedLogSchema] = useState([]); + const [availableTags, setAvailableTags] = useState([]); + const [selectedTags, setSelectedTags] = useState([]); // const [dateRangeValues, setDateRangeValues] = useState(getRange); const [startTime, setStartTime] = useState( getCurrentTime().subtract(10, "minutes"), @@ -94,6 +98,14 @@ const Dashboard = () => { // .format("YYYY-MM-DDThh:mm:ss), ); + const addAvailableTags = (label) => { + if (availableTags.includes(label)) { + return; + } else { + setAvailableTags([...availableTags, label]); + } + }; + const [endTime, setEndTime] = useState(getCurrentTime()); const refreshInterval = [ @@ -156,7 +168,12 @@ const Dashboard = () => { const allFields = data.data.fields.map((field) => { return field.name; }); - setSelectedLogSchema([...allFields]); + + setSelectedLogSchema([ + ...allFields.filter( + (field) => field !== "p_metadata" && field !== "p_tags", + ), + ]); }, }); @@ -173,6 +190,10 @@ const Dashboard = () => { } }, { + onSuccess: () => { + setSelectedTags([]); + setAvailableTags([]); + }, retry: false, enabled: !!(selectedLogSchema?.length !== 0), refetchOnWindowFocus: false, @@ -220,10 +241,8 @@ const Dashboard = () => { } }; - const clearLabel = (label) => { - const labelArray = labelSelected; - const filteredArray = [...labelArray.filter((item) => item !== label)]; - setLabelSelected(filteredArray); + const removeTag = (tag) => { + setSelectedTags([...selectedTags.filter((item) => item !== tag)]); }; return ( @@ -375,7 +394,7 @@ const Dashboard = () => { />
- { )) )} - + */}
@@ -501,18 +520,18 @@ const Dashboard = () => { Tag filters
- {labelSelected.length > 0 - ? labelSelected.map((label) => ( + {selectedTags.length > 0 + ? selectedTags.map((tag) => ( - {label} + {tag} clearLabel(label)} + onClick={() => removeTag(tag)} className="hover:text-gray-600 transform duration-200 text-gray-700 w-4 absolute top-1 right-1" /> @@ -532,43 +551,39 @@ const Dashboard = () => { leaveTo="opacity-0" > - {Object.keys( - logQueries?.data?.data ? logQueries?.data?.data : [], - ).length !== 0 ? ( - logQueries?.data?.data[0].labels - ?.split(",") - .map((person, personIdx) => ( - - `relative cursor-default select-none py-2 px-2 ${ - active - ? "bg-bluePrimary text-white" - : "text-gray-900" - }` - } - value={person} - > - {({ selected }) => ( - <> - - {selected ? ( -
- -
- ) : ( -
- )} - {person} -
- - )} -
- )) + {availableTags.length !== 0 ? ( + availableTags.map((person, personIdx) => ( + + `relative cursor-default select-none py-2 px-2 ${ + active + ? "bg-bluePrimary text-white" + : "text-gray-900" + }` + } + value={person} + > + {({ selected }) => ( + <> + + {selected ? ( +
+ +
+ ) : ( +
+ )} + {person} +
+ + )} +
+ )) ) : ( Nothing Found )} @@ -588,7 +603,7 @@ const Dashboard = () => {
-
+
{ {selectedLogSchema?.map((name) => ( {name} ))} + + Tags + {logQueries.isLoading && @@ -638,11 +659,16 @@ const Dashboard = () => { page?.data?.map( (data, index) => hasSubArray( - data.labels?.split(","), - labelSelected, - ) && ( + data.p_tags?.split(","), + selectedTags, + ) && + (searchQuery === "" || + JSON.stringify(data) + .toLowerCase() + .includes(searchQuery.toLowerCase())) && ( { + console.log(JSON.stringify(data)); setOpen(true); setClickedRow(data); }} @@ -654,6 +680,18 @@ const Dashboard = () => { {data[schema] || ""} ))} + + {data.p_tags + ?.split(",") + .map((tag, index) => { + addAvailableTags(tag); + return ( +
+ {tag} +
+ ); + })} + ), ), diff --git a/src/utils/api/query.js b/src/utils/api/query.js index 01be6263..a6fa2d99 100644 --- a/src/utils/api/query.js +++ b/src/utils/api/query.js @@ -26,7 +26,7 @@ const queryLogs = async ( { query: `select * from ${streamName} ${ dateStream !== null ? `order by ${dateStream}` : "" - } limit 10 offset ${pageParam * 10}`, + } limit 10 ${pageParam === 1 ? "" : `offset ${pageParam * 10}`} `, startTime: startTime, endTime: endTime, },