feat: implement WhatsApp bot setup flow in Assistant dashboard
This commit is contained in:
@@ -85,6 +85,10 @@ export default function OrganizationEdit() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wapView?.botSession === "ok") {
|
||||
return;
|
||||
}
|
||||
|
||||
wapServerBotGetQR({
|
||||
companyId: id,
|
||||
sessionUser: SessionInfo.userId,
|
||||
|
||||
@@ -17,6 +17,7 @@ import ColorLensIcon from '@mui/icons-material/ColorLens';
|
||||
import PinDropIcon from '@mui/icons-material/PinDrop';
|
||||
import ReviewsIcon from '@mui/icons-material/Reviews';
|
||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||
import WhatsAppIcon from '@mui/icons-material/WhatsApp';
|
||||
import API from "@services/Api.Service";
|
||||
|
||||
|
||||
@@ -81,10 +82,19 @@ const AVAILABLE_FLOWS: FlowDefinition[] = [
|
||||
flowId: "notifications",
|
||||
title: "Notificaciones",
|
||||
description: "Configura canales de notificación, recordatorios y horario silencioso para la organización o un cliente.",
|
||||
category: "Negocio",
|
||||
category: "Comunicación",
|
||||
keywords: ["notificaciones", "canales", "whatsapp", "email", "sistema", "recordatorio", "silencioso", "cliente"],
|
||||
icon: NotificationsIcon
|
||||
},
|
||||
{
|
||||
id: "whatsapp-bot",
|
||||
flowId: "whatsapp-bot",
|
||||
title: "WhatsApp",
|
||||
description: "Configura tu bot de WhatsApp para enviar notificaciones automáticas a tus clientes.",
|
||||
category: "Comunicación",
|
||||
keywords: ["whatsapp", "bot", "notificaciones", "wap", "mensajes", "automatico", "qr"],
|
||||
icon: WhatsAppIcon
|
||||
},
|
||||
{
|
||||
id: "new-service",
|
||||
flowId: "onboarding-service&action=new-service",
|
||||
|
||||
@@ -26,6 +26,7 @@ import Switch from "@components/Switch/Switch";
|
||||
import TextTime from "@components/TextTime/TextTime";
|
||||
import { NotificationChannel, ReminderRule } from "@models/NotificationPreferences.model";
|
||||
import { OrganizationClientView } from "@models/Clients.model";
|
||||
import WhatsAppIcon from "@mui/icons-material/WhatsApp";
|
||||
|
||||
interface StepEngineProps {
|
||||
flowId: string;
|
||||
@@ -149,6 +150,13 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
const [isUpgradeRequired, setIsUpgradeRequired] = useState(false);
|
||||
const [inviteFlowNotice, setInviteFlowNotice] = useState("");
|
||||
|
||||
// State for WhatsApp Bot Flow
|
||||
const [wapView, setWapView] = useState<any>(null);
|
||||
const [wapAction, setWapAction] = useState<string | null>(null);
|
||||
const [wapPhase, setWapPhase] = useState<string>("intro");
|
||||
const [wapQr, setWapQr] = useState<string | null>(null);
|
||||
const [wapError, setWapError] = useState<string | null>(null);
|
||||
|
||||
// State for Setup Collaborator Flow
|
||||
const [collaboratorToSetup, setCollaboratorToSetup] = useState<any | null>(null);
|
||||
const [setupMissingServices, setSetupMissingServices] = useState(false);
|
||||
@@ -319,11 +327,16 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
setNotifClients(res?.data || []);
|
||||
})
|
||||
.catch(err => console.error("Error cargando clientes de la organización:", err));
|
||||
} else if (flowId === "whatsapp-bot") {
|
||||
setWapPhase("intro");
|
||||
setWapView(null);
|
||||
setWapQr(null);
|
||||
setWapError(null);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (flowId === "new-collaborator" || flowId === "setup-collaborator" || flowId === "public-visibility" || flowId === "notifications" || flowId.startsWith("onboarding") || flowId === "update-schedule" || flowId === "override-schedule" || flowId === "disable-schedule") {
|
||||
if (flowId === "new-collaborator" || flowId === "setup-collaborator" || flowId === "public-visibility" || flowId === "notifications" || flowId === "whatsapp-bot" || flowId.startsWith("onboarding") || flowId === "update-schedule" || flowId === "override-schedule" || flowId === "disable-schedule") {
|
||||
setIsResuming(true);
|
||||
API.post<any[]>("companies/get-by-user", { sessionUser: SessionInfo.userId })
|
||||
.then(response => {
|
||||
@@ -344,7 +357,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
.finally(() => setIsResuming(false));
|
||||
}
|
||||
|
||||
if (flowId === "onboarding-service" || flowId === "onboarding-schedule" || flowId === "setup-collaborator" || flowId === "public-visibility" || flowId === "notifications" || (flowId === "onboarding-org" && action === "new-org")) {
|
||||
if (flowId === "onboarding-service" || flowId === "onboarding-schedule" || flowId === "setup-collaborator" || flowId === "public-visibility" || flowId === "notifications" || flowId === "whatsapp-bot" || (flowId === "onboarding-org" && action === "new-org")) {
|
||||
setCurrentStepIndex(0);
|
||||
}
|
||||
}, [flowId, SessionInfo.metrics.organizationsCount, SessionInfo.userId, router]);
|
||||
@@ -638,6 +651,543 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
}
|
||||
};
|
||||
|
||||
// --- WhatsApp Bot Flow Helpers ---
|
||||
|
||||
const getImageFromBase64 = (base64: string): string => {
|
||||
const base64Data = base64.includes("base64,") ? base64.split(",")[1] : base64;
|
||||
const byteCharacters = atob(base64Data);
|
||||
const byteArray = new Uint8Array(byteCharacters.length);
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
byteArray[i] = byteCharacters.charCodeAt(i);
|
||||
}
|
||||
const blob = new Blob([byteArray], { type: "image/png" });
|
||||
return URL.createObjectURL(blob);
|
||||
};
|
||||
|
||||
const loadWapView = async () => {
|
||||
if (!createdCompanyId) return;
|
||||
setWapAction("load");
|
||||
setWapError(null);
|
||||
try {
|
||||
const view = await API.post<any>("views/wap-server-view", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
setWapView(view);
|
||||
|
||||
const hasPlan = SessionInfo.organizationSubscriptions?.[createdCompanyId]?.plan?.wapNotifications;
|
||||
if (!hasPlan) {
|
||||
setWapPhase("plan-required");
|
||||
} else if (!view?.serverId) {
|
||||
setWapPhase("assign-server");
|
||||
} else if (!view?.exists) {
|
||||
setWapPhase("create-bot");
|
||||
} else if (view?.runningState && !String(view.runningState).includes("Up")) {
|
||||
setWapPhase("start-bot");
|
||||
} else if (view?.runningState && String(view.runningState).includes("Up") && view?.botSession !== "ok") {
|
||||
setWapPhase("scan-qr");
|
||||
try {
|
||||
const qrData = await API.post<string>("wapserver/qr", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
setWapQr(qrData);
|
||||
} catch {
|
||||
setWapQr(null);
|
||||
}
|
||||
} else if (view?.runningState && String(view.runningState).includes("Up") && view?.botSession === "ok") {
|
||||
setWapPhase("bot-ready");
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("Error loading WAP view:", err);
|
||||
setWapError(err?.message || "Error al verificar el estado del bot.");
|
||||
setWapPhase("error");
|
||||
} finally {
|
||||
setWapAction(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleWapAssignServer = async () => {
|
||||
if (!createdCompanyId || wapAction) return;
|
||||
setWapAction("assign");
|
||||
setWapError(null);
|
||||
try {
|
||||
await API.post<any>("wapserver/assign", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await loadWapView();
|
||||
} catch (err: any) {
|
||||
console.error("Error assigning server:", err);
|
||||
setWapError(err?.message || "Error al asignar el servidor.");
|
||||
setWapPhase("error");
|
||||
} finally {
|
||||
setWapAction(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleWapCreateBot = async () => {
|
||||
if (!createdCompanyId || wapAction) return;
|
||||
setWapAction("create");
|
||||
setWapError(null);
|
||||
try {
|
||||
await API.post<any>("wapserver/create", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
await loadWapView();
|
||||
} catch (err: any) {
|
||||
console.error("Error creating bot:", err);
|
||||
setWapError(err?.message || "Error al crear el bot.");
|
||||
setWapPhase("error");
|
||||
} finally {
|
||||
setWapAction(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleWapStartBot = async () => {
|
||||
if (!createdCompanyId || wapAction) return;
|
||||
setWapAction("start");
|
||||
setWapError(null);
|
||||
try {
|
||||
await API.post<any>("wapserver/start", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
await loadWapView();
|
||||
} catch (err: any) {
|
||||
console.error("Error starting bot:", err);
|
||||
setWapError(err?.message || "Error al iniciar el bot.");
|
||||
setWapPhase("error");
|
||||
} finally {
|
||||
setWapAction(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleWapVerifyQr = async () => {
|
||||
if (!createdCompanyId) return;
|
||||
setWapAction("verify");
|
||||
try {
|
||||
const view = await API.post<any>("views/wap-server-view", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
setWapView(view);
|
||||
if (view?.botSession === "ok") {
|
||||
setIsSuccess(true);
|
||||
} else {
|
||||
setWapError("Aún no se detectó la conexión. Asegurate de haber escaneado el QR y volvé a intentar.");
|
||||
}
|
||||
} catch (err: any) {
|
||||
setWapError(err?.message || "Error al verificar la conexión.");
|
||||
} finally {
|
||||
setWapAction(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleWapStopBot = async () => {
|
||||
if (!createdCompanyId || wapAction) return;
|
||||
setWapAction("stop");
|
||||
setWapError(null);
|
||||
try {
|
||||
await API.post<any>("wapserver/stop", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await loadWapView();
|
||||
} catch (err: any) {
|
||||
console.error("Error stopping bot:", err);
|
||||
setWapError(err?.message || "Error al detener el bot.");
|
||||
setWapPhase("error");
|
||||
} finally {
|
||||
setWapAction(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleWapDeleteBot = async () => {
|
||||
if (!createdCompanyId || wapAction) return;
|
||||
setWapAction("delete");
|
||||
setWapError(null);
|
||||
try {
|
||||
await API.post<any>("wapserver/delete", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
await loadWapView();
|
||||
} catch (err: any) {
|
||||
console.error("Error deleting bot:", err);
|
||||
setWapError(err?.message || "Error al eliminar el bot.");
|
||||
setWapPhase("error");
|
||||
} finally {
|
||||
setWapAction(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleWapQuitServer = async () => {
|
||||
if (!createdCompanyId || wapAction) return;
|
||||
setWapAction("quit");
|
||||
setWapError(null);
|
||||
try {
|
||||
await API.post<any>("wapserver/quit", {
|
||||
companyId: createdCompanyId,
|
||||
sessionUser: SessionInfo.userId
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await loadWapView();
|
||||
} catch (err: any) {
|
||||
console.error("Error quitting server:", err);
|
||||
setWapError(err?.message || "Error al desasignar el servidor.");
|
||||
setWapPhase("error");
|
||||
} finally {
|
||||
setWapAction(null);
|
||||
}
|
||||
};
|
||||
|
||||
const renderWhatsAppBotFlow = () => {
|
||||
return (
|
||||
<>
|
||||
{/* Intro */}
|
||||
<QuestionCard
|
||||
isActive={wapPhase === "intro"}
|
||||
title="Bot de WhatsApp"
|
||||
description="Vamos a configurar tu bot de WhatsApp. Esto te permitirá enviar notificaciones automáticas a tus clientes. Primero verificamos el estado actual de tu organización."
|
||||
onNext={() => {
|
||||
loadWapView();
|
||||
}}
|
||||
nextLabel="Verificar estado"
|
||||
topAccessory={
|
||||
<WhatsAppIcon style={{ fontSize: 48, color: "#25D366" }} />
|
||||
}
|
||||
>
|
||||
{null}
|
||||
</QuestionCard>
|
||||
|
||||
{/* Plan required */}
|
||||
<QuestionCard
|
||||
isActive={wapPhase === "plan-required"}
|
||||
title="Plan no compatible"
|
||||
description="Tu plan actual no incluye notificaciones por WhatsApp. Necesitás un plan que incluya esta funcionalidad para configurar el bot."
|
||||
onNext={() => {
|
||||
router.push(`/landing/upgrade-plan?org=${createdCompanyId}`);
|
||||
}}
|
||||
nextLabel="Ver planes"
|
||||
topAccessory={
|
||||
<div style={{ fontSize: 48 }}>📦</div>
|
||||
}
|
||||
>
|
||||
<div style={{
|
||||
padding: "14px",
|
||||
borderRadius: "12px",
|
||||
background: "rgba(255,255,255,0.05)",
|
||||
border: "1px solid rgba(255,255,255,0.1)",
|
||||
color: "rgba(255,255,255,0.7)",
|
||||
fontSize: "13px",
|
||||
lineHeight: 1.5
|
||||
}}>
|
||||
Actualizá tu plan para desbloquear notificaciones por WhatsApp y mejorar la comunicación con tus clientes.
|
||||
</div>
|
||||
</QuestionCard>
|
||||
|
||||
{/* Assign server */}
|
||||
<QuestionCard
|
||||
isActive={wapPhase === "assign-server"}
|
||||
title="Asignar servidor"
|
||||
description="Tu organización necesita un servidor de WhatsApp. Vamos a asignar uno automáticamente."
|
||||
onNext={handleWapAssignServer}
|
||||
disableNext={wapAction !== null}
|
||||
nextLabel={wapAction === "assign" ? "Asignando servidor..." : "Asignar servidor"}
|
||||
topAccessory={
|
||||
<div style={{ fontSize: 48 }}>🖥️</div>
|
||||
}
|
||||
>
|
||||
{wapAction === "assign" && (
|
||||
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "12px", padding: "20px 0" }}>
|
||||
<div style={{
|
||||
width: 36, height: 36, border: "3px solid rgba(255,255,255,0.2)",
|
||||
borderTopColor: "var(--wine-red)", borderRadius: "50%",
|
||||
animation: "spin 1s linear infinite"
|
||||
}} />
|
||||
<span style={{ color: "rgba(255,255,255,0.7)", fontSize: "14px" }}>Asignando servidor...</span>
|
||||
</div>
|
||||
)}
|
||||
</QuestionCard>
|
||||
|
||||
{/* Create bot */}
|
||||
<QuestionCard
|
||||
isActive={wapPhase === "create-bot"}
|
||||
title="Crear bot de WhatsApp"
|
||||
description={`Servidor asignado: ${wapView?.serverName || "disponible"}. Ahora vamos a crear tu bot de WhatsApp.`}
|
||||
onNext={handleWapCreateBot}
|
||||
disableNext={wapAction !== null}
|
||||
nextLabel={wapAction === "create" ? "Creando bot..." : "Crear bot de WhatsApp"}
|
||||
topAccessory={
|
||||
<div style={{ fontSize: 48 }}>🤖</div>
|
||||
}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "12px", width: "100%" }}>
|
||||
{wapAction === "create" && (
|
||||
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "12px", padding: "20px 0" }}>
|
||||
<div style={{
|
||||
width: 36, height: 36, border: "3px solid rgba(255,255,255,0.2)",
|
||||
borderTopColor: "var(--wine-red)", borderRadius: "50%",
|
||||
animation: "spin 1s linear infinite"
|
||||
}} />
|
||||
<span style={{ color: "rgba(255,255,255,0.7)", fontSize: "14px" }}>Creando bot...</span>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleWapQuitServer}
|
||||
disabled={wapAction !== null}
|
||||
style={{
|
||||
display: "flex", alignItems: "center", justifyContent: "center", gap: "8px",
|
||||
padding: "12px 20px", borderRadius: "10px", width: "100%",
|
||||
background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.15)",
|
||||
color: "rgba(255,255,255,0.7)", cursor: "pointer", fontSize: "14px", fontWeight: 600,
|
||||
fontFamily: "inherit", transition: "all 0.2s ease", marginTop: "4px"
|
||||
}}
|
||||
>
|
||||
{wapAction === "quit" ? (
|
||||
<>
|
||||
<div style={{
|
||||
width: 18, height: 18, border: "2px solid rgba(255,255,255,0.2)",
|
||||
borderTopColor: "var(--wine-red)", borderRadius: "50%",
|
||||
animation: "spin 1s linear infinite"
|
||||
}} />
|
||||
Desasignando servidor...
|
||||
</>
|
||||
) : "Desasignar servidor"}
|
||||
</button>
|
||||
</div>
|
||||
</QuestionCard>
|
||||
|
||||
{/* Start bot */}
|
||||
<QuestionCard
|
||||
isActive={wapPhase === "start-bot"}
|
||||
title="Iniciar bot"
|
||||
description="Tu bot de WhatsApp está creado pero detenido. Vamos a iniciarlo."
|
||||
onNext={handleWapStartBot}
|
||||
disableNext={wapAction !== null}
|
||||
nextLabel={wapAction === "start" ? "Iniciando bot..." : "Iniciar bot"}
|
||||
topAccessory={
|
||||
<div style={{ fontSize: 48 }}>▶️</div>
|
||||
}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "12px", width: "100%" }}>
|
||||
{wapAction === "start" && (
|
||||
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "12px", padding: "20px 0" }}>
|
||||
<div style={{
|
||||
width: 36, height: 36, border: "3px solid rgba(255,255,255,0.2)",
|
||||
borderTopColor: "var(--wine-red)", borderRadius: "50%",
|
||||
animation: "spin 1s linear infinite"
|
||||
}} />
|
||||
<span style={{ color: "rgba(255,255,255,0.7)", fontSize: "14px" }}>Iniciando bot...</span>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleWapDeleteBot}
|
||||
disabled={wapAction !== null}
|
||||
style={{
|
||||
display: "flex", alignItems: "center", justifyContent: "center", gap: "8px",
|
||||
padding: "12px 20px", borderRadius: "10px", width: "100%",
|
||||
background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.15)",
|
||||
color: "rgba(255,255,255,0.7)", cursor: "pointer", fontSize: "14px", fontWeight: 600,
|
||||
fontFamily: "inherit", transition: "all 0.2s ease", marginTop: "4px"
|
||||
}}
|
||||
>
|
||||
{wapAction === "delete" ? (
|
||||
<>
|
||||
<div style={{
|
||||
width: 18, height: 18, border: "2px solid rgba(255,255,255,0.2)",
|
||||
borderTopColor: "var(--wine-red)", borderRadius: "50%",
|
||||
animation: "spin 1s linear infinite"
|
||||
}} />
|
||||
Eliminando bot...
|
||||
</>
|
||||
) : "Eliminar bot"}
|
||||
</button>
|
||||
</div>
|
||||
</QuestionCard>
|
||||
|
||||
{/* Scan QR */}
|
||||
<QuestionCard
|
||||
isActive={wapPhase === "scan-qr"}
|
||||
title="Escanear código QR"
|
||||
description="Tu bot está funcionando. Ahora necesitás vincular tu número de WhatsApp escaneando este código QR con tu teléfono."
|
||||
onNext={handleWapVerifyQr}
|
||||
disableNext={wapAction !== null}
|
||||
nextLabel={wapAction === "verify" ? "Verificando..." : "Ya escaneé el código"}
|
||||
topAccessory={
|
||||
<div style={{ fontSize: 48 }}>📱</div>
|
||||
}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "16px", width: "100%" }}>
|
||||
{wapQr ? (
|
||||
<div style={{
|
||||
padding: "16px",
|
||||
borderRadius: "12px",
|
||||
background: "white",
|
||||
boxShadow: "0 4px 20px rgba(0,0,0,0.3)"
|
||||
}}>
|
||||
<img
|
||||
src={getImageFromBase64(wapQr)}
|
||||
width={250}
|
||||
height={250}
|
||||
alt="Escaneá el código QR"
|
||||
style={{ display: "block" }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{
|
||||
width: 250, height: 250, borderRadius: "12px",
|
||||
background: "rgba(255,255,255,0.05)",
|
||||
border: "2px dashed rgba(255,255,255,0.2)",
|
||||
display: "flex", alignItems: "center", justifyContent: "center",
|
||||
color: "rgba(255,255,255,0.5)", fontSize: "14px"
|
||||
}}>
|
||||
Cargando QR...
|
||||
</div>
|
||||
)}
|
||||
<div style={{
|
||||
padding: "12px 16px",
|
||||
borderRadius: "10px",
|
||||
background: "rgba(255,255,255,0.05)",
|
||||
border: "1px solid rgba(255,255,255,0.1)",
|
||||
color: "rgba(255,255,255,0.7)",
|
||||
fontSize: "13px",
|
||||
lineHeight: 1.5,
|
||||
textAlign: "center",
|
||||
maxWidth: "350px"
|
||||
}}>
|
||||
Abrí WhatsApp en tu teléfono → <strong style={{ color: "white" }}>Configuración</strong> → <strong style={{ color: "white" }}>Dispositivos vinculados</strong> → <strong style={{ color: "white" }}>Vincular dispositivo</strong>
|
||||
</div>
|
||||
{wapError && (
|
||||
<div style={{
|
||||
padding: "12px 16px",
|
||||
borderRadius: "10px",
|
||||
background: "rgba(255,60,60,0.1)",
|
||||
border: "1px solid rgba(255,60,60,0.3)",
|
||||
color: "#ff9999",
|
||||
fontSize: "13px",
|
||||
textAlign: "center",
|
||||
maxWidth: "350px"
|
||||
}}>
|
||||
{wapError}
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleWapStopBot}
|
||||
disabled={wapAction !== null}
|
||||
style={{
|
||||
display: "flex", alignItems: "center", justifyContent: "center", gap: "8px",
|
||||
padding: "12px 20px", borderRadius: "10px", width: "100%",
|
||||
background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.15)",
|
||||
color: "rgba(255,255,255,0.7)", cursor: "pointer", fontSize: "14px", fontWeight: 600,
|
||||
fontFamily: "inherit", transition: "all 0.2s ease", marginTop: "4px"
|
||||
}}
|
||||
>
|
||||
{wapAction === "stop" ? (
|
||||
<>
|
||||
<div style={{
|
||||
width: 18, height: 18, border: "2px solid rgba(255,255,255,0.2)",
|
||||
borderTopColor: "var(--wine-red)", borderRadius: "50%",
|
||||
animation: "spin 1s linear infinite"
|
||||
}} />
|
||||
Deteniendo bot...
|
||||
</>
|
||||
) : "Detener bot"}
|
||||
</button>
|
||||
</div>
|
||||
</QuestionCard>
|
||||
|
||||
{/* Bot ready */}
|
||||
<QuestionCard
|
||||
isActive={wapPhase === "bot-ready"}
|
||||
title="¡Bot funcionando!"
|
||||
description="Tu bot de WhatsApp está conectado y funcionando correctamente. Ya puede enviar notificaciones a tus clientes."
|
||||
onNext={() => setIsSuccess(true)}
|
||||
nextLabel="Finalizar"
|
||||
topAccessory={
|
||||
<div style={{ fontSize: 48 }}>✅</div>
|
||||
}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "12px", width: "100%" }}>
|
||||
<div style={{
|
||||
padding: "14px", borderRadius: "12px",
|
||||
background: "rgba(74,222,128,0.08)", border: "1px solid rgba(74,222,128,0.2)",
|
||||
color: "#4ade80", fontSize: "13px", textAlign: "center", fontWeight: 600
|
||||
}}>
|
||||
Sesión de WhatsApp activa — Bot operativo
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleWapStopBot}
|
||||
disabled={wapAction !== null}
|
||||
style={{
|
||||
display: "flex", alignItems: "center", justifyContent: "center", gap: "8px",
|
||||
padding: "12px 20px", borderRadius: "10px", width: "100%",
|
||||
background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.15)",
|
||||
color: "rgba(255,255,255,0.7)", cursor: "pointer", fontSize: "14px", fontWeight: 600,
|
||||
fontFamily: "inherit", transition: "all 0.2s ease", marginTop: "4px"
|
||||
}}
|
||||
>
|
||||
{wapAction === "stop" ? (
|
||||
<>
|
||||
<div style={{
|
||||
width: 18, height: 18, border: "2px solid rgba(255,255,255,0.2)",
|
||||
borderTopColor: "var(--wine-red)", borderRadius: "50%",
|
||||
animation: "spin 1s linear infinite"
|
||||
}} />
|
||||
Deteniendo bot...
|
||||
</>
|
||||
) : "Detener bot"}
|
||||
</button>
|
||||
</div>
|
||||
</QuestionCard>
|
||||
|
||||
{/* Error */}
|
||||
<QuestionCard
|
||||
isActive={wapPhase === "error"}
|
||||
title="Error"
|
||||
description={wapError || "Ocurrió un error inesperado. Por favor, intentá nuevamente."}
|
||||
onNext={() => {
|
||||
setWapPhase("intro");
|
||||
setWapError(null);
|
||||
}}
|
||||
nextLabel="Reintentar"
|
||||
topAccessory={
|
||||
<div style={{ fontSize: 48 }}>⚠️</div>
|
||||
}
|
||||
>
|
||||
<div style={{
|
||||
padding: "14px",
|
||||
borderRadius: "12px",
|
||||
background: "rgba(255,60,60,0.1)",
|
||||
border: "1px solid rgba(255,60,60,0.3)",
|
||||
color: "#ff9999",
|
||||
fontSize: "13px",
|
||||
lineHeight: 1.5
|
||||
}}>
|
||||
Si el problema persiste, contactá al soporte de TurnosXpress.
|
||||
</div>
|
||||
</QuestionCard>
|
||||
|
||||
<style>{`
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderOnboardingOrgFlow = () => {
|
||||
return (
|
||||
<>
|
||||
@@ -3945,6 +4495,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
if (flowId === "new-collaborator") return renderNewCollaboratorFlow();
|
||||
if (flowId === "public-visibility") return renderPublicVisibilityFlow();
|
||||
if (flowId === "notifications") return renderNotificationsFlow();
|
||||
if (flowId === "whatsapp-bot") return renderWhatsAppBotFlow();
|
||||
if (flowId === "disable-schedule") return renderDisableScheduleFlow();
|
||||
if (flowId === "override-schedule") return renderOverrideScheduleFlow();
|
||||
if (flowId === "update-schedule") return renderUpdateScheduleFlow();
|
||||
@@ -3964,6 +4515,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
|
||||
if (flowId === "new-collaborator") return "El colaborador fue invitado exitosamente.";
|
||||
if (flowId === "setup-collaborator") return "El colaborador ha sido configurado exitosamente y ya puede comenzar a recibir reservas.";
|
||||
if (flowId === "notifications") return "La configuración de notificaciones se ha guardado exitosamente.";
|
||||
if (flowId === "whatsapp-bot") return "Tu bot de WhatsApp está configurado y funcionando. Tus clientes recibirán notificaciones automáticas por WhatsApp.";
|
||||
if (action) return "Tus cambios se han guardado exitosamente.";
|
||||
return "Has configurado exitosamente tu negocio. Todo está listo para que empieces a recibir reservas y gestionar tu agenda como un profesional.";
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user