missions: no-store fetch + visible updated-at timestamp on refresh
- api client: cache: no-store so manual Refresh guarantees a fresh server response (was potentially hitting stale HTTP cache). - MissionCanvas: renders "updated HH:MM:SS" next to the refresh button; the timestamp bumps on every successful load so the click is visibly acknowledged even when nothing else on the page changed.
This commit is contained in:
@@ -118,6 +118,7 @@ export function MissionCanvas({
|
|||||||
const [editOpen, setEditOpen] = useState(false);
|
const [editOpen, setEditOpen] = useState(false);
|
||||||
const [deleteBusy, setDeleteBusy] = useState(false);
|
const [deleteBusy, setDeleteBusy] = useState(false);
|
||||||
const [runs, setRuns] = useState<MissionRunSummary[]>([]);
|
const [runs, setRuns] = useState<MissionRunSummary[]>([]);
|
||||||
|
const [lastLoadedAt, setLastLoadedAt] = useState<Date | null>(null);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
if (!selectedId) {
|
if (!selectedId) {
|
||||||
@@ -133,6 +134,7 @@ export function MissionCanvas({
|
|||||||
]);
|
]);
|
||||||
setMission(m);
|
setMission(m);
|
||||||
setRuns(r.runs);
|
setRuns(r.runs);
|
||||||
|
setLastLoadedAt(new Date());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setError(e instanceof Error ? e.message : "load failed");
|
setError(e instanceof Error ? e.message : "load failed");
|
||||||
} finally {
|
} finally {
|
||||||
@@ -424,6 +426,18 @@ export function MissionCanvas({
|
|||||||
{refining ? "Refining…" : "Refine"}
|
{refining ? "Refining…" : "Refine"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{lastLoadedAt && (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: 10,
|
||||||
|
color: "#6a6a72",
|
||||||
|
fontFamily: mono,
|
||||||
|
}}
|
||||||
|
title={lastLoadedAt.toISOString()}
|
||||||
|
>
|
||||||
|
updated {lastLoadedAt.toLocaleTimeString()}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={load}
|
onClick={load}
|
||||||
|
|||||||
@@ -174,7 +174,12 @@ export interface CreateMissionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function api<T>(path: string, init?: RequestInit): Promise<T> {
|
async function api<T>(path: string, init?: RequestInit): Promise<T> {
|
||||||
|
// Missions endpoints are mutable state (statuses transition every
|
||||||
|
// few seconds) so we always bypass the browser cache. The old cache
|
||||||
|
// heuristic could serve a stale response for the manual Refresh
|
||||||
|
// click, making it look like a no-op.
|
||||||
const r = await fetch(path, {
|
const r = await fetch(path, {
|
||||||
|
cache: "no-store",
|
||||||
...init,
|
...init,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|||||||
Reference in New Issue
Block a user