Add LynkedUp roofing portal: full login + registration auth UI

- Split-screen dark/orange auth shell with animated scan showcase
- Login state machine: social/email, passkey, password, OTP (email/SMS/WhatsApp),
  TOTP, push, try-another-way, forgot-password, terms gate, success
- 4-step registration wizard: Account, Property, Verify, Address
- US-default phone + address with /api/geo lookup (PIN auto-fill / search)
- Roofing-themed content, Inter font, logo from public/image

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 18:36:08 +05:30
parent 50b120e184
commit 8c5df83d63
13 changed files with 2520 additions and 21 deletions
+63
View File
@@ -0,0 +1,63 @@
import { NextResponse } from "next/server";
// Keyless geo BFF for the registration address step.
// - GET /api/geo?pin=201310 → { ok, city, state, localities[] }
// - GET /api/geo?addr=foo&country=us → { ok, suggestions[] }
// All data is mocked so the demo works without any external key.
const PINS: Record<string, { city: string; state: string; localities: string[] }> = {
"201310": { city: "Greater Noida", state: "Uttar Pradesh", localities: ["Sector 18", "Pari Chowk", "Knowledge Park", "Alpha 1"] },
"201301": { city: "Noida", state: "Uttar Pradesh", localities: ["Sector 1", "Sector 15", "Sector 18", "Sector 62"] },
"110001": { city: "New Delhi", state: "Delhi", localities: ["Connaught Place", "Barakhamba", "Janpath"] },
"400001": { city: "Mumbai", state: "Maharashtra", localities: ["Fort", "Colaba", "Churchgate"] },
"560001": { city: "Bengaluru", state: "Karnataka", localities: ["MG Road", "Cubbon Park", "Shivajinagar"] },
};
function pinFallback(pin: string) {
// derive a plausible state from the first digit for unknown PINs
const map: Record<string, { city: string; state: string }> = {
"1": { city: "Delhi NCR", state: "Delhi" },
"2": { city: "Lucknow", state: "Uttar Pradesh" },
"3": { city: "Jaipur", state: "Rajasthan" },
"4": { city: "Mumbai", state: "Maharashtra" },
"5": { city: "Hyderabad", state: "Telangana" },
"6": { city: "Chennai", state: "Tamil Nadu" },
"7": { city: "Kolkata", state: "West Bengal" },
};
const hit = map[pin[0]] ?? { city: "City", state: "State" };
return { ...hit, localities: ["Central", "North", "South", "East", "West"] };
}
export function GET(request: Request) {
const { searchParams } = new URL(request.url);
const pin = searchParams.get("pin");
const addr = searchParams.get("addr");
const country = searchParams.get("country") ?? "";
if (pin) {
if (!/^\d{6}$/.test(pin)) return NextResponse.json({ ok: false, error: "invalid_pin" });
const data = PINS[pin] ?? pinFallback(pin);
return NextResponse.json({ ok: true, ...data });
}
if (addr) {
const q = addr.trim();
if (q.length < 3) return NextResponse.json({ ok: true, suggestions: [] });
const cities: Record<string, { city: string; state: string }[]> = {
us: [{ city: "New York", state: "New York" }, { city: "Newark", state: "New Jersey" }, { city: "San Francisco", state: "California" }],
gb: [{ city: "London", state: "" }, { city: "Manchester", state: "" }, { city: "Birmingham", state: "" }],
ae: [{ city: "Dubai", state: "Dubai" }, { city: "Abu Dhabi", state: "Abu Dhabi" }],
};
const pool = cities[country.toLowerCase()] ?? cities.us;
const suggestions = pool.map((c, i) => ({
line1: `${q} ${10 + i * 7}, ${c.city}`,
line2: i === 0 ? "Downtown" : "Suburb",
city: c.city,
state: c.state,
postal: country.toLowerCase() === "us" ? `${10001 + i}` : `${q.slice(0, 2).toUpperCase()}${i}A 1BB`,
}));
return NextResponse.json({ ok: true, suggestions });
}
return NextResponse.json({ ok: false, error: "missing_query" });
}