EduPlatform / frontend /src /views /admin /SecurityView.vue
erranova's picture
Upload 720 files
8fd091f verified
Raw History Blame Contribute Delete
4.33 kB
<template>
<div>
<div class="panel">
<div class="panel-header">
<h3>{{ t('admin.security.title') }}</h3>
<div class="panel-actions">
<span class="badge badge-red">{{ logs.length }} {{ t('admin.security.entries') }}</span>
<button class="btn-clear" @click="clearLogs" :disabled="clearing || logs.length === 0" id="btn-clear-security">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
{{ t('admin.security.clear') }}
</button>
</div>
</div>
<div class="loading-state" v-if="loading"><div class="spinner"></div></div>
<div class="log-table-wrap" v-else>
<table>
<thead>
<tr>
<th>{{ t('admin.security.action') }}</th>
<th>{{ t('admin.security.user') }}</th>
<th>{{ t('admin.security.ip') }}</th>
<th>{{ t('admin.security.date') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="log in logs" :key="log.id" :class="rowClass(log.action)">
<td><span :class="actionBadge(log.action)">{{ log.action }}</span></td>
<td>{{ log.user }}</td>
<td><code>{{ log.ip_address }}</code></td>
<td>{{ formatTime(log.timestamp) }}</td>
</tr>
</tbody>
</table>
<div class="empty-state" v-if="logs.length === 0"><p>{{ t('admin.security.empty') }}</p></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import api from '@/api/client'
const { t } = useI18n()
const logs = ref([])
const loading = ref(true)
const clearing = ref(false)
function formatTime(t) { return new Date(t).toLocaleString('fr-MA') }
function rowClass(a) { return a.includes('failed') || a.includes('locked') || a.includes('suspicious') ? 'danger-row' : '' }
function actionBadge(a) {
if (a.includes('success') || a.includes('réussie')) return 'badge badge-green'
if (a.includes('failed') || a.includes('locked') || a.includes('échouée')) return 'badge badge-red'
return 'badge badge-gold'
}
async function fetchLogs() {
loading.value = true
try {
const { data } = await api.get('/auth/admin/security-logs/')
logs.value = data
} catch(e) { console.error(e) } finally { loading.value = false }
}
async function clearLogs() {
if (!confirm(t('admin.security.clear_confirm'))) return
clearing.value = true
try {
await api.delete('/auth/admin/security-logs/')
logs.value = []
} catch(e) { console.error(e) } finally { clearing.value = false }
}
onMounted(fetchLogs)
</script>
<style scoped>
.panel { background:#fff; border-radius:14px; padding:24px; box-shadow:0 2px 12px rgba(0,0,0,0.06); }
.panel-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; flex-wrap: wrap; gap: 12px; }
.panel-header h3 { font-family:'Playfair Display',serif; font-size:1.05rem; color:var(--ink); }
.panel-actions { display: flex; align-items: center; gap: 12px; }
.btn-clear { display: flex; align-items: center; gap: 6px; padding: 6px 14px; background: rgba(229,62,62,0.06); color: var(--error); border: 1px solid rgba(229,62,62,0.2); border-radius: 8px; font-size: 0.8rem; font-weight: 600; font-family: 'DM Sans', sans-serif; cursor: pointer; transition: all 0.25s; }
.btn-clear:hover:not(:disabled) { background: var(--error); color: #fff; border-color: var(--error); }
.btn-clear:disabled { opacity: 0.4; cursor: not-allowed; }
.log-table-wrap { overflow-x:auto; }
table { width:100%; border-collapse:collapse; font-size:0.85rem; }
th { padding:10px 14px; background:#f8f5ee; text-align:left; font-size:0.76rem; text-transform:uppercase; letter-spacing:0.05em; color:var(--muted); }
td { padding:11px 14px; border-top:1px solid #f0ece2; }
code { background:var(--cream); padding:2px 8px; border-radius:6px; font-size:0.82rem; }
.danger-row td { background:rgba(229,62,62,0.03); }
.loading-state { display:flex; justify-content:center; padding:40px; }
.empty-state { text-align:center; padding:30px; color:var(--muted); }
</style>