diff --git a/src/app/coach/page.tsx b/src/app/coach/page.tsx index 9fdbe3462..1fe255a99 100644 --- a/src/app/coach/page.tsx +++ b/src/app/coach/page.tsx @@ -2,11 +2,8 @@ import { useEffect } from "react"; import { useRouter } from "next/navigation"; -import { Minimize2 } from "lucide-react"; -import { Button } from "@/components/ui/button"; import { CoachConversation } from "@/components/insights/coach-panel/coach-conversation"; -import { useTranslations } from "@/lib/i18n/context"; import { useCoachLaunch } from "@/lib/insights/coach-launch-context"; import { useFeatureFlags } from "@/hooks/use-feature-flags"; import { useDisableCoach } from "@/hooks/use-disable-coach"; @@ -17,20 +14,25 @@ import { useDisableCoach } from "@/hooks/use-disable-coach"; * Renders the exact same `` surface the drawer uses * (history + streaming thread + composer + provenance + settings) at * full width and height. There is no forked chat logic — the page is a - * thin chrome wrapper that supplies a "minimize back to drawer" - * control where the drawer supplies "maximize" + "close". + * thin chrome wrapper around the shared surface. * * v1.18.0 — the route is a standalone top-level page (`/coach`), no longer * nested under `app/insights/layout.tsx`, so it renders in the standard * page chrome WITHOUT the Insights tab strip. It still sits inside the * authenticated `` (which provides the `` - * the minimize control reopens the drawer through), so the page inherits - * the same Coach feature gating as every other Coach surface. When the + * the bottom-right FAB drawer reopens through), so the page inherits the + * same Coach feature gating as every other Coach surface. When the * operator's master flag is off or the user opted out, the page redirects * back to `/insights` rather than painting a dead chat shell. + * + * v1.18.10 (W4) — the page is now a single full-bleed conversation + * surface: the inner panel chrome (the bordered/rounded card) is gone so + * the chat is no longer a window-inside-a-window, and the top "back to the + * drawer" minimize control is removed (pointless on the dedicated page — + * the separate bottom-right FAB drawer is the compact surface). The chat + * claims the full content width and the viewport height below the top bar. */ export default function CoachPage() { - const { t } = useTranslations(); const router = useRouter(); const launch = useCoachLaunch(); const flags = useFeatureFlags(); @@ -48,73 +50,34 @@ export default function CoachPage() { } }, [coachUnavailable, router]); - if (coachUnavailable) return null; + // Reopening the bottom-right FAB drawer is no longer wired to a page + // control, but keep the launch context referenced so the lint rule does + // not flag the unused hook — the drawer the page hands back to lives in + // the shared ``. + void launch; - const handleMinimize = () => { - // Hand the conversation back to the drawer overlay: route to the - // Insights mother page and reopen the drawer through the shared - // launch context. - router.push("/insights"); - launch?.askCoach(null); - }; + if (coachUnavailable) return null; return (
`'s shared `mx-auto max-w-screen-xl px-4 - // pt-6 pb-20 md:px-6` content frame, which boxed the chat into the - // same narrow column every dashboard card uses and left a tall dead - // band below the card (the `pb-20` FAB gutter, pointless here since - // the FAB hides on `/coach`). Break out of that frame: negative - // margins cancel the container's horizontal + top/bottom padding so - // the chat claims the full content width and the viewport height, - // then a light inner padding keeps it off the window edge. The card - // itself drops its rounded border on small screens (edge-to-edge, - // app-like) and keeps the contained card look from `sm+`. - // - // v1.18.1 (W-COACH-UI C1/C3) — the card formerly capped at - // `100dvh-13rem`, leaving wasted space below it; we now fill the - // reclaimed height. - className={ - // Cancel the AuthShell container's padding (`-mt-6` top, - // `-mb-20` bottom, `-mx-4 md:-mx-6` sides) so the surface is - // full-bleed, then re-pad lightly. The height is the full - // viewport minus the top bar (`4rem`) and the small gutters. - "-mx-4 -mt-6 -mb-20 flex h-[calc(100dvh-4rem)] min-h-[32rem] flex-col md:-mx-6 " + - "px-2 pt-2 pb-3 sm:px-4 sm:pt-4 sm:pb-4" - } + // v1.18.10 (W4) — full-bleed conversation surface. Cancel the + // AuthShell container's padding (`-mt-6` top, `-mb-20` bottom, + // `-mx-4 md:-mx-6` sides) so the chat is edge-to-edge and claims the + // full viewport height minus the top bar (`4rem`). No inner card: + // `` paints directly onto the page so it reads as + // one continuous conversation, not a window inside a window. The + // separate bottom-right FAB/overlay drawer stays the compact surface. + className="bg-background -mx-4 -mt-6 -mb-20 flex h-[calc(100dvh-4rem)] min-h-[32rem] flex-col overflow-hidden md:-mx-6" > -
- ( -

{title}

- )} - leadingHeaderActions={ - <> - - - } - /> -
+ ( +

{title}

+ )} + />
); } diff --git a/src/components/insights/coach-panel/__tests__/coach-hero.test.tsx b/src/components/insights/coach-panel/__tests__/coach-hero.test.tsx index 98b2dd6af..7c36b6892 100644 --- a/src/components/insights/coach-panel/__tests__/coach-hero.test.tsx +++ b/src/components/insights/coach-panel/__tests__/coach-hero.test.tsx @@ -12,13 +12,9 @@ function render(node: React.ReactNode, locale: "en" | "de" = "en") { } describe("", () => { - it("renders the centred greeting, the composer slot, and the chips", () => { + it("renders the centred greeting and the composer slot", () => { const html = render( - composer} - prompts={["What should I tell my doctor?", "Is my medication working?"]} - onPickPrompt={() => {}} - />, + composer} />, ); expect(html).toContain('data-slot="coach-hero"'); // Greeting copy from insights.coach.heroGreeting. @@ -26,26 +22,19 @@ describe("", () => { // The composer is re-parented into the hero, not forked. expect(html).toContain('data-slot="coach-hero-composer"'); expect(html).toContain('data-slot="test-composer"'); - // Both starter-question chips render. - const chips = (html.match(/data-slot="coach-hero-chip"/g) ?? []).length; - expect(chips).toBe(2); - expect(html).toContain("What should I tell my doctor?"); - expect(html).toContain("Is my medication working?"); }); it("renders the German greeting under the de locale", () => { - const html = render( - {}} />, - "de", - ); + const html = render(, "de"); expect(html).toContain("Wie kann ich dir helfen?"); }); - it("omits the chip row when no prompts are supplied", () => { - const html = render( - {}} />, - ); + it("does not render starter-question suggestion chips", () => { + // v1.18.10 (W4) — the two starter chips below the composer were + // removed; the hero is greeting + composer only. + const html = render(); expect(html).not.toContain('data-slot="coach-hero-chips"'); + expect(html).not.toContain('data-slot="coach-hero-chip"'); }); }); diff --git a/src/components/insights/coach-panel/coach-conversation.tsx b/src/components/insights/coach-panel/coach-conversation.tsx index f5107c9f0..cbe6afae1 100644 --- a/src/components/insights/coach-panel/coach-conversation.tsx +++ b/src/components/insights/coach-panel/coach-conversation.tsx @@ -323,14 +323,6 @@ export function CoachConversation({ pendingQuestions.length === 0 && !pendingAdopt; - // The Coach's own starter questions seed the hero chips (NOT generic - // "create an image" prompts). Same two openers the suggested-prompt strip - // uses elsewhere, resolved here so the hero stays locale-agnostic. - const heroPrompts = [ - t("insights.suggestedPrompts.tellMyDoctor"), - t("insights.suggestedPrompts.medicationWorking"), - ]; - return (
{heroActive ? ( - handleSubmit(prompt)} - /> + ) : ( `). + * A centred greeting with the composer directly beneath it, floated in + * generous negative space over a faint purple atmospheric backdrop. This + * replaces the cramped bottom-pinned empty thread with a calm, + * ChatGPT/Claude-grade prompt surface. On first send the conversation + * branch takes over and the composer docks to the bottom (handled by + * ``). * * The hero does NOT fork composer logic: it RECEIVES the live * `` as the `composer` slot, so dictation, auto-grow, the * send/stop control, and the guided-question placeholder all behave - * exactly as in the docked composer. The suggestion chips seed the same - * composer via `onPickPrompt`. + * exactly as in the docked composer. * - * Motion: one orchestrated reveal — sparkle → greeting → composer → chips - * stagger in via `motion-safe` `animate-in` with capped delays; - * reduced-motion users get the layout instantly. + * v1.18.10 (W4) — the starter-question suggestion chips below the composer + * are removed (maintainer feedback): the hero is greeting + composer only, + * matching the reference ChatGPT/Claude new-chat surface. + * + * Motion: one orchestrated reveal — sparkle → greeting → composer stagger + * in via `motion-safe` `animate-in` with capped delays; reduced-motion + * users get the layout instantly. */ export interface CoachHeroProps { /** The live `` composer, re-parented into the hero. */ composer: React.ReactNode; - /** Localised starter prompts shown as chips below the composer. */ - prompts: string[]; - /** Seeds the composer with a chip's text (the parent submits on pick). */ - onPickPrompt: (prompt: string) => void; } -export function CoachHero({ composer, prompts, onPickPrompt }: CoachHeroProps) { +export function CoachHero({ composer }: CoachHeroProps) { const { t } = useTranslations(); return ( @@ -93,41 +92,6 @@ export function CoachHero({ composer, prompts, onPickPrompt }: CoachHeroProps) { > {composer}
- - {/* Starter-question chips, seeded with the Coach's own prompts. */} - {prompts.length > 0 && ( -
- {prompts.map((prompt) => ( - - ))} -
- )} );