← back to Homesonspec
apps/web/src/lib/parcel.ts
96 lines
// Parcel / public-records drill helpers.
//
// SOURCING-COMPLIANCE: we source parcel facts (lot, county) that the ingest
// pipeline already legitimately captured, and we link OUT to the county's own
// public-records / assessor parcel-search surface. We NEVER fabricate an APN —
// the data model has no APN column, so we surface what we honestly have (lot
// number + county) and hand the user a public-records search scoped to the
// county so they can look the parcel up on the authoritative source themselves.
//
// A small curated map of county → assessor parcel-search URL covers the highest
// -volume counties in our data with a known, stable public search page. For any
// county not in the map we fall back to a generic public-records search that is
// still scoped to the county + state, so there is never a dead-end data point.
interface CountyAssessor {
/** Display name of the authority the link points to. */
authority: string;
/** Landing/search URL on the county's own public parcel-search surface. */
url: (city: string, state: string) => string;
}
// Normalize "Solano County" / "solano" → "solano" for keying.
export function normCounty(county: string | null | undefined): string {
if (!county) return "";
return county
.toLowerCase()
.replace(/\s+county$/i, "")
.trim();
}
// state-scoped county key so identical county names in different states don't collide.
function key(county: string | null | undefined, state: string): string {
return `${state.toUpperCase()}:${normCounty(county)}`;
}
// Curated public parcel-search surfaces. URLs point at the county's OWN public
// search landing page (public-records backbone) — the user runs the lookup.
const ASSESSOR_MAP: Record<string, CountyAssessor> = {
// California
"CA:solano": { authority: "Solano County Assessor", url: () => "https://services.solanocounty.com/apps/PropertyInformation/" },
"CA:san diego": { authority: "San Diego County Assessor", url: () => "https://arcc.sdcounty.ca.gov/Pages/PropertySearch.aspx" },
"CA:kern": { authority: "Kern County Assessor", url: () => "https://recorderonline.co.kern.ca.us/" },
"CA:riverside": { authority: "Riverside County Assessor", url: () => "https://ca-riverside-acr.publicaccessnow.com/" },
"CA:sacramento": { authority: "Sacramento County Assessor", url: () => "https://assessorparcelviewer.saccounty.gov/" },
// Texas
"TX:harris": { authority: "Harris County Appraisal District", url: () => "https://hcad.org/property-search/" },
"TX:fort bend": { authority: "Fort Bend Central Appraisal District", url: () => "https://www.fbcad.org/property-search/" },
"TX:comal": { authority: "Comal Appraisal District", url: () => "https://esearch.comalad.org/" },
"TX:kendall": { authority: "Kendall Appraisal District", url: () => "https://esearch.kendallad.org/" },
"TX:travis": { authority: "Travis Central Appraisal District", url: () => "https://traviscad.org/property-search/" },
// North Carolina
"NC:wake": { authority: "Wake County Real Estate", url: () => "https://services.wake.gov/realestate/" },
"NC:forsyth": { authority: "Forsyth County Tax Assessor", url: () => "https://www.forsyth.cc/tax/property_records.aspx" },
"NC:cherokee": { authority: "Cherokee County Tax Office", url: () => "https://cherokeecounty-nc.gov/213/Tax-Assessor" },
// Delaware
"DE:sussex": { authority: "Sussex County Assessment", url: () => "https://sussexcountyde.gov/property-information" },
// Virginia
"VA:albemarle": { authority: "Albemarle County Real Estate", url: () => "https://www.albemarle.org/government/finance-budget/real-estate-tax-assessment" },
// Georgia
"GA:forsyth": { authority: "Forsyth County GA Assessor", url: () => "https://qpublic.schneidercorp.com/Application.aspx?App=ForsythCountyGA" },
};
export interface ParcelLink {
/** Human label for the authority the link points at. */
authority: string;
/** URL to the county's public parcel/records search. */
url: string;
/** True when we matched a curated county assessor; false = generic fallback. */
curated: boolean;
}
// Resolve a public-records drill target for a home's county+state. Always returns
// something — a curated assessor when known, else a generic county-scoped
// public-records web search — so a county/parcel data point is never a dead end.
export function parcelLink(
county: string | null | undefined,
state: string | null | undefined,
city: string | null | undefined,
): ParcelLink | null {
if (!state) return null;
const st = state.toUpperCase();
const hit = county ? ASSESSOR_MAP[key(county, st)] : undefined;
if (hit) {
return { authority: hit.authority, url: hit.url(city ?? "", st), curated: true };
}
// Generic, honest fallback: a web search scoped to the county assessor. No
// fabricated parcel id — just routes the user to the authoritative source.
const label = county ? `${normCounty(county).replace(/\b\w/g, (c) => c.toUpperCase())} County` : st;
const q = encodeURIComponent(`${label} ${st} county assessor parcel property records search`);
return {
authority: county ? `${label} public records` : `${st} public records`,
url: `https://www.google.com/search?q=${q}`,
curated: false,
};
}