frontend: refactor room recordings page layout and enhance loading state styling

This commit is contained in:
Carlos Santos 2025-07-04 12:16:26 +02:00
parent 6ae98e452a
commit 3c629c8511
3 changed files with 184 additions and 68 deletions

View File

@ -1,55 +1,49 @@
<div class="dashboard-container">
<mat-toolbar class="header">
<span id="header-title">
Recordings for room: <span id="room-id">{{ roomId }}</span>
</span>
</mat-toolbar>
<div class="ov-page-container">
<!-- Room Recordings Header -->
<div class="recordings-page-header fade-in">
<div class="recordings-header">
<mat-icon class="ov-recording-icon room-icon">video_library</mat-icon>
<div class="room-info">
<h1 class="room-title">
Recordings for room
<span class="room-id">
{{ roomId }}
</span>
</h1>
</div>
</div>
</div>
<div class="dashboard-body">
@if (isLoading) {
@if (showLoadingSpinner) {
<!-- Enhanced Loading State with delay -->
<div class="ov-page-loading">
<div class="loading-content">
<div class="loading-header">
<div class="loading-title">
<mat-icon class="ov-recording-icon loading-icon">video_library</mat-icon>
<h1>Loading Recordings</h1>
</div>
<p class="loading-subtitle">Please wait while we fetch your recordings...</p>
</div>
<div class="loading-spinner-container">
<mat-spinner diameter="48"></mat-spinner>
</div>
</div>
</div>
}
} @else {
<div class="ov-page-container ov-mb-xxl">
<div class="page-header">
<div class="title">
<mat-icon class="ov-recording-icon">video_library</mat-icon>
<h1>Recordings</h1>
</div>
<p class="subtitle">
Manage recordings from room, play, download, and share them with others.
</p>
</div>
<div class="page-content">
<ov-recording-lists
[recordings]="recordings()"
[canDeleteRecordings]="canDeleteRecordings"
[loading]="isLoading"
[showFilters]="false"
[showSelection]="true"
(recordingAction)="onRecordingAction($event)"
(refresh)="refreshRecordings()"
>
</ov-recording-lists>
<!-- Loading State -->
@if (isLoading && showLoadingSpinner) {
<div class="recordings-loading-container fade-in">
<div class="loading-card">
<mat-icon class="ov-recording-icon loading-icon">video_library</mat-icon>
<h2 class="loading-title">Loading Recordings</h2>
<p class="loading-subtitle">Please wait while we fetch your recordings...</p>
<div class="loading-spinner-container">
<mat-spinner diameter="48"></mat-spinner>
</div>
</div>
}
</div>
</div>
}
<!-- Content -->
@if (!isLoading || recordings().length > 0) {
<div class="recordings-content fade-in-delayed">
<div class="section-content">
<ov-recording-lists
[recordings]="recordings()"
[canDeleteRecordings]="canDeleteRecordings"
[loading]="isLoading"
[showFilters]="false"
[showSelection]="true"
(recordingAction)="onRecordingAction($event)"
(refresh)="refreshRecordings()"
class="recordings-list"
>
</ov-recording-lists>
</div>
</div>
}
</div>

View File

