SPB Git

spb/job-ka

Public

feat(doc): page documentation /doc (guide + captures) + lien footer + PDF téléchargeable

Simon-Pierre Boucher committed (Aug 24, 2026) parent a8b1668 Browse files

8 changed files +193 −1

added frontend/public/doc/img/etape1.png +0 −0

Binary file not shown.

added frontend/public/doc/img/etape2.png +0 −0

Binary file not shown.

added frontend/public/doc/img/etape3.png +0 −0

Binary file not shown.

added frontend/public/doc/img/etape4.png +0 −0

Binary file not shown.

added frontend/public/doc/index.html +189 −0
@@ -0,0 +1,189 @@
1+<!DOCTYPE html>
2+<!-- Page /doc de Job·Ka — générée depuis le gabarit commun Groupe KA (2026-08-24). -->
3+<html lang="fr">
4+<head>
5+<meta charset="utf-8">
6+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
7+<title>Documentation — Job·Ka</title>
8+<meta name="description" content="Comment fonctionne Job·Ka : guide pas à pas, données, architecture.">
9+<style>
10+ :root{ --ink:#101014; --paper:#faf9f6; --accent:#0c8599; --muted:#6b6b70; --line:#e6e4de; }
11+ *{box-sizing:border-box} html,body{margin:0;padding:0;background:var(--paper);color:var(--ink);
12+ font:16px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;-webkit-text-size-adjust:100%}
13+ .container{max-width:960px;margin:0 auto;padding:0 20px}
14+ header.doc{background:var(--ink);color:var(--paper);padding:20px 0}
15+ header.doc .container{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
16+ header.doc a{color:var(--paper);text-decoration:none}
17+ .wordmark{font-weight:800;font-size:20px;letter-spacing:.02em}
18+ .wordmark .ka{color:var(--accent)}
19+ .btn-pdf{display:inline-block;background:var(--accent);color:#fff;font-weight:700;
20+ padding:10px 18px;border-radius:999px;text-decoration:none;font-size:15px}
21+ .hero{padding:48px 0 8px}
22+ .hero h1{font-size:clamp(28px,5vw,44px);line-height:1.1;margin:0 0 12px}
23+ .hero h1 mark{background:var(--accent);color:#fff;padding:2px 8px}
24+ .hero p.lead{font-size:18px;color:var(--muted);max-width:640px}
25+ .kicker{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
26+ border-left:3px solid var(--accent);padding-left:10px;margin:40px 0 8px;font-weight:700}
27+ section{padding:8px 0 16px}
28+ h2{font-size:26px;margin:6px 0 12px}
29+ .step{display:grid;grid-template-columns:56px 1fr;gap:16px;margin:26px 0;align-items:start}
30+ .step .num{width:44px;height:44px;border-radius:50%;background:var(--ink);color:var(--accent);
31+ display:flex;align-items:center;justify-content:center;font-weight:800;font-size:18px}
32+ .step h3{margin:6px 0 6px;font-size:19px}
33+ .step p{margin:0 0 12px;color:#3a3a40}
34+ .shot{border:1px solid var(--line);border-radius:12px;overflow:hidden;box-shadow:0 8px 30px rgba(16,16,20,.08)}
35+ .shot img{display:block;width:100%;height:auto}
36+ .cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px;margin:16px 0}
37+ .card{border:1px solid var(--line);border-radius:12px;padding:16px;background:#fff}
38+ .card b{display:block;margin-bottom:6px}
39+ table{border-collapse:collapse;width:100%;font-size:15px}
40+ th,td{border:1px solid var(--line);padding:8px 10px;text-align:left;vertical-align:top}
41+ th{background:var(--ink);color:var(--paper)}
42+ footer.doc{margin-top:56px;background:var(--ink);color:var(--paper);padding:28px 0;font-size:14px}
43+ footer.doc a{color:var(--accent);text-decoration:none}
44+ @media print{
45+ header.doc .btn-pdf{display:none}
46+ .shot{box-shadow:none;break-inside:avoid}
47+ .step{break-inside:avoid}
48+ a{color:inherit;text-decoration:none}
49+ }
50+ @media(max-width:640px){ .step{grid-template-columns:40px 1fr} .step .num{width:34px;height:34px;font-size:15px} }
51+</style>
52+</head>
53+<body>
54+<header class="doc">
55+ <div class="container">
56+ <a class="wordmark" href="/">Job<span class="ka">·Ka</span></a>
57+ <nav style="display:flex;gap:14px;align-items:center">
58+ <a href="/">← Retour au site</a>
59+ <a class="btn-pdf" href="./job-ka-documentation.pdf" download>Télécharger le PDF</a>
60+ </nav>
61+ </div>
62+</header>
63+
64+<div class="container">
65+ <div class="hero">
66+ <p class="kicker">Documentation · mise à jour 2026-08-24</p>
67+ <h1>Comment fonctionne <mark>Job·Ka</mark></h1>
68+ <p class="lead">Tous les emplois des employeurs québécois, collectés directement sur leurs pages carrières — un seul endroit, zéro boîte noire.</p>
69+ </div>
70+
71+ <section>
72+ <p class="kicker">Vue d'ensemble</p>
73+ <h2>À quoi sert le site</h2>
74+ <p>Les grands sites d'emploi mélangent annonces payantes, doublons repostés et offres mortes. L'offre originale —
75+ celle de la page carrière de l'employeur — est la seule source fiable. Job·Ka se branche <b>directement sur
76+ les systèmes de recrutement (ATS)</b> des employeurs québécois, complétés par trois portails publics
77+ (Guichet-Emplois, Jobillico, Espresso-Jobs) — l'offre directe gagne toujours. Chaque offre est traçable à sa
78+ source et la candidature se fait chez l'employeur. Pour les chercheurs d'emploi québécois qui veulent
79+ l'inventaire réel, à jour et transparent des postes affichés dans la province.</p>
80+ <div class="cards">
81+ <div class="card"><b>21 000+ offres actives</b>De plus de 1 600 employeurs (≈960 sources), dont ~19 800
82+ offres directes collectées sur les pages carrières.</div>
83+ <div class="card"><b>968 connecteurs employeurs directs</b>Sur 17 plateformes ATS : Workday, Lever,
84+ Greenhouse, SmartRecruiters, BambooHR, Taleo, iCIMS, SuccessFactors…</div>
85+ <div class="card"><b>~6 900 offres avec salaire affiché</b>Salaires normalisés en $/h et $/an, jamais
86+ inventés — un signe monétaire ou une unité explicite est exigé.</div>
87+ <div class="card"><b>Resynchronisation jour et nuit</b>Un watcher horaire recollecte toutes les sources,
88+ détecte les changements et désactive les offres disparues.</div>
89+ </div>
90+ </section>
91+
92+ <section>
93+ <p class="kicker">Guide pas à pas</p>
94+ <h2>Utiliser le site en 4 étapes</h2>
95+
96+ <div class="step">
97+ <div class="num">1</div>
98+ <div>
99+ <h3>Arriver sur l'accueil</h3>
100+ <p>L'accueil présente la barre de recherche et les filtres : ville, région, catégorie (16 catégories : TI,
101+ Santé, Finance, Ingénierie…), mode de travail (dont télétravail), type d'emploi, langue et la case
102+ « Salaire affiché ». Les offres les plus récentes défilent dessous.</p>
103+ <div class="shot"><img src="./img/etape1.png" alt="Étape 1 — accueil de Job·Ka"></div>
104+ </div>
105+ </div>
106+
107+ <div class="step">
108+ <div class="num">2</div>
109+ <div>
110+ <h3>Chercher et filtrer les offres</h3>
111+ <p>Tapez un métier ou un mot-clé (ici « machiniste ») et combinez les filtres : la liste se met à jour avec
112+ le nombre d'offres trouvées. Chaque carte montre l'employeur, la ville, le salaire quand il est affiché,
113+ et le badge « Offre directe » quand l'offre vient de la page carrière de l'employeur.</p>
114+ <div class="shot"><img src="./img/etape2.png" alt="Étape 2 — recherche filtrée d'offres"></div>
115+ </div>
116+ </div>
117+
118+ <div class="step">
119+ <div class="num">3</div>
120+ <div>
121+ <h3>Lire la fiche et postuler chez l'employeur</h3>
122+ <p>La fiche donne la description complète, le salaire normalisé ($/h et l'équivalent $/an), le type d'emploi
123+ et la plateforme d'origine. Le bouton « Postuler chez … » mène directement au site de l'employeur :
124+ Job·Ka n'est pas un intermédiaire, la candidature se fait chez l'employeur.</p>
125+ <div class="shot"><img src="./img/etape3.png" alt="Étape 3 — fiche d'offre avec bouton Postuler chez l'employeur"></div>
126+ </div>
127+ </div>
128+
129+ <div class="step">
130+ <div class="num">4</div>
131+ <div>
132+ <h3>Explorer par la carte (et plus)</h3>
133+ <p>L'onglet « Carte » situe les offres géocodées partout au Québec — zoomez sur votre coin de pays et ouvrez
134+ les offres d'un secteur. À explorer aussi : « Stats » (tableau de bord + rapports PDF), « Employeurs »
135+ (les sources), « Publier » (dépôt direct d'offres par les employeurs) et les favoris « Mon univers Ka ».</p>
136+ <div class="shot"><img src="./img/etape4.png" alt="Étape 4 — carte des offres"></div>
137+ </div>
138+ </div>
139+ </section>
140+
141+ <section>
142+ <p class="kicker">Sous le capot</p>
143+ <h2>D'où viennent les données</h2>
144+ <p>Job·Ka interroge <b>968 connecteurs employeurs directs</b> répartis sur <b>17 plateformes ATS</b> (Workday,
145+ Lever, Greenhouse, SmartRecruiters, BambooHR, Breezy, Recruitee, Ashby, Workable, Taleo, Njoyn, UltiPro,
146+ iCIMS, SuccessFactors, ADP WFN, Digital Recruiters, Workland), des sites carrières maison et
147+ <b>3 portails publics</b> (Guichet-Emplois, Jobillico, Espresso-Jobs). Chaque offre est normalisée vers un
148+ schéma commun, puis passe par un <b>diff engine anti-dérive</b> : comparaison par empreinte de contenu à
149+ chaque synchronisation, une offre absente 2 syncs est désactivée (jamais supprimée), et si une source chute
150+ anormalement, les retraits sont suspendus le temps de vérifier. Les <b>salaires ne sont jamais inventés</b>
151+ (unité explicite exigée, bornes de plausibilité, conversion $/h ↔ $/an), le <b>géocodage</b> passe par
152+ Nominatim avec repli sur les Adresses Québec officielles (MERN), et la <b>déduplication inter-sources</b>
153+ privilégie l'offre directe : en cas de doublon, la page carrière de l'employeur devient la fiche canonique,
154+ les copies portail sont masquées. La collecte est polie : throttling par source et User-Agent identifiable
155+ <code>JobKaBot/1.0</code>. Le tout est resynchronisé jour et nuit, toutes les heures.</p>
156+ </section>
157+
158+ <section>
159+ <p class="kicker">Questions fréquentes</p>
160+ <h2>FAQ</h2>
161+ <div class="cards">
162+ <div class="card"><b>Est-ce que je postule sur Job·Ka ?</b>
163+ Non — le bouton « Postuler » mène directement au site de l'employeur (sa page carrière ou son ATS). Job·Ka
164+ ne collecte aucune donnée personnelle de candidat.</div>
165+ <div class="card"><b>Que signifie le badge « Offre directe » ?</b>
166+ Que l'offre a été collectée directement sur la page carrière de l'employeur — la source la plus fiable. En
167+ cas de doublon avec un portail, c'est l'offre directe qui est affichée.</div>
168+ <div class="card"><b>Les salaires affichés sont-ils fiables ?</b>
169+ Oui : un salaire n'est retenu que si la source affiche un montant avec unité explicite. Il est ensuite
170+ normalisé en $/h et $/an pour comparer les offres — jamais estimé ni inventé.</div>
171+ <div class="card"><b>À quelle fréquence les offres sont-elles mises à jour ?</b>
172+ Toutes les heures, jour et nuit. Une offre retirée par l'employeur disparaît de la liste après
173+ 2 synchronisations consécutives d'absence.</div>
174+ <div class="card"><b>Je suis employeur : comment publier mes offres ?</b>
175+ Via la page « Publier une offre » (/employeurs) — dépôt direct avec modération manuelle, gratuit. Si vous
176+ utilisez un ATS répandu, votre page carrière peut aussi être branchée comme connecteur.</div>
177+ </div>
178+ </section>
179+</div>
180+
181+<footer class="doc">
182+ <div class="container">
183+ <p><b>Job·Ka</b> — un service <a href="https://www.groupe-ka.com">Groupe KA</a>.
184+ Écosystème : les 13 plateformes sont liées au pied de chaque site.</p>
185+ <p>© 2026 Groupe KA — Simon-Pierre Boucher · <a href="mailto:contact@spboucher.ai">contact@spboucher.ai</a></p>
186+ </div>
187+</footer>
188+</body>
189+</html>
added frontend/public/doc/job-ka-documentation.pdf +0 −0

Binary file not shown.

modified frontend/src/App.tsx +1 −1
@@ -198,7 +198,7 @@ export default function App() {
198 <NavLink to="/contact">Contact</NavLink> 198 <NavLink to="/contact">Contact</NavLink>
199 </div> 199 </div>
200 </div> 200 </div>
201− <KaFooter siteId="job-ka" /> 201+ <KaFooter siteId="job-ka" localLegal={[{ label: "Documentation", href: "/doc/" }]} />
202 </FavProvider> 202 </FavProvider>
203 ); 203 );
204 } 204 }
modified jobka/web.py +3 −0
@@ -595,6 +595,9 @@ if FRONTEND_DIST.exists():
595 target = FRONTEND_DIST / full_path 595 target = FRONTEND_DIST / full_path
596 if full_path and target.is_file(): 596 if full_path and target.is_file():
597 return FileResponse(target) 597 return FileResponse(target)
598+ if full_path and target.is_dir() and (target / "index.html").is_file():
599+ # sous-répertoire statique avec index (ex. /doc/) — servi tel quel
600+ return FileResponse(target / "index.html")
598 known = (full_path == "" or full_path in _CLIENT_ROUTES 601 known = (full_path == "" or full_path in _CLIENT_ROUTES
599 or any(full_path.startswith(p) for p in _CLIENT_PREFIXES)) 602 or any(full_path.startswith(p) for p in _CLIENT_PREFIXES))
600 return FileResponse(FRONTEND_DIST / "index.html", 603 return FileResponse(FRONTEND_DIST / "index.html",
601 604
Menu

Theme