feat: add backup recovery, setup wizard and user-view guards
Magent CI/CD / verify (push) Successful in 5m20s
Magent CI/CD / deploy-beta (push) Successful in 1m41s

This commit is contained in:
2026-09-18 17:23:03 +12:00
parent a6a4a9aa24
commit fd6671cf7e
44 changed files with 4650 additions and 114 deletions
+38 -23
View File
@@ -4,8 +4,9 @@ import Image from "next/image";
import { useParams, useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";
import { authFetch, clearToken, getApiBase, getToken } from "../../lib/auth";
import { canAccess } from "../../lib/features";
import { canAccess, type FeatureAccess } from "../../lib/features";
import { lockBodyScroll } from "../../lib/scrollLock";
import { useEffectiveRole } from "../../lib/viewMode";
import PageHeading from "../../ui/PageHeading";
import LatestActivity from "./LatestActivity";
import RequestLanguage from "./RequestLanguage";
@@ -329,8 +330,10 @@ export default function RequestTimelinePage() {
const [historySnapshots, setHistorySnapshots] = useState<SnapshotHistory[]>([]);
const [historyActions, setHistoryActions] = useState<ActionHistory[]>([]);
const [operationProgress, setOperationProgress] = useState<OperationProgress | null>(null);
const [isAdmin, setIsAdmin] = useState(false);
const [canReportIssues, setCanReportIssues] = useState(false);
const [viewer, setViewer] = useState<{ role?: string; features?: Partial<FeatureAccess> } | null>(null);
const effectiveRole = useEffectiveRole(viewer?.role);
const isAdmin = effectiveRole === "admin";
const canReportIssues = canAccess(viewer ? { ...viewer, role: effectiveRole ?? undefined } : null, "issues");
const [selectedAdditionalSeasons, setSelectedAdditionalSeasons] = useState<number[]>([]);
const awaitingMediaIndex = Boolean(
snapshot?.presentation?.pipeline?.some((stage) => stage.id === "available" && stage.state === "active"),
@@ -389,32 +392,13 @@ export default function RequestTimelinePage() {
throw new Error("Unable to verify your request access.");
}
const me = await meResponse.json();
const viewerIsAdmin = me?.role === "admin";
setIsAdmin(viewerIsAdmin);
setCanReportIssues(canAccess(me, "issues"));
setViewer(me);
if (!snapshotResponse.ok) {
throw new Error(await readApiError(snapshotResponse, "Unable to load this request."));
}
const snapshotData = await snapshotResponse.json();
if (!isSnapshotPayload(snapshotData)) throw new Error("Unable to load this request.");
setSnapshot(snapshotData);
if (viewerIsAdmin) {
const [historyResponse, actionsResponse] = await Promise.all([
authFetch(`${baseUrl}/requests/${requestId}/history?limit=10`),
authFetch(`${baseUrl}/requests/${requestId}/actions?limit=10`),
]);
if (historyResponse.ok) {
const historyData = await historyResponse.json();
if (Array.isArray(historyData.snapshots)) setHistorySnapshots(historyData.snapshots);
}
if (actionsResponse.ok) {
const actionsData = await actionsResponse.json();
if (Array.isArray(actionsData.actions)) setHistoryActions(actionsData.actions);
}
} else {
setHistorySnapshots([]);
setHistoryActions([]);
}
} catch (error) {
console.error(error);
setLoadError(error instanceof Error ? error.message : "Unable to load this request.");
@@ -425,6 +409,37 @@ export default function RequestTimelinePage() {
void load();
}, [requestId, router]);
useEffect(() => {
if (!isAdmin || !requestId) {
setShowDetails(false);
setHistorySnapshots([]);
setHistoryActions([]);
return;
}
const controller = new AbortController();
const loadHistory = async () => {
try {
const baseUrl = getApiBase();
const [historyResponse, actionsResponse] = await Promise.all([
authFetch(`${baseUrl}/requests/${requestId}/history?limit=10`, { signal: controller.signal }),
authFetch(`${baseUrl}/requests/${requestId}/actions?limit=10`, { signal: controller.signal }),
]);
if (historyResponse.ok) {
const data = await historyResponse.json();
if (!controller.signal.aborted && Array.isArray(data.snapshots)) setHistorySnapshots(data.snapshots);
}
if (actionsResponse.ok) {
const data = await actionsResponse.json();
if (!controller.signal.aborted && Array.isArray(data.actions)) setHistoryActions(data.actions);
}
} catch (error) {
if (!controller.signal.aborted) console.error(error);
}
};
void loadHistory();
return () => controller.abort();
}, [isAdmin, requestId]);
useEffect(() => {
if (!getToken() || !requestId) return;
let stopped = false;