@jutech-devs/agent-sdk
Version:
Modern embeddable AI agent chat widget with voice support and payment integration
1,359 lines (1,345 loc) • 92 kB
JavaScript
"use client"
// src/AgentWidget.tsx
import { useState as useState6, useEffect as useEffect5 } from "react";
// src/components/ChatBubble.tsx
import { jsx, jsxs } from "react/jsx-runtime";
function ChatBubble({ isOpen, onClick, primaryColor = "#3b82f6", unreadCount = 0 }) {
return /* @__PURE__ */ jsxs(
"button",
{
onClick,
style: {
position: "fixed",
bottom: "24px",
right: "24px",
width: "64px",
height: "64px",
borderRadius: "50%",
background: `linear-gradient(135deg, ${primaryColor}, ${primaryColor}dd)`,
border: "none",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
boxShadow: "0 8px 25px rgba(102, 126, 234, 0.35), 0 3px 10px rgba(0, 0, 0, 0.1)",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
zIndex: 999999
},
"aria-label": "Open chat",
children: [
unreadCount > 0 && /* @__PURE__ */ jsx("div", { style: {
position: "absolute",
top: "-8px",
right: "-8px",
width: "24px",
height: "24px",
background: "linear-gradient(135deg, #ef4444, #dc2626)",
color: "white",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "12px",
fontWeight: "bold",
border: "2px solid white",
boxShadow: "0 4px 12px rgba(239, 68, 68, 0.4)"
}, children: unreadCount > 9 ? "9+" : unreadCount }),
/* @__PURE__ */ jsx(
"svg",
{
width: "24",
height: "24",
fill: "none",
stroke: "white",
viewBox: "0 0 24 24",
style: { filter: "drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2))" },
children: /* @__PURE__ */ jsx(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2.5,
d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
}
)
}
)
]
}
);
}
// src/components/ChatWindow.tsx
import { useState as useState2, useRef as useRef2, useEffect as useEffect2 } from "react";
// src/components/MessageList.tsx
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
var messageStyles = `
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 0.3s ease-out;
}
`;
if (typeof document !== "undefined") {
const styleSheet = document.createElement("style");
styleSheet.textContent = messageStyles;
document.head.appendChild(styleSheet);
}
function MessageList({ messages, primaryColor }) {
return /* @__PURE__ */ jsx2("div", { className: "space-y-4", children: messages.map((message, index) => /* @__PURE__ */ jsxs2(
"div",
{
className: `flex ${message.role === "user" ? "justify-end" : "justify-start"} animate-fade-in`,
style: { animationDelay: `${index * 100}ms` },
children: [
message.role === "assistant" && /* @__PURE__ */ jsx2("div", { className: "flex-shrink-0 mr-3", children: /* @__PURE__ */ jsx2(
"div",
{
className: "w-8 h-8 rounded-full flex items-center justify-center shadow-md",
style: {
background: `linear-gradient(135deg, ${primaryColor}, ${primaryColor}dd)`
},
children: /* @__PURE__ */ jsx2("svg", { className: "w-4 h-4 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" }) })
}
) }),
/* @__PURE__ */ jsx2(
"div",
{
className: `max-w-[280px] rounded-2xl px-4 py-3 text-sm shadow-lg backdrop-blur-sm transition-all duration-200 hover:shadow-xl ${message.role === "user" ? "text-white rounded-br-md" : "bg-white/90 text-gray-800 rounded-bl-md border border-gray-200/50"}`,
style: {
background: message.role === "user" ? `linear-gradient(135deg, ${primaryColor}, ${primaryColor}dd)` : void 0,
boxShadow: message.role === "user" ? `0 8px 25px -8px ${primaryColor}40` : "0 4px 20px rgba(0,0,0,0.08)"
},
children: message.content || (message.isStreaming ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
/* @__PURE__ */ jsxs2("div", { className: "flex space-x-1", children: [
/* @__PURE__ */ jsx2(
"div",
{
className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce",
style: { animationDelay: "0ms" }
}
),
/* @__PURE__ */ jsx2(
"div",
{
className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce",
style: { animationDelay: "150ms" }
}
),
/* @__PURE__ */ jsx2(
"div",
{
className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce",
style: { animationDelay: "300ms" }
}
)
] }),
/* @__PURE__ */ jsx2("span", { className: "text-xs text-gray-500", children: "AI is typing..." })
] }) : /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
/* @__PURE__ */ jsxs2("div", { className: "flex space-x-1", children: [
/* @__PURE__ */ jsx2("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-pulse" }),
/* @__PURE__ */ jsx2("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-pulse", style: { animationDelay: "0.5s" } }),
/* @__PURE__ */ jsx2("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-pulse", style: { animationDelay: "1s" } })
] }),
/* @__PURE__ */ jsx2("span", { className: "text-xs text-gray-500", children: "Thinking..." })
] }))
}
),
message.role === "user" && /* @__PURE__ */ jsx2("div", { className: "flex-shrink-0 ml-3", children: /* @__PURE__ */ jsx2("div", { className: "w-8 h-8 rounded-full bg-gradient-to-br from-gray-400 to-gray-600 flex items-center justify-center shadow-md", children: /* @__PURE__ */ jsx2("svg", { className: "w-4 h-4 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }) }) })
]
},
message.id
)) });
}
// src/components/VoiceButton.tsx
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
function VoiceButton({ isListening, isSupported, onToggle, primaryColor }) {
if (!isSupported) return null;
return /* @__PURE__ */ jsx3(
"button",
{
onClick: onToggle,
style: {
background: isListening ? primaryColor : "rgba(255, 255, 255, 0.1)",
border: `1px solid ${isListening ? "white" : "rgba(255, 255, 255, 0.3)"}`,
borderRadius: "50%",
width: "40px",
height: "40px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
backdropFilter: "blur(10px)",
boxShadow: isListening ? `0 0 20px ${primaryColor}40, 0 4px 16px rgba(0, 0, 0, 0.1)` : "0 4px 16px rgba(0, 0, 0, 0.1)",
animation: isListening ? "pulse 2s infinite" : "none"
},
title: isListening ? "Stop listening" : "Start voice input",
children: /* @__PURE__ */ jsxs3(
"svg",
{
width: "18",
height: "18",
fill: "none",
stroke: "white",
viewBox: "0 0 24 24",
style: {
filter: isListening ? "drop-shadow(0 0 4px rgba(255,255,255,0.5))" : "none"
},
children: [
/* @__PURE__ */ jsx3(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"
}
),
/* @__PURE__ */ jsx3(
"path",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
d: "M19 10v2a7 7 0 0 1-14 0v-2"
}
),
/* @__PURE__ */ jsx3(
"line",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
x1: "12",
y1: "19",
x2: "12",
y2: "23"
}
),
/* @__PURE__ */ jsx3(
"line",
{
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: 2,
x1: "8",
y1: "23",
x2: "16",
y2: "23"
}
)
]
}
)
}
);
}
// src/hooks/useVoice.ts
import { useState, useCallback, useRef, useEffect } from "react";
function useVoice({ onTranscript, enabled = true }) {
const [isListening, setIsListening] = useState(false);
const [isSupported, setIsSupported] = useState(false);
const recognitionRef = useRef(null);
useEffect(() => {
if (typeof window !== "undefined") {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
setIsSupported(!!SpeechRecognition && enabled);
if (SpeechRecognition && enabled) {
const recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = "en-US";
recognition.onstart = () => {
setIsListening(true);
};
recognition.onresult = (event) => {
const transcript = event.results[0]?.transcript;
if (transcript) {
onTranscript(transcript);
}
};
recognition.onend = () => {
setIsListening(false);
};
recognition.onerror = (event) => {
console.error("Speech recognition error:", event.error);
setIsListening(false);
};
recognitionRef.current = recognition;
}
}
return () => {
if (recognitionRef.current) {
recognitionRef.current.abort();
}
};
}, [onTranscript, enabled]);
const startListening = useCallback(() => {
if (recognitionRef.current && !isListening) {
try {
recognitionRef.current.start();
} catch (error) {
console.error("Failed to start speech recognition:", error);
}
}
}, [isListening]);
const stopListening = useCallback(() => {
if (recognitionRef.current && isListening) {
recognitionRef.current.stop();
}
}, [isListening]);
const speak = useCallback((text) => {
if (typeof window !== "undefined" && window.speechSynthesis && enabled) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.rate = 0.9;
utterance.pitch = 1;
utterance.volume = 0.8;
window.speechSynthesis.speak(utterance);
}
}, [enabled]);
return {
isListening,
isSupported,
startListening,
stopListening,
speak
};
}
// src/components/MessageInput.tsx
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
function MessageInput({ value, onChange, onSend, isLoading, placeholder, primaryColor, voiceEnabled = false }) {
const { isListening, isSupported, startListening, stopListening } = useVoice({
onTranscript: (text) => {
onChange(value + (value ? " " : "") + text);
},
enabled: voiceEnabled
});
const handleKeyPress = (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
onSend();
}
};
const handleVoiceToggle = () => {
if (isListening) {
stopListening();
} else {
startListening();
}
};
return /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3 p-4 bg-gradient-to-t from-gray-50/80 to-transparent backdrop-blur-sm", children: [
/* @__PURE__ */ jsxs4("div", { className: "flex-1 relative", children: [
/* @__PURE__ */ jsx4(
"input",
{
type: "text",
value,
onChange: (e) => onChange(e.target.value),
onKeyPress: handleKeyPress,
placeholder,
disabled: isLoading,
className: "w-full rounded-2xl border-0 bg-white/90 backdrop-blur-sm px-4 py-3 pr-12 text-sm placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-2 focus:ring-opacity-50 disabled:opacity-50 shadow-lg transition-all duration-200",
style: {
boxShadow: `0 4px 20px rgba(0,0,0,0.08), 0 0 0 1px ${primaryColor}20`,
focusRingColor: `${primaryColor}80`
}
}
),
value.trim() && /* @__PURE__ */ jsx4("div", { className: "absolute right-4 top-1/2 transform -translate-y-1/2", children: /* @__PURE__ */ jsx4("div", { className: "w-2 h-2 bg-green-500 rounded-full animate-pulse" }) })
] }),
voiceEnabled && /* @__PURE__ */ jsx4(
VoiceButton,
{
isListening,
isSupported,
onToggle: handleVoiceToggle,
primaryColor
}
),
/* @__PURE__ */ jsx4(
"button",
{
onClick: onSend,
disabled: !value.trim() || isLoading,
className: `flex h-12 w-12 items-center justify-center rounded-2xl text-white transition-all duration-200 transform ${!value.trim() || isLoading ? "bg-gray-300 cursor-not-allowed scale-95" : "hover:scale-105 active:scale-95 shadow-lg hover:shadow-xl"}`,
style: {
background: !value.trim() || isLoading ? void 0 : `linear-gradient(135deg, ${primaryColor}, ${primaryColor}dd)`,
boxShadow: !value.trim() || isLoading ? void 0 : `0 8px 25px -8px ${primaryColor}60`
},
"aria-label": "Send message",
children: isLoading ? /* @__PURE__ */ jsx4("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-white border-t-transparent" }) : /* @__PURE__ */ jsx4("svg", { className: "h-5 w-5 transition-transform duration-200", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8" }) })
}
)
] });
}
// src/components/ProductCard.tsx
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
function ProductCard({ product, onBuyNow, primaryColor }) {
return /* @__PURE__ */ jsxs5(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "16px",
margin: "8px 0",
backgroundColor: "#ffffff"
},
children: [
(product.imageUrl || product.images?.[0]) && /* @__PURE__ */ jsx5(
"img",
{
src: product.imageUrl || product.images[0],
alt: product.name,
style: {
width: "100%",
height: "120px",
objectFit: "cover",
borderRadius: "6px",
marginBottom: "12px"
}
}
),
/* @__PURE__ */ jsx5(
"h3",
{
style: {
fontSize: "16px",
fontWeight: "600",
margin: "0 0 8px 0",
color: "#1f2937"
},
children: product.name
}
),
/* @__PURE__ */ jsx5(
"p",
{
style: {
fontSize: "14px",
color: "#6b7280",
margin: "0 0 12px 0",
lineHeight: "1.4"
},
children: product.description
}
),
/* @__PURE__ */ jsxs5(
"div",
{
style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center"
},
children: [
/* @__PURE__ */ jsxs5(
"span",
{
style: {
fontSize: "18px",
fontWeight: "700",
color: primaryColor
},
children: [
product.currency,
" ",
product.price
]
}
),
/* @__PURE__ */ jsx5(
"button",
{
onClick: () => onBuyNow(product),
style: {
backgroundColor: primaryColor,
color: "white",
border: "none",
borderRadius: "6px",
padding: "8px 16px",
fontSize: "14px",
fontWeight: "500",
cursor: "pointer"
},
children: "Buy Now"
}
)
]
}
)
]
}
);
}
// src/components/OrdersView.tsx
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
function OrdersView({ orders, onClose, onPayOrder }) {
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit"
});
};
const getStatusColor = (status) => {
switch (status) {
case "completed":
return "#10b981";
case "processing":
return "#3b82f6";
case "shipped":
return "#8b5cf6";
case "delivered":
return "#059669";
case "pending":
return "#f59e0b";
case "failed":
return "#ef4444";
case "cancelled":
return "#6b7280";
default:
return "#6b7280";
}
};
return /* @__PURE__ */ jsxs6("div", { style: { padding: "20px" }, children: [
/* @__PURE__ */ jsxs6("div", { style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "20px"
}, children: [
/* @__PURE__ */ jsx6("h3", { style: {
fontSize: "18px",
fontWeight: "600",
margin: 0,
color: "#1f2937"
}, children: "My Orders" }),
/* @__PURE__ */ jsx6(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
fontSize: "24px",
cursor: "pointer",
color: "#6b7280",
padding: "4px"
},
children: "\xD7"
}
)
] }),
orders.length === 0 ? /* @__PURE__ */ jsx6("div", { style: {
textAlign: "center",
padding: "40px 20px",
color: "#6b7280"
}, children: /* @__PURE__ */ jsx6("p", { children: "No orders found" }) }) : /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: orders.map((order) => /* @__PURE__ */ jsx6(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "16px",
backgroundColor: "#ffffff"
},
children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: "12px" }, children: [
order.productId && order.productId.imageUrl && /* @__PURE__ */ jsx6(
"img",
{
src: order.productId.imageUrl,
alt: order.productId.name,
style: {
width: "60px",
height: "60px",
objectFit: "cover",
borderRadius: "6px"
}
}
),
/* @__PURE__ */ jsxs6("div", { style: { flex: 1 }, children: [
/* @__PURE__ */ jsx6("h4", { style: {
fontSize: "16px",
fontWeight: "600",
margin: "0 0 4px 0",
color: "#1f2937"
}, children: order.productId ? order.productId.name : order.itemName || "Service Order" }),
/* @__PURE__ */ jsx6("p", { style: {
fontSize: "14px",
color: "#6b7280",
margin: "0 0 8px 0"
}, children: order.productId ? order.productId.description : "Service booking order" }),
/* @__PURE__ */ jsxs6("div", { style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}, children: [
/* @__PURE__ */ jsxs6("span", { style: {
fontSize: "16px",
fontWeight: "600",
color: "#1f2937"
}, children: [
order.currency,
" ",
order.amount
] }),
/* @__PURE__ */ jsx6("span", { style: {
fontSize: "12px",
padding: "4px 8px",
borderRadius: "12px",
backgroundColor: `${getStatusColor(order.status)}20`,
color: getStatusColor(order.status),
fontWeight: "500",
textTransform: "capitalize"
}, children: order.status })
] }),
/* @__PURE__ */ jsx6("p", { style: {
fontSize: "12px",
color: "#9ca3af",
margin: "8px 0 0 0"
}, children: formatDate(order.createdAt) }),
order.trackingNumber && /* @__PURE__ */ jsxs6("p", { style: {
fontSize: "12px",
color: "#3b82f6",
margin: "4px 0 0 0",
fontWeight: "500"
}, children: [
"Tracking: ",
order.trackingNumber
] }),
order.deliveryAddress && /* @__PURE__ */ jsxs6("p", { style: {
fontSize: "12px",
color: "#6b7280",
margin: "4px 0 0 0"
}, children: [
order.deliveryAddress.street,
", ",
order.deliveryAddress.city,
", ",
order.deliveryAddress.state,
" ",
order.deliveryAddress.zipCode
] }),
order.status === "pending" && onPayOrder && /* @__PURE__ */ jsx6(
"button",
{
onClick: () => onPayOrder(order),
style: {
marginTop: "8px",
padding: "6px 12px",
backgroundColor: "#3b82f6",
color: "white",
border: "none",
borderRadius: "4px",
fontSize: "12px",
fontWeight: "500",
cursor: "pointer"
},
children: "Pay Now"
}
)
] })
] })
},
order._id
)) })
] });
}
// src/components/ServiceCard.tsx
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
function ServiceCard({ service, onBook }) {
return /* @__PURE__ */ jsxs7(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "16px",
backgroundColor: "white",
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)"
},
children: [
service.imageUrl && /* @__PURE__ */ jsx7(
"img",
{
src: service.imageUrl,
alt: service.name,
style: {
width: "100%",
height: "120px",
objectFit: "cover",
borderRadius: "6px",
marginBottom: "12px"
}
}
),
/* @__PURE__ */ jsx7("h4", { style: { fontSize: "16px", fontWeight: "600", margin: "0 0 8px 0" }, children: service.name }),
/* @__PURE__ */ jsx7("p", { style: { fontSize: "14px", color: "#6b7280", margin: "0 0 12px 0", lineHeight: "1.4" }, children: service.description }),
/* @__PURE__ */ jsxs7("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
/* @__PURE__ */ jsxs7("div", { children: [
/* @__PURE__ */ jsxs7("span", { style: { fontSize: "18px", fontWeight: "700", color: "#1f2937" }, children: [
service.currency,
" ",
service.price
] }),
service.duration && /* @__PURE__ */ jsxs7("div", { style: { fontSize: "12px", color: "#6b7280" }, children: [
service.duration,
" minutes"
] })
] }),
/* @__PURE__ */ jsx7(
"button",
{
onClick: () => onBook(service),
style: {
padding: "8px 16px",
backgroundColor: "#3b82f6",
color: "white",
border: "none",
borderRadius: "6px",
fontSize: "14px",
fontWeight: "500",
cursor: "pointer"
},
children: "Book Now"
}
)
] })
]
}
);
}
// src/components/BookingsView.tsx
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
function BookingsView({ bookings, onClose }) {
return /* @__PURE__ */ jsxs8("div", { style: { padding: "16px" }, children: [
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }, children: [
/* @__PURE__ */ jsx8("h3", { style: { fontSize: "18px", fontWeight: "600", margin: 0 }, children: "My Bookings" }),
/* @__PURE__ */ jsx8(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
fontSize: "20px",
cursor: "pointer",
color: "#6b7280"
},
children: "\xD7"
}
)
] }),
bookings.length === 0 ? /* @__PURE__ */ jsx8("div", { style: { textAlign: "center", padding: "32px", color: "#6b7280" }, children: /* @__PURE__ */ jsx8("p", { children: "No bookings found" }) }) : /* @__PURE__ */ jsx8("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: bookings.map((booking) => /* @__PURE__ */ jsxs8(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "12px",
backgroundColor: "#f9fafb"
},
children: [
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "start", marginBottom: "8px" }, children: [
/* @__PURE__ */ jsx8("h4", { style: { fontSize: "16px", fontWeight: "600", margin: 0 }, children: booking.serviceName }),
/* @__PURE__ */ jsx8(
"span",
{
style: {
padding: "2px 8px",
borderRadius: "12px",
fontSize: "12px",
backgroundColor: booking.status === "confirmed" ? "#dcfce7" : "#fef3c7",
color: booking.status === "confirmed" ? "#166534" : "#92400e"
},
children: booking.status
}
)
] }),
/* @__PURE__ */ jsxs8("div", { style: { fontSize: "14px", color: "#6b7280", marginBottom: "4px" }, children: [
"\u{1F4C5} ",
new Date(booking.date).toLocaleDateString(),
" at ",
booking.time
] }),
booking.notes && /* @__PURE__ */ jsxs8("div", { style: { fontSize: "14px", color: "#6b7280" }, children: [
"\u{1F4DD} ",
booking.notes
] })
]
},
booking._id
)) })
] });
}
// src/components/ChatWindow.tsx
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
function ChatWindow({
isOpen,
onClose,
messages,
isLoading,
error,
onSendMessage,
onClearMessages,
primaryColor,
title,
subtitle,
placeholder,
position,
products,
onBuyProduct,
showProducts,
onToggleProducts,
voiceEnabled,
orders,
onToggleOrders,
showOrders,
onPayOrder,
services,
onBookService,
showServices,
onToggleServices,
bookings,
onToggleBookings,
showBookings,
bookingEnabled
}) {
const [input, setInput] = useState2("");
const messagesEndRef = useRef2(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect2(() => {
if (isOpen) {
scrollToBottom();
}
}, [messages, isOpen]);
const handleSend = () => {
if (input.trim() && !isLoading) {
onSendMessage(input.trim());
setInput("");
}
};
if (!isOpen) return null;
return /* @__PURE__ */ jsxs9("div", { style: {
position: "fixed",
bottom: "96px",
right: "24px",
width: "400px",
height: "600px",
maxWidth: "calc(100vw - 48px)",
maxHeight: "calc(100vh - 140px)",
background: "linear-gradient(145deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 250, 252, 0.95) 100%)",
backdropFilter: "blur(24px) saturate(180%)",
WebkitBackdropFilter: "blur(24px) saturate(180%)",
border: "1px solid rgba(255, 255, 255, 0.3)",
borderRadius: "24px",
boxShadow: "0 32px 80px rgba(0, 0, 0, 0.12), 0 16px 40px rgba(0, 0, 0, 0.08), 0 8px 20px rgba(0, 0, 0, 0.04), 0 0 0 1px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.6)",
display: "flex",
flexDirection: "column",
overflow: "hidden",
transformOrigin: "bottom right",
animation: "slideInUp 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)",
zIndex: 999999,
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
}, children: [
/* @__PURE__ */ jsxs9("div", { style: {
padding: "24px 28px",
background: `linear-gradient(135deg, ${primaryColor} 0%, ${primaryColor}dd 100%)`,
color: "white",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
position: "relative",
overflow: "hidden",
borderRadius: "24px 24px 0 0"
}, children: [
/* @__PURE__ */ jsx9("div", { style: {
position: "absolute",
inset: 0,
background: "radial-gradient(circle at 15% 15%, rgba(255,255,255,0.3) 0%, transparent 40%), radial-gradient(circle at 85% 85%, rgba(255,255,255,0.15) 0%, transparent 40%), linear-gradient(135deg, rgba(255,255,255,0.15) 0%, transparent 70%)"
} }),
/* @__PURE__ */ jsxs9("div", { style: { zIndex: 1 }, children: [
/* @__PURE__ */ jsx9("h3", { style: {
fontSize: "20px",
fontWeight: "700",
margin: "0 0 6px 0",
letterSpacing: "-0.02em",
textShadow: "0 1px 2px rgba(0, 0, 0, 0.1)"
}, children: title }),
/* @__PURE__ */ jsx9("p", { style: {
fontSize: "14px",
opacity: 0.95,
margin: 0,
fontWeight: "400",
letterSpacing: "-0.01em"
}, children: subtitle })
] }),
/* @__PURE__ */ jsxs9("div", { style: { display: "flex", gap: "8px", zIndex: 2 }, children: [
products.length > 0 && onToggleProducts && /* @__PURE__ */ jsx9(
"button",
{
onClick: onToggleProducts,
style: {
background: "rgba(255, 255, 255, 0.2)",
border: "1px solid rgba(255, 255, 255, 0.3)",
borderRadius: "50%",
width: "36px",
height: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
backdropFilter: "blur(10px)",
boxShadow: "0 4px 16px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
},
children: /* @__PURE__ */ jsx9("svg", { width: "16", height: "16", fill: "none", stroke: "white", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) })
}
),
products.length > 0 && onToggleOrders && /* @__PURE__ */ jsx9(
"button",
{
onClick: onToggleOrders,
style: {
background: "rgba(255, 255, 255, 0.2)",
border: "1px solid rgba(255, 255, 255, 0.3)",
borderRadius: "50%",
width: "36px",
height: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
backdropFilter: "blur(10px)",
boxShadow: "0 4px 16px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
},
children: /* @__PURE__ */ jsx9("svg", { width: "16", height: "16", fill: "none", stroke: "white", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" }) })
}
),
bookingEnabled && services.length > 0 && onToggleServices && /* @__PURE__ */ jsx9(
"button",
{
onClick: onToggleServices,
style: {
background: "rgba(255, 255, 255, 0.2)",
border: "1px solid rgba(255, 255, 255, 0.3)",
borderRadius: "50%",
width: "36px",
height: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
backdropFilter: "blur(10px)",
boxShadow: "0 4px 16px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
},
children: /* @__PURE__ */ jsx9("svg", { width: "16", height: "16", fill: "none", stroke: "white", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3a2 2 0 012-2h4a2 2 0 012 2v4m-6 0V6a2 2 0 012-2h4a2 2 0 012 2v1m-6 0h6m-6 0l-.5 8.5A2 2 0 0013.5 21h-3A2 2 0 018.5 15.5L8 7z" }) })
}
),
bookingEnabled && onToggleBookings && /* @__PURE__ */ jsx9(
"button",
{
onClick: onToggleBookings,
style: {
background: "rgba(255, 255, 255, 0.2)",
border: "1px solid rgba(255, 255, 255, 0.3)",
borderRadius: "50%",
width: "36px",
height: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
backdropFilter: "blur(10px)",
boxShadow: "0 4px 16px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
},
children: /* @__PURE__ */ jsx9("svg", { width: "16", height: "16", fill: "none", stroke: "white", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3a2 2 0 012-2h4a2 2 0 012 2v4m-6 0V6a2 2 0 012-2h4a2 2 0 012 2v1m-6 0h6m-6 0l-.5 8.5A2 2 0 0013.5 21h-3A2 2 0 018.5 15.5L8 7z" }) })
}
),
/* @__PURE__ */ jsx9(
"button",
{
onClick: onClose,
style: {
background: "rgba(255, 255, 255, 0.2)",
border: "1px solid rgba(255, 255, 255, 0.3)",
borderRadius: "50%",
width: "36px",
height: "36px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
zIndex: 2,
backdropFilter: "blur(10px)",
boxShadow: "0 4px 16px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
},
onMouseEnter: (e) => {
e.currentTarget.style.background = "rgba(255, 255, 255, 0.3)";
e.currentTarget.style.transform = "scale(1.1) rotate(90deg)";
e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.5)";
},
onMouseLeave: (e) => {
e.currentTarget.style.background = "rgba(255, 255, 255, 0.2)";
e.currentTarget.style.transform = "scale(1) rotate(0deg)";
e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.3)";
},
children: /* @__PURE__ */ jsx9("svg", { width: "16", height: "16", fill: "none", stroke: "white", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
}
)
] })
] }),
/* @__PURE__ */ jsxs9("div", { style: {
flex: 1,
padding: "28px",
overflowY: "auto",
display: "flex",
flexDirection: "column",
gap: "20px",
background: "radial-gradient(circle at 30% 20%, rgba(102, 126, 234, 0.02) 0%, transparent 60%), radial-gradient(circle at 70% 80%, rgba(139, 92, 246, 0.01) 0%, transparent 60%), linear-gradient(180deg, rgba(248, 250, 252, 0.6) 0%, rgba(255, 255, 255, 0.8) 100%)",
position: "relative"
}, children: [
messages.length === 0 ? /* @__PURE__ */ jsxs9("div", { style: {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
height: "100%",
textAlign: "center",
padding: "40px 20px"
}, children: [
/* @__PURE__ */ jsx9("div", { style: {
width: "80px",
height: "80px",
borderRadius: "50%",
background: `linear-gradient(135deg, ${primaryColor}15, ${primaryColor}08)`,
display: "flex",
alignItems: "center",
justifyContent: "center",
marginBottom: "20px",
boxShadow: "0 8px 32px rgba(102, 126, 234, 0.1)"
}, children: /* @__PURE__ */ jsx9("svg", { width: "36", height: "36", fill: "none", stroke: "#9ca3af", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" }) }) }),
/* @__PURE__ */ jsx9("h4", { style: {
fontSize: "20px",
fontWeight: "600",
color: "#1f2937",
margin: "0 0 12px 0",
letterSpacing: "-0.01em"
}, children: title }),
/* @__PURE__ */ jsx9("p", { style: {
fontSize: "15px",
color: "#6b7280",
margin: "0 0 8px 0",
lineHeight: "1.5"
}, children: subtitle }),
/* @__PURE__ */ jsx9("p", { style: {
fontSize: "13px",
color: "#9ca3af",
margin: 0
}, children: "Type a message to get started" })
] }) : showOrders ? /* @__PURE__ */ jsx9(
OrdersView,
{
orders,
onClose: () => onToggleOrders && onToggleOrders(),
onPayOrder
}
) : showProducts ? /* @__PURE__ */ jsxs9("div", { children: [
/* @__PURE__ */ jsx9("h4", { style: { fontSize: "18px", fontWeight: "600", marginBottom: "16px", color: "#1f2937" }, children: "Products" }),
products.map((product) => /* @__PURE__ */ jsx9(
ProductCard,
{
product,
onBuyNow: onBuyProduct,
primaryColor
},
product._id
))
] }) : showServices ? /* @__PURE__ */ jsxs9("div", { children: [
/* @__PURE__ */ jsx9("h4", { style: { fontSize: "18px", fontWeight: "600", marginBottom: "16px", color: "#1f2937" }, children: "Services" }),
services.map((service) => /* @__PURE__ */ jsx9(
ServiceCard,
{
service,
onBook: onBookService
},
service._id
))
] }) : showBookings ? /* @__PURE__ */ jsx9(
BookingsView,
{
bookings,
onClose: () => onToggleBookings && onToggleBookings()
}
) : /* @__PURE__ */ jsx9(MessageList, { messages, primaryColor }),
error && /* @__PURE__ */ jsx9("div", { style: {
background: "rgba(254, 242, 242, 0.95)",
border: "1px solid rgba(252, 165, 165, 0.6)",
borderRadius: "16px",
padding: "16px",
margin: "16px 0",
backdropFilter: "blur(10px)"
}, children: /* @__PURE__ */ jsxs9("div", { style: {
display: "flex",
alignItems: "center",
gap: "12px"
}, children: [
/* @__PURE__ */ jsx9("svg", { width: "20", height: "20", fill: "none", stroke: "#ef4444", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
/* @__PURE__ */ jsx9("p", { style: {
fontSize: "14px",
color: "#dc2626",
margin: 0,
fontWeight: "500"
}, children: error })
] }) }),
/* @__PURE__ */ jsx9("div", { ref: messagesEndRef })
] }),
/* @__PURE__ */ jsx9(
MessageInput,
{
value: input,
onChange: setInput,
onSend: handleSend,
isLoading,
placeholder,
primaryColor,
voiceEnabled
}
)
] });
}
// src/components/PaymentModal.tsx
import { useEffect as useEffect3 } from "react";
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
function PaymentModal({
isOpen,
onClose,
product,
order,
customerEmail,
paystackPublicKey,
onPaymentSuccess,
onPaymentError
}) {
useEffect3(() => {
if (isOpen && !window.PaystackPop) {
const script = document.createElement("script");
script.src = "https://js.paystack.co/v1/inline.js";
script.async = true;
document.body.appendChild(script);
}
}, [isOpen]);
const handlePayment = () => {
console.log("Payment attempt with key:", paystackPublicKey);
console.log("Order:", order);
console.log("Product:", product);
if (!paystackPublicKey) {
onPaymentError("Paystack public key not configured");
return;
}
if (!window.PaystackPop || !order || !product) {
onPaymentError("Payment system not loaded");
return;
}
const handler = window.PaystackPop.setup({
key: paystackPublicKey,
email: customerEmail,
amount: order.amount * 100,
// Paystack expects amount in kobo
currency: order.currency,
ref: `order_${order.orderId}_${Date.now()}`,
metadata: {
orderId: order.orderId,
productName: product.name
},
callback: function(response) {
onPaymentSuccess(response.reference);
onClose();
},
onClose: function() {
onClose();
}
});
handler.openIframe();
};
if (!isOpen || !product || !order) return null;
return /* @__PURE__ */ jsx10(
"div",
{
style: {
position: "fixed",
inset: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)",
display: "flex",
alignItems: "center",
justifyContent: "center",
zIndex: 1e6,
backdropFilter: "blur(4px)"
},
onClick: onClose,
children: /* @__PURE__ */ jsxs10(
"div",
{
style: {
backgroundColor: "white",
borderRadius: "16px",
padding: "24px",
maxWidth: "400px",
width: "90%",
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.15)",
position: "relative"
},
onClick: (e) => e.stopPropagation(),
children: [
/* @__PURE__ */ jsx10(
"button",
{
onClick: onClose,
style: {
position: "absolute",
top: "16px",
right: "16px",
background: "none",
border: "none",
fontSize: "24px",
cursor: "pointer",
color: "#6b7280"
},
children: "\xD7"
}
),
/* @__PURE__ */ jsx10(
"h3",
{
style: {
fontSize: "20px",
fontWeight: "600",
marginBottom: "16px",
color: "#1f2937"
},
children: "Complete Payment"
}
),
/* @__PURE__ */ jsxs10("div", { style: { marginBottom: "20px" }, children: [
/* @__PURE__ */ jsxs10(
"div",
{
style: {
display: "flex",
alignItems: "center",
gap: "12px",
marginBottom: "16px"
},
children: [
product.imageUrl && /* @__PURE__ */ jsx10(
"img",
{
src: product.imageUrl,
alt: product.name,
style: {
width: "60px",
height: "60px",
objectFit: "cover",
borderRadius: "8px"
}
}
),
/* @__PURE__ */ jsxs10("div", { children: [
/* @__PURE__ */ jsx10(
"h4",
{
style: {
fontSize: "16px",
fontWeight: "600",
margin: "0 0 4px 0",
color: "#1f2937"
},
children: product.name
}
),
/* @__PURE__ */ jsx10(
"p",
{
style: {
fontSize: "14px",
color: "#6b7280",
margin: 0
},
children: product.description
}
)
] })
]
}
),
/* @__PURE__ */ jsxs10(
"div",
{
style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "16px",
backgroundColor: "#f9fafb",
borderRadius: "8px",
marginBottom: "20px"
},
children: [
/* @__PURE__ */ jsx10("span", { style: { fontSize: "16px", color: "#374151" }, children: "Total:" }),
/* @__PURE__ */ jsxs10(
"span",
{
style: {
fontSize: "20px",
fontWeight: "700",
color: "#1f2937"
},
children: [
order.currency,
" ",
order.amount
]
}
)
]
}
),
/* @__PURE__ */ jsx10("p", { style: { fontSize: "14px", color: "#6b7280", marginBottom: "20px" }, children: "Payment will be processed securely through Paystack" })
] }),
/* @__PURE__ */ jsxs10("div", { style: { display: "flex", gap: "12px" }, children: [
/* @__PURE__ */ jsx10(
"button",
{
onClick: onClose,
style: {
flex: 1,
padding: "12px 16px",
border: "1px solid #d1d5db",
borderRadius: "8px",
backgroundColor: "white",
color: "#374151",
fontSize: "14px",
fontWeight: "500",
cursor: "pointer"
},
children: "Cancel"
}
),