mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
feat(medications,coach): the take-now prompt joins the schedule grammar and the coach opens in place
An open take-window renders as the right-aligned value of the next- intake row — same label/value columns as every other card line — instead of a standalone line above the slot; the escalation tiers (late, very late, taken-early) keep the top line. The coach launcher opens the side drawer on the page you are on instead of navigating to the chat page, and grows to the standard FAB size.
This commit is contained in:
1 parent
8b0fbfb522
commit
4493eb2cac
3 files changed
+51
-12
No files matched your search
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Sparkles } from "lucide-react";
|
||||
|
||||
@@ -58,7 +58,6 @@ export function isNudgeUnread(
|
||||
|
||||
export function LayoutCoachFab() {
|
||||
const { t } = useTranslations();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const launch = useCoachLaunch();
|
||||
const flags = useFeatureFlags();
|
||||
@@ -137,7 +136,10 @@ export function LayoutCoachFab() {
|
||||
// Best effort — see above.
|
||||
}
|
||||
}
|
||||
router.push("/insights/coach");
|
||||
// v1.16.11 — open the side drawer in place (the launch context the
|
||||
// layout mount consumes) instead of navigating to the chat page;
|
||||
// the conversation arrives without losing the page underneath.
|
||||
launch?.askCoach();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -157,7 +159,7 @@ export function LayoutCoachFab() {
|
||||
// not `lg:` — keeps the FAB from floating mid-air 768-1023px).
|
||||
// The right inset mirrors the desktop bottom inset so the
|
||||
// corner gap is even.
|
||||
"fixed right-6 z-40 size-12 rounded-full shadow-lg",
|
||||
"fixed right-6 z-40 size-14 rounded-full shadow-lg",
|
||||
"bottom-[calc(env(safe-area-inset-bottom,0px)+5rem)] md:bottom-6",
|
||||
// Dark glyph on the purple/pink gradient — white sat at
|
||||
// ≈2.3:1 against the gradient midpoint; the background token
|
||||
@@ -185,7 +187,7 @@ export function LayoutCoachFab() {
|
||||
"[body:has([data-testid=onboarding-tour])_&]:invisible",
|
||||
)}
|
||||
>
|
||||
<Sparkles className="size-5" aria-hidden="true" />
|
||||
<Sparkles className="size-6" aria-hidden="true" />
|
||||
{unread ? (
|
||||
<span
|
||||
data-slot="coach-fab-unread"
|
||||
|
||||
@@ -154,6 +154,18 @@ export function MedicationCardBody({
|
||||
? t("medications.overdue")
|
||||
: null;
|
||||
|
||||
// v1.16.11 — the plain take-now prompt rides the "next intake" row as
|
||||
// its right-aligned value ("Nächste Einnahme: ✓ Jetzt einnehmen —
|
||||
// 20:00–22:00") instead of a standalone line above the slot, so the
|
||||
// card keeps one label/value grammar whether a window is open or not.
|
||||
// The escalation tiers (late / very-late / taken-early) stay on the
|
||||
// top line — they are interruptions, not schedule values.
|
||||
const takeNowOnNextLine =
|
||||
windowStatus !== null &&
|
||||
windowStatus.status === "in_window" &&
|
||||
windowStatus.takenEarlyDaysAgo == null &&
|
||||
overdueLabel === null;
|
||||
|
||||
return (
|
||||
<Card
|
||||
// The card surface is a constant neutral surface — dose status is never
|
||||
@@ -195,7 +207,7 @@ export function MedicationCardBody({
|
||||
<AlertTriangle className="size-3.5 shrink-0" aria-hidden="true" />
|
||||
{overdueLabel}
|
||||
</p>
|
||||
) : windowStatus ? (
|
||||
) : windowStatus && !takeNowOnNextLine ? (
|
||||
<MedicationStatusPill
|
||||
status={windowStatus.status}
|
||||
windowStart={windowStatus.windowStart}
|
||||
@@ -204,8 +216,23 @@ export function MedicationCardBody({
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* Next / last intake — the two decisive lines, each shown once. */}
|
||||
<MedicationNextLastSlot next={nextLine} last={lastLine} />
|
||||
{/* Next / last intake — the two decisive lines, each shown once.
|
||||
An open take-window renders as the next-intake value. */}
|
||||
<MedicationNextLastSlot
|
||||
next={
|
||||
takeNowOnNextLine ? (
|
||||
<MedicationStatusPill
|
||||
status={windowStatus.status}
|
||||
windowStart={windowStatus.windowStart}
|
||||
windowEnd={windowStatus.windowEnd}
|
||||
inline
|
||||
/>
|
||||
) : (
|
||||
nextLine
|
||||
)
|
||||
}
|
||||
last={lastLine}
|
||||
/>
|
||||
|
||||
{/* Compliance bars — always two rows; constant-height skeleton holds
|
||||
the slot while the query is in flight so the grid row stays even.
|
||||
|
||||
@@ -26,6 +26,14 @@ interface MedicationStatusPillProps {
|
||||
* with the card pill — only the suffix is dropped.
|
||||
*/
|
||||
compact?: boolean;
|
||||
/**
|
||||
* v1.16.11 — inline variant: renders a `<span>` root instead of the
|
||||
* block `<p>` so the pill can sit as the right-aligned VALUE of the
|
||||
* "next intake" row (the take-now prompt lives on that row now, not
|
||||
* on its own line above the slot). A `<p>` inside the row's `<span>`
|
||||
* would be invalid nesting.
|
||||
*/
|
||||
inline?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,12 +50,14 @@ export function MedicationStatusPill({
|
||||
windowEnd,
|
||||
takenEarlyDaysAgo = null,
|
||||
compact = false,
|
||||
inline = false,
|
||||
}: MedicationStatusPillProps) {
|
||||
const { t, locale } = useTranslations();
|
||||
const Root = inline ? "span" : "p";
|
||||
|
||||
if (takenEarlyDaysAgo != null) {
|
||||
return (
|
||||
<p className="text-sm">
|
||||
<Root className="text-sm">
|
||||
<span className="text-muted-foreground inline-flex items-center gap-1 font-medium">
|
||||
<CircleCheck className="size-3.5 shrink-0" aria-hidden="true" />
|
||||
{takenEarlyDaysAgo === 1
|
||||
@@ -60,12 +70,12 @@ export function MedicationStatusPill({
|
||||
— {formatTimeWindowRange(windowStart, windowEnd, locale)}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<p className="text-sm">
|
||||
<Root className="text-sm">
|
||||
<span
|
||||
className={
|
||||
// v1.12.2 — converge the three tiers onto the semantic feedback
|
||||
@@ -101,6 +111,6 @@ export function MedicationStatusPill({
|
||||
— {formatTimeWindowRange(windowStart, windowEnd, locale)}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user