AI Analysis: Service and UI implementation

This commit is contained in:
jeremy bayse
2026-03-22 22:25:43 +01:00
parent 878f4bb102
commit 6c1f6af523
4 changed files with 278 additions and 0 deletions

View File

@@ -119,6 +119,31 @@ const updateAnswerScore = (answerId, score) => {
preserveScroll: true,
});
};
const aiAnalysis = ref(null);
const isAnalyzing = ref(false);
const runAI = async () => {
if (!props.candidate.job_position_id) {
alert("Veuillez d'abord associer une fiche de poste à ce candidat.");
return;
}
isAnalyzing.value = true;
try {
const response = await fetch(route('admin.candidates.analyze', props.candidate.id));
const data = await response.json();
if (data.error) {
alert(data.error);
} else {
aiAnalysis.value = data;
}
} catch (error) {
alert("Une erreur est survenue lors de l'analyse.");
} finally {
isAnalyzing.value = false;
}
};
</script>
<template>
@@ -415,6 +440,106 @@ const updateAnswerScore = (answerId, score) => {
</div>
</div>
<!-- AI Analysis Section -->
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-700 p-8 overflow-hidden relative">
<div class="flex items-center justify-between mb-8">
<div>
<h4 class="text-xl font-bold flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
Analyse d'Adéquation IA
</h4>
<p class="text-xs text-slate-400 mt-1 uppercase font-bold tracking-widest">Matching CV/Lettre vs Fiche de poste</p>
</div>
<PrimaryButton
@click="runAI"
:disabled="isAnalyzing"
class="!bg-indigo-600 hover:!bg-indigo-500 !border-none !rounded-xl group"
>
<span v-if="isAnalyzing" class="flex items-center gap-2">
<svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Analyse en cours...
</span>
<span v-else class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 group-hover:scale-110 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Lancer l'analyse intelligente
</span>
</PrimaryButton>
</div>
<!-- AI Results -->
<div v-if="aiAnalysis" class="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-slate-50 dark:bg-slate-900/50 p-6 rounded-3xl border border-slate-100 dark:border-slate-800 text-center flex flex-col items-center justify-center">
<div class="text-[10px] font-black uppercase tracking-widest text-slate-400 mb-4">Score d'Adéquation</div>
<div class="text-5xl font-black text-indigo-600 mb-2">{{ aiAnalysis.match_score }}%</div>
<div
class="px-3 py-1 rounded-full text-[10px] font-black uppercase tracking-widest"
:class="[
aiAnalysis.match_score >= 80 ? 'bg-emerald-100 text-emerald-700' :
aiAnalysis.match_score >= 60 ? 'bg-amber-100 text-amber-700' :
'bg-red-100 text-red-700'
]"
>
{{ aiAnalysis.verdict }}
</div>
</div>
<div class="md:col-span-2 bg-slate-50 dark:bg-slate-900/50 p-6 rounded-3xl border border-slate-100 dark:border-slate-800">
<div class="text-[10px] font-black uppercase tracking-widest text-slate-400 mb-4">Synthèse du Profil</div>
<p class="text-sm leading-relaxed text-slate-600 dark:text-slate-400 italic">
" {{ aiAnalysis.summary }} "
</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="space-y-4">
<h5 class="flex items-center gap-2 text-xs font-black uppercase tracking-widest text-emerald-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Points Forts Identifiés
</h5>
<ul class="space-y-3">
<li v-for="(strength, idx) in aiAnalysis.strengths" :key="idx" class="flex items-start gap-3 p-3 bg-emerald-50/50 dark:bg-emerald-900/10 rounded-2xl border border-emerald-100 dark:border-emerald-800/50 text-[13px]">
<span class="text-emerald-500 font-bold mt-0.5"></span>
<span class="text-emerald-800 dark:text-emerald-400">{{ strength }}</span>
</li>
</ul>
</div>
<div class="space-y-4">
<h5 class="flex items-center gap-2 text-xs font-black uppercase tracking-widest text-amber-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
Points de Vigilance / Gaps
</h5>
<ul class="space-y-3">
<li v-for="(gap, idx) in aiAnalysis.gaps" :key="idx" class="flex items-start gap-3 p-3 bg-amber-50/50 dark:bg-amber-900/10 rounded-2xl border border-amber-100 dark:border-amber-800/50 text-[13px]">
<span class="text-amber-500 font-bold mt-0.5"></span>
<span class="text-amber-800 dark:text-amber-400">{{ gap }}</span>
</li>
</ul>
</div>
</div>
</div>
<div v-else-if="!isAnalyzing" class="py-12 border-2 border-dashed border-slate-100 dark:border-slate-800 rounded-3xl text-center">
<p class="text-slate-400 text-sm font-medium">Aucune analyse effectuée pour le moment.</p>
</div>
<div v-if="isAnalyzing" class="absolute inset-0 bg-white/60 dark:bg-slate-800/60 backdrop-blur-[2px] z-10 flex flex-col items-center justify-center gap-4">
<div class="flex gap-1 animate-pulse"><div class="w-2 h-2 bg-indigo-500 rounded-full"></div><div class="w-2 h-2 bg-indigo-500 rounded-full"></div><div class="w-2 h-2 bg-indigo-500 rounded-full"></div></div>
<p class="text-sm font-black text-indigo-600 uppercase tracking-widest">Analyse en cours...</p>
</div>
</div>
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-700 p-8">
<h3 class="text-xl font-bold mb-8 flex items-center justify-between">
Historique des Tests