From 5aed6520d500459b81524bbc10237c11e483b66e Mon Sep 17 00:00:00 2001 From: Kartik Gupta Date: Fri, 21 Jul 2023 16:55:53 +0530 Subject: [PATCH 1/3] Stats --- src/@types/parseable/api/stream.ts | 13 + src/api/constants.ts | 3 + src/api/logStream.ts | 14 +- src/components/Navbar/index.tsx | 2 +- src/constants/routes.ts | 1 + src/hooks/useGetLogStreamAlert.ts | 40 +++ src/hooks/useGetLogStreamRetention.ts | 40 +++ src/hooks/useGetLogStreamStat.ts | 40 +++ src/pages/Stats/Alerts.tsx | 52 ++++ src/pages/Stats/Status.tsx | 51 ++++ src/pages/Stats/index.tsx | 24 ++ src/pages/Stats/styles.tsx | 407 ++++++++++++++++++++++++++ src/routes/elements.tsx | 10 + src/routes/index.tsx | 5 +- 14 files changed, 698 insertions(+), 4 deletions(-) create mode 100644 src/hooks/useGetLogStreamAlert.ts create mode 100644 src/hooks/useGetLogStreamRetention.ts create mode 100644 src/hooks/useGetLogStreamStat.ts create mode 100644 src/pages/Stats/Alerts.tsx create mode 100644 src/pages/Stats/Status.tsx create mode 100644 src/pages/Stats/index.tsx create mode 100644 src/pages/Stats/styles.tsx diff --git a/src/@types/parseable/api/stream.ts b/src/@types/parseable/api/stream.ts index 9875acb0..e4c1fc66 100644 --- a/src/@types/parseable/api/stream.ts +++ b/src/@types/parseable/api/stream.ts @@ -6,3 +6,16 @@ export type LogStreamSchemaData = { fields: Array; metadata: Record; }; +export type LogStreamStat= { + ingestion:{ + count :number; + format: string; + size: string; + } + storage:{ + format:string; + size:string; + } + stream:string; + time:string +} diff --git a/src/api/constants.ts b/src/api/constants.ts index 0a03ff10..76d6519d 100644 --- a/src/api/constants.ts +++ b/src/api/constants.ts @@ -4,3 +4,6 @@ export const HEALTH_LIVENESS_URL = `${API_V1}/liveness`; export const LOG_STREAM_LIST_URL = `${API_V1}/logstream`; export const LOG_STREAMS_SCHEMA_URL = (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/schema`; export const LOG_QUERY_URL = `${API_V1}/query`; +export const STATS_STREAMS_ALERTS_URL= (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/alert`; +export const STATS_STREAMS_RETRNTION_URL= (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/retention`; +export const STATS_STREAMS_STATS_URL = (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/stats`; \ No newline at end of file diff --git a/src/api/logStream.ts b/src/api/logStream.ts index 7c034a99..4b545dc3 100644 --- a/src/api/logStream.ts +++ b/src/api/logStream.ts @@ -1,5 +1,5 @@ import { Axios } from './axios'; -import { LOG_STREAMS_SCHEMA_URL, LOG_STREAM_LIST_URL } from './constants'; +import { LOG_STREAMS_SCHEMA_URL, LOG_STREAM_LIST_URL, STATS_STREAMS_ALERTS_URL, STATS_STREAMS_RETRNTION_URL, STATS_STREAMS_STATS_URL } from './constants'; import { LogStreamData, LogStreamSchemaData } from '@/@types/parseable/api/stream'; export const getLogStreamList = () => { @@ -9,3 +9,15 @@ export const getLogStreamList = () => { export const getLogStreamSchema = (streamName: string) => { return Axios().get(LOG_STREAMS_SCHEMA_URL(streamName)); }; + +export const getLogStreamAlerts = (streamName: string) => { + return Axios().get(STATS_STREAMS_ALERTS_URL(streamName)); +} + +export const getLogStreamRetention = (streamName: string) => { + return Axios().get(STATS_STREAMS_RETRNTION_URL(streamName)); +} + +export const getLogStreamStats = (streamName: string) => { + return Axios().get(STATS_STREAMS_STATS_URL(streamName)); +} \ No newline at end of file diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index ba8ad835..a320c781 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -20,7 +20,7 @@ import { LOGIN_ROUTE } from '@/constants/routes'; const links = [ { icon: IconColumns , label: 'Logs', pathname: "/logs" }, { icon: IconZoomCode , label: 'Query', pathname: "/query" }, - { icon: IconReportAnalytics , label: 'Stats', pathname: "/metrics"} + { icon: IconReportAnalytics , label: 'Stats', pathname: "/stats"} ]; type NavbarProps = Omit; diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 0ef34bf8..4b9909d1 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -3,3 +3,4 @@ export const LOGS_ROUTE = '/:streamName/logs'; export const LOGIN_ROUTE = '/login'; export const ALL_ROUTE = '*'; export const QUERY_ROUTE = '/:streamName/query'; +export const STATS_ROUTE = '/:streamName/stats'; diff --git a/src/hooks/useGetLogStreamAlert.ts b/src/hooks/useGetLogStreamAlert.ts new file mode 100644 index 00000000..d0ba346a --- /dev/null +++ b/src/hooks/useGetLogStreamAlert.ts @@ -0,0 +1,40 @@ +import { LogStreamSchemaData } from '@/@types/parseable/api/stream'; +import { getLogStreamAlerts } from '@/api/logStream'; +import { StatusCodes } from 'http-status-codes'; +import useMountedState from './useMountedState'; + +export const useGetLogStreamAlert = () => { + const [data, setData] = useMountedState(null); + const [error, setError] = useMountedState(null); + const [loading, setLoading] = useMountedState(false); + + const getLogAlert= async (streamName: string) => { + try { + setLoading(true); + setError(null); + const res = await getLogStreamAlerts(streamName); + + switch (res.status) { + case StatusCodes.OK: { + const streams = res.data; + + setData(streams); + break; + } + default: { + setError('Failed to get ALert'); + } + } + } catch { + setError('Failed to get ALert'); + } finally { + setLoading(false); + } + }; + + const resetData = () => { + setData(null); + }; + + return { data, error, loading, getLogAlert, resetData }; +}; diff --git a/src/hooks/useGetLogStreamRetention.ts b/src/hooks/useGetLogStreamRetention.ts new file mode 100644 index 00000000..a3237e48 --- /dev/null +++ b/src/hooks/useGetLogStreamRetention.ts @@ -0,0 +1,40 @@ +import { LogStreamSchemaData } from '@/@types/parseable/api/stream'; +import { getLogStreamRetention } from '@/api/logStream'; +import { StatusCodes } from 'http-status-codes'; +import useMountedState from './useMountedState'; + +export const useGetLogStreamRetention = () => { + const [data, setData] = useMountedState(null); + const [error, setError] = useMountedState(null); + const [loading, setLoading] = useMountedState(false); + + const getLogRetention= async (streamName: string) => { + try { + setLoading(true); + setError(null); + const res = await getLogStreamRetention(streamName); + + switch (res.status) { + case StatusCodes.OK: { + const streams = res.data; + + setData(streams); + break; + } + default: { + setError('Failed to get ALert'); + } + } + } catch { + setError('Failed to get ALert'); + } finally { + setLoading(false); + } + }; + + const resetData = () => { + setData(null); + }; + + return { data, error, loading, getLogRetention, resetData }; +}; diff --git a/src/hooks/useGetLogStreamStat.ts b/src/hooks/useGetLogStreamStat.ts new file mode 100644 index 00000000..98514100 --- /dev/null +++ b/src/hooks/useGetLogStreamStat.ts @@ -0,0 +1,40 @@ +import { LogStreamStat } from '@/@types/parseable/api/stream'; +import { getLogStreamStats } from '@/api/logStream'; +import { StatusCodes } from 'http-status-codes'; +import useMountedState from './useMountedState'; + +export const useGetLogStreamStat = () => { + const [data, setData] = useMountedState(null); + const [error, setError] = useMountedState(null); + const [loading, setLoading] = useMountedState(false); + + const getLogStat= async (streamName: string) => { + try { + setLoading(true); + setError(null); + const res = await getLogStreamStats(streamName); + + switch (res.status) { + case StatusCodes.OK: { + const streams = res.data; + + setData(streams); + break; + } + default: { + setError('Failed to get ALert'); + } + } + } catch { + setError('Failed to get ALert'); + } finally { + setLoading(false); + } + }; + + const resetData = () => { + setData(null); + }; + + return { data, error, loading, getLogStat, resetData }; +}; diff --git a/src/pages/Stats/Alerts.tsx b/src/pages/Stats/Alerts.tsx new file mode 100644 index 00000000..d3cb12db --- /dev/null +++ b/src/pages/Stats/Alerts.tsx @@ -0,0 +1,52 @@ + +import { useGetLogStreamAlert } from '@/hooks/useGetLogStreamAlert'; +import { useGetLogStreamRetention } from '@/hooks/useGetLogStreamRetention'; +import { useGetLogStreamStat } from '@/hooks/useGetLogStreamStat'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; +import { Box, Text } from '@mantine/core'; +import { useDocumentTitle } from '@mantine/hooks'; +import { FC, useEffect } from 'react'; +import { useAlertsStyles } from './styles'; + + +const Alerts: FC = () => { + useDocumentTitle('Parseable | Login'); + const { state: { subLogQuery } } = useHeaderContext(); + const { data, error, loading, getLogAlert, resetData } = useGetLogStreamAlert(); + const { data: data2, error: error2, loading: loading2, getLogRetention: getLogRetention, resetData: resetData2 } = useGetLogStreamRetention(); + const { data: data3, error: error3, loading: loading3, getLogStat: getLogStat, resetData: resetData3 } = useGetLogStreamStat(); + useEffect(() => { + getLogAlert(subLogQuery.get().streamName); + getLogRetention(subLogQuery.get().streamName); + getLogStat(subLogQuery.get().streamName); + return () => { + resetData(); + resetData2(); + resetData3(); + } + }, [subLogQuery]); + const { classes } = useAlertsStyles(); + const { container, headContainer,statusText ,statusTextResult ,genterateContiner ,genterateText,genterateTextResult} = classes + return ( + + + + Status: {"- Receiving"} + + + + + Genterated at : + + + {"07/07/2023 - 18:01:59"} + + + + + + + ); +}; + +export default Alerts; diff --git a/src/pages/Stats/Status.tsx b/src/pages/Stats/Status.tsx new file mode 100644 index 00000000..d2bd1179 --- /dev/null +++ b/src/pages/Stats/Status.tsx @@ -0,0 +1,51 @@ +import { useGetLogStreamRetention } from '@/hooks/useGetLogStreamRetention'; +import { useGetLogStreamStat } from '@/hooks/useGetLogStreamStat'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; +import { useDocumentTitle } from '@mantine/hooks'; +import { FC, useEffect } from 'react'; +import { useStatusStyles } from './styles'; +import { Box, Text } from '@mantine/core'; +import dayjs from 'dayjs'; + + +const Status: FC = () => { + useDocumentTitle('Parseable | Login'); + const { state: { subLogQuery } } = useHeaderContext(); + const { data: data2, error: error2, loading: loading2, getLogRetention: getLogRetention, resetData: resetData2 } = useGetLogStreamRetention(); + const { data: dataStat, error: errorStat, loading: loadingStat, getLogStat, resetData: resetStat } = useGetLogStreamStat(); + useEffect(() => { + getLogRetention(subLogQuery.get().streamName); + getLogStat(subLogQuery.get().streamName); + console.log(loadingStat) + return () => { + resetData2(); + resetStat(); + } + }, [subLogQuery]); + + const { classes } = useStatusStyles(); + const { container, headContainer, statusText, statusTextResult, genterateContiner, genterateText, genterateTextResult } = classes + return ( + + + + Status: {"- Receiving"} + + + + + Genterated at : + + + {!loadingStat ? (errorStat ? "ERROR" : dataStat ? dayjs(dataStat?.time).format('DD-MM-YY HH:mm') : "") : "Loading..."} + + + + + + + + ); +}; + +export default Status; diff --git a/src/pages/Stats/index.tsx b/src/pages/Stats/index.tsx new file mode 100644 index 00000000..d6be546b --- /dev/null +++ b/src/pages/Stats/index.tsx @@ -0,0 +1,24 @@ +import { Box } from '@mantine/core'; +import { useDocumentTitle } from '@mantine/hooks'; +import { FC } from 'react'; +import Status from './Status'; +import Alerts from './Alerts'; +import { useStatsStyles } from './styles'; + + +const Stats: FC = () => { + useDocumentTitle('Parseable | Stats'); + + + const {classes}=useStatsStyles(); + const {container}=classes + return ( + + {/* */} + + + + ); +}; + +export default Stats; diff --git a/src/pages/Stats/styles.tsx b/src/pages/Stats/styles.tsx new file mode 100644 index 00000000..eb23d4fe --- /dev/null +++ b/src/pages/Stats/styles.tsx @@ -0,0 +1,407 @@ +import { HEADER_HEIGHT, NAVBAR_WIDTH } from '@/constants/theme'; +import { createStyles } from '@mantine/core'; +export const useStatsStyles = createStyles((theme) => { + const { widths } = theme.other; + return { + container: { + flex: 1, + width: `calc(${widths.full} - ${NAVBAR_WIDTH}px)`, + display: 'flex', + position: 'relative', + }, + }; +}); + +export const useStatusStyles = createStyles((theme) => { + const { colors, other, spacing, radius, shadows, primaryColor, fontSizes } = theme; + + const { fontWeights, widths, heights } = other; + + const pColor = colors[primaryColor][2]; + + const defaultRadius = radius[theme.defaultRadius as string]; + + return { + container: { + position: 'relative', + flex: 1, + maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT}px)`, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden' + }, + headContainer: { + display: "flex", + justifyContent: "space-between", + padding:spacing.md, + height:"55px", + alignItems:"center", + borderBottom: `${widths.px} ${colors.gray[1]} solid`, + + }, + + statusText: { + fontSize: fontSizes.md, + fontWeight: fontWeights.semibold, + color: colors.gray[6], + }, + statusTextResult: { + color: "#00CC14" + }, + genterateContiner: { + marginRight:spacing.sm + }, + genterateText: { + fontSize: fontSizes.xs, + fontWeight: fontWeights.semibold, + color: colors.gray[6], + }, + genterateTextResult: { + fontSize: fontSizes.xs, + color: colors.gray[6], + }, + + + + }; +}); +export const useAlertsStyles = createStyles((theme) => { + const { colors, other, spacing, radius, shadows, primaryColor, fontSizes } = theme; + + const { fontWeights, widths, heights } = other; + + const pColor = colors[primaryColor][2]; + + const defaultRadius = radius[theme.defaultRadius as string]; + + return { + container: { + position: 'relative', + flex: 1, + maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT}px)`, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden' + }, + headContainer: { + display: "flex", + justifyContent: "space-between", + padding:spacing.md, + height:"55px", + alignItems:"center", + borderBottom: `${widths.px} ${colors.gray[1]} solid`, + + }, + + statusText: { + fontSize: fontSizes.md, + fontWeight: fontWeights.semibold, + color: colors.gray[6], + }, + statusTextResult: { + color: "#00CC14" + }, + genterateContiner: { + marginRight:spacing.sm + }, + genterateText: { + fontSize: fontSizes.xs, + fontWeight: fontWeights.semibold, + color: colors.gray[6], + }, + genterateTextResult: { + fontSize: fontSizes.xs, + color: colors.gray[6], + }, + + + + }; +}); + +export const useLogTableStyles = createStyles((theme) => { + const { spacing, other, radius, shadows, colors ,fontSizes} = theme; + const { heights, widths, fontWeights ,} = other; + const pColor = colors.brandPrimary[0]; + const sColor = colors.brandSecondary[0]; + + const defaultRadius = radius[theme.defaultRadius as string]; + + return { + container: { + position: 'relative', + flex: 1, + maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT}px)`, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', + // padding: px(spacing.sm), + }, + + innerContainer: { + position: 'relative', + flex: 1, + maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT}px)`, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', + }, + + paginationRow: { + '.mantine-Pagination-control': { + border: `solid 1px ${colors.gray[2]}`, + fontSize: fontSizes.sm, + '&:hover': { + color: sColor, + }, + '&:data-active=true': { + color: pColor, + }, + }, + '.mantine-Pagination-control[data-active=true]': { + background: pColor, + '&:hover': { + backgroundColor: sColor, + color: colors.white[0], + }, + }, + }, + + tableContainer: { + position: 'relative' + }, + + tableStyle: { + whiteSpace: 'nowrap', + overflow: 'scroll', + width: '100%', + padding: 0, + }, + + theadStyle: { + position: 'sticky', + zIndex: 1, + top: 0, + '& th:last-of-type': { + position: 'sticky', + boxShadow: shadows.sm, + right: 0, + }, + }, + + trStyle: { + cursor: 'pointer', + background: colors.white, + '&:hover': { + background: colors.gray[1], + }, + + '& td': { + height: heights[10], + textAlign: 'left', + verticalAlign: 'middle', + border:"none !important", + + }, + }, + trEvenStyle:{ + cursor: 'pointer', + background: colors.gray[0], + + '&:hover': { + background: colors.gray[1], + }, + + '& td': { + height: heights[10], + textAlign: 'left', + verticalAlign: 'middle', + border:"none !important", + + }, + }, + + tdArrowContainer: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + + tdArrow: { + position: 'sticky', + right: 0, + background:"inherit", + boxShadow: shadows.sm, + + 'tr:hover &': { + background: colors.gray[1], + }, + }, + + thColumnMenuBtn: { + width: widths[10], + height: heights[10], + }, + + thColumnMenuDropdown: { + maxHeight: heights[96], + overflowX: 'hidden', + overflowY: 'scroll', + }, + + footerContainer: { + padding: spacing.md, + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + background: colors.gray[0], + borderTop: `${widths.px} ${colors.gray[1]} solid`, + }, + + errorContainer: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + height: heights.full, + }, + + limitContainer: { + + }, + + limitBtn: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + cursor: 'pointer', + fontSize: fontSizes.sm, + background: colors.white[0], + padding: `0.2rem ${spacing.xs}`, + border: `${widths.px} ${colors.gray[1]} solid`, + borderRadius: defaultRadius, + '&:hover': { + background: colors.gray[1], + }, + }, + + limitBtnText: { + + }, + + limitActive: { + background: pColor, + fontWeight: fontWeights.medium, + color: colors.white[0], + + '&:hover': { + background: sColor, + }, + }, + limitOption: { + fontWeight: fontWeights.normal, + + '&:hover': { + color: sColor, + }, + }, + }; +}); + +export const useViewLogStyles = createStyles((theme) => { + const { spacing, other, colors, fontSizes } = theme; + const { fontWeights } = other; + const pColor = colors.brandPrimary[0]; + + return { + container: { + padding: spacing.lg, + }, + + headerContainer: { + background: pColor, + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + }, + + headerTimeStampTitle: { + color: colors.white, + fontSize: fontSizes.sm, + fontWeight: fontWeights.semibold, + }, + + headerTimeStamp: { + color: colors.white, + fontSize: fontSizes.sm, + fontWeight: fontWeights.medium, + }, + + dataChipContainer: { + display: 'flex', + flexWrap: 'wrap', + }, + }; +}); + + +export const useTableColumnStyle = createStyles((theme) => { + const { spacing, colors, fontSizes, other, primaryColor } = theme; + const { fontWeights, widths } = other; + + const pColor = colors[primaryColor]; + + return { + labelBtn: { + width: widths.full, + display: 'flex', + alignItems: 'center', + textAlign: 'left', + height: "100%", + '&:hover': { + background: colors.gray[1], + }, + }, + + labelIcon: { + color: colors.gray[5], + marginLeft:spacing.xs + }, + + labelIconActive: { + color: pColor[0], + }, + + searchInputStyle: { + marginBottom: spacing.xs, + }, + + checkBoxStyle: { + height: 35, + paddingTop: spacing.xs, + paddingBottom: spacing.xxs, + fontWeight: fontWeights.medium, + overflow: 'hidden', + whiteSpace: 'nowrap', + + '& .mantine-Checkbox-label': { + fontSize: fontSizes.sm, + }, + + '&:hover': { + background: colors.gray[1], + }, + }, + + applyBtn: { + marginTop: spacing.xs, + width: widths.full, + background: pColor[0], + + '&:hover': { + background: pColor[1], + }, + }, + }; +}); diff --git a/src/routes/elements.tsx b/src/routes/elements.tsx index 11a6ef9a..709e2bc8 100644 --- a/src/routes/elements.tsx +++ b/src/routes/elements.tsx @@ -53,3 +53,13 @@ export const MainLayoutElement: FC = () => { ); } + +const Stats = lazy(() => import('@/pages/Stats')); + +export const StatsElement: FC = () => { + return ( + + + + ); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 5d3d4fa5..0f197836 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,10 +1,10 @@ -import { ALL_ROUTE, HOME_ROUTE, LOGIN_ROUTE, LOGS_ROUTE, QUERY_ROUTE} from '@/constants/routes'; +import { ALL_ROUTE, HOME_ROUTE, LOGIN_ROUTE, LOGS_ROUTE, QUERY_ROUTE, STATS_ROUTE} from '@/constants/routes'; import FullPageLayout from '@/layouts/FullPageLayout'; import NotFound from '@/pages/Errors/NotFound'; import type { FC } from 'react'; import { Route, Routes } from 'react-router-dom'; import PrivateRoute from './PrivateRoute'; -import { HomeElement, LoginElement, LogsElement, QueryElement ,MainLayoutElement} from './elements'; +import { HomeElement, LoginElement, LogsElement, QueryElement ,MainLayoutElement, StatsElement} from './elements'; const AppRouter: FC = () => { return ( @@ -15,6 +15,7 @@ const AppRouter: FC = () => { } /> } /> } /> + } /> } /> From f1dcd6deed07d7356e5e401ebaaade084aea309e Mon Sep 17 00:00:00 2001 From: Kartik Gupta Date: Fri, 21 Jul 2023 16:58:33 +0530 Subject: [PATCH 2/3] fomat --- .github/workflows/cla.yaml | 6 +- index.html | 26 +-- signatures/version1/cla.json | 54 +++--- src/@types/parseable/api/stream.ts | 22 +-- src/api/constants.ts | 6 +- src/api/logStream.ts | 14 +- src/api/query.ts | 5 +- src/components/ErrorBoundary/index.tsx | 2 - src/components/Header/SubHeader.tsx | 32 +-- src/components/Header/index.tsx | 16 +- src/components/Header/styles.tsx | 27 ++- src/components/Mantine/theme.tsx | 11 +- src/components/Navbar/index.tsx | 121 ++++++++---- src/components/Navbar/styles.tsx | 40 ++-- src/hooks/useGetLogStreamAlert.ts | 2 +- src/hooks/useGetLogStreamRetention.ts | 4 +- src/hooks/useGetLogStreamStat.ts | 2 +- src/hooks/useQueryResult.ts | 85 ++++---- src/pages/Home/index.tsx | 4 +- src/pages/Logs/Column.tsx | 6 +- src/pages/Logs/Context.tsx | 3 +- src/pages/Logs/LogRow.tsx | 4 +- src/pages/Logs/LogTable.tsx | 39 ++-- src/pages/Logs/ViewLog.tsx | 4 +- src/pages/Logs/index.tsx | 2 - src/pages/Logs/styles.tsx | 35 ++-- src/pages/Query/Context.tsx | 9 +- src/pages/Query/ErrorMarker.tsx | 71 ++++--- src/pages/Query/QueryCodeEditor.tsx | 257 ++++++++++++------------- src/pages/Query/QueryResultEditor.tsx | 156 +++++++-------- src/pages/Query/QuerySchemaList.tsx | 71 +++---- src/pages/Query/index.tsx | 60 +++--- src/pages/Query/styles.tsx | 139 +++++++------ src/pages/Stats/Alerts.tsx | 90 +++++---- src/pages/Stats/Status.tsx | 94 +++++---- src/pages/Stats/index.tsx | 15 +- src/pages/Stats/styles.tsx | 75 +++----- src/routes/elements.tsx | 13 +- src/routes/index.tsx | 4 +- tsconfig.json | 48 ++--- tsconfig.node.json | 16 +- 41 files changed, 870 insertions(+), 820 deletions(-) diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml index b9887238..3180404e 100644 --- a/.github/workflows/cla.yaml +++ b/.github/workflows/cla.yaml @@ -1,9 +1,9 @@ -name: "CLA Assistant" +name: 'CLA Assistant' on: issue_comment: types: [created] pull_request_target: - types: [opened,closed,synchronize] + types: [opened, closed, synchronize] # explicitly configure permissions, in case your GITHUB_TOKEN workflow permissions are set to read-only in repository settings permissions: @@ -16,7 +16,7 @@ jobs: CLAAssistant: runs-on: ubuntu-latest steps: - - name: "CLA Assistant" + - name: 'CLA Assistant' if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' uses: contributor-assistant/github-action@v2.3.0 env: diff --git a/index.html b/index.html index 2b07e43a..24e72330 100644 --- a/index.html +++ b/index.html @@ -1,21 +1,21 @@ - - - - + + + + - - Parseable Log Storage + + Parseable Log Storage - - - - -
- - + + + +
+ + diff --git a/signatures/version1/cla.json b/signatures/version1/cla.json index 5991bde6..0352f989 100644 --- a/signatures/version1/cla.json +++ b/signatures/version1/cla.json @@ -1,28 +1,28 @@ { - "signedContributors": [ - { - "name": "Ryiski", - "id": 47484379, - "comment_id": 1564572383, - "created_at": "2023-05-26T15:34:15Z", - "repoId": 523035254, - "pullRequestNo": 62 - }, - { - "name": "kartik-gupta-ij", - "id": 84975264, - "comment_id": 1625309802, - "created_at": "2023-07-07T12:02:14Z", - "repoId": 523035254, - "pullRequestNo": 70 - }, - { - "name": "nitisht", - "id": 5156139, - "comment_id": 1625358492, - "created_at": "2023-07-07T12:41:06Z", - "repoId": 523035254, - "pullRequestNo": 71 - } - ] -} \ No newline at end of file + "signedContributors": [ + { + "name": "Ryiski", + "id": 47484379, + "comment_id": 1564572383, + "created_at": "2023-05-26T15:34:15Z", + "repoId": 523035254, + "pullRequestNo": 62 + }, + { + "name": "kartik-gupta-ij", + "id": 84975264, + "comment_id": 1625309802, + "created_at": "2023-07-07T12:02:14Z", + "repoId": 523035254, + "pullRequestNo": 70 + }, + { + "name": "nitisht", + "id": 5156139, + "comment_id": 1625358492, + "created_at": "2023-07-07T12:41:06Z", + "repoId": 523035254, + "pullRequestNo": 71 + } + ] +} diff --git a/src/@types/parseable/api/stream.ts b/src/@types/parseable/api/stream.ts index e4c1fc66..0bac3820 100644 --- a/src/@types/parseable/api/stream.ts +++ b/src/@types/parseable/api/stream.ts @@ -6,16 +6,16 @@ export type LogStreamSchemaData = { fields: Array; metadata: Record; }; -export type LogStreamStat= { - ingestion:{ - count :number; +export type LogStreamStat = { + ingestion: { + count: number; format: string; size: string; - } - storage:{ - format:string; - size:string; - } - stream:string; - time:string -} + }; + storage: { + format: string; + size: string; + }; + stream: string; + time: string; +}; diff --git a/src/api/constants.ts b/src/api/constants.ts index 76d6519d..64920165 100644 --- a/src/api/constants.ts +++ b/src/api/constants.ts @@ -4,6 +4,6 @@ export const HEALTH_LIVENESS_URL = `${API_V1}/liveness`; export const LOG_STREAM_LIST_URL = `${API_V1}/logstream`; export const LOG_STREAMS_SCHEMA_URL = (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/schema`; export const LOG_QUERY_URL = `${API_V1}/query`; -export const STATS_STREAMS_ALERTS_URL= (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/alert`; -export const STATS_STREAMS_RETRNTION_URL= (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/retention`; -export const STATS_STREAMS_STATS_URL = (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/stats`; \ No newline at end of file +export const STATS_STREAMS_ALERTS_URL = (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/alert`; +export const STATS_STREAMS_RETRNTION_URL = (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/retention`; +export const STATS_STREAMS_STATS_URL = (streamName: string) => `${LOG_STREAM_LIST_URL}/${streamName}/stats`; diff --git a/src/api/logStream.ts b/src/api/logStream.ts index 4b545dc3..adf8a8e7 100644 --- a/src/api/logStream.ts +++ b/src/api/logStream.ts @@ -1,5 +1,11 @@ import { Axios } from './axios'; -import { LOG_STREAMS_SCHEMA_URL, LOG_STREAM_LIST_URL, STATS_STREAMS_ALERTS_URL, STATS_STREAMS_RETRNTION_URL, STATS_STREAMS_STATS_URL } from './constants'; +import { + LOG_STREAMS_SCHEMA_URL, + LOG_STREAM_LIST_URL, + STATS_STREAMS_ALERTS_URL, + STATS_STREAMS_RETRNTION_URL, + STATS_STREAMS_STATS_URL, +} from './constants'; import { LogStreamData, LogStreamSchemaData } from '@/@types/parseable/api/stream'; export const getLogStreamList = () => { @@ -12,12 +18,12 @@ export const getLogStreamSchema = (streamName: string) => { export const getLogStreamAlerts = (streamName: string) => { return Axios().get(STATS_STREAMS_ALERTS_URL(streamName)); -} +}; export const getLogStreamRetention = (streamName: string) => { return Axios().get(STATS_STREAMS_RETRNTION_URL(streamName)); -} +}; export const getLogStreamStats = (streamName: string) => { return Axios().get(STATS_STREAMS_STATS_URL(streamName)); -} \ No newline at end of file +}; diff --git a/src/api/query.ts b/src/api/query.ts index 87e573c8..398e2ecd 100644 --- a/src/api/query.ts +++ b/src/api/query.ts @@ -19,8 +19,7 @@ export const getQueryLogs = (logsQuery: LogsQuery) => { }; export const getQueryResult = (logsQuery: LogsQuery, query = '') => { - const { startTime, endTime} = logsQuery; - + const { startTime, endTime } = logsQuery; return Axios().post( LOG_QUERY_URL, @@ -31,4 +30,4 @@ export const getQueryResult = (logsQuery: LogsQuery, query = '') => { }, {}, ); -}; \ No newline at end of file +}; diff --git a/src/components/ErrorBoundary/index.tsx b/src/components/ErrorBoundary/index.tsx index 754fe66e..bf591e13 100644 --- a/src/components/ErrorBoundary/index.tsx +++ b/src/components/ErrorBoundary/index.tsx @@ -15,8 +15,6 @@ type ErrorHandlerFn = (error: Error, info: { componentStack: string }) => void; const ErrorBoundary: FC = ({ children }) => { const errorHandler: ErrorHandlerFn = (error, info) => { // TODO: Send Errors to parseable maybe ? - - }; return ( diff --git a/src/components/Header/SubHeader.tsx b/src/components/Header/SubHeader.tsx index e29bd09d..2ef4e393 100644 --- a/src/components/Header/SubHeader.tsx +++ b/src/components/Header/SubHeader.tsx @@ -1,7 +1,7 @@ import useMountedState from '@/hooks/useMountedState'; import { Box, Breadcrumbs, Button, Menu, Text, TextInput, UnstyledButton, px } from '@mantine/core'; import { DateTimePicker } from '@mantine/dates'; -import { IconClock,IconRefresh, IconReload , IconRefreshOff, IconSearch } from '@tabler/icons-react'; +import { IconClock, IconRefresh, IconReload, IconRefreshOff, IconSearch } from '@tabler/icons-react'; import dayjs from 'dayjs'; import ms from 'ms'; import type { ChangeEvent, FC, KeyboardEvent } from 'react'; @@ -17,7 +17,6 @@ const SubHeader: FC = () => { } = useHeaderContext(); const [streamName, setStreamName] = useMountedState(subLogQuery.get().streamName); - useEffect(() => { const listener = subLogQuery.subscribe((state) => { setStreamName(state.streamName); @@ -30,20 +29,29 @@ const SubHeader: FC = () => { - - + + - Streams - {streamName} - {useMatch("/:streamName/logs") ? "Logs" : "Query"} + Streams + {streamName} + {useMatch('/:streamName/logs') ? 'Logs' : 'Query'} - {useMatch("/:streamName/logs") && } - {useMatch("/:streamName/logs") && } + {useMatch('/:streamName/logs') && } + {useMatch('/:streamName/logs') && } @@ -58,7 +66,7 @@ const Search: FC = () => { state: { subLogSearch }, } = useHeaderContext(); - const [searchValue, setSearchValue] = useMountedState(""); + const [searchValue, setSearchValue] = useMountedState(''); const { classes } = useLogQueryStyles(); useEffect(() => { @@ -108,7 +116,6 @@ const RefreshNow: FC = () => { state: { subLogQuery, subLogSelectedTimeRange }, } = useHeaderContext(); - const onRefresh = () => { if (subLogSelectedTimeRange.get().includes('Past')) { const now = dayjs(); @@ -123,9 +130,8 @@ const RefreshNow: FC = () => { const { refreshNowBtn } = classes; return ( - ); }; diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 457f66b5..a140d28f 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -7,14 +7,14 @@ import { FC, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { useHeaderStyles } from './styles'; import SubHeader from './SubHeader'; -import {useHeaderContext} from '@/layouts/MainLayout/Context'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; import useMountedState from '@/hooks/useMountedState'; type HeaderProps = Omit; const Header: FC = (props) => { const { classes } = useHeaderStyles(); - const { container, logoContainer, burgerIcon ,navContainer, imageSty} = classes; + const { container, logoContainer, burgerIcon, navContainer, imageSty } = classes; const { state: { subNavbarTogle }, } = useHeaderContext(); @@ -35,17 +35,15 @@ const Header: FC = (props) => { subNavbarTogle.set((state) => !state)} - size={"sm"} + onClick={() => subNavbarTogle.set((state) => !state)} + size={'sm'} /> - - - - + + + ); }; - export default Header; diff --git a/src/components/Header/styles.tsx b/src/components/Header/styles.tsx index 1a0a853b..969a5a41 100644 --- a/src/components/Header/styles.tsx +++ b/src/components/Header/styles.tsx @@ -3,8 +3,7 @@ import { createStyles } from '@mantine/core'; export const useHeaderStyles = createStyles((theme) => { const { colors, spacing, fontSizes } = theme; - const { fontWeights} = theme.other; - + const { fontWeights } = theme.other; return { container: { @@ -15,7 +14,7 @@ export const useHeaderStyles = createStyles((theme) => { fontFamily: theme.fontFamily, fontSize: fontSizes.md, fontWeight: fontWeights.normal, - lineHeight: "normal", + lineHeight: 'normal', height: HEADER_HEIGHT, width: '100%', }, @@ -23,25 +22,23 @@ export const useHeaderStyles = createStyles((theme) => { display: 'flex', alignItems: 'self-end', width: NAVBAR_WIDTH, - justifyContent: "space-between", + justifyContent: 'space-between', // padding: spacing.md, }, - imageSty:{ + imageSty: { marginLeft: spacing.md, }, burgerIcon: { - size:"24px", + size: '24px', color: theme.colors.gray[7], }, navContainer: { width: `calc(100% - ${NAVBAR_WIDTH}px)`, - justifyContent: "space-between", + justifyContent: 'space-between', }, - }; }); - export const useLogQueryStyles = createStyles((theme) => { const { spacing, radius, colors, fontSizes } = theme; const { sizing, widths, fontWeights } = theme.other; @@ -62,12 +59,12 @@ export const useLogQueryStyles = createStyles((theme) => { color: colors.gray[6], }, homeIcon: { - size:"24px", + size: '24px', strokeWidth: 1.0, }, - activeBtn:{ + activeBtn: { color: colors.brandPrimary[0], - textDecoration: "underline", + textDecoration: 'underline', }, intervalBtn: { @@ -86,9 +83,9 @@ export const useLogQueryStyles = createStyles((theme) => { }, refreshNowBtn: { background: colors.white[0], - padding:0, + padding: 0, marginRight: spacing.xs, - width: "36px", + width: '36px', color: theme.colors.gray[6], border: `${sizing.px} ${colors.gray[2]} solid`, '&:hover': { @@ -207,4 +204,4 @@ export const useLogQueryStyles = createStyles((theme) => { }, }, }; -}); \ No newline at end of file +}); diff --git a/src/components/Mantine/theme.tsx b/src/components/Mantine/theme.tsx index fd53aa8c..aeb5b53f 100644 --- a/src/components/Mantine/theme.tsx +++ b/src/components/Mantine/theme.tsx @@ -21,7 +21,7 @@ export const theme: MantineThemeOverride = { white: ['#FFFFFF'], brandPrimary: ['#545BEB', '#1F288E', '#1A237E', '#10143E'], brandSecondary: ['#FC466B', '#F29C38', '#C27D2D'], - gray: ['#F1F1F1', '#E0E0E0', '#D4D4D4', '#828282', '#4F4F4F', '#777777' , '#211F1F'], + gray: ['#F1F1F1', '#E0E0E0', '#D4D4D4', '#828282', '#4F4F4F', '#777777', '#211F1F'], error: ['#8F0F27'], dimmed: ['#868e96'], }, @@ -108,22 +108,20 @@ export const theme: MantineThemeOverride = { }, Table: { styles: ({ defaultRadius: _defaultRadius, colors, fontSizes, other: { fontWeights } }) => { - return { root: { background: colors.white, borderCollapse: 'separate', borderSpacing: 0, - padding:0, - height: 20, + padding: 0, + height: 20, '& tr th': { background: theme.colors?.white, - border:"none !important", + border: 'none !important', overflow: 'hidden', whiteSpace: 'nowrap', textAlign: 'left', padding: 0, - }, '& tr th .label': { @@ -134,7 +132,6 @@ export const theme: MantineThemeOverride = { height: '100%', textAlign: 'left', }, - }, }; }, diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index a320c781..4224f120 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,12 +1,22 @@ import type { NavbarProps as MantineNavbarProps } from '@mantine/core'; import { Navbar as MantineNavbar, NavLink, Select, Anchor, Card, Box, Modal, Text, Image } from '@mantine/core'; -import { IconColumns , IconZoomCode,IconReportAnalytics , IconCheck, IconFileAlert, IconReload, IconHelpCircle, IconLogout, IconUser } from '@tabler/icons-react'; +import { + IconColumns, + IconZoomCode, + IconReportAnalytics, + IconCheck, + IconFileAlert, + IconReload, + IconHelpCircle, + IconLogout, + IconUser, +} from '@tabler/icons-react'; import { FC, useEffect, useState } from 'react'; import docImage from '@/assets/images/doc.webp'; import githubLogo from '@/assets/images/github-logo.webp'; import slackLogo from '@/assets/images/slack-logo.webp'; import { useNavbarStyles } from './styles'; -import { useLocation, useParams } from "react-router-dom"; +import { useLocation, useParams } from 'react-router-dom'; import { useGetLogStreamList } from '@/hooks/useGetLogStreamList'; import { notifications } from '@mantine/notifications'; import { useNavigate } from 'react-router-dom'; @@ -16,11 +26,10 @@ import dayjs from 'dayjs'; import { useDisclosure, useLocalStorage } from '@mantine/hooks'; import { LOGIN_ROUTE } from '@/constants/routes'; - const links = [ - { icon: IconColumns , label: 'Logs', pathname: "/logs" }, - { icon: IconZoomCode , label: 'Query', pathname: "/query" }, - { icon: IconReportAnalytics , label: 'Stats', pathname: "/stats"} + { icon: IconColumns, label: 'Logs', pathname: '/logs' }, + { icon: IconZoomCode, label: 'Query', pathname: '/query' }, + { icon: IconReportAnalytics, label: 'Stats', pathname: '/stats' }, ]; type NavbarProps = Omit; @@ -29,13 +38,22 @@ const Navbar: FC = (props) => { const [username] = useLocalStorage({ key: 'username', getInitialValueInEffect: false }); const navigate = useNavigate(); const { data: streams, loading, error, getData } = useGetLogStreamList(); - const [activeStream, setActiveStream] = useState(""); - const [searchValue, setSearchValue] = useState(""); + const [activeStream, setActiveStream] = useState(''); + const [searchValue, setSearchValue] = useState(''); const { classes } = useNavbarStyles(); - const [currentPage, setCurrentPage] = useState("/logs"); - const { container, linkBtnActive, linkBtn, - selectStreambtn, streamsBtn, lowerContainer, - actionBtn, helpTitle, helpDescription, userBtn } = classes; + const [currentPage, setCurrentPage] = useState('/logs'); + const { + container, + linkBtnActive, + linkBtn, + selectStreambtn, + streamsBtn, + lowerContainer, + actionBtn, + helpTitle, + helpDescription, + userBtn, + } = classes; const { streamName } = useParams(); const nav = useNavigate(); const [, , removeCredentials] = useLocalStorage({ key: 'credentials' }); @@ -45,7 +63,7 @@ const Navbar: FC = (props) => { } = useHeaderContext(); const [isSubNavbarOpen, setIsSubNavbarOpen] = useMountedState(false); const [opened, { close, open }] = useDisclosure(); - let location = useLocation() + let location = useLocation(); useEffect(() => { const listener = subNavbarTogle.subscribe(setIsSubNavbarOpen); @@ -54,7 +72,6 @@ const Navbar: FC = (props) => { }; }, [subNavbarTogle.get()]); - const onSignOut = () => { removeCredentials(); removeUsername(); @@ -66,7 +83,9 @@ const Navbar: FC = (props) => { ); }; - const { state: { subLogQuery, subLogSelectedTimeRange, subLogSearch, subRefreshInterval } } = useHeaderContext(); + const { + state: { subLogQuery, subLogSelectedTimeRange, subLogSearch, subRefreshInterval }, + } = useHeaderContext(); useEffect(() => { if (streamName) { @@ -87,8 +106,7 @@ const Navbar: FC = (props) => { subRefreshInterval.set(null); setCurrentPage(location.pathname); } - } - else if (streams && Boolean(streams.length)) { + } else if (streams && Boolean(streams.length)) { navigate(`/${streams[0].name}/logs`); } }, [streams, location]); @@ -109,8 +127,8 @@ const Navbar: FC = (props) => { message: 'Streams will be loaded.', autoClose: false, withCloseButton: false, - }) - }; + }); + } if (streams && Boolean(streams.length)) { notifications.update({ id: 'load-data', @@ -131,26 +149,34 @@ const Navbar: FC = (props) => { autoClose: 2000, }); } - }, [streams, error, loading]); - return (