Add src/pages/Activate.jsx
This commit is contained in:
parent
502d00d1d4
commit
e9dab789b8
|
|
@ -0,0 +1,198 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { base44 } from "@/api/base44Client";
|
||||||
|
import { createPageUrl } from "@/utils";
|
||||||
|
import { ShieldCheck, Loader2, AlertCircle } from "lucide-react";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import ActivationSuccessModal from "@/components/onboarding/ActivationSuccessModal";
|
||||||
|
import DemoControlsPanel from "@/components/demo/DemoControlsPanel";
|
||||||
|
import { getDemoControls } from "@/components/demo/useDemoControls";
|
||||||
|
|
||||||
|
export default function Activate() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const userId = urlParams.get("uid");
|
||||||
|
const codeFromUrl = urlParams.get("code") || "";
|
||||||
|
|
||||||
|
const [code, setCode] = useState(codeFromUrl);
|
||||||
|
const [user, setUser] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [showSuccess, setShowSuccess] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadUser = async () => {
|
||||||
|
if (!userId) {
|
||||||
|
setError("Invalid activation link. Please check your email and try again.");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const results = await base44.entities.BoUser.filter({ id: userId });
|
||||||
|
if (!results || results.length === 0) {
|
||||||
|
setError("Account not found. Please contact support.");
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const foundUser = results[0];
|
||||||
|
if (foundUser.is_activated) {
|
||||||
|
// Already activated — send to dashboard
|
||||||
|
restoreSessionAndRedirect(foundUser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setUser(foundUser);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
loadUser();
|
||||||
|
}, [userId]);
|
||||||
|
|
||||||
|
const restoreSessionAndRedirect = (u) => {
|
||||||
|
sessionStorage.setItem("demo_user", JSON.stringify({
|
||||||
|
email: u.email,
|
||||||
|
store_id: u.store_id,
|
||||||
|
store_name: u.store_name,
|
||||||
|
store_address: u.store_address,
|
||||||
|
full_name: u.full_name,
|
||||||
|
first_login_completed: u.first_login_completed
|
||||||
|
}));
|
||||||
|
window.location.href = createPageUrl("Dashboard") + "?first_login=true";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setError("");
|
||||||
|
if (!code.trim()) {
|
||||||
|
setError("Please enter the verification code from your email.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Demo: simulate invalid code
|
||||||
|
const demoControls = getDemoControls();
|
||||||
|
if (!demoControls.activationCodeValid) {
|
||||||
|
setError("The verification code you entered is invalid. Please check your email and try again.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (code.trim().toUpperCase() !== user.activation_code) {
|
||||||
|
setError("That code doesn't match. Please check your email and try again.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSubmitting(true);
|
||||||
|
await base44.entities.BoUser.update(user.id, {
|
||||||
|
is_activated: true,
|
||||||
|
activation_completed_at: new Date().toISOString()
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
setShowSuccess(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSuccessConfirm = () => {
|
||||||
|
restoreSessionAndRedirect(user);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center bg-[#f0f2f5]">
|
||||||
|
<Loader2 className="w-8 h-8 animate-spin text-[#1a5276]" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-[#f0f2f5] flex items-center justify-center px-4">
|
||||||
|
<ActivationSuccessModal
|
||||||
|
open={showSuccess}
|
||||||
|
storeName={user?.store_name}
|
||||||
|
storeAddress={user?.store_address}
|
||||||
|
onConfirm={handleSuccessConfirm}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
className="w-full max-w-md"
|
||||||
|
>
|
||||||
|
<div className="bg-white rounded-2xl shadow-xl border border-gray-200 overflow-hidden">
|
||||||
|
{/* Header — logo + title, plain white like production */}
|
||||||
|
<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>
|
||||||
|
<div className="w-12 h-12 bg-blue-50 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||||
|
<ShieldCheck className="w-6 h-6 text-[#2980b9]" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-1">Verify Your Account</h2>
|
||||||
|
<p className="text-sm text-[#2980b9]">Enter the code from your email</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Body */}
|
||||||
|
<div className="px-8 py-6">
|
||||||
|
{error && !user ? (
|
||||||
|
<div className="flex items-start gap-3 bg-red-50 border border-red-100 rounded-xl p-4">
|
||||||
|
<AlertCircle className="w-5 h-5 text-red-500 mt-0.5 shrink-0" />
|
||||||
|
<p className="text-sm text-red-700">{error}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p className="text-gray-500 text-sm mb-6 leading-relaxed">
|
||||||
|
We sent a verification code to{" "}
|
||||||
|
<span className="font-semibold text-gray-800">{user?.email}</span>.
|
||||||
|
Enter it below to complete your account activation.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Label htmlFor="code" className="text-sm font-medium text-gray-700">
|
||||||
|
Verification Code <span className="text-red-500">*</span>
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="code"
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter your verification code"
|
||||||
|
value={code}
|
||||||
|
onChange={(e) => setCode(e.target.value.toUpperCase())}
|
||||||
|
maxLength={8}
|
||||||
|
className={`h-11 text-center text-lg tracking-[0.3em] font-semibold border-gray-200 focus:border-[#2980b9] focus:ring-[#2980b9] ${
|
||||||
|
error ? "border-red-400" : ""
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
{error && (
|
||||||
|
<p className="text-xs text-red-500 flex items-center gap-1">
|
||||||
|
<AlertCircle className="w-3 h-3" /> {error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={submitting}
|
||||||
|
className="w-full h-11 bg-[#2980b9] hover:bg-[#2471a3] text-white font-semibold rounded-lg"
|
||||||
|
>
|
||||||
|
{submitting ? (
|
||||||
|
<Loader2 className="w-5 h-5 animate-spin" />
|
||||||
|
) : (
|
||||||
|
"Verify & Activate Account"
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p className="text-xs text-gray-400 text-center mt-5">
|
||||||
|
Need help? Contact{" "}
|
||||||
|
<a href="mailto:support@packagehub360.com" className="text-[#1a5276] underline">
|
||||||
|
support@packagehub360.com
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DemoControlsPanel />
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue