@jutech-devs/agent-sdk
Version:
Modern embeddable AI agent chat widget with voice support and payment integration
1,183 lines (1,169 loc) • 171 kB
JavaScript
"use client"
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
AgentWidget: () => AgentWidget,
ChatbotEmbed: () => ChatbotEmbed
});
module.exports = __toCommonJS(index_exports);
// src/AgentWidget.tsx
var import_react9 = require("react");
// src/components/ChatBubble.tsx
var import_jsx_runtime = require("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__ */ (0, import_jsx_runtime.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__ */ (0, import_jsx_runtime.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__ */ (0, import_jsx_runtime.jsx)(
"svg",
{
width: "24",
height: "24",
fill: "none",
stroke: "currentColor",
viewBox: "0 0 24 24",
children: /* @__PURE__ */ (0, import_jsx_runtime.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
var import_react4 = require("react");
// src/components/MessageList.tsx
var import_jsx_runtime2 = require("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__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
marginBottom: "16px",
display: "flex",
gap: "8px",
justifyContent: "flex-start",
alignItems: "flex-end"
}, children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "14", height: "14", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsxs)("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__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
display: "flex",
gap: "4px",
alignItems: "center"
}, children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out"
} }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.2s"
} }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.4s"
} })
] }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#6b7280", fontSize: "13px" }, children: "AI is thinking..." })
] })
] });
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [
messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
"div",
{
style: {
display: "flex",
justifyContent: message.role === "user" ? "flex-end" : "flex-start",
marginBottom: "16px",
alignItems: "flex-end",
gap: "8px"
},
children: [
message.role === "assistant" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "14", height: "14", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsxs)("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__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
message.content || (message.isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
display: "flex",
gap: "4px",
padding: "8px 0",
alignItems: "center"
}, children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out"
} }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.2s"
} }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.4s"
} })
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
display: "flex",
gap: "4px",
padding: "8px 0",
alignItems: "center"
}, children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out"
} }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
width: "8px",
height: "8px",
borderRadius: "50%",
backgroundColor: "#6b7280",
animation: "typing 1.4s infinite ease-in-out",
animationDelay: "0.2s"
} }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginTop: "12px", display: "flex", flexWrap: "wrap", gap: "8px" }, children: [
message.metadata.showProducts && onShowProducts && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: onShowProducts, style: buttonStyle, children: "\u{1F6CD}\uFE0F View Products" }),
message.metadata.showServices && onShowServices && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: onShowServices, style: buttonStyle, children: "\u{1F6CE}\uFE0F View Services" }),
message.metadata.showBookingForm && onShowBookingForm && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: onShowBookingForm, style: buttonStyle, children: "\u{1F4C5} Book Now" }),
message.metadata.showPaymentOptions && onShowPaymentOptions && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: onShowPaymentOptions, style: buttonStyle, children: "\u{1F4B3} Payment Options" }),
message.metadata.showInvoice && onShowInvoice && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: onShowInvoice, style: buttonStyle, children: "\u{1F9FE} View Invoice" }),
message.metadata.requestFeedback && onRequestFeedback && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: onRequestFeedback, style: buttonStyle, children: "\u2B50 Rate Experience" }),
message.metadata.showCart && onShowCart && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: onShowCart, style: buttonStyle, children: "\u{1F6D2} View Cart" }),
message.metadata.showEndButton && onEndConversation && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: onEndConversation, style: buttonStyle, children: "\u2705 End Conversation" })
] })
] }),
message.timestamp && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "14", height: "14", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)(ThinkingIndicator, {})
] });
}
// src/hooks/useVoice.ts
var import_react = require("react");
function useVoice({ onTranscript, enabled = true }) {
const [isListening, setIsListening] = (0, import_react.useState)(false);
const [isSupported, setIsSupported] = (0, import_react.useState)(() => {
if (typeof window !== "undefined") {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
return !!SpeechRecognition && enabled;
}
return false;
});
const recognitionRef = (0, import_react.useRef)(null);
const createRecognition = (0, import_react.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 = (0, import_react.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 = (0, import_react.useCallback)(() => {
console.log("Stopping voice recognition...");
if (recognitionRef.current) {
recognitionRef.current.stop();
recognitionRef.current = null;
}
setIsListening(false);
}, []);
const speak = (0, import_react.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
var import_jsx_runtime3 = require("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__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
padding: "16px",
borderTop: "1px solid #e5e7eb",
backgroundColor: "white"
}, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
display: "flex",
gap: "8px",
alignItems: "flex-end"
}, children: [
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
"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__ */ (0, import_jsx_runtime3.jsx)(
"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__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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__ */ (0, import_jsx_runtime3.jsx)(
"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__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
width: "20px",
height: "20px",
border: "2px solid transparent",
borderTop: "2px solid currentColor",
borderRadius: "50%",
animation: "spin 1s linear infinite"
} }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8" }) })
}
)
] }) });
}
// src/components/ProductCard.tsx
var import_jsx_runtime4 = require("react/jsx-runtime");
function ProductCard({ product, onBuyNow, primaryColor }) {
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "16px",
margin: "8px 0",
backgroundColor: "#ffffff"
},
children: [
(product.imageUrl || product.images?.[0]) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"img",
{
src: product.imageUrl || product.images[0],
alt: product.name,
style: {
width: "100%",
height: "120px",
objectFit: "cover",
borderRadius: "6px",
marginBottom: "12px"
}
}
),
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"h3",
{
style: {
fontSize: "16px",
fontWeight: "600",
margin: "0 0 8px 0",
color: "#1f2937"
},
children: product.name
}
),
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"p",
{
style: {
fontSize: "14px",
color: "#6b7280",
margin: "0 0 12px 0",
lineHeight: "1.4"
},
children: product.description
}
),
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
"div",
{
style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center"
},
children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
"span",
{
style: {
fontSize: "18px",
fontWeight: "700",
color: primaryColor
},
children: [
product.currency,
" ",
product.price
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"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
var import_jsx_runtime5 = require("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__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { padding: "20px" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "20px"
}, children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { style: {
fontSize: "18px",
fontWeight: "600",
margin: 0,
color: "#1f2937"
}, children: "My Orders" }),
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
fontSize: "24px",
cursor: "pointer",
color: "#6b7280",
padding: "4px"
},
children: "\xD7"
}
)
] }),
orders.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: {
textAlign: "center",
padding: "40px 20px",
color: "#6b7280"
}, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "No orders found" }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: orders.map((order) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "16px",
backgroundColor: "#ffffff"
},
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: "12px" }, children: [
order.productId && order.productId.imageUrl && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
"img",
{
src: order.productId.imageUrl,
alt: order.productId.name,
style: {
width: "60px",
height: "60px",
objectFit: "cover",
borderRadius: "6px"
}
}
),
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { flex: 1 }, children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h4", { style: {
fontSize: "16px",
fontWeight: "600",
margin: "0 0 4px 0",
color: "#1f2937"
}, children: order.productId ? order.productId.name : order.itemName || "Service Order" }),
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: {
fontSize: "14px",
color: "#6b7280",
margin: "0 0 8px 0"
}, children: order.productId ? order.productId.description : "Service booking order" }),
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}, children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: {
fontSize: "16px",
fontWeight: "600",
color: "#1f2937"
}, children: [
order.currency,
" ",
order.amount
] }),
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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__ */ (0, import_jsx_runtime5.jsx)("p", { style: {
fontSize: "12px",
color: "#9ca3af",
margin: "8px 0 0 0"
}, children: formatDate(order.createdAt) }),
order.trackingNumber && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { style: {
fontSize: "12px",
color: "#3b82f6",
margin: "4px 0 0 0",
fontWeight: "500"
}, children: [
"Tracking: ",
order.trackingNumber
] }),
order.deliveryAddress && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("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__ */ (0, import_jsx_runtime5.jsx)(
"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
var import_jsx_runtime6 = require("react/jsx-runtime");
function ServiceCard({ service, onBook }) {
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
"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__ */ (0, import_jsx_runtime6.jsx)(
"img",
{
src: service.imageUrl,
alt: service.name,
style: {
width: "100%",
height: "120px",
objectFit: "cover",
borderRadius: "6px",
marginBottom: "12px"
}
}
),
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h4", { style: { fontSize: "16px", fontWeight: "600", margin: "0 0 8px 0" }, children: service.name }),
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { fontSize: "14px", color: "#6b7280", margin: "0 0 12px 0", lineHeight: "1.4" }, children: service.description }),
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: "18px", fontWeight: "700", color: "#1f2937" }, children: [
service.currency,
" ",
service.price
] }),
service.duration && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { fontSize: "12px", color: "#6b7280" }, children: [
service.duration,
" minutes"
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
"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
var import_jsx_runtime7 = require("react/jsx-runtime");
function BookingsView({ bookings, onClose }) {
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { padding: "16px" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { style: { fontSize: "18px", fontWeight: "600", margin: 0 }, children: "My Bookings" }),
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
fontSize: "20px",
cursor: "pointer",
color: "#6b7280"
},
children: "\xD7"
}
)
] }),
bookings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { textAlign: "center", padding: "32px", color: "#6b7280" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: "No bookings found" }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: bookings.map((booking) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
"div",
{
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "12px",
backgroundColor: "#f9fafb"
},
children: [
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "start", marginBottom: "8px" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h4", { style: { fontSize: "16px", fontWeight: "600", margin: 0 }, children: booking.serviceName }),
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
"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__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontSize: "14px", color: "#6b7280", marginBottom: "4px" }, children: [
"\u{1F4C5} ",
new Date(booking.date).toLocaleDateString(),
" at ",
booking.time
] }),
booking.notes && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontSize: "14px", color: "#6b7280" }, children: [
"\u{1F4DD} ",
booking.notes
] })
]
},
booking._id
)) })
] });
}
// src/components/PaymentOptionsView.tsx
var import_jsx_runtime8 = require("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__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h4", { style: { fontSize: "18px", fontWeight: "600", margin: 0 }, children: "Payment Methods" }),
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
cursor: "pointer",
padding: "4px",
borderRadius: "4px"
},
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "grid", gap: "12px" }, children: paymentMethods.map((method) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
"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__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "24px" }, children: method.icon }),
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: 1 }, children: [
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontWeight: "600", marginBottom: "4px" }, children: method.name }),
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "14px", color: "#6b7280" }, children: method.description })
] }),
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
]
},
method.id
)) }),
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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
var import_jsx_runtime9 = require("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__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { style: { fontSize: "18px", fontWeight: "600", margin: 0 }, children: "Invoice" }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
"button",
{
onClick: onClose,
style: {
background: "none",
border: "none",
cursor: "pointer",
padding: "4px",
borderRadius: "4px"
},
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "16", height: "16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
padding: "20px",
backgroundColor: "white"
}, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { marginBottom: "24px", textAlign: "center" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { style: { fontSize: "24px", fontWeight: "bold", color: primaryColor, margin: "0 0 8px 0" }, children: "INVOICE" }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { style: { margin: 0, color: "#6b7280" }, children: [
"#",
mockInvoice.id
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "20px", marginBottom: "24px" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { style: { fontSize: "14px", fontWeight: "600", marginBottom: "8px", color: "#374151" }, children: "Bill To:" }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { margin: "0 0 4px 0", fontWeight: "600" }, children: mockInvoice.customerName }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { margin: 0, color: "#6b7280", fontSize: "14px" }, children: mockInvoice.customerEmail })
] }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { textAlign: "right" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { style: { fontSize: "14px", fontWeight: "600", marginBottom: "8px", color: "#374151" }, children: "Invoice Date:" }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { margin: 0, fontSize: "14px" }, children: mockInvoice.date })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { marginBottom: "24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { style: { borderBottom: "2px solid #e5e7eb" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: { textAlign: "left", padding: "12px 0", fontSize: "14px", fontWeight: "600" }, children: "Item" }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: { textAlign: "center", padding: "12px 0", fontSize: "14px", fontWeight: "600" }, children: "Qty" }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { style: { textAlign: "right", padding: "12px 0", fontSize: "14px", fontWeight: "600" }, children: "Price" })
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("tbody", { children: mockInvoice.items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { style: { borderBottom: "1px solid #f3f4f6" }, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: { padding: "12px 0", fontSize: "14px" }, children: item.name }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { style: { textAlign: "center", padding: "12px 0", fontSize: "14px" }, children: item.quantity }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("td", { style: { textAlign: "right", padding: "12px 0", fontSize: "14px" }, children: [
mockInvoice.currency,
" ",
item.price.toFixed(2)
] })
] }, index)) })
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { textAlign: "right", marginBottom: "24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: {