/** * ============================================================================= * Job·Ka — Groupe KA * Auteur : Simon-Pierre Boucher * Contact : contact@spboucher.ai * Fichier : frontend/src/fiche/SalaryCard.tsx * Rôle : « Salaire et marché » — KPI (salaire affiché, médiane de la * catégorie, fourchette P25–P75), jauge de position du point médian * de l'offre dans la fenêtre interquartile, historique du salaire * affiché, méthodologie. Jamais d'estimation : sans salaire publié, * la carte le dit et montre seulement le marché. * Créé : 2026-09-07 * ============================================================================= */ import { Job, compactMoney, formatSalary, yearlyEquiv } from "../api"; import { Ico } from "../components/Icons"; import { SalaryCapsule } from "./JobHero"; import { ComparaisonSalaire } from "./synthese"; import { Accordion, SectionCard, StatTile, NBSP } from "./ui"; function Gauge({ p25, p75, mid }: { p25: number; p75: number; mid: number }) { const lo = Math.min(p25 * 0.75, mid * 0.95), hi = Math.max(p75 * 1.25, mid * 1.05); const W = 320, H = 54; const x = (v: number) => 8 + ((Math.min(Math.max(v, lo), hi) - lo) / (hi - lo)) * (W - 16); const anchor = (px: number) => (px < 60 ? "start" : px > W - 60 ? "end" : "middle"); return (
P25 {compactMoney(p25)} P75 {compactMoney(p75)} cette offre ≈ {compactMoney(mid)}/an
); } export default function SalaryCard({ job, cmp }: { job: Job; cmp: ComparaisonSalaire | null }) { const ctx = job.salary_context; const salaire = formatSalary(job); const yearly = yearlyEquiv(job); const hist = (job.salary_history ?? []).filter((h) => h.s_min != null); if (!salaire && !ctx) return null; return ( } aside={cmp && }>
{ctx && } {ctx && }
{ctx && ctx.job_mid != null && } {ctx && ctx.job_mid == null && salaire == null && (

Sans salaire publié, Job·Ka n'estime rien : seule la référence du marché de la catégorie est affichée.

)} {hist.length > 1 && ( <>

Évolution du salaire affiché

    {hist.map((h, i) => (
  • {new Date(h.ts * 1000).toLocaleDateString("fr-CA", { day: "numeric", month: "short", year: "numeric" })} {formatSalary({ salary_min: h.s_min, salary_max: h.s_max, salary_unit: h.unit })}
  • ))}
)}

Le salaire est celui publié par l'employeur (jamais estimé). La référence de marché est calculée par Job·Ka sur les offres actives de la même catégorie qui affichent un salaire, converties en équivalent annuel : médiane et quartiles P25–P75 ; l'écart compare le point médian de la fourchette de l'offre à cette médiane. Les avantages, primes et horaires ne sont pas pris en compte.{NBSP}

); }