feat(web): auto fresh every 5 seconds for workflow display
This commit is contained in:
parent
825c8a63fb
commit
3c4bce630e
@ -1,4 +1,5 @@
|
|||||||
import { LoadingWrapper } from "@/components/LoadingWrapper";
|
import { LoadingWrapper } from "@/components/LoadingWrapper";
|
||||||
|
import { RouteRefresher } from "@/components/RouteRefresher";
|
||||||
import { RunsTable } from "@/components/RunsTable";
|
import { RunsTable } from "@/components/RunsTable";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
|
||||||
@ -13,8 +14,11 @@ export default async function Page({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-full h-fit min-w-0">
|
<Card className="w-full h-fit min-w-0">
|
||||||
<CardHeader>
|
<CardHeader className="relative">
|
||||||
<CardTitle>Run</CardTitle>
|
<CardTitle>Run</CardTitle>
|
||||||
|
<div className="absolute right-6 top-6">
|
||||||
|
<RouteRefresher interval={5000} />
|
||||||
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
@ -54,7 +54,7 @@ export function LiveStatus({
|
|||||||
? `${data.json.event} - ${data.json.data.node}`
|
? `${data.json.event} - ${data.json.data.node}`
|
||||||
: "-"}
|
: "-"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="truncate text-right">
|
||||||
<StatusBadge status={status} />
|
<StatusBadge status={status} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</>
|
</>
|
||||||
|
@ -12,6 +12,22 @@ export function PaginationControl(props: {
|
|||||||
totalPage: number;
|
totalPage: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
}) {
|
}) {
|
||||||
|
let startPage = Math.max(props.currentPage - 2, 1);
|
||||||
|
let endPage = Math.min(startPage + 3, props.totalPage);
|
||||||
|
|
||||||
|
if (props.currentPage <= 2) {
|
||||||
|
endPage = Math.min(4, props.totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.currentPage > props.totalPage - 2) {
|
||||||
|
startPage = Math.max(props.totalPage - 3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageNumbers = Array.from(
|
||||||
|
{ length: endPage - startPage + 1 },
|
||||||
|
(_, i) => startPage + i
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pagination>
|
<Pagination>
|
||||||
<PaginationContent>
|
<PaginationContent>
|
||||||
@ -22,9 +38,15 @@ export function PaginationControl(props: {
|
|||||||
: `?page=${props.currentPage}`
|
: `?page=${props.currentPage}`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<PaginationLink href="#" isActive>
|
{pageNumbers.map((page) => (
|
||||||
{props.currentPage}
|
<PaginationLink
|
||||||
|
key={page}
|
||||||
|
href={`?page=${page}`}
|
||||||
|
isActive={props.currentPage === page}
|
||||||
|
>
|
||||||
|
{page}
|
||||||
</PaginationLink>
|
</PaginationLink>
|
||||||
|
))}
|
||||||
<PaginationItem>
|
<PaginationItem>
|
||||||
<PaginationEllipsis />
|
<PaginationEllipsis />
|
||||||
</PaginationItem>
|
</PaginationItem>
|
||||||
|
41
web/src/components/RouteRefresher.tsx
Normal file
41
web/src/components/RouteRefresher.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { LoadingIcon } from "@/components/LoadingIcon";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useEffect, useTransition } from "react";
|
||||||
|
|
||||||
|
export function RouteRefresher(props: { interval: number }) {
|
||||||
|
const [isPending, startTransition] = useTransition();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let timeout: NodeJS.Timeout;
|
||||||
|
|
||||||
|
const refresh = () => {
|
||||||
|
console.log("refreshing");
|
||||||
|
|
||||||
|
startTransition(() => {
|
||||||
|
router.refresh();
|
||||||
|
});
|
||||||
|
timeout = setTimeout(refresh, props.interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVisibilityChange = () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
} else {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("visibilitychange", handleVisibilityChange);
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
window.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||||
|
};
|
||||||
|
}, [props.interval, router]);
|
||||||
|
|
||||||
|
return <div>{isPending && <LoadingIcon />}</div>;
|
||||||
|
}
|
@ -24,8 +24,12 @@ export async function RunDisplay({
|
|||||||
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>{run.number}</TableCell>
|
<TableCell>{run.number}</TableCell>
|
||||||
<TableCell className="font-medium">{run.machine?.name}</TableCell>
|
<TableCell className="font-medium truncate">
|
||||||
<TableCell>{getRelativeTime(run.created_at)}</TableCell>
|
{run.machine?.name}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="truncate">
|
||||||
|
{getRelativeTime(run.created_at)}
|
||||||
|
</TableCell>
|
||||||
<TableCell>{run.version?.version}</TableCell>
|
<TableCell>{run.version?.version}</TableCell>
|
||||||
<LiveStatus run={run} />
|
<LiveStatus run={run} />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { parseAsInteger } from "next-usequerystate";
|
import { parseAsInteger } from "next-usequerystate";
|
||||||
|
|
||||||
const itemPerPage = 4;
|
const itemPerPage = 6;
|
||||||
const pageParser = parseAsInteger.withDefault(1);
|
const pageParser = parseAsInteger.withDefault(1);
|
||||||
|
|
||||||
export async function RunsTable(props: {
|
export async function RunsTable(props: {
|
||||||
@ -33,7 +33,7 @@ export async function RunsTable(props: {
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="overflow-auto h-[400px] w-full">
|
<div className="overflow-auto h-fit w-full">
|
||||||
<Table className="">
|
<Table className="">
|
||||||
{/* <TableCaption>A list of your recent runs.</TableCaption> */}
|
{/* <TableCaption>A list of your recent runs.</TableCaption> */}
|
||||||
<TableHeader className="bg-background top-0 sticky">
|
<TableHeader className="bg-background top-0 sticky">
|
||||||
@ -42,7 +42,7 @@ export async function RunsTable(props: {
|
|||||||
<TableHead className="">Machine</TableHead>
|
<TableHead className="">Machine</TableHead>
|
||||||
<TableHead className="">Time</TableHead>
|
<TableHead className="">Time</TableHead>
|
||||||
<TableHead className="w-[100px]">Version</TableHead>
|
<TableHead className="w-[100px]">Version</TableHead>
|
||||||
<TableHead className="">Live Status</TableHead>
|
<TableHead className="truncate">Live Status</TableHead>
|
||||||
<TableHead className=" text-right">Status</TableHead>
|
<TableHead className=" text-right">Status</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user