Ajout du score global et du tri dynamique dans la liste des candidats
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user