fix(frontend): stop chart svg height warnings and idle consult status 404s

This commit is contained in:
jesse-ux
2026-09-25 01:43:59 +08:00
parent 514951d708
commit 90b027f0fd
11 changed files with 117 additions and 7 deletions
+5 -1
View File
@@ -67,7 +67,11 @@ export async function GET(request: Request) {
if (error) {
return NextResponse.json({ error: "暂时无法读取咨询状态" }, { status: 503 });
}
if (!data || typeof data.session_id !== "string") {
if (activeLookup) {
if (!data || typeof data.session_id !== "string") {
return new NextResponse(null, { status: 204 });
}
} else if (!data || typeof data.session_id !== "string") {
return NextResponse.json({ error: "咨询请求不存在" }, { status: 404 });
}
@@ -32,7 +32,6 @@ export function WesternWheelSvg({ western: suppliedWestern, skeleton = false }:
<svg
viewBox={`0 0 ${SIZE} ${SIZE}`}
width="100%"
height="auto"
role="img"
aria-label={skeleton ? undefined : "回归黄道本命盘,上升在左,宫位按 Placidus"}
aria-hidden={skeleton || undefined}
@@ -299,7 +299,6 @@ export function VedicChartSvgDrawing({ chart: suppliedChart, ariaLabel, highligh
<svg
viewBox={`0 0 ${CHART_VIEWBOX_WIDTH} ${viewBoxHeight}`}
width="100%"
height="auto"
role="img"
aria-label={ariaLabel}
data-skeleton={skeleton ? "true" : undefined}
+1 -1
View File
@@ -585,8 +585,8 @@ export async function fetchConsultationStatus(sessionId: string, requestId: stri
export async function fetchActiveConsultationStatus(signal?: AbortSignal): Promise<ConsultationStatus | null> {
const response = await fetch("/api/consult/status", { signal, cache: "no-store" });
if (response.status === 204 || response.status === 404) return null;
const payload: unknown = await response.json().catch(() => null);
if (response.status === 404) return null;
if (!response.ok) throw new Error(payloadMessage(payload, "暂时无法恢复后台回答"));
const status = parseConsultationStatus(payload);
if (status.status !== "reserved") throw new Error("后台回答状态无效");
+1 -2
View File
@@ -34,8 +34,7 @@ export function reportChartExportSvg(chart: NorthIndianChartModel, ariaLabel: st
const rendered = serializeSvgNode(VedicChartSvgDrawing({ chart, ariaLabel, chartId: "export-chart" }))
.replace(/<defs>[\s\S]*?<\/defs>/g, "")
.replace(/ clip-path="[^"]*"/g, "")
.replace(/<svg\b/, '<svg xmlns="http://www.w3.org/2000/svg"')
.replace(/height="auto"/, 'height="452"')
.replace(/<svg\b/, '<svg xmlns="http://www.w3.org/2000/svg" height="452"')
.replace(/class="([^"]*)"/g, (_match, classes: string) => {
if (classes.includes("personal-report-chart-cell")) return 'fill="none" stroke="currentColor" stroke-width="1.25"';
if (classes.includes("personal-report-chart-retrograde")) return 'stroke="currentColor" stroke-width="1.9"';
+9
View File
@@ -330,6 +330,15 @@ test("chart skeleton has one scoped breath rule and reduced motion is static", (
assert.match(globalStyles, /@media \(prefers-reduced-motion: reduce\)\s*\{\s*\.chart-page-skeleton \{ animation: none; opacity: 0\.5; \}/);
});
test("chart svgs omit the invalid height attribute and western height stays in css", () => {
const westernSvg = readFileSync(new URL("../src/components/chart-page/western-wheel-svg.tsx", import.meta.url), "utf8");
assert.doesNotMatch(vedicSvg, /height="auto"/);
assert.doesNotMatch(westernSvg, /height="auto"/);
assert.match(westernSvg, /width="100%"/);
assert.match(vedicSvg, /width="100%"/);
assert.match(globalStyles, /\.chart-page-western-svg \{[^}]*height: auto/);
});
test("the (app) layout mounts one sidebar for home and the four secondary routes", () => {
// 原值:`(secondary)/layout.tsx` 挂只读侧栏,`useSidebarData` 另拉一份列表
// 新值:`(app)/layout.tsx` 一份 SessionListProvider + SidebarProvider,首页也在里面
@@ -152,3 +152,26 @@ test("status expires stale reservations after fifteen minutes but leaves fresh r
assert.match(leaseBlock, /\.eq\("session_id", statusData\.session_id\)[\s\S]*\.eq\("request_id", statusData\.request_id\)/);
assert.doesNotMatch(statusRoute.slice(0, statusRoute.indexOf('if (statusData.status === "reserved"')), /cancel_consultation_credit/);
});
test("an empty active consultation lookup is 204 and a missing bound request stays 404", () => {
const activeEmpty = statusRoute.slice(
statusRoute.lastIndexOf("if (activeLookup)"),
statusRoute.indexOf("咨询请求不存在"),
);
assert.match(activeEmpty, /!data \|\| typeof data\.session_id !== "string"/);
assert.match(activeEmpty, /new NextResponse\(null, \{ status: 204 \}\)/);
assert.doesNotMatch(activeEmpty, /咨询请求不存在|status: 404/);
assert.match(statusRoute, /return NextResponse\.json\(\{ error: "咨询请求不存在" \}, \{ status: 404 \}\)/);
const cloud = read("src/lib/home-cloud-sync.ts");
const activeFn = cloud.slice(
cloud.indexOf("export async function fetchActiveConsultationStatus"),
cloud.indexOf("export async function patchSessionModel"),
);
const boundFn = cloud.slice(
cloud.indexOf("export async function fetchConsultationStatus"),
cloud.indexOf("export async function fetchActiveConsultationStatus"),
);
const statusGuard = activeFn.indexOf("if (response.status === 204 || response.status === 404) return null;");
assert.ok(statusGuard >= 0 && statusGuard < activeFn.indexOf("response.json()"));
assert.doesNotMatch(boundFn, /status === 204/);
});