diff --git a/package.json b/package.json index 62ddf41c..59b29dbc 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@emotion/react": "^11.11.1", + "@mantine/carousel": "^6.0.15", "@mantine/core": "^6.0.15", "@mantine/dates": "^6.0.15", "@mantine/form": "^6.0.15", @@ -25,6 +26,7 @@ "@types/js-cookie": "^3.0.3", "axios": "^1.4.0", "dayjs": "^1.11.8", + "embla-carousel-react": "7.1.0", "http-status-codes": "^2.2.0", "immer": "^10.0.2", "js-cookie": "^3.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac389350..165152a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ dependencies: '@emotion/react': specifier: ^11.11.1 version: 11.11.1(@types/react@18.2.14)(react@18.2.0) + '@mantine/carousel': + specifier: ^6.0.15 + version: 6.0.15(@mantine/core@6.0.15)(@mantine/hooks@6.0.15)(embla-carousel-react@7.1.0)(react@18.2.0) '@mantine/core': specifier: ^6.0.15 version: 6.0.15(@emotion/react@11.11.1)(@mantine/hooks@6.0.15)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) @@ -41,6 +44,9 @@ dependencies: dayjs: specifier: ^1.11.8 version: 1.11.8 + embla-carousel-react: + specifier: 7.1.0 + version: 7.1.0(react@18.2.0) http-status-codes: specifier: ^2.2.0 version: 2.2.0 @@ -569,6 +575,21 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true + /@mantine/carousel@6.0.15(@mantine/core@6.0.15)(@mantine/hooks@6.0.15)(embla-carousel-react@7.1.0)(react@18.2.0): + resolution: {integrity: sha512-5eYPSi8Q6Ze9YDQ8iaNmBHI542PZlzj4SIxF900B1R0D9NckXbev/1pa16Z9ogKNwxttgYzmMq6GDKvfgGzR3A==} + peerDependencies: + '@mantine/core': 6.0.15 + '@mantine/hooks': 6.0.15 + embla-carousel-react: ^7.0.0 + react: '>=16.8.0' + dependencies: + '@mantine/core': 6.0.15(@emotion/react@11.11.1)(@mantine/hooks@6.0.15)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) + '@mantine/hooks': 6.0.15(react@18.2.0) + '@mantine/utils': 6.0.15(react@18.2.0) + embla-carousel-react: 7.1.0(react@18.2.0) + react: 18.2.0 + dev: false + /@mantine/core@6.0.15(@emotion/react@11.11.1)(@mantine/hooks@6.0.15)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-CN2UV2RXumxac75cWgUPMcHiE36T4ciIpFf20JwpazshnwFNu7scvy6GJDwUouf0zTBLnPMAD1S/B4mIRc3aQw==} peerDependencies: @@ -1531,6 +1552,19 @@ packages: csstype: 3.1.2 dev: false + /embla-carousel-react@7.1.0(react@18.2.0): + resolution: {integrity: sha512-tbYRPRZSDNd2QLNqYDcArAakGIxtUbhS7tkP0dGXktXHGgcX+3ji3VrOUTOftBiujZrMV8kRxtrRUe/1soloIQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.1 || ^18.0.0 + dependencies: + embla-carousel: 7.1.0 + react: 18.2.0 + dev: false + + /embla-carousel@7.1.0: + resolution: {integrity: sha512-Bh8Pa8NWzgugLkf8sAGexQlBCNDFaej5BXiKgQdRJ1mUC9NWBrw9Z23YVPVGkguWoz5LMjZXXFVGCobl3UPt/Q==} + dev: false + /end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: diff --git a/src/components/Header/RefreshInterval.tsx b/src/components/Header/RefreshInterval.tsx index c8661a9f..84f8a34d 100644 --- a/src/components/Header/RefreshInterval.tsx +++ b/src/components/Header/RefreshInterval.tsx @@ -38,7 +38,9 @@ const RefreshInterval: FC = () => { {selectedInterval ? ms(selectedInterval) : 'Off'} - + {REFRESH_INTERVALS.map((interval) => { if (interval === selectedInterval) return null; diff --git a/src/components/Header/TimeRange.tsx b/src/components/Header/TimeRange.tsx index 7ba42cc2..5104ee6a 100644 --- a/src/components/Header/TimeRange.tsx +++ b/src/components/Header/TimeRange.tsx @@ -15,6 +15,7 @@ const TimeRange: FC = () => { state: { subLogQuery, subLogSelectedTimeRange }, } = useHeaderContext(); + const [opened, setOpened] = useMountedState(false); const [selectedRange, setSelectedRange] = useMountedState(subLogSelectedTimeRange.get().value); useEffect(() => { @@ -26,7 +27,7 @@ const TimeRange: FC = () => { }, []); const onDurationSelect = (duration: FixedDurations) => { - subLogSelectedTimeRange.set((state)=>{ + subLogSelectedTimeRange.set((state) => { state.value = duration.name; state.state = 'fixed'; }); @@ -36,6 +37,7 @@ const TimeRange: FC = () => { query.startTime = now.subtract(duration.milliseconds, 'milliseconds').toDate(); query.endTime = now.toDate(); }); + setOpened(false); }; const { classes, cx } = useLogQueryStyles(); @@ -49,7 +51,7 @@ const TimeRange: FC = () => { } = classes; return ( - + + + + + ); +}; + +export default FillCarousel; diff --git a/src/pages/Logs/Column.tsx b/src/pages/Logs/Column.tsx index a66a18fd..168b1ad0 100644 --- a/src/pages/Logs/Column.tsx +++ b/src/pages/Logs/Column.tsx @@ -110,11 +110,15 @@ const Column: FC = (props) => { const { labelBtn, applyBtn, labelIcon, labelIconActive, searchInputStyle, filterText } = classes; return ( - + - {capitalizeFirstLetter(columnName)} + {capitalizeFirstLetter(columnName)} ; subViewLog: SubData; + subGapTime: SubData; } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -29,10 +36,12 @@ interface LogsPageProviderProps { const LogsPageProvider: FC = ({ children }) => { const subLogStreamError = useSubscribeState(null); const subViewLog = useSubscribeState(null); + const subGapTime = useSubscribeState(null); const state: LogsPageContextState = { subLogStreamError, subViewLog, + subGapTime, }; const methods: LogsPageContextMethods = {}; diff --git a/src/pages/Logs/CustomPagination.tsx b/src/pages/Logs/CustomPagination.tsx deleted file mode 100644 index 52c2e0c2..00000000 --- a/src/pages/Logs/CustomPagination.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { useGetQueryCount } from '@/hooks/useGetQueryCount'; -import useMountedState from '@/hooks/useMountedState'; -import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import { Group, Pagination, Tooltip } from '@mantine/core'; - -import dayjs from 'dayjs'; -import { FC, useEffect } from 'react'; - -type PaginationProps = { - currentStartTime: Date | null; - setCurrentStartTime: (date: Date | null) => void; - pageLogData: any; - goToPage: (page: number, limit: number) => void; - setCurrentCount: (currentCount:number)=>void; -}; -const CustomPagination: FC = (props) => { - const { currentStartTime, pageLogData, goToPage, setCurrentStartTime,setCurrentCount } = props; - const [nextStartTimeTemp, setNextStartTimeTemp] = useMountedState(null); - const [nextStartTime, setNextStartTime] = useMountedState(null); - const [prevStartTimeTemp, setPrevStartTimeTemp] = useMountedState(null); - const [prevStartTime, setPrevStartTime] = useMountedState(null); - - const { - state: { subLogQuery }, - } = useHeaderContext(); - const { data: queryCountResForNext, getQueryCountData: getQueryCountDataForNext } = useGetQueryCount(); - const { data: queryCountResForPrev, getQueryCountData: getQueryCountDataForPrev } = useGetQueryCount(); - - useEffect(() => { - if (currentStartTime) { - setPrevStartTimeTemp(dayjs(currentStartTime).add(1, 'minute').toDate()); - setNextStartTimeTemp(dayjs(currentStartTime).subtract(1, 'minute').toDate()); - } - }, [currentStartTime]); - - useEffect(() => { - if (nextStartTimeTemp) { - getQueryCountDataForNext({ - startTime: nextStartTimeTemp, - endTime: dayjs(nextStartTimeTemp).add(1, 'minute').toDate(), - streamName: subLogQuery.get().streamName, - access: subLogQuery.get().access, - }); - } - }, [nextStartTimeTemp]); - - useEffect(() => { - if ( - queryCountResForNext && - queryCountResForNext[0].totalcurrentcount === 0 && - nextStartTimeTemp && - nextStartTimeTemp >= subLogQuery.get().startTime - ) { - setNextStartTimeTemp(new Date(dayjs(nextStartTimeTemp).subtract(1, 'minute').toDate())); - } else if (queryCountResForNext && queryCountResForNext[0].totalcurrentcount !== 0 &&nextStartTimeTemp && - nextStartTimeTemp >= subLogQuery.get().startTime) { - setNextStartTime(nextStartTimeTemp); - } - }, [queryCountResForNext]); - - useEffect(() => { - if (prevStartTimeTemp) { - getQueryCountDataForPrev({ - startTime: prevStartTimeTemp, - endTime: dayjs(prevStartTimeTemp).add(1, 'minute').toDate(), - streamName: subLogQuery.get().streamName, - access: subLogQuery.get().access, - }); - } - }, [prevStartTimeTemp]); - - useEffect(() => { - if ( - queryCountResForPrev && - queryCountResForPrev[0].totalcurrentcount === 0 && - prevStartTimeTemp && - prevStartTimeTemp <= subLogQuery.get().endTime - ) { - setPrevStartTimeTemp(new Date(dayjs(prevStartTimeTemp).add(1, 'minute').toDate())); - } else if ( - queryCountResForPrev && - queryCountResForPrev[0].totalcurrentcount !== 0 && - prevStartTimeTemp && - prevStartTimeTemp <= subLogQuery.get().endTime - ) { - setPrevStartTime(prevStartTimeTemp); - } - }, [queryCountResForPrev]); - - return ( - <> - - { - goToPage(page, pageLogData?.limit || 1); - }}> - - - { - if (prevStartTime) { - setCurrentStartTime(prevStartTime); - setCurrentCount(queryCountResForPrev[0].totalcurrentcount) - } - }} - /> - - - - - - { - if (nextStartTime) { - setCurrentStartTime(nextStartTime); - setCurrentCount(queryCountResForNext[0].totalcurrentcount) - } - }} - disabled={Boolean(!nextStartTime)} - /> - - - - - ); -}; - -export default CustomPagination; diff --git a/src/pages/Logs/HeaderPagination.tsx b/src/pages/Logs/HeaderPagination.tsx new file mode 100644 index 00000000..5d57697e --- /dev/null +++ b/src/pages/Logs/HeaderPagination.tsx @@ -0,0 +1,236 @@ +import { useGetQueryCount } from '@/hooks/useGetQueryCount'; +import useMountedState from '@/hooks/useMountedState'; +import { useHeaderContext } from '@/layouts/MainLayout/Context'; +import { Carousel } from '@mantine/carousel'; +import { Box, Button, Text } from '@mantine/core'; +import dayjs from 'dayjs'; + +import { FC, useEffect, useRef } from 'react'; +import FillCarousel from './CarouselSlide'; +import { useLogsPageContext } from './Context'; +import Loading from '@/components/Loading'; + +const Limit = 10000; +const gapOptions = [1, 5, 10, 15, 20, 30, 60]; + +const HeaderPagination: FC = () => { + const { + state: { subLogQuery }, + } = useHeaderContext(); + const [headDate, setHeadDate] = useMountedState(null); + + const [endDatePointer, setEndDatePointer] = useMountedState(null); + const [gapTemp, setGapTemp] = useMountedState(0); + const [gapMinute, setGapMinute] = useMountedState(0); + const [upperLimit, setUpperLimit] = useMountedState(20); + const slots = useRef< + | { + gapMinute: number; + endtime: Date; + id: number; + }[] + | null + >(null); + + const { + data: queryCountRes, + error: queryCountError, + loading: queryCountLoading, + getQueryCountData, + resetData: resetQueryCountData, + } = useGetQueryCount(); + const { + state: { subGapTime }, + } = useLogsPageContext(); + + const getMinuteCount = (minute: number) => { + if (endDatePointer) { + const startTime = dayjs(endDatePointer).subtract(minute, 'minute').toDate(); + if (subLogQuery.get().streamName) { + getQueryCountData({ + startTime: startTime, + endTime: endDatePointer, + streamName: subLogQuery.get().streamName, + access: subLogQuery.get().access, + }); + } + } + }; + + useEffect(() => { + const logQueryListener = subLogQuery.subscribe((query) => { + if (query.endTime) { + let tempDate = new Date(query.endTime); + tempDate.setSeconds(0, 0); + resetQueryCountData(); + subGapTime.set(null); + setGapTemp(0); + setGapMinute(0); + slots.current = null; + setUpperLimit(20); + setEndDatePointer(tempDate); + } + }); + if (subLogQuery.get().endTime) { + let tempDate = new Date(subLogQuery.get().endTime); + tempDate.setSeconds(0, 0); + setEndDatePointer(tempDate); + getMinuteCount(gapOptions[gapTemp]); + } + + return () => { + logQueryListener(); + }; + }, []); + + useEffect(() => { + if (endDatePointer) { + getMinuteCount(gapOptions[gapTemp]); + } + }, [endDatePointer]); + + useEffect(() => { + if (queryCountRes) { + if ( + queryCountRes[0].totalcurrentcount < Limit && + gapTemp !== gapOptions.length - 1 && + gapOptions[gapTemp + 1] < dayjs(subLogQuery.get().endTime).diff(dayjs(subLogQuery.get().startTime), 'minute') + ) { + setGapTemp(gapTemp + 1); + getMinuteCount(gapOptions[gapTemp + 1]); + } else if ( + queryCountRes[0].totalcurrentcount < Limit && + gapTemp <= gapOptions.length - 1 && + gapOptions[gapTemp + 1] >= dayjs(subLogQuery.get().endTime).diff(dayjs(subLogQuery.get().startTime), 'minute') + ) { + setGapMinute(gapOptions[gapTemp]); + } else if (queryCountRes[0].totalcurrentcount >= Limit || gapTemp === gapOptions.length - 1) { + setGapMinute(gapOptions[gapTemp]); + } + } + }, [queryCountRes]); + + useEffect(() => { + if (gapMinute !== 0) { + const tempSlots = []; + for ( + let i = dayjs(subLogQuery.get().endTime).toDate(), j = 1; + i > subLogQuery.get().startTime && j <= upperLimit; + i = dayjs(i).subtract(gapMinute, 'minute').toDate(), j++ + ) { + tempSlots.push({ + gapMinute: gapMinute, + endtime: i, + id: j, + }); + } + slots.current = tempSlots; + } + }, [gapMinute]); + + const loadMore = () => { + if ( + subLogQuery.get().endTime && + slots.current && + slots.current.length > 0 && + slots.current[slots.current.length - 1].endtime > subLogQuery.get().startTime + ) { + setUpperLimit(upperLimit + 20); + for ( + let i = dayjs(slots.current[slots.current.length - 1].endtime).toDate(), + j = slots.current[slots.current.length - 1].id + 1; + dayjs(i).subtract(gapMinute, 'minute').toDate() > subLogQuery.get().startTime && j <= upperLimit + 20; + i = dayjs(i).subtract(gapMinute, 'minute').toDate(), j++ + ) { + slots.current.push({ + gapMinute: gapMinute, + endtime: i, + id: j, + }); + } + } + }; + + const onSlideChange = (index: number) => { + if (slots.current && slots.current.length - 1 >= index * 9) { + setHeadDate(dayjs(slots.current[index * 9].endtime).toDate()); + } + }; + + return ( + + {gapMinute === 0 || queryCountLoading ? ( + + ) : ( + <> + + {' '} + Loaded events for {dayjs(headDate).format('DD-MM-YYYY')}. Showing {gapMinute} minute intervals. + + + + {slots.current?.map((slot) => ( + + ))} + + + + + + + )} + {queryCountError &&
{queryCountError}
} +
+ ); +}; + +export default HeaderPagination; diff --git a/src/pages/Logs/LogTable.tsx b/src/pages/Logs/LogTable.tsx index f5ba75a7..2c3ffc1c 100644 --- a/src/pages/Logs/LogTable.tsx +++ b/src/pages/Logs/LogTable.tsx @@ -1,8 +1,21 @@ -import Loading from '@/components/Loading'; import { Tbody, Thead } from '@/components/Table'; import { useGetLogStreamSchema } from '@/hooks/useGetLogStreamSchema'; import { useQueryLogs } from '@/hooks/useQueryLogs'; -import { Box, Center, Checkbox, Menu, ScrollArea, Table, px, ActionIcon, Text, Flex, Button } from '@mantine/core'; +import { + Box, + Center, + Checkbox, + Menu, + ScrollArea, + Table, + px, + ActionIcon, + Text, + Flex, + Button, + Pagination, + Loader, +} from '@mantine/core'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import type { FC } from 'react'; import { LOG_QUERY_LIMITS, useLogsPageContext } from './Context'; @@ -20,14 +33,12 @@ import FilterPills from './FilterPills'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; import dayjs from 'dayjs'; import { SortOrder } from '@/@types/parseable/api/query'; -import { useGetQueryCount } from '@/hooks/useGetQueryCount'; -import CustomPagination from './CustomPagination'; const skipFields = ['p_metadata', 'p_tags']; const LogTable: FC = () => { const { - state: { subLogStreamError }, + state: { subLogStreamError, subGapTime }, } = useLogsPageContext(); const { state: { subLogSearch, subLogQuery, subRefreshInterval, subLogSelectedTimeRange }, @@ -37,17 +48,6 @@ const LogTable: FC = () => { const [logStreamError, setLogStreamError] = useMountedState(null); const [columnToggles, setColumnToggles] = useMountedState>(new Map()); const [pinnedColumns, setPinnedColumns] = useMountedState>(new Set()); - const [currentStartTimeTemp, setCurrentStartTimeTemp] = useMountedState(null); - const [currentStartTime, setCurrentStartTime] = useMountedState(null); - const [currentCount, setCurrentCount] = useMountedState(0); - - const { - data: queryCountRes, - error: queryCountError, - loading: queryCountLoading, - getQueryCountData, - resetData: resetQueryCountData, - } = useGetQueryCount(); const { data: logsSchema, @@ -143,15 +143,21 @@ const LogTable: FC = () => { const onRetry = () => { const query = subLogQuery.get(); + const data = subGapTime.get(); if (logsSchema) { resetStreamData(); resetLogsData(); } - let tempDate = subLogQuery.get().endTime; - tempDate.setSeconds(0, 0); - setCurrentCount(0); - setCurrentStartTimeTemp(tempDate); + if (data) { + getQueryData({ + streamName: subLogQuery.get().streamName, + startTime: data.startTime, + endTime: data.endTime, + access: subLogQuery.get().access, + }); + } + getDataSchema(query.streamName); setColumnToggles(new Map()); }; @@ -160,17 +166,15 @@ const LogTable: FC = () => { const streamErrorListener = subLogStreamError.subscribe(setLogStreamError); const logSearchListener = subLogSearch.subscribe(setQuerySearch); const refreshIntervalListener = subRefreshInterval.subscribe(setRefreshInterval); - const logQueryListener = subLogQuery.subscribe((query) => { - if (query.streamName) { - if (logsSchema) { - resetStreamData(); - resetLogsData(); - } - let tempDate = subLogQuery.get().endTime; - tempDate.setSeconds(0, 0); - setCurrentStartTimeTemp(tempDate); - getDataSchema(query.streamName); - setCurrentCount(0); + const subID = subGapTime.subscribe((data) => { + if (data) { + getQueryData({ + streamName: subLogQuery.get().streamName, + startTime: data.startTime, + endTime: data.endTime, + access: subLogQuery.get().access, + }); + getDataSchema(subLogQuery.get().streamName); setColumnToggles(new Map()); setPinnedColumns(new Set()); } @@ -178,56 +182,12 @@ const LogTable: FC = () => { return () => { streamErrorListener(); - resetQueryCountData(); + subID(); refreshIntervalListener(); - logQueryListener(); logSearchListener(); }; }, [logsSchema]); - useEffect(() => { - if (currentStartTimeTemp) { - getQueryCountData({ - streamName: subLogQuery.get().streamName, - startTime: currentStartTimeTemp, - endTime: dayjs(currentStartTimeTemp).add(1, 'minute').toDate(), - access: subLogQuery.get().access, - }); - } - }, [currentStartTimeTemp]); - - useEffect(() => { - if ( - queryCountRes && - queryCountRes[0].totalcurrentcount === 0 && - currentStartTimeTemp && - currentStartTimeTemp <= subLogQuery.get().startTime - ) { - getQueryData({ - streamName: subLogQuery.get().streamName, - startTime: currentStartTimeTemp, - endTime: dayjs(currentStartTimeTemp).add(1, 'minute').toDate(), - access: subLogQuery.get().access, - }); - } else if (queryCountRes && queryCountRes[0].totalcurrentcount === 0) { - setCurrentStartTimeTemp(dayjs(currentStartTimeTemp).subtract(1, 'minute').toDate()); - } else if (queryCountRes && queryCountRes[0].totalcurrentcount !== 0 && currentStartTimeTemp) { - setCurrentStartTime(currentStartTimeTemp); - setCurrentCount(queryCountRes[0].totalcurrentcount); - } - }, [queryCountRes]); - - useEffect(() => { - if (currentStartTime) { - getQueryData({ - streamName: subLogQuery.get().streamName, - startTime: currentStartTime, - endTime: dayjs(currentStartTime).add(1, 'minute').toDate(), - access: subLogQuery.get().access, - }); - } - }, [currentStartTime]); - useEffect(() => { if (subRefreshInterval.get()) { const interval = setInterval(() => { @@ -244,22 +204,6 @@ const LogTable: FC = () => { } }, [refreshInterval]); - useEffect(() => { - const query = subLogQuery.get(); - - if (query.streamName) { - if (logsSchema) { - resetStreamData(); - resetLogsData; - } - let tempDate = subLogQuery.get().endTime; - tempDate.setSeconds(0, 0); - setCurrentStartTimeTemp(tempDate); - getDataSchema(query.streamName); - setColumnToggles(new Map()); - } - }, [subLogQuery]); - const renderTh = useMemo(() => { if (logsSchema) { return logsSchema.fields @@ -349,113 +293,86 @@ const LogTable: FC = () => { - - {' '} - {currentCount} events loaded from{' '} - {subLogSelectedTimeRange.get().state === 'fixed' - ? subLogSelectedTimeRange.get().value - : 'selected time range'} - - + borderBottom: '1px solid #D4D4D4', + }}> - {!(logStreamError || logStreamSchemaError || logsError || queryCountError) ? ( - !loading && !logsLoading && Boolean(logsSchema) && Boolean(pageLogData) && !queryCountLoading ? ( - Boolean(logsSchema?.fields.length) && Boolean(pageLogData?.data.length) ? ( - - - ({ - scrollbar: { - '&[data-orientation="vertical"] .mantine-ScrollArea-thumb': { - display: 'none', - }, + {!(logStreamError || logStreamSchemaError || logsError) ? ( + Boolean(logsSchema?.fields.length) && Boolean(pageLogData?.data.length) ? ( + + + ({ + scrollbar: { + '&[data-orientation="vertical"] .mantine-ScrollArea-thumb': { + display: 'none', }, - })} - onMouseEnter={() => { - active.current = 'left'; - }} - viewportRef={leftRef} - onScrollPositionChange={({ y }) => { - if (active.current === 'right') return; - rightRef.current!.scrollTop = y; - }}> - - - {renderPinnedTh} - - isColumnPinned(field.name)) || []} - isColumnActive={isColumnActive} - /> - -
-
-
- {pinnedColumnsWidth > 0 && } - { - active.current = 'right'; - }} - viewportRef={rightRef} - onScrollPositionChange={({ y }) => { - if (active.current === 'left') return; - leftRef.current!.scrollTop = y; - }}> - - - - {renderTh} - - - - !isColumnPinned(field.name)) || []} - isColumnActive={isColumnActive} - rowArrows={true} - /> - -
-
-
-
- - - - - + }, + })} + onMouseEnter={() => { + active.current = 'left'; + }} + viewportRef={leftRef} + onScrollPositionChange={({ y }) => { + if (active.current === 'right') return; + rightRef.current!.scrollTop = y; + }}> + + + {renderPinnedTh} + + isColumnPinned(field.name)) || []} + isColumnActive={isColumnActive} + /> + +
+
+
+ {pinnedColumnsWidth > 0 && } + { + active.current = 'right'; + }} + viewportRef={rightRef} + onScrollPositionChange={({ y }) => { + if (active.current === 'left') return; + leftRef.current!.scrollTop = y; + }}> + + + + {renderTh} + + + + !isColumnPinned(field.name)) || []} + isColumnActive={isColumnActive} + rowArrows={true} + /> + +
+
+
- ) : ( - - ) +
+ ) : pageLogData?.data.length === 0 ? ( + ) : ( - + ) ) : (
@@ -463,6 +380,20 @@ const LogTable: FC = () => { {(logsError || logStreamSchemaError) && }
)} + + + {!loading && !logsLoading ? ( + { + goToPage(page, pageLogData?.limit || 1); + }}> + ) : ( + + )} + +
); }; diff --git a/src/pages/Logs/index.tsx b/src/pages/Logs/index.tsx index cddcdee8..dec12bbb 100644 --- a/src/pages/Logs/index.tsx +++ b/src/pages/Logs/index.tsx @@ -4,6 +4,7 @@ import { FC } from 'react'; import LogTable from './LogTable'; import { useLogsStyles } from './styles'; import ViewLog from './ViewLog'; +import HeaderPagination from './HeaderPagination'; const Logs: FC = () => { useDocumentTitle('Parseable | Logs'); @@ -13,6 +14,7 @@ const Logs: FC = () => { return ( + diff --git a/src/pages/Logs/styles.tsx b/src/pages/Logs/styles.tsx index 88ccf7cc..ee0a3a37 100644 --- a/src/pages/Logs/styles.tsx +++ b/src/pages/Logs/styles.tsx @@ -7,6 +7,7 @@ export const useLogsStyles = createStyles(() => { flex: 1, display: 'flex', position: 'relative', + flexDirection: 'column', }, }; }); @@ -23,7 +24,7 @@ export const useLogTableStyles = createStyles((theme) => { container: { position: 'relative', flex: 1, - maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT*2}px)`, + maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT * 2 + 95}px )`, display: 'flex', flexDirection: 'column', overflow: 'hidden', @@ -32,7 +33,7 @@ export const useLogTableStyles = createStyles((theme) => { innerContainer: { position: 'relative', flex: 1, - maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT*2}px)`, + maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT * 2 + 95}px -200px)`, display: 'flex', flexDirection: 'column', overflow: 'hidden', @@ -121,8 +122,8 @@ export const useLogTableStyles = createStyles((theme) => { }, thColumnMenuBtn: { - width: widths[10], - height: heights[10], + width: widths[6], + height: heights[6], }, thColumnMenuResetBtn: { @@ -254,15 +255,16 @@ export const useTableColumnStyle = createStyles((theme) => { return { labelBtn: { - padding: '15px', width: widths.full, display: 'flex', alignItems: 'center', textAlign: 'left', - height: '100%', + padding: `${spacing.xs} ${spacing.sm}`, + fontWeight: fontWeights.medium, '&:hover': { background: colors.gray[1], }, + height: '100%', }, labelIcon: { diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..b33da4fc --- /dev/null +++ b/vercel.json @@ -0,0 +1,16 @@ +{ + "rewrites": [ + { + "source": "/api", + "destination": "https://demo.parseable.io/api/" + }, + { + "source": "/api/:path*", + "destination": "https://demo.parseable.io/api/:path*" + }, + { + "source": "/(.*)", + "destination": "/" + } + ] +}