250 lines
14 KiB
TypeScript
250 lines
14 KiB
TypeScript
"use client";
|
|
import ApiRequest from "@services/Api.Service";
|
|
import AnimatedContainer from "@core/app/components/AnimatedConainer/AnimatedContainer";
|
|
import SecurePage from "@core/app/components/Secure/Secure";
|
|
import { LOGIN_ACTIONS } from "@core/Store/LoginAction.Store";
|
|
import { useSessionStore } from "@core/Store/Sesion.Store";
|
|
import { useParams } from "next/navigation";
|
|
import { useSearchParams } from "next/navigation";
|
|
import { useEffect, useState, Suspense } from "react";
|
|
import {
|
|
CreateMPPreApprovalParams,
|
|
MP_PRE_APPROVAL_STATUS,
|
|
MPPreApprovalResponse,
|
|
} from "@core/Models/Subscription.Model";
|
|
import { useAlert } from "@core/Store/Alert.Store";
|
|
import { EVENT_TYPES, useEventHandlerStore } from "@core/Store/EventHandler.Store";
|
|
import { useNavigation } from "@core/app/hooks/goto";
|
|
import SuspenseLoading from "@core/app/components/SuspendeLoading/SuspenseLoading";
|
|
import { IPlan } from "@core/Models/Plans.model";
|
|
import styles from "./page.module.css";
|
|
|
|
type UrlData = {
|
|
planid: string;
|
|
};
|
|
|
|
function SubscriptionAddContent() {
|
|
const data = useParams<UrlData>();
|
|
const searchParams = useSearchParams();
|
|
const id = data.planid;
|
|
const isRenewal = searchParams.get("renew") === "1";
|
|
const SessionInfo = useSessionStore();
|
|
const alert = useAlert();
|
|
const eventHandler = useEventHandlerStore();
|
|
const { goTo } = useNavigation();
|
|
|
|
const [plan, setPlan] = useState<IPlan | null>(null);
|
|
const [selectedMonths, setSelectedMonths] = useState<number>(1);
|
|
const [finalPrice, setFinalPrice] = useState<number>(0);
|
|
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
|
|
|
|
const getPlanInfo = () => {
|
|
eventHandler.setEventType(EVENT_TYPES.LOADING);
|
|
ApiRequest.post<IPlan[]>("plans/find", { _id: id })
|
|
.then((res) => {
|
|
if (res && res.length > 0) {
|
|
setPlan(res[0]);
|
|
setFinalPrice(res[0].price);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
alert.showError("No se pudo cargar la información del plan");
|
|
})
|
|
.finally(() => {
|
|
eventHandler.setEventType(EVENT_TYPES.SLEEP);
|
|
});
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (id) {
|
|
getPlanInfo();
|
|
}
|
|
}, [id]);
|
|
|
|
useEffect(() => {
|
|
if (plan) {
|
|
let discount = 0;
|
|
if (selectedMonths === 3) discount = plan.discount3Months || 0;
|
|
else if (selectedMonths === 6) discount = plan.discount6Months || 0;
|
|
else if (selectedMonths === 12) discount = plan.discount12Months || 0;
|
|
|
|
const total = plan.price * selectedMonths;
|
|
const final = total - (total * (discount / 100));
|
|
setFinalPrice(final);
|
|
}
|
|
}, [selectedMonths, plan]);
|
|
|
|
const getLink = () => {
|
|
if (isSubmitting) {
|
|
return;
|
|
}
|
|
|
|
if (SessionInfo.userId && SessionInfo.loged) {
|
|
const preApprovalData: CreateMPPreApprovalParams = {
|
|
planId: id,
|
|
sessionUser: SessionInfo.userId,
|
|
months: selectedMonths,
|
|
};
|
|
|
|
eventHandler.setEventType(EVENT_TYPES.LOADING);
|
|
setIsSubmitting(true);
|
|
|
|
ApiRequest.post<MPPreApprovalResponse>("suscriptions/preapproval", preApprovalData)
|
|
.then(async (response) => {
|
|
if (response.status === MP_PRE_APPROVAL_STATUS.READY_TO_START) {
|
|
if (plan?.price === 0) {
|
|
// Actualizamos la sesión manualmente para evitar la "race condition" con los sockets
|
|
// Esto asegura que el estado de Zustand tenga la nueva suscripción antes de ir al dashboard
|
|
await SessionInfo.checkSession();
|
|
}
|
|
goTo(response.init_point);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
alert.showError(error.format ? error.format() : "Ocurrió un error");
|
|
})
|
|
.finally(() => {
|
|
setIsSubmitting(false);
|
|
eventHandler.setEventType(EVENT_TYPES.SLEEP);
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Suspense fallback={<SuspenseLoading />}>
|
|
<SecurePage loginAction={LOGIN_ACTIONS.PLAN_SUBSCRIPTION}>
|
|
<AnimatedContainer
|
|
color="#aeaeae"
|
|
minSize={150}
|
|
maxSize={500}
|
|
items={10}
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
padding: "20px",
|
|
}}
|
|
>
|
|
<div className={styles.container}>
|
|
<div className={styles.card}>
|
|
|
|
<>
|
|
<div className={styles.leftPanel}>
|
|
<div style={{ marginBottom: "2rem" }}>
|
|
<span style={{ background: "rgba(255,255,255,0.2)", padding: "5px 12px", borderRadius: "20px", fontSize: "0.85rem", fontWeight: "bold", textTransform: "uppercase", letterSpacing: "1px" }}>
|
|
{isRenewal ? "Resumen de renovación" : "Resumen de contratación"}
|
|
</span>
|
|
</div>
|
|
<h1 className={styles.title}>{plan?.name}</h1>
|
|
<p className={styles.subtitle}>
|
|
{plan?.price === 0
|
|
? "Activa tu plan de forma instantánea. Sin tarjetas de crédito, sin complicaciones."
|
|
: isRenewal
|
|
? "Elegí cuántos meses querés sumar. El nuevo período se agrega al vencimiento actual cuando se apruebe el pago."
|
|
: "Selecciona la duración de tu plan. ¡Aprovechá los descuentos exclusivos pagando por más meses por adelantado!"}
|
|
</p>
|
|
|
|
{plan?.features && (
|
|
<ul className={styles.featureList}>
|
|
{plan.features.slice(0, 4).map((feature, idx) => (
|
|
<li key={idx} className={styles.featureItem}>
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4ade80" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
|
{feature}
|
|
</li>
|
|
))}
|
|
{plan.features.length > 4 && (
|
|
<li className={styles.featureItem} style={{ opacity: 0.7, fontStyle: 'italic' }}>
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg>
|
|
Y mucho más...
|
|
</li>
|
|
)}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
|
|
<div className={styles.rightPanel}>
|
|
<div className={styles.monthsContainer}>
|
|
{plan?.price === 0 ? (
|
|
<div className={`${styles.monthOption} ${styles.monthOptionSelected}`} style={{ cursor: 'default' }}>
|
|
<div>
|
|
<span className={styles.monthLabel}>Suscripción Base</span>
|
|
<span className={styles.discountBadge} style={{ backgroundColor: 'var(--wine-dark)' }}>
|
|
Gratis por siempre
|
|
</span>
|
|
</div>
|
|
<div className={styles.monthPrice}>
|
|
$0
|
|
</div>
|
|
</div>
|
|
) : (
|
|
[
|
|
{ months: 1, label: '1 Mes', discount: 0 },
|
|
{ months: 3, label: '3 Meses', discount: plan?.discount3Months || 0 },
|
|
{ months: 6, label: '6 Meses', discount: plan?.discount6Months || 0 },
|
|
{ months: 12, label: '12 Meses (1 Año)', discount: plan?.discount12Months || 0 },
|
|
].map((option) => (
|
|
<div
|
|
key={option.months}
|
|
className={`${styles.monthOption} ${selectedMonths === option.months ? styles.monthOptionSelected : ''}`}
|
|
onClick={() => setSelectedMonths(option.months)}
|
|
>
|
|
<div>
|
|
<span className={styles.monthLabel}>{option.label}</span>
|
|
{option.discount > 0 && (
|
|
<span className={styles.discountBadge}>
|
|
{option.discount}% OFF
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className={styles.monthPrice}>
|
|
${plan ? ((plan.price * option.months) * (1 - option.discount / 100)).toLocaleString('es-AR') : '0'}
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
|
|
<div className={styles.totalSection}>
|
|
<div className={styles.totalRow}>
|
|
<span className={styles.totalLabel}>Total a pagar hoy:</span>
|
|
<span className={styles.totalValue}>
|
|
${plan?.price === 0 ? "0" : finalPrice.toLocaleString('es-AR')}
|
|
</span>
|
|
</div>
|
|
|
|
<div className={styles.actionButtons}>
|
|
<button className={styles.btnSecondary} onClick={() => goTo("/landing/pricing")} disabled={isSubmitting}>
|
|
Volver
|
|
</button>
|
|
|
|
{plan?.price === 0 ? (
|
|
<button className={`${styles.btnPrimary} ${styles.btnFree}`} style={{ margin: 0 }} onClick={getLink} disabled={isSubmitting}>
|
|
Activar Gratis Ahora
|
|
</button>
|
|
) : (
|
|
<button className={styles.btnPrimary} onClick={getLink} disabled={isSubmitting}>
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect><line x1="1" y1="10" x2="23" y2="10"></line></svg>
|
|
{isSubmitting ? "Generando pago..." : isRenewal ? "Extender con MercadoPago" : "Pagar con MercadoPago"}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
</div>
|
|
</div>
|
|
</AnimatedContainer>
|
|
</SecurePage>
|
|
</Suspense>
|
|
);
|
|
}
|
|
|
|
export default function SubscriptionAddPage() {
|
|
return (
|
|
<Suspense fallback={<SuspenseLoading />}>
|
|
<SubscriptionAddContent />
|
|
</Suspense>
|
|
);
|
|
}
|