feat: SAC 0.7.3 custom error pages 401/403/404/429
Centered logo pages with event explanation and allowed actions; router and apiFetch redirect to them on auth, permission, not-found, and rate-limit errors. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="error-page">
|
||||
<div class="error-card card">
|
||||
<img src="/sac-icon.png" alt="" class="error-logo" width="96" height="96" />
|
||||
<p class="error-code">{{ config.code }}</p>
|
||||
<h1 class="error-title">{{ config.title }}</h1>
|
||||
<p class="error-lead">{{ config.lead }}</p>
|
||||
<p class="error-hint">{{ config.hint }}</p>
|
||||
<div class="error-actions">
|
||||
<RouterLink :to="config.primaryAction.to" class="error-btn error-btn-primary">
|
||||
{{ config.primaryAction.label }}
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="showSecondary"
|
||||
:to="config.secondaryAction!.to"
|
||||
class="error-btn error-btn-secondary"
|
||||
>
|
||||
{{ config.secondaryAction!.label }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
<p class="error-brand muted">Security Alert Center</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { getErrorPageConfig } from "../errorPages";
|
||||
|
||||
const props = defineProps<{
|
||||
code: number;
|
||||
}>();
|
||||
|
||||
const config = computed(() => getErrorPageConfig(props.code));
|
||||
|
||||
const showSecondary = computed(() => Boolean(config.value.secondaryAction));
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.error-card {
|
||||
max-width: 520px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding: 2rem 1.75rem;
|
||||
}
|
||||
|
||||
.error-logo {
|
||||
border-radius: 16px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
margin: 0;
|
||||
font-size: 3rem;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
color: #7eb8ff;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
margin: 0.75rem 0 0;
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.error-lead {
|
||||
margin: 1rem 0 0;
|
||||
color: #e8eaed;
|
||||
}
|
||||
|
||||
.error-hint {
|
||||
margin: 1rem 0 0;
|
||||
padding: 0.85rem 1rem;
|
||||
background: #0f1419;
|
||||
border: 1px solid #2a3441;
|
||||
border-radius: 8px;
|
||||
color: #9aa4b2;
|
||||
font-size: 0.92rem;
|
||||
text-align: left;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.error-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.65rem;
|
||||
justify-content: center;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.error-btn {
|
||||
display: inline-block;
|
||||
padding: 0.55rem 1.1rem;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.error-btn-primary {
|
||||
background: #2563eb;
|
||||
border: 1px solid #2563eb;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.error-btn-secondary {
|
||||
background: transparent;
|
||||
border: 1px solid #3d4f63;
|
||||
color: #e8eaed;
|
||||
}
|
||||
|
||||
.error-brand {
|
||||
margin: 1.5rem 0 0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user