frontend: Refactors rooms lists component to use Angular signals
Migrates the rooms lists component to leverage Angular's signal-based inputs. This improves change detection and simplifies data flow within the component. Updates the component's template to reflect the use of signal accessors. Ensures initial filters are correctly applied.
This commit is contained in:
parent
520816b983
commit
e70dc6619f
@ -1,4 +1,4 @@
|
||||
@if (!loading && rooms.length === 0 && !showEmptyFilterMessage) {
|
||||
@if (!loading() && rooms().length === 0 && !showEmptyFilterMessage) {
|
||||
<!-- Empty State -->
|
||||
<div class="no-rooms-state">
|
||||
<div class="empty-content">
|
||||
@ -18,7 +18,7 @@
|
||||
<mat-toolbar class="rooms-toolbar" id="rooms-toolbar">
|
||||
<!-- Left Section: Search -->
|
||||
<div class="toolbar-left" id="toolbar-left">
|
||||
@if (showSearchBox) {
|
||||
@if (showSearchBox()) {
|
||||
<mat-form-field class="search-field" appearance="outline" id="search-field">
|
||||
<mat-label>Search rooms</mat-label>
|
||||
<input
|
||||
@ -33,7 +33,7 @@
|
||||
matSuffix
|
||||
class="search-btn"
|
||||
(click)="triggerSearch()"
|
||||
[disabled]="loading || !nameFilterControl.value"
|
||||
[disabled]="loading() || !nameFilterControl.value"
|
||||
matTooltip="Search"
|
||||
id="search-btn"
|
||||
>
|
||||
@ -44,14 +44,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Center Section: Batch Actions (visible when items selected) -->
|
||||
@if (showSelection && selectedRooms().size > 0) {
|
||||
@if (showSelection() && selectedRooms().size > 0) {
|
||||
<div class="toolbar-center" id="toolbar-center">
|
||||
<div class="batch-actions" id="batch-actions">
|
||||
<button
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
(click)="bulkDeleteSelected()"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
matTooltip="Delete selected rooms"
|
||||
id="bulk-delete-btn"
|
||||
>
|
||||
@ -68,7 +68,7 @@
|
||||
mat-icon-button
|
||||
class="clear-btn"
|
||||
(click)="clearFilters()"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
matTooltip="Clear all filters"
|
||||
id="clear-filters-btn"
|
||||
>
|
||||
@ -80,7 +80,7 @@
|
||||
mat-icon-button
|
||||
class="refresh-btn"
|
||||
(click)="refreshRooms()"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
matTooltip="Refresh rooms"
|
||||
id="refresh-btn"
|
||||
>
|
||||
@ -92,7 +92,7 @@
|
||||
Create Room
|
||||
</button>
|
||||
|
||||
@if (showFilters) {
|
||||
@if (showFilters()) {
|
||||
<button mat-icon-button [matMenuTriggerFor]="filtersMenu" matTooltip="Filter rooms" id="filters-btn">
|
||||
<mat-icon>tune</mat-icon>
|
||||
</button>
|
||||
@ -117,12 +117,12 @@
|
||||
</mat-toolbar>
|
||||
|
||||
<!-- Loading Spinner -->
|
||||
@if (loading) {
|
||||
@if (loading()) {
|
||||
<div class="loading-container">
|
||||
<mat-spinner diameter="40"></mat-spinner>
|
||||
<span>Loading rooms...</span>
|
||||
</div>
|
||||
} @else if (rooms.length === 0 && showEmptyFilterMessage) {
|
||||
} @else if (rooms().length === 0 && showEmptyFilterMessage) {
|
||||
<!-- No rooms match the current filters -->
|
||||
<div class="no-rooms-state">
|
||||
<div class="empty-content">
|
||||
@ -141,7 +141,7 @@
|
||||
<div class="table-container" id="table-container">
|
||||
<table
|
||||
mat-table
|
||||
[dataSource]="rooms"
|
||||
[dataSource]="rooms()"
|
||||
matSort
|
||||
matSortDisableClear
|
||||
[matSortActive]="currentSortField"
|
||||
@ -151,14 +151,14 @@
|
||||
id="rooms-table"
|
||||
>
|
||||
<!-- Selection Column -->
|
||||
@if (showSelection) {
|
||||
@if (showSelection()) {
|
||||
<ng-container matColumnDef="select">
|
||||
<th mat-header-cell *matHeaderCellDef id="select-header">
|
||||
<mat-checkbox
|
||||
[checked]="allSelected()"
|
||||
[indeterminate]="someSelected()"
|
||||
(change)="toggleAllSelection()"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
id="select-all-checkbox"
|
||||
>
|
||||
</mat-checkbox>
|
||||
@ -168,7 +168,7 @@
|
||||
<mat-checkbox
|
||||
[checked]="isRoomSelected(room)"
|
||||
(change)="toggleRoomSelection(room)"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
id="select-room-{{ room.roomId }}"
|
||||
>
|
||||
</mat-checkbox>
|
||||
@ -309,7 +309,7 @@
|
||||
mat-icon-button
|
||||
matTooltip="Open Room"
|
||||
(click)="openRoom(room)"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
class="primary-action"
|
||||
id="open-room-btn-{{ room.roomId }}"
|
||||
>
|
||||
@ -323,7 +323,7 @@
|
||||
mat-icon-button
|
||||
matTooltip="Room Settings"
|
||||
(click)="editRoom(room)"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
id="edit-room-btn-{{ room.roomId }}"
|
||||
>
|
||||
<mat-icon class="ov-settings-icon">settings</mat-icon>
|
||||
@ -335,7 +335,7 @@
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="actionsMenu"
|
||||
matTooltip="More Actions"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
id="more-actions-btn-{{ room.roomId }}"
|
||||
>
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
@ -406,13 +406,13 @@
|
||||
</div>
|
||||
|
||||
<!-- Load More Section -->
|
||||
@if (showLoadMore) {
|
||||
@if (showLoadMore()) {
|
||||
<div class="load-more-container" id="load-more-container">
|
||||
<button
|
||||
mat-button
|
||||
class="load-more-btn"
|
||||
(click)="loadMoreRooms()"
|
||||
[disabled]="loading"
|
||||
[disabled]="loading()"
|
||||
id="load-more-btn"
|
||||
>
|
||||
<mat-icon>expand_more</mat-icon>
|
||||
|
||||
@ -1,15 +1,5 @@
|
||||
import { CommonModule, DatePipe } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
HostBinding,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
Output,
|
||||
signal,
|
||||
SimpleChanges
|
||||
} from '@angular/core';
|
||||
import { Component, effect, EventEmitter, HostBinding, input, OnInit, Output, signal } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
@ -98,20 +88,19 @@ export interface RoomTableFilter {
|
||||
templateUrl: './rooms-lists.component.html',
|
||||
styleUrl: './rooms-lists.component.scss'
|
||||
})
|
||||
export class RoomsListsComponent implements OnInit, OnChanges {
|
||||
// Input properties
|
||||
@Input() rooms: MeetRoom[] = [];
|
||||
@Input() showSearchBox = true;
|
||||
@Input() showFilters = true;
|
||||
@Input() showSelection = true;
|
||||
@Input() showLoadMore = false;
|
||||
@Input() loading = false;
|
||||
@Input() initialFilters: RoomTableFilter = {
|
||||
export class RoomsListsComponent implements OnInit {
|
||||
rooms = input<MeetRoom[]>([]);
|
||||
showSearchBox = input(true);
|
||||
showFilters = input(true);
|
||||
showSelection = input(true);
|
||||
showLoadMore = input(false);
|
||||
loading = input(false);
|
||||
initialFilters = input<RoomTableFilter>({
|
||||
nameFilter: '',
|
||||
statusFilter: '',
|
||||
sortField: 'creationDate',
|
||||
sortOrder: 'desc'
|
||||
};
|
||||
});
|
||||
|
||||
// Host binding for styling when rooms are selected
|
||||
@HostBinding('class.has-selections')
|
||||
@ -151,34 +140,33 @@ export class RoomsListsComponent implements OnInit, OnChanges {
|
||||
{ value: MeetRoomStatus.CLOSED, label: 'Closed' }
|
||||
];
|
||||
|
||||
constructor() {}
|
||||
constructor() {
|
||||
effect(() => {
|
||||
// Update selected rooms based on current rooms
|
||||
const rooms = this.rooms();
|
||||
const validIds = new Set(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
|
||||
this.showEmptyFilterMessage = rooms.length === 0 && this.hasActiveFilters();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.setupFilters();
|
||||
this.updateDisplayedColumns();
|
||||
}
|
||||
|
||||
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
|
||||
this.showEmptyFilterMessage = this.rooms.length === 0 && this.hasActiveFilters();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== INITIALIZATION METHODS =====
|
||||
|
||||
private setupFilters() {
|
||||
// Set up initial filter values
|
||||
this.nameFilterControl.setValue(this.initialFilters.nameFilter);
|
||||
this.statusFilterControl.setValue(this.initialFilters.statusFilter);
|
||||
this.currentSortField = this.initialFilters.sortField;
|
||||
this.currentSortOrder = this.initialFilters.sortOrder;
|
||||
// Initialize from initialFilters input
|
||||
this.nameFilterControl.setValue(this.initialFilters().nameFilter);
|
||||
this.statusFilterControl.setValue(this.initialFilters().statusFilter);
|
||||
this.currentSortField = this.initialFilters().sortField;
|
||||
this.currentSortOrder = this.initialFilters().sortOrder;
|
||||
|
||||
// Set up name filter change detection
|
||||
this.nameFilterControl.valueChanges.subscribe((value) => {
|
||||
@ -197,7 +185,7 @@ export class RoomsListsComponent implements OnInit, OnChanges {
|
||||
private updateDisplayedColumns() {
|
||||
this.displayedColumns = [];
|
||||
|
||||
if (this.showSelection) {
|
||||
if (this.showSelection()) {
|
||||
this.displayedColumns.push('select');
|
||||
}
|
||||
|
||||
@ -211,7 +199,7 @@ export class RoomsListsComponent implements OnInit, OnChanges {
|
||||
if (this.allSelected()) {
|
||||
selected.clear();
|
||||
} else {
|
||||
this.rooms.forEach((room) => {
|
||||
this.rooms().forEach((room) => {
|
||||
if (this.canSelectRoom(room)) {
|
||||
selected.add(room.roomId);
|
||||
}
|
||||
@ -233,7 +221,7 @@ export class RoomsListsComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
private updateSelectionState() {
|
||||
const selectableRooms = this.rooms.filter((r) => this.canSelectRoom(r));
|
||||
const selectableRooms = this.rooms().filter((r) => this.canSelectRoom(r));
|
||||
const selectedCount = this.selectedRooms().size;
|
||||
const selectableCount = selectableRooms.length;
|
||||
|
||||
@ -251,7 +239,7 @@ export class RoomsListsComponent implements OnInit, OnChanges {
|
||||
|
||||
getSelectedRooms(): MeetRoom[] {
|
||||
const selected = this.selectedRooms();
|
||||
return this.rooms.filter((r) => selected.has(r.roomId));
|
||||
return this.rooms().filter((r) => selected.has(r.roomId));
|
||||
}
|
||||
|
||||
// ===== ACTION METHODS =====
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
[showRoomInfo]="false"
|
||||
[showLoadMore]="hasMoreRecordings"
|
||||
[roomName]="roomName"
|
||||
[initialFilters]="initialFilters"
|
||||
[initialFilters]="initialFilters()"
|
||||
(recordingAction)="onRecordingAction($event)"
|
||||
(loadMore)="loadMoreRecordings($event)"
|
||||
(refresh)="refreshRecordings($event)"
|
||||
|
||||
@ -9,11 +9,8 @@ import { ILogger, LoggerService } from 'openvidu-components-angular';
|
||||
import { NavigationService } from '../../../../shared/services/navigation.service';
|
||||
import { NotificationService } from '../../../../shared/services/notification.service';
|
||||
import { MeetingContextService } from '../../../meeting/services';
|
||||
import {
|
||||
RecordingListsComponent,
|
||||
RecordingTableAction,
|
||||
RecordingTableFilter
|
||||
} from '../../../recordings/components/recording-lists/recording-lists.component';
|
||||
import { RecordingListsComponent } from '../../../recordings/components/recording-lists/recording-lists.component';
|
||||
import { RecordingTableAction, RecordingTableFilter } from '../../../recordings/models/recording-list.model';
|
||||
import { RecordingService } from '../../../recordings/services/recording.service';
|
||||
import { RoomMemberService } from '../../services/room-member.service';
|
||||
|
||||
@ -34,12 +31,12 @@ export class RoomRecordingsComponent implements OnInit {
|
||||
showInitialLoader = false;
|
||||
isLoading = false;
|
||||
|
||||
initialFilters: RecordingTableFilter = {
|
||||
initialFilters = signal<RecordingTableFilter>({
|
||||
nameFilter: '',
|
||||
statusFilter: '',
|
||||
sortField: 'startDate',
|
||||
sortOrder: 'desc'
|
||||
};
|
||||
});
|
||||
|
||||
// Pagination
|
||||
hasMoreRecordings = false;
|
||||
@ -68,7 +65,7 @@ export class RoomRecordingsComponent implements OnInit {
|
||||
this.showInitialLoader = true;
|
||||
}, 200);
|
||||
|
||||
await this.loadRecordings(this.initialFilters);
|
||||
await this.loadRecordings(this.initialFilters());
|
||||
|
||||
clearTimeout(delayLoader);
|
||||
this.showInitialLoader = false;
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
[showFilters]="true"
|
||||
[showSelection]="true"
|
||||
[showLoadMore]="hasMoreRooms"
|
||||
[initialFilters]="initialFilters"
|
||||
[initialFilters]="initialFilters()"
|
||||
(roomAction)="onRoomAction($event)"
|
||||
(loadMore)="loadMoreRooms($event)"
|
||||
(refresh)="refreshRooms($event)"
|
||||
|
||||
@ -67,12 +67,12 @@ export class RoomsComponent implements OnInit {
|
||||
showInitialLoader = false;
|
||||
isLoading = false;
|
||||
|
||||
initialFilters: RoomTableFilter = {
|
||||
initialFilters = signal<RoomTableFilter>({
|
||||
nameFilter: '',
|
||||
statusFilter: '',
|
||||
sortField: 'creationDate',
|
||||
sortOrder: 'desc'
|
||||
};
|
||||
});
|
||||
|
||||
// Pagination
|
||||
hasMoreRooms = false;
|
||||
@ -96,7 +96,7 @@ export class RoomsComponent implements OnInit {
|
||||
this.showInitialLoader = true;
|
||||
}, 200);
|
||||
|
||||
await this.loadRooms(this.initialFilters);
|
||||
await this.loadRooms(this.initialFilters());
|
||||
|
||||
clearTimeout(delayLoader);
|
||||
this.showInitialLoader = false;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user