frontend: add loading state for room creation with spinner and loading messages
This commit is contained in:
parent
10b3531cf3
commit
275d15f68f
@ -18,21 +18,45 @@
|
|||||||
|
|
||||||
<main class="wizard-content">
|
<main class="wizard-content">
|
||||||
<section class="step-content ov-surface">
|
<section class="step-content ov-surface">
|
||||||
@switch (currentStep()?.id) {
|
@if (isCreatingRoom()) {
|
||||||
@case ('basic') {
|
<!-- Room Creation Loading State -->
|
||||||
<ov-room-wizard-basic-info></ov-room-wizard-basic-info>
|
<div class="room-creation-loading">
|
||||||
}
|
<div class="loading-content">
|
||||||
@case ('recording') {
|
<div class="loading-header">
|
||||||
<ov-recording-preferences></ov-recording-preferences>
|
<div class="loading-title">
|
||||||
}
|
<mat-icon class="ov-room-icon loading-icon">video_chat</mat-icon>
|
||||||
@case ('recordingTrigger') {
|
<h2>{{ editMode ? 'Updating Room' : 'Creating Room' }}</h2>
|
||||||
<ov-recording-trigger></ov-recording-trigger>
|
</div>
|
||||||
}
|
<p class="loading-subtitle">
|
||||||
@case ('recordingLayout') {
|
{{
|
||||||
<ov-recording-layout></ov-recording-layout>
|
editMode
|
||||||
}
|
? 'Please wait while we update your room settings...'
|
||||||
@case ('preferences') {
|
: 'Please wait while we set up your video room...'
|
||||||
<ov-room-preferences></ov-room-preferences>
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="loading-spinner-container">
|
||||||
|
<mat-spinner diameter="48"></mat-spinner>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
@switch (currentStep()?.id) {
|
||||||
|
@case ('basic') {
|
||||||
|
<ov-room-wizard-basic-info></ov-room-wizard-basic-info>
|
||||||
|
}
|
||||||
|
@case ('recording') {
|
||||||
|
<ov-recording-preferences></ov-recording-preferences>
|
||||||
|
}
|
||||||
|
@case ('recordingTrigger') {
|
||||||
|
<ov-recording-trigger></ov-recording-trigger>
|
||||||
|
}
|
||||||
|
@case ('recordingLayout') {
|
||||||
|
<ov-recording-layout></ov-recording-layout>
|
||||||
|
}
|
||||||
|
@case ('preferences') {
|
||||||
|
<ov-room-preferences></ov-room-preferences>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -77,6 +77,65 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Room Creation Loading State
|
||||||
|
.room-creation-loading {
|
||||||
|
@include ov-flex-center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 400px;
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--ov-meet-spacing-xl);
|
||||||
|
|
||||||
|
.loading-content {
|
||||||
|
@include ov-flex-center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--ov-meet-spacing-xl);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.loading-header {
|
||||||
|
margin-bottom: var(--ov-meet-spacing-lg);
|
||||||
|
|
||||||
|
.loading-title {
|
||||||
|
@include ov-flex-center;
|
||||||
|
gap: var(--ov-meet-spacing-md);
|
||||||
|
margin-bottom: var(--ov-meet-spacing-md);
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
@include ov-icon(xl);
|
||||||
|
animation: pulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: var(--ov-meet-font-size-xl);
|
||||||
|
font-weight: var(--ov-meet-font-weight-medium);
|
||||||
|
color: var(--ov-meet-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-subtitle {
|
||||||
|
font-size: var(--ov-meet-font-size-md);
|
||||||
|
color: var(--ov-meet-text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
animation: fadeIn 1s ease-out 0.5s both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner-container {
|
||||||
|
mat-spinner {
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
::ng-deep {
|
||||||
|
.mdc-circular-progress__determinate-circle,
|
||||||
|
.mdc-circular-progress__indeterminate-circle-graphic {
|
||||||
|
stroke: var(--ov-meet-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Responsive design using the system mixins
|
// Responsive design using the system mixins
|
||||||
@include ov-tablet-down {
|
@include ov-tablet-down {
|
||||||
.wizard-container {
|
.wizard-container {
|
||||||
@ -90,6 +149,20 @@
|
|||||||
font-size: var(--ov-meet-font-size-xl);
|
font-size: var(--ov-meet-font-size-xl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.room-creation-loading {
|
||||||
|
padding: var(--ov-meet-spacing-lg);
|
||||||
|
|
||||||
|
.loading-content {
|
||||||
|
.loading-header {
|
||||||
|
.loading-title {
|
||||||
|
h2 {
|
||||||
|
font-size: var(--ov-meet-font-size-lg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include ov-mobile-down {
|
@include ov-mobile-down {
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, computed, OnInit, Signal } from '@angular/core';
|
import { Component, computed, OnInit, Signal, signal } from '@angular/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { StepIndicatorComponent, WizardNavComponent } from '@lib/components';
|
import { SpinnerComponent, StepIndicatorComponent, WizardNavComponent } from '@lib/components';
|
||||||
import { WizardNavigationConfig, WizardStep } from '@lib/models';
|
import { WizardNavigationConfig, WizardStep } from '@lib/models';
|
||||||
import { NavigationService, NotificationService, RoomService, RoomWizardStateService } from '@lib/services';
|
import { NavigationService, NotificationService, RoomService, RoomWizardStateService } from '@lib/services';
|
||||||
import { MeetRoomOptions } from '@lib/typings/ce';
|
import { MeetRoomOptions } from '@lib/typings/ce';
|
||||||
@ -21,8 +22,10 @@ import { RoomPreferencesComponent } from './steps/room-preferences/room-preferen
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
StepIndicatorComponent,
|
StepIndicatorComponent,
|
||||||
WizardNavComponent,
|
WizardNavComponent,
|
||||||
|
SpinnerComponent,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
MatSlideToggleModule,
|
MatSlideToggleModule,
|
||||||
RoomWizardBasicInfoComponent,
|
RoomWizardBasicInfoComponent,
|
||||||
RecordingPreferencesComponent,
|
RecordingPreferencesComponent,
|
||||||
@ -37,6 +40,7 @@ export class RoomWizardComponent implements OnInit {
|
|||||||
editMode: boolean = false;
|
editMode: boolean = false;
|
||||||
roomId?: string;
|
roomId?: string;
|
||||||
existingRoomData?: MeetRoomOptions;
|
existingRoomData?: MeetRoomOptions;
|
||||||
|
isCreatingRoom = signal(false);
|
||||||
|
|
||||||
steps: Signal<WizardStep[]>;
|
steps: Signal<WizardStep[]>;
|
||||||
currentStep: Signal<WizardStep | undefined>;
|
currentStep: Signal<WizardStep | undefined>;
|
||||||
@ -114,21 +118,27 @@ export class RoomWizardComponent implements OnInit {
|
|||||||
const roomOptions = this.wizardService.roomOptions();
|
const roomOptions = this.wizardService.roomOptions();
|
||||||
console.log('Wizard completed with data:', roomOptions);
|
console.log('Wizard completed with data:', roomOptions);
|
||||||
|
|
||||||
|
// Activate loading state
|
||||||
|
this.isCreatingRoom.set(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.editMode && this.roomId && roomOptions.preferences) {
|
if (this.editMode && this.roomId && roomOptions.preferences) {
|
||||||
await this.roomService.updateRoom(this.roomId, roomOptions.preferences);
|
await this.roomService.updateRoom(this.roomId, roomOptions.preferences);
|
||||||
|
await this.navigationService.navigateTo('rooms', undefined, true);
|
||||||
this.notificationService.showSnackbar('Room updated successfully');
|
this.notificationService.showSnackbar('Room updated successfully');
|
||||||
} else {
|
} else {
|
||||||
// Create new room
|
// Create new room
|
||||||
await this.roomService.createRoom(roomOptions);
|
const { moderatorRoomUrl } = await this.roomService.createRoom(roomOptions);
|
||||||
this.notificationService.showSnackbar('Room created successfully');
|
await this.navigationService.redirectTo(moderatorRoomUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.navigationService.navigateTo('rooms', undefined, true);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = `Failed to ${this.editMode ? 'update' : 'create'} room`;
|
const errorMessage = `Failed to ${this.editMode ? 'update' : 'create'} room`;
|
||||||
this.notificationService.showSnackbar(errorMessage);
|
this.notificationService.showSnackbar(errorMessage);
|
||||||
console.error(errorMessage, error);
|
console.error(errorMessage, error);
|
||||||
|
} finally {
|
||||||
|
this.wizardService.resetWizard();
|
||||||
|
// Deactivate loading state
|
||||||
|
this.isCreatingRoom.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user