mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
fix(documents): refresh what confirmed facts and a take-over create
Confirming a document's facts creates medications, conditions and lab values, and the profile take-over creates a condition or an allergy. Both evicted only the roots of those lists, so the dashboard and the Today digest showed the old medication set until the 120 s poll, the day and timeline kept the old state for five minutes, and adherence and nutrient reads were not evicted at all. Both now spread the bundles of what they create, and the condition paths pair them with the inactive refetch of the daily reads.
This commit is contained in:
1 parent
7f54a820b0
commit
971d5d32cc
4 files changed
+90
-29
No files matched your search
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Confirming a document's facts creates medications, conditions and lab
|
||||
* values. The confirm used to evict only the roots of those lists, so the
|
||||
* dashboard, the Today digest, adherence, nutrients, the day and the timeline
|
||||
* kept showing the record without them.
|
||||
*/
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { invalidateConfirmedFactReads } from "../use-document-facts";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
|
||||
const client = new QueryClient();
|
||||
|
||||
afterEach(() => client.clear());
|
||||
|
||||
describe("invalidateConfirmedFactReads", () => {
|
||||
it.each([
|
||||
["the dashboard snapshot", queryKeys.dashboardSnapshot()],
|
||||
["the Today digest", queryKeys.dailyDigest()],
|
||||
["the nutrient reads", queryKeys.nutrientsRoot()],
|
||||
["an open day", queryKeys.day("2026-11-10")],
|
||||
["the timeline", queryKeys.timelineRoot()],
|
||||
["the lab results", queryKeys.labResults()],
|
||||
["the condition journal", queryKeys.illness()],
|
||||
["the document", queryKeys.inboundDocument("doc-1")],
|
||||
])("marks %s stale", async (_label, key) => {
|
||||
client.setQueryData(key, { seeded: true });
|
||||
|
||||
await invalidateConfirmedFactReads(client, "doc-1");
|
||||
|
||||
expect(client.getQueryState(key)?.isInvalidated).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,11 @@
|
||||
* approve/reject decisions the person made; a fact without a decision stays
|
||||
* pending on the server.
|
||||
*/
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
type QueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
import { resolveAiRun, useAiRunPhase } from "@/hooks/use-ai-run";
|
||||
import { apiPatch, apiPost } from "@/lib/api/api-fetch";
|
||||
@@ -21,7 +25,15 @@ import type {
|
||||
AiRunAccepted,
|
||||
DocumentExtractRunResult,
|
||||
} from "@/lib/documents/ai-runs/types";
|
||||
import { invalidateKeys, queryKeys } from "@/lib/query-keys";
|
||||
import {
|
||||
documentDependentKeys,
|
||||
illnessDependentKeys,
|
||||
invalidateKeys,
|
||||
labDependentKeys,
|
||||
medicationDependentKeys,
|
||||
queryKeys,
|
||||
refetchInactiveDailyReads,
|
||||
} from "@/lib/query-keys";
|
||||
import type {
|
||||
ExtractedFactDto,
|
||||
InboundFactEdit,
|
||||
@@ -101,11 +113,32 @@ export function useEditFact() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Every read a confirm can touch: the medications, conditions and lab values
|
||||
* it creates, with what derives from them, and the document itself (status
|
||||
* and fact states). A new medication or condition also reaches the dashboard
|
||||
* and the Today digest, which are unmounted while the person reviews.
|
||||
*/
|
||||
export function invalidateConfirmedFactReads(
|
||||
queryClient: QueryClient,
|
||||
documentId: string,
|
||||
) {
|
||||
return Promise.all([
|
||||
invalidateKeys(queryClient, [
|
||||
...medicationDependentKeys,
|
||||
...illnessDependentKeys,
|
||||
...labDependentKeys,
|
||||
...documentDependentKeys,
|
||||
queryKeys.inboundDocument(documentId),
|
||||
]),
|
||||
refetchInactiveDailyReads(queryClient),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the review decisions. Approved facts land in their structured
|
||||
* stores (labs / conditions / medications) through the server's existing
|
||||
* field-by-field commit; rejected facts are discarded. Invalidates every
|
||||
* read the commit can touch, plus the document itself (status + fact states).
|
||||
* field-by-field commit; rejected facts are discarded.
|
||||
*/
|
||||
export function useConfirmFacts() {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -127,15 +160,7 @@ export function useConfirmFacts() {
|
||||
`/api/documents/inbound/${documentId}/confirm`,
|
||||
{ decisions },
|
||||
),
|
||||
onSuccess: (_data, { documentId }) => {
|
||||
void invalidateKeys(queryClient, [
|
||||
queryKeys.inboundDocument(documentId),
|
||||
queryKeys.documents(),
|
||||
queryKeys.labResults(),
|
||||
queryKeys.biomarkers(),
|
||||
queryKeys.illness(),
|
||||
queryKeys.medications(),
|
||||
]);
|
||||
},
|
||||
onSuccess: (_data, { documentId }) =>
|
||||
void invalidateConfirmedFactReads(queryClient, documentId),
|
||||
});
|
||||
}
|
||||
@@ -31,7 +31,13 @@ import { withBaseToken } from "@/lib/api/optimistic-token";
|
||||
import { showErrorToast } from "@/lib/errors/error-toast";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import type { LifeCircumstancesDto } from "@/lib/profile/life-circumstances";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import {
|
||||
allergyDependentKeys,
|
||||
illnessDependentKeys,
|
||||
invalidateKeys,
|
||||
queryKeys,
|
||||
refetchInactiveDailyReads,
|
||||
} from "@/lib/query-keys";
|
||||
import { dateOnlyAtNoonUtc, isCalendarDateKey } from "@/lib/tz/date-only";
|
||||
|
||||
interface AboutMeData {
|
||||
@@ -66,13 +72,12 @@ export function FreeTextTakeOver({
|
||||
const afterCreate = () => {
|
||||
setSaved(true);
|
||||
toastWrittenOutcome("success", t("records.life.takeOver.savedToast"));
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.lifeCircumstances(),
|
||||
});
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey:
|
||||
field === "conditions" ? queryKeys.illness() : queryKeys.allergies(),
|
||||
});
|
||||
void invalidateKeys(queryClient, [
|
||||
queryKeys.lifeCircumstances(),
|
||||
...(field === "conditions" ? illnessDependentKeys : allergyDependentKeys),
|
||||
]);
|
||||
// A chronic condition frames the dashboard and the Today digest.
|
||||
if (field === "conditions") void refetchInactiveDailyReads(queryClient);
|
||||
};
|
||||
|
||||
const createCondition = useMutation({
|
||||
@@ -103,12 +108,10 @@ export function FreeTextTakeOver({
|
||||
onSuccess: () => {
|
||||
toastWrittenOutcome("success", t("records.life.takeOver.clearedToast"));
|
||||
close();
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.coachAboutMe(),
|
||||
});
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.lifeCircumstances(),
|
||||
});
|
||||
void invalidateKeys(queryClient, [
|
||||
queryKeys.coachAboutMe(),
|
||||
queryKeys.lifeCircumstances(),
|
||||
]);
|
||||
},
|
||||
onError: (error) =>
|
||||
showErrorToast(t("records.life.takeOver.clearError"), { cause: error }),
|
||||
|
||||
Reference in new issue
Block a user