mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
Merge branch 'fix/375-generated-narratives' into release/v1.25.0
This commit is contained in:
commit
c0a91dbbcd
19 files changed
+643
-19
No files matched your search
@@ -238,4 +238,55 @@ describe("<DailyBriefing>", () => {
|
||||
expect(html).toContain('data-slot="daily-briefing-findings"');
|
||||
expect(html).not.toContain('data-slot="daily-briefing-no-provider"');
|
||||
});
|
||||
|
||||
// v1.25 — generation-failed affordances. The briefing keeps its last good
|
||||
// text; the card adds an honest "couldn't refresh" footer hint on a held
|
||||
// briefing and a "couldn't generate — retry" empty state when there is none.
|
||||
it("renders the refresh-failed footer hint on a held briefing", () => {
|
||||
const html = render(
|
||||
<DailyBriefing
|
||||
briefing={baseBriefing}
|
||||
onRegenerate={() => {}}
|
||||
generationFailed
|
||||
/>,
|
||||
);
|
||||
expect(html).toContain('data-slot="daily-briefing-refresh-failed"');
|
||||
// The rendered apostrophe is HTML-escaped; match the unambiguous tail.
|
||||
expect(html).toContain("refresh the briefing");
|
||||
expect(html).toContain('data-slot="daily-briefing-refresh-failed-retry"');
|
||||
});
|
||||
|
||||
it("does NOT render the refresh-failed hint when generation did not fail", () => {
|
||||
const html = render(
|
||||
<DailyBriefing briefing={baseBriefing} onRegenerate={() => {}} />,
|
||||
);
|
||||
expect(html).not.toContain('data-slot="daily-briefing-refresh-failed"');
|
||||
});
|
||||
|
||||
it("suppresses the refresh-failed hint when no provider is connected", () => {
|
||||
const html = render(
|
||||
<DailyBriefing
|
||||
briefing={baseBriefing}
|
||||
onRegenerate={() => {}}
|
||||
generationFailed
|
||||
noProviderStale
|
||||
/>,
|
||||
);
|
||||
expect(html).not.toContain('data-slot="daily-briefing-refresh-failed"');
|
||||
expect(html).toContain('data-slot="daily-briefing-stale-no-provider"');
|
||||
});
|
||||
|
||||
it("renders the 'couldn't generate' empty state with a retry CTA when failed and empty", () => {
|
||||
const html = render(
|
||||
<DailyBriefing
|
||||
briefing={null}
|
||||
onRegenerate={() => {}}
|
||||
generationFailed
|
||||
/>,
|
||||
);
|
||||
// The rendered apostrophe is HTML-escaped; match the unambiguous tail.
|
||||
expect(html).toContain("generate the briefing");
|
||||
expect(html).toMatch(/data-slot="daily-briefing-empty-cta"/);
|
||||
expect(html).toContain("Retry");
|
||||
});
|
||||
});
|
||||
@@ -119,6 +119,14 @@ interface DailyBriefingProps {
|
||||
* the staleness reads as intentional, not as a live report.
|
||||
*/
|
||||
noProviderStale?: boolean;
|
||||
/**
|
||||
* v1.25 — the most recent generation attempt failed (provider timeout /
|
||||
* error). The briefing keeps its last good text, so this never blanks a
|
||||
* shown card; it only adds an honest "couldn't refresh" hint to the footer
|
||||
* of a held briefing, and — when there is no briefing to show — swaps the
|
||||
* generic empty state for a "couldn't generate" one whose CTA retries.
|
||||
*/
|
||||
generationFailed?: boolean;
|
||||
/**
|
||||
* Optional slot for a meta control mounted in the card header — the
|
||||
* comparison toggle migrates here from the hero in commit 5.
|
||||
@@ -349,6 +357,7 @@ export function DailyBriefing({
|
||||
regenerating = false,
|
||||
noProvider = false,
|
||||
noProviderStale = false,
|
||||
generationFailed = false,
|
||||
metaSlot,
|
||||
memory = null,
|
||||
}: DailyBriefingProps) {
|
||||
@@ -468,7 +477,7 @@ export function DailyBriefing({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{(updatedAt || noProviderStale) && (
|
||||
{(updatedAt || noProviderStale || generationFailed) && (
|
||||
<div className="border-border/60 space-y-1.5 border-t pt-3">
|
||||
{updatedAt && (
|
||||
<p
|
||||
@@ -505,6 +514,32 @@ export function DailyBriefing({
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
{/* v1.25 — the held briefing is shown, but the last refresh
|
||||
attempt failed (provider timeout / error). State it plainly
|
||||
and offer a retry so the staleness reads as held, not
|
||||
current. Suppressed when no provider is configured (that
|
||||
hint owns the footer) — a retry would be futile there. */}
|
||||
{generationFailed && !noProviderStale && (
|
||||
<p
|
||||
data-slot="daily-briefing-refresh-failed"
|
||||
className="text-muted-foreground flex flex-wrap items-center justify-end gap-x-1.5 gap-y-1 text-right text-xs"
|
||||
>
|
||||
<span>
|
||||
{t("insights.dailyBriefing.refreshFailedHint")}
|
||||
</span>
|
||||
{onRegenerate && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRegenerate}
|
||||
disabled={regenerating}
|
||||
data-slot="daily-briefing-refresh-failed-retry"
|
||||
className="text-foreground/80 hover:text-foreground underline underline-offset-2 disabled:opacity-60"
|
||||
>
|
||||
{t("insights.dailyBriefing.retryAction")}
|
||||
</button>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* v1.21.0 (C4 H2) — hand off to the Coach for the whole
|
||||
@@ -542,8 +577,20 @@ export function DailyBriefing({
|
||||
data-slot="daily-briefing-empty"
|
||||
variant="plain"
|
||||
icon={<FileText className="size-5" />}
|
||||
title={t("insights.dailyBriefing.emptyTitle")}
|
||||
description={t("insights.dailyBriefing.emptyDescription")}
|
||||
// v1.25 — when the last attempt FAILED and there is no last good
|
||||
// text to fall back on, say so honestly ("couldn't generate")
|
||||
// rather than the generic "no briefing yet". The CTA below is the
|
||||
// same explicit regenerate — it retries the failed generation.
|
||||
title={
|
||||
generationFailed
|
||||
? t("insights.dailyBriefing.failedTitle")
|
||||
: t("insights.dailyBriefing.emptyTitle")
|
||||
}
|
||||
description={
|
||||
generationFailed
|
||||
? t("insights.dailyBriefing.failedDescription")
|
||||
: t("insights.dailyBriefing.emptyDescription")
|
||||
}
|
||||
action={
|
||||
onRegenerate ? (
|
||||
// v1.4.28 BK-M2 — switch the empty-state CTA from
|
||||
@@ -564,7 +611,9 @@ export function DailyBriefing({
|
||||
<span>
|
||||
{regenerating
|
||||
? t("insights.heroRegenerating")
|
||||
: t("insights.dailyBriefing.emptyAction")}
|
||||
: generationFailed
|
||||
? t("insights.dailyBriefing.retryAction")
|
||||
: t("insights.dailyBriefing.emptyAction")}
|
||||
</span>
|
||||
</Button>
|
||||
) : null
|
||||
|
||||
@@ -71,6 +71,15 @@ export interface InsightAdvisorPayload {
|
||||
* predates the field — treated as "provider present" (no hint).
|
||||
*/
|
||||
hasProvider?: boolean;
|
||||
/**
|
||||
* v1.25 — true when the most recent generation attempt failed (a failure
|
||||
* marker newer than the last successful generation). The briefing keeps its
|
||||
* last good text on failure, so this is the honest signal that a shown
|
||||
* briefing is held, or — with no last good text — that the empty state
|
||||
* should read "couldn't generate" with a retry rather than the generic one.
|
||||
* Absent on a pre-field cached payload → treated as "not failed" (no hint).
|
||||
*/
|
||||
generationFailed?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,6 +288,13 @@ export interface UseInsightsAdvisorResult {
|
||||
* or an unsettled query.
|
||||
*/
|
||||
hasProvider: boolean;
|
||||
/**
|
||||
* v1.25 — true when the GET reported the last generation attempt failed.
|
||||
* Pairs a shown-but-held briefing with a discreet "couldn't refresh" hint,
|
||||
* and swaps the generic empty state for a "couldn't generate — retry" one
|
||||
* when there is no last good text. Defaults false (no hint) when absent.
|
||||
*/
|
||||
generationFailed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,5 +380,8 @@ export function useInsightsAdvisorQuery(
|
||||
// assume a provider is present so a transient unknown never flashes a
|
||||
// false "no provider" hint.
|
||||
hasProvider: query.data?.payload?.hasProvider ?? true,
|
||||
// Absent (pre-field cached payload or unsettled query) → not failed, so a
|
||||
// transient unknown never flashes a false "couldn't refresh" hint.
|
||||
generationFailed: query.data?.payload?.generationFailed ?? false,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user