import { useState } from "react"; import { Form, required, useNotify, useTranslate } from "ra-core"; import { Layout } from "@/components/supabase/layout"; import type { FieldValues, SubmitHandler } from "react-hook-form"; import { TextInput } from "@/components/admin/text-input"; import { Button } from "@/components/ui/button"; import { OtpInput } from "@/components/supabase/otp-input"; import { supabase } from "@/components/atomic-crm/providers/supabase/supabase"; interface EmailFormData { email: string; } type Step = "email" | "otp"; export const OtpLoginPage = () => { const [loading, setLoading] = useState(false); const [step, setStep] = useState("email"); const [email, setEmail] = useState(""); const [otp, setOtp] = useState(""); const [otpError, setOtpError] = useState(false); const notify = useNotify(); const translate = useTranslate(); const submitEmail = async (values: EmailFormData) => { try { setLoading(true); // Normalize email to lowercase const normalizedEmail = values.email.trim().toLowerCase(); setEmail(normalizedEmail); const { error } = await supabase.auth.signInWithOtp({ email: normalizedEmail, options: { shouldCreateUser: false, // Only allow existing users }, }); if (error) { throw error; } notify(translate("crm.auth.code_sent"), { type: "success" }); setStep("otp"); } catch (error: any) { notify( typeof error === "string" ? error : typeof error === "undefined" || !error.message ? "ra.auth.sign_in_error" : error.message, { type: "warning", messageArgs: { _: typeof error === "string" ? error : error && error.message ? error.message : undefined, }, }, ); } finally { setLoading(false); } }; const verifyOtp = async (otpCode: string) => { try { setLoading(true); setOtpError(false); // Trim whitespace from OTP code const cleanOtp = otpCode.trim(); const { data, error } = await supabase.auth.verifyOtp({ email: email.trim().toLowerCase(), // Normalize email token: cleanOtp, type: "magiclink", // Changed from 'email' - some Supabase versions treat OTP as magiclink }); if (error) { throw error; } if (!data.session) { throw new Error("Failed to create session"); } // Check if user exists in sales table (access control) const { data: saleData, error: saleError } = await supabase .from("sales") .select("id, email_confirmed_at") .eq("user_id", data.user.id) .single(); if (saleError || !saleData) { // User authenticated but not in sales table - deny access await supabase.auth.signOut(); throw new Error(translate("crm.auth.no_access")); } // User is logged in and authorized notify(translate("crm.auth.login_successful"), { type: "success" }); // Check if this is their first login (email not confirmed yet) if (!saleData.email_confirmed_at) { // Navigate to change password window.location.href = "#/change-password"; window.location.reload(); } else { // Navigate to dashboard window.location.href = "#/"; window.location.reload(); } } catch (error: any) { setOtpError(true); notify( typeof error === "string" ? error : typeof error === "undefined" || !error.message ? translate("crm.auth.invalid_code") : error.message, { type: "warning", messageArgs: { _: typeof error === "string" ? error : error && error.message ? error.message : undefined, }, }, ); } finally { setLoading(false); } }; const handleOtpComplete = (otpCode: string) => { verifyOtp(otpCode); }; const handleResendCode = async () => { setOtp(""); setOtpError(false); await submitEmail({ email }); }; return ( {step === "email" ? ( <>

{translate("crm.auth.login_with_code")}

{translate("crm.auth.enter_email_for_code")}

className="space-y-8" onSubmit={submitEmail as SubmitHandler} > ) : ( <>

{translate("crm.auth.enter_verification_code")}

{translate("crm.auth.code_sent_to", { email })}

{otpError && (

{translate("crm.auth.invalid_code")}

)}
)}
); }; OtpLoginPage.path = "otp-login";