Add src/pages/Onboarding.jsx
This commit is contained in:
parent
3f6b112274
commit
523ca30de8
|
|
@ -0,0 +1,236 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { base44 } from "@/api/base44Client";
|
||||||
|
import { Store, MapPin, Hash, ArrowRight } from "lucide-react";
|
||||||
|
import WarningModal from "@/components/onboarding/WarningModal";
|
||||||
|
import SignupForm from "@/components/onboarding/SignupForm";
|
||||||
|
import ActivationPending from "@/components/onboarding/ActivationPending";
|
||||||
|
|
||||||
|
// Demo token resolver — simulates decoding the onboarding token
|
||||||
|
function resolveToken(token) {
|
||||||
|
return {
|
||||||
|
store_id: "AA1112",
|
||||||
|
store_name: "Radi Box N Ship",
|
||||||
|
store_address: "1939 W. Manchester Ave., Los Angeles, CA 90047",
|
||||||
|
onboarding_token: token || "demo-token-abc123"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Onboarding() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const token = urlParams.get("token") || "demo-token-abc123";
|
||||||
|
|
||||||
|
const [storeData] = useState(() => resolveToken(token));
|
||||||
|
const [showWarning, setShowWarning] = useState(false);
|
||||||
|
const [warningDismissed, setWarningDismissed] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [activationPending, setActivationPending] = useState(false);
|
||||||
|
const [pendingEmail, setPendingEmail] = useState("");
|
||||||
|
const [pendingUserId, setPendingUserId] = useState("");
|
||||||
|
const [pendingCode, setPendingCode] = useState("");
|
||||||
|
const [pendingUserData, setPendingUserData] = useState(null);
|
||||||
|
// Show landing first, then modal on CTA click
|
||||||
|
const [showLanding, setShowLanding] = useState(true);
|
||||||
|
|
||||||
|
const handleBeginOnboarding = () => {
|
||||||
|
setShowLanding(false);
|
||||||
|
setShowWarning(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleWarningContinue = () => {
|
||||||
|
setShowWarning(false);
|
||||||
|
setWarningDismissed(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateCode = () => {
|
||||||
|
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
||||||
|
let code = "";
|
||||||
|
for (let i = 0; i < 6; i++) code += chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
return code;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendActivationEmail = async (email, userId, code) => {
|
||||||
|
await base44.integrations.Core.SendEmail({
|
||||||
|
to: email,
|
||||||
|
subject: "Final Step: Verify your PackageHub360 account",
|
||||||
|
body: `<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; color: #222;">
|
||||||
|
<div style="background: linear-gradient(135deg, #1a5276, #2980b9); padding: 32px 40px; border-radius: 12px 12px 0 0; text-align: center;">
|
||||||
|
<h1 style="color: #ffffff; font-size: 22px; margin: 0;">PackageHub<span style="color: #e67e22;">360</span></h1>
|
||||||
|
<p style="color: #bde0f5; font-size: 13px; margin: 6px 0 0;">Business Centers</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="background: #ffffff; padding: 36px 40px; border: 1px solid #e5e7eb; border-top: none; border-radius: 0 0 12px 12px;">
|
||||||
|
<h2 style="font-size: 20px; color: #1a5276; margin-top: 0;">Verify Your Email Address</h2>
|
||||||
|
|
||||||
|
<p style="font-size: 15px; line-height: 1.6; color: #444;">
|
||||||
|
Thank you for creating your PackageHub360 account. To complete your registration, please verify your email address using the code below.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style="background: #f0f6fc; border: 1px solid #c5dff5; border-radius: 10px; padding: 20px; text-align: center; margin: 28px 0;">
|
||||||
|
<p style="font-size: 13px; color: #666; margin: 0 0 8px;">Your verification code is:</p>
|
||||||
|
<p style="font-size: 36px; font-weight: bold; letter-spacing: 10px; color: #1a5276; margin: 0;">${code}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr style="border: none; border-top: 1px solid #e5e7eb; margin: 28px 0;" />
|
||||||
|
|
||||||
|
<p style="font-size: 13px; color: #888; line-height: 1.6;">
|
||||||
|
If you did not create a PackageHub360 account, please ignore this email or contact us at
|
||||||
|
<a href="mailto:support@packagehub360.com" style="color: #1a5276;">support@packagehub360.com</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="font-size: 14px; color: #555; margin-top: 20px;">
|
||||||
|
Thanks,<br />
|
||||||
|
<strong>The PackageHub Team</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSignup = async ({ email, password, mobile, username }) => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const activationCode = generateCode();
|
||||||
|
|
||||||
|
const newUser = await base44.entities.BoUser.create({
|
||||||
|
email,
|
||||||
|
username,
|
||||||
|
password_hash: password,
|
||||||
|
store_id: storeData.store_id,
|
||||||
|
store_name: storeData.store_name,
|
||||||
|
store_address: storeData.store_address,
|
||||||
|
onboarding_token: storeData.onboarding_token,
|
||||||
|
first_login_completed: false,
|
||||||
|
stripe_onboarding_started: false,
|
||||||
|
full_name: "New Member",
|
||||||
|
phone: mobile,
|
||||||
|
city: "Los Angeles",
|
||||||
|
state: "CA",
|
||||||
|
zip_code: "90047",
|
||||||
|
street_address: "1939 W. Manchester Ave.",
|
||||||
|
status: "active",
|
||||||
|
activation_code: activationCode,
|
||||||
|
activation_email_sent: true,
|
||||||
|
is_activated: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendActivationEmail(email, newUser.id, activationCode);
|
||||||
|
|
||||||
|
setPendingEmail(email);
|
||||||
|
setPendingUserId(newUser.id);
|
||||||
|
setPendingCode(activationCode);
|
||||||
|
setPendingUserData({
|
||||||
|
email,
|
||||||
|
username,
|
||||||
|
store_id: storeData.store_id,
|
||||||
|
store_name: storeData.store_name,
|
||||||
|
full_name: "New Member",
|
||||||
|
is_activated: false,
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
setActivationPending(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResendEmail = async () => {
|
||||||
|
await sendActivationEmail(pendingEmail, pendingUserId, pendingCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-[#f0f2f5]">
|
||||||
|
{/* Warning Modal */}
|
||||||
|
<WarningModal open={showWarning} onContinue={handleWarningContinue} />
|
||||||
|
|
||||||
|
{showLanding && !warningDismissed &&
|
||||||
|
<div className="min-h-screen flex flex-col items-center justify-center px-4">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.6 }}
|
||||||
|
className="max-w-lg w-full">
|
||||||
|
|
||||||
|
{/* Welcome Card */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-xl border border-gray-100 overflow-hidden text-left">
|
||||||
|
|
||||||
|
{/* Card Header with logo */}
|
||||||
|
<div className="px-8 pt-8 pb-2 text-center">
|
||||||
|
<div className="flex justify-center mb-4">
|
||||||
|
<img
|
||||||
|
src="https://qtrypzzcjebvfcihiynt.supabase.co/storage/v1/object/public/base44-prod/public/69ac537c5138f01ec706f4bc/9c287bb3b_Logo_PBC_horz_color.png"
|
||||||
|
alt="PackageHub Business Centers"
|
||||||
|
className="h-16 w-auto object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold text-gray-900 mb-1">Welcome</h1>
|
||||||
|
<p className="text-sm text-[#2980b9]">You've been invited to set up your PackageHub360 store account.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Card Body */}
|
||||||
|
<div className="px-8 py-7">
|
||||||
|
<div className="space-y-3 mb-7">
|
||||||
|
<div className="flex items-center gap-3 p-3 bg-blue-50 rounded-xl">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-[#1a5276] flex items-center justify-center shrink-0">
|
||||||
|
<Store className="w-5 h-5 text-white" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-medium text-gray-400 uppercase tracking-wide">Store Name</p>
|
||||||
|
<p className="text-sm font-semibold text-gray-800">{storeData.store_name}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 p-3 bg-gray-50 rounded-xl">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-gray-200 flex items-center justify-center shrink-0">
|
||||||
|
<Hash className="w-5 h-5 text-gray-500" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-medium text-gray-400 uppercase tracking-wide">Store ID</p>
|
||||||
|
<p className="text-sm font-semibold text-gray-800">{storeData.store_id}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 p-3 bg-gray-50 rounded-xl">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-gray-200 flex items-center justify-center shrink-0">
|
||||||
|
<MapPin className="w-5 h-5 text-gray-500" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-medium text-gray-400 uppercase tracking-wide">Store Address</p>
|
||||||
|
<p className="text-sm font-semibold text-gray-800">{storeData.store_address}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleBeginOnboarding}
|
||||||
|
className="w-full h-12 bg-[#1a5276] hover:bg-[#154360] text-white text-sm font-semibold rounded-xl transition-all duration-200 shadow-md flex items-center justify-center gap-2">
|
||||||
|
Begin Account Setup
|
||||||
|
<ArrowRight className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p className="text-red-600 mt-5 text-xs font-medium text-center leading-relaxed">
|
||||||
|
This one-time link was generated specifically for your store. Do not share it.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{/* Signup Form */}
|
||||||
|
{warningDismissed && !activationPending &&
|
||||||
|
<div className="min-h-screen flex items-center justify-center px-4 py-12">
|
||||||
|
<SignupForm storeData={storeData} onSubmit={handleSignup} isLoading={isLoading} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{/* Activation Pending Screen */}
|
||||||
|
{activationPending &&
|
||||||
|
<div className="min-h-screen flex items-center justify-center px-4 py-12">
|
||||||
|
<ActivationPending
|
||||||
|
email={pendingEmail}
|
||||||
|
userId={pendingUserId}
|
||||||
|
expectedCode={pendingCode}
|
||||||
|
onResend={handleResendEmail}
|
||||||
|
userData={pendingUserData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>);
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue