💄 show loading animation on http request logs table

This commit is contained in:
Fred KISSIE
2026-05-20 04:50:49 +02:00
parent 1dfb3408e8
commit a163cc3678
4 changed files with 88 additions and 10 deletions

View File

@@ -1620,6 +1620,7 @@
"certificateStatus": "Certificate",
"certificateStatusAutoRefreshHint": "Status refreshes automatically.",
"loading": "Loading",
"loadingEllipsis": "Loading...",
"loadingAnalytics": "Loading Analytics",
"restart": "Restart",
"domains": "Domains",

View File

@@ -19,6 +19,7 @@ import { useParams, useRouter, useSearchParams } from "next/navigation";
import { useMemo, useState, useTransition } from "react";
import { useStoredPageSize } from "@app/hooks/useStoredPageSize";
import { build } from "@server/build";
import type { QueryRequestAuditLogResponse } from "@server/routers/auditLogs/types";
export default function GeneralPage() {
const router = useRouter();
@@ -79,7 +80,9 @@ export default function GeneralPage() {
if (dateRange.startDate?.date) {
const dt = new Date(dateRange.startDate.date);
if (dateRange.startDate.time) {
const [h, m, s] = dateRange.startDate.time.split(":").map(Number);
const [h, m, s] = dateRange.startDate.time
.split(":")
.map(Number);
dt.setHours(h, m, s || 0);
}
timeStart = dt.toISOString();
@@ -114,7 +117,7 @@ export default function GeneralPage() {
};
}, [dateRange, currentPage, pageSize, filters]);
const { data, isFetching, refetch } = useQuery({
const { data, isFetching, isLoading, refetch } = useQuery({
...logQueries.requests({
orgId: orgId as string,
filters: queryFilters
@@ -122,7 +125,7 @@ export default function GeneralPage() {
enabled: build !== "oss"
});
const rows = data?.log ?? [];
const rows = isLoading ? generateSampleRequestLogs() : (data?.log ?? []);
const totalCount = data?.pagination?.total ?? 0;
const filterAttributes = data?.filterAttributes ?? {
actors: [],
@@ -681,7 +684,7 @@ export default function GeneralPage() {
currentPage={currentPage}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
isLoading={isFetching}
isLoading={isLoading}
pageSize={pageSize}
// Row expansion props
expandable={true}
@@ -690,3 +693,63 @@ export default function GeneralPage() {
</>
);
}
function generateSampleRequestLogs(): QueryRequestAuditLogResponse["log"] {
const methods = ["GET", "POST", "PUT", "DELETE", "PATCH"];
const paths = [
"/api/v1/users",
"/dashboard",
"/settings",
"/health",
"/metrics"
];
const hosts = ["app.example.com", "api.example.com", "admin.example.com"];
const locations = ["US", "DE", "GB", "FR", "JP", "CA", "AU"];
const allowedReasons = [100, 101, 102, 103, 104, 105, 106, 107, 108];
const deniedReasons = [201, 202, 203, 204, 205, 299];
const actors = [
"alice@example.com",
"bob@example.com",
"carol@example.com",
null
];
const now = Math.floor(Date.now() / 1000);
const sevenDaysAgo = now - 7 * 24 * 60 * 60;
return Array.from({ length: 10 }, (_, i) => {
const action = Math.random() > 0.3;
const reason = action
? allowedReasons[Math.floor(Math.random() * allowedReasons.length)]
: deniedReasons[Math.floor(Math.random() * deniedReasons.length)];
const actor = actors[Math.floor(Math.random() * actors.length)];
return {
timestamp: Math.floor(
sevenDaysAgo + Math.random() * (now - sevenDaysAgo)
),
action,
reason,
orgId: "sample-org",
actorType: actor ? "user" : null,
actor,
actorId: actor ? `user-${i}` : null,
resourceId: Math.floor(Math.random() * 5) + 1,
siteResourceId: null,
resourceNiceId: `resource-${(i % 3) + 1}`,
resourceName: `Resource ${(i % 3) + 1}`,
ip: `${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`,
location: locations[Math.floor(Math.random() * locations.length)],
userAgent: "Mozilla/5.0",
metadata: null,
headers: null,
query: null,
originalRequestURL: null,
scheme: "https",
host: hosts[Math.floor(Math.random() * hosts.length)],
path: paths[Math.floor(Math.random() * paths.length)],
method: methods[Math.floor(Math.random() * methods.length)],
tls: true
};
});
}

View File

@@ -28,6 +28,7 @@ import {
ChevronRight,
Download,
Loader,
LoaderIcon,
RefreshCw
} from "lucide-react";
import { useTranslations } from "next-intl";
@@ -427,7 +428,7 @@ export function LogDataTable<TData, TValue>({
)}
</div>
</CardHeader>
<CardContent>
<CardContent className="relative">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
@@ -535,6 +536,19 @@ export function LogDataTable<TData, TValue>({
)}
</TableBody>
</Table>
{isLoading && (
<>
<div className="backdrop-blur-[2px] z-10 absolute inset-0 top-10"></div>
<div className="absolute z-20 left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 border border-border rounded-md bg-muted">
<div className="flex items-center gap-2 p-6">
<LoaderIcon className="size-4 animate-spin" />
{t("loadingEllipsis")}
</div>
</div>
</>
)}
<div className="mt-4">
<DataTablePagination
table={table}

View File

@@ -1,18 +1,20 @@
import { build } from "@server/build";
import { StatusHistoryResponse } from "@server/lib/statusHistory";
import type { ListAlertRulesResponse } from "@server/routers/alertRule/types";
import type { QueryRequestAnalyticsResponse } from "@server/routers/auditLogs";
import type { QueryRequestAuditLogResponse } from "@server/routers/auditLogs/types";
import type { ListClientsResponse } from "@server/routers/client";
import type {
ListDomainsResponse,
GetDNSRecordsResponse
GetDNSRecordsResponse,
ListDomainsResponse
} from "@server/routers/domain";
import type { GetDomainResponse } from "@server/routers/domain/getDomain";
import { ListHealthChecksResponse } from "@server/routers/healthChecks/types";
import type {
GetResourceWhitelistResponse,
ListResourceNamesResponse,
ListResourcesResponse
} from "@server/routers/resource";
import type { ListAlertRulesResponse } from "@server/routers/alertRule/types";
import type { ListRolesResponse } from "@server/routers/role";
import type { ListSitesResponse } from "@server/routers/site";
import type {
@@ -32,8 +34,6 @@ import type { AxiosResponse } from "axios";
import z from "zod";
import { remote } from "./api";
import { durationToMs } from "./durationToMs";
import { ListHealthChecksResponse } from "@server/routers/healthChecks/types";
import { StatusHistoryResponse } from "@server/lib/statusHistory";
export type ProductUpdate = {
link: string | null;