frontend: enhance recording lists component with improved empty state messaging and filter handling
This commit is contained in:
parent
5f289d12b8
commit
3513733071
@ -1,10 +1,18 @@
|
||||
<!-- Loading Spinner -->
|
||||
@if (loading) {
|
||||
<div class="loading-container">
|
||||
<mat-spinner diameter="40"></mat-spinner>
|
||||
<span>Loading recordings...</span>
|
||||
@if (!loading && recordings.length === 0 && !showEmptyFilterMessage) {
|
||||
<!-- Empty State -->
|
||||
<div class="no-recordings-state">
|
||||
<div class="empty-content">
|
||||
<h3>No recordings yet</h3>
|
||||
<p>Recordings from your meetings will appear here. Start a recording in any room to see them listed.</p>
|
||||
<div class="getting-started-actions">
|
||||
<button mat-button (click)="refresh.emit()" class="refresh-recordings-btn primary-button">
|
||||
<mat-icon>refresh</mat-icon>
|
||||
Refresh Recordings
|
||||
</button>
|
||||
</div>
|
||||
} @else if (recordings.length > 0) {
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<!-- Recordings Toolbar -->
|
||||
<mat-toolbar class="recordings-toolbar">
|
||||
<!-- Left Section: Search -->
|
||||
@ -52,14 +60,26 @@
|
||||
|
||||
<!-- Right Section: Filters -->
|
||||
<div class="toolbar-right">
|
||||
@if (hasActiveFilters()) {
|
||||
<button
|
||||
mat-icon-button
|
||||
class="clear-btn"
|
||||
(click)="clearFilters()"
|
||||
[disabled]="loading"
|
||||
matTooltip="Clear all filters"
|
||||
>
|
||||
<mat-icon>filter_alt_off</mat-icon>
|
||||
</button>
|
||||
}
|
||||
|
||||
<button
|
||||
mat-icon-button
|
||||
class="refresh-btn"
|
||||
(click)="refresh.emit()"
|
||||
(click)="refreshRecordings()"
|
||||
[disabled]="loading"
|
||||
matTooltip="Refresh recordings"
|
||||
>
|
||||
<mat-icon> refresh </mat-icon>
|
||||
<mat-icon>refresh</mat-icon>
|
||||
</button>
|
||||
|
||||
@if (showFilters) {
|
||||
@ -84,6 +104,28 @@
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
||||
<!-- Loading Spinner -->
|
||||
@if (loading) {
|
||||
<div class="loading-container">
|
||||
<mat-spinner diameter="40"></mat-spinner>
|
||||
<span>Loading recordings...</span>
|
||||
</div>
|
||||
} @else if (recordings.length === 0 && showEmptyFilterMessage) {
|
||||
<!-- No recordings match the current filters -->
|
||||
<div class="no-recordings-state">
|
||||
<div class="empty-content">
|
||||
<h3>No recordings match your search criteria and/or filters</h3>
|
||||
<p>Try adjusting or clearing your filters to see more recordings.</p>
|
||||
<div class="getting-started-actions">
|
||||
<button mat-button (click)="clearFilters()" class="clear-filters-btn primary-button">
|
||||
<mat-icon>filter_alt_off</mat-icon>
|
||||
Clear Filters
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<!-- Recordings Table -->
|
||||
<div class="table-container">
|
||||
<table mat-table [dataSource]="recordings" class="recordings-table">
|
||||
<!-- Selection Column -->
|
||||
@ -277,29 +319,5 @@
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
<!-- Empty State -->
|
||||
<div class="no-recordings-state">
|
||||
<div class="empty-content">
|
||||
<h3>No recordings yet</h3>
|
||||
<p>Recordings from your meetings will appear here. Start a recording in any room to see them listed.</p>
|
||||
<div class="getting-started-actions">
|
||||
<button mat-button (click)="refresh.emit()" class="refresh-recordings-btn primary-button">
|
||||
<mat-icon>refresh</mat-icon>
|
||||
Refresh Recordings
|
||||
</button>
|
||||
</div>
|
||||
<!-- TODO: Show this when no recordings match the filters
|
||||
@if (hasActiveFilters()) {
|
||||
<h3>No recordings match your search criteria</h3>
|
||||
<p>Try adjusting or clearing your filters to see more recordings.</p>
|
||||
<div class="getting-started-actions">
|
||||
<button mat-raised-button color="primary" (click)="clearFilters()" class="clear-filters-btn">
|
||||
<mat-icon>filter_alt_off</mat-icon>
|
||||
Clear Filters
|
||||
</button>
|
||||
</div>
|
||||
} -->
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,10 @@
|
||||
::ng-deep .refresh-btn {
|
||||
padding: var(--ov-meet-spacing-sm);
|
||||
}
|
||||
|
||||
::ng-deep .clear-btn {
|
||||
padding: var(--ov-meet-spacing-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,6 +156,10 @@
|
||||
.refresh-recordings-btn {
|
||||
@extend .refresh-btn;
|
||||
}
|
||||
|
||||
.clear-filters-btn {
|
||||
@extend .refresh-btn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -90,6 +90,7 @@ export class RecordingListsComponent implements OnInit, OnChanges {
|
||||
@Input() showRoomInfo = true;
|
||||
@Input() showLoadMore = false;
|
||||
@Input() loading = false;
|
||||
@Input() initialFilters: { nameFilter: string; statusFilter: string } = { nameFilter: '', statusFilter: '' };
|
||||
|
||||
// Host binding for styling when recordings are selected
|
||||
@HostBinding('class.has-selections')
|
||||
@ -107,6 +108,8 @@ export class RecordingListsComponent implements OnInit, OnChanges {
|
||||
nameFilterControl = new FormControl('');
|
||||
statusFilterControl = new FormControl('');
|
||||
|
||||
showEmptyFilterMessage = false; // Show message when no recordings match filters
|
||||
|
||||
// Selection state
|
||||
selectedRecordings = signal<Set<string>>(new Set());
|
||||
allSelected = signal(false);
|
||||
@ -156,16 +159,28 @@ export class RecordingListsComponent implements OnInit, OnChanges {
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['recordings']) {
|
||||
// Update selected recordings based on current recordings
|
||||
const validIds = new Set(this.recordings.map((r) => r.recordingId));
|
||||
const filteredSelection = new Set([...this.selectedRecordings()].filter((id) => validIds.has(id)));
|
||||
this.selectedRecordings.set(filteredSelection);
|
||||
this.updateSelectionState();
|
||||
|
||||
// Show message when no recordings match filters
|
||||
if (this.recordings.length === 0 && this.hasActiveFilters()) {
|
||||
this.showEmptyFilterMessage = true;
|
||||
} else {
|
||||
this.showEmptyFilterMessage = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== INITIALIZATION METHODS =====
|
||||
|
||||
private setupFilters() {
|
||||
// Set up initial filter values
|
||||
this.nameFilterControl.setValue(this.initialFilters.nameFilter);
|
||||
this.statusFilterControl.setValue(this.initialFilters.statusFilter);
|
||||
|
||||
// Set up name filter with debounce
|
||||
this.nameFilterControl.valueChanges.pipe(debounceTime(300), distinctUntilChanged()).subscribe((value) => {
|
||||
this.filterChange.emit({
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
[showFilters]="false"
|
||||
[showSelection]="true"
|
||||
[showLoadMore]="hasMoreRecordings"
|
||||
[initialFilters]="initialFilters"
|
||||
(recordingAction)="onRecordingAction($event)"
|
||||
(loadMore)="loadMoreRecordings($event)"
|
||||
(refresh)="refreshRecordings($event)"
|
||||
|
||||
@ -22,6 +22,11 @@ export class RecordingsComponent implements OnInit {
|
||||
showInitialLoader = false;
|
||||
isLoading = false;
|
||||
|
||||
initialFilters = {
|
||||
nameFilter: '',
|
||||
statusFilter: ''
|
||||
};
|
||||
|
||||
// Pagination
|
||||
hasMoreRecordings = false;
|
||||
private nextPageToken?: string;
|
||||
@ -45,7 +50,8 @@ export class RecordingsComponent implements OnInit {
|
||||
|
||||
if (roomId) {
|
||||
// If a specific room ID is provided, filter recordings by that room
|
||||
await this.loadRecordings({ nameFilter: roomId, statusFilter: '' });
|
||||
this.initialFilters.nameFilter = roomId;
|
||||
await this.loadRecordings(this.initialFilters);
|
||||
} else {
|
||||
// Load all recordings if no room ID is specified
|
||||
await this.loadRecordings();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user