feat(otp): mock phone OTP fallback while Twilio is down #17

Merged
tanweer919 merged 1 commits from tanweer919/lynkeduppro-crm:feat/login-methods into goutamnextflow 2026-07-13 08:31:30 +00:00
Showing only changes of commit 27a5aa939e - Show all commits
+14 -2
View File
@@ -19,6 +19,12 @@ type Addr = { line1?: string; line2?: string; city?: string; state?: string; pos
const STEPS = ["Account", "Verify", "Address"]; const STEPS = ["Account", "Verify", "Address"];
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Demo fallback for phone OTP while the Twilio sender is down. When on, the Verify
// step skips Supabase/Twilio and accepts a fixed code. Flip NEXT_PUBLIC_MOCK_OTP to
// "false" (or remove it) to restore real SMS verification — no other change needed.
const MOCK_OTP = process.env.NEXT_PUBLIC_MOCK_OTP === "true";
const DEMO_OTP = "123456";
export function RegisterFlow({ mode = "register" }: { mode?: "register" | "onboard" }) { export function RegisterFlow({ mode = "register" }: { mode?: "register" | "onboard" }) {
// "onboard" = an already-authenticated OAuth (Google) user completing their CRM // "onboard" = an already-authenticated OAuth (Google) user completing their CRM
// profile: email is skipped (Google-verified), the auth account already exists so // profile: email is skipped (Google-verified), the auth account already exists so
@@ -390,6 +396,7 @@ function PhoneVerify({ cc, country, initialPhone, verified, onVerified }: {
if (value.replace(/\D/g, "").length !== country.digits) { setState("invalid_mobile"); return; } if (value.replace(/\D/g, "").length !== country.digits) { setState("invalid_mobile"); return; }
setState("sending"); setState("sending");
setOtpError(""); setOtpError("");
if (MOCK_OTP) { setState("sent"); return; } // demo: skip Twilio entirely
try { try {
await addPhone(e164); // Supabase → Twilio sends the SMS OTP await addPhone(e164); // Supabase → Twilio sends the SMS OTP
setState("sent"); setState("sent");
@@ -400,6 +407,11 @@ function PhoneVerify({ cc, country, initialPhone, verified, onVerified }: {
async function submit(code: string) { async function submit(code: string) {
setOtpError(""); setOtpError("");
if (MOCK_OTP) {
if (code === DEMO_OTP) onVerified();
else setOtpError(`Demo mode — enter ${DEMO_OTP} to verify.`);
return;
}
try { try {
await verifyPhone(e164, code); // confirm the OTP (phone_change) await verifyPhone(e164, code); // confirm the OTP (phone_change)
onVerified(); onVerified();
@@ -436,13 +448,13 @@ function PhoneVerify({ cc, country, initialPhone, verified, onVerified }: {
</div> </div>
<div className="row between" style={{ marginTop: 12 }}> <div className="row between" style={{ marginTop: 12 }}>
<span className="faint" style={{ fontSize: 12 }}>Standard SMS rates may apply.</span> <span className="faint" style={{ fontSize: 12 }}>{MOCK_OTP ? "Demo verification (no SMS sent)." : "Standard SMS rates may apply."}</span>
<button className="btn btn-sm" style={{ width: "auto" }} disabled={state === "sending" || state === "sent"} onClick={send}> <button className="btn btn-sm" style={{ width: "auto" }} disabled={state === "sending" || state === "sent"} onClick={send}>
{state === "sending" ? "Sending…" : state === "sent" ? "Sent" : "Send code"} {state === "sending" ? "Sending…" : state === "sent" ? "Sent" : "Send code"}
</button> </button>
</div> </div>
{state === "sent" && <p className="faint" style={{ fontSize: 12, marginTop: 10 }}>Code sent to {cc} {value} by SMS.</p>} {state === "sent" && <p className="faint" style={{ fontSize: 12, marginTop: 10 }}>{MOCK_OTP ? `Demo mode — enter ${DEMO_OTP} to verify (SMS temporarily disabled).` : `Code sent to ${cc} ${value} by SMS.`}</p>}
{state === "invalid_mobile" && <div style={{ marginTop: 10 }}><FlashNote tone="error">Enter a valid {country.digits}-digit mobile number.</FlashNote></div>} {state === "invalid_mobile" && <div style={{ marginTop: 10 }}><FlashNote tone="error">Enter a valid {country.digits}-digit mobile number.</FlashNote></div>}
{state === "send_error" && <div style={{ marginTop: 10 }}><FlashNote tone="error">Couldn&apos;t send the code. Check the number and try again.</FlashNote></div>} {state === "send_error" && <div style={{ marginTop: 10 }}><FlashNote tone="error">Couldn&apos;t send the code. Check the number and try again.</FlashNote></div>}