Refresh status pages in realtime
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { RealtimeRefresh } from "@/components/app-runtime";
|
||||
import { AppShell, OtaCenterCard, PageNav, StatusBar } from "@/components/app-ui";
|
||||
import { requirePageSession } from "@/lib/boss-auth";
|
||||
import { getOtaStatus, readState } from "@/lib/boss-data";
|
||||
@@ -10,6 +11,7 @@ export default async function AboutPage() {
|
||||
|
||||
return (
|
||||
<AppShell bottomNav={false}>
|
||||
<RealtimeRefresh events={["ota.updated"]} />
|
||||
<StatusBar />
|
||||
<PageNav title="关于 / OTA" backHref="/me" />
|
||||
<div className="flex flex-col gap-3 px-[18px] pb-6">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { RealtimeRefresh } from "@/components/app-runtime";
|
||||
import { AppShell, PageNav, StatusBar } from "@/components/app-ui";
|
||||
import { requirePageSession } from "@/lib/boss-auth";
|
||||
import { getAuditSummaryView } from "@/lib/boss-projections";
|
||||
@@ -13,6 +14,7 @@ export default async function AuditPage() {
|
||||
|
||||
return (
|
||||
<AppShell bottomNav={false}>
|
||||
<RealtimeRefresh events={["project.context_risk.updated"]} />
|
||||
<StatusBar />
|
||||
<PageNav title="运维与修复" backHref="/me/ops" />
|
||||
<div className="flex min-h-0 flex-1 flex-col px-[18px] pb-0">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { RealtimeRefresh } from "@/components/app-runtime";
|
||||
import { AppShell, PageNav, RepairTicketActions, StatusBar } from "@/components/app-ui";
|
||||
import { requirePageSession } from "@/lib/boss-auth";
|
||||
import { getOpsSummaryView } from "@/lib/boss-projections";
|
||||
@@ -13,6 +14,7 @@ export default async function OpsPage() {
|
||||
|
||||
return (
|
||||
<AppShell bottomNav={false}>
|
||||
<RealtimeRefresh events={["project.context_risk.updated"]} />
|
||||
<StatusBar />
|
||||
<PageNav title="运维与修复" backHref="/me" />
|
||||
<div className="flex min-h-0 flex-1 flex-col px-[18px] pb-0">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { RealtimeRefresh } from "@/components/app-runtime";
|
||||
import {
|
||||
AppShell,
|
||||
HeaderTitle,
|
||||
@@ -17,6 +18,7 @@ export default async function MePage() {
|
||||
|
||||
return (
|
||||
<AppShell>
|
||||
<RealtimeRefresh events={["ota.updated"]} />
|
||||
<StatusBar />
|
||||
<HeaderTitle title="我的" />
|
||||
<div className="flex flex-col gap-3 px-[18px] pb-5">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { RealtimeRefresh } from "@/components/app-runtime";
|
||||
import { AppShell, PageNav, StatusBar } from "@/components/app-ui";
|
||||
import { requirePageSession } from "@/lib/boss-auth";
|
||||
import { getThreadContextDetailView } from "@/lib/boss-projections";
|
||||
@@ -21,6 +22,10 @@ export default async function ThreadPage({
|
||||
|
||||
return (
|
||||
<AppShell bottomNav={false}>
|
||||
<RealtimeRefresh
|
||||
projectId={detail.snapshot.projectId}
|
||||
events={["project.context_risk.updated", "conversation.updated"]}
|
||||
/>
|
||||
<StatusBar />
|
||||
<PageNav title="线程详情" backHref={`/conversations/${detail.snapshot.projectId}`} />
|
||||
<div className="space-y-3 px-[18px] pb-6">
|
||||
|
||||
49
tests/status-pages-realtime-refresh.test.ts
Normal file
49
tests/status-pages-realtime-refresh.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import path from "node:path";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const testsDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
async function readWorkspaceFile(relativePath: string) {
|
||||
return readFile(path.join(testsDir, "..", relativePath), "utf8");
|
||||
}
|
||||
|
||||
test("thread detail page refreshes when context risk updates for its project", async () => {
|
||||
const source = await readWorkspaceFile("src/app/threads/[threadId]/page.tsx");
|
||||
|
||||
assert.match(source, /import \{ RealtimeRefresh \}/, "expected thread page to import RealtimeRefresh");
|
||||
assert.match(source, /projectId=\{detail\.snapshot\.projectId\}/, "expected thread page to scope realtime refresh to its project");
|
||||
assert.match(source, /"project\.context_risk\.updated"/, "expected thread page to listen for context risk updates");
|
||||
});
|
||||
|
||||
test("me and about pages refresh when OTA status changes", async () => {
|
||||
const [mePage, aboutPage] = await Promise.all([
|
||||
readWorkspaceFile("src/app/me/page.tsx"),
|
||||
readWorkspaceFile("src/app/me/about/page.tsx"),
|
||||
]);
|
||||
|
||||
for (const [label, source] of [
|
||||
["me", mePage],
|
||||
["about", aboutPage],
|
||||
] as const) {
|
||||
assert.match(source, /<RealtimeRefresh/, `expected ${label} page to render RealtimeRefresh`);
|
||||
assert.match(source, /"ota\.updated"/, `expected ${label} page to listen for OTA updates`);
|
||||
}
|
||||
});
|
||||
|
||||
test("ops pages refresh when risk or audit status changes", async () => {
|
||||
const [opsPage, auditPage] = await Promise.all([
|
||||
readWorkspaceFile("src/app/me/ops/page.tsx"),
|
||||
readWorkspaceFile("src/app/me/ops/audit/page.tsx"),
|
||||
]);
|
||||
|
||||
for (const [label, source] of [
|
||||
["ops", opsPage],
|
||||
["audit", auditPage],
|
||||
] as const) {
|
||||
assert.match(source, /<RealtimeRefresh/, `expected ${label} page to render RealtimeRefresh`);
|
||||
assert.match(source, /"project\.context_risk\.updated"/, `expected ${label} page to listen for risk updates`);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user