Skip to content

Commit 5e55f8e

Browse files
feat: add pagination based on limit and offset (#162)
Also temporarily avoid time slot based approach for log drill down.
1 parent 377328a commit 5e55f8e

File tree

5 files changed

+170
-46
lines changed

5 files changed

+170
-46
lines changed

src/api/query.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ import { Axios } from './axios';
22
import { LOG_QUERY_URL } from './constants';
33
import { LogsQuery } from '@/@types/parseable/api/query';
44

5-
export const getQueryLogs = (logsQuery: LogsQuery) => {
6-
const { startTime, endTime, streamName } = logsQuery;
5+
type QueryLogs = {
6+
streamName: string;
7+
startTime: Date;
8+
endTime: Date;
9+
limit: number;
10+
pageOffset: number;
11+
};
712

8-
const query = `SELECT * FROM ${streamName}`;
13+
export const getQueryLogs = (logsQuery: QueryLogs) => {
14+
const { startTime, endTime, streamName, limit, pageOffset } = logsQuery;
15+
16+
const query = `SELECT * FROM ${streamName} LIMIT ${limit} OFFSET ${pageOffset}`;
917

1018
return Axios().post(
1119
LOG_QUERY_URL,
@@ -46,4 +54,4 @@ export const getQueryCount = (logsQuery: LogsQuery) => {
4654
},
4755
{},
4856
);
49-
};
57+
};

src/hooks/useQueryLogs.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1-
import { SortOrder, type Log, type LogsData, type LogsQuery, type LogsSearch } from '@/@types/parseable/api/query';
1+
import { SortOrder, type Log, type LogsData, type LogsSearch } from '@/@types/parseable/api/query';
22
import { getQueryLogs } from '@/api/query';
33
import { StatusCodes } from 'http-status-codes';
44
import useMountedState from './useMountedState';
55
import { useCallback, useEffect, useMemo, useRef, useTransition } from 'react';
66
import { LOG_QUERY_LIMITS } from '@/pages/Logs/Context';
77
import { parseLogData } from '@/utils';
88

9+
type QueryLogs = {
10+
streamName: string;
11+
startTime: Date;
12+
endTime: Date;
13+
limit: number;
14+
pageOffset: number;
15+
};
16+
917
export const useQueryLogs = () => {
1018
// data ref will always have the unfiltered data.
1119
// Only mutate it when data is fetched, otherwise read only
@@ -125,7 +133,7 @@ export const useQueryLogs = () => {
125133
}
126134
}, [data]);
127135

