Initialer Laravel Commit für BetiX
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled

This commit is contained in:
2026-04-04 18:01:50 +02:00
commit 0280278978
374 changed files with 65210 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
import { useTwoFactorAuth } from '@/composables/useTwoFactorAuth';
import { regenerateRecoveryCodes } from '@/routes/two-factor';
const loading = ref(false);
const regenerating = ref(false);
const { recoveryCodesList, fetchRecoveryCodes, errors, clearErrors } = useTwoFactorAuth();
async function loadCodes() {
loading.value = true;
clearErrors();
try {
await fetchRecoveryCodes();
} finally {
loading.value = false;
nextTick(() => { if ((window as any).lucide) (window as any).lucide.createIcons(); });
}
}
async function regenerate() {
regenerating.value = true;
clearErrors();
try {
const res = await fetch(regenerateRecoveryCodes.url(), {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement)?.content || '',
'Accept': 'application/json',
},
});
if (!res.ok) {
throw new Error('Failed to regenerate recovery codes');
}
await loadCodes();
} catch (e: any) {
errors.value.push(e?.message || 'Failed to regenerate recovery codes');
} finally {
regenerating.value = false;
}
}
onMounted(() => {
// Lazy-load by default; uncomment to auto-load
loadCodes();
});
</script>
<template>
<div class="rc-panel">
<div class="rc-head">
<div class="rc-title"><i data-lucide="key-round"></i> Recovery Codes</div>
<div class="rc-actions">
<button class="btn ghost" type="button" @click="loadCodes" :disabled="loading">
<span v-if="loading" class="spinner" />
<span>Show Codes</span>
</button>
<button class="btn danger" type="button" @click="regenerate" :disabled="regenerating">
<span v-if="regenerating" class="spinner" />
<span>Regenerate</span>
</button>
</div>
</div>
<div v-if="errors.length" class="rc-errors">
<div v-for="(err, i) in errors" :key="i" class="err-item">
<i data-lucide="alert-triangle"></i>{{ err }}
</div>
</div>
<div v-if="!recoveryCodesList.length && !loading" class="empty">
<i data-lucide="folder-open"></i>
<div>No recovery codes yet</div>
</div>
<ul v-else class="codes">
<li v-for="(code, idx) in recoveryCodesList" :key="idx" class="code">
<i data-lucide="shield"></i>
<span>{{ code }}</span>
</li>
</ul>
</div>
</template>
<style scoped>
:global(:root) { --bg-card:#0a0a0a; --border:#151515; --cyan:#00f2ff; --red:#ff5b5b; }
.rc-panel { border:1px solid var(--border); background:#0a0a0a; border-radius:14px; padding:16px; display:grid; gap:12px; }
.rc-head { display:flex; align-items:center; justify-content:space-between; gap:12px; }
.rc-title { display:flex; align-items:center; gap:8px; font-weight:900; color:#fff; }
.rc-title i { width:14px; color:#666; }
.rc-actions { display:flex; gap:8px; }
.btn { background: var(--cyan); color:#000; border:none; border-radius:10px; padding:10px 14px; font-weight:900; cursor:pointer; display:flex; align-items:center; gap:8px; }
.btn.ghost { background: #111; color:#ddd; border:1px solid #181818; }
.btn.danger { background: var(--red); color:#000; }
.btn:disabled { opacity:.6; cursor:not-allowed; }
.spinner { width: 14px; height: 14px; border: 2px solid rgba(0,0,0,0.1); border-top-color:#000; border-radius: 50%; animation: spin .8s linear infinite; }
.codes { list-style:none; padding:0; margin:0; display:grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap:10px; }
.code { display:flex; align-items:center; gap:10px; border:1px solid #151515; background:#050505; border-radius:10px; padding:10px 12px; font-weight:800; color:#eee; }
.code i { width:14px; color:#333; }
.empty { color:#666; display:flex; align-items:center; gap:10px; }
.empty i { width:18px; }
.rc-errors { display:grid; gap:6px; }
.err-item { display:flex; align-items:center; gap:8px; color:#ff5b5b; font-weight:800; }
.err-item i { width:14px; }
@keyframes spin { to { transform: rotate(360deg); } }
</style>