frontend: redesign error page with enhanced layout, responsive design, and improved error handling

This commit is contained in:
Carlos Santos 2025-07-15 16:58:42 +02:00
parent 3ca68dae80
commit 0e4cfa5bb5
3 changed files with 256 additions and 38 deletions

View File

@ -1,10 +1,31 @@
<div class="container">
<mat-card class="card">
<mat-card-header>
<mat-card-title>{{ errorName }}</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>{{ message }}</p>
</mat-card-content>
</mat-card>
<div class="error-page">
<div class="error-container fade-in">
<div class="error-content">
<div class="error-icon-section">
<mat-icon class="error-main-icon">error_outline</mat-icon>
</div>
<div class="error-details">
<h1 class="error-title">{{ errorName }}</h1>
<p class="error-message">{{ message }}</p>
</div>
<div class="error-actions">
<button mat-flat-button color="primary" (click)="goBack()" class="action-button">
<mat-icon>arrow_back</mat-icon>
Go Back
</button>
</div>
</div>
<div class="error-illustration">
<div class="illustration-container">
<mat-icon class="illustration-icon">cloud_off</mat-icon>
<div class="illustration-text">
<span class="illustration-title">Oops!</span>
<span class="illustration-subtitle">Something went wrong</span>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,33 +1,220 @@
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: var(--ov-background-color);
@import '../../../../../../src/assets/styles/design-tokens';
.error-page {
@include ov-theme-transition;
@include ov-flex-center;
min-height: 100vh;
background: var(--ov-meet-background-color);
padding: var(--ov-meet-spacing-lg);
.error-container {
@include ov-theme-transition;
display: flex;
flex-direction: row;
align-items: center;
gap: var(--ov-meet-spacing-xxl);
max-width: 800px;
width: 100%;
@include ov-tablet-down {
flex-direction: column;
gap: var(--ov-meet-spacing-xl);
text-align: center;
}
.error-content {
flex: 1;
display: flex;
flex-direction: column;
gap: var(--ov-meet-spacing-lg);
.error-icon-section {
@include ov-flex-center;
justify-content: flex-start;
@include ov-tablet-down {
justify-content: center;
}
.error-main-icon {
@include ov-icon(xl);
color: var(--ov-meet-color-error);
background: rgba(var(--ov-meet-color-error), 0.1);
border-radius: var(--ov-meet-radius-circle);
padding: var(--ov-meet-spacing-md);
width: 64px;
height: 64px;
font-size: 32px;
}
}
.error-details {
.error-title {
margin: 0 0 var(--ov-meet-spacing-sm) 0;
font-size: var(--ov-meet-font-size-xxl);
font-weight: var(--ov-meet-font-weight-semibold);
color: var(--ov-meet-text-primary);
line-height: var(--ov-meet-line-height-tight);
@include ov-mobile-down {
font-size: var(--ov-meet-font-size-xl);
}
}
.error-message {
margin: 0;
font-size: var(--ov-meet-font-size-md);
color: var(--ov-meet-text-secondary);
line-height: var(--ov-meet-line-height-normal);
max-width: 400px;
@include ov-tablet-down {
max-width: none;
}
@include ov-mobile-down {
font-size: var(--ov-meet-font-size-sm);
}
}
}
.error-actions {
display: flex;
gap: var(--ov-meet-spacing-md);
margin-top: var(--ov-meet-spacing-md);
@include ov-tablet-down {
justify-content: center;
}
@include ov-mobile-down {
flex-direction: column;
align-items: center;
}
.action-button {
@include ov-button-base;
@include ov-flex-center;
gap: var(--ov-meet-spacing-xs);
min-width: 120px;
&.secondary {
color: var(--ov-meet-text-secondary);
border-color: var(--ov-meet-border-color);
&:hover {
color: var(--ov-meet-text-primary);
border-color: var(--ov-meet-text-primary);
background: var(--ov-meet-surface-hover);
}
}
mat-icon {
@include ov-icon(sm);
}
@include ov-mobile-down {
min-width: 200px;
}
}
}
}
.error-illustration {
flex: 1;
@include ov-flex-center;
@include ov-tablet-down {
order: -1;
}
.illustration-container {
@include ov-flex-center;
flex-direction: column;
gap: var(--ov-meet-spacing-lg);
opacity: 0.6;
.illustration-icon {
font-size: 120px;
width: 120px;
height: 120px;
color: var(--ov-meet-text-hint);
@include ov-mobile-down {
font-size: 80px;
width: 80px;
height: 80px;
}
}
.illustration-text {
display: flex;
flex-direction: column;
gap: var(--ov-meet-spacing-xs);
text-align: center;
.illustration-title {
font-size: var(--ov-meet-font-size-xl);
font-weight: var(--ov-meet-font-weight-bold);
color: var(--ov-meet-text-secondary);
@include ov-mobile-down {
font-size: var(--ov-meet-font-size-lg);
}
}
.illustration-subtitle {
font-size: var(--ov-meet-font-size-sm);
color: var(--ov-meet-text-hint);
}
}
}
}
}
}
.card {
width: 400px;
padding: 20px;
text-align: center;
background-color: var(--ov-surface-color);
border-radius: var(--ov-surface-radius);
// Animation states
.fade-in {
animation: fadeIn 0.6s ease-out forwards;
}
mat-card-header {
justify-content: center;
align-items: center;
// Enhanced responsive design
@include ov-mobile-down {
.error-page {
padding: var(--ov-meet-spacing-md);
.error-container {
.error-content {
.error-details {
.error-title {
margin-bottom: var(--ov-meet-spacing-md);
}
}
.error-actions {
margin-top: var(--ov-meet-spacing-lg);
.action-button {
padding: var(--ov-meet-spacing-md) var(--ov-meet-spacing-lg);
font-size: var(--ov-meet-font-size-md);
}
}
}
}
}
}
mat-card-title {
font-size: 1.5em;
}
mat-card-content p {
font-size: 1em;
color: #555;
}
mat-card-actions {
margin-top: 20px;
// Dark theme enhancements
[data-theme='dark'] {
.error-page {
.error-container {
.error-content {
.error-icon-section {
.error-main-icon {
background: rgba(var(--ov-meet-color-error), 0.15);
}
}
}
}
}
}

View File

@ -1,12 +1,15 @@
import { Component, OnInit } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { ActivatedRoute } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
import { ActivatedRoute, Router } from '@angular/router';
import { Location } from '@angular/common';
import { ErrorReason } from '@lib/models';
@Component({
selector: 'ov-error',
standalone: true,
imports: [MatCardModule],
imports: [MatCardModule, MatIconModule, MatButtonModule],
templateUrl: './error.component.html',
styleUrl: './error.component.scss'
})
@ -14,7 +17,10 @@ export class ErrorComponent implements OnInit {
errorName = 'Error';
message = '';
constructor(private route: ActivatedRoute) {}
constructor(
private route: ActivatedRoute,
private location: Location
) {}
ngOnInit(): void {
this.route.queryParams.subscribe((params) => {
@ -64,4 +70,8 @@ export class ErrorComponent implements OnInit {
}
});
}
goBack(): void {
this.location.back();
}
}