Ajout du score global et du tri dynamique dans la liste des candidats

This commit is contained in:
jeremy bayse
2026-03-20 17:51:17 +01:00
parent 5c2bcb0169
commit 898b56c535
2 changed files with 95 additions and 13 deletions

View File

@@ -46,6 +46,37 @@ const deleteCandidate = (id) => {
const openPreview = (doc) => {
selectedDocument.value = doc;
};
// Sorting Logic
const sortKey = ref('user.name');
const sortOrder = ref(1); // 1 = asc, -1 = desc
const sortBy = (key) => {
if (sortKey.value === key) {
sortOrder.value *= -1;
} else {
sortKey.value = key;
sortOrder.value = 1;
}
};
const getNestedValue = (obj, path) => {
return path.split('.').reduce((o, i) => (o ? o[i] : null), obj);
};
const sortedCandidates = computed(() => {
return [...props.candidates].sort((a, b) => {
let valA = getNestedValue(a, sortKey.value);
let valB = getNestedValue(b, sortKey.value);
if (typeof valA === 'string') valA = valA.toLowerCase();
if (typeof valB === 'string') valB = valB.toLowerCase();
if (valA < valB) return -1 * sortOrder.value;
if (valA > valB) return 1 * sortOrder.value;
return 0;
});
});
</script>
<template>
@@ -81,34 +112,85 @@ const openPreview = (doc) => {
<table class="w-full text-left">
<thead class="bg-slate-50 dark:bg-slate-700/50 border-b border-slate-200 dark:border-slate-700">
<tr>
<th class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300">Nom</th>
<th class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300">Email</th>
<th class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300">Statut</th>
<th @click="sortBy('user.name')" class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-700/50 transition-colors">
<div class="flex items-center gap-2">
Nom
<svg v-show="sortKey === 'user.name'" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" :class="{ 'rotate-180': sortOrder === -1 }" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</div>
</th>
<th @click="sortBy('user.email')" class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-700/50 transition-colors">
<div class="flex items-center gap-2">
Email
<svg v-show="sortKey === 'user.email'" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" :class="{ 'rotate-180': sortOrder === -1 }" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</div>
</th>
<th @click="sortBy('status')" class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-700/50 transition-colors">
<div class="flex items-center gap-2">
Statut
<svg v-show="sortKey === 'status'" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" :class="{ 'rotate-180': sortOrder === -1 }" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</div>
</th>
<th @click="sortBy('weighted_score')" class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300 cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-700/50 transition-colors">
<div class="flex items-center gap-2">
Score /20
<svg v-show="sortKey === 'weighted_score'" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" :class="{ 'rotate-180': sortOrder === -1 }" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</div>
</th>
<th class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300">Documents</th>
<th class="px-6 py-4 font-semibold text-slate-700 dark:text-slate-300 text-right">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200 dark:divide-slate-700">
<tr v-for="candidate in candidates" :key="candidate.id" class="hover:bg-slate-50/50 dark:hover:bg-slate-700/30 transition-colors">
<tr v-for="candidate in sortedCandidates" :key="candidate.id" class="hover:bg-slate-50/50 dark:hover:bg-slate-700/30 transition-colors">
<td class="px-6 py-4">
<div class="font-medium">{{ candidate.user.name }}</div>
<div class="text-xs text-slate-500">{{ candidate.phone }}</div>
<div class="font-bold text-slate-900 dark:text-white">{{ candidate.user.name }}</div>
<div class="text-[10px] text-slate-500 font-bold uppercase tracking-tight">{{ candidate.phone }}</div>
</td>
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">
<td class="px-6 py-4 text-sm text-slate-600 dark:text-slate-400">
{{ candidate.user.email }}
</td>
<td class="px-6 py-4">
<td class="px-6 py-4 text-xs font-bold uppercase tracking-widest">
<span
class="px-2 py-1 rounded-full text-xs font-semibold"
class="px-3 py-1 rounded-lg"
:class="{
'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400': candidate.status === 'en_attente',
'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400': candidate.status === 'en_cours',
'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400': candidate.status === 'termine'
'bg-amber-50 text-amber-700 border border-amber-200 dark:bg-amber-900/20 dark:border-amber-800 dark:text-amber-400': candidate.status === 'en_attente',
'bg-indigo-50 text-indigo-700 border border-indigo-200 dark:bg-indigo-900/20 dark:border-indigo-800 dark:text-indigo-400': candidate.status === 'en_cours',
'bg-emerald-50 text-emerald-700 border border-emerald-200 dark:bg-emerald-900/20 dark:border-emerald-800 dark:text-emerald-400': candidate.status === 'termine'
}"
>
{{ candidate.status }}
</span>
</td>
<td class="px-6 py-4">
<div class="flex items-center gap-2">
<div class="w-12 h-1.5 bg-slate-100 dark:bg-slate-700 rounded-full overflow-hidden">
<div
class="h-full rounded-full transition-all duration-500"
:style="{ width: (candidate.weighted_score / 20) * 100 + '%' }"
:class="{
'bg-emerald-500': candidate.weighted_score >= 14,
'bg-amber-500': candidate.weighted_score >= 10 && candidate.weighted_score < 14,
'bg-rose-500': candidate.weighted_score < 10
}"
></div>
</div>
<span class="font-black text-sm" :class="{
'text-emerald-600': candidate.weighted_score >= 14,
'text-amber-600': candidate.weighted_score >= 10 && candidate.weighted_score < 14,
'text-rose-600': candidate.weighted_score < 10
}">
{{ candidate.weighted_score }}
</span>
</div>
</td>
<td class="px-6 py-4">
<div class="flex gap-2">
<button