frontend: redesign error page with enhanced layout, responsive design, and improved error handling
This commit is contained in:
parent
3ca68dae80
commit
0e4cfa5bb5
@ -1,10 +1,31 @@
|
|||||||
<div class="container">
|
<div class="error-page">
|
||||||
<mat-card class="card">
|
<div class="error-container fade-in">
|
||||||
<mat-card-header>
|
<div class="error-content">
|
||||||
<mat-card-title>{{ errorName }}</mat-card-title>
|
<div class="error-icon-section">
|
||||||
</mat-card-header>
|
<mat-icon class="error-main-icon">error_outline</mat-icon>
|
||||||
<mat-card-content>
|
</div>
|
||||||
<p>{{ message }}</p>
|
|
||||||
</mat-card-content>
|
<div class="error-details">
|
||||||
</mat-card>
|
<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>
|
</div>
|
||||||
|
|||||||
@ -1,33 +1,220 @@
|
|||||||
.container {
|
@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;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100vh;
|
gap: var(--ov-meet-spacing-xxl);
|
||||||
background-color: var(--ov-background-color);
|
max-width: 800px;
|
||||||
}
|
width: 100%;
|
||||||
|
|
||||||
.card {
|
@include ov-tablet-down {
|
||||||
width: 400px;
|
flex-direction: column;
|
||||||
padding: 20px;
|
gap: var(--ov-meet-spacing-xl);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: var(--ov-surface-color);
|
|
||||||
border-radius: var(--ov-surface-radius);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-card-header {
|
.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;
|
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;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-card-title {
|
.action-button {
|
||||||
font-size: 1.5em;
|
@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-card-content p {
|
mat-icon {
|
||||||
font-size: 1em;
|
@include ov-icon(sm);
|
||||||
color: #555;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-card-actions {
|
@include ov-mobile-down {
|
||||||
margin-top: 20px;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation states
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.6s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
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';
|
import { ErrorReason } from '@lib/models';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ov-error',
|
selector: 'ov-error',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [MatCardModule],
|
imports: [MatCardModule, MatIconModule, MatButtonModule],
|
||||||
templateUrl: './error.component.html',
|
templateUrl: './error.component.html',
|
||||||
styleUrl: './error.component.scss'
|
styleUrl: './error.component.scss'
|
||||||
})
|
})
|
||||||
@ -14,7 +17,10 @@ export class ErrorComponent implements OnInit {
|
|||||||
errorName = 'Error';
|
errorName = 'Error';
|
||||||
message = '';
|
message = '';
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute) {}
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private location: Location
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.queryParams.subscribe((params) => {
|
this.route.queryParams.subscribe((params) => {
|
||||||
@ -64,4 +70,8 @@ export class ErrorComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goBack(): void {
|
||||||
|
this.location.back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user