mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 04:32:32 +00:00
web: customs archive reads the generated types, annotations page follows nextLink through the typed builder, dead PaginatedResponse removed (audit package 5, stage 4a)
The five hand-written customs-archive response types in lib/api.ts duplicated CustomsArchiveRunListResponse, CustomsArchiveEntryListResponse, CustomsArchiveRunSummary, CustomsArchiveEntrySummary and TenantOffboardingCustomsArchiveEntry from the manifest; the two pages import those. The annotations page built its next-page URL from API_BASE_URL + nextLink instead of the scoped builder plus extractContinuation. PaginatedResponse<T> had no importer. types/diagnostics.ts states why it stays hand-written (no wire DTO for GET diagnostics/paths). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -3,17 +3,12 @@
|
||||
import { Suspense, useCallback, useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import {
|
||||
api,
|
||||
type CustomsArchiveEntrySummary,
|
||||
type CustomsArchiveFullEntry,
|
||||
type CustomsArchiveListEntriesResponse,
|
||||
} from "@/lib/api";
|
||||
import { api } from "@/lib/api";
|
||||
import { apiErrorText, fetchJson, fetchOk } from "@/lib/apiClient";
|
||||
import { useAdminConfig } from "../../AdminConfigContext";
|
||||
import { AdminNotifications } from "../../AdminNotifications";
|
||||
import { DeleteConfirmModal } from "../components/DeleteConfirmModal";
|
||||
import type { CustomsArchiveEntryResponse } from "@/utils/wire-types.generated";
|
||||
import type { CustomsArchiveEntryListResponse, CustomsArchiveEntryResponse, CustomsArchiveEntrySummary, TenantOffboardingCustomsArchiveEntry } from "@/utils/wire-types.generated";
|
||||
|
||||
export default function CustomsArchiveDetailPage() {
|
||||
// useSearchParams() in CustomsArchiveDetailContent requires a Suspense boundary
|
||||
@@ -33,7 +28,7 @@ function CustomsArchiveDetailContent() {
|
||||
const { getAccessToken, setError, setSuccessMessage } = useAdminConfig();
|
||||
const [entries, setEntries] = useState<CustomsArchiveEntrySummary[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [expanded, setExpanded] = useState<Record<string, CustomsArchiveFullEntry | "loading" | undefined>>({});
|
||||
const [expanded, setExpanded] = useState<Record<string, TenantOffboardingCustomsArchiveEntry | "loading" | undefined>>({});
|
||||
const [deletingRk, setDeletingRk] = useState<string | null>(null);
|
||||
const [pendingDelete, setPendingDelete] = useState<CustomsArchiveEntrySummary | null>(null);
|
||||
|
||||
@@ -41,7 +36,7 @@ function CustomsArchiveDetailContent() {
|
||||
try {
|
||||
setLoading(true);
|
||||
const url = api.customsArchive.listEntries(tenantId, historyRowKey);
|
||||
const body = await fetchJson<CustomsArchiveListEntriesResponse>(url, getAccessToken);
|
||||
const body = await fetchJson<CustomsArchiveEntryListResponse>(url, getAccessToken);
|
||||
setEntries(body.entries ?? []);
|
||||
} catch (err) {
|
||||
setError(apiErrorText(err));
|
||||
@@ -75,7 +70,7 @@ function CustomsArchiveDetailContent() {
|
||||
api.customsArchive.getEntry(tenantId, historyRowKey, entry.rowKey),
|
||||
getAccessToken,
|
||||
);
|
||||
setExpanded((prev) => ({ ...prev, [key]: body.entry as CustomsArchiveFullEntry }));
|
||||
setExpanded((prev) => ({ ...prev, [key]: body.entry }));
|
||||
} catch (err) {
|
||||
setExpanded((prev) => {
|
||||
const next = { ...prev };
|
||||
@@ -153,7 +148,7 @@ function CustomsArchiveDetailContent() {
|
||||
const detail = expanded[entry.rowKey];
|
||||
const isOpen = !!detail;
|
||||
const isLoadingFull = detail === "loading";
|
||||
const fullEntry = typeof detail === "object" ? (detail as CustomsArchiveFullEntry) : null;
|
||||
const fullEntry = typeof detail === "object" ? (detail as TenantOffboardingCustomsArchiveEntry) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -3,16 +3,12 @@
|
||||
import { customsArchiveUrl } from "@/lib/routes";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
api,
|
||||
type CustomsArchiveListRunsResponse,
|
||||
type CustomsArchiveRunSummary,
|
||||
} from "@/lib/api";
|
||||
import { api } from "@/lib/api";
|
||||
import { apiErrorText, fetchJson } from "@/lib/apiClient";
|
||||
import { useAdminConfig } from "../AdminConfigContext";
|
||||
import { AdminNotifications } from "../AdminNotifications";
|
||||
import { DeleteConfirmModal } from "./components/DeleteConfirmModal";
|
||||
import type { CustomsArchiveDeleteRunResponse } from "@/utils/wire-types.generated";
|
||||
import type { CustomsArchiveDeleteRunResponse, CustomsArchiveRunListResponse, CustomsArchiveRunSummary } from "@/utils/wire-types.generated";
|
||||
|
||||
export default function CustomsArchivePage() {
|
||||
const { getAccessToken, setError, setSuccessMessage } = useAdminConfig();
|
||||
@@ -26,7 +22,7 @@ export default function CustomsArchivePage() {
|
||||
try {
|
||||
setLoading(true);
|
||||
const url = api.customsArchive.listRuns({ tenantId: filter.trim() || undefined });
|
||||
const body = await fetchJson<CustomsArchiveListRunsResponse>(url, getAccessToken);
|
||||
const body = await fetchJson<CustomsArchiveRunListResponse>(url, getAccessToken);
|
||||
setRuns(body.runs ?? []);
|
||||
} catch (err) {
|
||||
setError(apiErrorText(err));
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { sessionUrl } from "@/lib/routes";
|
||||
import { scopedApi } from "@/lib/scopedApi";
|
||||
import { API_BASE_URL } from "@/utils/config";
|
||||
import { ProtectedRoute } from "@/components/ProtectedRoute";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { useAdminMode, useAggregatedAdminScope } from "@/hooks";
|
||||
@@ -23,6 +22,7 @@ import {
|
||||
import { DocsLink } from "@/components/DocsLink";
|
||||
import { DOCS_PATHS } from "@/lib/docsPaths";
|
||||
import { fetchJson, nullOn404 } from "@/lib/apiClient";
|
||||
import { extractContinuation } from "@/lib/paginationLink";
|
||||
|
||||
/**
|
||||
* Annotations overview: every annotated session in one list, so a judged session can be
|
||||
@@ -294,7 +294,13 @@ export default function AnnotationsPage() {
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
setLoadError(null);
|
||||
fetchPage(`${API_BASE_URL}${nextLink}`, true);
|
||||
fetchPage(
|
||||
scopedApi.annotationsList(
|
||||
{ routeGlobal, selectedTenantId, effectiveTenantId },
|
||||
{ verdict: verdictFilter || undefined, lane: laneFilter || undefined, q: submittedQuery || undefined, continuation: extractContinuation(nextLink) ?? undefined },
|
||||
),
|
||||
true,
|
||||
);
|
||||
}}
|
||||
className="px-4 py-1.5 text-sm font-medium bg-white border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
|
||||
@@ -784,48 +784,6 @@ export const api = {
|
||||
},
|
||||
};
|
||||
|
||||
// ── Customs Archive response types ────────────────────────────────────────
|
||||
// Shared by /admin/customs-archive (list runs) and
|
||||
// /admin/customs-archive/[tenantId]/[historyRowKey] (list/inspect entries).
|
||||
export interface CustomsArchiveRunSummary {
|
||||
partitionKey: string;
|
||||
tenantId: string;
|
||||
historyRowKey: string;
|
||||
archivedAt: string;
|
||||
gatherRulesCount: number;
|
||||
analyzeRulesCount: number;
|
||||
imeLogPatternsCount: number;
|
||||
}
|
||||
|
||||
export interface CustomsArchiveListRunsResponse {
|
||||
success: boolean;
|
||||
count: number;
|
||||
runs: CustomsArchiveRunSummary[];
|
||||
}
|
||||
|
||||
export interface CustomsArchiveEntrySummary {
|
||||
partitionKey: string;
|
||||
rowKey: string;
|
||||
originalTable: string;
|
||||
originalRowKey: string;
|
||||
archivedAt: string;
|
||||
entityJsonPreview: string;
|
||||
}
|
||||
|
||||
export interface CustomsArchiveListEntriesResponse {
|
||||
success: boolean;
|
||||
count: number;
|
||||
entries: CustomsArchiveEntrySummary[];
|
||||
}
|
||||
|
||||
export interface CustomsArchiveFullEntry extends CustomsArchiveEntrySummary {
|
||||
tenantId: string;
|
||||
originalPartitionKey: string;
|
||||
entityJson: string;
|
||||
historyRowKey: string;
|
||||
archivedBy: string;
|
||||
}
|
||||
|
||||
// ── Backups response types (PR1 + PR2) ────────────────────────────────────
|
||||
|
||||
export interface BackupListResponse {
|
||||
|
||||
@@ -8,16 +8,6 @@
|
||||
* url builder.
|
||||
*/
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
count: number;
|
||||
nextLink?: string | null;
|
||||
/** legacy collection key — varies per endpoint (events / sessions / logs / reports) */
|
||||
events?: T[];
|
||||
sessions?: T[];
|
||||
logs?: T[];
|
||||
reports?: T[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the `continuation` query param from a backend-supplied nextLink.
|
||||
* Returns null when the input is empty or carries no continuation.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// GET /api/diagnostics/paths has no wire DTO in the shared manifest (the payload is assembled
|
||||
// in GetDiagnosticsPathsFunction); this module is the client-owned shape of that response.
|
||||
export interface DiagnosticsLogPath {
|
||||
path: string;
|
||||
description: string;
|
||||
|
||||
Reference in New Issue
Block a user