Enhance portal profile/support modules and add rebuild prompt doc

Add profile (verify-to-reveal, KYC rules, contact-change OTP flows,
notifications/consent tabs, devices) and support (5 channels, message
center, ticket routing, live chat, help center) updates, the geo BFF
route, login auth flow, and a self-contained module rebuild prompt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Goutam0612
2026-06-29 21:02:05 +05:30
parent 783c7da7a1
commit e44e24ace2
20 changed files with 2779 additions and 388 deletions
+124
View File
@@ -0,0 +1,124 @@
import { NextResponse } from "next/server";
// Lightweight backend-for-frontend over free, keyless geo services.
// GET /api/geo?pin=203201 → India Post: { ok, pin, city, district, state, localities }
// GET /api/geo?q=alpha → India Post locality search: { ok, results }
// GET /api/geo?addr=350 5th&country=us → OpenStreetMap/Nominatim house-number autosuggest:
// { ok, results: [{ label, line1, line2, city, state, postal }] }
// If an upstream service is unreachable we fall back to a small built-in dataset
// so the registration flow keeps working offline / in the demo.
type Locality = { city: string; district: string; state: string; localities: string[] };
const FALLBACK_PINS: Record<string, Locality> = {
"203201": { city: "Greater Noida", district: "Gautam Buddh Nagar", state: "Uttar Pradesh", localities: ["Dankaur", "Dhanouri Kalan", "Jaganpur Afjalpur"] },
"201301": { city: "Noida", district: "Gautam Buddh Nagar", state: "Uttar Pradesh", localities: ["Noida Sector 1", "Noida Sector 27", "Hoshiarpur"] },
"110001": { city: "New Delhi", district: "Central Delhi", state: "Delhi", localities: ["Connaught Place", "Janpath", "Sansad Marg"] },
"122001": { city: "Gurugram", district: "Gurugram", state: "Haryana", localities: ["Gurgaon", "Civil Lines", "Gurgaon Sector 14"] },
"226001": { city: "Lucknow", district: "Lucknow", state: "Uttar Pradesh", localities: ["Lucknow GPO", "Hazratganj", "Lalbagh"] },
"302001": { city: "Jaipur", district: "Jaipur", state: "Rajasthan", localities: ["Jaipur GPO", "Kishanpole Bazar", "Mi Road"] },
"456010": { city: "Ujjain", district: "Ujjain", state: "Madhya Pradesh", localities: ["Ujjain GPO", "Freeganj", "Mahakal"] },
};
type IndiaPostOffice = { Name: string; District: string; State: string; Pincode: string; BranchType?: string };
type IndiaPostResp = Array<{ Status: string; PostOffice: IndiaPostOffice[] | null }>;
async function fetchUpstream(url: string): Promise<IndiaPostResp> {
const res = await fetch(url, {
headers: { "User-Agent": "YEIDA-Portal/1.0 (address autofill)" },
// The PIN → locality mapping is effectively static; cache for a day.
next: { revalidate: 86400 },
});
if (!res.ok) throw new Error(`upstream ${res.status}`);
return (await res.json()) as IndiaPostResp;
}
const clean = (s: string) => s.replace(/\s+/g, " ").trim();
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const pin = (searchParams.get("pin") || "").replace(/\D/g, "");
const q = clean(searchParams.get("q") || "");
const addr = clean(searchParams.get("addr") || "");
const country = (searchParams.get("country") || "").toLowerCase().replace(/[^a-z]/g, "");
// --- PIN lookup → city / district / state + locality options ---
if (pin) {
if (pin.length !== 6) {
return NextResponse.json({ ok: false, error: "PIN must be 6 digits" }, { status: 400 });
}
try {
const data = await fetchUpstream(`https://api.postalpincode.in/pincode/${pin}`);
const offices = data?.[0]?.Status === "Success" ? data[0].PostOffice ?? [] : [];
if (offices.length) {
const first = offices[0];
const localities = [...new Set(offices.map((o) => clean(o.Name)))];
return NextResponse.json({
ok: true, pin, city: first.District, district: first.District, state: first.State, localities,
});
}
} catch {
/* fall through to the built-in fallback below */
}
const fb = FALLBACK_PINS[pin];
if (fb) {
return NextResponse.json({ ok: true, pin, ...fb, source: "fallback" });
}
return NextResponse.json({ ok: false, error: "PIN not found" }, { status: 404 });
}
// --- Address / locality typeahead by name ---
if (q) {
if (q.length < 3) return NextResponse.json({ ok: true, results: [] });
try {
const data = await fetchUpstream(`https://api.postalpincode.in/postoffice/${encodeURIComponent(q)}`);
const offices = data?.[0]?.Status === "Success" ? data[0].PostOffice ?? [] : [];
const results = offices.slice(0, 8).map((o) => ({
label: `${clean(o.Name)} · ${o.District}, ${o.State}${o.Pincode}`,
line2: clean(o.Name), city: o.District, state: o.State, pin: o.Pincode,
}));
return NextResponse.json({ ok: true, results });
} catch {
const ql = q.toLowerCase();
const results = Object.entries(FALLBACK_PINS)
.filter(([, v]) => v.city.toLowerCase().includes(ql) || v.localities.some((l) => l.toLowerCase().includes(ql)))
.slice(0, 8)
.map(([p, v]) => ({ label: `${v.city} · ${v.district}, ${v.state}${p}`, line2: v.city, city: v.city, state: v.state, pin: p }));
return NextResponse.json({ ok: true, results, source: "fallback" });
}
}
// --- Worldwide house-number / street autosuggest (non-India) via OpenStreetMap ---
if (addr) {
if (addr.length < 3) return NextResponse.json({ ok: true, results: [] });
try {
const url = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(addr)}` +
`${country ? `&countrycodes=${country}` : ""}&format=jsonv2&addressdetails=1&limit=6`;
const res = await fetch(url, {
headers: { "User-Agent": "YEIDA-Portal/1.0 (address autofill)" },
next: { revalidate: 3600 },
});
if (!res.ok) throw new Error(`upstream ${res.status}`);
type NomRow = { display_name: string; name?: string; address?: Record<string, string> };
const rows = (await res.json()) as NomRow[];
const results = rows.map((r) => {
const a = r.address || {};
const line1 = clean([a.house_number, a.road || a.pedestrian || a.name || r.name].filter(Boolean).join(" "));
const city = a.city || a.town || a.village || a.municipality || a.suburb || a.county || "";
return {
label: r.display_name,
line1,
line2: a.neighbourhood || a.suburb || a.quarter || "",
city,
state: a.state || a.region || "",
postal: a.postcode || "",
};
}).filter((r) => r.line1);
return NextResponse.json({ ok: true, results });
} catch {
return NextResponse.json({ ok: true, results: [], source: "fallback" });
}
}
return NextResponse.json({ ok: false, error: "Provide a ?pin=, ?q= or ?addr= parameter" }, { status: 400 });
}