Chockfull
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
updateNotificationConfig,
|
||||
testNotificationChannels,
|
||||
} from "../../api/notificationConfig";
|
||||
import { createCheckoutSession, getSubscriptionStatus, verifyCheckoutSession, activateFreeTier } from "../../api/stripe";
|
||||
import { createOnboardingCheckout, verifyCheckoutSession, activateFreeTier } from "../../api/stripe";
|
||||
import Input from "../../components/Input";
|
||||
import Button from "../../components/Button";
|
||||
import TierPicker from "../../components/TierPicker";
|
||||
@@ -20,8 +20,8 @@ const TIER_LIMITS = {
|
||||
};
|
||||
|
||||
const STEPS = [
|
||||
"Choose Plan",
|
||||
"Create Account",
|
||||
"Choose Plan",
|
||||
"Add Wallet",
|
||||
"Alert Rules",
|
||||
"Notifications",
|
||||
@@ -33,8 +33,9 @@ export default function Subscribe() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
const [step, setStep] = useState(1);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const hasPaymentReturn = searchParams.get("payment") === "success";
|
||||
const [step, setStep] = useState(hasPaymentReturn || currentUser ? 2 : 1);
|
||||
const [loading, setLoading] = useState(hasPaymentReturn);
|
||||
const [error, setError] = useState("");
|
||||
const [skipWarning, setSkipWarning] = useState("");
|
||||
const [testResults, setTestResults] = useState(null);
|
||||
@@ -79,40 +80,61 @@ export default function Subscribe() {
|
||||
}, [currentUser, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentUser) return;
|
||||
const payment = searchParams.get("payment");
|
||||
const sessionId = searchParams.get("session_id");
|
||||
if (payment === "success" && sessionId) {
|
||||
setSearchParams({}, { replace: true });
|
||||
setLoading(true);
|
||||
verifyCheckoutSession(sessionId)
|
||||
.then(() => {
|
||||
setStep(3);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError("Payment verification failed: " + err.message);
|
||||
setStep(2);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
} else if (payment === "cancelled") {
|
||||
|
||||
if (payment === "cancelled") {
|
||||
setSearchParams({}, { replace: true });
|
||||
setStep(2);
|
||||
setError("Payment was cancelled. Please try again.");
|
||||
return;
|
||||
}
|
||||
}, [currentUser, searchParams, setSearchParams]);
|
||||
|
||||
if (payment !== "success" || !sessionId) return;
|
||||
|
||||
// Phase 1: no account yet — create it. This triggers an auth state change
|
||||
// which remounts the component (App.jsx swaps route trees). The URL params
|
||||
// are preserved so Phase 2 runs on the next mount.
|
||||
if (!currentUser) {
|
||||
const savedEmail = sessionStorage.getItem("kp_onboard_email");
|
||||
const savedPw = sessionStorage.getItem("kp_onboard_pw");
|
||||
if (!savedEmail || !savedPw) {
|
||||
setSearchParams({}, { replace: true });
|
||||
setError("Session expired. Please start the signup process again.");
|
||||
setStep(1);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
signup(savedEmail, savedPw).catch((err) => {
|
||||
setSearchParams({}, { replace: true });
|
||||
setError("Account creation failed: " + err.message);
|
||||
setStep(1);
|
||||
setLoading(false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Phase 2: authenticated — verify the checkout and activate subscription.
|
||||
setSearchParams({}, { replace: true });
|
||||
setLoading(true);
|
||||
sessionStorage.removeItem("kp_onboard_email");
|
||||
sessionStorage.removeItem("kp_onboard_pw");
|
||||
|
||||
verifyCheckoutSession(sessionId)
|
||||
.then(() => {
|
||||
setStep(3);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError("Payment verification failed: " + err.message);
|
||||
setStep(2);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}, [currentUser, searchParams, setSearchParams, signup]);
|
||||
|
||||
// ── Step handlers ─────────────────────────────────────────────────────────
|
||||
|
||||
function handleStep1() {
|
||||
setError("");
|
||||
if (!data.selectedTier) {
|
||||
setError("Please select a plan to continue");
|
||||
return;
|
||||
}
|
||||
setStep(2);
|
||||
}
|
||||
|
||||
async function handleStep2() {
|
||||
setError("");
|
||||
if (!data.email || !data.password || !data.confirmPassword) {
|
||||
setError("Please fill in all fields");
|
||||
@@ -126,24 +148,30 @@ export default function Subscribe() {
|
||||
setError("Password must be at least 6 characters");
|
||||
return;
|
||||
}
|
||||
setStep(2);
|
||||
}
|
||||
|
||||
async function handleStep2() {
|
||||
setError("");
|
||||
if (!data.selectedTier) {
|
||||
setError("Please select a plan to continue");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setLoading(true);
|
||||
if (!currentUser) {
|
||||
await signup(data.email, data.password);
|
||||
}
|
||||
|
||||
if (data.selectedTier === "free") {
|
||||
if (!currentUser) {
|
||||
await signup(data.email, data.password);
|
||||
}
|
||||
await activateFreeTier();
|
||||
setStep(3);
|
||||
return;
|
||||
}
|
||||
|
||||
const status = await getSubscriptionStatus();
|
||||
if (status.subscription_status === "active" || status.subscription_status === "trialing") {
|
||||
setStep(3);
|
||||
return;
|
||||
}
|
||||
const { url } = await createCheckoutSession(data.selectedTier);
|
||||
sessionStorage.setItem("kp_onboard_email", data.email);
|
||||
sessionStorage.setItem("kp_onboard_pw", data.password);
|
||||
const { url } = await createOnboardingCheckout(data.email, data.selectedTier);
|
||||
window.location.href = url;
|
||||
} catch (err) {
|
||||
if (err.code === "auth/email-already-in-use") {
|
||||
@@ -153,8 +181,9 @@ export default function Subscribe() {
|
||||
} else if (err.code === "auth/weak-password") {
|
||||
setError("Password is too weak");
|
||||
} else {
|
||||
setError("Failed to create account: " + err.message);
|
||||
setError("Failed to process plan selection: " + err.message);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
@@ -325,21 +354,6 @@ export default function Subscribe() {
|
||||
|
||||
// ── Step content ──────────────────────────────────────────────────────────
|
||||
|
||||
function StepChoosePlan() {
|
||||
return (
|
||||
<>
|
||||
<h2 className="mb-sm">Choose your plan</h2>
|
||||
<p className="subscribe__subtitle">
|
||||
Select the plan that works best for you. You can upgrade anytime.
|
||||
</p>
|
||||
<TierPicker
|
||||
onSelect={(tier) => set("selectedTier", tier)}
|
||||
selectedTier={data.selectedTier}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function StepCreateAccount() {
|
||||
return (
|
||||
<>
|
||||
@@ -373,6 +387,21 @@ export default function Subscribe() {
|
||||
);
|
||||
}
|
||||
|
||||
function StepChoosePlan() {
|
||||
return (
|
||||
<>
|
||||
<h2 className="mb-sm">Choose your monitoring plan</h2>
|
||||
<p className="subscribe__subtitle">
|
||||
Select the plan that works best for you. You can upgrade anytime.
|
||||
</p>
|
||||
<TierPicker
|
||||
onSelect={(tier) => set("selectedTier", tier)}
|
||||
selectedTier={data.selectedTier}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function StepAddWallet() {
|
||||
return (
|
||||
<>
|
||||
@@ -676,11 +705,13 @@ export default function Subscribe() {
|
||||
}
|
||||
|
||||
const nextLabel = step === 1
|
||||
? "Continue"
|
||||
? "Create Account"
|
||||
: step === 2
|
||||
? data.selectedTier === "free"
|
||||
? "Create Account"
|
||||
: "Create Account & Subscribe"
|
||||
? !data.selectedTier
|
||||
? "Continue"
|
||||
: data.selectedTier === "free"
|
||||
? "Start Free Trial"
|
||||
: "Subscribe & Continue"
|
||||
: step === 5
|
||||
? "Finish"
|
||||
: "Next →";
|
||||
@@ -711,7 +742,7 @@ export default function Subscribe() {
|
||||
)}
|
||||
<Button
|
||||
onClick={handleNext}
|
||||
disabled={loading || (step === 1 && !data.selectedTier)}
|
||||
disabled={loading || (step === 2 && !data.selectedTier)}
|
||||
className="text-bold"
|
||||
>
|
||||
{loading ? "Please wait..." : nextLabel}
|
||||
@@ -724,8 +755,8 @@ export default function Subscribe() {
|
||||
// ── Render ────────────────────────────────────────────────────────────────
|
||||
|
||||
const stepContent = {
|
||||
1: StepChoosePlan(),
|
||||
2: StepCreateAccount(),
|
||||
1: StepCreateAccount(),
|
||||
2: StepChoosePlan(),
|
||||
3: StepAddWallet(),
|
||||
4: StepAlertRules(),
|
||||
5: StepNotifications(),
|
||||
@@ -734,7 +765,7 @@ export default function Subscribe() {
|
||||
|
||||
return (
|
||||
<div className="subscribe">
|
||||
<div className={`subscribe__container${step === 1 ? " subscribe__container--wide" : ""}`}>
|
||||
<div className={`subscribe__container${step === 2 ? " subscribe__container--wide" : ""}`}>
|
||||
<h1 className="subscribe__title">Koin Ping</h1>
|
||||
|
||||
{ProgressBar()}
|
||||
@@ -752,7 +783,7 @@ export default function Subscribe() {
|
||||
{Footer()}
|
||||
</div>
|
||||
|
||||
{step === 2 && (
|
||||
{step === 1 && (
|
||||
<p className="subscribe__login-link">
|
||||
Already have an account?{" "}
|
||||
<a href="/login">Log in here</a>
|
||||
|
||||
Reference in New Issue
Block a user