frontend: refactor room recordings page layout and enhance loading state styling
This commit is contained in:
parent
6ae98e452a
commit
3c629c8511
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user