125 lines
6.4 KiB
Vue
125 lines
6.4 KiB
Vue
<script setup>
|
|
import AdminLayout from '@/Layouts/AdminLayout.vue';
|
|
import { Head, Link } from '@inertiajs/vue3';
|
|
import { ref, computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
candidates: Array
|
|
});
|
|
|
|
const searchQuery = ref('');
|
|
|
|
const filteredCandidates = computed(() => {
|
|
return props.candidates.filter(c =>
|
|
c.user.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
|
c.user.email.toLowerCase().includes(searchQuery.value.toLowerCase())
|
|
).sort((a, b) => {
|
|
const scoreA = a.attempts[0]?.score || 0;
|
|
const scoreB = b.attempts[0]?.score || 0;
|
|
return scoreB - scoreA;
|
|
});
|
|
});
|
|
|
|
const getScorePercentage = (candidate) => {
|
|
const attempt = candidate.attempts[0];
|
|
if (!attempt || !attempt.quiz) return 0;
|
|
|
|
// Total points possible (this would ideally be passed from backend or calculated)
|
|
// For now, we assume attempt.score is the absolute score.
|
|
return attempt.score;
|
|
};
|
|
|
|
const formatDuration = (started, finished) => {
|
|
if (!started || !finished) return '--';
|
|
const start = new Date(started);
|
|
const end = new Date(finished);
|
|
const diff = Math.floor((end - start) / 1000 / 60);
|
|
return diff + ' min';
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Head title="Comparateur de Candidats" />
|
|
|
|
<AdminLayout>
|
|
<template #header>
|
|
Comparateur de Candidats
|
|
</template>
|
|
|
|
<div class="mb-8 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
|
<h3 class="text-2xl font-bold">Classement par Score</h3>
|
|
|
|
<div class="relative w-full sm:w-64">
|
|
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-slate-400">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
</svg>
|
|
</span>
|
|
<input
|
|
v-model="searchQuery"
|
|
type="text"
|
|
placeholder="Rechercher..."
|
|
class="block w-full pl-10 pr-3 py-2 border border-slate-300 dark:border-slate-700 rounded-lg leading-5 bg-white dark:bg-slate-800 placeholder-slate-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm transition-all"
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-700 overflow-hidden">
|
|
<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-bold text-slate-700 dark:text-slate-300 uppercase tracking-wider text-xs">Rang</th>
|
|
<th class="px-6 py-4 font-bold text-slate-700 dark:text-slate-300 uppercase tracking-wider text-xs">Candidat</th>
|
|
<th class="px-6 py-4 font-bold text-slate-700 dark:text-slate-300 uppercase tracking-wider text-xs text-center">Score</th>
|
|
<th class="px-6 py-4 font-bold text-slate-700 dark:text-slate-300 uppercase tracking-wider text-xs">Temps passé</th>
|
|
<th class="px-6 py-4 font-bold text-slate-700 dark:text-slate-300 uppercase tracking-wider text-xs">Test</th>
|
|
<th class="px-6 py-4 font-bold text-slate-700 dark:text-slate-300 uppercase tracking-wider text-xs text-right">Date</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-200 dark:divide-slate-700">
|
|
<tr v-for="(candidate, index) in filteredCandidates" :key="candidate.id" class="hover:bg-slate-50/50 dark:hover:bg-slate-700/30 transition-colors group">
|
|
<td class="px-6 py-4">
|
|
<div
|
|
class="w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm"
|
|
:class="[
|
|
index === 0 ? 'bg-yellow-100 text-yellow-700' :
|
|
index === 1 ? 'bg-slate-200 text-slate-700' :
|
|
index === 2 ? 'bg-orange-100 text-orange-700' : 'text-slate-500'
|
|
]"
|
|
>
|
|
{{ index + 1 }}
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<div class="font-bold text-slate-900 dark:text-slate-100">{{ candidate.user.name }}</div>
|
|
<div class="text-xs text-slate-500">{{ candidate.user.email }}</div>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<div class="flex flex-col items-center">
|
|
<span class="text-xl font-black text-indigo-600 dark:text-indigo-400">{{ candidate.attempts[0]?.score }}</span>
|
|
<span class="text-[10px] uppercase font-bold text-slate-400">Points</span>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 font-medium">
|
|
{{ formatDuration(candidate.attempts[0]?.started_at, candidate.attempts[0]?.finished_at) }}
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<span class="text-sm px-2 py-1 bg-slate-100 dark:bg-slate-700 rounded-md font-medium">
|
|
{{ candidate.attempts[0]?.quiz?.title }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 text-right text-slate-500 text-sm">
|
|
{{ new Date(candidate.attempts[0]?.finished_at).toLocaleDateString() }}
|
|
</td>
|
|
</tr>
|
|
<tr v-if="filteredCandidates.length === 0">
|
|
<td colspan="6" class="px-6 py-12 text-center text-slate-500 italic">
|
|
Aucun candidat n'a encore terminé de test.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</AdminLayout>
|
|
</template>
|