diff --git a/web/app/adopt/page.js b/web/app/adopt/page.js index f32eab3a..7cc23723 100644 --- a/web/app/adopt/page.js +++ b/web/app/adopt/page.js @@ -131,7 +131,7 @@ export default function AdoptPage() { setSearch(e.target.value)} /> diff --git a/web/app/ai-chat/page.js b/web/app/ai-chat/page.js index 64f3ae80..742fe66b 100644 --- a/web/app/ai-chat/page.js +++ b/web/app/ai-chat/page.js @@ -68,11 +68,23 @@ function AttachmentPreview({ url, name, token }) { ); } +function useIsMobile() { + const [isMobile, setIsMobile] = useState(false); + useEffect(() => { + const check = () => setIsMobile(window.innerWidth < 640); + check(); + window.addEventListener("resize", check); + return () => window.removeEventListener("resize", check); + }, []); + return isMobile; +} + function AiChatPage() { const { user, token, loading: authLoading } = useAuth(); const router = useRouter(); const searchParams = useSearchParams(); const conversationIdParam = searchParams.get("id"); + const isMobile = useIsMobile(); const [conversation, setConversation] = useState(null); const [messages, setMessages] = useState([]); @@ -498,8 +510,8 @@ function AiChatPage() { - - + + All Conversations @@ -519,7 +531,10 @@ function AiChatPage() { {conv.status} - {conv.mode === "HUMAN" ? "π€ Live" : "π€ AI"} + + + {conv.mode === "HUMAN" ? "Live" : "AI"} + {conv.createdAt ? new Date(conv.createdAt).toLocaleDateString() : ""} @@ -542,7 +557,10 @@ function AiChatPage() { CLOSED - {conv.mode === "HUMAN" ? "π€ Live" : "π€ AI"} + + + {conv.mode === "HUMAN" ? "Live" : "AI"} + {conv.createdAt ? new Date(conv.createdAt).toLocaleDateString() : ""} @@ -554,10 +572,10 @@ function AiChatPage() { - + {!conversation ? ( - πΎ + No active conversation Start a new conversation with the AI assistant. {error && {error}} @@ -570,9 +588,9 @@ function AiChatPage() { ) : ( - + - {isEscalated ? "π€" : "πΎ"} + {isEscalated ? "π€" : } {isEscalated ? (hasStaff ? "Support Agent" : "Leon's Pet Store Support") : "Leon's Pet Assistant"} @@ -583,14 +601,14 @@ function AiChatPage() { - + {!isEscalated && !isClosed && ( - + Chat with a Real Person )} {!isClosed && ( - + Close Chat )} @@ -607,7 +625,7 @@ function AiChatPage() { {messages.length === 0 && ( - {isEscalated ? "π¬" : "πΎ"} + {isEscalated ? "π¬" : } {isEscalated ? "Your conversation has started. A support agent will join soon." : `Hello${user.fullName ? `, ${user.fullName.split(" ")[0]}` : ""}! I'm your pet care assistant. Ask me about pet recommendations, care tips, supplies, or anything pet-related!`} @@ -624,7 +642,7 @@ function AiChatPage() { ...(isOwn ? s.messageRowUser : s.messageRowAgent), }} > - {!isOwn && {isEscalated ? "π€" : "πΎ"}} + {!isOwn && {isEscalated ? "π€" : }} - πΎ + @@ -792,7 +810,6 @@ const s = { padding: "1.5rem 1rem 2rem", }, sidebar: { - width: 230, flexShrink: 0, background: "white", borderRadius: 16, @@ -800,8 +817,7 @@ const s = { display: "flex", flexDirection: "column", overflow: "hidden", - maxHeight: "calc(100vh - 220px)", - minHeight: 300, + minHeight: 200, }, sidebarHeader: { display: "flex", @@ -901,7 +917,7 @@ const s = { display: "flex", flexDirection: "column", height: "calc(100vh - 220px)", - minHeight: 450, + minHeight: 400, }, chatHeader: { display: "flex", @@ -922,6 +938,7 @@ const s = { height: 44, borderRadius: "50%", background: "linear-gradient(135deg, #444, #666)", + border: "3px solid #666", display: "flex", alignItems: "center", justifyContent: "center", @@ -953,8 +970,8 @@ const s = { border: "2px solid #ff8c00", color: "#ff8c00", borderRadius: 8, - padding: "0.45rem 0.9rem", - fontSize: "0.82rem", + padding: "0.3rem 0.65rem", + fontSize: "0.72rem", fontWeight: 600, cursor: "pointer", whiteSpace: "nowrap", @@ -997,8 +1014,8 @@ const s = { border: "2px solid #c0392b", color: "#c0392b", borderRadius: 8, - padding: "0.45rem 0.9rem", - fontSize: "0.82rem", + padding: "0.3rem 0.65rem", + fontSize: "0.72rem", fontWeight: 600, cursor: "pointer", whiteSpace: "nowrap", @@ -1105,6 +1122,7 @@ const s = { height: 30, borderRadius: "50%", background: "linear-gradient(135deg, #444, #666)", + border: "3px solid #666", display: "flex", alignItems: "center", justifyContent: "center", diff --git a/web/app/page.js b/web/app/page.js index 7ec75afe..a9394ecf 100644 --- a/web/app/page.js +++ b/web/app/page.js @@ -66,11 +66,11 @@ export default function Home() { - What We Do + What We Do Leon's Pet Store is a full-service pet shop offering adoptions, grooming, veterinary appointments, and a wide range of supplies to keep your pets happy and healthy. - Our Focus + Our Focus Support responsible pet adoption Provide grooming and care services @@ -79,7 +79,7 @@ export default function Home() { - Visit the Store + Visit the Store Come visit us in person or explore our services online. Whether you're a first-time pet owner or a seasoned animal lover, we're here to help every step of the way. diff --git a/web/app/products/page.js b/web/app/products/page.js index 48959a2a..155ba778 100644 --- a/web/app/products/page.js +++ b/web/app/products/page.js @@ -56,7 +56,7 @@ export default function ProductsPage() { setSearch(e.target.value)} /> diff --git a/web/app/profile/page.js b/web/app/profile/page.js index d76487c1..39cc17dc 100644 --- a/web/app/profile/page.js +++ b/web/app/profile/page.js @@ -444,7 +444,7 @@ export default function ProfilePage() { {fields.map(({ label, value }) => ( {label} - {value} + {value} ))} @@ -552,7 +552,7 @@ export default function ProfilePage() { - + {submitting ? "Saving..." : editingPet ? "Save Changes" : "Add Pet"} diff --git a/web/components/FloatingChat.js b/web/components/FloatingChat.js index 4e54546f..741db28a 100644 --- a/web/components/FloatingChat.js +++ b/web/components/FloatingChat.js @@ -20,6 +20,7 @@ export default function FloatingChat() { } = useChatWidget(); const [input, setInput] = useState(""); + const [fabHovered, setFabHovered] = useState(false); const messagesEndRef = useRef(null); const prevAiLengthRef = useRef(0); const prevLiveLengthRef = useRef(0); @@ -65,8 +66,15 @@ const prevLiveLengthRef = useRef(0); return ( <> {/* Floating toggle button */} - - {isOpen ? "β" : "π¬"} + setFabHovered(true)} onMouseLeave={() => setFabHovered(false)}> + {isOpen ? ( + β + ) : ( + + + + + )} {!isOpen && openConvCount > 0 && {openConvCount}} @@ -77,7 +85,7 @@ const prevLiveLengthRef = useRef(0); {/* Header */} - πΎ + Leon's Assistant @@ -99,7 +107,7 @@ const prevLiveLengthRef = useRef(0); {/* Guest */} {!user && ( - πΎ + Log in to chat with our pet assistant! @@ -129,7 +137,7 @@ const prevLiveLengthRef = useRef(0); {!convsLoading && conversations.length === 0 && ( - π¬ + No conversations yet.Start a live chat above. )} @@ -223,7 +231,7 @@ const prevLiveLengthRef = useRef(0); return ( {!isUser && ( - {msg.senderRole === "BOT" ? "πΎ" : "π€"} + )} {!isUser && ( @@ -282,7 +290,7 @@ const prevLiveLengthRef = useRef(0); {aiMessages.length === 0 && ( - πΎ + Hi{user?.fullName ? `, ${user.fullName.split(" ")[0]}` : ""}! Ask me anything about pets. @@ -292,7 +300,7 @@ const prevLiveLengthRef = useRef(0); {aiMessages.map((msg) => ( - {msg.role === "assistant" && πΎ} + {msg.role === "assistant" && } {msg.content.split("\n").map((line, i, arr) => ( {line}{i < arr.length - 1 && } @@ -308,7 +316,7 @@ const prevLiveLengthRef = useRef(0); {aiSending && ( - πΎ + diff --git a/web/components/Navigation.js b/web/components/Navigation.js index cacf5f8e..2112a6a7 100644 --- a/web/components/Navigation.js +++ b/web/components/Navigation.js @@ -14,8 +14,11 @@ const cartBadgeCls = "absolute -top-1 -right-1.5 bg-[#e53935] text-white rounded function CartIcon({ itemCount, onClick }) { return ( - - π + + + + + {itemCount > 0 && ( {itemCount > 99 ? "99+" : itemCount} )} @@ -59,7 +62,7 @@ export default function DisplayNav() { return ( - + {/* Desktop nav links */} diff --git a/web/components/ProductProfile.js b/web/components/ProductProfile.js index 34944073..98cb356c 100644 --- a/web/components/ProductProfile.js +++ b/web/components/ProductProfile.js @@ -26,6 +26,7 @@ export default function ProductProfile({ prodId, prodName, categoryName, prodDes await addItem(prodId, quantity); setFeedback({ type: "success", message: `${quantity} Γ ${prodName} added to cart!` }); setQuantity(1); + setTimeout(() => setFeedback(null), 1000); } catch (err) { setFeedback({ type: "error", message: err.message ?? "Failed to add to cart." }); } finally { @@ -90,15 +91,17 @@ export default function ProductProfile({ prodId, prodName, categoryName, prodDes + {adding ? "Addingβ¦" : "Add to Cart"} - {feedback && ( - + {feedback?.type === "error" && ( + {feedback.message} )} diff --git a/web/public/bootstrap/cart-fill.svg b/web/public/bootstrap/cart-fill.svg new file mode 100644 index 00000000..974fc295 --- /dev/null +++ b/web/public/bootstrap/cart-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/public/bootstrap/cart-plus-fill.svg b/web/public/bootstrap/cart-plus-fill.svg new file mode 100644 index 00000000..59e46e48 --- /dev/null +++ b/web/public/bootstrap/cart-plus-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/public/bootstrap/cart.svg b/web/public/bootstrap/cart.svg new file mode 100644 index 00000000..0e0f96ce --- /dev/null +++ b/web/public/bootstrap/cart.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/public/bootstrap/chat-fill.svg b/web/public/bootstrap/chat-fill.svg new file mode 100644 index 00000000..c8969390 --- /dev/null +++ b/web/public/bootstrap/chat-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/public/bootstrap/chat.svg b/web/public/bootstrap/chat.svg new file mode 100644 index 00000000..487d142a --- /dev/null +++ b/web/public/bootstrap/chat.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/public/bootstrap/person-circle.svg b/web/public/bootstrap/person-circle.svg new file mode 100644 index 00000000..a75f25fc --- /dev/null +++ b/web/public/bootstrap/person-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/public/bootstrap/person-fill.svg b/web/public/bootstrap/person-fill.svg new file mode 100644 index 00000000..46d1a75f --- /dev/null +++ b/web/public/bootstrap/person-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/public/bootstrap/robot.svg b/web/public/bootstrap/robot.svg new file mode 100644 index 00000000..a2242026 --- /dev/null +++ b/web/public/bootstrap/robot.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/public/images/pet-placeholder.png b/web/public/images/pet-placeholder.png deleted file mode 100644 index 207e9d29..00000000 Binary files a/web/public/images/pet-placeholder.png and /dev/null differ diff --git a/web/public/logo.png b/web/public/logo.png new file mode 100644 index 00000000..58d6808f Binary files /dev/null and b/web/public/logo.png differ diff --git a/web/public/logo_simple.png b/web/public/logo_simple.png deleted file mode 100644 index 207e9d29..00000000 Binary files a/web/public/logo_simple.png and /dev/null differ
Start a new conversation with the AI assistant.
{isEscalated ? "Your conversation has started. A support agent will join soon." : `Hello${user.fullName ? `, ${user.fullName.split(" ")[0]}` : ""}! I'm your pet care assistant. Ask me about pet recommendations, care tips, supplies, or anything pet-related!`}
Leon's Pet Store is a full-service pet shop offering adoptions, grooming, veterinary appointments, and a wide range of supplies to keep your pets happy and healthy.
Come visit us in person or explore our services online. Whether you're a first-time pet owner or a seasoned animal lover, we're here to help every step of the way.
Log in to chat with our pet assistant!
No conversations yet.Start a live chat above.
Hi{user?.fullName ? `, ${user.fullName.split(" ")[0]}` : ""}! Ask me anything about pets. @@ -292,7 +300,7 @@ const prevLiveLengthRef = useRef(0); {aiMessages.map((msg) => (
+ {feedback?.type === "error" && ( +
{feedback.message}