@@ -131,7 +131,7 @@ export default function AdoptPage() {
|
||||
<input
|
||||
className={`${inputCls} flex-1 max-w-[400px] font-[inherit] max-[600px]:max-w-full`}
|
||||
type="text"
|
||||
placeholder="Search by name, species, or breed..."
|
||||
placeholder="Search"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
|
||||
@@ -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() {
|
||||
</section>
|
||||
|
||||
<section style={s.chatSection}>
|
||||
<div style={{ display: "flex", gap: "1rem", alignItems: "flex-start" }}>
|
||||
<div style={s.sidebar}>
|
||||
<div style={{ display: "flex", flexDirection: isMobile ? "column" : "row", gap: "1rem", alignItems: "flex-start" }}>
|
||||
<div style={{ ...s.sidebar, width: isMobile ? "100%" : 230, maxHeight: isMobile ? 260 : "calc(100vh - 220px)" }}>
|
||||
<div style={s.sidebarHeader}>
|
||||
<span style={s.sidebarTitle}>All Conversations</span>
|
||||
</div>
|
||||
@@ -519,7 +531,10 @@ function AiChatPage() {
|
||||
<span style={{ ...s.convStatusBadge, ...s.convStatusOpen }}>{conv.status}</span>
|
||||
</div>
|
||||
<div style={s.convItemBottom}>
|
||||
<span style={s.convItemMode}>{conv.mode === "HUMAN" ? "👤 Live" : "🤖 AI"}</span>
|
||||
<span style={{ ...s.convItemMode, display: "flex", alignItems: "center", gap: "0.25rem" }}>
|
||||
<img src={conv.mode === "HUMAN" ? "/bootstrap/person-fill.svg" : "/bootstrap/robot.svg"} alt="" style={{ width: 12, height: 12 }} />
|
||||
{conv.mode === "HUMAN" ? "Live" : "AI"}
|
||||
</span>
|
||||
<span style={s.convItemDate}>{conv.createdAt ? new Date(conv.createdAt).toLocaleDateString() : ""}</span>
|
||||
</div>
|
||||
</button>
|
||||
@@ -542,7 +557,10 @@ function AiChatPage() {
|
||||
<span style={{ ...s.convStatusBadge, ...s.convStatusClosed }}>CLOSED</span>
|
||||
</div>
|
||||
<div style={s.convItemBottom}>
|
||||
<span style={s.convItemMode}>{conv.mode === "HUMAN" ? "👤 Live" : "🤖 AI"}</span>
|
||||
<span style={{ ...s.convItemMode, display: "flex", alignItems: "center", gap: "0.25rem" }}>
|
||||
<img src={conv.mode === "HUMAN" ? "/bootstrap/person-fill.svg" : "/bootstrap/robot.svg"} alt="" style={{ width: 12, height: 12 }} />
|
||||
{conv.mode === "HUMAN" ? "Live" : "AI"}
|
||||
</span>
|
||||
<span style={s.convItemDate}>{conv.createdAt ? new Date(conv.createdAt).toLocaleDateString() : ""}</span>
|
||||
</div>
|
||||
</button>
|
||||
@@ -554,10 +572,10 @@ function AiChatPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ flex: 1, minWidth: 0, width: isMobile ? "100%" : undefined }}>
|
||||
{!conversation ? (
|
||||
<div style={s.noConvCard}>
|
||||
<div style={s.noConvIcon}>🐾</div>
|
||||
<div style={s.noConvIcon}><img src="/bootstrap/person-circle.svg" alt="assistant" style={{ width: "3rem", height: "3rem", opacity: 0.6 }} /></div>
|
||||
<h2 style={s.noConvTitle}>No active conversation</h2>
|
||||
<p style={s.noConvText}>Start a new conversation with the AI assistant.</p>
|
||||
{error && <div style={s.errorInline}>{error}</div>}
|
||||
@@ -570,9 +588,9 @@ function AiChatPage() {
|
||||
</div>
|
||||
) : (
|
||||
<div style={s.chatCard}>
|
||||
<div style={s.chatHeader}>
|
||||
<div style={{ ...s.chatHeader, flexDirection: isMobile ? "column" : "row", alignItems: isMobile ? "flex-start" : "center", gap: isMobile ? "0.6rem" : 0 }}>
|
||||
<div style={s.chatHeaderLeft}>
|
||||
<div style={isEscalated ? s.agentAvatar : s.aiAvatar}>{isEscalated ? "👤" : "🐾"}</div>
|
||||
<div style={isEscalated ? s.agentAvatar : s.aiAvatar}>{isEscalated ? "👤" : <img src="/bootstrap/person-circle.svg" alt="assistant" style={{ width: "100%", height: "100%", filter: "brightness(0) invert(1)" }} />}</div>
|
||||
<div>
|
||||
<div style={s.chatHeaderTitle}>
|
||||
{isEscalated ? (hasStaff ? "Support Agent" : "Leon's Pet Store Support") : "Leon's Pet Assistant"}
|
||||
@@ -583,14 +601,14 @@ function AiChatPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: "0.5rem" }}>
|
||||
<div style={{ display: "flex", gap: "0.5rem", width: isMobile ? "100%" : undefined }}>
|
||||
{!isEscalated && !isClosed && (
|
||||
<button style={s.humanBtn} onClick={handleSwitchToHuman} title="Connect with a human support agent">
|
||||
<button style={isMobile ? { ...s.humanBtn, flex: 1 } : s.humanBtn} onClick={handleSwitchToHuman} title="Connect with a human support agent">
|
||||
Chat with a Real Person
|
||||
</button>
|
||||
)}
|
||||
{!isClosed && (
|
||||
<button style={s.closeConvBtn} onClick={handleCloseConversation} title="Close this conversation">
|
||||
<button style={isMobile ? { ...s.closeConvBtn, flex: 1 } : s.closeConvBtn} onClick={handleCloseConversation} title="Close this conversation">
|
||||
Close Chat
|
||||
</button>
|
||||
)}
|
||||
@@ -607,7 +625,7 @@ function AiChatPage() {
|
||||
<div style={s.messagesArea} ref={messagesAreaRef}>
|
||||
{messages.length === 0 && (
|
||||
<div style={s.emptyState}>
|
||||
<div style={s.emptyIcon}>{isEscalated ? "💬" : "🐾"}</div>
|
||||
<div style={s.emptyIcon}>{isEscalated ? "💬" : <img src="/bootstrap/person-circle.svg" alt="assistant" style={{ width: "3rem", height: "3rem", opacity: 0.6 }} />}</div>
|
||||
<p style={s.emptyText}>
|
||||
{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!`}
|
||||
</p>
|
||||
@@ -624,7 +642,7 @@ function AiChatPage() {
|
||||
...(isOwn ? s.messageRowUser : s.messageRowAgent),
|
||||
}}
|
||||
>
|
||||
{!isOwn && <div style={isEscalated ? s.agentAvatarSmall : s.aiAvatarSmall}>{isEscalated ? "👤" : "🐾"}</div>}
|
||||
{!isOwn && <div style={isEscalated ? s.agentAvatarSmall : s.aiAvatarSmall}>{isEscalated ? "👤" : <img src="/bootstrap/person-circle.svg" alt="assistant" style={{ width: "100%", height: "100%", filter: "brightness(0) invert(1)" }} />}</div>}
|
||||
<div
|
||||
style={{
|
||||
...s.messageBubble,
|
||||
@@ -655,7 +673,7 @@ function AiChatPage() {
|
||||
|
||||
{botTyping && !isEscalated && (
|
||||
<div style={{ ...s.messageRow, ...s.messageRowAgent }}>
|
||||
<div style={s.aiAvatarSmall}>🐾</div>
|
||||
<div style={s.aiAvatarSmall}><img src="/bootstrap/person-circle.svg" alt="assistant" style={{ width: "100%", height: "100%", filter: "brightness(0) invert(1)" }} /></div>
|
||||
<div style={{ ...s.messageBubble, ...s.bubbleAgent, display: "flex", alignItems: "center", gap: "4px", padding: "0.6rem 0.9rem" }}>
|
||||
<span className="fc-dot" />
|
||||
<span className="fc-dot" style={{ animationDelay: "0.2s" }} />
|
||||
@@ -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",
|
||||
|
||||
@@ -66,11 +66,11 @@ export default function Home() {
|
||||
</div>
|
||||
<div className="max-w-[1200px] mx-auto px-8 pb-6 grid grid-cols-3 gap-6 max-[768px]:grid-cols-1">
|
||||
<div className="bg-white rounded-2xl shadow-[0_4px_12px_rgba(0,0,0,0.08)] p-6">
|
||||
<h3 className="mt-0 mb-4 text-[#222]">What We Do</h3>
|
||||
<h3 className="mt-0 mb-4 text-[#222] underline decoration-[#e68672] underline-offset-4 text-[1.3rem] font-bold">What We Do</h3>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-2xl shadow-[0_4px_12px_rgba(0,0,0,0.08)] p-6">
|
||||
<h3 className="mt-0 mb-4 text-[#222]">Our Focus</h3>
|
||||
<h3 className="mt-0 mb-4 text-[#222] underline decoration-[#e68672] underline-offset-4 text-[1.3rem] font-bold">Our Focus</h3>
|
||||
<ul className="m-0 pl-5 grid gap-2 list-disc">
|
||||
<li>Support responsible pet adoption</li>
|
||||
<li>Provide grooming and care services</li>
|
||||
@@ -79,7 +79,7 @@ export default function Home() {
|
||||
</ul>
|
||||
</div>
|
||||
<div className="bg-white rounded-2xl shadow-[0_4px_12px_rgba(0,0,0,0.08)] p-6">
|
||||
<h3 className="mt-0 mb-4 text-[#222]">Visit the Store</h3>
|
||||
<h3 className="mt-0 mb-4 text-[#222] underline decoration-[#e68672] underline-offset-4 text-[1.3rem] font-bold">Visit the Store</h3>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function ProductsPage() {
|
||||
<input
|
||||
className="flex-1 max-w-[400px] px-4 py-[0.6rem] border-2 border-[#ddd] rounded-md text-base outline-none transition-colors focus:border-[#e68672] font-[inherit]"
|
||||
type="text"
|
||||
placeholder="Search by name or category..."
|
||||
placeholder="Search"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
|
||||
@@ -444,7 +444,7 @@ export default function ProfilePage() {
|
||||
{fields.map(({ label, value }) => (
|
||||
<div key={label} className="flex gap-2 py-1">
|
||||
<dt className="text-[0.85rem] font-semibold text-[#888] min-w-[100px]">{label}</dt>
|
||||
<dd className="text-[0.9rem] text-[#333] m-0">{value}</dd>
|
||||
<dd className="text-[0.9rem] text-[#333] m-0 break-words min-w-0">{value}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
@@ -552,7 +552,7 @@ export default function ProfilePage() {
|
||||
</select>
|
||||
</label>
|
||||
<div className="flex gap-3">
|
||||
<button type="submit" className={submitBtnCls} disabled={submitting}>
|
||||
<button type="submit" className={`${submitBtnCls} px-5 py-2 text-[0.9rem]`} disabled={submitting}>
|
||||
{submitting ? "Saving..." : editingPet ? "Save Changes" : "Add Pet"}
|
||||
</button>
|
||||
<button type="button" className="px-4 py-2 border border-[#ddd] rounded-lg bg-white text-[#555] text-[0.9rem] cursor-pointer hover:border-[#aaa] transition-colors" onClick={closeForm}>
|
||||
|
||||
Reference in New Issue
Block a user