mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
Enforce the reminder nag cap, compare the disclaimer version, drop the unread analytics columns
- The daily Coach sweep now charges each ignored day against surfaceCount and auto-dismisses at three — the cap the schema comment promised since v1.22 but nothing enforced. - The welcome gate compares the stored disclaimer acknowledgment version against the current one, so revised disclaimer text re-prompts; /api/auth/me carries the version for the client. - slope90 and anomalyCount leave the analytics wire: slope90 had no reader anywhere (computed on every request, in SQL twice), and the slim path could never compute anomalyCount (it always reported 0). anomalyCount stays as an in-process field on summarize() for the insights feature builder, which is its only real consumer.
This commit is contained in:
1 parent
6b3a5a4182
commit
3b1314d1b0
29 files changed
+214
-104
No files matched your search
@@ -21,7 +21,7 @@ export interface RangeDisplayConfig {
|
||||
* mean / median / avg7 / avg30 / avg30LastMonth / avg30LastYear) divides
|
||||
* by 60; the slope tuples scale their `slope` (per-day rate) by the same
|
||||
* factor so the trend arrow stays consistent; count / direction /
|
||||
* confidence / anomalyCount are unit-free and pass through.
|
||||
* confidence are unit-free and pass through.
|
||||
*/
|
||||
export function toHoursSummary(s: DataSummary): DataSummary {
|
||||
const h = (v: number | null | undefined): number | null =>
|
||||
@@ -43,7 +43,6 @@ export function toHoursSummary(s: DataSummary): DataSummary {
|
||||
avg30LastYear: h(s.avg30LastYear),
|
||||
slope7: scaleSlope(s.slope7),
|
||||
slope30: scaleSlope(s.slope30),
|
||||
slope90: scaleSlope(s.slope90),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ function summary(over: Partial<DataSummary>): DataSummary {
|
||||
avg30: null,
|
||||
slope7: null,
|
||||
slope30: null,
|
||||
slope90: null,
|
||||
anomalyCount: 0,
|
||||
...over,
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@ describe("<InsightsEditMode> — detail-page manager retired (v1.15.20)", () =>
|
||||
expect(html).toContain(
|
||||
'href="/settings/layout/insights#insights-pill-order"',
|
||||
);
|
||||
expect(html).toContain("Manage pills & detail pages in Settings");
|
||||
expect(html).toContain("Manage chips & detail pages in Settings");
|
||||
});
|
||||
|
||||
it("still renders the section rows with their eye toggles", () => {
|
||||
|
||||
@@ -25,7 +25,6 @@ function fakeSummary(count: number): DataSummary {
|
||||
avg30: null,
|
||||
slope7: null,
|
||||
slope30: null,
|
||||
slope90: null,
|
||||
anomalyCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ function fakeSummary(count: number): DataSummary {
|
||||
avg30: null,
|
||||
slope7: null,
|
||||
slope30: null,
|
||||
slope90: null,
|
||||
anomalyCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ const populated: DataSummary = {
|
||||
avg30: 70,
|
||||
slope7: null,
|
||||
slope30: null,
|
||||
slope90: null,
|
||||
anomalyCount: 0,
|
||||
avg30LastMonth: null,
|
||||
avg30LastYear: null,
|
||||
|
||||
@@ -20,7 +20,6 @@ function summary(count: number): DataSummary {
|
||||
avg30: null,
|
||||
slope7: null,
|
||||
slope30: null,
|
||||
slope90: null,
|
||||
anomalyCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@ import { cn } from "@/lib/utils";
|
||||
import { apiPost } from "@/lib/api/api-fetch";
|
||||
import { localizedApiError } from "@/lib/api/localized-error";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import { DISCLAIMER_VERSION } from "@/lib/onboarding/disclaimer";
|
||||
import {
|
||||
DISCLAIMER_VERSION,
|
||||
isDisclaimerAcknowledgmentCurrent,
|
||||
} from "@/lib/onboarding/disclaimer";
|
||||
|
||||
/**
|
||||
* v1.4.25 W14b-Content — onboarding welcome carousel.
|
||||
@@ -87,11 +90,15 @@ export function WelcomeCarousel() {
|
||||
const [active, setActive] = useState(0);
|
||||
const [advancing, setAdvancing] = useState(false);
|
||||
// v1.18.6 (DISC-02) — the one-time medical-disclaimer acknowledgment gates
|
||||
// "Get started". Pre-checked for an account that already acknowledged (a
|
||||
// re-walk of step 0) so a returning user is not re-asked.
|
||||
const [acknowledged, setAcknowledged] = useState(
|
||||
() => user?.disclaimerAcknowledgedAt != null,
|
||||
// "Get started". Pre-checked only when the account acknowledged the
|
||||
// CURRENT disclaimer version (a re-walk of step 0), so a returning user is
|
||||
// not re-asked — but a revised disclaimer re-prompts instead of riding an
|
||||
// acknowledgment given for older text.
|
||||
const disclaimerCurrent = isDisclaimerAcknowledgmentCurrent(
|
||||
user?.disclaimerAcknowledgedAt,
|
||||
user?.disclaimerAcknowledgedVersion,
|
||||
);
|
||||
const [acknowledged, setAcknowledged] = useState(() => disclaimerCurrent);
|
||||
|
||||
// Track which slide is centred in the rail. We observe each slide
|
||||
// with an IntersectionObserver — simpler and more reliable than
|
||||
@@ -146,9 +153,10 @@ export function WelcomeCarousel() {
|
||||
if (advancing || !acknowledged) return;
|
||||
setAdvancing(true);
|
||||
try {
|
||||
// v1.18.6 (DISC-02) — record the one-time acknowledgment before
|
||||
// advancing. Skipped when the account already acknowledged (re-walk).
|
||||
if (user?.disclaimerAcknowledgedAt == null) {
|
||||
// v1.18.6 (DISC-02) — record the acknowledgment before advancing.
|
||||
// Skipped only when the account already acknowledged the CURRENT
|
||||
// version (re-walk); an older-version acknowledgment re-records.
|
||||
if (!disclaimerCurrent) {
|
||||
await apiPost("/api/onboarding/disclaimer", {
|
||||
version: DISCLAIMER_VERSION,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user