// Login / Register page
const { useState: useStateLogin, useEffect: useEffectLogin, useRef: useRefLogin } = React;
// Sconto annuale — deve corrispondere a ANNUAL_DISCOUNT in stripe_service.py
const ANNUAL_DISCOUNT = 0.90;
const PLANS_META = [
{
id: "starter",
name: "Starter",
monthlyPrice: 19,
properties: "1 struttura",
features: ["Chatbot WhatsApp/Telegram", "100 messaggi per sessione", "Check-in PDF", "QR Code"],
},
{
id: "pro",
name: "Pro",
monthlyPrice: 39,
properties: "fino a 10 strutture",
highlight: true,
features: ["Tutto di Starter", "500 messaggi per sessione", "Check-in digitale OCR", "Supporto prioritario"],
},
{
id: "business",
name: "Business",
monthlyPrice: 79,
properties: "strutture illimitate",
features: ["Tutto di Pro", "1.000 messaggi per sessione", "Account manager dedicato", "API access"],
},
];
function fmtPrice(monthly, interval) {
if (interval === "monthly") return `€${monthly}`;
return `€${(monthly * ANNUAL_DISCOUNT).toFixed(2).replace(".00", "")}`;
}
function yearlySaving(monthly) {
return Math.round(monthly * 12 * (1 - ANNUAL_DISCOUNT));
}
// Monta il form Stripe Embedded Checkout in un div interno dedicato.
// stripeCustomerId: se presente usa /auth/checkout-anon (registrazione), altrimenti /billing/checkout (host autenticato).
function EmbeddedCheckout({ planId, interval, stripeCustomerId }) {
const mountRef = useRefLogin(null);
const [error, setError] = useStateLogin(null);
useEffectLogin(() => {
if (!planId) return;
let checkout = null;
(async () => {
try {
const stripe = await window.API.getStripe();
if (!stripe) { setError("Pagamenti non disponibili al momento."); return; }
checkout = await stripe.initEmbeddedCheckout({
fetchClientSecret: async () => {
const data = stripeCustomerId
? await window.API.checkoutAnon(stripeCustomerId, planId, interval)
: await window.API.createCheckout(planId, interval);
return data.client_secret;
},
});
if (mountRef.current) checkout.mount(mountRef.current);
} catch (e) {
setError(e.message || "Errore durante il caricamento del pagamento.");
}
})();
return () => { if (checkout) checkout.destroy(); };
}, [planId, interval, stripeCustomerId]);
if (error) return (
{error}
);
return ;
}
function PlanSelectPage({ onLogin, heading, subtitle, stripeCustomerId, billingStatus }) {
const [interval, setInterval] = useStateLogin("monthly");
const [selectedPlan, setSelectedPlan] = useStateLogin(null);
const selectPlan = (planId) => setSelectedPlan(planId);
// Piano custom preassegnato dall'admin: mostra solo il CTA di attivazione
const isCustomPlan = billingStatus?.plan_type === "custom";
const assignedPlanId = billingStatus?.plan;
const assignedPlanName = billingStatus?.plan_display_name;
if (isCustomPlan && assignedPlanId) {
const hasStripePrices = billingStatus?.has_stripe_prices === true;
return (
Il tuo account è pronto
Hai il piano {assignedPlanName} preconfigurato per te.
{hasStripePrices
? "Clicca qui sotto per completare l'attivazione e iniziare a usare Argo."
: "Entra nel dashboard per iniziare a gestire le tue strutture."}
{hasStripePrices
?
: (
)
}
);
}
return (
{heading || "Scegli il tuo piano"}
{subtitle || "Inizia subito — puoi cambiare piano in qualsiasi momento."}
{/* Toggle mensile / annuale */}
{["monthly", "yearly"].map(iv => (
))}
{/* Layout: cards a sinistra, checkout a destra */}
);
}
function LoginPage({ onLogin, resetToken }) {
const registrationSuccess = new URLSearchParams(window.location.search).get("registration_success") === "true";
const initialMode = resetToken ? "reset" : "signin";
const [mode, setMode] = useStateLogin(initialMode);
const [email, setEmail] = useStateLogin("");
const [password, setPassword] = useStateLogin("");
const [name, setName] = useStateLogin("");
const [loading, setLoading] = useStateLogin(false);
const [error, setError] = useStateLogin(null);
const [success, setSuccess] = useStateLogin(registrationSuccess
? "Account attivato! Accedi con le tue credenziali per iniziare."
: null
);
// stripeCustomerId è impostato dopo la prima fase della registrazione
const [pendingStripeCustomerId, setPendingStripeCustomerId] = useStateLogin(null);
const switchMode = (m) => { setMode(m); setError(null); setSuccess(null); };
if (pendingStripeCustomerId) {
return (
);
}
const submit = async (e) => {
e.preventDefault();
setError(null);
setSuccess(null);
setLoading(true);
try {
if (mode === "signin") {
await window.API.login(email, password);
onLogin();
} else if (mode === "signup") {
// Fase 1: valida + crea customer Stripe + salva in Redis
const res = await window.API.register(email, password, name);
// Fase 2: mostra selezione piano con checkout anonimo
setPendingStripeCustomerId(res.stripe_customer_id);
} else if (mode === "forgot") {
const res = await window.API.forgotPassword(email);
setSuccess(res.detail);
} else if (mode === "reset") {
await window.API.resetPassword(resetToken, password);
setSuccess("Password aggiornata! Puoi accedere con le nuove credenziali.");
setTimeout(() => switchMode("signin"), 2000);
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const isSignIn = mode === "signin";
return (
{/* Tabs signin/signup — nascosti in forgot e reset */}
{(mode === "signin" || mode === "signup") && (
)}
{mode === "signin" && "Bentornato"}
{mode === "signup" && "Inizia con Argo"}
{mode === "forgot" && "Password dimenticata"}
{mode === "reset" && "Scegli una nuova password"}
{mode === "signin" && "Accedi per gestire le tue strutture e il chatbot."}
{mode === "signup" && "Crea il tuo account e imposta il chatbot in pochi minuti."}
{mode === "forgot" && "Inserisci la tua email e ti invieremo un link per reimpostare la password."}
{mode === "reset" && "Scegli una password sicura di almeno 8 caratteri."}