feat: implement candidate security honeypots and redesign authenticated layout
This commit is contained in:
@@ -356,6 +356,22 @@ const barColor = (pct) => pct >= 80 ? 'bg-success' : pct >= 60 ? 'bg-highlight'
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Security Alert Badge -->
|
||||
<div v-if="candidate.user.security_alerts?.length" class="bg-accent/10 border border-accent/20 rounded-2xl p-5 relative overflow-hidden">
|
||||
<div class="absolute top-0 right-0 w-24 h-24 bg-[radial-gradient(circle_at_top_right,_var(--tw-gradient-stops))] from-accent/20 to-transparent"></div>
|
||||
<div class="flex items-center gap-3 mb-2 relative z-10">
|
||||
<div class="w-8 h-8 rounded-full bg-accent/20 flex items-center justify-center shrink-0">
|
||||
<svg class="w-4 h-4 text-accent" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||||
</div>
|
||||
<p class="text-[11px] font-black uppercase tracking-[0.1em] text-accent leading-tight">
|
||||
{{ candidate.user.security_alerts.length }} Alerte{{ candidate.user.security_alerts.length > 1 ? 's' : '' }} de sécurité
|
||||
</p>
|
||||
</div>
|
||||
<button @click="activeTab = 'security'" class="relative z-10 mt-1 text-[10px] font-bold uppercase tracking-widest text-accent/70 hover:text-accent transition-colors flex items-center gap-1">
|
||||
Voir les détails <svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- AI Summary card (if analysed) -->
|
||||
<div v-if="aiAnalysis" class="bg-surface rounded-2xl border border-ink/[0.07] shadow-sm p-5">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
@@ -410,7 +426,8 @@ const barColor = (pct) => pct >= 80 ? 'bg-success' : pct >= 60 ? 'bg-highlight'
|
||||
{ id:'interview', label:'Évaluation' },
|
||||
{ id:'documents', label:'Documents', count: candidate.documents?.length },
|
||||
{ id:'tests', label:'Tests', count: candidate.attempts?.length },
|
||||
]" :key="tab.id" @click="activeTab = tab.id"
|
||||
{ id:'security', label:'Sécurité', count: candidate.user.security_alerts?.length },
|
||||
].filter(t => t.id !== 'security' || t.count > 0)" :key="tab.id" @click="activeTab = tab.id"
|
||||
class="relative flex items-center gap-2 px-5 py-4 text-[11px] font-black uppercase tracking-[0.1em] whitespace-nowrap transition-all duration-150"
|
||||
:class="activeTab === tab.id ? 'text-primary' : 'text-ink/35 hover:text-ink/60'">
|
||||
{{ tab.label }}
|
||||
@@ -773,6 +790,55 @@ const barColor = (pct) => pct >= 80 ? 'bg-success' : pct >= 60 ? 'bg-highlight'
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Tab: Sécurité ── -->
|
||||
<div v-if="activeTab === 'security'" class="p-6 bg-accent/[0.02]">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-serif font-black text-accent flex items-center gap-2">
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||||
Alertes de Sécurité
|
||||
</h3>
|
||||
<p class="text-xs text-ink/50 mt-1 font-semibold">Le candidat a déclenché un ou plusieurs honeypots sur la plateforme.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div v-for="alert in candidate.user.security_alerts" :key="alert.id" class="p-5 rounded-2xl border border-accent/20 bg-white shadow-sm overflow-hidden relative group">
|
||||
<div class="absolute left-0 top-0 bottom-0 w-1.5 bg-accent"></div>
|
||||
|
||||
<div class="flex items-center justify-between mb-4 pl-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2.5 py-1 rounded bg-accent/10 text-accent text-[10px] font-black uppercase tracking-widest border border-accent/20">
|
||||
{{ alert.type.replace('_', ' ') }}
|
||||
</span>
|
||||
<span class="text-xs font-bold text-ink/70 flex items-center gap-1.5">
|
||||
<svg class="w-3.5 h-3.5 text-ink/30" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
{{ formatDateTime(alert.created_at) }}
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-[10px] font-black font-mono text-ink/40 bg-ink/5 px-2 py-0.5 rounded">{{ alert.ip_address }}</span>
|
||||
</div>
|
||||
|
||||
<div class="pl-3 space-y-3">
|
||||
<div>
|
||||
<p class="text-[9px] font-black uppercase tracking-[0.16em] text-ink/35 mb-1">Endpoint Visé</p>
|
||||
<p class="text-xs font-mono font-bold text-ink/80 bg-neutral/50 px-3 py-2 rounded-lg border border-ink/5 inline-block">
|
||||
{{ alert.endpoint || 'Inconnu' }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="alert.payload && Object.keys(alert.payload).length > 0">
|
||||
<p class="text-[9px] font-black uppercase tracking-[0.16em] text-ink/35 mb-1">Payload / Paramètres</p>
|
||||
<pre class="text-[10px] text-ink/70 font-mono font-semibold bg-surface border border-ink/10 p-3 rounded-lg overflow-x-auto">{{ JSON.stringify(alert.payload, null, 2) }}</pre>
|
||||
</div>
|
||||
<div v-if="alert.user_agent">
|
||||
<p class="text-[9px] font-black uppercase tracking-[0.16em] text-ink/35 mb-1">Navigateur (User Agent)</p>
|
||||
<p class="text-[10px] text-ink/50 bg-neutral/30 px-3 py-2 rounded-lg truncate" :title="alert.user_agent">{{ alert.user_agent }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end tabs card -->
|
||||
</div><!-- end right panel -->
|
||||
</div><!-- end flex layout -->
|
||||
|
||||
Reference in New Issue
Block a user