diff --git a/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.html b/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.html index bf33db5..809df8c 100644 --- a/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.html +++ b/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.html @@ -1,19 +1,46 @@ - -@if (loading) { -
- - Loading rooms... +@if (!loading && rooms.length === 0 && !showEmptyFilterMessage) { + +
+
+

No rooms created yet

+

No rooms found. Create your first room to start hosting meetings and manage your video conferences.

+ +
+ +
+
-} @else if (rooms.length > 0) { +} @else {
- - Search rooms - - search - + @if (showSearchBox) { + + Search rooms + + + + }
@@ -36,10 +63,23 @@
+ @if (hasActiveFilters()) { + + } +
-
- + } + } } diff --git a/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.scss b/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.scss index c15c092..71ae4ba 100644 --- a/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.scss +++ b/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.scss @@ -4,12 +4,22 @@ .rooms-toolbar { @extend .ov-data-toolbar; + .toolbar-left { + ::ng-deep .search-btn { + padding: var(--ov-meet-spacing-sm); + } + } + .toolbar-right { gap: var(--ov-meet-spacing-sm); ::ng-deep .refresh-btn { padding: var(--ov-meet-spacing-sm); } + + ::ng-deep .clear-btn { + padding: var(--ov-meet-spacing-sm); + } } } @@ -251,10 +261,15 @@ } .getting-started-actions { - display: flex; - flex-direction: column; - gap: var(--ov-meet-spacing-md); - align-items: center; + @extend .action-buttons; + + .create-room-btn { + @extend .refresh-btn; + } + + .clear-filters-btn { + @extend .refresh-btn; + } } } diff --git a/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.ts b/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.ts index d71ff8f..9259390 100644 --- a/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.ts +++ b/frontend/projects/shared-meet-components/src/lib/components/rooms-lists/rooms-lists.component.ts @@ -92,10 +92,12 @@ export interface RoomTableAction { export class RoomsListsComponent implements OnInit, OnChanges { // Input properties @Input() rooms: MeetRoom[] = []; + @Input() showSearchBox = true; @Input() showFilters = false; @Input() showSelection = true; @Input() showLoadMore = false; @Input() loading = false; + @Input() initialFilters: { nameFilter: string; statusFilter: string } = { nameFilter: '', statusFilter: '' }; // Host binding for styling when rooms are selected @HostBinding('class.has-selections') @@ -106,13 +108,15 @@ export class RoomsListsComponent implements OnInit, OnChanges { // Output events @Output() roomAction = new EventEmitter(); @Output() filterChange = new EventEmitter<{ nameFilter: string; statusFilter: string }>(); - @Output() loadMore = new EventEmitter(); - @Output() refresh = new EventEmitter(); + @Output() loadMore = new EventEmitter<{ nameFilter: string; statusFilter: string }>(); + @Output() refresh = new EventEmitter<{ nameFilter: string; statusFilter: string }>(); // Filter controls nameFilterControl = new FormControl(''); statusFilterControl = new FormControl(''); + showEmptyFilterMessage = false; // Show message when no rooms match filters + // Selection state selectedRooms = signal>(new Set()); allSelected = signal(false); @@ -137,30 +141,39 @@ export class RoomsListsComponent implements OnInit, OnChanges { ngOnChanges(changes: SimpleChanges) { if (changes['rooms']) { + // Update selected rooms based on current rooms const validIds = new Set(this.rooms.map((r) => r.roomId)); const filteredSelection = new Set([...this.selectedRooms()].filter((id) => validIds.has(id))); this.selectedRooms.set(filteredSelection); this.updateSelectionState(); + + // Show message when no rooms match filters + if (this.rooms.length === 0 && this.hasActiveFilters()) { + this.showEmptyFilterMessage = true; + } else { + this.showEmptyFilterMessage = false; + } } } // ===== INITIALIZATION METHODS ===== private setupFilters() { - // Set up name filter with debounce - this.nameFilterControl.valueChanges.pipe(debounceTime(300), distinctUntilChanged()).subscribe((value) => { - this.filterChange.emit({ - nameFilter: value || '', - statusFilter: this.statusFilterControl.value || '' - }); + // Set up initial filter values + this.nameFilterControl.setValue(this.initialFilters.nameFilter); + this.statusFilterControl.setValue(this.initialFilters.statusFilter); + + // Set up name filter change detection + this.nameFilterControl.valueChanges.subscribe((value) => { + // Emit filter change if value is empty + if (!value) { + this.emitFilterChange(); + } }); - // Set up status filter - this.statusFilterControl.valueChanges.subscribe((value) => { - this.filterChange.emit({ - nameFilter: this.nameFilterControl.value || '', - statusFilter: value || '' - }); + // Set up status filter change detection + this.statusFilterControl.valueChanges.subscribe(() => { + this.emitFilterChange(); }); } @@ -261,8 +274,31 @@ export class RoomsListsComponent implements OnInit, OnChanges { } } + loadMoreRooms() { + const nameFilter = this.nameFilterControl.value || ''; + const statusFilter = this.statusFilterControl.value || ''; + this.loadMore.emit({ nameFilter, statusFilter }); + } + + refreshRooms() { + const nameFilter = this.nameFilterControl.value || ''; + const statusFilter = this.statusFilterControl.value || ''; + this.refresh.emit({ nameFilter, statusFilter }); + } + // ===== FILTER METHODS ===== + triggerSearch() { + this.emitFilterChange(); + } + + private emitFilterChange() { + this.filterChange.emit({ + nameFilter: this.nameFilterControl.value || '', + statusFilter: this.statusFilterControl.value || '' + }); + } + hasActiveFilters(): boolean { return !!(this.nameFilterControl.value || this.statusFilterControl.value); } diff --git a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/rooms.component.html b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/rooms.component.html index 5623bc8..f2d1d44 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/rooms.component.html +++ b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/rooms.component.html @@ -1,27 +1,28 @@ -@if (isLoading) { - @if (showLoadingSpinner) { - -
-
-
-
- video_chat -

Loading Rooms

-
-

Please wait while we fetch your rooms...

+ +@if (isInitializing && showInitialLoader) { +
+
+
+
+ video_chat +

Loading Rooms

+

Please wait while we fetch your rooms...

+
-
- -
+
+
- } -} @else { +
+} + +@if (!isInitializing) {
+ +
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/rooms.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/rooms.component.ts index feee62d..18589ea 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/rooms.component.ts +++ b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/rooms.component.ts @@ -49,8 +49,16 @@ export class RoomsComponent implements OnInit { // searchTerm = ''; rooms = signal([]); + + // Loading state + isInitializing = true; + showInitialLoader = false; isLoading = false; - showLoadingSpinner = false; + + initialFilters = { + nameFilter: '', + statusFilter: '' + }; // Pagination hasMoreRooms = false; @@ -69,7 +77,15 @@ export class RoomsComponent implements OnInit { } async ngOnInit() { + const delayLoader = setTimeout(() => { + this.showInitialLoader = true; + }, 200); + await this.loadRooms(); + + clearTimeout(delayLoader); + this.showInitialLoader = false; + this.isInitializing = false; } async onRoomAction(action: RoomTableAction) { @@ -101,24 +117,34 @@ export class RoomsComponent implements OnInit { } } - private async loadRooms() { - this.isLoading = true; - const delaySpinner = setTimeout(() => { - this.showLoadingSpinner = true; + private async loadRooms(filters?: { nameFilter: string; statusFilter: string }, refresh = false) { + const delayLoader = setTimeout(() => { + this.isLoading = true; }, 200); try { const roomFilters: MeetRoomFilters = { maxItems: 50, - nextPageToken: this.nextPageToken + nextPageToken: !refresh ? this.nextPageToken : undefined }; + + // Apply room ID filter if provided + // if (filters?.nameFilter) { + // roomFilters.roomName = filters.nameFilter; + // } + const response = await this.roomService.listRooms(roomFilters); - // TODO: Filter rooms + // TODO: Filter rooms by status - // Update rooms list - const currentRooms = this.rooms(); - this.rooms.set([...currentRooms, ...response.rooms]); + if (!refresh) { + // Update rooms list + const currentRooms = this.rooms(); + this.rooms.set([...currentRooms, ...response.rooms]); + } else { + // Replace rooms list + this.rooms.set(response.rooms); + } // TODO: Sort rooms // this.dataSource.data = this.rooms(); @@ -131,9 +157,8 @@ export class RoomsComponent implements OnInit { this.notificationService.showAlert('Error loading rooms'); this.log.e('Error loading rooms:', error); } finally { + clearTimeout(delayLoader); this.isLoading = false; - clearTimeout(delaySpinner); - this.showLoadingSpinner = false; } } @@ -188,16 +213,13 @@ export class RoomsComponent implements OnInit { // } // } - async loadMoreRooms() { + async loadMoreRooms(filters?: { nameFilter: string; statusFilter: string }) { if (!this.hasMoreRooms || this.isLoading) return; - await this.loadRooms(); + await this.loadRooms(filters); } - async refreshRooms() { - this.rooms.set([]); - this.nextPageToken = undefined; - this.hasMoreRooms = false; - await this.loadRooms(); + async refreshRooms(filters?: { nameFilter: string; statusFilter: string }) { + await this.loadRooms(filters, true); } private async createRoom() {