mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(coach): drop the search field from the conversations panel
The panel lists the history grouped by recency with load-more, rename and delete; title search stays on the full /coach/conversations page.
This commit is contained in:
1 parent
6cea4be2a1
commit
b6ce54e2c6
5 files changed
+18
-40
No files matched your search
@@ -472,7 +472,7 @@ test.describe("Coach page frame", () => {
|
||||
expect(Math.abs(closed.composer - open.composer)).toBeLessThanOrEqual(1);
|
||||
// Closed, nothing inside the panel is reachable.
|
||||
await expect(
|
||||
panel(page).locator('[data-slot="coach-history-search"]'),
|
||||
panel(page).locator('[data-slot="coach-history-select"]').first(),
|
||||
).not.toBeInViewport();
|
||||
await expectNoSidewaysScroll(page);
|
||||
await shot(page, testInfo, `coach-frame-${width}-closed`);
|
||||
@@ -484,7 +484,10 @@ test.describe("Coach page frame", () => {
|
||||
// Open again, then Escape from inside closes it, focus to the toggle.
|
||||
await toggle(page).click();
|
||||
await expect(panel(page)).toHaveAttribute("data-state", "open");
|
||||
await panel(page).locator('[data-slot="coach-history-search"]').focus();
|
||||
await panel(page)
|
||||
.locator('[data-slot="coach-history-select"]')
|
||||
.first()
|
||||
.focus();
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(panel(page)).toHaveAttribute("data-state", "closed");
|
||||
await expect(toggle(page)).toBeFocused();
|
||||
|
||||
@@ -141,15 +141,15 @@ describe("<HistoryRail>", () => {
|
||||
expect(html).toContain("Your conversations will appear here.");
|
||||
});
|
||||
|
||||
it("mounts the search input with the localised placeholder", () => {
|
||||
it("carries no search field (search lives on the full conversations page)", () => {
|
||||
const client = makeClientWithConversations(samplePage);
|
||||
const html = render(
|
||||
<HistoryRail activeId={null} onSelect={() => {}} />,
|
||||
client,
|
||||
);
|
||||
expect(html).toMatch(
|
||||
/data-slot="coach-history-search"[^>]*placeholder="Search conversations…"/,
|
||||
);
|
||||
expect(html).toContain('data-slot="coach-history-list"');
|
||||
expect(html).not.toContain('type="search"');
|
||||
expect(html).not.toContain("Search conversations");
|
||||
});
|
||||
|
||||
it("uses German strings when locale is 'de'", () => {
|
||||
|
||||
@@ -176,7 +176,7 @@ export function CoachDrawer({
|
||||
|
||||
// v1.21.4 (Coach-UI B) — the drawer's "Conversations" affordance now hands
|
||||
// off to the dedicated conversation-history page (`/coach/conversations`),
|
||||
// which renders the search + recency-grouped list as a sibling of the
|
||||
// which renders the recency-grouped list as a sibling of the
|
||||
// Coach page. The former `?view=conversations` in-page slide-in drawer is
|
||||
// gone; selecting a row there routes back to `/coach?c=<id>`.
|
||||
const handleOpenConversations = useCallback(() => {
|
||||
|
||||
@@ -366,9 +366,8 @@ export function ConversationsPanel({
|
||||
data-slot="coach-conversations-panel"
|
||||
className="w-[22rem] max-w-[90vw] gap-0 p-0 sm:max-w-[22rem]"
|
||||
onOpenAutoFocus={(event) => {
|
||||
// Land on the sheet itself, not its first control: on a phone
|
||||
// focusing the search field would raise the keyboard over the
|
||||
// list the person opened the sheet to read.
|
||||
// Land on the sheet itself, not its first control, so opening
|
||||
// the sheet does not paint a focus ring on a header button.
|
||||
event.preventDefault();
|
||||
(event.currentTarget as HTMLElement | null)?.focus();
|
||||
}}
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Paperclip,
|
||||
Pencil,
|
||||
RotateCw,
|
||||
Search,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
@@ -20,10 +19,8 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { useDebouncedValue } from "@/hooks/use-debounced-value";
|
||||
import { useLoadMoreSentinel } from "@/hooks/use-load-more-sentinel";
|
||||
import type { CoachConversationDTO } from "@/lib/ai/coach/types";
|
||||
import { groupConversationsByRecency } from "@/lib/insights/coach-conversation-groups";
|
||||
@@ -36,14 +33,14 @@ import {
|
||||
} from "./use-coach";
|
||||
|
||||
/**
|
||||
* The Coach's conversation list: search, the full history grouped by recency
|
||||
* The Coach's conversation list: the full history grouped by recency
|
||||
* (Today / Yesterday / This week / Earlier), and one row per conversation with
|
||||
* a "⋯" menu for Rename and Delete.
|
||||
*
|
||||
* It fills the Coach page's conversations panel and the drawer's history
|
||||
* tray. The history is cursor-paginated (`useInfiniteQuery`, loaded as the
|
||||
* list scrolls near its end) and searched server-side on the title, debounced
|
||||
* 200 ms.
|
||||
* list scrolls near its end). Title search lives on the standalone
|
||||
* `/coach/conversations` page, not here.
|
||||
*
|
||||
* Rename edits the title in place (`ConversationRename`, Enter saves, Escape
|
||||
* cancels without closing the panel around it) and returns focus to the row's
|
||||
@@ -88,8 +85,6 @@ export function HistoryRail({
|
||||
onUndoDeleteActive,
|
||||
}: HistoryRailProps) {
|
||||
const { t } = useTranslations();
|
||||
const [filter, setFilter] = useState<string>("");
|
||||
const debouncedFilter = useDebouncedValue(filter, 200);
|
||||
const {
|
||||
conversations,
|
||||
isLoading,
|
||||
@@ -98,19 +93,17 @@ export function HistoryRail({
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
} = useCoachConversationHistory({ search: debouncedFilter });
|
||||
} = useCoachConversationHistory();
|
||||
const ownDeletion = useDeleteCoachConversationWithUndo();
|
||||
const { pendingDeleteIds, requestDelete, undoDelete } =
|
||||
deletion ?? ownDeletion;
|
||||
|
||||
// `pendingDeleteIds` hides rows inside the undo window; the server-side
|
||||
// search already resolved everything else.
|
||||
// `pendingDeleteIds` hides rows inside the undo window.
|
||||
const visible = useMemo(
|
||||
() => conversations.filter((c) => !pendingDeleteIds.has(c.id)),
|
||||
[conversations, pendingDeleteIds],
|
||||
);
|
||||
const groups = useMemo(() => groupConversationsByRecency(visible), [visible]);
|
||||
const isSearching = debouncedFilter.trim().length > 0;
|
||||
|
||||
// A `useState`-backed callback ref so the sentinel hook re-runs once the
|
||||
// scroll container exists (null on the very first render).
|
||||
@@ -153,21 +146,6 @@ export function HistoryRail({
|
||||
{t("insights.coach.historyTitle")}
|
||||
</h3>
|
||||
)}
|
||||
<div className="relative w-full">
|
||||
<Search
|
||||
aria-hidden="true"
|
||||
className="text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2"
|
||||
/>
|
||||
<Input
|
||||
type="search"
|
||||
value={filter}
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
placeholder={t("insights.coach.historySearchPlaceholder")}
|
||||
aria-label={t("insights.coach.historySearchPlaceholder")}
|
||||
data-slot="coach-history-search"
|
||||
className="h-10 w-full pl-9"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={setListNode}
|
||||
data-slot="coach-history-list"
|
||||
@@ -202,9 +180,7 @@ export function HistoryRail({
|
||||
data-slot="coach-history-empty"
|
||||
className="text-muted-foreground px-2 py-3 text-sm leading-relaxed"
|
||||
>
|
||||
{isSearching
|
||||
? t("insights.coach.historySearchEmpty")
|
||||
: t("insights.coach.historyEmpty")}
|
||||
{t("insights.coach.historyEmpty")}
|
||||
</p>
|
||||
) : (
|
||||
groups.map((group) => (
|
||||
|
||||
Reference in new issue
Block a user