128-
const getQueryData = async (logsQuery: LogsQuery) => {
136+
const getQueryData = async (logsQuery: QueryLogs) => {
129137
try {
130138
setLoading(true);
131139
setError(null);

src/pages/Logs/LogTable.tsx

+144-36
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
Button,
1616
Pagination,
1717
Loader,
18+
Group,
1819
} from '@mantine/core';
1920
import { useCallback, useEffect, useMemo, useRef } from 'react';
2021
import type { FC } from 'react';
@@ -33,12 +34,14 @@ import FilterPills from './FilterPills';
3334
import { useHeaderContext } from '@/layouts/MainLayout/Context';
3435
import dayjs from 'dayjs';
3536
import { SortOrder } from '@/@types/parseable/api/query';
37+
import { usePagination } from '@mantine/hooks';
3638

3739
const skipFields = ['p_metadata', 'p_tags'];
40+
const loadLimit = 9000;
3841

3942
const LogTable: FC = () => {
4043
const {
41-
state: { subLogStreamError, subGapTime },
44+
state: { subLogStreamError },
4245
} = useLogsPageContext();
4346
const {
4447
state: { subLogSearch, subLogQuery, subRefreshInterval, subLogSelectedTimeRange },
@@ -48,6 +51,7 @@ const LogTable: FC = () => {
4851
const [logStreamError, setLogStreamError] = useMountedState<string | null>(null);
4952
const [columnToggles, setColumnToggles] = useMountedState<Map<string, boolean>>(new Map());
5053
const [pinnedColumns, setPinnedColumns] = useMountedState<Set<string>>(new Set());
54+
const [pageOffset, setPageOffset] = useMountedState(0);
5155

5256
const {
5357
data: logsSchema,
@@ -140,61 +144,122 @@ const LogTable: FC = () => {
140144
setPinnedColumns(new Set());
141145
setColumnToggles(new Map());
142146
};
143-
144147
const onRetry = () => {
145148
const query = subLogQuery.get();
146-
const data = subGapTime.get();
149+
resetStreamData();
150+
resetLogsData();
151+
resetColumns();
152+
setPageOffset(0);
147153

148-
if (logsSchema) {
149-
resetStreamData();
150-
resetLogsData();
151-
}
152-
if (data) {
154+
if (query) {
153155
getQueryData({
154-
streamName: subLogQuery.get().streamName,
155-
startTime: data.startTime,
156-
endTime: data.endTime,
157-
access: subLogQuery.get().access,
156+
streamName: query.streamName,
157+
startTime: query.startTime,
158+
endTime: query.endTime,
159+
limit: loadLimit,
160+
pageOffset: 0,
158161
});
162+
getDataSchema(query.streamName);
159163
}
160-
161-
getDataSchema(query.streamName);
162-
setColumnToggles(new Map());
163164
};
165+
// const onRetry = () => {
166+
// const query = subLogQuery.get();
167+
// const data = subGapTime.get();
168+
169+
// if (logsSchema) {
170+
// resetStreamData();
171+
// resetLogsData();
172+
// setPageOffset(0);
173+
// }
174+
// if (data) {
175+
// getQueryData({
176+
// streamName: subLogQuery.get().streamName,
177+
// startTime: data.startTime,
178+
// endTime: data.endTime,
179+
// access: subLogQuery.get().access,
180+
// });
181+
// }
182+
183+
// getDataSchema(query.streamName);
184+
// setColumnToggles(new Map());
185+
// };
164186

165187
useEffect(() => {
166-
const streamErrorListener = subLogStreamError.subscribe(setLogStreamError);
167-
const logSearchListener = subLogSearch.subscribe(setQuerySearch);
168-
const refreshIntervalListener = subRefreshInterval.subscribe(setRefreshInterval);
169-
const subID = subGapTime.subscribe((data) => {
170-
if (data) {
188+
if (subLogQuery.get()) {
189+
const query = subLogQuery.get();
190+
resetColumns();
191+
setPageOffset(0);
192+
resetStreamData();
193+
resetLogsData();
194+
if (query) {
171195
getQueryData({
172-
streamName: subLogQuery.get().streamName,
173-
startTime: data.startTime,
174-
endTime: data.endTime,
175-
access: subLogQuery.get().access,
196+
streamName: query.streamName,
197+
startTime: query.startTime,
198+
endTime: query.endTime,
199+
limit: loadLimit,
200+
pageOffset: 0,
176201
});
177-
getDataSchema(subLogQuery.get().streamName);
178-
setColumnToggles(new Map());
179-
setPinnedColumns(new Set());
202+
getDataSchema(query.streamName);
180203
}
181-
});
204+
}
205+
}, []);
182206

183-
const subLogQueryListener = subLogQuery.subscribe(() => {
207+
useEffect(() => {
208+
const streamErrorListener = subLogStreamError.subscribe(setLogStreamError);
209+
const logSearchListener = subLogSearch.subscribe(setQuerySearch);
210+
const refreshIntervalListener = subRefreshInterval.subscribe(setRefreshInterval);
211+
// const subID = subGapTime.subscribe((data) => {
212+
// if (data) {
213+
// getQueryData({
214+
// streamName: subLogQuery.get().streamName,
215+
// startTime: data.startTime,
216+
// endTime: data.endTime,
217+
// access: subLogQuery.get().access,
218+
// });
219+
// getDataSchema(subLogQuery.get().streamName);
220+
// setColumnToggles(new Map());
221+
// setPinnedColumns(new Set());
222+
// }
223+
// });
224+
225+
const subLogQueryListener = subLogQuery.subscribe((state) => {
226+
setPageOffset(0);
184227
resetLogsData();
185228
resetStreamData();
186229
resetColumns();
230+
231+
if (state) {
232+
getQueryData({
233+
streamName: state.streamName,
234+
startTime: state.startTime,
235+
endTime: state.endTime,
236+
limit: loadLimit,
237+
pageOffset: 0,
238+
});
239+
getDataSchema(state.streamName);
240+
}
187241
});
188242

189243
return () => {
190244
streamErrorListener();
191245
subLogQueryListener();
192-
subID();
246+
// subID();
193247
refreshIntervalListener();
194248
logSearchListener();
195249
};
196250
}, [logsSchema]);
197251

252+
useEffect(() => {
253+
const state = subLogQuery.get();
254+
getQueryData({
255+
streamName: state.streamName,
256+
startTime: state.startTime,
257+
endTime: state.endTime,
258+
limit: loadLimit,
259+
pageOffset: pageOffset,
260+
});
261+
}, [pageOffset]);
262+
198263
useEffect(() => {
199264
if (subRefreshInterval.get()) {
200265
const interval = setInterval(() => {
@@ -277,6 +342,7 @@ const LogTable: FC = () => {
277342
const rightRef = useRef<HTMLDivElement>(null);
278343
const pinnedContianerRef = useRef<HTMLDivElement>(null);
279344
const [pinnedColumnsWidth, setPinnedColumnsWidth] = useMountedState(0);
345+
const pagination = usePagination({ total: pageLogData?.totalPages ?? 1, initialPage: 1 });
280346

281347
useEffect(() => {
282348
if (
@@ -298,10 +364,6 @@ const LogTable: FC = () => {
298364

299365
return (
300366
<Box className={container}>
301-
<Box
302-
sx={{
303-
borderBottom: '1px solid #D4D4D4',
304-
}}></Box>
305367
<FilterPills />
306368
{!(logStreamError || logStreamSchemaError || logsError) ? (
307369
Boolean(logsSchema?.fields.length) && Boolean(pageLogData?.data.length) ? (
@@ -390,12 +452,58 @@ const LogTable: FC = () => {
390452
<Box className={footerContainer}>
391453
<Box></Box>
392454
{!loading && !logsLoading ? (
393-
<Pagination
455+
// <Pagination
456+
// total={pageLogData?.totalPages || 1}
457+
// value={pageLogData?.page || 1}
458+
// onChange={(page) => {
459+
// goToPage(page, pageLogData?.limit || 1);
460+
// }}></Pagination>
461+
462+
<Pagination.Root
394463
total={pageLogData?.totalPages || 1}
395464
value={pageLogData?.page || 1}
396465
onChange={(page) => {
397466
goToPage(page, pageLogData?.limit || 1);
398-
}}></Pagination>
467+
pagination.setPage(page);
468+
}}>
469+
<Group spacing={5} position="center">
470+
<Pagination.First
471+
onClick={() => {
472+
if (pageOffset !== 0) setPageOffset((value) => value - loadLimit);
473+
}}
474+
disabled={pageOffset === 0}
475+
/>
476+
<Pagination.Previous />
477+
{pagination.range.map((page) => {
478+
if (page === 'dots') {
479+
return <Pagination.Dots key={page} />;
480+
} else {
481+
return (
482+
<Pagination.Control
483+
value={page}
484+
key={page}
485+
active={pageLogData?.page === page}
486+
onClick={() => {
487+
goToPage(page);
488+
pagination.setPage(page);
489+
}}>
490+
{pageLogData?.limit ? page + pageOffset / pageLogData?.limit ?? 1 : page}
491+
</Pagination.Control>
492+
);
493+
}
494+
})}
495+
496+
<Pagination.Next />
497+
<Pagination.Last
498+
onClick={() => {
499+
setPageOffset((value) => {
500+
return value + loadLimit;
501+
});
502+
}}
503+
disabled={false}
504+
/>
505+
</Group>
506+
</Pagination.Root>
399507
) : (
400508
<Loader variant="dots" />
401509
)}

src/pages/Logs/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FC } from 'react';
44
import LogTable from './LogTable';
55
import { useLogsStyles } from './styles';
66
import ViewLog from './ViewLog';
7-
import HeaderPagination from './HeaderPagination';
7+
// import HeaderPagination from './HeaderPagination';
88

99
const Logs: FC = () => {
1010
useDocumentTitle('Parseable | Logs');
@@ -14,7 +14,7 @@ const Logs: FC = () => {
1414

1515
return (
1616
<Box className={container}>
17-
<HeaderPagination />
17+
{/* <HeaderPagination /> */}
1818
<LogTable />
1919
<ViewLog />
2020
</Box>

src/pages/Logs/styles.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const useLogTableStyles = createStyles((theme) => {
2424
container: {
2525
position: 'relative',
2626
flex: 1,
27-
maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT * 2 + 105}px )`,
27+
maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT * 2 }px )`,
2828
display: 'flex',
2929
flexDirection: 'column',
3030
overflow: 'hidden',
@@ -33,7 +33,7 @@ export const useLogTableStyles = createStyles((theme) => {
3333
innerContainer: {
3434
position: 'relative',
3535
flex: 1,
36-
maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT * 2 + 105}px -200px)`,
36+
maxHeight: `calc(${heights.screen} - ${HEADER_HEIGHT * 2 }px)`,
3737
display: 'flex',
3838
flexDirection: 'column',
3939
overflow: 'hidden',

0 commit comments

Comments
 (0)