@ -1,26 +1,146 @@
@import '../../../../../../src/assets/styles/design-tokens';
.dashboard-container {
height: 100%;
// === ROOM RECORDINGS PAGE LAYOUT ===
.recordings-page-header {
@include ov-container;
@include ov-page-content;
padding-top: var(--ov-meet-spacing-xxl);
background: var(--ov-meet-background-color);
gap: var(--ov-meet-spacing-xxl);
}
.header {
height: 50px;
background-color: var(--ov-primary-action-color);
color: var(--ov-text-primary-color);
justify-content: space-between;
// Room Header
.recordings-header {
@include ov-flex-center;
flex-direction: row;
gap: var(--ov-meet-spacing-md);
text-align: center;
.room-icon {
@include ov-icon(xl);
color: var(--ov-meet-icon-recordings);
margin-bottom: var(--ov-meet-spacing-sm);
}
.room-info {
.room-title {
margin: 0;
font-size: var(--ov-meet-font-size-xl);
font-weight: var(--ov-meet-font-weight-light);
color: var(--ov-meet-text-primary);
line-height: var(--ov-meet-line-height-tight);
.room-id {
font-weight: var(--ov-meet-font-weight-semibold);
color: var(--ov-meet-text-primary);
}
}
}
}
#header-title {
font-weight: bold;
// === CONTENT SECTIONS ===
.recordings-content {
@include ov-container;
@include ov-page-content;
margin-top: var(--ov-meet-spacing-xl);
}
#room-id {
font-weight: normal;
font-style: italic;
// === LOADING STATE ===
.recordings-loading-container {
@include ov-container;
@include ov-flex-center;
min-height: 60vh;
padding: var(--ov-meet-spacing-xxl);
}
.dashboard-body {
background-color: var(--ov-secondary-action-color);
height: 100%;
.loading-card {
@include ov-card;
@include ov-flex-center;
flex-direction: column;
gap: var(--ov-meet-spacing-lg);
text-align: center;
max-width: 400px;
padding: var(--ov-meet-spacing-xxl);
.loading-icon {
@include ov-icon(xl);
color: var(--ov-meet-icon-recordings);
margin-bottom: var(--ov-meet-spacing-md);
}
.loading-title {
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 {
margin: 0;
font-size: var(--ov-meet-font-size-md);
color: var(--ov-meet-text-secondary);
line-height: var(--ov-meet-line-height-relaxed);
}
.loading-spinner-container {
margin-top: var(--ov-meet-spacing-lg);
}
}
// === RESPONSIVE DESIGN ===
@include ov-tablet-down {
.recordings-page-header {
padding-top: var(--ov-meet-spacing-lg);
gap: var(--ov-meet-spacing-lg);
}
.recordings-header {
.room-icon {
@include ov-icon(lg);
}
.room-info {
.room-title {
font-size: var(--ov-meet-font-size-xxl);
}
.room-subtitle {
font-size: var(--ov-meet-font-size-md);
}
}
}
.description-card {
padding: var(--ov-meet-spacing-md);
.description-content .description-text {
font-size: var(--ov-meet-font-size-sm);
}
}
}
@include ov-mobile-down {
.recordings-page-header {
padding: var(--ov-meet-spacing-md);
}
.loading-card {
padding: var(--ov-meet-spacing-lg);
.loading-icon {
@include ov-icon(lg);
}
.loading-title {
font-size: var(--ov-meet-font-size-lg);
}
.loading-subtitle {
font-size: var(--ov-meet-font-size-sm);
}
}
}

View File

@ -1,10 +1,11 @@
import { Component, OnInit, signal } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatToolbarModule } from '@angular/material/toolbar';
import { ActivatedRoute } from '@angular/router';
import { RecordingListsComponent, RecordingTableAction } from '@lib/components';
import { NotificationService, RecordingManagerService } from '@lib/services';
import { NavigationService, NotificationService, RecordingManagerService } from '@lib/services';
import { MeetRecordingFilters, MeetRecordingInfo } from '@lib/typings/ce';
import { ILogger, LoggerService } from 'openvidu-components-angular';
@ -13,7 +14,7 @@ import { ILogger, LoggerService } from 'openvidu-components-angular';
templateUrl: './room-recordings.component.html',
styleUrls: ['./room-recordings.component.scss'],
standalone: true,
imports: [MatToolbarModule, RecordingListsComponent, MatIconModule, MatProgressSpinnerModule]
imports: [MatToolbarModule, MatButtonModule, RecordingListsComponent, MatIconModule, MatProgressSpinnerModule]
})
export class RoomRecordingsComponent implements OnInit {
recordings = signal<MeetRecordingInfo[]>([]);
@ -33,6 +34,7 @@ export class RoomRecordingsComponent implements OnInit {
protected loggerService: LoggerService,
protected recordingService: RecordingManagerService,
protected notificationService: NotificationService,
protected navigationService: NavigationService,
protected route: ActivatedRoute
) {
this.log = this.loggerService.get('OpenVidu Meet - RoomRecordingsComponent');