@jutech-devs/agent-sdk
Version:
Modern embeddable AI agent chat widget with voice support and payment integration
1,275 lines (1,262 loc) • 160 kB
JavaScript
"use client"
// src/AgentWidget.tsx
import { useState as useState8, useEffect as useEffect4 } from "react";
// src/components/ChatBubble.tsx
import { jsx, jsxs } from "react/jsx-runtime";
function ChatBubble({ isOpen, onClick, primaryColor = "#3b82f6", unreadCount = 0, position = "bottom-right" }) {
const adjustColor = (color, percent) => {
const num = parseInt(color.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = (num >> 16) + amt;
const G = (num >> 8 & 255) + amt;
const B = (num & 255) + amt;
return "#" + (16777216 + (R < 255 ? R < 1 ? 0 : R : 255) * 65536 + (G < 255 ? G < 1 ? 0 : G : 255) * 256 + (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
};
return /* @__PURE__ */ jsxs(
"button",
{
onClick,
style: {
position: "fixed",
bottom: position.includes("bottom") ? "20px" : "auto",
top: position.includes("top") ? "20px" : "auto",
right: position.includes("right") ? "20px" : "auto",
left: position.includes("left") ? "20px" : "auto",
width: "60px",
height: "60px",
borderRadius: "50%",
backgroundColor: primaryColor,
color: "white",
border: "none",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "24px",
boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
zIndex: 9999,
transition: "all 0.2s ease"
},
onMouseEnter: (e) => {
e.currentTarget.style.backgroundColor = adjustColor(primaryColor, -10);
e.currentTarget.style.transform = "scale(1.05)";
},
onMouseLeave: (e) => {
e.currentTarget.style.backgroundColor = primaryColor;
e.currentTarget.style.transform = "scale(1)";
},
"aria-label": "Open chat",
children: [
unreadCount > 0 && /* @__PURE__ */ jsx("span", { style: {
position: "absolute",
top: "-2px",
right: "-2px",
background: "linear-gradient(135deg, #ef4444, #dc2626)",
color: "white",
borderRadius: "50%",
minWidth: "20px",
height: "20px",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "11px",
fontWeight: "600",
padding: "0 4px",
border: "2px solid white"
}, children: unreadCount > 9 ? "9+" : unreadCount }),
/* @__PURE__ */ jsx(
"svg",
{
width: "24",
height: "24",
fill: "none",
stroke: "currentColor",
viewBox: "0 0 24 24",
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 useState4, useRef as useRef2, useEffect } from "react";
// src/components/MessageList.tsx
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
function MessageList({
messages,
primaryColor,
isLoading = false,
onEndConversation,
onShowProducts,
onShowServices,
onShowBookingForm,
onShowPaymentOptions,
onShowInvoice,
onRequestFeedback,
onShowCart
}) {
const adjustColor = (color, percent) => {
const num = parseInt(color.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = (num >> 16) + amt;
const G = (num >> 8 & 255) + amt;
const B = (num & 255) + amt;
return "#" + (16777216 + (R < 255 ? R < 1 ? 0 : R : 255) * 65536 + (G < 255 ? G < 1 ? 0 : G : 255) * 256 + (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
};
const buttonStyle = {
padding: "8px 16px",
backgroundColor: primaryColor,
color: "white",
border: "none",
borderRadius: "16px",
fontSize: "13px",
fontWeight: "500",
cursor: "pointer",
display: "inline-flex",
alignItems: "center",
gap: "6px",
transition: "all 0.2s ease"
};
const ThinkingIndicator = () => /* @__PURE__ */ jsxs2("div", { style: {
marginBottom: "16px",
display: "flex",
gap: "8px",
justifyContent: "flex-start",
alignItems: "flex-end"
}, children: [
/* @__PURE__ */ jsx2("div", { style: {
width: "28px",
height: "28px",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "12px",
flexShrink: 0,
background: `linear-gradient(135deg, ${primaryColor}, ${adjustColor(primaryColor, -10)})`,
color: "white",
marginBottom: "2px"
}, children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", 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__ */ jsxs2("div", { style: {
maxWidth: "85%",
padding: "12px 16px",
borderRadius: "16px",
borderBottomLeftRadius: "4px",
fontSize: "14px",
lineHeight: "1.5",
backgroundColor: "#f8f9fa",
color: "#1f2937",
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.1)",
display: "flex",
alignItems: "center",
gap: "8px"
}, children: [
/* @__PURE__ */ jsxs2("div", { style: {
display: "flex",
gap: "4px",
alignItems: "center"
}, children: [
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out"
} }),
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.2s"
} }),
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.4s"
} })
] }),
/* @__PURE__ */ jsx2("span", { style: { color: "#6b7280", fontSize: "13px" }, children: "AI is thinking..." })
] })
] });
return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [
messages.map((message) => /* @__PURE__ */ jsxs2(
"div",
{
style: {
display: "flex",
justifyContent: message.role === "user" ? "flex-end" : "flex-start",
marginBottom: "16px",
alignItems: "flex-end",
gap: "8px"
},
children: [
message.role === "assistant" && /* @__PURE__ */ jsx2("div", { style: {
width: "28px",
height: "28px",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "12px",
flexShrink: 0,
background: `linear-gradient(135deg, ${primaryColor}, ${adjustColor(primaryColor, -10)})`,
color: "white",
marginBottom: "2px"
}, children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", 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__ */ jsxs2("div", { style: {
maxWidth: "85%",
padding: "12px 16px",
borderRadius: "16px",
borderBottomLeftRadius: message.role === "assistant" ? "4px" : "16px",
borderBottomRightRadius: message.role === "user" ? "4px" : "16px",
fontSize: "14px",
lineHeight: "1.5",
wordWrap: "break-word",
backgroundColor: message.role === "assistant" ? "#f8f9fa" : primaryColor,
color: message.role === "assistant" ? "#1f2937" : "white",
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.1)",
marginLeft: message.role === "user" ? "auto" : "0"
}, children: [
/* @__PURE__ */ jsxs2("div", { children: [
message.content || (message.isStreaming ? /* @__PURE__ */ jsxs2("div", { style: {
display: "flex",
gap: "4px",
padding: "8px 0",
alignItems: "center"
}, children: [
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out"
} }),
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.2s"
} }),
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.4s"
} })
] }) : /* @__PURE__ */ jsxs2("div", { style: {
display: "flex",
gap: "4px",
padding: "8px 0",
alignItems: "center"
}, children: [
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out"
} }),
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.2s"
} }),
/* @__PURE__ */ jsx2("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.4s"
} })
] })),
message.role === "assistant" && message.metadata && /* @__PURE__ */ jsxs2("div", { style: { marginTop: "12px", display: "flex", flexWrap: "wrap", gap: "8px" }, children: [
message.metadata.showProducts && onShowProducts && /* @__PURE__ */ jsx2("button", { onClick: onShowProducts, style: buttonStyle, children: "\u{1F6CD}\uFE0F View Products" }),
message.metadata.showServices && onShowServices && /* @__PURE__ */ jsx2("button", { onClick: onShowServices, style: buttonStyle, children: "\u{1F6CE}\uFE0F View Services" }),
message.metadata.showBookingForm && onShowBookingForm && /* @__PURE__ */ jsx2("button", { onClick: onShowBookingForm, style: buttonStyle, children: "\u{1F4C5} Book Now" }),
message.metadata.showPaymentOptions && onShowPaymentOptions && /* @__PURE__ */ jsx2("button", { onClick: onShowPaymentOptions, style: buttonStyle, children: "\u{1F4B3} Payment Options" }),
message.metadata.showInvoice && onShowInvoice && /* @__PURE__ */ jsx2("button", { onClick: onShowInvoice, style: buttonStyle, children: "\u{1F9FE} View Invoice" }),
message.metadata.requestFeedback && onRequestFeedback && /* @__PURE__ */ jsx2("button", { onClick: onRequestFeedback, style: buttonStyle, children: "\u2B50 Rate Experience" }),
message.metadata.showCart && onShowCart && /* @__PURE__ */ jsx2("button", { onClick: onShowCart, style: buttonStyle, children: "\u{1F6D2} View Cart" }),
message.metadata.showEndButton && onEndConversation && /* @__PURE__ */ jsx2("button", { onClick: onEndConversation, style: buttonStyle, children: "\u2705 End Conversation" })
] })
] }),
message.timestamp && /* @__PURE__ */ jsx2("div", { style: {
fontSize: "10px",
color: message.role === "assistant" ? "#9ca3af" : "rgba(255,255,255,0.7)",
marginTop: "6px",
textAlign: message.role === "user" ? "right" : "left"
}, children: new Date(message.timestamp).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit"
}) })
] }),
message.role === "user" && /* @__PURE__ */ jsx2("div", { style: {
width: "28px",
height: "28px",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "12px",
flexShrink: 0,
background: "linear-gradient(135deg, #6b7280, #4b5563)",
color: "white",
marginBottom: "2px"
}, children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", 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
)),
isLoading && !messages.some((msg) => msg.isStreaming) && /* @__PURE__ */ jsx2(ThinkingIndicator, {})
] });
}
// src/hooks/useVoice.ts
import { useState, useCallback, useRef } from "react";
function useVoice({ onTranscript, enabled = true }) {
const [isListening, setIsListening] = useState(false);
const [isSupported, setIsSupported] = useState(() => {
if (typeof window !== "undefined") {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
return !!SpeechRecognition && enabled;
}
return false;
});
const recognitionRef = useRef(null);
const createRecognition = useCallback(() => {
if (typeof window === "undefined") return null;
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) return null;
const recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = "en-US";
recognition.maxAlternatives = 1;
recognition.onstart = () => {
console.log("Speech recognition started");
setIsListening(true);
};
recognition.onresult = (event) => {
console.log("Speech recognition result:", event);
console.log("Results length:", event.results.length);
for (let i = 0; i < event.results.length; i++) {
const result = event.results[i];
console.log(`Result ${i}:`, result);
console.log(`Result ${i} transcript:`, result[0]?.transcript);
if (result[0]?.transcript) {
const transcript = result[0].transcript.trim();
console.log("Final transcript:", transcript);
onTranscript(transcript);
setIsListening(false);
return;
}
}
};
recognition.onend = () => {
console.log("Speech recognition ended");
setIsListening(false);
};
recognition.onerror = (event) => {
console.error("Speech recognition error:", event.error);
setIsListening(false);
};
return recognition;
}, [onTranscript]);
const startListening = useCallback(() => {
console.log("Starting voice recognition...");
if (recognitionRef.current) {
recognitionRef.current.abort();
}
const recognition = createRecognition();
if (!recognition) {
console.error("Speech recognition not supported");
return;
}
recognitionRef.current = recognition;
try {
recognition.start();
} catch (error) {
console.error("Failed to start recognition:", error);
setIsListening(false);
}
}, [createRecognition]);
const stopListening = useCallback(() => {
console.log("Stopping voice recognition...");
if (recognitionRef.current) {
recognitionRef.current.stop();
recognitionRef.current = null;
}
setIsListening(false);
}, []);
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 jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
function MessageInput({ value, onChange, onSend, isLoading, placeholder, primaryColor, voiceEnabled = false }) {
const { isListening, isSupported, startListening, stopListening } = useVoice({
onTranscript: (text) => {
console.log("MessageInput received transcript:", text);
onChange(text);
console.log("MessageInput onChange called with:", text);
},
enabled: voiceEnabled
});
const handleKeyPress = (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
onSend();
}
};
const handleVoiceToggle = () => {
console.log("Voice toggle clicked, isListening:", isListening);
if (isListening) {
console.log("Stopping voice recognition");
stopListening();
} else {
console.log("Starting voice recognition");
startListening();
}
};
const adjustColor = (color, percent) => {
const num = parseInt(color.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = (num >> 16) + amt;
const G = (num >> 8 & 255) + amt;
const B = (num & 255) + amt;
return "#" + (16777216 + (R < 255 ? R < 1 ? 0 : R : 255) * 65536 + (G < 255 ? G < 1 ? 0 : G : 255) * 256 + (B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1);
};
return /* @__PURE__ */ jsx3("div", { style: {
padding: "16px",
borderTop: "1px solid #e5e7eb",
backgroundColor: "white"
}, children: /* @__PURE__ */ jsxs3("div", { style: {
display: "flex",
gap: "8px",
alignItems: "flex-end"
}, children: [
/* @__PURE__ */ jsx3(
"textarea",
{
value,
onChange: (e) => onChange(e.target.value),
onKeyPress: handleKeyPress,
placeholder,
disabled: isLoading,
rows: 1,
style: {
flex: 1,
border: "1px solid #e5e7eb",
borderRadius: "20px",
padding: "12px 16px",
fontSize: "14px",
outline: "none",
resize: "none",
overflow: "hidden",
minHeight: "44px",
maxHeight: "120px",
backgroundColor: "white",
color: "#1f2937",
fontFamily: "inherit"
},
onFocus: (e) => {
e.target.style.borderColor = primaryColor;
e.target.style.boxShadow = `0 0 0 3px ${primaryColor}20`;
},
onBlur: (e) => {
e.target.style.borderColor = "#e5e7eb";
e.target.style.boxShadow = "none";
},
onInput: (e) => {
const target = e.target;
target.style.height = "auto";
target.style.height = Math.min(target.scrollHeight, 120) + "px";
}
}
),
voiceEnabled && !value.trim() ? /* @__PURE__ */ jsx3(
"button",
{
onClick: handleVoiceToggle,
disabled: !isSupported,
style: {
background: isListening ? `linear-gradient(135deg, #ef4444, #dc2626)` : `linear-gradient(135deg, ${primaryColor}, ${adjustColor(primaryColor, -10)})`,
color: "white",
border: "none",
borderRadius: "50%",
width: "44px",
height: "44px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: !isSupported ? "not-allowed" : "pointer",
opacity: !isSupported ? 0.5 : 1,
transition: "all 0.2s ease",
animation: isListening ? "pulse 1.5s infinite" : "none"
},
onMouseEnter: (e) => {
if (isSupported) {
e.currentTarget.style.transform = "scale(1.05)";
}
},
onMouseLeave: (e) => {
e.currentTarget.style.transform = "scale(1)";
},
"aria-label": isListening ? "Stop recording" : "Start voice recording",
children: /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" }) })
}
) : /* @__PURE__ */ jsx3(
"button",
{
onClick: onSend,
disabled: !value.trim() || isLoading,
style: {
background: `linear-gradient(135deg, ${primaryColor}, ${adjustColor(primaryColor, -10)})`,
color: "white",
border: "none",
borderRadius: "50%",
width: "44px",
height: "44px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: !value.trim() || isLoading ? "not-allowed" : "pointer",
opacity: !value.trim() || isLoading ? 0.5 : 1,
transition: "all 0.2s ease"
},
onMouseEnter: (e) => {
if (!(!value.trim() || isLoading)) {
e.currentTarget.style.transform = "scale(1.05)";
}
},
onMouseLeave: (e) => {
e.currentTarget.style.transform = "scale(1)";
},
"aria-label": "Send message",
children: isLoading ? /* @__PURE__ */ jsx3("div", { style: {
width: "20px",
height: "20px",
border: "2px solid transparent",
borderTop: "2px solid currentColor",
borderRadius: "50%",
animation: "spin 1s linear infinite"
} }) : /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("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 jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
function ProductCard({ product, onBuyNow, primaryColor }) {
return /* @__PURE__ */ jsxs4(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "16px",
margin: "8px 0",
backgroundColor: "#ffffff"
},
children: [
(product.imageUrl || product.images?.[0]) && /* @__PURE__ */ jsx4(
"img",
{
src: product.imageUrl || product.images[0],
alt: product.name,
style: {
width: "100%",
height: "120px",
objectFit: "cover",
borderRadius: "6px",
marginBottom: "12px"
}
}
),
/* @__PURE__ */ jsx4(
"h3",
{
style: {
fontSize: "16px",
fontWeight: "600",
margin: "0 0 8px 0",
color: "#1f2937"
},
children: product.name
}
),
/* @__PURE__ */ jsx4(
"p",
{
style: {
fontSize: "14px",
color: "#6b7280",
margin: "0 0 12px 0",
lineHeight: "1.4"
},
children: product.description
}
),
/* @__PURE__ */ jsxs4(
"div",
{
style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center"
},
children: [
/* @__PURE__ */ jsxs4(
"span",
{
style: {
fontSize: "18px",
fontWeight: "700",
color: primaryColor
},
children: [
product.currency,
" ",
product.price
]
}
),
/* @__PURE__ */ jsx4(
"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 jsx5, jsxs as jsxs5 } 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__ */ jsxs5("div", { style: { padding: "20px" }, children: [
/* @__PURE__ */ jsxs5("div", { style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "20px"
}, children: [
/* @__PURE__ */ jsx5("h3", { style: {
fontSize: "18px",
fontWeight: "600",
margin: 0,
color: "#1f2937"
}, children: "My Orders" }),
/* @__PURE__ */ jsx5(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
fontSize: "24px",
cursor: "pointer",
color: "#6b7280",
padding: "4px"
},
children: "\xD7"
}
)
] }),
orders.length === 0 ? /* @__PURE__ */ jsx5("div", { style: {
textAlign: "center",
padding: "40px 20px",
color: "#6b7280"
}, children: /* @__PURE__ */ jsx5("p", { children: "No orders found" }) }) : /* @__PURE__ */ jsx5("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: orders.map((order) => /* @__PURE__ */ jsx5(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "16px",
backgroundColor: "#ffffff"
},
children: /* @__PURE__ */ jsxs5("div", { style: { display: "flex", gap: "12px" }, children: [
order.productId && order.productId.imageUrl && /* @__PURE__ */ jsx5(
"img",
{
src: order.productId.imageUrl,
alt: order.productId.name,
style: {
width: "60px",
height: "60px",
objectFit: "cover",
borderRadius: "6px"
}
}
),
/* @__PURE__ */ jsxs5("div", { style: { flex: 1 }, children: [
/* @__PURE__ */ jsx5("h4", { style: {
fontSize: "16px",
fontWeight: "600",
margin: "0 0 4px 0",
color: "#1f2937"
}, children: order.productId ? order.productId.name : order.itemName || "Service Order" }),
/* @__PURE__ */ jsx5("p", { style: {
fontSize: "14px",
color: "#6b7280",
margin: "0 0 8px 0"
}, children: order.productId ? order.productId.description : "Service booking order" }),
/* @__PURE__ */ jsxs5("div", { style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}, children: [
/* @__PURE__ */ jsxs5("span", { style: {
fontSize: "16px",
fontWeight: "600",
color: "#1f2937"
}, children: [
order.currency,
" ",
order.amount
] }),
/* @__PURE__ */ jsx5("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__ */ jsx5("p", { style: {
fontSize: "12px",
color: "#9ca3af",
margin: "8px 0 0 0"
}, children: formatDate(order.createdAt) }),
order.trackingNumber && /* @__PURE__ */ jsxs5("p", { style: {
fontSize: "12px",
color: "#3b82f6",
margin: "4px 0 0 0",
fontWeight: "500"
}, children: [
"Tracking: ",
order.trackingNumber
] }),
order.deliveryAddress && /* @__PURE__ */ jsxs5("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__ */ jsx5(
"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 jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
function ServiceCard({ service, onBook }) {
return /* @__PURE__ */ jsxs6(
"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__ */ jsx6(
"img",
{
src: service.imageUrl,
alt: service.name,
style: {
width: "100%",
height: "120px",
objectFit: "cover",
borderRadius: "6px",
marginBottom: "12px"
}
}
),
/* @__PURE__ */ jsx6("h4", { style: { fontSize: "16px", fontWeight: "600", margin: "0 0 8px 0" }, children: service.name }),
/* @__PURE__ */ jsx6("p", { style: { fontSize: "14px", color: "#6b7280", margin: "0 0 12px 0", lineHeight: "1.4" }, children: service.description }),
/* @__PURE__ */ jsxs6("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
/* @__PURE__ */ jsxs6("div", { children: [
/* @__PURE__ */ jsxs6("span", { style: { fontSize: "18px", fontWeight: "700", color: "#1f2937" }, children: [
service.currency,
" ",
service.price
] }),
service.duration && /* @__PURE__ */ jsxs6("div", { style: { fontSize: "12px", color: "#6b7280" }, children: [
service.duration,
" minutes"
] })
] }),
/* @__PURE__ */ jsx6(
"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 jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
function BookingsView({ bookings, onClose }) {
return /* @__PURE__ */ jsxs7("div", { style: { padding: "16px" }, children: [
/* @__PURE__ */ jsxs7("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }, children: [
/* @__PURE__ */ jsx7("h3", { style: { fontSize: "18px", fontWeight: "600", margin: 0 }, children: "My Bookings" }),
/* @__PURE__ */ jsx7(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
fontSize: "20px",
cursor: "pointer",
color: "#6b7280"
},
children: "\xD7"
}
)
] }),
bookings.length === 0 ? /* @__PURE__ */ jsx7("div", { style: { textAlign: "center", padding: "32px", color: "#6b7280" }, children: /* @__PURE__ */ jsx7("p", { children: "No bookings found" }) }) : /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: bookings.map((booking) => /* @__PURE__ */ jsxs7(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "12px",
backgroundColor: "#f9fafb"
},
children: [
/* @__PURE__ */ jsxs7("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "start", marginBottom: "8px" }, children: [
/* @__PURE__ */ jsx7("h4", { style: { fontSize: "16px", fontWeight: "600", margin: 0 }, children: booking.serviceName }),
/* @__PURE__ */ jsx7(
"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__ */ jsxs7("div", { style: { fontSize: "14px", color: "#6b7280", marginBottom: "4px" }, children: [
"\u{1F4C5} ",
new Date(booking.date).toLocaleDateString(),
" at ",
booking.time
] }),
booking.notes && /* @__PURE__ */ jsxs7("div", { style: { fontSize: "14px", color: "#6b7280" }, children: [
"\u{1F4DD} ",
booking.notes
] })
]
},
booking._id
)) })
] });
}
// src/components/PaymentOptionsView.tsx
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
function PaymentOptionsView({ onClose, primaryColor = "#3b82f6" }) {
const paymentMethods = [
{ id: "card", name: "Credit/Debit Card", icon: "\u{1F4B3}", description: "Visa, Mastercard, Verve" },
{ id: "mobile", name: "Mobile Money", icon: "\u{1F4F1}", description: "MTN, Vodafone, AirtelTigo" },
{ id: "bank", name: "Bank Transfer", icon: "\u{1F3E6}", description: "Direct bank transfer" },
{ id: "paystack", name: "Paystack", icon: "\u{1F4B0}", description: "Secure online payment" }
];
return /* @__PURE__ */ jsxs8("div", { children: [
/* @__PURE__ */ jsxs8("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }, children: [
/* @__PURE__ */ jsx8("h4", { style: { fontSize: "18px", fontWeight: "600", margin: 0 }, children: "Payment Methods" }),
/* @__PURE__ */ jsx8(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
cursor: "pointer",
padding: "4px",
borderRadius: "4px"
},
children: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
}
)
] }),
/* @__PURE__ */ jsx8("div", { style: { display: "grid", gap: "12px" }, children: paymentMethods.map((method) => /* @__PURE__ */ jsxs8(
"div",
{
style: {
padding: "16px",
border: "1px solid #e5e7eb",
borderRadius: "8px",
display: "flex",
alignItems: "center",
gap: "12px",
cursor: "pointer",
transition: "all 0.2s"
},
onMouseEnter: (e) => {
e.currentTarget.style.borderColor = primaryColor;
e.currentTarget.style.backgroundColor = `${primaryColor}10`;
},
onMouseLeave: (e) => {
e.currentTarget.style.borderColor = "#e5e7eb";
e.currentTarget.style.backgroundColor = "transparent";
},
children: [
/* @__PURE__ */ jsx8("div", { style: { fontSize: "24px" }, children: method.icon }),
/* @__PURE__ */ jsxs8("div", { style: { flex: 1 }, children: [
/* @__PURE__ */ jsx8("div", { style: { fontWeight: "600", marginBottom: "4px" }, children: method.name }),
/* @__PURE__ */ jsx8("div", { style: { fontSize: "14px", color: "#6b7280" }, children: method.description })
] }),
/* @__PURE__ */ jsx8("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
]
},
method.id
)) }),
/* @__PURE__ */ jsx8("div", { style: {
marginTop: "16px",
padding: "12px",
backgroundColor: "#f3f4f6",
borderRadius: "8px",
fontSize: "14px",
color: "#6b7280"
}, children: "\u{1F4A1} All payments are secured with 256-bit SSL encryption" })
] });
}
// src/components/InvoiceView.tsx
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
function InvoiceView({ onClose, primaryColor = "#3b82f6", invoice }) {
const mockInvoice = invoice || {
id: "INV-2024-001",
date: (/* @__PURE__ */ new Date()).toLocaleDateString(),
amount: 150,
currency: "GHS",
items: [
{ name: "Product/Service", quantity: 1, price: 150 }
],
customerName: "Customer",
customerEmail: "customer@example.com"
};
const handleDownload = () => {
alert("Invoice download started...");
};
const handlePrint = () => {
window.print();
};
return /* @__PURE__ */ jsxs9("div", { children: [
/* @__PURE__ */ jsxs9("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }, children: [
/* @__PURE__ */ jsx9("h4", { style: { fontSize: "18px", fontWeight: "600", margin: 0 }, children: "Invoice" }),
/* @__PURE__ */ jsx9(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
cursor: "pointer",
padding: "4px",
borderRadius: "4px"
},
children: /* @__PURE__ */ jsx9("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", 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: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "20px",
backgroundColor: "white"
}, children: [
/* @__PURE__ */ jsxs9("div", { style: { marginBottom: "24px", textAlign: "center" }, children: [
/* @__PURE__ */ jsx9("h3", { style: { fontSize: "24px", fontWeight: "bold", color: primaryColor, margin: "0 0 8px 0" }, children: "INVOICE" }),
/* @__PURE__ */ jsxs9("p", { style: { margin: 0, color: "#6b7280" }, children: [
"#",
mockInvoice.id
] })
] }),
/* @__PURE__ */ jsxs9("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "20px", marginBottom: "24px" }, children: [
/* @__PURE__ */ jsxs9("div", { children: [
/* @__PURE__ */ jsx9("h4", { style: { fontSize: "14px", fontWeight: "600", marginBottom: "8px", color: "#374151" }, children: "Bill To:" }),
/* @__PURE__ */ jsx9("p", { style: { margin: "0 0 4px 0", fontWeight: "600" }, children: mockInvoice.customerName }),
/* @__PURE__ */ jsx9("p", { style: { margin: 0, color: "#6b7280", fontSize: "14px" }, children: mockInvoice.customerEmail })
] }),
/* @__PURE__ */ jsxs9("div", { style: { textAlign: "right" }, children: [
/* @__PURE__ */ jsx9("h4", { style: { fontSize: "14px", fontWeight: "600", marginBottom: "8px", color: "#374151" }, children: "Invoice Date:" }),
/* @__PURE__ */ jsx9("p", { style: { margin: 0, fontSize: "14px" }, children: mockInvoice.date })
] })
] }),
/* @__PURE__ */ jsx9("div", { style: { marginBottom: "24px" }, children: /* @__PURE__ */ jsxs9("table", { style: { width: "100%", borderCollapse: "collapse" }, children: [
/* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs9("tr", { style: { borderBottom: "2px solid #e5e7eb" }, children: [
/* @__PURE__ */ jsx9("th", { style: { textAlign: "left", padding: "12px 0", fontSize: "14px", fontWeight: "600" }, children: "Item" }),
/* @__PURE__ */ jsx9("th", { style: { textAlign: "center", padding: "12px 0", fontSize: "14px", fontWeight: "600" }, children: "Qty" }),
/* @__PURE__ */ jsx9("th", { style: { textAlign: "right", padding: "12px 0", fontSize: "14px", fontWeight: "600" }, children: "Price" })
] }) }),
/* @__PURE__ */ jsx9("tbody", { children: mockInvoice.items.map((item, index) => /* @__PURE__ */ jsxs9("tr", { style: { borderBottom: "1px solid #f3f4f6" }, children: [
/* @__PURE__ */ jsx9("td", { style: { padding: "12px 0", fontSize: "14px" }, children: item.name }),
/* @__PURE__ */ jsx9("td", { style: { textAlign: "center", padding: "12px 0", fontSize: "14px" }, children: item.quantity }),
/* @__PURE__ */ jsxs9("td", { style: { textAlign: "right", padding: "12px 0", fontSize: "14px" }, children: [
mockInvoice.currency,
" ",
item.price.toFixed(2)
] })
] }, index)) })
] }) }),
/* @__PURE__ */ jsx9("div", { style: { textAlign: "right", marginBottom: "24px" }, children: /* @__PURE__ */ jsx9("div", { style: {
display: "inline-block",
padding: "16px",
backgroundColor: `${primaryColor}10`,
borderRadius: "8px",
border: `1px solid ${primaryColor}30`
}, children: /* @__PURE__ */ jsxs9("div", { style: { fontSize: "18px", fontWeight: "bold", color: primaryColor }, children: [
"Total: ",
mockInvoice.currency,
" ",
mockInvoice.amount.toFixed(2)
] }) }) }),
/* @__PURE__ */ jsxs9("div", { style: { display: "flex", gap: "12px", justifyContent: "center" }, children: [
/* @__PURE__ */ jsxs9(
"button",
{
onClick: handleDownload,
style: {
padding: "10px 20px",
backgroundColor: primaryColor,
color: "white",
border: "none",
borderRadius: "6px",
cursor: "pointer",
fontSize: "14px",
fontWeight: "600",
display: "flex",
alignItems: "center",
gap: "8px"
},
children: [
/* @__PURE__ */ jsx9("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }),
"Download PDF"
]
}
),
/* @__PURE__ */ jsxs9(
"button",
{
onClick: handlePrint,
style: {
padding: "10px 20px",
backgroundColor: "white",
color: primaryColor,
border: `1px solid ${primaryColor}`,
borderRadius: "6px",
cursor: "pointer",
fontSize: "14px",
fontWeight: "600",
display: "flex",
alignItems: "center",
gap: "8px"
},
children: [
/* @__PURE__ */ jsx9("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" }) }),
"Print"
]
}
)
] })
] })
] });
}
// src/components/FeedbackView.tsx
import { useState as useState2 } from "react";
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
function FeedbackView({ onClose, onSubmit, primaryColor = "#3b82f6" }) {
const [rating, setRating] = useState2(0);
const [hoveredRating, setHoveredRating] = useState2(0);
const [comment, setComment] = useState2("");
const [isSubmitting, setIsSubmitting] = useState2(false);
const handleSubmit = async () => {
if (rating === 0) {
alert("Please select a rating");
return;
}
setIsSubmitting(true);
try {
await onSubmit({ rating, comment });
alert("Thank you for your feedback!");
onClose();
} catch (error) {
alert("Failed to submit feedback. Please try again.");
} finally {
setIsSubmitting(false);
}
};
const StarIcon = ({ filled, onHover, onClick }) => /* @__PURE__ */ jsx10(
"svg",
{
width: "32",
height: "32",
viewBox: "0 0 24 24",
fill: filled ? primaryColor : "none",
stroke: filled ? primaryColor : "#d1d5db",
strokeWidth: "2",
style: { cursor: "pointer" },
onMouseEnter: onHover,
onClick,
children: /* @__PURE__ */ jsx10("polygon", { points: "12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" })
}
);
return /* @__PURE__ */ jsxs10("div", { children: [
/* @__PURE__ */ jsxs10("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }, children: [
/* @__PURE__ */ jsx10("h4", { style: { fontSize: "18px", fontWeight: "600", margin: 0 }, children: "Rate Your Experience" }),
/* @__PURE__ */ jsx10(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
cursor: "pointer",
padding: "4px",
borderRadius: "4px"
},
children: /* @__PURE__ */ jsx10("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
}
)
] }),
/* @__PURE__ */ jsxs10("div", { style: {
padding: "24px",
border: "1px solid #e5e7eb",
borderRadius: "12px",
backgroundColor: "white",
textAlign: "center"
}, children: [
/* @__PURE__ */ jsxs10("div", { style: { marginBottom: "24px" }, children: [
/* @__PURE__