Modernize frontend: new design system, redesign all pages
- Convert codexInfo.md to CLAUDE.md as the central project context. - Rewrite GUI/style.md with a modernized, file-first direction (layered surfaces, refined typography, motion budget, ambient gradients). - Refresh global tokens in global.css, page-layouts.css and surface-patterns.css: extended palette (primary 050/800, surface elevated, border subtle), four-tier shadow system, dark-mode parity, new utilities (hoard-chip, hoard-icon-tile, hoard-spotlight, hoard-section-head, hoard-divider-soft, status pulse dot). - Rebuild Layout.vue: premium app shell with brand halo, animated active-indicator, account pill with avatar/initials, drawer footer card, refined banner stack and footer. - Redesign every route while preserving routing and API contracts: Home, Login, ChangePassword, Dashboard, AdminUsers, AdminUserDetail, Impressum, 404, Forbidden. Adds search/admin stats, password hint list, dashboard greeting with avatar, modernized hero/spotlight treatments and consistent mobile layouts (safe-areas, 44/48px tap targets). - Drop @fontsource/roboto import in vuetify.ts and load Inter via the rsms.me CSS in index.html; update Vuetify defaults and palette to match the new tokens.
This commit is contained in:
@@ -8,6 +8,9 @@ const isLoading = ref(true)
|
||||
const isAuthenticated = ref(false)
|
||||
|
||||
const primaryActionLabel = computed(() => (isAuthenticated.value ? 'Zum Dashboard' : 'Zum Login'))
|
||||
const primaryActionIcon = computed(() =>
|
||||
isAuthenticated.value ? 'mdi-view-dashboard-outline' : 'mdi-login',
|
||||
)
|
||||
|
||||
async function resolveAuthState() {
|
||||
try {
|
||||
@@ -29,6 +32,15 @@ async function navigatePrimaryAction() {
|
||||
await router.replace({ name: 'Login' })
|
||||
}
|
||||
|
||||
async function navigateBack() {
|
||||
if (window.history.length > 1) {
|
||||
router.back()
|
||||
return
|
||||
}
|
||||
|
||||
await router.replace({ name: isAuthenticated.value ? 'Dashboard' : 'Home' })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void resolveAuthState()
|
||||
})
|
||||
@@ -36,23 +48,34 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<v-container fluid class="forbidden-page hoard-page hoard-page--centered">
|
||||
<section class="forbidden-shell hoard-shell-grid hoard-panel">
|
||||
<section class="forbidden-shell hoard-panel hoard-panel-gradient hoard-spotlight">
|
||||
<div class="forbidden-icon">
|
||||
<span class="forbidden-icon__halo" aria-hidden="true" />
|
||||
<v-icon icon="mdi-shield-alert-outline" size="40" />
|
||||
</div>
|
||||
|
||||
<header class="forbidden-head">
|
||||
<p class="hoard-kicker">Fehlende Berechtigung</p>
|
||||
<h1>Kein Zugriff</h1>
|
||||
<p>Dein Konto hat keine ausreichende Rolle für diese Seite.</p>
|
||||
<p class="hoard-kicker hoard-kicker--wide">Fehlende Berechtigung</p>
|
||||
<h1>Kein Zugriff.</h1>
|
||||
<p>
|
||||
Dein Konto hat aktuell keine ausreichende Rolle, um diese Seite zu sehen. Falls das ein Fehler ist,
|
||||
wende dich an einen Admin – oder wechsle zurück in deinen freigegebenen Bereich.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="forbidden-actions hoard-action-row">
|
||||
<v-btn
|
||||
color="primary"
|
||||
prepend-icon="mdi-arrow-right"
|
||||
variant="elevated"
|
||||
:prepend-icon="primaryActionIcon"
|
||||
:loading="isLoading"
|
||||
:disabled="isLoading"
|
||||
@click="navigatePrimaryAction"
|
||||
>
|
||||
{{ primaryActionLabel }}
|
||||
</v-btn>
|
||||
<v-btn variant="outlined" prepend-icon="mdi-arrow-left" @click="navigateBack">
|
||||
Zurück
|
||||
</v-btn>
|
||||
</div>
|
||||
</section>
|
||||
</v-container>
|
||||
@@ -60,30 +83,91 @@ onMounted(() => {
|
||||
|
||||
<style scoped>
|
||||
.forbidden-page {
|
||||
--hoard-shell-width: min(640px, 100%);
|
||||
--hoard-centered-offset: 200px;
|
||||
}
|
||||
|
||||
.forbidden-shell {
|
||||
gap: var(--space-4);
|
||||
--hoard-gradient-angle: 130deg;
|
||||
--hoard-gradient-start: color-mix(in srgb, var(--color-warning) 14%, var(--color-surface) 86%);
|
||||
--hoard-gradient-end: var(--color-surface);
|
||||
--hoard-gradient-end-stop: 65%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-5);
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
width: min(640px, 100%);
|
||||
padding: var(--space-10) var(--space-7);
|
||||
border-radius: var(--radius-xl);
|
||||
}
|
||||
|
||||
.forbidden-head h1,
|
||||
.forbidden-head p {
|
||||
margin: 0;
|
||||
.forbidden-icon {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: var(--radius-full);
|
||||
background:
|
||||
linear-gradient(135deg, var(--color-warning), color-mix(in srgb, var(--color-warning) 60%, var(--color-danger) 40%));
|
||||
color: #fff;
|
||||
box-shadow:
|
||||
var(--shadow-md),
|
||||
inset 0 0 0 2px color-mix(in srgb, var(--color-accent-lime) 18%, transparent);
|
||||
}
|
||||
|
||||
.forbidden-icon__halo {
|
||||
position: absolute;
|
||||
inset: -10px;
|
||||
border-radius: var(--radius-full);
|
||||
background:
|
||||
radial-gradient(
|
||||
closest-side,
|
||||
color-mix(in srgb, var(--color-warning) 38%, transparent),
|
||||
transparent 70%
|
||||
);
|
||||
filter: blur(12px);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.forbidden-head h1 {
|
||||
margin-top: var(--space-2);
|
||||
margin-bottom: var(--space-2);
|
||||
margin: 0 0 var(--space-2);
|
||||
font-size: clamp(1.8rem, 1.4rem + 1vw, 2.4rem);
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.forbidden-head p {
|
||||
margin: 0 auto;
|
||||
max-width: 50ch;
|
||||
color: var(--color-text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.forbidden-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.forbidden-shell {
|
||||
animation: hoard-soft-enter 260ms both;
|
||||
animation: hoard-soft-enter 280ms both;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
.forbidden-shell {
|
||||
padding: var(--space-7) var(--space-5);
|
||||
}
|
||||
|
||||
.forbidden-actions {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.forbidden-actions .v-btn) {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user