From a692f136ae22ad90fd4b06134431371e6c0ce2a7 Mon Sep 17 00:00:00 2001 From: hrshjswniii Date: Sun, 26 Jul 2026 21:22:52 +0530 Subject: [PATCH] feat: add Favorite Phrases Category Tags & Search --- client/src/components/FavoriteMessages.jsx | 169 ++++++++++++++---- .../src/components/FavoriteMessages.test.js | 27 +++ 2 files changed, 159 insertions(+), 37 deletions(-) create mode 100644 client/src/components/FavoriteMessages.test.js diff --git a/client/src/components/FavoriteMessages.jsx b/client/src/components/FavoriteMessages.jsx index 306e8588..26ff9347 100644 --- a/client/src/components/FavoriteMessages.jsx +++ b/client/src/components/FavoriteMessages.jsx @@ -1,64 +1,159 @@ -import React, { useState } from "react"; -import { Pin, X } from "lucide-react"; +import React, { useMemo, useState } from "react"; +import { Pin, X, Search, Filter } from "lucide-react"; -const FEW_SHOWN = 5; +const FEW_SHOWN = 6; + +export function getPhraseCategory(text = "") { + const str = text.toLowerCase().trim(); + if (str.includes("?") || /^(what|why|how|when|where|who|can|could|would|is|are|do|does)\b/.test(str)) { + return "questions"; + } + if (/(help|need|want|water|food|bathroom|emergency|pain|doctor|medicine|please|urgent)/.test(str)) { + return "needs"; + } + if (/(hello|hi|hey|good morning|good evening|goodbye|bye|thanks|thank|welcome|nice)/.test(str)) { + return "greetings"; + } + return "social"; +} export function FavoriteMessages({ history, favorites, onReuse, onUnpin }) { const [expanded, setExpanded] = useState(false); + const [search, setSearch] = useState(""); + const [category, setCategory] = useState("all"); + + const totalPinnedCount = useMemo(() => { + return history.filter((message) => favorites.has(message.id)).length; + }, [history, favorites]); + + const filteredPinned = useMemo(() => { + let list = history.filter((message) => favorites.has(message.id)); + + if (category !== "all") { + list = list.filter((msg) => getPhraseCategory(msg.text) === category); + } + + if (search.trim()) { + const q = search.toLowerCase().trim(); + list = list.filter((msg) => msg.text.toLowerCase().includes(q)); + } - const pinned = history.filter((message) => favorites.has(message.id)); - if (pinned.length === 0) return null; + return list; + }, [history, favorites, category, search]); - const displayed = expanded ? pinned : pinned.slice(0, FEW_SHOWN); - const hasMore = pinned.length > FEW_SHOWN; + if (totalPinnedCount === 0) return null; + + const displayed = expanded ? filteredPinned : filteredPinned.slice(0, FEW_SHOWN); + const hasMore = filteredPinned.length > FEW_SHOWN; + + const categories = [ + { key: "all", label: "All" }, + { key: "greetings", label: "Greetings" }, + { key: "needs", label: "Needs" }, + { key: "questions", label: "Questions" }, + { key: "social", label: "Social" }, + ]; return (
-
-
- -
- {displayed.map((message) => ( -
+
+
+ + {/* Compact Search Bar */} +
+
+
+ + {/* Category Tag Chips */} +
+ {categories.map((cat) => { + const isActive = category === cat.key; + return ( -
- ))} + ); + })} +
+ + {/* Pinned Phrase List */} +
+ {displayed.length === 0 ? ( +

+ No pinned phrases found in this category. +

+ ) : ( + displayed.map((message) => ( +
+ + +
+ )) + )} {hasMore && ( )}
diff --git a/client/src/components/FavoriteMessages.test.js b/client/src/components/FavoriteMessages.test.js new file mode 100644 index 00000000..57380acd --- /dev/null +++ b/client/src/components/FavoriteMessages.test.js @@ -0,0 +1,27 @@ +import { describe, it, expect } from "vitest"; +import { getPhraseCategory } from "./FavoriteMessages"; + +describe("getPhraseCategory inference utility", () => { + it("categorizes question phrases with a question mark or interrogative lead word", () => { + expect(getPhraseCategory("What time is it?")).toBe("questions"); + expect(getPhraseCategory("How can I help you")).toBe("questions"); + expect(getPhraseCategory("Where are we going")).toBe("questions"); + }); + + it("categorizes urgent needs and medical request phrases", () => { + expect(getPhraseCategory("I need water please")).toBe("needs"); + expect(getPhraseCategory("Emergency doctor help")).toBe("needs"); + expect(getPhraseCategory("I have pain in my arm")).toBe("needs"); + }); + + it("categorizes greetings and thank you phrases", () => { + expect(getPhraseCategory("Hello good morning!")).toBe("greetings"); + expect(getPhraseCategory("Thank you so much")).toBe("greetings"); + expect(getPhraseCategory("Goodbye see you later")).toBe("greetings"); + }); + + it("categorizes general phrases under social category", () => { + expect(getPhraseCategory("That sounds great!")).toBe("social"); + expect(getPhraseCategory("Working on VoiceForge project")).toBe("social"); + }); +});