fix: enforce current physique range in funnel

This commit is contained in:
jjbrunton committed 2026-08-10 18:35:22 +01:00
1 parent d8d635e1ee
commit 1515b3e291
12 files changed
+92 -13

No files matched your search

@@ -38,7 +38,29 @@ function formatWaitTime(timestamp: number, now: number): string {
return `in ${Math.ceil(remaining / 86_400_000)}d`;
}
function PreparedCandidateDetails({ candidate }: { candidate: OutreachCandidate }) {
type PhysiqueTargeting = { enabled: boolean; minScore: number; maxScore: number };
function targetingLabel(current: PhysiqueTargeting, prepared: PhysiqueTargeting | null): string {
if (current.enabled) {
const currentLabel = `Current target ${current.minScore}–${current.maxScore}`;
if (!prepared?.enabled) return `${currentLabel} · Prepared with filter off`;
if (prepared.minScore !== current.minScore || prepared.maxScore !== current.maxScore) {
return `${currentLabel} · Prepared for ${prepared.minScore}–${prepared.maxScore}`;
}
return currentLabel;
}
return prepared?.enabled
? `Current filter off · Prepared for ${prepared.minScore}–${prepared.maxScore}`
: 'Current filter off';
}
function PreparedCandidateDetails({
candidate,
currentTargeting,
}: {
candidate: OutreachCandidate;
currentTargeting: PhysiqueTargeting;
}) {
const preparation = candidate.preparation;
if (!preparation) return null;
const targeting = preparation.physiqueTargeting;
@@ -61,7 +83,7 @@ function PreparedCandidateDetails({ candidate }: { candidate: OutreachCandidate
<div className="candidate-preparation-copy">
<div className="candidate-score-grid" aria-label="Preparation scoring">
<div><span>Attractiveness</span><strong>{preparation.attractivenessScore === null ? 'Not scored' : `${preparation.attractivenessScore}/100`}</strong><small>Used in the opener prompt</small></div>
<div><span>Physique</span><strong>{preparation.physiqueScore === null ? 'Not scorable' : `${preparation.physiqueScore}/10`}</strong><small>{targeting?.enabled ? `Target ${targeting.minScore}–${targeting.maxScore}` : 'Range filter off'}</small></div>
<div><span>Physique</span><strong>{preparation.physiqueScore === null ? 'Not scorable' : `${preparation.physiqueScore}/10`}</strong><small>{targetingLabel(currentTargeting, targeting)}</small></div>
<div><span>Detected gender</span><strong>{preparation.detectedGender}</strong><small>Image analysis</small></div>
</div>
<div className="candidate-preparation-metadata">
@@ -84,7 +106,15 @@ function PreparedCandidateDetails({ candidate }: { candidate: OutreachCandidate
);
}
function CandidateCard({ candidate, now }: { candidate: OutreachCandidate; now: number }) {
function CandidateCard({
candidate,
currentTargeting,
now,
}: {
candidate: OutreachCandidate;
currentTargeting: PhysiqueTargeting;
now: number;
}) {
const waitingUntil = candidate.availableAt > now;
const isKnownStatus = KNOWN_STATUSES.has(candidate.status);
return (
@@ -114,7 +144,7 @@ function CandidateCard({ candidate, now }: { candidate: OutreachCandidate; now:
</div>
{candidate.note ? <p className="candidate-note"><strong>Latest note</strong>{candidate.note}</p> : null}
{candidate.owner ? <AccountOwnerBadge owner={candidate.owner} /> : null}
<PreparedCandidateDetails candidate={candidate} />
<PreparedCandidateDetails candidate={candidate} currentTargeting={currentTargeting} />
</article>
</li>
);
@@ -200,7 +230,14 @@ export function OutreachPage() {
<div className="empty-state panel"><strong>The active queue is clear.</strong><span>New potential posts will appear here as discovery finds them.</span></div>
) : (
<ol aria-label="Active outreach candidates" className="outreach-candidates">
{data.candidates.map((candidate) => <CandidateCard candidate={candidate} key={candidate.postId} now={now} />)}
{data.candidates.map((candidate) => (
<CandidateCard
candidate={candidate}
currentTargeting={data.physiqueTargeting}
key={candidate.postId}
now={now}
/>
))}
</ol>
)}
</section>