feat(review+990pf): Part XV application info — parse the preselected-only kill signal, surface how-to-apply at review
Parser captures 990-PF Part XV line 2 (OnlyContriToPreselectedInd) even when the application group is absent. Synthesis skips preselected-only funders and closes their previously-synthesized grant rows (serverCloseGrantsForFunders). Match detail page gains a 'How to apply (funder's own 990-PF, Part XV)' panel — application form, deadlines, restrictions, red do-not-pitch banner — fed by serverGetMatchDetail. Backfill re-parse across all 747 NH foundations: 192 are preselected- only; 62 of 123 synthesized foundation leads were unpitchable by their funder's own filing and are now closed, with 24,436 stale pending matches cleaned. Post re-match: 714 easy wins across 327 orgs, every foundation lead now backed by a funder that actually accepts applications. 156 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,15 @@ const SUBSCORE_MAX: Record<string, number> = {
|
||||
export default function MatchDetail({ loaderData }: Route.ComponentProps) {
|
||||
const { detail } = loaderData ?? {};
|
||||
if (detail == null) return null;
|
||||
const { match, org, grant, funderGivingHistory } = detail;
|
||||
const { match, org, grant, funderGivingHistory, funderApplicationInfo } =
|
||||
detail;
|
||||
const applyInfo = (funderApplicationInfo ?? null) as {
|
||||
preselectedOnly?: boolean;
|
||||
recipientName?: string;
|
||||
formAndInfoAndMaterials?: string;
|
||||
submissionDeadlines?: string;
|
||||
restrictionsOnAwards?: string;
|
||||
} | null;
|
||||
const subscores = (match.subscores ?? {}) as Record<string, number>;
|
||||
const rationale = (match.rationale ?? {}) as Record<string, unknown>;
|
||||
const nhHistory = funderGivingHistory;
|
||||
@@ -233,6 +241,47 @@ export default function MatchDetail({ loaderData }: Route.ComponentProps) {
|
||||
)}
|
||||
</section>
|
||||
|
||||
{applyInfo != null && (
|
||||
<section className="rounded border p-4">
|
||||
<h2 className="mb-2 text-lg font-semibold">
|
||||
How to apply (funder's own 990-PF, Part XV)
|
||||
</h2>
|
||||
{applyInfo.preselectedOnly === true && (
|
||||
<p className="mb-2 rounded bg-red-100 p-2 text-sm font-medium text-red-800">
|
||||
This funder states it only contributes to preselected
|
||||
organizations and does not accept unsolicited requests — do
|
||||
not pitch.
|
||||
</p>
|
||||
)}
|
||||
<dl className="space-y-1 text-sm">
|
||||
{applyInfo.formAndInfoAndMaterials != null && (
|
||||
<div>
|
||||
<dt className="inline font-medium">Application form: </dt>
|
||||
<dd className="inline">{applyInfo.formAndInfoAndMaterials}</dd>
|
||||
</div>
|
||||
)}
|
||||
{applyInfo.submissionDeadlines != null && (
|
||||
<div>
|
||||
<dt className="inline font-medium">Deadlines: </dt>
|
||||
<dd className="inline">{applyInfo.submissionDeadlines}</dd>
|
||||
</div>
|
||||
)}
|
||||
{applyInfo.restrictionsOnAwards != null && (
|
||||
<div>
|
||||
<dt className="inline font-medium">Restrictions: </dt>
|
||||
<dd className="inline">{applyInfo.restrictionsOnAwards}</dd>
|
||||
</div>
|
||||
)}
|
||||
{applyInfo.recipientName != null && (
|
||||
<div>
|
||||
<dt className="inline font-medium">Applications to: </dt>
|
||||
<dd className="inline">{applyInfo.recipientName}</dd>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section className="rounded border p-4">
|
||||
<h2 className="mb-2 text-lg font-semibold">Grant synopsis</h2>
|
||||
<p className="whitespace-pre-wrap text-sm">{grant.synopsis ?? '—'}</p>
|
||||
|
||||
Reference in New Issue
Block a user