Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ export const KeysOverviewLogsTable = ({ apiId, setSelectedLog, log: selectedLog
selectedItem={selectedLog}
keyExtractor={(log) => log.request_id}
rowClassName={(log) => getRowClassName(log, selectedLog as KeysOverviewLog)}
loadMoreFooterProps={{
hide: true,
}}
emptyState={
<div className="w-full flex justify-center items-center h-full">
<Empty className="w-[400px] flex items-start">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function useLogsQuery({
}: UseLogsQueryParams = {}) {
const [historicalLogsMap, setHistoricalLogsMap] = useState(() => new Map<string, Log>());
const [realtimeLogsMap, setRealtimeLogsMap] = useState(() => new Map<string, Log>());
const [totalCount, setTotalCount] = useState(0);

const { filters } = useFilters();
const queryClient = trpc.useUtils();
Expand Down Expand Up @@ -213,6 +214,10 @@ export function useLogsQuery({
});
});
setHistoricalLogsMap(newMap);

if (initialData.pages.length > 0) {
setTotalCount(initialData.pages[0].total);
}
}
}, [initialData]);

Expand All @@ -231,6 +236,7 @@ export function useLogsQuery({
loadMore: fetchNextPage,
isLoadingMore: isFetchingNextPage,
isPolling: startPolling,
total: totalCount,
};
}

