Job-Scorer / components /BatchStyleScoreBreakdown.tsx
zimejin's picture
Surface visa sponsorship signal on scored roles with badge and flags.
3c36912
Raw
History Blame Contribute Delete
5.23 kB
"use client";
import { ScoreBar } from "@/components/ScoreBar";
import { AccessibilityBadge } from "@/components/AccessibilityBadge";
import { VisaSponsorshipNote } from "@/components/VisaSponsorshipNote";
import { isScoreResult, type ScoreResult } from "@/lib/types";
function verdictStyle(verdict: ScoreResult["verdict"]): { color: string; bg: string } {
if (verdict === "Strong Match") return { color: "#1D9E75", bg: "#E1F5EE" };
if (verdict === "Possible") return { color: "#BA7517", bg: "#FAEEDA" };
return { color: "#E24B4A", bg: "#FCEBEB" };
}
function safeBarScore(n: unknown): number {
return typeof n === "number" && Number.isFinite(n) ? n : 0;
}
/** Two-stage session + profile bars (above standard dimensions). */
export function TwoStageSessionProfileScoreRows({ result }: { result: ScoreResult }) {
if (!result.session_fit || !result.profile_fit) return null;
return (
<>
<div>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 4,
gap: 8,
}}
>
<span style={{ fontSize: 12, color: "var(--muted, #666)" }}>Search fit</span>
</div>
<ScoreBar
score={safeBarScore(result.session_fit.score)}
reason={typeof result.session_fit.reason === "string" ? result.session_fit.reason : undefined}
/>
</div>
<div>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 4,
gap: 8,
}}
>
<span style={{ fontSize: 12, color: "var(--muted, #666)" }}>Profile fit</span>
</div>
<ScoreBar
score={safeBarScore(result.profile_fit.score)}
reason={typeof result.profile_fit.reason === "string" ? result.profile_fit.reason : undefined}
/>
</div>
</>
);
}
export function BatchStyleScoreBreakdown({ result }: { result: ScoreResult }) {
if (!isScoreResult(result)) {
return (
<p className="muted" style={{ marginTop: 8, fontSize: 12 }}>
Stored score is incomplete.
</p>
);
}
const verdict =
result.verdict === "Strong Match" || result.verdict === "Possible" || result.verdict === "Skip"
? result.verdict
: "Skip";
const { color, bg } = verdictStyle(verdict);
const dims = [
{ label: "Role & skill fit", key: "role_fit" as const },
{ label: "Global remote", key: "global_remote" as const },
{ label: "Async culture", key: "culture_async" as const },
{ label: "Contractor friendly", key: "contractor_friendly" as const },
];
return (
<div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 8 }}>
<div
style={{
display: "flex",
alignItems: "center",
gap: 10,
padding: "10px 12px",
background: bg,
borderRadius: 10,
border: `0.5px solid ${color}55`,
}}
>
<span style={{ fontSize: 14, fontWeight: 600, color }}>{verdict}</span>
<span style={{ fontSize: 12, color: "#555", flex: 1, lineHeight: 1.35 }}>
{typeof result.verdict_reason === "string" ? result.verdict_reason : ""}
</span>
</div>
<AccessibilityBadge assessment={result.accessibility} />
<VisaSponsorshipNote assessment={result.accessibility} />
<div
style={{
background: "var(--surface, #fff)",
border: "0.5px solid var(--hairline, #e0e0e0)",
borderRadius: 10,
padding: "10px 12px",
display: "flex",
flexDirection: "column",
gap: 12,
}}
>
<TwoStageSessionProfileScoreRows result={result} />
{dims.map((d) => {
const dim = result[d.key];
return (
<div key={d.key}>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 4,
gap: 8,
}}
>
<span style={{ fontSize: 12, color: "var(--muted, #666)" }}>{d.label}</span>
</div>
<ScoreBar
score={safeBarScore(dim?.score)}
reason={typeof dim?.reason === "string" ? dim.reason : undefined}
/>
</div>
);
})}
{result.semantic_score &&
typeof result.semantic_score === "object" &&
result.semantic_score !== null ? (
<div>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginBottom: 4,
}}
>
<span style={{ fontSize: 12, color: "var(--muted, #666)" }}>Semantic match</span>
</div>
<ScoreBar
score={safeBarScore(result.semantic_score.score)}
reason={
typeof result.semantic_score.reason === "string"
? result.semantic_score.reason
: undefined
}
/>
</div>
) : null}
</div>
</div>
);
}