Add corporate record suggestions on Officer step, search all states

- OfficerStep searches entity_cache for matching corporations when loaded
- Shows clickable suggestions with RA name and address to auto-fill Officer 1
- Pre-fills contact_name/email/phone from entity data (helps data-only filers)
- Corp search endpoint: state param now optional (searches all states)
- Corp search returns registered_agent and principal_address fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-04-28 23:02:16 -05:00
parent ab7a2d7dc0
commit 159a576157
2 changed files with 103 additions and 18 deletions

View file

@ -246,24 +246,35 @@ router.get("/api/v1/corp/search", async (req: Request, res: Response) => {
res.status(400).json({ error: "q parameter required (min 2 chars)" });
return;
}
if (!state || state.length !== 2) {
res.status(400).json({ error: "state parameter required (2-letter code)" });
return;
}
const limit = Math.min(Number(req.query.limit) || 10, 20);
try {
const { rows } = await pool.query(
`SELECT entity_name, entity_number, entity_type, status, formation_date,
similarity(entity_name, $2) AS sim
FROM entity_cache
WHERE state = $1 AND entity_name % $2
ORDER BY sim DESC
LIMIT 10`,
[state, q],
);
// State is optional — if omitted, search all states
const { rows } = state
? await pool.query(
`SELECT entity_name, entity_number, entity_type, status, formation_date,
registered_agent, principal_address, state,
similarity(entity_name, $2) AS sim
FROM entity_cache
WHERE state = $1 AND entity_name % $2
ORDER BY sim DESC
LIMIT $3`,
[state, q, limit],
)
: await pool.query(
`SELECT entity_name, entity_number, entity_type, status, formation_date,
registered_agent, principal_address, state,
similarity(entity_name, $2) AS sim
FROM entity_cache
WHERE entity_name % $1
ORDER BY sim DESC
LIMIT $2`,
[q, limit],
);
res.json({
state,
state: state || "all",
query: q,
count: rows.length,
results: rows.map((r: any) => ({
@ -274,11 +285,14 @@ router.get("/api/v1/corp/search", async (req: Request, res: Response) => {
formation_date: r.formation_date
? new Date(r.formation_date).toISOString().slice(0, 10)
: null,
registered_agent: r.registered_agent || null,
principal_address: r.principal_address || null,
state: r.state || null,
})),
});
} catch (err: any) {
if (err?.code === "42P01") {
res.json({ state, query: q, count: 0, results: [], note: "Entity database not yet populated for this state." });
res.json({ state: state || "all", query: q, count: 0, results: [], note: "Entity database not yet populated for this state." });
} else {
console.error("[corp/search] Error:", err);
res.status(500).json({ error: "Search failed" });