Expand Down
22 changes: 18 additions & 4 deletions apps/dashboard/app/(app)/logs/components/table/logs-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,11 @@ const additionalColumns: Column<Log>[] = [

export const LogsTable = () => {
const { displayProperties, setSelectedLog, selectedLog, isLive } = useLogsContext();
const { realtimeLogs, historicalLogs, isLoading, isLoadingMore, loadMore } = useLogsQuery({
startPolling: isLive,
pollIntervalMs: 2000,
});
const { realtimeLogs, historicalLogs, isLoading, isLoadingMore, loadMore, hasMore, total } =
useLogsQuery({
startPolling: isLive,
pollIntervalMs: 2000,
});

const getRowClassName = (log: Log) => {
const style = getStatusStyle(log.response_status);
Expand Down Expand Up @@ -246,6 +247,19 @@ export const LogsTable = () => {
keyExtractor={(log) => log.request_id}
rowClassName={getRowClassName}
selectedClassName={getSelectedClassName}
loadMoreFooterProps={{
hide: isLoading,
buttonText: "Load more logs",
hasMore,
countInfoText: (
<div className="flex gap-2">
<span>Showing</span> <span className="text-accent-12">{historicalLogs.length}</span>
<span>of</span>
{total}
<span>requests</span>
</div>
),
}}
emptyState={
<div className="w-full flex justify-center items-center h-full">
<Empty className="w-[400px] flex items-start">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type UseLogsQueryParams = {
};

export function useRatelimitOverviewLogsQuery({ namespaceId, limit = 50 }: UseLogsQueryParams) {
const [totalCount, setTotalCount] = useState(0);
const [historicalLogsMap, setHistoricalLogsMap] = useState(
() => new Map<string, RatelimitOverviewLog>(),
);
Expand Down Expand Up @@ -109,6 +110,9 @@ export function useRatelimitOverviewLogsQuery({ namespaceId, limit = 50 }: UseLo
newMap.set(log.identifier, log);
});
});
if (initialData.pages.length > 0) {
setTotalCount(initialData.pages[0].total);
}
setHistoricalLogsMap(newMap);
}
}, [initialData]);
Expand All @@ -117,6 +121,7 @@ export function useRatelimitOverviewLogsQuery({ namespaceId, limit = 50 }: UseLo
historicalLogs,
isLoading: isLoadingInitial,
hasMore: hasNextPage,
totalCount,
loadMore: fetchNextPage,
isLoadingMore: isFetchingNextPage,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ export const RatelimitOverviewLogsTable = ({
}) => {
const [selectedLog, setSelectedLog] = useState<RatelimitOverviewLog>();
const { getSortDirection, toggleSort } = useSort<SortFields>();
const { historicalLogs, isLoading, isLoadingMore, loadMore } = useRatelimitOverviewLogsQuery({
namespaceId,
});
const { historicalLogs, isLoading, isLoadingMore, loadMore, hasMore, totalCount } =
useRatelimitOverviewLogsQuery({
namespaceId,
});

const columns = (namespaceId: string): Column<RatelimitOverviewLog>[] => {
return [
Expand Down Expand Up @@ -214,6 +215,19 @@ export const RatelimitOverviewLogsTable = ({
columns={columns(namespaceId)}
keyExtractor={(log) => log.identifier}
rowClassName={(rowLog) => getRowClassName(rowLog, selectedLog as RatelimitOverviewLog)}
loadMoreFooterProps={{
itemLabel: "identifiers",
buttonText: "Load more logs",
hasMore,
hide: isLoading,
countInfoText: (
<div className="flex gap-2">
<span>Showing</span> <span className="text-accent-12">{historicalLogs.length}</span>
<span>of {totalCount}</span>
<span>rate limit identifiers</span>
</div>
),
}}
emptyState={
<div className="w-full flex justify-center items-center h-full">
<Empty className="w-[400px] flex items-start">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function useRatelimitLogsQuery({
}: UseLogsQueryParams) {
const [historicalLogsMap, setHistoricalLogsMap] = useState(() => new Map<string, RatelimitLog>());
const [realtimeLogsMap, setRealtimeLogsMap] = useState(() => new Map<string, RatelimitLog>());
const [totalCount, setTotalCount] = useState(0);

const { filters } = useFilters();
const queryClient = trpc.useUtils();
Expand Down Expand Up @@ -193,6 +194,9 @@ export function useRatelimitLogsQuery({
newMap.set(log.request_id, log);
});
});
if (initialData.pages.length > 0) {
setTotalCount(initialData.pages[0].total);
}
setHistoricalLogsMap(newMap);
}
}, [initialData]);
Expand All @@ -212,6 +216,7 @@ export function useRatelimitLogsQuery({
loadMore: fetchNextPage,
isLoadingMore: isFetchingNextPage,
isPolling: startPolling,
totalCount,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const getSelectedClassName = (log: RatelimitLog, isSelected: boolean) => {

export const RatelimitLogsTable = () => {
const { setSelectedLog, selectedLog, isLive, namespaceId } = useRatelimitLogsContext();
const { realtimeLogs, historicalLogs, isLoading, isLoadingMore, loadMore } =
const { realtimeLogs, historicalLogs, isLoading, isLoadingMore, loadMore, totalCount, hasMore } =
useRatelimitLogsQuery({
namespaceId,
startPolling: isLive,
Expand Down Expand Up @@ -218,6 +218,19 @@ export const RatelimitLogsTable = () => {
keyExtractor={(log) => log.request_id}
rowClassName={getRowClassName}
selectedClassName={getSelectedClassName}
loadMoreFooterProps={{
buttonText: "Load more logs",
hasMore,
hide: isLoading,
countInfoText: (
<div className="flex gap-2">
<span>Showing</span> <span className="text-accent-12">{historicalLogs.length}</span>
<span>of</span>
{totalCount}
<span>ratelimit requests</span>
</div>
),
}}
emptyState={
<div className="w-full flex justify-center items-center h-full">
<Empty className="w-[400px] flex items-start">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,66 @@
export const LoadingIndicator = () => (
<div className="fixed bottom-0 left-0 right-0 py-2 bg-background/90 backdrop-blur-sm border-t border-border">
<div className="flex items-center justify-center gap-2 text-sm text-accent-11">
<div className="h-1.5 w-1.5 rounded-full bg-accent-11 animate-pulse" />
Loading more data
import { Button } from "@unkey/ui";

type LoadMoreFooterProps = {
onLoadMore?: () => void;
isFetchingNextPage?: boolean;
totalVisible: number;
totalCount: number;
className?: string;
itemLabel?: string;
buttonText?: string;
hasMore?: boolean;
hide?: boolean;
countInfoText?: React.ReactNode;
};

export const LoadMoreFooter = ({
onLoadMore,
isFetchingNextPage = false,
totalVisible,
totalCount,
itemLabel = "items",
buttonText = "Load more",
hasMore = true,
countInfoText,
hide,
}: LoadMoreFooterProps) => {
const shouldShow = !!onLoadMore;

if (hide) {
return;
}

return (
<div
className="fixed bottom-0 left-0 right-0 w-full items-center justify-center flex z-10 transition-opacity duration-200"
style={{
opacity: shouldShow ? 1 : 0,
pointerEvents: shouldShow ? "auto" : "none",
}}
>
<div className="w-[740px] border bg-gray-1 dark:bg-black border-gray-6 h-[60px] flex items-center justify-center p-[18px] rounded-[10px] drop-shadow-lg shadow-sm mb-5">
<div className="flex w-full justify-between items-center text-[13px] text-accent-9">
{countInfoText && <div>{countInfoText}</div>}
{!countInfoText && (
<div className="flex gap-2">
<span>Viewing</span> <span className="text-accent-12">{totalVisible}</span>
<span>of</span>
<span className="text-grayA-12">{totalCount}</span>
<span>{itemLabel}</span>
</div>
)}

<Button
variant="outline"
size="sm"
onClick={onLoadMore}
loading={isFetchingNextPage}
disabled={isFetchingNextPage || !hasMore}
>
{buttonText}
</Button>
</div>
</div>
</div>
</div>
);
);
};
1 change: 0 additions & 1 deletion apps/dashboard/components/virtual-table/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export const DEFAULT_CONFIG: TableConfig = {
rowHeight: 26,
loadingRows: 50,
overscan: 5,
tableBorder: 1,
throttleDelay: 350,
headerHeight: 40,
} as const;
19 changes: 5 additions & 14 deletions apps/dashboard/components/virtual-table/hooks/useTableHeight.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,27 @@
import { useEffect, useState } from "react";

export const useTableHeight = (
containerRef: React.RefObject<HTMLDivElement>,
headerHeight: number,
tableBorder: number,
) => {
// Adds bottom spacing to prevent the table from extending to the edge of the viewport
const BREATHING_SPACE = 20;
export const useTableHeight = (containerRef: React.RefObject<HTMLDivElement>) => {
const [fixedHeight, setFixedHeight] = useState(0);

useEffect(() => {
const calculateHeight = () => {
if (!containerRef.current) {
return;
}
const rect = containerRef.current.getBoundingClientRect();
const totalHeaderHeight = headerHeight + tableBorder;
const availableHeight = window.innerHeight - rect.top - totalHeaderHeight;
const availableHeight = window.innerHeight - rect.top - BREATHING_SPACE;
setFixedHeight(Math.max(availableHeight, 0));
};

calculateHeight();
const resizeObserver = new ResizeObserver(calculateHeight);
window.addEventListener("resize", calculateHeight);

if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}

return () => {
resizeObserver.disconnect();
window.removeEventListener("resize", calculateHeight);
};
}, [containerRef, headerHeight, tableBorder]);

}, [containerRef]);
return fixedHeight;
};
13 changes: 10 additions & 3 deletions apps/dashboard/components/virtual-table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { cn } from "@/lib/utils";
import { CaretDown, CaretExpandY, CaretUp, CircleCarretRight } from "@unkey/icons";
import { Fragment, type Ref, forwardRef, useImperativeHandle, useMemo, useRef } from "react";
import { EmptyState } from "./components/empty-state";
import { LoadingIndicator } from "./components/loading-indicator";
import { LoadMoreFooter } from "./components/loading-indicator";
import { DEFAULT_CONFIG } from "./constants";
import { useTableData } from "./hooks/useTableData";
import { useTableHeight } from "./hooks/useTableHeight";
Expand Down Expand Up @@ -48,14 +48,15 @@ export const VirtualTable = forwardRef<VirtualTableRef, VirtualTableProps<any>>(
selectedClassName,
selectedItem,
isFetchingNextPage,
loadMoreFooterProps,
}: VirtualTableProps<TTableData>,
ref: Ref<unknown> | undefined,
) {
const config = { ...DEFAULT_CONFIG, ...userConfig };
const parentRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);

const fixedHeight = useTableHeight(containerRef, config.headerHeight, config.tableBorder);
const fixedHeight = useTableHeight(containerRef);
const tableData = useTableData<TTableData>(realtimeData, historicData);

const virtualizer = useVirtualData({
Expand Down Expand Up @@ -276,7 +277,13 @@ export const VirtualTable = forwardRef<VirtualTableRef, VirtualTableProps<any>>(
/>
</tbody>
</table>
{isFetchingNextPage && <LoadingIndicator />}
<LoadMoreFooter
{...loadMoreFooterProps}
onLoadMore={onLoadMore}
isFetchingNextPage={isFetchingNextPage}
totalVisible={virtualizer.getVirtualItems().length}
totalCount={tableData.getTotalLength()}
/>
</div>
</div>
);
Expand Down
8 changes: 7 additions & 1 deletion apps/dashboard/components/virtual-table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export type TableConfig = {
rowHeight: number;
loadingRows: number;
overscan: number;
tableBorder: number;
throttleDelay: number;
headerHeight: number;
};
Expand All @@ -45,6 +44,13 @@ export type VirtualTableProps<T> = {
selectedClassName?: (item: T, isSelected: boolean) => string;
selectedItem?: T | null;
isFetchingNextPage?: boolean;
loadMoreFooterProps?: {
itemLabel?: string;
buttonText?: string;
countInfoText?: React.ReactNode;
hasMore?: boolean;
hide?: boolean;
};
};

export type SeparatorItem = {
Expand Down
Loading
Loading