Finish the wiring polish: verbosity gate, upload flash, /about links, dead settings slugs

- The Coach verbosity picker is disabled while the concise tone forces
  brief, with a hint saying so, instead of a live control that silently
  does nothing.
- A fresh document upload flashes its new row like the duplicate path
  always did; the settle decision is a pure exported helper with a test.
- /about gains its two inbound links: the /privacy footer and a
  Settings→About credits row (the GeoLite2 EULA wants the attribution
  reachable from the running app).
- The eight settings slugs shadowed by permanent redirects (the seven
  layout children + sharing) leave the slug registry: no more
  prerendering unreachable pages, no dead component wirings, and the
  SectionPlaceholder fallback that could never render is gone. The
  redirects, section components, and layout-hub subpages are unchanged.
This commit is contained in:
Marc-André Bombeck committed 2026-08-14 05:31:33 +02:00
1 parent 3b1314d1b0
commit 542ca91be5
25 files changed
+181 -223

No files matched your search

@@ -0,0 +1,44 @@
/**
* The upload manager's settle decision (§3.2 + the fresh-upload flash).
*
* Watched red: with `settleDecision` returning `flashId: null` on the
* non-duplicate branch (the pre-v1.37.19 behaviour — only the duplicate
* path flashed the row), the fresh-upload case below fails. A fresh upload
* used to give no feedback beyond its queue row disappearing.
*/
import { describe, expect, it } from "vitest";
import { settleDecision } from "../use-document-upload";
import type { UploadResult } from "../vault-utils";
function success(duplicate: boolean): UploadResult {
return {
ok: true,
duplicate,
document: { id: "doc-1" },
} as unknown as UploadResult;
}
describe("settleDecision", () => {
it("flashes the NEW row on a fresh upload (no toast)", () => {
expect(settleDecision(success(false))).toEqual({
removeFromQueue: true,
toastDuplicate: false,
flashId: "doc-1",
});
});
it("flashes the EXISTING row and toasts on a duplicate", () => {
expect(settleDecision(success(true))).toEqual({
removeFromQueue: true,
toastDuplicate: true,
flashId: "doc-1",
});
});
it("keeps a failed upload in the queue with no flash", () => {
expect(
settleDecision({ ok: false, reason: "generic" } as UploadResult),
).toEqual({ removeFromQueue: false, toastDuplicate: false, flashId: null });
});
});
@@ -136,6 +136,29 @@ function uploadViaXhr(
});
}
/**
* The pure settle decision — what a finished upload does to the UI.
*
* Every success flashes the row (fresh upload → the new row, duplicate →
* the existing row); only the duplicate additionally toasts. Exported for
* the unit test: until v1.37.19 only the duplicate branch flashed, so a
* fresh upload gave no feedback beyond its queue row disappearing.
*/
export function settleDecision(result: UploadResult): {
removeFromQueue: boolean;
toastDuplicate: boolean;
flashId: string | null;
} {
if (!result.ok) {
return { removeFromQueue: false, toastDuplicate: false, flashId: null };
}
return {
removeFromQueue: true,
toastDuplicate: result.duplicate,
flashId: result.document.id,
};
}
export interface DocumentUploadManager {
/** Live queue — uploading entries first-in-first, then error entries. */
items: UploadQueueItem[];
@@ -196,16 +219,17 @@ export function useDocumentUpload(
const settle = useCallback(
(localId: string, result: UploadResult) => {
if (result.ok) {
removeItem(localId);
void invalidateKeys(queryClient, [queryKeys.documents()]);
if (result.duplicate) {
toast.info(t("documents.toast.duplicate"));
flashHighlight(result.document.id);
}
if (!result.ok) {
patchItem(localId, { status: "error", failure: result, progress: 0 });
return;
}
patchItem(localId, { status: "error", failure: result, progress: 0 });
const decision = settleDecision(result);
removeItem(localId);
void invalidateKeys(queryClient, [queryKeys.documents()]);
if (decision.toastDuplicate) {
toast.info(t("documents.toast.duplicate"));
}
if (decision.flashId) flashHighlight(decision.flashId);
},
[flashHighlight, patchItem, queryClient, removeItem, t],
);