refactor: simplify StepEngine action logic and add public link display after onboarding completion

This commit is contained in:
2026-07-28 19:14:34 -03:00
parent f39ba44f59
commit 0a285776c7
2 changed files with 44 additions and 25 deletions
-7
View File
@@ -46,13 +46,6 @@ app.use(
app.use(json()); app.use(json());
app.use((req, _res, next) => {
if (req.path === '/companies/create') {
require('fs').writeFileSync('request_log.json', JSON.stringify({ body: req.body, headers: req.headers }, null, 2));
}
next();
});
RegisterRoutes(app); RegisterRoutes(app);
app.use(function errorHandler( app.use(function errorHandler(
@@ -40,6 +40,7 @@ interface StepEngineProps {
export default function StepEngine({ flowId, action, initialCompanyId, initialEmployeeId }: StepEngineProps) { export default function StepEngine({ flowId, action, initialCompanyId, initialEmployeeId }: StepEngineProps) {
const router = useRouter(); const router = useRouter();
const SessionInfo = useSessionStore(); const SessionInfo = useSessionStore();
const isStandaloneAction = Boolean(action && action !== "new-org");
const [currentStepIndex, setCurrentStepIndex] = useState(0); const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [imageError, setImageError] = useState(false); const [imageError, setImageError] = useState(false);
@@ -51,6 +52,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
const [orgDescription, setOrgDescription] = useState(""); const [orgDescription, setOrgDescription] = useState("");
const [isCreating, setIsCreating] = useState(false); const [isCreating, setIsCreating] = useState(false);
const [createdCompanyId, setCreatedCompanyId] = useState<string | null>(null); const [createdCompanyId, setCreatedCompanyId] = useState<string | null>(null);
const [showOnboardingPublicLink, setShowOnboardingPublicLink] = useState(false);
// State for Location // State for Location
const [street, setStreet] = useState(""); const [street, setStreet] = useState("");
@@ -487,7 +489,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
}; };
const proceedNext = () => { const proceedNext = () => {
if (action) { if (isStandaloneAction) {
handleActionSuccess(); handleActionSuccess();
} else { } else {
handleNext(); handleNext();
@@ -554,6 +556,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
const newCompanyId = company?.id || company?._id; const newCompanyId = company?.id || company?._id;
if (newCompanyId) { if (newCompanyId) {
setCreatedCompanyId(newCompanyId); setCreatedCompanyId(newCompanyId);
setSelectedOrganization(company);
} else { } else {
console.error("No se pudo obtener el ID de la organización creada", company); console.error("No se pudo obtener el ID de la organización creada", company);
} }
@@ -601,7 +604,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
zipCode: zipCode, zipCode: zipCode,
latitude: latitude, latitude: latitude,
longitude: longitude, longitude: longitude,
onboardingStep: action ? undefined : currentStepIndex + 1, onboardingStep: isStandaloneAction ? undefined : currentStepIndex + 1,
sessionUser: SessionInfo.userId sessionUser: SessionInfo.userId
}); });
proceedNext(); // Move to Step 5 (Contact) proceedNext(); // Move to Step 5 (Contact)
@@ -690,7 +693,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
} }
// Finish Org Onboarding // Finish Org Onboarding
if (action) { handleActionSuccess(); } else { router.push("/admin/assistant?flow=onboarding-service"); } if (isStandaloneAction) { handleActionSuccess(); } else { router.push("/admin/assistant?flow=onboarding-service"); }
} catch (error: any) { } catch (error: any) {
console.error("Error actualizando apariencia:", error); console.error("Error actualizando apariencia:", error);
alert(error?.message || "Hubo un error al guardar la configuración de apariencia."); alert(error?.message || "Hubo un error al guardar la configuración de apariencia.");
@@ -1351,7 +1354,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
description={`¿Dónde queda "${orgName}"? Esto es crucial para que tus clientes te encuentren.`} description={`¿Dónde queda "${orgName}"? Esto es crucial para que tus clientes te encuentren.`}
onNext={handleUpdateLocation} onNext={handleUpdateLocation}
disableNext={isCreating} disableNext={isCreating}
nextLabel={isCreating ? "Guardando..." : (action ? "Guardar Cambios" : "Siguiente")} nextLabel={isCreating ? "Guardando..." : (isStandaloneAction ? "Guardar Cambios" : "Siguiente")}
> >
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px', width: '100%' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '15px', width: '100%' }}>
<OnboardingLocation <OnboardingLocation
@@ -1416,7 +1419,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
description="¿Cómo pueden contactarte tus clientes? Ingresa el número de teléfono (preferiblemente WhatsApp)." description="¿Cómo pueden contactarte tus clientes? Ingresa el número de teléfono (preferiblemente WhatsApp)."
onNext={handleUpdateContact} onNext={handleUpdateContact}
disableNext={!validatePhone(phoneAreaCode, phoneNumber).isValid || isCreating} disableNext={!validatePhone(phoneAreaCode, phoneNumber).isValid || isCreating}
nextLabel={isCreating ? "Guardando..." : (action ? "Guardar Cambios" : "Siguiente")} nextLabel={isCreating ? "Guardando..." : (isStandaloneAction ? "Guardar Cambios" : "Siguiente")}
> >
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px', width: '100%', alignItems: 'center' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '15px', width: '100%', alignItems: 'center' }}>
<OnboardingPhone <OnboardingPhone
@@ -1523,7 +1526,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
description="Sube una foto de portada para tu negocio y elige tus colores corporativos. Esto es lo primero que verán tus clientes." description="Sube una foto de portada para tu negocio y elige tus colores corporativos. Esto es lo primero que verán tus clientes."
onNext={handleUploadHeader} onNext={handleUploadHeader}
disableNext={isCreating} disableNext={isCreating}
nextLabel={isCreating ? "Guardando..." : (action ? "Guardar Cambios" : "Siguiente")} nextLabel={isCreating ? "Guardando..." : (isStandaloneAction ? "Guardar Cambios" : "Siguiente")}
> >
<div style={{ display: 'flex', flexDirection: 'column', gap: '20px', width: '100%', alignItems: 'center' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '20px', width: '100%', alignItems: 'center' }}>
<div style={{ <div style={{
@@ -1687,7 +1690,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
.then(res => { .then(res => {
setPreviewServices(res || []); setPreviewServices(res || []);
}); });
} else if (action) { } else if (isStandaloneAction) {
handleActionSuccess(); handleActionSuccess();
} else { } else {
router.push("/admin/assistant?flow=onboarding-schedule"); router.push("/admin/assistant?flow=onboarding-schedule");
@@ -2048,10 +2051,10 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
} catch (e) { } catch (e) {
console.error("Error setting onboarding completed", e); console.error("Error setting onboarding completed", e);
} }
if (action) { if (isStandaloneAction) {
handleActionSuccess(); handleActionSuccess();
} else { } else {
setIsSuccess(true); setShowOnboardingPublicLink(true);
} }
} catch (error: any) { } catch (error: any) {
console.error("Error creando horarios:", error); console.error("Error creando horarios:", error);
@@ -4079,8 +4082,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
}; };
const getSelectedOrganizationSlug = () => { const getSelectedOrganizationSlug = () => {
if (!selectedOrganization) return ""; return selectedOrganization?.slug || getSlug(selectedOrganization?.name || orgName || "");
return selectedOrganization.slug || (selectedOrganization.name ? getSlug(selectedOrganization.name) : "");
}; };
const getPublicLink = () => { const getPublicLink = () => {
@@ -4108,7 +4110,8 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
} }
}; };
const renderPublicLinkFlow = () => { const renderPublicLinkFlow = (options?: { onboardingCompletion?: boolean }) => {
const isOnboardingCompletion = options?.onboardingCompletion === true;
const publicLink = getPublicLink(); const publicLink = getPublicLink();
const publicLinkQrUrl = getPublicLinkQrUrl(); const publicLinkQrUrl = getPublicLinkQrUrl();
const orgSlug = getSelectedOrganizationSlug(); const orgSlug = getSelectedOrganizationSlug();
@@ -4118,13 +4121,21 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
isActive={true} isActive={true}
title="Link público y código QR" title="Link público y código QR"
description="Compartí este enlace para que tus clientes entren directo a la página pública de reservas de la organización. También podés usar el código QR en redes, vidriera o piezas impresas." description="Compartí este enlace para que tus clientes entren directo a la página pública de reservas de la organización. También podés usar el código QR en redes, vidriera o piezas impresas."
onNext={() => router.push("/admin/assistant")} onNext={() => {
nextLabel="Volver al asistente" if (isOnboardingCompletion) {
setShowOnboardingPublicLink(false);
setIsSuccess(true);
return;
}
router.push("/admin/assistant");
}}
nextLabel={isOnboardingCompletion ? "Finalizar" : "Volver al asistente"}
topAccessory={<QrCode2Icon style={{ fontSize: 52, color: "var(--wine-red)" }} />} topAccessory={<QrCode2Icon style={{ fontSize: 52, color: "var(--wine-red)" }} />}
> >
<div style={{ display: "flex", flexDirection: "column", gap: "18px", width: "100%", maxWidth: "560px", margin: "0 auto" }}> <div style={{ display: "flex", flexDirection: "column", gap: "18px", width: "100%", maxWidth: "560px", margin: "0 auto" }}>
<div style={{ padding: "16px", borderRadius: "14px", background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.12)", color: "rgba(255,255,255,0.75)", lineHeight: 1.5, fontSize: "14px" }}> <div style={{ padding: "16px", borderRadius: "14px", background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.12)", color: "rgba(255,255,255,0.75)", lineHeight: 1.5, fontSize: "14px" }}>
Organización: <strong style={{ color: "white" }}>{selectedOrganization?.name || "Sin organización seleccionada"}</strong> Organización: <strong style={{ color: "white" }}>{selectedOrganization?.name || orgName || "Sin organización seleccionada"}</strong>
{orgSlug && ( {orgSlug && (
<div style={{ marginTop: "6px", color: "rgba(255,255,255,0.62)", fontSize: "13px" }}> <div style={{ marginTop: "6px", color: "rgba(255,255,255,0.62)", fontSize: "13px" }}>
Slug público: <strong style={{ color: "white" }}>{orgSlug}</strong> Slug público: <strong style={{ color: "white" }}>{orgSlug}</strong>
@@ -4904,6 +4915,21 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
return renderOnboardingOrgFlow(); // Default to org flow return renderOnboardingOrgFlow(); // Default to org flow
}; };
if (showOnboardingPublicLink) {
return (
<div className={style.engineContainer}>
<div className={style.header}>
<div className={style.flowTitle}>
Configuración de Negocio
</div>
</div>
<div className={style.chatContainer}>
{renderPublicLinkFlow({ onboardingCompletion: true })}
</div>
</div>
);
}
if (isSuccess) { if (isSuccess) {
const getSuccessMessage = () => { const getSuccessMessage = () => {
if (action === "new-service") return "Has agregado tu nuevo servicio exitosamente."; if (action === "new-service") return "Has agregado tu nuevo servicio exitosamente.";
@@ -4917,7 +4943,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
if (flowId === "notifications") return "La configuración de notificaciones se ha guardado exitosamente."; 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 (flowId === "whatsapp-bot") return "Tu bot de WhatsApp está configurado y funcionando. Tus clientes recibirán notificaciones automáticas por WhatsApp.";
if (flowId === "open-reservation-periods") return "El período de reservas fue extendido exitosamente."; if (flowId === "open-reservation-periods") return "El período de reservas fue extendido exitosamente.";
if (action) return "Tus cambios se han guardado exitosamente."; if (isStandaloneAction) 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."; return "Has configurado exitosamente tu negocio. Todo está listo para que empieces a recibir reservas y gestionar tu agenda como un profesional.";
}; };
@@ -4971,7 +4997,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
<button <button
onClick={() => { onClick={() => {
if (action) { if (isStandaloneAction) {
router.push("/admin/assistant"); router.push("/admin/assistant");
} else { } else {
window.location.href = "/landing/dashboard"; window.location.href = "/landing/dashboard";
@@ -4981,7 +5007,7 @@ export default function StepEngine({ flowId, action, initialCompanyId, initialEm
onMouseOver={handleSuccessButtonMouseOver} onMouseOver={handleSuccessButtonMouseOver}
onMouseOut={handleSuccessButtonMouseOut} onMouseOut={handleSuccessButtonMouseOut}
> >
{action ? "Volver al Menú Principal" : "Ir a mi Panel de Control"} {isStandaloneAction ? "Volver al Menú Principal" : "Ir a mi Panel de Control"}
</button> </button>
<style>{` <style>{`