refactor: simplify StepEngine action logic and add public link display after onboarding completion
This commit is contained in:
@@ -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>{`
|
||||||
|
|||||||
Reference in New Issue
Block a user