AI Analysis: Service and UI implementation
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user