Merge branch 'fix/v1437-data' into release/v1.43.7

This commit is contained in:
Marc-André Bombeck committed 2026-10-20 08:12:32 +02:00
commit dee6a27ae4
100 files changed
+2314 -506

No files matched your search

@@ -22,6 +22,8 @@ import {
resolveBulkShareDocuments,
SHARE_LINK_MAX_DOCUMENTS,
vaultFiltersToSearch,
STAGED_ATTACHMENT_POLL_MS,
stagedAttachmentPollInterval,
} from "../vault-utils";
function doc(overrides: Partial<InboundDocumentDto> = {}): InboundDocumentDto {
@@ -541,3 +543,36 @@ describe("buildProcedureChoices", () => {
expect(buildProcedureChoices([], null, labels)).toEqual([]);
});
});
describe("stagedAttachmentPollInterval", () => {
const now = Date.parse("2026-10-20T10:00:00Z");
const at = (msAgo: number) => new Date(now - msAgo).toISOString();
it("polls a fresh upload until its index lands", () => {
expect(
stagedAttachmentPollInterval(
{ createdAt: at(30_000), hasContentIndex: false },
now,
),
).toBe(STAGED_ATTACHMENT_POLL_MS);
expect(
stagedAttachmentPollInterval(
{ createdAt: at(30_000), hasContentIndex: true },
now,
),
).toBe(false);
});
it("stops polling a document that is never indexed once the window passes", () => {
expect(
stagedAttachmentPollInterval(
{ createdAt: at(RECENT_UPLOAD_WINDOW_MS + 1), hasContentIndex: false },
now,
),
).toBe(false);
});
it("does not poll before the detail has loaded", () => {
expect(stagedAttachmentPollInterval(undefined, now)).toBe(false);
});
});
+20
View File
@@ -167,6 +167,26 @@ export function isDocumentProcessing(
return !doc.hasContentIndex && isRecentlyUploaded(doc.createdAt, now);
}
/**
* How often the Coach polls a staged attachment for its content index.
*
* The same "still processing" question the vault list asks, so the same
* bounded window answers it: a document that will never be indexed (an
* unsupported format, indexing disabled) used to be polled every 2.5 s for as
* long as the Coach stayed open. `false` once the index exists or the window
* has passed.
*/
export const STAGED_ATTACHMENT_POLL_MS = 2_500;
export function stagedAttachmentPollInterval(
doc: Pick<InboundDocumentDto, "createdAt" | "hasContentIndex"> | undefined,
now: number = Date.now(),
): number | false {
return doc && isDocumentProcessing(doc, now)
? STAGED_ATTACHMENT_POLL_MS
: false;
}
/**
* Whether ANY document in the list is still inside its processing window —
* drives the vault list query's poll-while-indexing `refetchInterval`.
@@ -21,7 +21,10 @@ import type { CoachLaunchScope } from "@/lib/insights/coach-launch-context";
import type { InboundDocumentDetailDto } from "@/lib/validations/inbound-documents";
import { MAX_COACH_ATTACHMENTS } from "@/lib/validations/inbound-documents";
import { useModuleEnabled } from "@/hooks/use-module-enabled";
import { parseUploadResponse } from "@/components/documents/vault-utils";
import {
parseUploadResponse,
stagedAttachmentPollInterval,
} from "@/components/documents/vault-utils";
import {
metricScopeLabelFallback,
scopeSourceMetricLabelKey,
@@ -399,10 +402,11 @@ export function CoachConversation({
queryFn: async () =>
apiGet<InboundDocumentDetailDto>(`/api/documents/inbound/${id}`),
staleTime: 15_000,
// Bounded by the upload processing window, like the vault list: a
// document that is never indexed stops being polled.
refetchInterval: (query: {
state: { data?: InboundDocumentDetailDto };
}) =>
query.state.data && !query.state.data.hasContentIndex ? 2500 : false,
}) => stagedAttachmentPollInterval(query.state.data),
})),
});
const stagedDetailById = new Map<
@@ -368,7 +368,12 @@ export function AccountSection() {
setFieldErrors(describeRejectedProfileFields(json?.details?.issues, t));
if (errorCode === "profile.update.nothingSaved" && field) {
setSaveMsg({ key: "settings.profileNothingSaved", params: { field } });
} else if (errorCode) {
} else if (
errorCode &&
t(`apiErrors.${errorCode}`) !== `apiErrors.${errorCode}`
) {
// Only a code with a sentence of its own: every refusal carries a
// code now, and the generic `http.*` family has no translation.
setSaveMsg({ key: `apiErrors.${errorCode}` });
} else {
setSaveMsg({ key: "settings.savingError" });