Import initial de Vivier depuis l'hebergement OVH
This commit is contained in:
commit
839e5e57c5
126 files changed
+22447
No files matched your search
@@ -0,0 +1 @@
|
||||
Require all denied
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// Champs du formulaire d'un besoin client, communs à la création et à la modification.
|
||||
// Attend $saisie : valeurs à afficher.
|
||||
?>
|
||||
<div class="champs">
|
||||
<label><?= e(t('Client')) ?> <input type="text" name="client" maxlength="200" required value="<?= e($saisie['client'] ?? '') ?>"></label>
|
||||
<label><?= e(t('Intitulé du poste')) ?> <input type="text" name="intitule" maxlength="200" required value="<?= e($saisie['intitule'] ?? '') ?>" placeholder="<?= e(t('Développeur Java senior')) ?>"></label>
|
||||
<label class="large"><?= e(t('Technologies demandées, séparées par des virgules')) ?>
|
||||
<input type="text" name="technologies" maxlength="1000" value="<?= e($saisie['technologies'] ?? '') ?>" placeholder="Java, Spring Boot, PostgreSQL">
|
||||
</label>
|
||||
<label><?= e(t('Séniorité minimale')) ?>
|
||||
<select name="seniorite_min">
|
||||
<option value=""><?= e(t('Indifférente')) ?></option>
|
||||
<?php foreach (SENIORITES as $code => $libelle): ?><option value="<?= e($code) ?>"<?= ($saisie['seniorite_min'] ?? '') === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Langues demandées, séparées par des virgules')) ?>
|
||||
<input type="text" name="langues" maxlength="300" value="<?= e($saisie['langues'] ?? '') ?>" placeholder="<?= e(t('Français, Anglais')) ?>">
|
||||
</label>
|
||||
<label><?= e(t('Lieu')) ?> <input type="text" name="lieu" maxlength="150" value="<?= e($saisie['lieu'] ?? '') ?>"></label>
|
||||
<label><?= e(t('Télétravail')) ?>
|
||||
<select name="teletravail">
|
||||
<option value=""><?= e(t('Non précisé')) ?></option>
|
||||
<?php foreach (TELETRAVAIL as $code => $libelle): ?><option value="<?= e($code) ?>"<?= ($saisie['teletravail'] ?? '') === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Début souhaité')) ?> <input type="date" name="date_debut" value="<?= e($saisie['date_debut'] ?? '') ?>"></label>
|
||||
<label><?= e(t('Budget journalier, en euros')) ?>
|
||||
<input type="text" inputmode="numeric" pattern="[0-9]*" name="tjm_max" maxlength="5" value="<?= e($saisie['tjm_max'] ?? '') ?>">
|
||||
</label>
|
||||
<label class="large"><?= e(t('Description')) ?>
|
||||
<textarea name="description" rows="4" maxlength="10000"><?= e($saisie['description'] ?? '') ?></textarea>
|
||||
</label>
|
||||
</div>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
$u = utilisateur();
|
||||
$admin = ($u['role'] ?? '') === 'admin';
|
||||
// Sans compte connecté, seul le guide Sécurité est lisible : il s'ouvre par sa propre adresse
|
||||
$lien = static fn (string $g, string $l): string => $u ? url('aide', ['guide' => $g, 'langue' => $l]) : url('securite', ['langue' => $l]);
|
||||
$guides = array_filter(DOCUMENTS_AIDE, static fn ($code) => $code !== 'administration' || $admin, ARRAY_FILTER_USE_KEY);
|
||||
?>
|
||||
<?php if (!$u): ?>
|
||||
<p class="fil"><a href="<?= e(url('connexion')) ?>"><?= e(t('Retour à la connexion')) ?></a></p>
|
||||
<?php endif; ?>
|
||||
<div class="barre-titre aide-tete">
|
||||
<h1><?= e(t(DOCUMENTS_AIDE[$guide])) ?></h1>
|
||||
<div class="aide-outils">
|
||||
<span class="versions" role="group" aria-label="<?= e(t('Langue du guide')) ?>">
|
||||
<?php foreach (LANGUES_INTERFACE as $code => $nom): ?>
|
||||
<a href="<?= e($lien($guide, $code)) ?>" lang="<?= e($code) ?>" title="<?= e($nom) ?>"<?= $code === $aide['langue'] ? ' aria-current="true"' : '' ?>><?= e(strtoupper($code)) ?></a>
|
||||
<?php endforeach; ?>
|
||||
</span>
|
||||
<button type="button" class="secondaire" data-imprimer><?= e(t('Imprimer ou enregistrer en PDF')) ?></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aide-page">
|
||||
<nav class="aide-sommaire" aria-label="<?= e(t('Sommaire')) ?>">
|
||||
<?php if ($u): ?>
|
||||
<p class="aide-guides">
|
||||
<?php foreach ($guides as $code => $libelle): ?>
|
||||
<a href="<?= e($lien($code, $aide['langue'])) ?>"<?= $code === $guide ? ' aria-current="page"' : '' ?>><?= e(t($libelle)) ?></a>
|
||||
<?php endforeach; ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<ol>
|
||||
<?php foreach ($aide['chapitres'] as $ch): ?>
|
||||
<li><a href="#<?= e($ch['id']) ?>"><?= e($ch['titre']) ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
</nav>
|
||||
<article class="carte rapport aide-texte" lang="<?= e($aide['langue']) ?>">
|
||||
<?= $aide['html'] ?>
|
||||
</article>
|
||||
</div>
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
$ouvert = $b['statut'] === 'ouvert';
|
||||
$actives = array_filter($b['propositions'], static fn ($p) => $p['etat'] !== 'ecarte');
|
||||
?>
|
||||
<p class="fil"><a href="<?= e(url('besoins')) ?>"><?= e(t('Besoins clients')) ?></a> / <?= e($b['client']) ?></p>
|
||||
|
||||
<div class="entete-profil">
|
||||
<div>
|
||||
<h1><?= e($b['intitule']) ?></h1>
|
||||
<p class="sous-titre">
|
||||
<strong><?= e($b['client']) ?></strong>
|
||||
<span class="badge badge-besoin-<?= e($b['statut']) ?>"><?= e(t(ETATS_BESOIN[$b['statut']])) ?></span>
|
||||
<span><?= e(t('créé par %s le %s', $b['nom_affiche'], date_fr($b['cree_le']))) ?></span>
|
||||
</p>
|
||||
</div>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('besoin', ['id' => $b['id']])) ?>" class="statut-form">
|
||||
<?= champ_csrf() ?>
|
||||
<label><?= e(t('Statut du besoin')) ?>
|
||||
<select name="statut">
|
||||
<?php foreach (ETATS_BESOIN as $code => $libelle): ?><option value="<?= e($code) ?>"<?= $b['statut'] === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit" name="action" value="statut" class="secondaire"><?= e(t('Mettre à jour')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($erreur !== null): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
|
||||
<div class="deux-colonnes">
|
||||
<div>
|
||||
<section class="carte" id="propositions">
|
||||
<h2><?= e(t('Profils proposés')) ?> <small><?= count($actives) ?></small></h2>
|
||||
<?php if (!$b['propositions']): ?>
|
||||
<p class="vide"><?= e(t('Aucun profil proposé. Choisissez-en dans la liste ci-dessous, ou depuis la fiche d\'un profil.')) ?></p>
|
||||
<?php else: ?>
|
||||
<ul class="simple agenda">
|
||||
<?php foreach ($b['propositions'] as $p): ?>
|
||||
<li class="<?= $p['etat'] === 'ecarte' ? 'faite' : '' ?>">
|
||||
<?= avatar($p) ?>
|
||||
<div>
|
||||
<a href="<?= e(url('profil', ['id' => $p['id']])) ?>"><?= e($p['prenom'] . ' ' . $p['nom']) ?></a>
|
||||
<span class="badge badge-proposition-<?= e($p['etat']) ?>"><?= e(t(ETATS_PROPOSITION[$p['etat']])) ?></span>
|
||||
<p><?= e(implode(' · ', array_filter([
|
||||
$p['titre'], t(SENIORITES[$p['seniorite']]), annees($p['annees_experience']), t(STATUTS[$p['statut']]),
|
||||
$p['archive_le'] ? t('Archivé') : null, $p['tjm'] ? t('TJM %s', euros($p['tjm'])) : null,
|
||||
]))) ?></p>
|
||||
<?php if ($p['note']): ?><p><?= e($p['note']) ?></p><?php endif; ?>
|
||||
<small><?= e(t('ajouté par %s le %s', $p['nom_affiche'], date_fr($p['ajoute_le']))) ?></small>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<details>
|
||||
<summary><?= e(t('Mettre à jour')) ?></summary>
|
||||
<form method="post" action="<?= e(url('besoin', ['id' => $b['id']])) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="candidat_id" value="<?= (int) $p['id'] ?>">
|
||||
<label><?= e(t('État')) ?>
|
||||
<select name="etat">
|
||||
<?php foreach (ETATS_PROPOSITION as $code => $libelle): ?><option value="<?= e($code) ?>"<?= $p['etat'] === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Note')) ?> <input type="text" name="note" maxlength="500" value="<?= e($p['note'] ?? '') ?>"></label>
|
||||
<div class="actions">
|
||||
<button type="submit" name="action" value="retirer" class="lien"><?= e(t('Retirer de ce besoin')) ?></button>
|
||||
<button type="submit" name="action" value="etat" class="secondaire"><?= e(t('Enregistrer')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if ($ouvert): ?>
|
||||
<section class="carte" id="rapprochement">
|
||||
<h2><?= e(t('Profils du vivier qui répondent au besoin')) ?></h2>
|
||||
<?php if (!$suggestions): ?>
|
||||
<p class="vide"><?= e(t('Aucun profil ne porte les technologies ni les mots de l\'intitulé. Élargissez la demande, ou importez de nouveaux CV.')) ?></p>
|
||||
<?php else: ?>
|
||||
<ul class="simple agenda rapprochement">
|
||||
<?php foreach ($suggestions as $r): ?>
|
||||
<li>
|
||||
<?= avatar($r) ?>
|
||||
<div>
|
||||
<a href="<?= e(url('profil', ['id' => $r['id']])) ?>"><?= e($r['prenom'] . ' ' . $r['nom']) ?></a>
|
||||
<span class="note-rapprochement" title="<?= e(t('Note de rapprochement sur 100')) ?>"><?= (int) $r['score'] ?><small>/100</small></span>
|
||||
<p><?= e(implode(' · ', array_filter([
|
||||
$r['titre'], t(SENIORITES[$r['seniorite']]), annees($r['annees_experience']), t(STATUTS[$r['statut']]),
|
||||
libelle_disponibilite($r) ?: null,
|
||||
$r['tjm'] ? t('TJM %s', euros($r['tjm'])) : null,
|
||||
]))) ?></p>
|
||||
<?php if ($r['trouvees'] || $r['manquantes']): ?>
|
||||
<p class="technos">
|
||||
<?php foreach ($r['trouvees'] as $t): ?><span class="trouve"><?= e($t['nom']) ?><?= $t['annees'] !== null ? ' ' . e(annees($t['annees'])) : '' ?></span><?php endforeach; ?>
|
||||
<?php foreach ($r['manquantes'] as $t): ?><span class="absent"><?= e(t('%s absent', $t)) ?></span><?php endforeach; ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($r['alertes'] as $a): ?><small class="retard"><?= e($a) ?></small><?php endforeach; ?>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('besoin', ['id' => $b['id']])) ?>" class="actions-ligne">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="candidat_id" value="<?= (int) $r['id'] ?>">
|
||||
<button type="submit" name="action" value="proposer" class="secondaire"><?= e(t('Proposer')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<p class="aide"><?= e(t('Note sur 100 : technologies 55, intitulé 15, séniorité 10, langues 10, disponibilité 5, tarif 5. Une donnée inconnue vaut la moitié des points. Les profils archivés ou non retenus sont exclus.')) ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<aside>
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Demande')) ?></h2>
|
||||
<dl class="grille serre">
|
||||
<dt><?= e(t('Séniorité')) ?></dt><dd><?= $b['seniorite_min'] ? e(t('%s au moins', t(SENIORITES[$b['seniorite_min']]))) : '<em>' . e(t('Indifférente')) . '</em>' ?></dd>
|
||||
<dt><?= e(t('Langues')) ?></dt><dd><?= e($b['langues']) ?: '<em>' . e(t('non renseigné')) . '</em>' ?></dd>
|
||||
<dt><?= e(t('Lieu')) ?></dt><dd><?= e($b['lieu']) ?: '<em>' . e(t('non renseigné')) . '</em>' ?></dd>
|
||||
<dt><?= e(t('Télétravail')) ?></dt><dd><?= $b['teletravail'] ? e(t(TELETRAVAIL[$b['teletravail']])) : '<em>' . e(t('non renseigné')) . '</em>' ?></dd>
|
||||
<dt><?= e(t('Début')) ?></dt><dd><?= e(date_fr($b['date_debut'])) ?: '<em>' . e(t('non renseigné')) . '</em>' ?></dd>
|
||||
<dt><?= e(t('Budget')) ?></dt><dd><?= $b['tjm_max'] ? e(t('%s par jour', euros($b['tjm_max']))) : '<em>' . e(t('non renseigné')) . '</em>' ?></dd>
|
||||
</dl>
|
||||
<?php if ($b['technologies']): ?>
|
||||
<p class="technos"><?php foreach (liste_saisie($b['technologies']) as $t): ?><span><?= e($t) ?></span><?php endforeach; ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if ($b['description']): ?><p class="description-besoin"><?= nl2br(e($b['description'])) ?></p><?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Gestion')) ?></h2>
|
||||
<details<?= $erreur !== null ? ' open' : '' ?>>
|
||||
<summary><?= e(t('Modifier le besoin')) ?></summary>
|
||||
<form method="post" action="<?= e(url('besoin', ['id' => $b['id']])) ?>" class="pile besoin-etroit">
|
||||
<?= champ_csrf() ?>
|
||||
<?php require __DIR__ . '/_besoin_champs.php'; ?>
|
||||
<div class="actions"><button type="submit" name="action" value="modifier" class="secondaire"><?= e(t('Enregistrer')) ?></button></div>
|
||||
</form>
|
||||
</details>
|
||||
<form method="post" action="<?= e(url('besoin', ['id' => $b['id']])) ?>" class="danger"
|
||||
data-confirmer="<?= e(t('Supprimer ce besoin et la liste des profils proposés ? Les profils eux-mêmes sont conservés.')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<button type="submit" name="action" value="supprimer" class="dangereux"><?= e(t('Supprimer le besoin')) ?></button>
|
||||
</form>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</aside>
|
||||
</div>
|
||||
@@ -0,0 +1,53 @@
|
||||
<div class="barre-titre">
|
||||
<h1><?= e(t('Besoins clients')) ?> <small><?= e(tn(count($liste), '%d besoin', '%d besoins')) ?></small></h1>
|
||||
<form class="filtres" method="get" action="index.php" data-auto>
|
||||
<input type="hidden" name="p" value="besoins">
|
||||
<label><?= e(t('Statut')) ?>
|
||||
<select name="statut">
|
||||
<option value=""><?= e(t('Tous')) ?></option>
|
||||
<?php foreach (ETATS_BESOIN as $code => $libelle): ?><option value="<?= e($code) ?>"<?= $statut === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit" class="secondaire" data-auto-masquer><?= e(t('Filtrer')) ?></button>
|
||||
</form>
|
||||
</div>
|
||||
<p class="aide"><?= e(t('Un besoin décrit une mission à pourvoir chez un client. Vivier propose les profils qui y répondent le mieux.')) ?></p>
|
||||
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<section class="carte">
|
||||
<details<?= $erreur !== null || !$liste ? ' open' : '' ?>>
|
||||
<summary><?= e(t('Nouveau besoin')) ?></summary>
|
||||
<?php if ($erreur !== null): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
<form method="post" action="<?= e(url('besoins')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<?php require __DIR__ . '/_besoin_champs.php'; ?>
|
||||
<div class="actions"><button type="submit"><?= e(t('Enregistrer le besoin')) ?></button></div>
|
||||
</form>
|
||||
</details>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!$liste): ?>
|
||||
<section class="carte"><p class="vide"><?= e($statut !== '' ? t('Aucun besoin avec ce statut.') : t('Aucun besoin enregistré.')) ?></p></section>
|
||||
<?php else: ?>
|
||||
<ul class="resultats">
|
||||
<?php foreach ($liste as $b): ?>
|
||||
<li class="carte<?= $b['statut'] !== 'ouvert' ? ' inactif' : '' ?>">
|
||||
<div class="resultat-tete">
|
||||
<a class="nom" href="<?= e(url('besoin', ['id' => $b['id']])) ?>"><?= e($b['intitule']) ?></a>
|
||||
<span class="etat"><span class="badge badge-besoin-<?= e($b['statut']) ?>"><?= e(t(ETATS_BESOIN[$b['statut']])) ?></span></span>
|
||||
</div>
|
||||
<p class="sous-titre">
|
||||
<strong><?= e($b['client']) ?></strong>
|
||||
<?php if ($b['seniorite_min']): ?><span class="badge badge-<?= e($b['seniorite_min']) ?>"><?= e(t(SENIORITES[$b['seniorite_min']])) ?></span><?php endif; ?>
|
||||
<?php if ($b['lieu']): ?><span><?= e($b['lieu']) ?></span><?php endif; ?>
|
||||
<?php if ($b['date_debut']): ?><span><?= e(t('début le %s', date_fr($b['date_debut']))) ?></span><?php endif; ?>
|
||||
<span><?= e(tn((int) $b['proposes'], '%d profil proposé', '%d profils proposés')) ?></span>
|
||||
</p>
|
||||
<?php if ($b['technologies']): ?>
|
||||
<p class="technos"><?php foreach (liste_saisie($b['technologies']) as $t): ?><span><?= e($t) ?></span><?php endforeach; ?></p>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,13 @@
|
||||
<section class="carte moyen">
|
||||
<h1><?= e(t('Vos codes de secours')) ?></h1>
|
||||
<p><?= e(t('Gardez ces dix codes en lieu sûr : gestionnaire de mots de passe, papier rangé. Chacun remplace une fois le code du téléphone, si vous le perdez ou l\'oubliez.')) ?></p>
|
||||
<p class="flash flash-info"><?= e(t('Ils ne seront plus jamais affichés. Les codes précédents ne marchent plus.')) ?></p>
|
||||
<ol class="codes-secours">
|
||||
<?php foreach ($codes as $code): ?><li><code><?= e($code) ?></code></li><?php endforeach; ?>
|
||||
</ol>
|
||||
<div class="actions gauche">
|
||||
<button type="button" class="secondaire" data-copier="<?= e(implode("\n", $codes)) ?>" data-copie="<?= e(t('Copiés')) ?>"><?= e(t('Copier les codes')) ?></button>
|
||||
<button type="button" class="secondaire" data-imprimer><?= e(t('Imprimer')) ?></button>
|
||||
<a class="bouton" href="<?= e(url('kanban')) ?>"><?= e(t('J\'ai gardé mes codes, continuer')) ?></a>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php $restants = codes_secours_restants($compte); ?>
|
||||
<div class="barre-titre"><h1><?= e(t('Mon compte')) ?></h1></div>
|
||||
|
||||
<div class="deux-egales">
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Identité')) ?></h2>
|
||||
<dl class="grille serre">
|
||||
<dt><?= e(t('Nom')) ?></dt><dd><?= e($moi['nom_affiche']) ?></dd>
|
||||
<dt><?= e(t('Identifiant')) ?></dt><dd><?= e($moi['identifiant']) ?></dd>
|
||||
<dt><?= e(t('Rôle')) ?></dt><dd><?= e($moi['role'] === 'admin' ? t('Gestionnaire des comptes') : t('Utilisateur')) ?></dd>
|
||||
</dl>
|
||||
<p class="aide"><?php if ($moi['role'] === 'admin'): ?><?= e(t('Votre nom et votre identifiant se changent dans Gestion, Comptes.')) ?><?php else: ?><?= e(t('Pour changer de nom ou d\'identifiant, adressez-vous au gestionnaire de Vivier.')) ?><?php endif; ?></p>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="double-authentification">
|
||||
<h2><?= e(t('Double authentification')) ?></h2>
|
||||
<dl class="grille serre">
|
||||
<dt><?= e(t('État')) ?></dt><dd><?= e(t('Activée le %s', date_fr($compte['totp_active_le']))) ?></dd>
|
||||
<dt><?= e(t('Codes de secours')) ?></dt><dd class="<?= $restants <= 3 ? 'retard' : '' ?>"><?= e(tn($restants, '%d restant', '%d restants')) ?></dd>
|
||||
</dl>
|
||||
<details>
|
||||
<summary><?= e(t('Générer de nouveaux codes de secours')) ?></summary>
|
||||
<p class="aide"><?= e(t('Les dix nouveaux codes remplacent les anciens, qui ne marcheront plus.')) ?></p>
|
||||
<form method="post" action="<?= e(url('compte')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="codes">
|
||||
<label><?= e(t('Votre mot de passe')) ?> <input type="password" name="mot_de_passe" required autocomplete="current-password"></label>
|
||||
<div class="actions gauche"><button type="submit" class="secondaire"><?= e(t('Générer les codes')) ?></button></div>
|
||||
</form>
|
||||
</details>
|
||||
<p><a href="<?= e(url('double_authentification')) ?>"><?= e(t('Configurer une autre application (nouveau téléphone)')) ?></a></p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="deux-egales">
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Changer de mot de passe')) ?></h2>
|
||||
<form method="post" action="<?= e(url('compte')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="mot_de_passe">
|
||||
<label><?= e(t('Mot de passe actuel')) ?>
|
||||
<input type="password" name="actuel" required autocomplete="current-password">
|
||||
</label>
|
||||
<label><?= e(t('Nouveau mot de passe, %d caractères au moins', LONGUEUR_MOT_DE_PASSE)) ?>
|
||||
<input type="password" name="nouveau" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="new-password">
|
||||
</label>
|
||||
<label><?= e(t('Confirmation')) ?>
|
||||
<input type="password" name="confirmation" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="new-password">
|
||||
</label>
|
||||
<p class="aide"><?= e(t('Vivier refuse les mots de passe connus dans des fuites de données. Vos autres sessions se fermeront.')) ?></p>
|
||||
<div class="actions gauche"><button type="submit"><?= e(t('Changer le mot de passe')) ?></button></div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="sessions">
|
||||
<h2><?= e(t('Sessions et connexions')) ?></h2>
|
||||
<p class="aide"><?= e(t('Une session se ferme après 2 heures sans activité, et au plus tard 12 heures après la connexion.')) ?></p>
|
||||
<form method="post" action="<?= e(url('compte')) ?>" class="bloc-action">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="sessions">
|
||||
<button type="submit" class="secondaire"><?= e(t('Déconnecter mes autres sessions')) ?></button>
|
||||
</form>
|
||||
<p class="aide"><?= e(t('Utile si vous êtes resté connecté sur un autre ordinateur ou un téléphone perdu.')) ?></p>
|
||||
<?php if ($derniere): ?>
|
||||
<h3 class="intertitre"><?= e(t('Vos dernières connexions')) ?></h3>
|
||||
<ul class="simple connexions-courtes">
|
||||
<?php foreach ($derniere as $l): ?>
|
||||
<li><?= e(date_fr($l['moment'], true)) ?> <small><?= e(navigateur_lisible($l['navigateur'])) ?> · <?= e($l['ip']) ?></small></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<p class="aide"><?= e(t('Une connexion que vous ne reconnaissez pas ? Changez votre mot de passe et prévenez le gestionnaire de Vivier.')) ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
<div class="porte">
|
||||
<img src="assets/logo-itclub-or.png" alt="<?= e(t('IT Club')) ?>" width="224" height="70">
|
||||
<p class="porte-nom"><?= e(config('nom_app')) ?></p>
|
||||
<p class="porte-role"><?= e(t('Le suivi des CV d\'IT Club')) ?></p>
|
||||
<form method="post" action="<?= e(url('connexion')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<?php if ($erreur): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
<?php if ($info): ?><p class="flash flash-info" role="status"><?= e($info) ?></p><?php endif; ?>
|
||||
<label><?= e(t('Identifiant')) ?>
|
||||
<input type="text" name="identifiant" required autofocus autocomplete="username" autocapitalize="none" spellcheck="false" maxlength="190">
|
||||
</label>
|
||||
<label><?= e(t('Mot de passe')) ?>
|
||||
<input type="password" name="mot_de_passe" required autocomplete="current-password" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>">
|
||||
</label>
|
||||
<button type="submit"><?= e(t('Continuer')) ?></button>
|
||||
</form>
|
||||
<p class="porte-note"><?= e(t('Étape suivante : le code de votre application d\'authentification.')) ?></p>
|
||||
<p class="porte-lien">
|
||||
<a href="<?= e(url('inscription')) ?>"><?= e(t('Pas encore de compte ?')) ?></a>
|
||||
<span aria-hidden="true">·</span>
|
||||
<a href="<?= e(url('securite')) ?>"><?= e(t('Comment Vivier protège vos accès')) ?></a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,41 @@
|
||||
<div class="barre-titre">
|
||||
<h1><?= e(t('Connexions')) ?> <small><?= e(tn(count($lignes), '%d ligne', '%d lignes')) ?></small></h1>
|
||||
<form class="filtres" method="get" action="index.php" data-auto>
|
||||
<input type="hidden" name="p" value="connexions">
|
||||
<label><?= e(t('Compte')) ?>
|
||||
<select name="utilisateur">
|
||||
<option value="0"><?= e(t('Tous')) ?></option>
|
||||
<?php foreach ($comptes as $c): ?><option value="<?= (int) $c['id'] ?>"<?= $qui === (int) $c['id'] ? ' selected' : '' ?>><?= e($c['nom_affiche']) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Résultat')) ?>
|
||||
<select name="resultat">
|
||||
<option value=""><?= e(t('Tous')) ?></option>
|
||||
<option value="echecs"<?= $resultat === 'echecs' ? ' selected' : '' ?>><?= e(t('Échecs seulement')) ?></option>
|
||||
<?php foreach (RESULTATS_CONNEXION as $code => $libelle): ?><option value="<?= e($code) ?>"<?= $resultat === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit" data-auto-masquer><?= e(t('Filtrer')) ?></button>
|
||||
</form>
|
||||
</div>
|
||||
<p class="aide"><?= e(t('Chaque essai de connexion, réussi ou non, avec l\'adresse IP et le navigateur. Gardé %d mois. Un compte gestionnaire qui se connecte depuis un réseau jamais vu déclenche un email d\'alerte.', CONNEXIONS_MOIS)) ?></p>
|
||||
<?php if (!$lignes): ?>
|
||||
<section class="carte"><p class="vide"><?= e(t('Aucune connexion enregistrée.')) ?></p></section>
|
||||
<?php else: ?>
|
||||
<section class="carte defile">
|
||||
<table>
|
||||
<thead><tr><th><?= e(t('Date')) ?></th><th><?= e(t('Compte')) ?></th><th><?= e(t('Résultat')) ?></th><th><?= e(t('Adresse IP')) ?></th><th><?= e(t('Navigateur')) ?></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($lignes as $l): ?>
|
||||
<tr>
|
||||
<td><?= e(date_fr($l['moment'], true)) ?></td>
|
||||
<td><?= e($l['nom_affiche'] ?? $l['identifiant']) ?><?php if ($l['nom_affiche'] === null): ?> <small><?= e(t('identifiant inconnu')) ?></small><?php endif; ?></td>
|
||||
<td><span class="<?= $l['resultat'] === 'reussie' ? '' : 'retard' ?>"><?= e(t(RESULTATS_CONNEXION[$l['resultat']] ?? $l['resultat'])) ?></span></td>
|
||||
<td><?= e($l['ip']) ?></td>
|
||||
<td><?= e(navigateur_lisible($l['navigateur'])) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,45 @@
|
||||
<h1><?= e(t('Conservation')) ?> <small><?= e(tn(count($liste), '%d profil à traiter', '%d profils à traiter')) ?></small></h1>
|
||||
<p class="aide">
|
||||
<?= e(t('Profils dont la durée de conservation est dépassée ou se termine dans 60 jours. Prolongez si le candidat a confirmé son accord, sinon supprimez. La durée par défaut est de %d mois.', (int) config('conservation_mois'))) ?>
|
||||
<?= e(est_admin() ? t('Un profil supprimé part à la corbeille, d\'où il est effacé après %d jours.', CORBEILLE_JOURS) : t('La suppression revient au gestionnaire des comptes.')) ?>
|
||||
</p>
|
||||
|
||||
<section class="carte">
|
||||
<?php if (!$liste): ?>
|
||||
<p class="vide"><?= e(t('Aucun profil n\'arrive à échéance.')) ?></p>
|
||||
<?php else: ?>
|
||||
<div class="defile"><table>
|
||||
<thead><tr><th><?= e(t('Profil')) ?></th><th><?= e(t('Statut')) ?></th><th><?= e(t('Reçu le')) ?></th><th><?= e(t('Échéance')) ?></th><th><?= e(t('Accord')) ?></th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($liste as $l): ?>
|
||||
<tr>
|
||||
<td><a href="<?= e(url('profil', ['id' => $l['id']])) ?>#donnees"><?= e($l['prenom'] . ' ' . $l['nom']) ?></a><br><small><?= e($l['titre'] ?? '') ?></small></td>
|
||||
<td><?= e(t(STATUTS[$l['statut']])) ?><?php if ($l['archive_le']): ?><br><small><?= e(t('Archivé')) ?></small><?php endif; ?></td>
|
||||
<td><?= e(date_fr($l['cree_le'])) ?></td>
|
||||
<td>
|
||||
<?= e(date_fr($l['conserver_jusqu_au'])) ?><br>
|
||||
<small class="<?= $l['jours'] < 0 ? 'retard' : '' ?>"><?= e($l['jours'] < 0 ? t('dépassée de %d j', abs((int) $l['jours'])) : t('dans %d j', (int) $l['jours'])) ?></small>
|
||||
</td>
|
||||
<td><?= $l['consentement_le'] ? e(date_fr($l['consentement_le'])) : '<em>' . e(t('non enregistré')) . '</em>' ?></td>
|
||||
<td class="actions-ligne">
|
||||
<form method="post" action="<?= e(url('conservation')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="prolonger">
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<button type="submit" class="secondaire"><?= e(t('Prolonger')) ?></button>
|
||||
</form>
|
||||
<?php if (est_admin()): ?>
|
||||
<form method="post" action="<?= e(url('profil_suppr')) ?>" data-confirmer="<?= e(t('Supprimer ce profil ? Il part à la corbeille, puis il est effacé avec ses documents au bout de %d jours.', CORBEILLE_JOURS)) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<input type="hidden" name="retour" value="conservation">
|
||||
<button type="submit" class="dangereux"><?= e(t('Supprimer')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table></div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
@@ -0,0 +1,44 @@
|
||||
<h1><?= e(t('Corbeille')) ?> <small><?= e(tn(count($liste), '%d profil', '%d profils')) ?></small></h1>
|
||||
<p class="aide">
|
||||
<?= e(t('Un profil supprimé reste ici %d jours, puis Vivier l\'efface avec ses documents, sa photo, ses commentaires, ses entretiens, ses relances et ses mots-clés. Pendant ce délai, il n\'apparaît nulle part ailleurs. Les sauvegardes déjà faites gardent leur contenu.', CORBEILLE_JOURS)) ?>
|
||||
</p>
|
||||
|
||||
<section class="carte">
|
||||
<?php if (!$liste): ?>
|
||||
<p class="vide"><?= e(t('La corbeille est vide.')) ?></p>
|
||||
<?php else: ?>
|
||||
<div class="defile"><table>
|
||||
<thead><tr><th><?= e(t('Profil')) ?></th><th><?= e(t('Statut')) ?></th><th><?= e(t('Supprimé le')) ?></th><th><?= e(t('Effacement')) ?></th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($liste as $l): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="profil-ligne"><?= avatar($l) ?>
|
||||
<span><strong><?= e($l['prenom'] . ' ' . $l['nom']) ?></strong> <small><?= e(reference_profil($l)) ?></small><br>
|
||||
<small><?= e($l['titre'] ?? '') ?><?= $l['documents'] ? ($l['titre'] ? ' · ' : '') . e(tn((int) $l['documents'], '%d document', '%d documents')) : '' ?></small></span>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= e(t(STATUTS[$l['statut']])) ?><?php if ($l['archive_le']): ?><br><small><?= e(t('Archivé')) ?></small><?php endif; ?></td>
|
||||
<td><?= e(date_fr($l['supprime_le'], true)) ?><br><small><?= e($l['supprime_par_nom'] ?? '') ?></small></td>
|
||||
<td>
|
||||
<?= e(date_fr((new DateTimeImmutable($l['supprime_le']))->modify('+' . CORBEILLE_JOURS . ' days')->format('Y-m-d'))) ?><br>
|
||||
<small class="<?= (int) $l['jours_restants'] <= 3 ? 'retard' : '' ?>"><?= e((int) $l['jours_restants'] > 0 ? t('dans %d j', (int) $l['jours_restants']) : t('au prochain passage')) ?></small>
|
||||
</td>
|
||||
<td class="actions-ligne">
|
||||
<form method="post" action="<?= e(url('corbeille')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<button type="submit" name="action" value="restaurer" class="secondaire"><?= e(t('Restaurer')) ?></button>
|
||||
</form>
|
||||
<form method="post" action="<?= e(url('corbeille')) ?>" data-confirmer="<?= e(t('Effacer ce profil pour de bon, avec ses documents ? Cette action est sans retour.')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<button type="submit" name="action" value="effacer" class="dangereux"><?= e(t('Effacer maintenant')) ?></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table></div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/**
|
||||
* CV au format IT Club : aperçu A4, enregistrable en PDF depuis le navigateur.
|
||||
* Variables : $c (candidat), $d (contenu préparé par donnees_cv()), $langue, $source, $anonyme, $traduction.
|
||||
* La barre d'outils suit la langue de l'interface, la feuille suit la langue du CV.
|
||||
*/
|
||||
$lien = static fn (string $page, array $plus = []): string => url($page, $plus + ['id' => $c['id'], 'langue' => $langue] + ($anonyme ? ['anonyme' => 1] : []));
|
||||
$etat = $traduction['etat'];
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="<?= e(langue_courante()) ?>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<title><?= e($d['fichier']) ?></title>
|
||||
<link rel="stylesheet" href="assets/cv.css?v=1.8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="outils">
|
||||
<a href="<?= e(url('profil', ['id' => $c['id']])) ?>"><?= e(t('Retour à la fiche')) ?></a>
|
||||
<span class="versions" role="group" aria-label="<?= e(t('Version')) ?>">
|
||||
<a href="<?= e(url('cv', ['id' => $c['id'], 'langue' => $langue])) ?>"<?= $anonyme ? '' : ' aria-current="true"' ?>><?= e(t('Nominatif')) ?></a>
|
||||
<a href="<?= e(url('cv', ['id' => $c['id'], 'langue' => $langue, 'anonyme' => 1])) ?>"<?= $anonyme ? ' aria-current="true"' : '' ?>><?= e(t('Anonymisé')) ?></a>
|
||||
</span>
|
||||
<span class="versions" role="group" aria-label="<?= e(t('Langue du CV')) ?>">
|
||||
<?php foreach (LANGUES_CV as $code => $nom): ?>
|
||||
<a href="<?= e($lien('cv', ['langue' => $code])) ?>" lang="<?= e($code) ?>" title="<?= e($nom) ?>"<?= $code === $langue ? ' aria-current="true"' : '' ?>><?= e(strtoupper($code)) ?></a>
|
||||
<?php endforeach; ?>
|
||||
</span>
|
||||
<a class="action" href="<?= e($lien('cv_word')) ?>"><?= e(t('Télécharger en Word')) ?></a>
|
||||
<button type="button" class="action" id="imprimer"><?= e(t('Enregistrer en PDF')) ?></button>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($_SESSION['flash'])): ?>
|
||||
<?php foreach ($_SESSION['flash'] as $f): ?>
|
||||
<p class="note note-<?= e($f['type']) ?>" role="status"><?= e($f['message']) ?></p>
|
||||
<?php endforeach; unset($_SESSION['flash']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($etat === 'absente' || $etat === 'perimee'): ?>
|
||||
<div class="note note-traduction">
|
||||
<p>
|
||||
<strong><?= e($etat === 'perimee'
|
||||
? t('La fiche a changé depuis la traduction : le contenu s\'affiche dans sa langue d\'origine.')
|
||||
: t('Seuls les intitulés sont traduits : le contenu reste dans la langue du CV d\'origine (%s).', strtoupper($source))) ?></strong>
|
||||
</p>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<div class="note-actions">
|
||||
<?php if (Claude::configure()): ?>
|
||||
<form method="post" action="<?= e(url('cv_traduire')) ?>" data-attente="<?= e(t('Traduction en cours…')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<input type="hidden" name="langue" value="<?= e($langue) ?>">
|
||||
<?php if ($anonyme): ?><input type="hidden" name="anonyme" value="1"><?php endif; ?>
|
||||
<button type="submit"><?= e(t('Traduire le contenu')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<details>
|
||||
<summary><?= e(t('Traduire par fichier')) ?></summary>
|
||||
<ol>
|
||||
<li><a href="<?= e($lien('cv_paquet')) ?>"><?= e(t('Télécharger le fichier à traduire')) ?></a></li>
|
||||
<li><?= e(t('Le déposer dans votre projet Claude d\'analyse, qui renvoie le fichier traduit.')) ?></li>
|
||||
<li>
|
||||
<form method="post" action="<?= e(url('cv_traduction')) ?>" enctype="multipart/form-data">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<input type="hidden" name="langue" value="<?= e($langue) ?>">
|
||||
<?php if ($anonyme): ?><input type="hidden" name="anonyme" value="1"><?php endif; ?>
|
||||
<label><?= e(t('Charger le fichier traduit')) ?> <input type="file" name="traduction" accept=".json,application/json" required></label>
|
||||
<button type="submit"><?= e(t('Enregistrer la traduction')) ?></button>
|
||||
</form>
|
||||
</li>
|
||||
</ol>
|
||||
</details>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php elseif ($etat === 'a_jour'): ?>
|
||||
<p class="note note-info">
|
||||
<?= e($traduction['origine'] === 'automatique'
|
||||
? t('Contenu traduit automatiquement le %s. Relisez-le avant d\'envoyer le CV.', date_fr($traduction['cree_le']))
|
||||
: t('Contenu traduit par fichier le %s.', date_fr($traduction['cree_le']))) ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="page" lang="<?= e($langue) ?>">
|
||||
<header class="bandeau">
|
||||
<img class="logo" src="assets/logo-itclub-or.png" alt="IT Club" width="160" height="50">
|
||||
<div class="identite">
|
||||
<h1><?= e($d['nom']) ?></h1>
|
||||
<?php if ($d['titre'] !== ''): ?><p class="titre"><?= e($d['titre']) ?></p><?php endif; ?>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<?php /* Tableau de mise en page : l'en-tête et le pied vides se répètent sur chaque page imprimée et tiennent lieu de marges. */ ?>
|
||||
<table class="corps" role="presentation">
|
||||
<thead><tr><td><div class="marge-haut"></div></td></tr></thead>
|
||||
<tfoot><tr><td><div class="marge-bas"></div></td></tr></tfoot>
|
||||
<tbody><tr><td>
|
||||
|
||||
<p class="reperes">
|
||||
<?php foreach ($d['reperes'] as $libelle => $valeur): ?>
|
||||
<span><small><?= e($libelle) ?></small> <strong><?= e($valeur) ?></strong></span>
|
||||
<?php endforeach; ?>
|
||||
</p>
|
||||
|
||||
<?php if ($d['resume']): ?>
|
||||
<section>
|
||||
<h2><?= e($d['libelles']['synthese']) ?></h2>
|
||||
<?php foreach ($d['resume'] as $l): ?><p><?= e($l) ?></p><?php endforeach; ?>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($d['competences']): ?>
|
||||
<section>
|
||||
<h2><?= e($d['libelles']['competences']) ?></h2>
|
||||
<table class="competences">
|
||||
<?php foreach ($d['competences'] as $libelle => $technos): ?>
|
||||
<tr>
|
||||
<th scope="row"><?= e($libelle) ?></th>
|
||||
<td>
|
||||
<?php foreach ($technos as $i => $t): ?><?= $i ? ' · ' : '' ?><span class="techno"><?= e($t['nom']) ?><?php if ($t['annees'] !== ''): ?> <small><?= e($t['annees']) ?></small><?php endif; ?></span><?php endforeach; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($d['experiences']): ?>
|
||||
<section>
|
||||
<h2><?= e($d['libelles']['experiences']) ?></h2>
|
||||
<?php foreach ($d['experiences'] as $x): ?>
|
||||
<article class="experience">
|
||||
<p class="periode"><?= e($x['debut']) ?><span class="fleche"> → </span><br><?= e($x['fin']) ?></p>
|
||||
<div>
|
||||
<h3><?= e($x['poste']) ?><?php if ($x['contexte'] !== ''): ?> <span class="employeur"><?= e($x['contexte']) ?></span><?php endif; ?></h3>
|
||||
<?php if ($x['cadre'] !== ''): ?><p class="cadre"><?= e($x['cadre']) ?></p><?php endif; ?>
|
||||
<?php if ($x['mission'] !== ''): ?><p class="mission"><?= e($x['mission']) ?></p><?php endif; ?>
|
||||
<?php foreach ($x['lignes'] as $l): ?><p><?= e($l) ?></p><?php endforeach; ?>
|
||||
<?php if ($x['environnement'] !== ''): ?><p class="environnement"><strong><?= e($d['libelles']['environnement']) ?></strong> <?= e($x['environnement']) ?></p><?php endif; ?>
|
||||
</div>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($d['formations'] || $d['certifications']): ?>
|
||||
<section class="deux">
|
||||
<?php foreach ([$d['libelles']['formation'] => $d['formations'], $d['libelles']['certifications'] => $d['certifications']] as $intitule => $liste): if (!$liste) { continue; } ?>
|
||||
<div>
|
||||
<h2><?= e($intitule) ?></h2>
|
||||
<ul>
|
||||
<?php foreach ($liste as $f): ?>
|
||||
<li><strong><?= e($f['titre']) ?></strong><?= $f['detail'] !== '' ? ', ' . e($f['detail']) : '' ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<footer class="contact">
|
||||
<span class="signature"><?= e($d['signature']) ?></span>
|
||||
<span><?= e($d['contact']) ?></span>
|
||||
</footer>
|
||||
|
||||
</td></tr></tbody>
|
||||
</table>
|
||||
|
||||
<p class="mentions"><?= e($d['mentions']) ?></p>
|
||||
</div>
|
||||
|
||||
<script src="assets/cv.js?v=1.8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
* Activation de la double authentification : à la première connexion, ou depuis Mon compte (nouveau téléphone).
|
||||
* Variables : $cle (clé en base32), $adresse (otpauth://), $erreur, $connecte.
|
||||
*/
|
||||
$etapes = static function () use ($cle, $adresse, $erreur, $connecte): void { ?>
|
||||
<ol class="etapes-2fa">
|
||||
<li>
|
||||
<strong><?= e(t('Installez une application d\'authentification sur votre téléphone.')) ?></strong>
|
||||
<span><?= e(t('Google Authenticator, Microsoft Authenticator ou le gestionnaire de mots de passe que vous utilisez déjà (Bitwarden, 1Password, Mots de passe d\'Apple).')) ?></span>
|
||||
</li>
|
||||
<li>
|
||||
<strong><?= e(t('Dans l\'application, ajoutez un compte et scannez ce QR code.')) ?></strong>
|
||||
<span class="qr" data-qr="<?= e($adresse) ?>" role="img" aria-label="<?= e(t('QR code à scanner')) ?>"></span>
|
||||
<span><?= e(t('Pas de QR code ? Saisissez cette clé à la main, en type « basé sur l\'heure » :')) ?></span>
|
||||
<code class="cle-2fa"><?= e(Totp::lisible($cle)) ?></code>
|
||||
</li>
|
||||
<li>
|
||||
<strong><?= e(t('Saisissez le code à 6 chiffres que l\'application affiche.')) ?></strong>
|
||||
<form method="post" action="<?= e(url('double_authentification')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<?php if ($erreur): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
<label><?= e(t('Code')) ?>
|
||||
<input type="text" name="code" required autocomplete="one-time-code" inputmode="numeric" maxlength="7" class="code-saisie" placeholder="123456" spellcheck="false">
|
||||
</label>
|
||||
<?php if ($connecte): ?>
|
||||
<label><?= e(t('Votre mot de passe')) ?>
|
||||
<input type="password" name="mot_de_passe" required autocomplete="current-password">
|
||||
</label>
|
||||
<?php endif; ?>
|
||||
<button type="submit"><?= e(t('Activer')) ?></button>
|
||||
</form>
|
||||
</li>
|
||||
</ol>
|
||||
<?php };
|
||||
?>
|
||||
<?php if ($connecte): ?>
|
||||
<p class="fil"><a href="<?= e(url('compte')) ?>"><?= e(t('Mon compte')) ?></a> / <?= e(t('Double authentification')) ?></p>
|
||||
<section class="carte moyen">
|
||||
<h1><?= e(t('Configurer une autre application')) ?></h1>
|
||||
<p class="aide"><?= e(t('Nouveau téléphone ou nouvelle application : l\'ancienne cessera de fonctionner dès que celle-ci sera activée. Vos autres sessions seront fermées et vous recevrez dix nouveaux codes de secours.')) ?></p>
|
||||
<?php $etapes(); ?>
|
||||
</section>
|
||||
<?php else: ?>
|
||||
<div class="porte porte-large">
|
||||
<img src="assets/logo-itclub-or.png" alt="<?= e(t('IT Club')) ?>" width="224" height="70">
|
||||
<p class="porte-nom"><?= e(config('nom_app')) ?></p>
|
||||
<p class="porte-role"><?= e(t('Dernière étape : la double authentification. À chaque connexion, Vivier vous demandera votre mot de passe puis un code de votre téléphone.')) ?></p>
|
||||
<?php $etapes(); ?>
|
||||
<p class="porte-lien"><a href="<?= e(url('securite')) ?>" target="_blank" rel="noopener"><?= e(t('Pourquoi cette étape ?')) ?></a></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<script src="assets/qrcode.js?v=1.8" defer></script>
|
||||
@@ -0,0 +1,31 @@
|
||||
<div class="barre-titre">
|
||||
<h1><?= e(t('Erreurs')) ?> <small><?= count($lignes) ?></small></h1>
|
||||
<?php if ($lignes): ?>
|
||||
<form method="post" action="<?= e(url('erreurs')) ?>" data-confirmer="<?= e(t('Vider le journal des erreurs ?')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<button type="submit" name="action" value="vider" class="secondaire"><?= e(t('Vider le journal')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<p class="aide"><?= e(t('Les erreurs imprévues des %d derniers incidents. Une personne qui voit « Une erreur est survenue » reçoit une référence : cherchez-la ici. Aucune donnée de candidat n\'y figure.', ERREURS_MAX)) ?></p>
|
||||
<?php if (!$lignes): ?>
|
||||
<section class="carte"><p class="vide"><?= e(t('Aucune erreur notée.')) ?></p></section>
|
||||
<?php else: ?>
|
||||
<section class="carte defile">
|
||||
<table>
|
||||
<thead><tr><th><?= e(t('Date')) ?></th><th><?= e(t('Référence')) ?></th><th><?= e(t('Compte')) ?></th><th><?= e(t('Page')) ?></th><th><?= e(t('Cause')) ?></th><th><?= e(t('Endroit du code')) ?></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($lignes as $l): ?>
|
||||
<tr>
|
||||
<td><?= e(date_fr((string) $l['date'], true)) ?></td>
|
||||
<td><code><?= e((string) $l['ref']) ?></code></td>
|
||||
<td><?= e($l['compte'] !== null ? ($noms[$l['compte']] ?? '#' . $l['compte']) : '–') ?></td>
|
||||
<td><?= e((string) $l['page']) ?> <small><?= e((string) $l['methode']) ?></small></td>
|
||||
<td><?= e((string) $l['type']) ?><br><small><?= e((string) $l['message']) ?></small></td>
|
||||
<td><code><?= e((string) $l['ou']) ?></code></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
Whitespace-only changes.
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
$u = !empty($_SESSION['uid']) ? utilisateur() : null;
|
||||
$actif = static fn (string ...$pages): string => in_array($page ?? '', $pages, true) ? ' aria-current="page"' : '';
|
||||
$relancesDues = $u ? nombre_relances_dues() : 0;
|
||||
$pagesGestion = ['conservation', 'journal', 'connexions', 'utilisateurs', 'sauvegarde', 'corbeille', 'erreurs'];
|
||||
// Le menu Gestion est réservé au gestionnaire : conservation, journal, connexions, comptes, corbeille, sauvegarde, erreurs
|
||||
$admin = $u !== null && $u['role'] === 'admin';
|
||||
$echus = $admin ? nombre_echus() : 0;
|
||||
$demandes = $admin ? nombre_demandes() : 0;
|
||||
// Traductions à faire : le script les traite en arrière-plan, une par une
|
||||
$aTraduire = $u && peut_ecrire() && Claude::configure() ? nombre_traductions_a_faire() : 0;
|
||||
// Page publique (guide Sécurité lu sans être connecté) : fond clair, sans menu
|
||||
$classeCorps = $u ? 'connecte' : (($page ?? '') === 'securite' ? 'public' : 'accueil');
|
||||
$aLaCorbeille = $admin ? nombre_corbeille() : 0;
|
||||
// Menu du compte : initiales dans la pastille, prénom à côté
|
||||
$mots = $u ? (preg_split('/\s+/u', trim($u['nom_affiche'])) ?: []) : [];
|
||||
$initiales = mb_strtoupper(mb_substr($mots[0] ?? '', 0, 1) . (count($mots) > 1 ? mb_substr((string) end($mots), 0, 1) : ''));
|
||||
$prenomCompte = $mots[0] ?? '';
|
||||
$ici = 'index.php?' . http_build_query(array_filter($_GET, 'is_string'));
|
||||
// Textes dont le JavaScript a besoin, dans la langue de l'interface
|
||||
$textesJs = [
|
||||
'echecEnregistrement' => t('Le changement n\'a pas été enregistré.'),
|
||||
'connexionPerdue' => t('Connexion perdue. Le changement n\'a pas été enregistré.'),
|
||||
'analyseEnCours' => t('Analyse en cours…'),
|
||||
'lotEnAttente' => t('En attente'),
|
||||
'lotPret' => t('Prêt à relire'),
|
||||
'lotErreur' => t('Erreur'),
|
||||
'lotTermine' => t('Analyses terminées. La page se recharge.'),
|
||||
'fermer' => t('Fermer'),
|
||||
'filtres' => t('Filtres'),
|
||||
'copie' => t('Copié'),
|
||||
];
|
||||
$langues = static function () use ($ici): string {
|
||||
$html = '<form class="langues" method="post" action="' . e(url('langue')) . '">' . champ_csrf()
|
||||
. '<input type="hidden" name="retour" value="' . e($ici) . '">';
|
||||
foreach (LANGUES_INTERFACE as $code => $nom) {
|
||||
$html .= '<button type="submit" name="langue" value="' . e($code) . '" lang="' . e($code) . '" title="' . e($nom) . '"'
|
||||
. ($code === langue_courante() ? ' aria-pressed="true"' : ' aria-pressed="false"') . '>' . e(strtoupper($code)) . '</button>';
|
||||
}
|
||||
return $html . '</form>';
|
||||
};
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="<?= e(langue_courante()) ?>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta name="csrf" content="<?= e(jeton_csrf()) ?>">
|
||||
<meta name="textes" content="<?= e(json_encode($textesJs, JSON_UNESCAPED_UNICODE)) ?>">
|
||||
<?php if ($aTraduire): ?><meta name="traductions" content="<?= (int) $aTraduire ?>" data-url="<?= e(url('traduction_suivante')) ?>"><?php endif; ?>
|
||||
<title><?= e($titre ?? '') ?> · <?= e(config('nom_app')) ?></title>
|
||||
<link rel="preload" href="assets/polices/instrument-sans-latin-wght-normal.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="stylesheet" href="assets/style.css?v=1.8">
|
||||
</head>
|
||||
<body class="<?= e($classeCorps) ?>">
|
||||
<?php if ($u): ?>
|
||||
<a class="evitement" href="#contenu"><?= e(t('Aller au contenu')) ?></a>
|
||||
<header class="entete">
|
||||
<div class="entete-barre">
|
||||
<a class="marque" href="<?= e(url('kanban')) ?>">
|
||||
<img src="assets/logo-itclub-or.png" alt="IT Club" width="96" height="30">
|
||||
<span><?= e(config('nom_app')) ?></span>
|
||||
</a>
|
||||
<button type="button" class="menu-bouton" aria-expanded="false" aria-controls="menu"><?= e(t('Menu')) ?></button>
|
||||
</div>
|
||||
<div class="entete-menu" id="menu">
|
||||
<nav aria-label="<?= e(t('Navigation principale')) ?>">
|
||||
<a href="<?= e(url('kanban')) ?>"<?= $actif('kanban') ?>><?= e(t('Kanban')) ?></a>
|
||||
<a href="<?= e(url('recherche')) ?>"<?= $actif('recherche', 'profil', 'profil_modifier') ?>><?= e(t('Profils')) ?></a>
|
||||
<a href="<?= e(url('besoins')) ?>"<?= $actif('besoins', 'besoin') ?>><?= e(t('Besoins')) ?></a>
|
||||
<a href="<?= e(url('suivi')) ?>"<?= $actif('suivi') ?>><?= e(t('Suivi')) ?><?php if ($relancesDues): ?> <span class="pastille" title="<?= e(t('Relances à faire')) ?>"><?= $relancesDues ?></span><?php endif; ?></a>
|
||||
<a href="<?= e(url('tableau')) ?>"<?= $actif('tableau') ?>><?= e(t('Tableau de bord')) ?></a>
|
||||
<?php if ($admin): ?>
|
||||
<details class="menu-plus<?= in_array($page ?? '', $pagesGestion, true) ? ' actif' : '' ?>">
|
||||
<summary><?= e(t('Gestion')) ?><?php if ($echus + $demandes): ?> <span class="pastille" title="<?= e(implode(' · ', array_filter([$echus ? t('Conservation à traiter') . ' : ' . $echus : '', $demandes ? t('Demandes de compte à valider') . ' : ' . $demandes : '']))) ?>"><?= $echus + $demandes ?></span><?php endif; ?></summary>
|
||||
<div class="menu-plus-liste">
|
||||
<a href="<?= e(url('conservation')) ?>"<?= $actif('conservation') ?>><?= e(t('Conservation')) ?><?php if ($echus): ?> <span class="pastille"><?= $echus ?></span><?php endif; ?></a>
|
||||
<a href="<?= e(url('journal')) ?>"<?= $actif('journal') ?>><?= e(t('Journal')) ?></a>
|
||||
<a href="<?= e(url('connexions')) ?>"<?= $actif('connexions') ?>><?= e(t('Connexions')) ?></a>
|
||||
<a href="<?= e(url('utilisateurs')) ?>"<?= $actif('utilisateurs') ?>><?= e(t('Comptes')) ?><?php if ($demandes): ?> <span class="pastille pastille-demande" title="<?= e(t('Demandes de compte à valider')) ?>"><?= $demandes ?></span><?php endif; ?></a>
|
||||
<a href="<?= e(url('corbeille')) ?>"<?= $actif('corbeille') ?>><?= e(t('Corbeille')) ?><?php if ($aLaCorbeille): ?> <small class="compte-menu"><?= $aLaCorbeille ?></small><?php endif; ?></a>
|
||||
<a href="<?= e(url('sauvegarde')) ?>"<?= $actif('sauvegarde') ?>><?= e(t('Sauvegarde')) ?></a>
|
||||
<a href="<?= e(url('erreurs')) ?>"<?= $actif('erreurs') ?>><?= e(t('Erreurs')) ?></a>
|
||||
</div>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
<form class="recherche-rapide" method="get" action="index.php" role="search">
|
||||
<input type="hidden" name="p" value="recherche">
|
||||
<input type="search" name="q" placeholder="<?= e(t('Rechercher un profil')) ?>" aria-label="<?= e(t('Rechercher un profil')) ?>"
|
||||
title="<?= e(t('Exemple : %s', t('développeur Java senior'))) ?>" value="<?= e(($page ?? '') === 'recherche' ? ($_GET['q'] ?? '') : '') ?>">
|
||||
<button type="submit" aria-label="<?= e(t('Rechercher')) ?>" title="<?= e(t('Rechercher')) ?>">
|
||||
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><circle cx="7" cy="7" r="4.6"/><path d="m10.6 10.6 3.4 3.4"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
<a class="bouton bouton-entete" href="<?= e(url('import')) ?>"<?= $actif('import', 'import_relecture', 'import_lot') ?>><?= e(t('Importer')) ?></a>
|
||||
<a class="lien-aide" href="<?= e(url('aide')) ?>"<?= $actif('aide') ?> title="<?= e(t('Aide')) ?>" aria-label="<?= e(t('Aide')) ?>"><span aria-hidden="true">?</span><b><?= e(t('Aide')) ?></b></a>
|
||||
<details class="menu-plus menu-compte<?= ($page ?? '') === 'compte' ? ' actif' : '' ?>">
|
||||
<summary title="<?= e($u['nom_affiche']) ?>">
|
||||
<span class="initiales" aria-hidden="true"><?= e($initiales) ?></span>
|
||||
<span class="nom-compte"><?= e($prenomCompte) ?></span>
|
||||
</summary>
|
||||
<div class="menu-plus-liste">
|
||||
<p class="compte-qui"><?= e($u['nom_affiche']) ?><small><?= e($u['identifiant']) ?></small></p>
|
||||
<a href="<?= e(url('compte')) ?>"<?= $actif('compte') ?>><?= e(t('Mon compte')) ?></a>
|
||||
<div class="compte-langue"><span><?= e(t('Langue')) ?></span><?= $langues() ?></div>
|
||||
<div class="sortie">
|
||||
<form method="post" action="<?= e(url('deconnexion')) ?>" id="deconnexion">
|
||||
<?= champ_csrf() ?>
|
||||
<button class="lien" type="submit"><?= e(t('Déconnexion')) ?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</header>
|
||||
<?php elseif ($classeCorps === 'public'): ?>
|
||||
<header class="entete entete-public">
|
||||
<a class="marque" href="<?= e(url('connexion')) ?>">
|
||||
<img src="assets/logo-itclub-or.png" alt="IT Club" width="96" height="30">
|
||||
<span><?= e(config('nom_app')) ?></span>
|
||||
</a>
|
||||
</header>
|
||||
<?php else: ?>
|
||||
<div class="langues-accueil"><?= $langues() ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<main id="contenu">
|
||||
<?php foreach ($flashs as $f): ?>
|
||||
<p class="flash flash-<?= e($f['type']) ?>" role="status"><?= e($f['message']) ?></p>
|
||||
<?php endforeach; ?>
|
||||
<?= $contenu ?>
|
||||
</main>
|
||||
|
||||
<script src="assets/sortable.min.js" defer></script>
|
||||
<script src="assets/app.js?v=1.8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,48 @@
|
||||
<div class="barre-titre">
|
||||
<h1><?= e(t('Importer un profil')) ?></h1>
|
||||
<a class="bouton secondaire" href="<?= e(url('import_lot')) ?>"><?= e(t('Importer plusieurs CV')) ?><?php if ($lot): ?> <span class="pastille"><?= (int) $lot ?></span><?php endif; ?></a>
|
||||
</div>
|
||||
|
||||
<?php if ($erreurs): ?>
|
||||
<div class="flash flash-erreur" role="alert">
|
||||
<strong><?= e(count($erreurs) > 1 ? t('Import refusé : %d points à corriger.', count($erreurs)) : t('Import refusé.')) ?></strong>
|
||||
<ul><?php foreach ($erreurs as $m): ?><li><?= e($m) ?></li><?php endforeach; ?></ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="deux-egales">
|
||||
<section class="carte<?= $auto ? ' mise-en-avant' : '' ?>">
|
||||
<h2><?= e(t('Analyse automatique')) ?></h2>
|
||||
<?php if ($auto): ?>
|
||||
<p class="aide"><?= e(t('Chargez le CV. Vivier l\'envoie à Claude, qui extrait le profil et rédige le rapport. Vous relisez avant d\'enregistrer.')) ?></p>
|
||||
<form method="post" action="<?= e(url('import')) ?>" enctype="multipart/form-data" class="pile" data-attente="<?= e(t('Analyse en cours. Comptez une à deux minutes, sans fermer la page.')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="mode" value="auto">
|
||||
<label><?= e(t('CV du candidat (PDF, DOCX, ODT, MD ou TXT)')) ?>
|
||||
<input type="file" name="cv" accept=".pdf,.docx,.odt,.md,.txt" required>
|
||||
</label>
|
||||
<button type="submit"><?= e(t('Analyser le CV')) ?></button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<p class="aide"><?= e(t('Non configurée. Ajoutez une clé d\'API Claude dans')) ?> <code><?= e(t('config/config.php')) ?></code> <?= e(t('pour analyser un CV sans quitter Vivier.')) ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Fichiers déjà analysés')) ?></h2>
|
||||
<p class="aide"><?= e(t('Chargez les fichiers produits par votre projet Claude « Analyse CV IT Club », avec le CV d\'origine.')) ?></p>
|
||||
<form method="post" action="<?= e(url('import')) ?>" enctype="multipart/form-data" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<label><?= e(t('Profil structuré (.json)')) ?>
|
||||
<input type="file" name="profil" accept=".json,application/json" required>
|
||||
</label>
|
||||
<label><?= e(t('CV d\'origine (PDF, DOCX, DOC, ODT, RTF, MD ou TXT)')) ?>
|
||||
<input type="file" name="cv" accept=".pdf,.docx,.doc,.odt,.rtf,.md,.txt" required>
|
||||
</label>
|
||||
<label><?= e(t('Rapport détaillé, facultatif (MD, TXT, PDF ou DOCX)')) ?>
|
||||
<input type="file" name="rapport" accept=".md,.txt,.pdf,.docx">
|
||||
</label>
|
||||
<button type="submit" class="<?= $auto ? 'secondaire' : '' ?>"><?= e(t('Contrôler le profil')) ?></button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
$libelles = ['en_attente' => t('En attente'), 'pret' => t('Prêt à relire'), 'erreur' => t('Erreur')];
|
||||
$motifs = ['email' => t('même adresse email'), 'telephone' => t('même téléphone'), 'nom' => t('même prénom et même nom')];
|
||||
$attente = count(array_filter($lignes, static fn ($l) => $l['etat'] === 'en_attente'));
|
||||
$prets = count(array_filter($lignes, static fn ($l) => $l['etat'] === 'pret'));
|
||||
$places = LOT_MAX - count($lignes);
|
||||
?>
|
||||
<p class="fil"><a href="<?= e(url('import')) ?>"><?= e(t('Importer')) ?></a> / <?= e(t('Import en lot')) ?></p>
|
||||
<div class="barre-titre">
|
||||
<h1><?= e(t('Import en lot')) ?> <small><?= e(tn(count($lignes), '%d CV dans le lot', '%d CV dans le lot')) ?></small></h1>
|
||||
</div>
|
||||
<p class="aide"><?= e(t('Déposez plusieurs CV d\'un coup : %d au plus. Chaque profil est relu avant d\'entrer dans le Kanban. Un CV laissé dans le lot est retiré après 7 jours.', LOT_MAX)) ?></p>
|
||||
|
||||
<?php if ($places > 0): ?>
|
||||
<div class="deux-egales depot">
|
||||
<section class="carte<?= $auto ? ' mise-en-avant' : '' ?>">
|
||||
<h2><?= e(t('CV à analyser')) ?></h2>
|
||||
<?php if ($auto): ?>
|
||||
<p class="aide"><?= e(t('Vivier envoie chaque CV à Claude, l\'un après l\'autre. Comptez une à deux minutes par CV.')) ?></p>
|
||||
<form method="post" action="<?= e(url('import_lot')) ?>" enctype="multipart/form-data" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<label><?= e(t('CV des candidats (PDF, DOCX, ODT, MD ou TXT)')) ?>
|
||||
<input type="file" name="cv[]" accept=".pdf,.docx,.odt,.md,.txt" multiple required>
|
||||
</label>
|
||||
<button type="submit" name="action" value="deposer"><?= e(t('Déposer les CV')) ?></button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<p class="aide"><?= e(t('Non configurée. Ajoutez une clé d\'API Claude dans')) ?> <code><?= e(t('config/config.php')) ?></code> <?= e(t('pour analyser un CV sans quitter Vivier.')) ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Profils déjà analysés')) ?></h2>
|
||||
<p class="aide"><?= e(t('Sélectionnez ensemble les fichiers JSON, les CV d\'origine et les rapports. Vivier associe chaque profil au CV que nomme son JSON.')) ?></p>
|
||||
<form method="post" action="<?= e(url('import_lot')) ?>" enctype="multipart/form-data" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<label><?= e(t('Profils (.json), CV et rapports')) ?>
|
||||
<input type="file" name="fichiers[]" accept=".json,.pdf,.docx,.doc,.odt,.rtf,.md,.txt" multiple required>
|
||||
</label>
|
||||
<button type="submit" name="action" value="deposer_analyses" class="<?= $auto ? 'secondaire' : '' ?>"><?= e(t('Déposer les fichiers')) ?></button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="flash flash-info"><?= e(t('Le lot est plein. Enregistrez ou retirez des CV pour en déposer d\'autres.')) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($lignes): ?>
|
||||
<section class="carte" id="lot" data-url="<?= e(url('import_lot_analyse')) ?>" data-auto="<?= $auto ? '1' : '0' ?>">
|
||||
<div class="barre-titre">
|
||||
<h2><?= e(t('Contenu du lot')) ?></h2>
|
||||
<?php if ($prets): ?>
|
||||
<form method="post" action="<?= e(url('import_lot')) ?>" data-confirmer="<?= e(t('Enregistrer tous les profils prêts, sans les relire un par un ?')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<button type="submit" name="action" value="tout"><?= e(tn($prets, 'Enregistrer le profil prêt', 'Enregistrer les %d profils prêts')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($attente && $auto): ?>
|
||||
<p class="flash flash-info" id="lot-avancement" role="status">
|
||||
<?= e(tn($attente, '%d CV attend son analyse. Elle démarre seule : gardez cette page ouverte.', '%d CV attendent leur analyse. Elle démarre seule : gardez cette page ouverte.')) ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<div class="defile"><table class="lot">
|
||||
<thead><tr><th><?= e(t('Fichier')) ?></th><th><?= e(t('État')) ?></th><th><?= e(t('Candidat')) ?></th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($lignes as $l): ?>
|
||||
<tr data-id="<?= (int) $l['id'] ?>" data-etat="<?= e($l['etat']) ?>">
|
||||
<td><?= e($l['nom_fichier']) ?><br><small><?= e($l['nom_affiche']) ?>, <?= e(date_fr($l['cree_le'], true)) ?></small></td>
|
||||
<td>
|
||||
<span class="badge badge-lot-<?= e($l['etat']) ?> etat-lot"><?= e($libelles[$l['etat']]) ?></span>
|
||||
<?php if ($l['etat'] === 'erreur'): ?><p class="retard detail-lot"><?= e((string) $l['erreur']) ?></p><?php else: ?><p class="retard detail-lot" hidden></p><?php endif; ?>
|
||||
</td>
|
||||
<td class="candidat-lot">
|
||||
<?= e((string) $l['candidat']) ?>
|
||||
<?php foreach ($l['doublons'] as $d): ?>
|
||||
<br><small class="retard"><?= e(t('Ressemble à %s', $d['corbeille'] ? sprintf('ITC-%04d', $d['id']) . ' (' . t('profil à la corbeille') . ')' : $d['prenom'] . ' ' . $d['nom'])) ?> : <?= e(implode(', ', array_map(static fn ($m) => $motifs[$m], $d['motifs']))) ?></small>
|
||||
<?php endforeach; ?>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="<?= e(url('import_lot')) ?>" class="actions-ligne droite">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<?php if ($l['etat'] === 'pret'): ?>
|
||||
<button type="submit" name="action" value="relire" class="secondaire"><?= e(t('Relire')) ?></button>
|
||||
<?php elseif ($auto && ($l['meta']['mode'] ?? '') === 'auto'): ?>
|
||||
<button type="submit" name="action" value="analyser" class="secondaire bouton-analyse"><?= e($l['etat'] === 'erreur' ? t('Relancer l\'analyse') : t('Analyser')) ?></button>
|
||||
<?php endif; ?>
|
||||
<button type="submit" name="action" value="retirer" class="lien"><?= e(t('Retirer')) ?></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table></div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
$id = $profil['identite'];
|
||||
$adr = array_filter([$id['adresse']['rue'] ?? null, trim(($id['adresse']['code_postal'] ?? '') . ' ' . ($id['adresse']['ville'] ?? '')), $id['adresse']['pays'] ?? null]);
|
||||
$vide = '<em>' . e(t('non renseigné')) . '</em>';
|
||||
?>
|
||||
<section class="carte moyen">
|
||||
<h1><?= e(t('Relecture avant enregistrement')) ?></h1>
|
||||
<?php if (!empty($imp['auto'])): ?>
|
||||
<p class="aide"><?= e(t('Profil extrait par l\'analyse automatique. Vérifiez l\'identité et les dates avant d\'enregistrer.')) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($doublons): $motifs = ['email' => t('même adresse email'), 'telephone' => t('même téléphone'), 'nom' => t('même prénom et même nom')]; ?>
|
||||
<div class="flash <?= $bloquant ? 'flash-erreur' : 'flash-info' ?>" role="alert">
|
||||
<strong><?= e($bloquant ? t('Ce candidat est déjà dans Vivier.') : t('Un profil ressemblant existe déjà.')) ?></strong>
|
||||
<ul>
|
||||
<?php foreach ($doublons as $d): ?>
|
||||
<li>
|
||||
<?php if ($d['corbeille']): ?>
|
||||
<?= e(sprintf('ITC-%04d', $d['id'])) ?> : <?= e(implode(', ', array_map(static fn ($m) => $motifs[$m], $d['motifs']))) ?> · <?= e(t('profil à la corbeille')) ?>
|
||||
<?php else: ?>
|
||||
<a href="<?= e(url('profil', ['id' => $d['id']])) ?>" target="_blank" rel="noopener"><?= e($d['prenom'] . ' ' . $d['nom']) ?></a><?= $d['titre'] ? ', ' . e($d['titre']) : '' ?>
|
||||
: <?= e(implode(', ', array_map(static fn ($m) => $motifs[$m], $d['motifs']))) ?><?= $d['archive'] ? ' · ' . e(t('profil archivé')) : '' ?>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php if ($bloquant && array_filter($doublons, static fn ($d) => $d['corbeille'] && in_array('email', $d['motifs'], true))): ?>
|
||||
<p><?= e(t('Ce profil est à la corbeille : le gestionnaire des comptes peut le restaurer, ou l\'effacer pour libérer son adresse email.')) ?></p>
|
||||
<?php elseif ($bloquant): ?>
|
||||
<p><?= e(t('Pour mettre sa fiche à jour, ouvrez le profil existant, carte Gestion, « Mettre à jour depuis une nouvelle analyse ».')) ?></p>
|
||||
<?php else: ?>
|
||||
<label class="case"><input type="checkbox" name="confirmer_doublon" value="1" form="enregistrement" required> <?= e(t('C\'est une autre personne : enregistrer un nouveau profil')) ?></label>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="<?= e(url('import_relecture')) ?>" id="enregistrement">
|
||||
<?= champ_csrf() ?>
|
||||
</form>
|
||||
|
||||
<div class="relecture">
|
||||
<?php if (!empty($imp['photo'])): ?>
|
||||
<div class="relecture-photo">
|
||||
<img class="avatar avatar-grand" src="<?= e(url('import_photo', ['v' => $imp['dossier']])) ?>" alt="<?= e(t('Photo trouvée dans le CV')) ?>" width="96" height="96">
|
||||
<label class="case"><input type="checkbox" name="garder_photo" value="1" checked form="enregistrement"> <?= e(t('Garder cette photo')) ?></label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<dl class="grille">
|
||||
<dt><?= e(t('Candidat')) ?></dt><dd><?= e($id['prenom'] . ' ' . mb_strtoupper($id['nom'])) ?></dd>
|
||||
<dt><?= e(t('Titre')) ?></dt><dd><?= e($profil['titre'] ?? '') ?: $vide ?></dd>
|
||||
<dt><?= e(t('Email')) ?></dt><dd><?= e($id['email'] ?? '') ?: $vide ?></dd>
|
||||
<dt><?= e(t('Téléphone')) ?></dt><dd><?= e($id['telephone'] ?? '') ?: $vide ?></dd>
|
||||
<dt><?= e(t('Adresse')) ?></dt><dd><?= e(implode(', ', $adr)) ?: '<em>' . e(t('non renseignée')) . '</em>' ?></dd>
|
||||
<?php $natsImport = codes_nationalites($id['nationalites'] ?? []); ?>
|
||||
<dt><?= e(t('Nationalité')) ?></dt><dd><?= $natsImport ? e(implode(', ', array_map('nom_pays', $natsImport))) : $vide ?></dd>
|
||||
<?php $dispoImport = isset($profil['disponibilite_code']) ? ['code' => $profil['disponibilite_code'], 'date' => $profil['disponibilite_date'] ?? null] : classer_disponibilite($profil['disponibilite'] ?? null); ?>
|
||||
<dt><?= e(t('Disponibilité')) ?></dt><dd><?= ($libelle = libelle_disponibilite(['disponibilite_code' => $dispoImport['code'], 'disponible_le' => $dispoImport['code'] === 'date' ? $dispoImport['date'] : date_disponibilite($dispoImport['code'], null)])) !== '' ? e($libelle) : $vide ?><?php if (!empty($profil['disponibilite'])): ?> <small>(<?= e($profil['disponibilite']) ?>)</small><?php endif; ?></dd>
|
||||
<dt><?= e(t('Séniorité')) ?></dt>
|
||||
<dd>
|
||||
<span class="badge badge-<?= e($seniorite['niveau']) ?>"><?= e(t(SENIORITES[$seniorite['niveau']])) ?></span>
|
||||
<?= e(t('%s, recalculés à partir des dates', annees($seniorite['annees']))) ?>
|
||||
</dd>
|
||||
<dt><?= e(t('Contenu')) ?></dt>
|
||||
<dd><?= e(implode(', ', [
|
||||
tn(count($profil['experiences']), '%d expérience', '%d expériences'),
|
||||
tn(count($profil['technologies']), '%d technologie', '%d technologies'),
|
||||
tn(count($profil['langues']), '%d langue', '%d langues'),
|
||||
tn(count($profil['formations'] ?? []), '%d formation', '%d formations'),
|
||||
tn(count($profil['certifications'] ?? []), '%d certification', '%d certifications'),
|
||||
])) ?></dd>
|
||||
<dt><?= e(t('Fichiers')) ?></dt>
|
||||
<dd><?= e($imp['cv']['nom']) ?><?= $imp['rapport'] ? ', ' . e($imp['rapport']['nom']) : '' ?></dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<?php if ($seniorite['ecart']): ?>
|
||||
<p class="flash flash-info">
|
||||
<?= e(t('Écart avec l\'analyse : le fichier annonce %s (%s). Vivier retient la valeur recalculée. Vérifiez les dates des expériences.',
|
||||
annees($seniorite['annees_json']), t(SENIORITES[$seniorite['niveau_json']]))) ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($profil['points_attention'])): ?>
|
||||
<h2><?= e(t('Points d\'attention')) ?></h2>
|
||||
<ul><?php foreach ($profil['points_attention'] as $p): ?><li><?= e($p) ?></li><?php endforeach; ?></ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2><?= e(t('Expériences')) ?></h2>
|
||||
<div class="defile"><table>
|
||||
<thead><tr><th><?= e(t('Période')) ?></th><th><?= e(t('Poste')) ?></th><th><?= e(t('Employeur ou client')) ?></th><th><?= e(t('Type')) ?></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($profil['experiences'] as $x): ?>
|
||||
<tr>
|
||||
<td><?= e($x['date_debut']) ?> → <?= e($x['date_fin'] ?? t('en cours')) ?></td>
|
||||
<td><?= e($x['poste']) ?></td>
|
||||
<td><?= e(implode(' / ', array_filter([$x['employeur'] ?? null, $x['client'] ?? null]))) ?></td>
|
||||
<td><?= e(t(TYPES_EXPERIENCE[$x['type']] ?? $x['type'])) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table></div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="submit" name="action" value="annuler" class="secondaire" form="enregistrement" formnovalidate><?= e(!empty($imp['lot']) ? t('Retour au lot') : t('Annuler')) ?></button>
|
||||
<?php if (!$bloquant): ?>
|
||||
<button type="submit" name="action" value="enregistrer" form="enregistrement"><?= e(t('Enregistrer le profil')) ?></button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="porte">
|
||||
<img src="assets/logo-itclub-or.png" alt="<?= e(t('IT Club')) ?>" width="224" height="70">
|
||||
<p class="porte-nom"><?= e(config('nom_app')) ?></p>
|
||||
<p class="porte-role"><?= e(t('Obtenir un accès')) ?></p>
|
||||
<div class="porte-texte">
|
||||
<p><?= e(t('Vivier ne propose pas d\'inscription libre. Le gestionnaire de Vivier vous envoie un lien d\'invitation personnel, valable 48 heures et utilisable une seule fois.')) ?></p>
|
||||
<p><?= e(t('Avec ce lien, vous choisissez votre identifiant et votre mot de passe, puis vous activez la double authentification sur votre téléphone.')) ?></p>
|
||||
<p><?= e(t('Pour recevoir un lien, adressez-vous au gestionnaire de Vivier chez IT Club.')) ?></p>
|
||||
</div>
|
||||
<p class="porte-lien">
|
||||
<a href="<?= e(url('connexion')) ?>"><?= e(t('Retour à la connexion')) ?></a>
|
||||
<span aria-hidden="true">·</span>
|
||||
<a href="<?= e(url('securite')) ?>"><?= e(t('Comment Vivier protège vos accès')) ?></a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
<div class="porte">
|
||||
<img src="assets/logo-itclub-or.png" alt="<?= e(t('IT Club')) ?>" width="224" height="70">
|
||||
<p class="porte-nom"><?= e(config('nom_app')) ?></p>
|
||||
<p class="porte-role"><?= e(t('Premier lancement : créez votre compte. Vous gérerez ensuite ceux des autres.')) ?></p>
|
||||
<form method="post" action="<?= e(url('installation')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<?php if ($erreur): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
<label><?= e(t('Votre nom')) ?>
|
||||
<input type="text" name="nom" required value="<?= e($_POST['nom'] ?? '') ?>" autocomplete="name">
|
||||
</label>
|
||||
<label><?= e(t('Identifiant de connexion')) ?>
|
||||
<input type="text" name="identifiant" required autocomplete="username" autocapitalize="none" spellcheck="false" value="<?= e($_POST['identifiant'] ?? '') ?>">
|
||||
</label>
|
||||
<label><?= e(t('Mot de passe, %d caractères au moins', LONGUEUR_MOT_DE_PASSE)) ?>
|
||||
<input type="password" name="mot_de_passe" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="new-password">
|
||||
</label>
|
||||
<label><?= e(t('Confirmation')) ?>
|
||||
<input type="password" name="confirmation" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="new-password">
|
||||
</label>
|
||||
<button type="submit"><?= e(t('Créer mon compte')) ?></button>
|
||||
</form>
|
||||
<p class="porte-note"><?= e(t('Étape suivante : la double authentification. Gardez votre téléphone à portée de main.')) ?></p>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<section class="carte etroit">
|
||||
<h1><?= e($titre) ?></h1>
|
||||
<p><a href="<?= e(url('kanban')) ?>"><?= e(t('Retour au Kanban')) ?></a></p>
|
||||
</section>
|
||||
@@ -0,0 +1,33 @@
|
||||
<div class="porte">
|
||||
<img src="assets/logo-itclub-or.png" alt="<?= e(t('IT Club')) ?>" width="224" height="70">
|
||||
<p class="porte-nom"><?= e(config('nom_app')) ?></p>
|
||||
<?php if (!$inv): ?>
|
||||
<p class="porte-role"><?= e(t('Lien d\'invitation invalide')) ?></p>
|
||||
<p class="flash flash-erreur" role="alert"><?= e(t('Ce lien a expiré, a déjà servi ou a été annulé. Demandez un nouveau lien au gestionnaire de Vivier.')) ?></p>
|
||||
<p class="porte-lien"><a href="<?= e(url('connexion')) ?>"><?= e(t('Retour à la connexion')) ?></a></p>
|
||||
<?php else: ?>
|
||||
<p class="porte-role"><?= e(t('Créez votre compte. Le lien sert une seule fois, jusqu\'au %s.', date_fr($inv['expire_le'], true))) ?></p>
|
||||
<form method="post" action="<?= e(url('invitation')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="jeton" value="<?= e($jeton) ?>">
|
||||
<?php if ($erreur): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
<label><?= e(t('Votre nom')) ?>
|
||||
<input type="text" name="nom" required maxlength="120" value="<?= e($_POST['nom'] ?? $inv['nom_affiche'] ?? '') ?>" autocomplete="name">
|
||||
</label>
|
||||
<label><?= e(t('Identifiant de connexion')) ?>
|
||||
<input type="text" name="identifiant" required maxlength="40" autocomplete="username" autocapitalize="none" spellcheck="false"
|
||||
placeholder="<?= e(t('prenom.nom')) ?>" value="<?= e($_POST['identifiant'] ?? '') ?>">
|
||||
<small class="aide-champ"><?= e(t('3 à 40 caractères : lettres sans accent, chiffres, point, tiret.')) ?></small>
|
||||
</label>
|
||||
<label><?= e(t('Mot de passe, %d caractères au moins', LONGUEUR_MOT_DE_PASSE)) ?>
|
||||
<input type="password" name="mot_de_passe" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="new-password">
|
||||
</label>
|
||||
<label><?= e(t('Confirmation')) ?>
|
||||
<input type="password" name="confirmation" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="new-password">
|
||||
</label>
|
||||
<button type="submit"><?= e(t('Créer mon compte')) ?></button>
|
||||
</form>
|
||||
<p class="porte-note"><?= e(t('Étape suivante : la double authentification. Gardez votre téléphone à portée de main.')) ?></p>
|
||||
<p class="porte-lien"><a href="<?= e(url('securite')) ?>"><?= e(t('Comment Vivier protège vos accès')) ?></a></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
<h1><?= e(t('Journal')) ?> <small><?= e(tn(count($lignes), '%d dernière action', '%d dernières actions')) ?></small></h1>
|
||||
|
||||
<form class="filtres" method="get" action="index.php" data-auto>
|
||||
<input type="hidden" name="p" value="journal">
|
||||
<label><?= e(t('Compte')) ?>
|
||||
<select name="utilisateur">
|
||||
<option value="0"><?= e(t('Tous')) ?></option>
|
||||
<?php foreach ($comptes as $u): ?><option value="<?= (int) $u['id'] ?>"<?= $qui === (int) $u['id'] ? ' selected' : '' ?>><?= e($u['nom_affiche']) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit" data-auto-masquer><?= e(t('Filtrer')) ?></button>
|
||||
</form>
|
||||
|
||||
<section class="carte">
|
||||
<?php if (!$lignes): ?>
|
||||
<p class="vide"><?= e(t('Aucune action enregistrée.')) ?></p>
|
||||
<?php else: ?>
|
||||
<div class="defile"><table>
|
||||
<thead><tr><th><?= e(t('Date')) ?></th><th><?= e(t('Compte')) ?></th><th><?= e(t('Profil')) ?></th><th><?= e(t('Action')) ?></th><th><?= e(t('Détail')) ?></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($lignes as $h): ?>
|
||||
<tr>
|
||||
<td><?= e(date_fr($h['cree_le'], true)) ?></td>
|
||||
<td><?= e($h['nom_affiche']) ?></td>
|
||||
<td>
|
||||
<?php if ($h['nom'] !== null && $h['supprime_le'] !== null): ?>
|
||||
<em><?= e(sprintf('ITC-%04d', $h['candidat_id'])) ?> · <?= e(t('à la corbeille')) ?></em>
|
||||
<?php elseif ($h['nom'] !== null): ?>
|
||||
<a href="<?= e(url('profil', ['id' => $h['candidat_id']])) ?>#historique"><?= e($h['prenom'] . ' ' . $h['nom']) ?></a>
|
||||
<?php elseif ($h['candidat_id'] !== null): ?><em><?= e(t('supprimé')) ?></em><?php endif; ?>
|
||||
</td>
|
||||
<td><?= e(t(ACTIONS_JOURNAL[$h['action']] ?? $h['action'])) ?></td>
|
||||
<td><?= e(detail_journal($h)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table></div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
$total = array_sum(array_map('count', $colonnes));
|
||||
$limiteTitre = date('Y-m-d', strtotime('+2 months'));
|
||||
?>
|
||||
<div class="barre-titre">
|
||||
<h1><?= e(t('Kanban')) ?> <small><?= e(tn($total, '%d profil', '%d profils')) ?></small></h1>
|
||||
<form class="filtres" id="filtre-kanban" role="search">
|
||||
<label><?= e(t('Filtrer les cartes')) ?>
|
||||
<input type="search" id="filtre-texte" placeholder="<?= e(t('Nom, titre ou technologie')) ?>" autocomplete="off">
|
||||
</label>
|
||||
<label><?= e(t('Séniorité')) ?>
|
||||
<select id="filtre-seniorite">
|
||||
<option value=""><?= e(t('Toutes')) ?></option>
|
||||
<?php foreach (SENIORITES as $code => $libelle): ?><option value="<?= e($code) ?>"><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="kanban" data-modifiable="<?= peut_ecrire() ? '1' : '0' ?>" data-url="<?= e(url('statut')) ?>">
|
||||
<?php foreach (STATUTS as $code => $libelle): ?>
|
||||
<section class="colonne colonne-<?= e($code) ?>">
|
||||
<h2><?= echelle($code) ?><span class="nom-colonne"><?= e(t($libelle)) ?></span> <span class="compteur"><?= count($colonnes[$code]) ?></span></h2>
|
||||
<ul class="cartes" data-statut="<?= e($code) ?>" data-vide="<?= e(t('Aucun profil')) ?>" aria-label="<?= e(t($libelle)) ?>">
|
||||
<?php foreach ($colonnes[$code] as $c): ?>
|
||||
<li class="fiche" data-id="<?= (int) $c['id'] ?>" data-seniorite="<?= e($c['seniorite']) ?>">
|
||||
<div class="fiche-tete">
|
||||
<?= avatar($c) ?>
|
||||
<div>
|
||||
<a href="<?= e(url('profil', ['id' => $c['id']])) ?>"><?= e($c['prenom'] . ' ' . $c['nom']) ?></a>
|
||||
<?php if ($c['titre']): ?><p class="poste"><?= e($c['titre']) ?></p><?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<p class="meta">
|
||||
<span class="badge badge-<?= e($c['seniorite']) ?>"><?= e(t(SENIORITES[$c['seniorite']])) ?></span>
|
||||
<?= e(annees($c['annees_experience'])) ?>
|
||||
</p>
|
||||
<?php if ($c['technologies']): ?>
|
||||
<p class="technos-ligne"><?= e(implode(', ', $c['technologies'])) ?></p>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
// Titre de séjour qui expire dans les 2 mois, ou déjà expiré (profils sans nationalité UE, EEE ou suisse)
|
||||
$titre = $c['permis_expire_le'] !== null && ($c['hors_ue'] === null || (int) $c['hors_ue'] === 1) && $c['permis_expire_le'] <= $limiteTitre
|
||||
? ($c['permis_expire_le'] < date('Y-m-d') ? t('Titre de séjour expiré') : t('Titre de séjour : expire le %s', date_fr($c['permis_expire_le'])))
|
||||
: null;
|
||||
?>
|
||||
<?php if ($c['prochain_entretien'] || $c['relances_dues'] || $titre): ?>
|
||||
<p class="reperes">
|
||||
<?php if ($c['prochain_entretien']): ?><span><?= e(t('Entretien le %s', date_fr($c['prochain_entretien'], !str_ends_with($c['prochain_entretien'], '00:00:00')))) ?></span><?php endif; ?>
|
||||
<?php if ($c['relances_dues']): ?><span class="retard"><?= e(tn((int) $c['relances_dues'], '%d relance à faire', '%d relances à faire')) ?></span><?php endif; ?>
|
||||
<?php if ($titre): ?><span class="retard"><?= e($titre) ?></span><?php endif; ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if ($code === 'non_retenu' && $c['motif_refus']): ?>
|
||||
<p class="motif"><?= e($c['motif_refus']) ?></p>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<dialog id="dialogue-motif">
|
||||
<form method="dialog" class="pile">
|
||||
<h2><?= e(t('Motif du refus')) ?></h2>
|
||||
<label><?= e(t('Pourquoi ce profil n\'est pas retenu ?')) ?>
|
||||
<textarea name="motif" rows="3" maxlength="500" required></textarea>
|
||||
</label>
|
||||
<div class="actions">
|
||||
<button type="button" class="secondaire" value="annuler"><?= e(t('Annuler')) ?></button>
|
||||
<button type="submit" value="valider"><?= e(t('Enregistrer')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
<p id="annonce" class="flash flash-erreur" role="alert" hidden></p>
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="porte">
|
||||
<img src="assets/logo-itclub-or.png" alt="<?= e(t('IT Club')) ?>" width="224" height="70">
|
||||
<p class="porte-nom"><?= e(config('nom_app')) ?></p>
|
||||
<p class="porte-role"><?= e(t('Le gestionnaire vous a donné ce mot de passe. Choisissez le vôtre : lui seul le connaîtra.')) ?></p>
|
||||
<form method="post" action="<?= e(url('nouveau_mot_de_passe')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<?php if ($erreur): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
<label><?= e(t('Nouveau mot de passe, %d caractères au moins', LONGUEUR_MOT_DE_PASSE)) ?>
|
||||
<input type="password" name="nouveau" required autofocus minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="new-password">
|
||||
</label>
|
||||
<label><?= e(t('Confirmation')) ?>
|
||||
<input type="password" name="confirmation" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="new-password">
|
||||
</label>
|
||||
<button type="submit"><?= e(t('Continuer')) ?></button>
|
||||
</form>
|
||||
<p class="porte-note"><?= e(t('Une phrase de plusieurs mots se retient mieux qu\'un mélange de signes. Vivier refuse les mots de passe connus dans des fuites de données.')) ?></p>
|
||||
</div>
|
||||
+762
@@ -0,0 +1,762 @@
|
||||
<?php
|
||||
/*
|
||||
* Fiche d'un profil.
|
||||
* Un bandeau fixe (identité, statut, repères commerciaux, choses à faire), puis quatre onglets :
|
||||
* Profil : qui est le candidat (lecture)
|
||||
* Suivi : entretiens, relances, commentaires (le travail de l'équipe)
|
||||
* Missions : données commerciales et besoins clients
|
||||
* Dossier : documents, mots-clés, photo, données personnelles, historique, gestion
|
||||
* Sans JavaScript, les quatre onglets s'affichent l'un sous l'autre.
|
||||
*/
|
||||
$fr = static fn ($n): string => $n === null ? '' : nombre($n);
|
||||
$mois = static fn (?string $d): string => $d ? (new DateTimeImmutable($d))->format('m/Y') : t('en cours');
|
||||
$adresse = implode(', ', array_filter([$c['rue'], trim(($c['code_postal'] ?? '') . ' ' . ($c['ville'] ?? '')), $c['pays']]));
|
||||
$taille = static fn (int $o): string => $o >= 1048576 ? number_format($o / 1048576, 1, ',', '') . ' Mo' : max(1, (int) round($o / 1024)) . ' Ko';
|
||||
$categories = array_map('t', [
|
||||
'langage' => 'Langage', 'framework' => 'Framework', 'base_de_donnees' => 'Base de données', 'outil' => 'Outil',
|
||||
'cloud' => 'Cloud', 'methode' => 'Méthode', 'autre' => 'Autre',
|
||||
]);
|
||||
$archive = $c['archive_le'] !== null;
|
||||
$dateHeure = static fn (string $d): string => date_fr($d, !str_ends_with($d, '00:00:00'));
|
||||
$aujourdhui = date('Y-m-d');
|
||||
$nonRenseigne = static fn (string $texte = 'non renseigné'): string => '<em>' . e(t($texte)) . '</em>';
|
||||
|
||||
// Ce qui demande une action : alimente la ligne « À faire » du bandeau et les pastilles des onglets
|
||||
$relancesOuvertes = array_values(array_filter($c['relances'], static fn ($r) => $r['fait_le'] === null));
|
||||
$relancesEchues = array_filter($relancesOuvertes, static fn ($r) => $r['echeance'] <= $aujourdhui);
|
||||
$entretiensAVenir = array_filter($c['entretiens'], static fn ($x) => substr($x['date_entretien'], 0, 10) >= $aujourdhui);
|
||||
$prochainEntretien = $entretiensAVenir ? min(array_column($entretiensAVenir, 'date_entretien')) : null;
|
||||
$avisADonner = array_filter($c['entretiens'], static fn ($x) => substr($x['date_entretien'], 0, 10) < $aujourdhui && !$x['avis']);
|
||||
$conservationDepassee = $c['conserver_jusqu_au'] !== null && $c['conserver_jusqu_au'] < $aujourdhui;
|
||||
$aSuivre = count($relancesEchues) + count($avisADonner);
|
||||
|
||||
// Disponibilité : la valeur choisie dans la liste ; à défaut, le texte du CV
|
||||
$disponibilite = libelle_disponibilite($c) ?: (string) $c['disponibilite'];
|
||||
|
||||
// Permis de travail : non requis avec une nationalité UE, EEE ou suisse ; sinon le texte saisi et la date de fin du titre
|
||||
$nationalites = implode(', ', array_map(static fn ($code) => nom_pays($code), $c['nationalites_codes']));
|
||||
$permisLibre = $c['hors_ue'] !== null && (int) $c['hors_ue'] === 0;
|
||||
$titreExpire = !$permisLibre && $c['permis_expire_le'] !== null && $c['permis_expire_le'] < $aujourdhui;
|
||||
$titreBientot = !$permisLibre && $c['permis_expire_le'] !== null && !$titreExpire && $c['permis_expire_le'] <= date('Y-m-d', strtotime('+2 months'));
|
||||
|
||||
// Traduction du contenu dans la langue de l'interface
|
||||
$tr = $c['traduction'];
|
||||
$langueNom = ['fr' => t('en français'), 'en' => t('en anglais')][langue_courante()] ?? langue_courante();
|
||||
|
||||
// Repli : les plus récents d'abord, le reste se déplie
|
||||
$nbExperiences = 3;
|
||||
$nbTechnologies = 10;
|
||||
$experiencesVisibles = array_slice($c['experiences'], 0, $nbExperiences);
|
||||
$experiencesAnciennes = array_slice($c['experiences'], $nbExperiences);
|
||||
$technologiesVisibles = array_slice($c['technologies'], 0, $nbTechnologies);
|
||||
$technologiesAutres = array_slice($c['technologies'], $nbTechnologies);
|
||||
|
||||
$onglets = [
|
||||
'profil' => ['libelle' => t('Profil'), 'pastille' => 0, 'titre' => ''],
|
||||
'suivi' => ['libelle' => t('Suivi'), 'pastille' => $aSuivre, 'titre' => t('Relances en retard et avis à donner')],
|
||||
'missions' => ['libelle' => t('Missions'), 'pastille' => 0, 'titre' => ''],
|
||||
'dossier' => ['libelle' => t('Dossier'), 'pastille' => $conservationDepassee ? 1 : 0, 'titre' => t('Conservation dépassée')],
|
||||
];
|
||||
$compteurs = ['suivi' => count($c['entretiens']) + count($c['commentaires']), 'missions' => count($c['besoins']), 'dossier' => count($c['documents'])];
|
||||
|
||||
/** Une expérience professionnelle. */
|
||||
$experience = static function (array $x) use ($mois): void { ?>
|
||||
<article class="experience">
|
||||
<p class="periode"><?= e($mois($x['date_debut'])) ?> → <?= e($mois($x['date_fin'])) ?> · <?= e(t(TYPES_EXPERIENCE[$x['type']] ?? $x['type'])) ?></p>
|
||||
<h3><?= e($x['poste']) ?></h3>
|
||||
<p class="employeur">
|
||||
<?= e(implode(' · ', array_filter([$x['employeur'], $x['client'] ? t('client : %s', $x['client']) : null, $x['lieu']]))) ?>
|
||||
</p>
|
||||
<?php if ($x['projet'] || $x['charge']): ?>
|
||||
<p class="cadre-mission"><?= e(implode(' · ', array_filter([$x['projet'] ? t('Projet : %s', $x['projet']) : null, $x['charge'] ? t('Charge : %s', $x['charge']) : null]))) ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if ($x['mission']): ?><p class="mission"><?= nl2br(e($x['mission'])) ?></p><?php endif; ?>
|
||||
<?php if ($x['description']): ?><p><?= nl2br(e($x['description'])) ?></p><?php endif; ?>
|
||||
<?php if ($x['technologies']): ?>
|
||||
<p class="technos"><?php foreach (explode(', ', $x['technologies']) as $t): ?><span><?= e($t) ?></span><?php endforeach; ?></p>
|
||||
<?php endif; ?>
|
||||
</article>
|
||||
<?php };
|
||||
|
||||
/** Une ligne du tableau des technologies. */
|
||||
$technologie = static function (array $t) use ($categories, $fr): void { ?>
|
||||
<tr><td><?= e($t['nom']) ?></td><td><?= e($categories[$t['categorie']]) ?></td><td class="nombre"><?= e($fr($t['annees'])) ?></td></tr>
|
||||
<?php };
|
||||
|
||||
/** Champs du formulaire d'un entretien, pour l'ajout comme pour la modification. */
|
||||
$champsEntretien = static function (?array $x) use ($besoinsOuverts): void {
|
||||
$besoins = $besoinsOuverts;
|
||||
// Un entretien garde son besoin même s'il n'est plus ouvert
|
||||
if ($x && $x['besoin_id'] && !in_array((int) $x['besoin_id'], array_map('intval', array_column($besoins, 'id')), true)) {
|
||||
$besoins[] = ['id' => $x['besoin_id'], 'client' => $x['besoin_client'], 'intitule' => $x['besoin_intitule']];
|
||||
}
|
||||
?>
|
||||
<div class="champs">
|
||||
<label><?= e(t('Date')) ?> <input type="date" name="date" required value="<?= e($x ? substr($x['date_entretien'], 0, 10) : '') ?>"></label>
|
||||
<label><?= e(t('Heure, facultative')) ?> <input type="time" name="heure" value="<?= e($x && !str_ends_with($x['date_entretien'], '00:00:00') ? substr($x['date_entretien'], 11, 5) : '') ?>"></label>
|
||||
<label><?= e(t('Type')) ?>
|
||||
<select name="type">
|
||||
<?php foreach (TYPES_ENTRETIEN as $code => $libelle): ?><option value="<?= e($code) ?>"<?= ($x['type'] ?? 'visio') === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Mené par')) ?> <input type="text" name="interlocuteur" maxlength="150" value="<?= e($x['interlocuteur'] ?? '') ?>"></label>
|
||||
<?php if ($besoins): ?>
|
||||
<label class="large"><?= e(t('Besoin client concerné')) ?>
|
||||
<select name="besoin_id">
|
||||
<option value="0"><?= e(t('Aucun')) ?></option>
|
||||
<?php foreach ($besoins as $b): ?><option value="<?= (int) $b['id'] ?>"<?= (int) ($x['besoin_id'] ?? 0) === (int) $b['id'] ? ' selected' : '' ?>><?= e($b['client'] . ' : ' . $b['intitule']) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<?php endif; ?>
|
||||
<label><?= e(t('Avis')) ?>
|
||||
<select name="avis">
|
||||
<option value=""><?= e(t('Pas encore d\'avis')) ?></option>
|
||||
<?php foreach (AVIS_ENTRETIEN as $code => $libelle): ?><option value="<?= e($code) ?>"<?= ($x['avis'] ?? '') === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label class="large"><?= e(t('Compte rendu')) ?>
|
||||
<textarea name="compte_rendu" rows="4" maxlength="10000"><?= e($x['compte_rendu'] ?? '') ?></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<?php
|
||||
};
|
||||
?>
|
||||
<p class="fil">
|
||||
<?php if ($archive): ?>
|
||||
<a href="<?= e(url('recherche', ['archives' => 'seuls'])) ?>"><?= e(t('Profils archivés')) ?></a> / <?= e(t(STATUTS[$c['statut']])) ?>
|
||||
<?php else: ?>
|
||||
<a href="<?= e(url('kanban')) ?>"><?= e(t('Kanban')) ?></a> / <?= e(t(STATUTS[$c['statut']])) ?>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<!-- Bandeau : visible quel que soit l'onglet -->
|
||||
<div class="entete-profil">
|
||||
<div class="identite-profil">
|
||||
<?= avatar($c, 'grand') ?>
|
||||
<?= echelle($c['statut'], 'grande') ?>
|
||||
<div>
|
||||
<h1><?= e($c['prenom'] . ' ' . $c['nom']) ?></h1>
|
||||
<p class="sous-titre">
|
||||
<?php if ($c['titre']): ?><span><?= e($c['titre']) ?></span><?php endif; ?>
|
||||
<span class="badge badge-<?= e($c['seniorite']) ?>"><?= e(t(SENIORITES[$c['seniorite']])) ?></span>
|
||||
<span><?= e(t('%s d\'expérience', annees($c['annees_experience']))) ?></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="exports">
|
||||
<?php if ($c['cv_origine']): ?>
|
||||
<a class="bouton secondaire" href="<?= e(url('document', ['id' => $c['cv_origine']])) ?>"><?= e(t('CV d\'origine')) ?></a>
|
||||
<?php endif; ?>
|
||||
<details class="menu-deroulant">
|
||||
<summary class="bouton"><?= e(t('CV IT Club')) ?></summary>
|
||||
<div class="menu-deroulant-liste">
|
||||
<a href="<?= e(url('cv', ['id' => $c['id']])) ?>" target="_blank" rel="noopener"><?= e(t('Nominatif')) ?></a>
|
||||
<a href="<?= e(url('cv', ['id' => $c['id'], 'anonyme' => 1])) ?>" target="_blank" rel="noopener"><?= e(t('Anonymisé')) ?></a>
|
||||
</div>
|
||||
</details>
|
||||
<details class="menu-deroulant">
|
||||
<summary class="bouton secondaire"><?= e(t('CV Europass')) ?></summary>
|
||||
<div class="menu-deroulant-liste">
|
||||
<a href="<?= e(url('europass', ['id' => $c['id']])) ?>" target="_blank" rel="noopener"><?= e(t('Nominatif')) ?></a>
|
||||
<a href="<?= e(url('europass', ['id' => $c['id'], 'anonyme' => 1])) ?>" target="_blank" rel="noopener"><?= e(t('Anonymisé')) ?></a>
|
||||
</div>
|
||||
</details>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<a class="bouton secondaire" href="<?= e(url('profil_modifier', ['id' => $c['id']])) ?>"><?= e(t('Modifier')) ?></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($archive): ?>
|
||||
<div class="flash flash-info bandeau-archive">
|
||||
<p><strong><?= e(t('Profil archivé le %s.', date_fr($c['archive_le']))) ?></strong> <?= e(t('Il n\'apparaît plus dans le Kanban ni dans les chiffres. Ses données et ses documents sont conservés.')) ?></p>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('archiver')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<button type="submit" name="action" value="desarchiver" class="secondaire"><?= e(t('Désarchiver')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($original) && $tr['etat'] === 'a_jour'): ?>
|
||||
<p class="flash flash-info bandeau-traduction"><?= e(t('Vous lisez le texte d\'origine de ce profil.')) ?> <a href="<?= e(url('profil', ['id' => $c['id']])) ?>"><?= e(t('Voir la traduction %s', $langueNom)) ?></a></p>
|
||||
<?php elseif ($tr['etat'] === 'absente' || $tr['etat'] === 'perimee'): ?>
|
||||
<div class="flash flash-info bandeau-traduction">
|
||||
<p><?= e($tr['etat'] === 'perimee'
|
||||
? t('La fiche a changé depuis sa traduction : le texte s\'affiche dans sa langue d\'origine.')
|
||||
: t('Ce profil n\'est pas encore traduit %s : le texte s\'affiche dans sa langue d\'origine.', $langueNom)) ?>
|
||||
<?php if ($enFile && $enFile['essais'] < TRADUCTION_ESSAIS && Claude::configure()): ?><?= e(t('La traduction se fait en arrière-plan.')) ?><?php endif; ?></p>
|
||||
<?php if (peut_ecrire() && Claude::configure()): ?>
|
||||
<form method="post" action="<?= e(url('traduire_profil')) ?>" data-attente="<?= e(t('Traduction en cours…')) ?>" data-attente-bouton="<?= e(t('Traduction en cours…')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<input type="hidden" name="langue" value="<?= e(langue_courante()) ?>">
|
||||
<button type="submit" class="secondaire"><?= e(t('Traduire maintenant')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php elseif ($tr['etat'] === 'a_jour'): ?>
|
||||
<p class="note-traduction"><?= e($tr['origine'] === 'automatique' ? t('Texte traduit automatiquement le %s.', date_fr($tr['cree_le'])) : t('Texte traduit le %s.', date_fr($tr['cree_le']))) ?>
|
||||
<a href="<?= e(url('profil', ['id' => $c['id'], 'original' => 1])) ?>"><?= e(t('Voir l\'original')) ?></a></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="barre-statut">
|
||||
<div class="barre-statut-ligne">
|
||||
<?php if (peut_ecrire() && !$archive): ?>
|
||||
<form method="post" action="<?= e(url('statut')) ?>" class="statut-form">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<label><?= e(t('Statut')) ?>
|
||||
<select name="statut" data-motif="champ-motif">
|
||||
<?php foreach (STATUTS as $code => $libelle): ?>
|
||||
<option value="<?= e($code) ?>"<?= $code === $c['statut'] ? ' selected' : '' ?>><?= e(t($libelle)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label id="champ-motif"<?= $c['statut'] === 'non_retenu' ? '' : ' hidden' ?>><?= e(t('Motif du refus')) ?>
|
||||
<input type="text" name="motif" maxlength="500" value="<?= e($c['motif_refus']) ?>">
|
||||
</label>
|
||||
<button type="submit"><?= e(t('Mettre à jour')) ?></button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<p class="statut-lecture"><span class="etat"><?= e(t(STATUTS[$c['statut']])) ?></span></p>
|
||||
<?php endif; ?>
|
||||
<dl class="reperes-profil">
|
||||
<div><dt><?= e(t('Disponibilité')) ?></dt><dd><?= $disponibilite !== '' ? e($disponibilite) : $nonRenseigne('non renseignée') ?></dd></div>
|
||||
<div><dt><?= e(t('TJM souhaité')) ?></dt><dd><?= e(euros($c['tjm'])) ?: $nonRenseigne() ?></dd></div>
|
||||
<?php if ($c['ville'] || $c['pays']): ?>
|
||||
<div><dt><?= e(t('Lieu')) ?></dt><dd><?= e($c['ville'] ?: $c['pays']) ?></dd></div>
|
||||
<?php endif; ?>
|
||||
</dl>
|
||||
</div>
|
||||
<?php // Le formulaire montre déjà le motif ; en lecture seule (profil archivé), on l'affiche ici
|
||||
if ($c['statut'] === 'non_retenu' && $c['motif_refus'] && !(peut_ecrire() && !$archive)): ?>
|
||||
<p class="motif-profil"><strong><?= e(t('Motif du refus')) ?></strong> : <?= e($c['motif_refus']) ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if ($relancesEchues || $prochainEntretien || $avisADonner || $conservationDepassee || $titreExpire || $titreBientot): ?>
|
||||
<ul class="a-faire" aria-label="<?= e(t('À faire')) ?>">
|
||||
<?php if ($relancesEchues): ?>
|
||||
<li class="urgent"><a href="#relances"><?= e(tn(count($relancesEchues), '%d relance en retard', '%d relances en retard')) ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if ($avisADonner): ?>
|
||||
<li class="urgent"><a href="#entretiens"><?= e(tn(count($avisADonner), '%d avis d\'entretien à donner', '%d avis d\'entretien à donner')) ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if ($prochainEntretien): ?>
|
||||
<li><a href="#entretiens"><?= e(t('Prochain entretien : %s', $dateHeure($prochainEntretien))) ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if ($conservationDepassee): ?>
|
||||
<li class="urgent"><a href="#donnees"><?= e(t('Conservation dépassée depuis le %s', date_fr($c['conserver_jusqu_au']))) ?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if ($titreExpire || $titreBientot): ?>
|
||||
<li class="urgent"><a href="#coordonnees"><?= e($titreExpire ? t('Titre de séjour expiré le %s', date_fr($c['permis_expire_le'])) : t('Titre de séjour : expire le %s', date_fr($c['permis_expire_le']))) ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<nav class="onglets" aria-label="<?= e(t('Sections de la fiche')) ?>" data-onglets>
|
||||
<?php foreach ($onglets as $cle => $o): ?>
|
||||
<a href="#<?= e($cle) ?>" id="tab-<?= e($cle) ?>" data-onglet="<?= e($cle) ?>">
|
||||
<?= e($o['libelle']) ?>
|
||||
<?php if ($o['pastille']): ?>
|
||||
<span class="pastille pastille-alerte" title="<?= e($o['titre']) ?>"><?= $cle === 'dossier' ? '!' : (int) $o['pastille'] ?></span>
|
||||
<?php elseif (!empty($compteurs[$cle])): ?>
|
||||
<small><?= (int) $compteurs[$cle] ?></small>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<!-- Onglet Profil : qui est le candidat -->
|
||||
<section class="panneau" id="profil" aria-labelledby="tab-profil">
|
||||
<h2 class="titre-panneau"><?= e(t('Profil')) ?></h2>
|
||||
<div class="deux-colonnes">
|
||||
<div>
|
||||
<?php if ($c['resume']): ?>
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Synthèse')) ?></h2>
|
||||
<p><?= nl2br(e($c['resume'])) ?></p>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($c['points_attention']): ?>
|
||||
<section class="carte attention">
|
||||
<h2><?= e(t('Points d\'attention')) ?></h2>
|
||||
<ul><?php foreach ($c['points_attention'] as $p): ?><li><?= e($p) ?></li><?php endforeach; ?></ul>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="carte" id="experiences">
|
||||
<h2><?= e(t('Expériences')) ?> <small><?= count($c['experiences']) ?></small></h2>
|
||||
<?php array_map($experience, $experiencesVisibles); ?>
|
||||
<?php if ($experiencesAnciennes): ?>
|
||||
<details class="suite">
|
||||
<summary><?= e(tn(count($experiencesAnciennes), 'Voir %d expérience plus ancienne', 'Voir %d expériences plus anciennes')) ?></summary>
|
||||
<?php array_map($experience, $experiencesAnciennes); ?>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
<?php if (!$c['experiences']): ?><p class="vide"><?= e(t('Aucune expérience renseignée.')) ?></p><?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="technologies">
|
||||
<h2><?= e(t('Technologies')) ?> <small><?= count($c['technologies']) ?></small></h2>
|
||||
<?php if ($c['technologies']): ?>
|
||||
<table class="compact">
|
||||
<thead><tr><th><?= e(t('Technologie')) ?></th><th><?= e(t('Catégorie')) ?></th><th class="nombre"><?= e(t('Années')) ?></th></tr></thead>
|
||||
<tbody><?php array_map($technologie, $technologiesVisibles); ?></tbody>
|
||||
</table>
|
||||
<?php if ($technologiesAutres): ?>
|
||||
<details class="suite">
|
||||
<summary><?= e(tn(count($technologiesAutres), 'Voir %d autre technologie', 'Voir %d autres technologies')) ?></summary>
|
||||
<table class="compact"><tbody><?php array_map($technologie, $technologiesAutres); ?></tbody></table>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
<?php else: ?><p class="vide"><?= e(t('Aucune technologie renseignée.')) ?></p><?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if ($c['formations'] || $c['certifications']): ?>
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Formation et certifications')) ?></h2>
|
||||
<ul class="simple">
|
||||
<?php foreach ($c['formations'] as $f): ?>
|
||||
<?php $dates = trim(date_europass($f['date_debut'] ?? null) . (($f['date_debut'] ?? null) && ($f['date_fin'] ?? null) ? ' – ' : '') . date_europass($f['date_fin'] ?? null)) ?: ($f['annee_fin'] ? (string) (int) $f['annee_fin'] : ''); ?>
|
||||
<li><strong><?= e($f['diplome']) ?></strong><?= $f['etablissement'] ? ', ' . e($f['etablissement']) : '' ?><?= $dates !== '' || ($f['lieu'] ?? '') ? ' <small>' . e(implode(' · ', array_filter([$dates, $f['lieu'] ?? null]))) . '</small>' : '' ?></li>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($c['certifications'] as $f): ?>
|
||||
<li><strong><?= e($f['nom']) ?></strong><?= $f['organisme'] ? ', ' . e($f['organisme']) : '' ?><?= $f['annee'] ? ' (' . (int) $f['annee'] . ')' : '' ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<aside>
|
||||
<section class="carte" id="coordonnees">
|
||||
<h2><?= e(t('Coordonnées')) ?></h2>
|
||||
<dl class="grille serre">
|
||||
<dt><?= e(t('Email')) ?></dt><dd><?= $c['email'] ? '<a href="mailto:' . e($c['email']) . '">' . e($c['email']) . '</a>' : $nonRenseigne() ?></dd>
|
||||
<dt><?= e(t('Téléphone')) ?></dt><dd><?= e($c['telephone']) ?: $nonRenseigne() ?></dd>
|
||||
<dt><?= e(t('Adresse')) ?></dt><dd><?= e($adresse) ?: $nonRenseigne('non renseignée') ?></dd>
|
||||
<dt><?= e(t('Nationalité')) ?></dt><dd><?= $nationalites !== '' ? e($nationalites) : $nonRenseigne('non renseignée') ?></dd>
|
||||
<dt><?= e(t('Permis de travail')) ?></dt>
|
||||
<dd><?php if ($permisLibre): ?><?= e(t('Non requis')) ?> <small>(<?= e(t('UE, EEE ou Suisse')) ?>)</small>
|
||||
<?php elseif ($c['permis_travail'] || $c['permis_expire_le']): ?><?= e((string) $c['permis_travail']) ?><?php if ($c['permis_expire_le']): ?><br><small class="<?= $titreExpire || $titreBientot ? 'retard' : '' ?>"><?= e(t('Titre valable jusqu\'au %s', date_fr($c['permis_expire_le']))) ?></small><?php endif; ?>
|
||||
<?php else: ?><?= $nonRenseigne($c['hors_ue'] === null ? 'à vérifier' : 'non renseigné') ?><?php endif; ?></dd>
|
||||
</dl>
|
||||
<?php if ($c['liens']): ?>
|
||||
<ul class="simple">
|
||||
<?php foreach ($c['liens'] as $l): ?>
|
||||
<li><a href="<?= e($l['url']) ?>" target="_blank" rel="noopener noreferrer"><?= e(ucfirst($l['type'])) ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="langues">
|
||||
<h2><?= e(t('Langues')) ?></h2>
|
||||
<ul class="simple">
|
||||
<?php foreach ($c['langues'] as $l): $grille = array_filter(array_intersect_key($l, GRILLE_CECRL)); ?>
|
||||
<li><?= e(t($l['langue'])) ?><?= $l['niveau'] ? ' · ' . e(t($l['niveau'])) : '' ?>
|
||||
<?php if ($grille): ?><small><?= e(implode(' · ', array_map(static fn ($k, $v) => t(GRILLE_CECRL[$k]) . ' ' . $v, array_keys($grille), $grille))) ?></small><?php endif; ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php if (!$c['langues']): ?><p class="vide"><?= e(t('Aucune langue renseignée.')) ?></p><?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if ($c['benevolat'] || $c['permis_conduire']): ?>
|
||||
<section class="carte" id="divers">
|
||||
<h2><?= e(t('Bénévolat et permis')) ?></h2>
|
||||
<dl class="grille serre">
|
||||
<?php if ($c['benevolat']): ?><dt><?= e(t('Bénévolat')) ?></dt><dd><?= nl2br(e($c['benevolat'])) ?></dd><?php endif; ?>
|
||||
<?php if ($c['permis_conduire']): ?><dt><?= e(t('Permis de conduire')) ?></dt><dd><?= e($c['permis_conduire']) ?></dd><?php endif; ?>
|
||||
</dl>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Onglet Suivi : le travail de l'équipe sur ce candidat -->
|
||||
<section class="panneau" id="suivi" aria-labelledby="tab-suivi">
|
||||
<h2 class="titre-panneau"><?= e(t('Suivi')) ?></h2>
|
||||
<div class="deux-colonnes">
|
||||
<div>
|
||||
<section class="carte" id="entretiens">
|
||||
<h2><?= e(t('Entretiens')) ?> <small><?= count($c['entretiens']) ?></small></h2>
|
||||
<?php foreach ($c['entretiens'] as $x): $futur = substr($x['date_entretien'], 0, 10) >= $aujourdhui; ?>
|
||||
<article class="entretien">
|
||||
<p class="periode">
|
||||
<?= e($dateHeure($x['date_entretien'])) ?> · <?= e(t(TYPES_ENTRETIEN[$x['type']])) ?>
|
||||
<?php if ($futur): ?><span class="badge"><?= e(t('à venir')) ?></span><?php endif; ?>
|
||||
<?php if ($x['avis']): ?><span class="badge badge-avis-<?= e($x['avis']) ?>"><?= e(t(AVIS_ENTRETIEN[$x['avis']])) ?></span><?php elseif (!$futur): ?><span class="badge badge-attente"><?= e(t('avis à donner')) ?></span><?php endif; ?>
|
||||
</p>
|
||||
<p class="employeur"><?= e(implode(' · ', array_filter([
|
||||
$x['interlocuteur'] ? t('mené par %s', $x['interlocuteur']) : null,
|
||||
$x['besoin_intitule'] ? $x['besoin_client'] . ' : ' . $x['besoin_intitule'] : null,
|
||||
t('saisi par %s', $x['nom_affiche']),
|
||||
]))) ?></p>
|
||||
<?php if ($x['compte_rendu']): ?><p><?= nl2br(e($x['compte_rendu'])) ?></p><?php endif; ?>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<details>
|
||||
<summary><?= e($x['avis'] ? t('Modifier') : t('Donner l\'avis et le compte rendu')) ?></summary>
|
||||
<form method="post" action="<?= e(url('entretien')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="entretien_id" value="<?= (int) $x['id'] ?>">
|
||||
<?php $champsEntretien($x); ?>
|
||||
<div class="actions">
|
||||
<button type="submit" name="action" value="modifier" class="secondaire"><?= e(t('Enregistrer')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
<form method="post" action="<?= e(url('entretien')) ?>" class="actions-ligne" data-confirmer="<?= e(t('Supprimer cet entretien ?')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="entretien_id" value="<?= (int) $x['id'] ?>">
|
||||
<button type="submit" name="action" value="supprimer" class="lien"><?= e(t('Supprimer cet entretien')) ?></button>
|
||||
</form>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
<?php if (!$c['entretiens']): ?><p class="vide"><?= e(t('Aucun entretien enregistré.')) ?></p><?php endif; ?>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<details class="ajout">
|
||||
<summary><?= e(t('Ajouter un entretien')) ?></summary>
|
||||
<form method="post" action="<?= e(url('entretien')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<?php $champsEntretien(null); ?>
|
||||
<?php if (!$archive && in_array($c['statut'], ['recu', 'en_cours'], true)): ?>
|
||||
<label class="case"><input type="checkbox" name="avancer" value="1" checked> <?= e(t('Passer le profil en « Entretiens en cours »')) ?></label>
|
||||
<?php endif; ?>
|
||||
<div class="actions">
|
||||
<button type="submit" name="action" value="ajouter"><?= e(t('Enregistrer l\'entretien')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="commentaires">
|
||||
<h2><?= e(t('Commentaires')) ?> <small><?= count($c['commentaires']) ?></small></h2>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('commentaire')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<label><?= e(t('Nouveau commentaire')) ?>
|
||||
<textarea name="texte" rows="2" required></textarea>
|
||||
</label>
|
||||
<div class="actions gauche"><button type="submit" class="secondaire"><?= e(t('Publier')) ?></button></div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($c['commentaires'] as $m): ?>
|
||||
<article class="commentaire">
|
||||
<p class="auteur"><?= e($m['nom_affiche']) ?> · <?= e(date_fr($m['cree_le'], true)) ?></p>
|
||||
<p><?= nl2br(e($m['texte'])) ?></p>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
<?php if (!$c['commentaires']): ?><p class="vide"><?= e(t('Aucun commentaire.')) ?></p><?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<aside>
|
||||
<section class="carte" id="relances">
|
||||
<h2><?= e(t('Relances')) ?> <small><?= count($relancesOuvertes) ?></small></h2>
|
||||
<ul class="simple relances">
|
||||
<?php foreach ($c['relances'] as $r): $faite = $r['fait_le'] !== null; ?>
|
||||
<li class="<?= $faite ? 'faite' : '' ?>">
|
||||
<span class="<?= !$faite && $r['echeance'] <= $aujourdhui ? 'retard' : '' ?>"><?= e(date_fr($r['echeance'])) ?></span> · <?= e(objet_relance($r)) ?>
|
||||
<small><?= e($faite ? t('faite le %s par %s', date_fr($r['fait_le']), (string) $r['fait_par_nom']) : t('créée par %s', $r['nom_affiche'])) ?></small>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('relance')) ?>" class="actions-ligne">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="relance_id" value="<?= (int) $r['id'] ?>">
|
||||
<?php if (!$faite): ?><button type="submit" name="action" value="faite" class="lien"><?= e(t('Marquer comme faite')) ?></button><?php endif; ?>
|
||||
<button type="submit" name="action" value="supprimer" class="lien"><?= e(t('Supprimer')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php if (!$c['relances']): ?><p class="vide"><?= e(t('Aucune relance prévue.')) ?></p><?php endif; ?>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<details class="ajout">
|
||||
<summary><?= e(t('Prévoir une relance')) ?></summary>
|
||||
<form method="post" action="<?= e(url('relance')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<label><?= e(t('À faire')) ?> <input type="text" name="objet" maxlength="200" required placeholder="<?= e(t('Rappeler après sa fin de mission')) ?>"></label>
|
||||
<label><?= e(t('Échéance')) ?> <input type="date" name="echeance" required value="<?= e(date('Y-m-d', strtotime('+7 days'))) ?>"></label>
|
||||
<button type="submit" name="action" value="ajouter" class="secondaire"><?= e(t('Prévoir la relance')) ?></button>
|
||||
</form>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Onglet Missions : ce qu'IT Club peut proposer -->
|
||||
<section class="panneau" id="missions" aria-labelledby="tab-missions">
|
||||
<h2 class="titre-panneau"><?= e(t('Missions')) ?></h2>
|
||||
<div class="deux-egales">
|
||||
<section class="carte" id="besoins">
|
||||
<h2><?= e(t('Besoins clients')) ?> <small><?= count($c['besoins']) ?></small></h2>
|
||||
<ul class="simple">
|
||||
<?php foreach ($c['besoins'] as $b): ?>
|
||||
<li>
|
||||
<a href="<?= e(url('besoin', ['id' => $b['id']])) ?>"><?= e($b['client'] . ' : ' . $b['intitule']) ?></a>
|
||||
<small><?= e(t(ETATS_PROPOSITION[$b['etat']])) ?><?= $b['statut'] !== 'ouvert' ? ' · ' . e(t('besoin %s', mb_strtolower(t(ETATS_BESOIN[$b['statut']])))) : '' ?><?= $b['note'] ? ' · ' . e($b['note']) : '' ?></small>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php if (!$c['besoins']): ?><p class="vide"><?= e(t('Ce profil n\'est proposé pour aucun besoin.')) ?></p><?php endif; ?>
|
||||
<?php $libres = array_filter($besoinsOuverts, static fn ($b) => !in_array((int) $b['id'], array_map('intval', array_column($c['besoins'], 'id')), true)); ?>
|
||||
<?php if (peut_ecrire() && $libres): ?>
|
||||
<form method="post" action="<?= e(url('besoin')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="candidat_id" value="<?= (int) $c['id'] ?>">
|
||||
<input type="hidden" name="retour" value="profil">
|
||||
<label><?= e(t('Proposer pour un besoin ouvert')) ?>
|
||||
<select name="besoin_id" required>
|
||||
<?php foreach ($libres as $b): ?><option value="<?= (int) $b['id'] ?>"><?= e($b['client'] . ' : ' . $b['intitule']) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<div class="actions gauche"><button type="submit" name="action" value="proposer" class="secondaire"><?= e(t('Proposer')) ?></button></div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="commercial">
|
||||
<h2><?= e(t('Données commerciales')) ?></h2>
|
||||
<dl class="grille serre">
|
||||
<dt><?= e(t('Disponibilité')) ?></dt><dd><?= e(libelle_disponibilite($c)) ?: $nonRenseigne('non renseignée') ?></dd>
|
||||
<?php if ($c['disponibilite']): ?><dt><?= e(t('Note')) ?></dt><dd><?= e($c['disponibilite']) ?></dd><?php endif; ?>
|
||||
<dt><?= e(t('TJM souhaité')) ?></dt><dd><?= e(euros($c['tjm'])) ?: $nonRenseigne() ?></dd>
|
||||
<dt><?= e(t('Salaire souhaité')) ?></dt><dd><?= $c['salaire_souhaite'] !== null ? e(t('%s brut par an', euros($c['salaire_souhaite']))) : $nonRenseigne() ?></dd>
|
||||
<dt><?= e(t('Mobilité')) ?></dt><dd><?= e($c['mobilite']) ?: $nonRenseigne('non renseignée') ?></dd>
|
||||
<dt><?= e(t('Télétravail')) ?></dt><dd><?= $c['teletravail'] ? e(t(TELETRAVAIL[$c['teletravail']])) : $nonRenseigne() ?></dd>
|
||||
</dl>
|
||||
<p class="aide"><?= e(t('Réservées à IT Club. Elles ne figurent sur aucun CV exporté.')) ?>
|
||||
<?php if (peut_ecrire()): ?><a href="<?= e(url('profil_modifier', ['id' => $c['id']])) ?>"><?= e(t('Modifier')) ?></a><?php endif; ?></p>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Onglet Dossier : pièces, classement, données personnelles, traçabilité, gestion -->
|
||||
<section class="panneau" id="dossier" aria-labelledby="tab-dossier">
|
||||
<h2 class="titre-panneau"><?= e(t('Dossier')) ?></h2>
|
||||
<div class="deux-colonnes">
|
||||
<div>
|
||||
<section class="carte" id="documents">
|
||||
<h2><?= e(t('Documents')) ?> <small><?= count($c['documents']) ?></small></h2>
|
||||
<ul class="simple">
|
||||
<?php foreach ($c['documents'] as $d): ?>
|
||||
<li>
|
||||
<a href="<?= e(url('document', ['id' => $d['id']])) ?>"><?= e($d['nom_fichier']) ?></a>
|
||||
<small><?= e(t(TYPES_DOCUMENT[$d['type']])) ?> · <?= e($taille((int) $d['taille_octets'])) ?> · <?= e($d['nom_affiche']) ?>, <?= e(date_fr($d['ajoute_le'])) ?></small>
|
||||
<span class="actions-ligne">
|
||||
<?php if (in_array(strtolower(pathinfo($d['nom_fichier'], PATHINFO_EXTENSION)), ['md', 'txt'], true)): ?>
|
||||
<a href="<?= e(url('rapport', ['id' => $d['id']])) ?>"><?= e(t('Lire')) ?></a>
|
||||
<?php endif; ?>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('document_suppr')) ?>" data-confirmer="<?= e(t('Supprimer ce document ?')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $d['id'] ?>">
|
||||
<button type="submit" class="lien"><?= e(t('Supprimer')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php if (!$c['documents']): ?><p class="vide"><?= e(t('Aucun document.')) ?></p><?php endif; ?>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<details class="ajout">
|
||||
<summary><?= e(t('Ajouter un document')) ?></summary>
|
||||
<form method="post" action="<?= e(url('document_ajout')) ?>" enctype="multipart/form-data" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<label><?= e(t('Fichier')) ?>
|
||||
<input type="file" name="fichier" required>
|
||||
</label>
|
||||
<label><?= e(t('Type')) ?>
|
||||
<select name="type">
|
||||
<?php foreach (TYPES_DOCUMENT as $code => $libelle): ?>
|
||||
<option value="<?= e($code) ?>"<?= $code === 'autre' ? ' selected' : '' ?>><?= e(t($libelle)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<div class="actions gauche"><button type="submit" class="secondaire"><?= e(t('Ajouter')) ?></button></div>
|
||||
</form>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="mots-cles">
|
||||
<h2><?= e(t('Mots-clés et classement')) ?></h2>
|
||||
<?php if ($c['niveau_etudes'] || $c['encadrement'] !== null): ?>
|
||||
<dl class="grille serre">
|
||||
<?php if ($c['niveau_etudes']): ?><dt><?= e(t('Études')) ?></dt><dd><?= e(t(NIVEAUX_ETUDES[$c['niveau_etudes']] ?? $c['niveau_etudes'])) ?></dd><?php endif; ?>
|
||||
<?php if ($c['encadrement'] !== null): ?><dt><?= e(t('Encadrement')) ?></dt><dd><?= e($c['encadrement'] ? t('Oui') : t('Non')) ?></dd><?php endif; ?>
|
||||
</dl>
|
||||
<?php endif; ?>
|
||||
<?php foreach (TYPES_MOT as $type => $libelle): if (!$c['mots_cles'][$type]) { continue; } ?>
|
||||
<p class="groupe-mots"><?= e(t($libelle)) ?></p>
|
||||
<ul class="mots">
|
||||
<?php foreach ($c['mots_cles'][$type] as $m): ?>
|
||||
<li<?= $m['manuel'] ? ' class="manuel" title="' . e(t('Ajouté à la main')) . '"' : '' ?>>
|
||||
<?= e($m['mot']) ?>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('mot_cle')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<input type="hidden" name="mot_id" value="<?= (int) $m['id'] ?>">
|
||||
<button type="submit" name="action" value="supprimer" aria-label="<?= e(t('Retirer %s', $m['mot'])) ?>">×</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endforeach; ?>
|
||||
<?php if (!array_filter($c['mots_cles'])): ?><p class="vide"><?= e(t('Aucun mot-clé. La recherche s\'appuie sur le reste de la fiche.')) ?></p><?php endif; ?>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('mot_cle')) ?>" class="ajout-mot">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<label><?= e(t('Ajouter')) ?>
|
||||
<input type="text" name="mot" maxlength="80" required>
|
||||
</label>
|
||||
<label><?= e(t('Type')) ?>
|
||||
<select name="type">
|
||||
<?php foreach (TYPES_MOT as $type => $libelle): ?>
|
||||
<option value="<?= e($type) ?>"<?= $type === 'mot_cle' ? ' selected' : '' ?>><?= e(t($libelle)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit" name="action" value="ajouter" class="secondaire"><?= e(t('Ajouter')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="historique">
|
||||
<h2><?= e(t('Historique')) ?> <small><?= count($c['historique']) ?></small></h2>
|
||||
<?php
|
||||
$ligneHistorique = static function (array $h): void { ?>
|
||||
<li>
|
||||
<span><?= e(date_fr($h['cree_le'], true)) ?> · <?= e($h['nom_affiche']) ?></span>
|
||||
<?= e(t(ACTIONS_JOURNAL[$h['action']] ?? $h['action'])) ?><?php $detail = detail_journal($h); ?><?= $detail !== '' ? ' : ' . e($detail) : '' ?>
|
||||
</li>
|
||||
<?php };
|
||||
// Les dix dernières actions suffisent le plus souvent : le reste se déplie
|
||||
$recentes = array_slice($c['historique'], 0, 10);
|
||||
$anciennes = array_slice($c['historique'], 10);
|
||||
?>
|
||||
<ul class="historique"><?php array_map($ligneHistorique, $recentes); ?></ul>
|
||||
<?php if ($anciennes): ?>
|
||||
<details class="suite-historique">
|
||||
<summary><?= e(tn(count($anciennes), '%d action plus ancienne', '%d actions plus anciennes')) ?></summary>
|
||||
<ul class="historique"><?php array_map($ligneHistorique, $anciennes); ?></ul>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<aside>
|
||||
<section class="carte<?= $conservationDepassee ? ' attention' : '' ?>" id="donnees">
|
||||
<h2><?= e(t('Données personnelles')) ?></h2>
|
||||
<dl class="grille serre">
|
||||
<dt><?= e(t('Conservation')) ?></dt><dd class="<?= $conservationDepassee ? 'retard' : '' ?>"><?= e(t('jusqu\'au %s', date_fr($c['conserver_jusqu_au']))) ?></dd>
|
||||
<dt><?= e(t('Accord')) ?></dt>
|
||||
<dd><?= $c['consentement_le']
|
||||
? e(t('donné le %s', date_fr($c['consentement_le']))) . ($c['consentement_note'] ? '<br><small>' . e($c['consentement_note']) . '</small>' : '')
|
||||
: $nonRenseigne('non enregistré') ?></dd>
|
||||
</dl>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<details>
|
||||
<summary><?= e($c['consentement_le'] ? t('Modifier l\'accord') : t('Enregistrer l\'accord du candidat')) ?></summary>
|
||||
<p class="aide"><?= e(t('Notez la date à laquelle le candidat a accepté que IT Club conserve son CV, et la façon dont il l\'a dit.')) ?></p>
|
||||
<form method="post" action="<?= e(url('consentement')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<label><?= e(t('Date de l\'accord')) ?> <input type="date" name="date" required max="<?= e($aujourdhui) ?>" value="<?= e($c['consentement_le'] ?? $aujourdhui) ?>"></label>
|
||||
<label><?= e(t('Recueilli comment')) ?> <input type="text" name="note" maxlength="200" value="<?= e($c['consentement_note'] ?? '') ?>" placeholder="<?= e(t('Email du candidat, formulaire, entretien')) ?>"></label>
|
||||
<div class="actions">
|
||||
<?php if ($c['consentement_le']): ?><button type="submit" name="action" value="retirer" class="secondaire" formnovalidate><?= e(t('Retirer l\'accord')) ?></button><?php endif; ?>
|
||||
<button type="submit" name="action" value="enregistrer" class="secondaire"><?= e(t('Enregistrer')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
<form method="post" action="<?= e(url('profil_export')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<button type="submit" class="secondaire"><?= e(t('Exporter les données du candidat')) ?></button>
|
||||
</form>
|
||||
<p class="aide"><?= e(t('Un fichier zip avec tout ce que Vivier détient sur cette personne. À lui remettre s\'il demande l\'accès à ses données.')) ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte" id="photo">
|
||||
<h2><?= e(t('Photo')) ?></h2>
|
||||
<p class="aide"><?= e(t('Sert à reconnaître le candidat dans Vivier. Elle ne figure pas sur les CV exportés.')) ?></p>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<details>
|
||||
<summary><?= e($c['photo'] ? t('Remplacer la photo') : t('Ajouter une photo')) ?></summary>
|
||||
<form method="post" action="<?= e(url('photo_maj')) ?>" enctype="multipart/form-data" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<label><?= e(t('Fichier')) ?>
|
||||
<input type="file" name="photo" accept=".jpg,.jpeg,.png,.webp,.gif,image/*" required>
|
||||
</label>
|
||||
<div class="actions gauche"><button type="submit" name="action" value="charger" class="secondaire"><?= e(t('Enregistrer la photo')) ?></button></div>
|
||||
</form>
|
||||
</details>
|
||||
<?php if ($c['photo']): ?>
|
||||
<form method="post" action="<?= e(url('photo_maj')) ?>" class="actions-ligne" data-confirmer="<?= e(t('Retirer la photo de ce profil ?')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<button type="submit" name="action" value="retirer" class="lien"><?= e(t('Retirer la photo')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<section class="carte" id="gestion">
|
||||
<h2><?= e(t('Gestion')) ?></h2>
|
||||
<details>
|
||||
<summary><?= e(t('Mettre à jour depuis une nouvelle analyse')) ?></summary>
|
||||
<p class="aide"><?= e(t('Remplace les expériences, technologies, langues et mots-clés. Garde le statut, les commentaires, les documents et les mots ajoutés à la main.')) ?></p>
|
||||
<form method="post" action="<?= e(url('profil_maj')) ?>" enctype="multipart/form-data" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<label><?= e(t('Profil structuré (.json)')) ?> <input type="file" name="profil" accept=".json,application/json" required></label>
|
||||
<label><?= e(t('Nouveau CV, facultatif')) ?> <input type="file" name="cv" accept=".pdf,.docx,.doc,.odt,.rtf,.md,.txt"></label>
|
||||
<label><?= e(t('Nouveau rapport, facultatif')) ?> <input type="file" name="rapport" accept=".md,.txt,.pdf,.docx"></label>
|
||||
<button type="submit" class="secondaire"><?= e(t('Mettre à jour')) ?></button>
|
||||
</form>
|
||||
</details>
|
||||
<?php if (!$archive): ?>
|
||||
<form method="post" action="<?= e(url('archiver')) ?>" class="danger">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<button type="submit" name="action" value="archiver" class="secondaire"><?= e(t('Archiver le profil')) ?></button>
|
||||
<p class="aide"><?= e(t('Le profil sort du Kanban et des chiffres. Il reste consultable dans Profils, filtre « Archivés ».')) ?></p>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php if (est_admin()): ?>
|
||||
<form method="post" action="<?= e(url('profil_suppr')) ?>" class="danger"
|
||||
data-confirmer="<?= e(t('Supprimer ce profil ? Il part à la corbeille, puis il est effacé avec ses documents au bout de %d jours.', CORBEILLE_JOURS)) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<button type="submit" class="dangereux"><?= e(t('Supprimer le profil')) ?></button>
|
||||
<p class="aide"><?= e(t('Le profil part à la corbeille. Vous pouvez le restaurer pendant %d jours.', CORBEILLE_JOURS)) ?></p>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<p class="aide danger"><?= e(t('Seul le gestionnaire des comptes peut supprimer un profil.')) ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
$pays = liste_pays();
|
||||
$nats = codes_nationalites(is_array($saisie['nationalites'] ?? null) ? $saisie['nationalites'] : (string) ($saisie['nationalites'] ?? ''));
|
||||
$libre = hors_ue($nats) === 0;
|
||||
?>
|
||||
<p class="fil"><a href="<?= e(url('profil', ['id' => $c['id']])) ?>"><?= e($c['prenom'] . ' ' . $c['nom']) ?></a> / <?= e(t('Modifier')) ?></p>
|
||||
<section class="carte moyen">
|
||||
<h1><?= e(t('Modifier la fiche')) ?></h1>
|
||||
<p class="aide"><?= e(t('Chaque changement est tracé dans l\'historique. Pour corriger les expériences, les technologies ou les langues, mettez la fiche à jour depuis une nouvelle analyse.')) ?></p>
|
||||
<?php if ($erreur): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
<form method="post" action="<?= e(url('profil_modifier', ['id' => $c['id']])) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $c['id'] ?>">
|
||||
<?php
|
||||
/** Un champ texte de la fiche. */
|
||||
$champ = static function (string $nom, string $classe = '', string $aide = '', string $exemple = '') use ($saisie): void {
|
||||
[$libelle, $max] = CHAMPS_MODIFIABLES[$nom]; ?>
|
||||
<label<?= $classe ? ' class="' . e($classe) . '"' : '' ?>><?= e(t($libelle)) ?>
|
||||
<input type="<?= $nom === 'email' ? 'email' : 'text' ?>" name="<?= e($nom) ?>" maxlength="<?= (int) $max ?>"
|
||||
value="<?= e($saisie[$nom] ?? '') ?>"<?= in_array($nom, ['prenom', 'nom'], true) ? ' required' : '' ?><?= $exemple ? ' placeholder="' . e($exemple) . '"' : '' ?>>
|
||||
<?php if ($aide): ?><small class="aide-champ"><?= e($aide) ?></small><?php endif; ?>
|
||||
</label>
|
||||
<?php }; ?>
|
||||
|
||||
<fieldset class="groupe">
|
||||
<legend><?= e(t('Identité et CV')) ?></legend>
|
||||
<p class="aide"><?= e(t('Ces informations figurent sur les CV IT Club et Europass.')) ?></p>
|
||||
<div class="champs">
|
||||
<?php $champ('prenom'); ?>
|
||||
<?php $champ('nom', '', t('Enregistré en capitales.')); ?>
|
||||
<?php $champ('titre', 'large'); ?>
|
||||
<label class="large"><?= e(t('Synthèse')) ?>
|
||||
<textarea name="resume" rows="5" maxlength="5000"><?= e($saisie['resume'] ?? '') ?></textarea>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="groupe">
|
||||
<legend><?= e(t('Coordonnées')) ?></legend>
|
||||
<p class="aide"><?= e(t('Elles ne figurent pas sur le CV IT Club. Le CV Europass nominatif les montre, sauf si vous les retirez de l\'aperçu.')) ?></p>
|
||||
<div class="champs">
|
||||
<?php $champ('email'); ?>
|
||||
<?php $champ('telephone'); ?>
|
||||
<?php $champ('rue', 'large'); ?>
|
||||
<?php $champ('code_postal'); ?>
|
||||
<?php $champ('ville'); ?>
|
||||
<?php $champ('pays'); ?>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="groupe">
|
||||
<legend><?= e(t('Nationalité et permis de travail')) ?></legend>
|
||||
<p class="aide"><?= e(t('Sert à savoir si un permis de travail est nécessaire au Luxembourg. Ne figure sur aucun CV.')) ?></p>
|
||||
<div class="champs">
|
||||
<?php for ($i = 0; $i < 3; $i++): ?>
|
||||
<label><?= e($i === 0 ? t('Nationalité') : t('Autre nationalité, facultative')) ?>
|
||||
<select name="nationalites[]" data-nationalite>
|
||||
<option value=""><?= e($i === 0 ? t('Non renseignée') : t('Aucune')) ?></option>
|
||||
<?php foreach ($pays as $code => $nom): ?><option value="<?= e($code) ?>"<?= ($nats[$i] ?? '') === $code ? ' selected' : '' ?>><?= e($nom) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
<p class="flash flash-succes permis-libre" id="permis-non-requis"<?= $libre ? '' : ' hidden' ?>><?= e(t('Permis de travail non requis : nationalité de l\'Union européenne, de l\'Espace économique européen ou suisse.')) ?></p>
|
||||
<div class="champs" id="bloc-permis" data-libres="<?= e(implode(',', PAYS_LIBRE_CIRCULATION)) ?>"<?= $libre ? ' hidden' : '' ?>>
|
||||
<?php $champ('permis_travail', 'large', t('Texte libre : type de titre, restrictions, démarches en cours.'), t('Autorisation de travail salarié, limitée au secteur IT')); ?>
|
||||
<label><?= e(t('Titre valable jusqu\'au')) ?>
|
||||
<input type="date" name="permis_expire_le" value="<?= e($saisie['permis_expire_le'] ?? '') ?>">
|
||||
<small class="aide-champ"><?= e(t('Vivier prévoit une relance 2 mois avant.')) ?></small>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="groupe">
|
||||
<legend><?= e(t('Disponibilité')) ?></legend>
|
||||
<div class="champs">
|
||||
<label><?= e(t('Disponibilité')) ?>
|
||||
<select name="disponibilite_code">
|
||||
<option value=""><?= e(t('Non renseignée')) ?></option>
|
||||
<?php foreach (DISPONIBILITES as $code => $libelle): ?><option value="<?= e($code) ?>"<?= ($saisie['disponibilite_code'] ?? '') === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
<small class="aide-champ"><?= e(t('« Sous N mois » donne une date estimée, utilisée pour les besoins clients.')) ?></small>
|
||||
</label>
|
||||
<label id="champ-disponible-le"<?= ($saisie['disponibilite_code'] ?? '') === 'date' ? '' : ' hidden' ?>><?= e(t('À partir du')) ?>
|
||||
<input type="date" name="disponible_le" value="<?= e(($saisie['disponibilite_code'] ?? '') === 'date' ? ($saisie['disponible_le'] ?? '') : '') ?>">
|
||||
</label>
|
||||
<?php $champ('disponibilite', 'large', t('Précision libre, en interne : la formulation du CV, un préavis à négocier.'), t('Préavis de 2 mois, négociable')); ?>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="groupe">
|
||||
<legend><?= e(t('Données commerciales')) ?></legend>
|
||||
<p class="aide"><?= e(t('Réservées à IT Club. Elles ne figurent sur aucun CV exporté.')) ?></p>
|
||||
<div class="champs">
|
||||
<label><?= e(t('Télétravail')) ?>
|
||||
<select name="teletravail">
|
||||
<option value=""><?= e(t('Non précisé')) ?></option>
|
||||
<?php foreach (TELETRAVAIL as $code => $libelle): ?><option value="<?= e($code) ?>"<?= ($saisie['teletravail'] ?? '') === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('TJM souhaité, en euros')) ?>
|
||||
<input type="text" inputmode="numeric" pattern="[0-9]*" name="tjm" maxlength="5" value="<?= e($saisie['tjm'] ?? '') ?>">
|
||||
</label>
|
||||
<label><?= e(t('Salaire annuel brut souhaité, en euros')) ?>
|
||||
<input type="text" inputmode="numeric" pattern="[0-9]*" name="salaire_souhaite" maxlength="7" value="<?= e($saisie['salaire_souhaite'] ?? '') ?>">
|
||||
</label>
|
||||
<label><?= e(t('Mobilité')) ?>
|
||||
<input type="text" name="mobilite" maxlength="200" value="<?= e($saisie['mobilite'] ?? '') ?>" placeholder="<?= e(t('Luxembourg, Metz, déplacements ponctuels')) ?>">
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="groupe">
|
||||
<legend><?= e(t('CV Europass')) ?></legend>
|
||||
<p class="aide"><?= e(t('Ces rubriques n\'apparaissent que sur le CV Europass, et seulement si elles sont remplies.')) ?></p>
|
||||
<div class="champs">
|
||||
<label class="large"><?= e(t('Bénévolat')) ?>
|
||||
<textarea name="benevolat" rows="2" maxlength="2000" placeholder="<?= e(t('Une activité par ligne')) ?>"><?= e($saisie['benevolat'] ?? '') ?></textarea>
|
||||
</label>
|
||||
<?php $champ('permis_conduire', 'large', t('Catégories séparées par des virgules.'), 'B, A1'); ?>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="actions">
|
||||
<a class="bouton secondaire" href="<?= e(url('profil', ['id' => $c['id']])) ?>"><?= e(t('Annuler')) ?></a>
|
||||
<button type="submit"><?= e(t('Enregistrer')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -0,0 +1,8 @@
|
||||
<p class="fil">
|
||||
<?php if ($c): ?><a href="<?= e(url('profil', ['id' => $c['id']])) ?>"><?= e($c['prenom'] . ' ' . $c['nom']) ?></a> / <?php endif; ?>
|
||||
<?= e($d['nom_fichier']) ?>
|
||||
</p>
|
||||
<article class="carte moyen rapport">
|
||||
<?= $html ?>
|
||||
</article>
|
||||
<p class="centre"><a href="<?= e(url('document', ['id' => $d['id']])) ?>"><?= e(t('Télécharger le fichier')) ?></a></p>
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
$fr = static fn ($n): string => nombre($n);
|
||||
$n = count($resultats);
|
||||
$filtre = $q !== '' || array_filter($filtres);
|
||||
?>
|
||||
<h1><?= e($q !== '' ? t('Recherche') : ($filtres['archives'] === 'seuls' ? t('Profils archivés') : t('Tous les profils'))) ?> <small><?= e(tn($n, '%d profil', '%d profils')) ?></small></h1>
|
||||
|
||||
<form class="filtres" method="get" action="index.php" role="search" data-auto data-repli>
|
||||
<input type="hidden" name="p" value="recherche">
|
||||
<label class="large"><?= e(t('Recherche')) ?>
|
||||
<input type="search" name="q" value="<?= e($q) ?>" placeholder="<?= e(t('développeur Java senior')) ?>" autofocus>
|
||||
</label>
|
||||
<label><?= e(t('Statut')) ?>
|
||||
<select name="statut">
|
||||
<option value=""><?= e(t('Tous')) ?></option>
|
||||
<?php foreach (STATUTS as $code => $libelle): ?><option value="<?= e($code) ?>"<?= $filtres['statut'] === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Séniorité')) ?>
|
||||
<select name="seniorite">
|
||||
<option value=""><?= e(t('Toutes')) ?></option>
|
||||
<?php foreach (SENIORITES as $code => $libelle): ?><option value="<?= e($code) ?>"<?= $filtres['seniorite'] === $code ? ' selected' : '' ?>><?= e(t($libelle)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Langue')) ?>
|
||||
<select name="langue">
|
||||
<option value=""><?= e(t('Toutes')) ?></option>
|
||||
<?php foreach ($langues as $l): ?><option value="<?= e($l) ?>"<?= $filtres['langue'] === $l ? ' selected' : '' ?>><?= e(t($l)) ?></option><?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Nationalité')) ?>
|
||||
<select name="nationalite">
|
||||
<option value=""><?= e(t('Toutes')) ?></option>
|
||||
<option value="ue"<?= $filtres['nationalite'] === 'ue' ? ' selected' : '' ?>><?= e(t('UE, EEE ou Suisse')) ?></option>
|
||||
<option value="hors_ue"<?= $filtres['nationalite'] === 'hors_ue' ? ' selected' : '' ?>><?= e(t('Hors UE')) ?></option>
|
||||
<option value="inconnue"<?= $filtres['nationalite'] === 'inconnue' ? ' selected' : '' ?>><?= e(t('Non renseignée')) ?></option>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Titre de séjour')) ?>
|
||||
<select name="permis">
|
||||
<option value=""><?= e(t('Tous')) ?></option>
|
||||
<option value="expire"<?= $filtres['permis'] === 'expire' ? ' selected' : '' ?>><?= e(t('Expiré ou expire sous 2 mois')) ?></option>
|
||||
</select>
|
||||
</label>
|
||||
<label><?= e(t('Archivés')) ?>
|
||||
<select name="archives">
|
||||
<option value=""><?= e(t('Masqués')) ?></option>
|
||||
<option value="inclus"<?= $filtres['archives'] === 'inclus' ? ' selected' : '' ?>><?= e(t('Inclus')) ?></option>
|
||||
<option value="seuls"<?= $filtres['archives'] === 'seuls' ? ' selected' : '' ?>><?= e(t('Seulement')) ?></option>
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit"><?= e(t('Rechercher')) ?></button>
|
||||
<?php if ($filtre): ?><a href="<?= e(url('recherche')) ?>"><?= e(t('Effacer')) ?></a><?php endif; ?>
|
||||
<?php if ($resultats && est_admin()): ?>
|
||||
<a class="bouton secondaire fin" href="<?= e(url('export_csv', array_filter(['q' => $q] + $filtres, 'strlen'))) ?>"><?= e(t('Exporter en CSV')) ?></a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
|
||||
<?php if ($analyse['seniorites']): ?>
|
||||
<p class="aide"><?= e(t('Séniorité déduite de la recherche : %s.', implode(' ' . t('ou') . ' ', array_map(static fn ($s) => t(SENIORITES[$s] ?? $s), $analyse['seniorites'])))) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!$resultats): ?>
|
||||
<section class="carte">
|
||||
<p><strong><?= e(t('Aucun profil ne correspond.')) ?></strong></p>
|
||||
<p class="aide"><?= e(t('Tous les mots doivent figurer sur la fiche. Retirez un mot, ou placez une expression entre guillemets pour la chercher telle quelle.')) ?></p>
|
||||
</section>
|
||||
<?php else: ?>
|
||||
<ul class="resultats">
|
||||
<?php foreach ($resultats as $r): ?>
|
||||
<li class="carte resultat">
|
||||
<?= avatar($r, 'moyen') ?>
|
||||
<div class="resultat-corps">
|
||||
<div class="resultat-tete">
|
||||
<a class="nom" href="<?= e(url('profil', ['id' => $r['id']])) ?>"><?= e($r['prenom'] . ' ' . $r['nom']) ?></a>
|
||||
<span class="etat"><?php if ($r['archive_le']): ?><span class="badge badge-attente"><?= e(t('Archivé')) ?></span><?php endif; ?><?= echelle($r['statut']) ?><?= e(t(STATUTS[$r['statut']])) ?></span>
|
||||
</div>
|
||||
<p class="sous-titre">
|
||||
<?php if ($r['titre']): ?><span><?= e($r['titre']) ?></span><?php endif; ?>
|
||||
<span class="badge badge-<?= e($r['seniorite']) ?>"><?= e(t(SENIORITES[$r['seniorite']])) ?></span>
|
||||
<span><?= e(annees($r['annees_experience'])) ?></span>
|
||||
<?php if ($r['ville']): ?><span><?= e($r['ville']) ?></span><?php endif; ?>
|
||||
<?php $dispo = libelle_disponibilite($r, null, false); if ($dispo !== ''): ?><span><?= e(t('Disponibilité : %s', mb_strtolower($dispo))) ?></span><?php endif; ?>
|
||||
<?php if (isset($cv[$r['id']])): ?><a href="<?= e(url('document', ['id' => $cv[$r['id']]])) ?>"><?= e(t('CV d\'origine')) ?></a><?php endif; ?>
|
||||
</p>
|
||||
<?php if ($r['technologies'] || $r['mots']): ?>
|
||||
<p class="technos">
|
||||
<?php foreach ($r['technologies'] as $t): ?>
|
||||
<span<?= $t['trouve'] ? ' class="trouve"' : '' ?>><?= e($t['nom']) ?><?= $t['annees'] !== null ? ' ' . e(annees($t['annees'])) : '' ?></span>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($r['mots'] as $m): if (!$m['trouve']) { continue; } ?>
|
||||
<span class="trouve"><?= e($m['mot']) ?></span>
|
||||
<?php endforeach; ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
$envois = [
|
||||
'envoye' => t('Envoyée par email'),
|
||||
'sans_piece' => t('Email sans pièce jointe'),
|
||||
'echec' => t('Email non parti'),
|
||||
'non_demande' => t('Sans email'),
|
||||
];
|
||||
$derniere = $etat['derniere'];
|
||||
$configuree = $reglages['destinataires'] && $reglages['expediteur'] !== '';
|
||||
$copies = count(array_filter($liste, static fn ($s) => $s['fichier'] !== null));
|
||||
?>
|
||||
<h1><?= e(t('Sauvegarde')) ?></h1>
|
||||
|
||||
<?php if ($etat['en_retard']): ?>
|
||||
<p class="flash flash-erreur" role="alert">
|
||||
<strong><?= e($etat['jours'] === null ? t('Aucune sauvegarde de la base n\'a encore été faite.') : t('Dernière sauvegarde réussie il y a %d jours.', $etat['jours'])) ?></strong>
|
||||
<?= e(t('La tâche planifiée de l\'hébergement ne tourne pas, ou elle échoue. Lancez une sauvegarde ci-dessous, puis vérifiez la tâche dans l\'espace client OVH.')) ?>
|
||||
</p>
|
||||
<?php elseif ($etat['dernier_echec']): ?>
|
||||
<p class="flash flash-erreur" role="alert">
|
||||
<strong><?= e(t('La dernière sauvegarde a rencontré un problème.')) ?></strong> <?= e((string) $etat['dernier_echec']['detail']) ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="deux-egales">
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Base de données')) ?></h2>
|
||||
<dl class="grille">
|
||||
<dt><?= e(t('Dernière réussie')) ?></dt>
|
||||
<dd><?= $derniere ? e(date_fr($derniere['cree_le'], true)) . ' · ' . e(tn((int) $derniere['profils'], '%d profil', '%d profils')) . ' · ' . e(Sauvegarde::taille((int) $derniere['taille_octets'])) : '<em>' . e(t('aucune')) . '</em>' ?></dd>
|
||||
<dt><?= e(t('Envoi')) ?></dt>
|
||||
<dd><?= $configuree ? e(implode(', ', $reglages['destinataires'])) . '<br><small>' . e(t('depuis %s', $reglages['expediteur'])) . '</small>' : '<em class="retard">' . e(t('adresses absentes de config/config.php')) . '</em>' ?></dd>
|
||||
<dt><?= e(t('Copies gardées')) ?></dt>
|
||||
<dd><?= e($reglages['semaines_conservees'] > 0 ? tn($reglages['semaines_conservees'], '%d semaine', '%d semaines') : t('sans limite de durée')) ?> · <?= e(tn($copies, '%d copie sur le serveur', '%d copies sur le serveur')) ?></dd>
|
||||
</dl>
|
||||
<p class="aide"><?= e(t('La tâche planifiée de l\'hébergement lance cette sauvegarde chaque semaine et l\'envoie par email. Le fichier contient les données des candidats et n\'est pas chiffré : la protection repose sur celle des boîtes de réception.')) ?></p>
|
||||
<form method="post" action="<?= e(url('sauvegarde')) ?>" class="pile" data-attente="<?= e(t('Sauvegarde en cours. Gardez la page ouverte.')) ?>" data-attente-bouton="<?= e(t('Sauvegarde en cours…')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="lancer">
|
||||
<label class="case"><input type="checkbox" name="envoyer" value="1"<?= $configuree ? ' checked' : ' disabled' ?>> <?= e(t('Envoyer aussi la sauvegarde par email')) ?></label>
|
||||
<div class="actions gauche">
|
||||
<button type="submit"><?= e(t('Sauvegarder maintenant')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
<?php if ($configuree): ?>
|
||||
<form method="post" action="<?= e(url('sauvegarde')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<button type="submit" name="action" value="essai" class="lien"><?= e(t('Envoyer un email d\'essai, sans pièce jointe')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Fichiers')) ?></h2>
|
||||
<dl class="grille">
|
||||
<dt><?= e(t('Contenu')) ?></dt>
|
||||
<dd><?= e(tn((int) $fichiers['n'], '%d document', '%d documents')) ?> · <?= e(Sauvegarde::taille((int) $fichiers['octets'])) ?> · <?= e(t('et les photos')) ?></dd>
|
||||
</dl>
|
||||
<p class="aide"><?= e(t('Les CV, rapports, pièces jointes et photos sont trop lourds pour un email. Téléchargez leur archive et gardez-la avec la sauvegarde de la base du même jour.')) ?></p>
|
||||
<form method="post" action="<?= e(url('sauvegarde')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<label><?= e(t('Votre mot de passe, pour confirmer')) ?>
|
||||
<input type="password" name="mon_mot_de_passe" required autocomplete="current-password">
|
||||
<small class="aide-champ"><?= e(t('L\'archive contient tous les CV : Vivier vérifie que c\'est bien vous.')) ?></small>
|
||||
</label>
|
||||
<div class="actions gauche"><button type="submit" name="action" value="fichiers" class="secondaire"><?= e(t('Télécharger l\'archive des fichiers')) ?></button></div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Historique des sauvegardes')) ?></h2>
|
||||
<?php if (!$liste): ?>
|
||||
<p class="vide"><?= e(t('Aucune sauvegarde pour l\'instant.')) ?></p>
|
||||
<?php else: ?>
|
||||
<div class="defile"><table>
|
||||
<thead><tr><th><?= e(t('Date')) ?></th><th><?= e(t('Origine')) ?></th><th><?= e(t('Résultat')) ?></th><th class="nombre"><?= e(t('Profils')) ?></th><th class="nombre"><?= e(t('Taille')) ?></th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($liste as $s): ?>
|
||||
<tr>
|
||||
<td><?= e(date_fr($s['cree_le'], true)) ?></td>
|
||||
<td><?= e($s['origine'] === 'planifiee' ? t('Tâche planifiée') : t('Manuelle, %s', (string) ($s['nom_affiche'] ?? '?'))) ?></td>
|
||||
<td>
|
||||
<?php if (!$s['reussie']): ?>
|
||||
<span class="retard"><?= e(t('Échec')) ?></span>
|
||||
<?php else: ?>
|
||||
<span class="<?= $s['envoi'] === 'echec' ? 'retard' : '' ?>"><?= e($envois[$s['envoi']]) ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if ($s['detail']): ?><br><small><?= e($s['detail']) ?></small><?php endif; ?>
|
||||
</td>
|
||||
<td class="nombre"><?= $s['profils'] !== null ? (int) $s['profils'] : '' ?></td>
|
||||
<td class="nombre"><?= $s['taille_octets'] !== null ? e(Sauvegarde::taille((int) $s['taille_octets'])) : '' ?></td>
|
||||
<td class="actions-ligne">
|
||||
<?php if ($s['fichier'] !== null && Sauvegarde::chemin($s) !== null): ?>
|
||||
<a href="<?= e(url('sauvegarde', ['telecharger' => $s['id']])) ?>"><?= e(t('Télécharger')) ?></a>
|
||||
<form method="post" action="<?= e(url('sauvegarde')) ?>" data-confirmer="<?= e(t('Supprimer cette copie du serveur ? Les copies reçues par email ne sont pas touchées.')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $s['id'] ?>">
|
||||
<button type="submit" name="action" value="supprimer" class="lien"><?= e(t('Supprimer')) ?></button>
|
||||
</form>
|
||||
<?php elseif ($s['reussie']): ?>
|
||||
<small><?= e(t('copie supprimée')) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table></div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
$dateHeure = static fn (string $d): string => date_fr($d, !str_ends_with($d, '00:00:00'));
|
||||
$dues = count(array_filter($relances, static fn ($r) => $r['jours'] <= 0));
|
||||
$delai = static function (int $j): string {
|
||||
return match (true) {
|
||||
$j < 0 => t('en retard de %d j', -$j),
|
||||
$j === 0 => t('aujourd\'hui'),
|
||||
default => t('dans %d j', $j),
|
||||
};
|
||||
};
|
||||
?>
|
||||
<h1><?= e(t('Suivi')) ?> <small><?= e(tn(count($relances), '%d relance prévue', '%d relances prévues')) ?><?php if ($dues): ?> · <span class="retard"><?= e(tn($dues, 'dont %d à faire aujourd\'hui ou en retard', 'dont %d à faire aujourd\'hui ou en retard')) ?></span><?php endif; ?></small></h1>
|
||||
|
||||
<div class="deux-egales">
|
||||
<section class="carte" id="relances">
|
||||
<h2><?= e(t('Relances')) ?></h2>
|
||||
<?php if (!$relances): ?>
|
||||
<p class="vide"><?= e(t('Aucune relance prévue. Une relance se crée sur la fiche d\'un profil.')) ?></p>
|
||||
<?php else: ?>
|
||||
<ul class="simple agenda">
|
||||
<?php foreach ($relances as $r): ?>
|
||||
<li>
|
||||
<?= avatar(['id' => $r['candidat_id'], 'prenom' => $r['prenom'], 'nom' => $r['nom'], 'photo' => $r['photo']]) ?>
|
||||
<div>
|
||||
<a href="<?= e(url('profil', ['id' => $r['candidat_id']])) ?>#relances"><?= e($r['prenom'] . ' ' . $r['nom']) ?></a>
|
||||
<span class="<?= $r['jours'] <= 0 ? 'retard' : '' ?>"><?= e(date_fr($r['echeance'])) ?>, <?= e($delai((int) $r['jours'])) ?></span>
|
||||
<p><?= e(objet_relance($r)) ?></p>
|
||||
<small><?= e(t(STATUTS[$r['statut']])) ?> · <?= e(t('créée par %s', $r['nom_affiche'])) ?></small>
|
||||
<?php if (peut_ecrire()): ?>
|
||||
<form method="post" action="<?= e(url('relance')) ?>" class="actions-ligne">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="relance_id" value="<?= (int) $r['id'] ?>">
|
||||
<input type="hidden" name="retour" value="suivi">
|
||||
<button type="submit" name="action" value="faite" class="lien"><?= e(t('Marquer comme faite')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<div>
|
||||
<section class="carte" id="entretiens">
|
||||
<h2><?= e(t('Entretiens des 30 prochains jours')) ?></h2>
|
||||
<?php if (!$entretiens): ?>
|
||||
<p class="vide"><?= e(t('Aucun entretien prévu. Un entretien se saisit sur la fiche d\'un profil.')) ?></p>
|
||||
<?php else: ?>
|
||||
<ul class="simple agenda">
|
||||
<?php foreach ($entretiens as $x): ?>
|
||||
<li>
|
||||
<?= avatar(['id' => $x['candidat_id'], 'prenom' => $x['prenom'], 'nom' => $x['nom'], 'photo' => $x['photo']]) ?>
|
||||
<div>
|
||||
<a href="<?= e(url('profil', ['id' => $x['candidat_id']])) ?>#entretiens"><?= e($x['prenom'] . ' ' . $x['nom']) ?></a>
|
||||
<span><?= e($dateHeure($x['date_entretien'])) ?></span>
|
||||
<p><?= e(implode(' · ', array_filter([
|
||||
t(TYPES_ENTRETIEN[$x['type']]),
|
||||
$x['interlocuteur'] ? t('mené par %s', $x['interlocuteur']) : null,
|
||||
$x['besoin_intitule'] ? $x['besoin_client'] . ' : ' . $x['besoin_intitule'] : null,
|
||||
]))) ?></p>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if ($sansAvis): ?>
|
||||
<section class="carte attention" id="sans-avis">
|
||||
<h2><?= e(t('Entretiens passés sans avis')) ?></h2>
|
||||
<ul class="simple agenda">
|
||||
<?php foreach ($sansAvis as $x): ?>
|
||||
<li>
|
||||
<?= avatar(['id' => $x['candidat_id'], 'prenom' => $x['prenom'], 'nom' => $x['nom'], 'photo' => $x['photo']]) ?>
|
||||
<div>
|
||||
<a href="<?= e(url('profil', ['id' => $x['candidat_id']])) ?>#entretiens"><?= e($x['prenom'] . ' ' . $x['nom']) ?></a>
|
||||
<span><?= e($dateHeure($x['date_entretien'])) ?> · <?= e(t(TYPES_ENTRETIEN[$x['type']])) ?></span>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
$fr = static fn ($n): string => nombre($n);
|
||||
$max = static fn (array $valeurs): int => max(1, ...array_values(array_map('intval', $valeurs)));
|
||||
/** Barre horizontale : la longueur est donnée par une classe de 0 à 20, la valeur est toujours écrite. */
|
||||
$barre = static fn (float $v, float $m): string => '<span class="barre"><span class="l' . (int) round(20 * $v / max($m, 0.0001)) . '"></span></span>';
|
||||
?>
|
||||
<div class="barre-titre"><h1><?= e(t('Tableau de bord')) ?></h1></div>
|
||||
|
||||
<?php if ($sauvegarde && ($sauvegarde['en_retard'] || $sauvegarde['dernier_echec'])): ?>
|
||||
<p class="flash flash-erreur" role="alert">
|
||||
<strong><?= e($sauvegarde['en_retard']
|
||||
? ($sauvegarde['jours'] === null ? t('Aucune sauvegarde de la base n\'a encore été faite.') : t('Dernière sauvegarde réussie il y a %d jours.', $sauvegarde['jours']))
|
||||
: t('La dernière sauvegarde a rencontré un problème.')) ?></strong>
|
||||
<?php if ($sauvegarde['dernier_echec']): ?><?= e((string) $sauvegarde['dernier_echec']['detail']) ?><?php endif; ?>
|
||||
<a href="<?= e(url('sauvegarde')) ?>"><?= e(t('Ouvrir la page Sauvegarde')) ?></a>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($s['total'] === 0 && $s['archives'] === 0): ?>
|
||||
<section class="carte">
|
||||
<p><strong><?= e(t('Aucun profil pour l\'instant.')) ?></strong></p>
|
||||
<p class="aide"><?= e(t('Les chiffres apparaîtront après le premier import.')) ?></p>
|
||||
<p><a class="bouton" href="<?= e(url('import')) ?>"><?= e(t('Importer un profil')) ?></a></p>
|
||||
</section>
|
||||
<?php else: ?>
|
||||
|
||||
<dl class="chiffres">
|
||||
<div><dt><?= e(t('Profils dans le vivier')) ?></dt><dd><?= (int) $s['total'] ?></dd></div>
|
||||
<div><dt><?= e(t('Reçus sur 30 jours')) ?></dt><dd><?= (int) $s['recus_30j'] ?></dd></div>
|
||||
<div><dt><?= e(t('En cours de parcours')) ?></dt><dd><?= (int) $s['en_parcours'] ?></dd></div>
|
||||
<div><dt><?= e(t('Embauchés')) ?></dt><dd><?= (int) $s['par_statut']['embauche'] ?></dd></div>
|
||||
<div><dt><a href="<?= e(url('suivi')) ?>"><?= e(t('Entretiens sur 7 jours')) ?></a></dt><dd><?= (int) $s['entretiens_7j'] ?></dd></div>
|
||||
<div><dt><a href="<?= e(url('suivi')) ?>"><?= e(t('Relances à faire')) ?></a></dt><dd><?= (int) $s['relances_dues'] ?></dd></div>
|
||||
<div><dt><a href="<?= e(url('besoins', ['statut' => 'ouvert'])) ?>"><?= e(t('Besoins ouverts')) ?></a></dt><dd><?= (int) $s['besoins_ouverts'] ?></dd></div>
|
||||
<div><dt><?php if (est_admin()): ?><a href="<?= e(url('conservation')) ?>"><?= e(t('Conservation à traiter')) ?></a><?php else: ?><?= e(t('Conservation à traiter')) ?><?php endif; ?></dt><dd><?= (int) $s['a_echeance'] ?></dd></div>
|
||||
</dl>
|
||||
|
||||
<div class="deux-egales">
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Profils par statut')) ?></h2>
|
||||
<table class="mesures">
|
||||
<tbody>
|
||||
<?php $m = $max($s['par_statut']); foreach (STATUTS as $code => $libelle): ?>
|
||||
<tr>
|
||||
<th scope="row"><?= echelle($code) ?><a href="<?= e(url('recherche', ['statut' => $code])) ?>"><?= e(t($libelle)) ?></a></th>
|
||||
<td><?= $barre($s['par_statut'][$code], $m) ?></td>
|
||||
<td class="nombre"><?= (int) $s['par_statut'][$code] ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php if ($s['archives']): ?>
|
||||
<p class="aide"><a href="<?= e(url('recherche', ['archives' => 'seuls'])) ?>"><?= e(tn((int) $s['archives'], '%d profil archivé', '%d profils archivés')) ?></a>, <?= e(t('hors de ces chiffres.')) ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Temps moyen passé à chaque étape')) ?></h2>
|
||||
<?php if (!$s['delais']): ?>
|
||||
<p class="vide"><?= e(t('Aucun changement de statut enregistré. Les délais se calculent à partir du premier passage d\'une étape à l\'autre.')) ?></p>
|
||||
<?php else: ?>
|
||||
<table class="mesures">
|
||||
<tbody>
|
||||
<?php $m = max(array_column($s['delais'], 'jours')); foreach ($s['delais'] as $code => $d): ?>
|
||||
<tr>
|
||||
<th scope="row"><?= echelle($code) ?><?= e(t(STATUTS[$code])) ?></th>
|
||||
<td><?= $barre((float) $d['jours'], (float) $m) ?></td>
|
||||
<td class="nombre"><?= e(t('%s j', nombre($d['jours']))) ?> <small><?= e(tn((int) $d['passages'], 'sur %d passage', 'sur %d passages')) ?></small></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="aide"><?= e(t('Durée entre l\'entrée dans un statut et sa sortie. Les profils supprimés ne comptent plus.')) ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Séniorité')) ?></h2>
|
||||
<table class="mesures">
|
||||
<tbody>
|
||||
<?php $m = $max($s['par_seniorite']); foreach (SENIORITES as $code => $libelle): ?>
|
||||
<tr>
|
||||
<th scope="row"><a href="<?= e(url('recherche', ['seniorite' => $code])) ?>"><?= e(t($libelle)) ?></a></th>
|
||||
<td><?= $barre($s['par_seniorite'][$code], $m) ?></td>
|
||||
<td class="nombre"><?= (int) $s['par_seniorite'][$code] ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Technologies les plus présentes')) ?></h2>
|
||||
<?php if (!$s['technologies']): ?><p class="vide"><?= e(t('Aucune technologie enregistrée.')) ?></p><?php else: ?>
|
||||
<table class="mesures">
|
||||
<tbody>
|
||||
<?php $m = (int) $s['technologies'][0]['n']; foreach ($s['technologies'] as $t): ?>
|
||||
<tr>
|
||||
<th scope="row"><a href="<?= e(url('recherche', ['q' => $t['nom']])) ?>"><?= e($t['nom']) ?></a></th>
|
||||
<td><?= $barre((float) $t['n'], (float) $m) ?></td>
|
||||
<td class="nombre"><?= (int) $t['n'] ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
/** Champ « votre mot de passe » des actions sensibles. */
|
||||
$confirmer = static function (string $aide = ''): void { ?>
|
||||
<label><?= e(t('Votre mot de passe, pour confirmer')) ?>
|
||||
<input type="password" name="mon_mot_de_passe" required autocomplete="current-password">
|
||||
<?php if ($aide !== ''): ?><small class="aide-champ"><?= e($aide) ?></small><?php endif; ?>
|
||||
</label>
|
||||
<?php };
|
||||
?>
|
||||
<div class="barre-titre">
|
||||
<h1><?= e(t('Comptes')) ?> <small><?= count($liste) ?></small></h1>
|
||||
<a class="bouton secondaire" href="<?= e(url('connexions')) ?>"><?= e(t('Journal des connexions')) ?></a>
|
||||
</div>
|
||||
<p class="aide"><?= e(t('Chaque compte lit et modifie tous les profils. Une personne rejoint Vivier par un lien d\'invitation, ou vous créez son compte ici. À la première connexion, chacun active la double authentification.')) ?></p>
|
||||
|
||||
<section class="carte mise-en-avant" id="invitations">
|
||||
<h2><?= e(t('Inviter une personne')) ?></h2>
|
||||
<?php if ($lien): ?>
|
||||
<div class="lien-invitation" role="status">
|
||||
<p><strong><?= e($lien['nom'] !== '' ? t('Lien d\'invitation pour %s', $lien['nom']) : t('Lien d\'invitation')) ?></strong></p>
|
||||
<p class="aide"><?= e(t('Copiez-le et envoyez-le à la personne, par email ou messagerie interne. Il ne sera plus affiché. Il sert une fois et expire dans %d heures.', INVITATION_HEURES)) ?></p>
|
||||
<div class="copie">
|
||||
<input type="text" readonly value="<?= e($lien['lien']) ?>" aria-label="<?= e(t('Lien d\'invitation')) ?>">
|
||||
<button type="button" data-copier="<?= e($lien['lien']) ?>" data-copie="<?= e(t('Copié')) ?>"><?= e(t('Copier')) ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="ajout-ligne">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="inviter">
|
||||
<label><?= e(t('Nom de la personne, facultatif')) ?> <input type="text" name="nom" maxlength="120" autocomplete="off"></label>
|
||||
<button type="submit"><?= e(t('Créer un lien d\'invitation')) ?></button>
|
||||
</form>
|
||||
<p class="aide"><?= e(t('La personne choisit son identifiant et son mot de passe, puis active la double authentification. Son compte est actif tout de suite.')) ?></p>
|
||||
<?php if ($invitations): ?>
|
||||
<h3 class="intertitre"><?= e(t('Liens en cours')) ?></h3>
|
||||
<ul class="simple">
|
||||
<?php foreach ($invitations as $i): ?>
|
||||
<li class="ligne-action">
|
||||
<span><?= e($i['nom_affiche'] ?: t('Sans nom')) ?> <small><?= e(t('créé le %s par %s, expire le %s', date_fr($i['cree_le'], true), $i['par'], date_fr($i['expire_le'], true))) ?></small></span>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>" data-confirmer="<?= e(t('Annuler ce lien ? Il ne fonctionnera plus.')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="annuler_invitation">
|
||||
<input type="hidden" name="invitation" value="<?= (int) $i['id'] ?>">
|
||||
<button type="submit" class="lien"><?= e(t('Annuler le lien')) ?></button>
|
||||
</form>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if ($demandes): ?>
|
||||
<section class="carte demandes" id="demandes">
|
||||
<h2><?= e(t('Demandes à valider')) ?> <small><?= count($demandes) ?></small></h2>
|
||||
<p class="aide"><?= e(t('Demandes faites avant la version 1.8, par l\'ancienne page publique. Elles ne donnent aucun accès avant votre accord. Vivier ne prévient pas la personne : faites-le vous-même.')) ?></p>
|
||||
<ul class="liste-demandes">
|
||||
<?php foreach ($demandes as $d): ?>
|
||||
<li>
|
||||
<div>
|
||||
<p class="compte-nom"><?= e($d['nom_affiche']) ?> <span class="badge badge-attente"><?= e(t('En attente')) ?></span></p>
|
||||
<p class="compte-meta">
|
||||
<span><?= e(t('Identifiant')) ?> : <strong><?= e($d['identifiant']) ?></strong></span>
|
||||
<span><?= e(t('Demande du %s', date_fr($d['cree_le'], true))) ?></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="actions-ligne">
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $d['id'] ?>">
|
||||
<button type="submit" name="action" value="accepter"><?= e(t('Accepter')) ?></button>
|
||||
</form>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>" data-confirmer="<?= e(t('Refuser cette demande ? Elle est effacée.')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="id" value="<?= (int) $d['id'] ?>">
|
||||
<button type="submit" name="action" value="refuser" class="dangereux"><?= e(t('Refuser')) ?></button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($blocages): ?>
|
||||
<section class="carte attention" id="blocages">
|
||||
<h2><?= e(t('Connexions bloquées')) ?> <small><?= count($blocages) ?></small></h2>
|
||||
<p class="aide"><?= e(t('Après 5 échecs, un identifiant est bloqué 15 minutes depuis l\'adresse en cause, puis 30 et 60 minutes. Une adresse qui accumule 20 échecs en une heure est bloquée pour tous les identifiants. Débloquez seulement si vous savez qui essaie.')) ?></p>
|
||||
<div class="defile">
|
||||
<table>
|
||||
<thead><tr><th><?= e(t('Bloqué')) ?></th><th><?= e(t('Adresse IP')) ?></th><th><?= e(t('Échecs sur 24 h')) ?></th><th><?= e(t('Jusqu\'à')) ?></th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($blocages as $b): ?>
|
||||
<tr>
|
||||
<td><?= $b['type'] === 'compte' ? e(t('Identifiant %s', $b['identifiant'])) : e(t('Toute l\'adresse')) ?></td>
|
||||
<td><?= e($b['ip']) ?></td>
|
||||
<td><?= (int) $b['echecs'] ?></td>
|
||||
<td><?= e(date('H:i', $b['fin'])) ?></td>
|
||||
<td class="actions-ligne">
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="debloquer">
|
||||
<input type="hidden" name="cle" value="<?= e($b['cle']) ?>">
|
||||
<button type="submit" class="lien"><?= e(t('Débloquer')) ?></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="deux-colonnes inverse">
|
||||
<section class="carte">
|
||||
<h2><?= e(t('Créer un compte')) ?></h2>
|
||||
<p class="aide"><?= e(t('Préférez le lien d\'invitation : personne d\'autre que la personne ne connaît alors son mot de passe. Un compte créé ici change son mot de passe à la première connexion.')) ?></p>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="creer">
|
||||
<label><?= e(t('Nom de la personne')) ?> <input type="text" name="nom" required autocomplete="off"></label>
|
||||
<label><?= e(t('Identifiant')) ?> <input type="text" name="identifiant" required autocomplete="off" autocapitalize="none" spellcheck="false" placeholder="<?= e(t('prenom.nom')) ?>"></label>
|
||||
<label><?= e(t('Mot de passe provisoire, %d caractères au moins', LONGUEUR_MOT_DE_PASSE)) ?>
|
||||
<input type="text" name="mot_de_passe" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="off" spellcheck="false">
|
||||
</label>
|
||||
<button type="submit" class="secondaire"><?= e(t('Créer le compte')) ?></button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<ul class="comptes">
|
||||
<?php foreach ($liste as $l): $moiMeme = (int) $l['id'] === (int) $moi['id']; ?>
|
||||
<li class="carte<?= $l['actif'] ? '' : ' inactif' ?>">
|
||||
<div class="compte-tete">
|
||||
<div>
|
||||
<p class="compte-nom"><?= e($l['nom_affiche']) ?><?= $moiMeme ? ' <span class="badge">' . e(t('vous')) . '</span>' : '' ?></p>
|
||||
<p class="compte-meta">
|
||||
<span><?= e(t('Identifiant')) ?> : <strong><?= e($l['identifiant']) ?></strong></span>
|
||||
<?php if ($l['role'] === 'admin'): ?><span><?= e(t('Gère les comptes')) ?></span><?php endif; ?>
|
||||
<?php if (!$l['actif']): ?><span class="retard"><?= e(t('Désactivé')) ?></span><?php endif; ?>
|
||||
<span><?= $l['totp_active_le'] ? e(t('Double authentification active')) : '<span class="badge badge-attente">' . e(t('Double authentification à activer')) . '</span>' ?></span>
|
||||
<?php if ($l['changer_mot_de_passe']): ?><span class="badge badge-attente"><?= e(t('Mot de passe à changer')) ?></span><?php endif; ?>
|
||||
<span><?= e(t('Dernière connexion')) ?> : <?= e(date_fr($l['derniere_connexion'], true) ?: t('jamais')) ?></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<details>
|
||||
<summary><?= e(t('Modifier le nom, l\'identifiant ou le mot de passe')) ?></summary>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="modifier">
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<div class="champs">
|
||||
<label><?= e(t('Nom')) ?> <input type="text" name="nom" required value="<?= e($l['nom_affiche']) ?>" autocomplete="off"></label>
|
||||
<label><?= e(t('Identifiant')) ?> <input type="text" name="identifiant" required value="<?= e($l['identifiant']) ?>" autocomplete="off" autocapitalize="none" spellcheck="false"></label>
|
||||
<label class="large"><?= e(t('Nouveau mot de passe provisoire, à laisser vide pour le garder')) ?>
|
||||
<input type="text" name="mot_de_passe" minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="off" spellcheck="false">
|
||||
<small class="aide-champ"><?= e($moiMeme ? t('Pour votre propre mot de passe, passez plutôt par Mon compte.') : t('La personne le changera à sa prochaine connexion. Ses sessions ouvertes se ferment.')) ?></small>
|
||||
</label>
|
||||
<label class="large"><?= e(t('Votre mot de passe, seulement pour un nouveau mot de passe')) ?>
|
||||
<input type="password" name="mon_mot_de_passe" autocomplete="current-password">
|
||||
</label>
|
||||
</div>
|
||||
<div class="actions gauche"><button type="submit" class="secondaire"><?= e(t('Enregistrer')) ?></button></div>
|
||||
</form>
|
||||
</details>
|
||||
<?php if (!$moiMeme): ?>
|
||||
<?php if ($l['totp_active_le']): ?>
|
||||
<details>
|
||||
<summary><?= e(t('Remettre à zéro la double authentification')) ?></summary>
|
||||
<p class="aide"><?= e(t('Pour une personne qui a perdu son téléphone et ses codes de secours. Elle activera une nouvelle application à sa prochaine connexion. Vérifiez d\'abord son identité, par téléphone ou en personne.')) ?></p>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="double_auth">
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<?php $confirmer(); ?>
|
||||
<div class="actions gauche"><button type="submit" class="dangereux"><?= e(t('Remettre à zéro')) ?></button></div>
|
||||
</form>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
<details>
|
||||
<summary><?= e($l['role'] === 'admin' ? t('Retirer la gestion des comptes') : t('Confier la gestion des comptes')) ?></summary>
|
||||
<p class="aide"><?= e(t('Un gestionnaire crée et modifie les comptes, supprime des profils, voit le journal, les connexions et les sauvegardes.')) ?></p>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="gestionnaire">
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<?php $confirmer(); ?>
|
||||
<div class="actions gauche"><button type="submit" class="secondaire"><?= e($l['role'] === 'admin' ? t('Retirer') : t('Confier')) ?></button></div>
|
||||
</form>
|
||||
</details>
|
||||
<div class="actions-ligne">
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>"<?= $l['actif'] ? ' data-confirmer="' . e(t('Désactiver ce compte ? Ses sessions se ferment tout de suite.')) . '"' : '' ?>>
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="basculer">
|
||||
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
|
||||
<button type="submit" class="lien"><?= e($l['actif'] ? t('Désactiver le compte') : t('Réactiver le compte')) ?></button>
|
||||
</form>
|
||||
<a href="<?= e(url('connexions', ['utilisateur' => $l['id']])) ?>"><?= e(t('Ses connexions')) ?></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<section class="carte" id="maintenance">
|
||||
<h2><?= e(t('Maintenance')) ?></h2>
|
||||
<div class="deux-egales">
|
||||
<div>
|
||||
<h3 class="intertitre"><?= e(t('Traduction des profils')) ?></h3>
|
||||
<p class="aide"><?= e(Claude::configure()
|
||||
? t('Chaque profil est traduit dans les langues de l\'interface, en arrière-plan, tant qu\'une page de Vivier reste ouverte. À lancer une fois après la mise à jour 1.8.')
|
||||
: t('L\'analyse automatique n\'est pas configurée : les profils se traduisent un par un, par fichier, depuis l\'aperçu du CV IT Club.')) ?></p>
|
||||
<p><?= e(tn($aTraduire, '%d traduction à faire.', '%d traductions à faire.')) ?></p>
|
||||
<?php if ($echecsTraduction): ?>
|
||||
<p class="retard"><?= e(tn(count($echecsTraduction), '%d traduction abandonnée après 3 essais :', '%d traductions abandonnées après 3 essais :')) ?></p>
|
||||
<ul class="simple">
|
||||
<?php foreach (array_slice($echecsTraduction, 0, 10) as $f): ?>
|
||||
<li><a href="<?= e(url('profil', ['id' => $f['candidat_id']])) ?>"><?= e($f['prenom'] . ' ' . $f['nom']) ?></a> <small><?= e(strtoupper($f['langue'])) ?> · <?= e((string) $f['erreur']) ?></small></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
<?php if (Claude::configure()): ?>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="traduire_tout">
|
||||
<button type="submit" class="secondaire"><?= e(t('Traduire tous les profils')) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="intertitre"><?= e(t('Index de recherche')) ?></h3>
|
||||
<p class="aide"><?= e(t('À relancer après une mise à jour de Vivier.')) ?></p>
|
||||
<form method="post" action="<?= e(url('utilisateurs')) ?>">
|
||||
<?= champ_csrf() ?>
|
||||
<input type="hidden" name="action" value="reindexer">
|
||||
<button type="submit" class="secondaire"><?= e(t('Réindexer tous les profils')) ?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="porte">
|
||||
<img src="assets/logo-itclub-or.png" alt="<?= e(t('IT Club')) ?>" width="224" height="70">
|
||||
<p class="porte-nom"><?= e(config('nom_app')) ?></p>
|
||||
<p class="porte-role"><?= e(t('Saisissez le code à 6 chiffres affiché par votre application d\'authentification.')) ?></p>
|
||||
<form method="post" action="<?= e(url('verification')) ?>" class="pile">
|
||||
<?= champ_csrf() ?>
|
||||
<?php if ($erreur): ?><p class="flash flash-erreur" role="alert"><?= e($erreur) ?></p><?php endif; ?>
|
||||
<label><?= e(t('Code')) ?>
|
||||
<input type="text" name="code" required autofocus autocomplete="one-time-code" inputmode="numeric" maxlength="12" class="code-saisie"
|
||||
placeholder="123456" spellcheck="false" autocapitalize="none">
|
||||
</label>
|
||||
<button type="submit"><?= e(t('Se connecter')) ?></button>
|
||||
</form>
|
||||
<details class="porte-details">
|
||||
<summary><?= e(t('Téléphone perdu ou pas sous la main ?')) ?></summary>
|
||||
<p><?= e(t('Saisissez un de vos codes de secours dans le même champ, par exemple « abcd-efgh ». Chaque code ne sert qu\'une fois.')) ?></p>
|
||||
<p><?= e(t('Plus de codes de secours ? Le gestionnaire de Vivier remet votre double authentification à zéro : vous en activerez une nouvelle à la connexion suivante.')) ?></p>
|
||||
</details>
|
||||
<p class="porte-lien"><a href="<?= e(url('connexion')) ?>"><?= e(t('Retour à la connexion')) ?></a></p>
|
||||
</div>
|
||||
Reference in new issue
Block a user