frontend: add toggle functionality for room status with UI updates

This commit is contained in:
juancarmore 2025-08-30 13:33:55 +02:00
parent 1af8f77b7b
commit 089877959a
7 changed files with 117 additions and 7 deletions

View File

@ -349,6 +349,16 @@
}
<mat-divider id="actions-divider-{{ room.roomId }}"></mat-divider>
<button
mat-menu-item
(click)="toggleRoomStatus(room)"
[ngClass]="getRoomToggleIconClass(room)"
id="toggle-room-status-btn-{{ room.roomId }}"
>
<mat-icon>{{ getRoomToggleIcon(room) }}</mat-icon>
<span>{{ getRoomToggleLabel(room) }}</span>
</button>
<button
mat-menu-item
(click)="deleteRoom(room)"

View File

@ -225,6 +225,7 @@
&:hover {
background-color: transparent;
}
&.primary-action {
color: var(--ov-meet-color-primary);
}
@ -247,6 +248,22 @@
}
}
.close-room-icon {
color: var(--ov-meet-color-warning) !important;
.mat-icon {
color: var(--ov-meet-color-warning) !important;
}
}
.open-room-icon {
color: var(--ov-meet-color-success) !important;
.mat-icon {
color: var(--ov-meet-color-success) !important;
}
}
.delete-action {
@extend .ov-delete-action;
}

View File

@ -35,6 +35,8 @@ export interface RoomTableAction {
| 'copyModeratorLink'
| 'copySpeakerLink'
| 'viewRecordings'
| 'reopen'
| 'close'
| 'delete'
| 'bulkDelete';
}
@ -263,6 +265,14 @@ export class RoomsListsComponent implements OnInit, OnChanges {
this.roomAction.emit({ rooms: [room], action: 'viewRecordings' });
}
toggleRoomStatus(room: MeetRoom) {
if (room.status !== MeetRoomStatus.CLOSED) {
this.roomAction.emit({ rooms: [room], action: 'close' });
} else {
this.roomAction.emit({ rooms: [room], action: 'reopen' });
}
}
deleteRoom(room: MeetRoom) {
this.roomAction.emit({ rooms: [room], action: 'delete' });
}
@ -410,4 +420,18 @@ export class RoomsListsComponent implements OnInit, OnChanges {
getAutoDeletionClass(room: MeetRoom): string {
return room.autoDeletionDate ? 'auto-deletion-scheduled' : 'auto-deletion-disabled';
}
// ===== ROOM TOGGLE =====
getRoomToggleIcon(room: MeetRoom): string {
return room.status !== MeetRoomStatus.CLOSED ? 'lock' : 'meeting_room';
}
getRoomToggleLabel(room: MeetRoom): string {
return room.status !== MeetRoomStatus.CLOSED ? 'Close Room' : 'Open Room';
}
getRoomToggleIconClass(room: MeetRoom): string {
return room.status !== MeetRoomStatus.CLOSED ? 'close-room-icon' : 'open-room-icon';
}
}

View File

@ -16,7 +16,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { RoomsListsComponent, RoomTableAction } from '@lib/components';
import { NavigationService, NotificationService, RoomService } from '@lib/services';
import { MeetingEndAction, MeetRoom, MeetRoomFilters } from '@lib/typings/ce';
import { MeetingEndAction, MeetRoom, MeetRoomFilters, MeetRoomStatus } from '@lib/typings/ce';
import { ILogger, LoggerService } from 'openvidu-components-angular';
@Component({
@ -108,6 +108,12 @@ export class RoomsComponent implements OnInit {
case 'viewRecordings':
await this.viewRecordings(action.rooms[0]);
break;
case 'reopen':
this.reopenRoom(action.rooms[0]);
break;
case 'close':
this.closeRoom(action.rooms[0]);
break;
case 'delete':
this.deleteRoom(action.rooms[0]);
break;
@ -265,6 +271,40 @@ export class RoomsComponent implements OnInit {
}
}
private async reopenRoom(room: MeetRoom) {
try {
const { room: updatedRoom } = await this.roomService.updateRoomStatus(room.roomId, MeetRoomStatus.OPEN);
// Update room in the list
this.rooms.set(this.rooms().map((r) => (r.roomId === updatedRoom.roomId ? updatedRoom : r)));
this.notificationService.showSnackbar('Room reopened successfully');
} catch (error) {
this.notificationService.showSnackbar('Failed to reopen room');
this.log.e('Error reopening room:', error);
}
}
private async closeRoom(room: MeetRoom) {
try {
const { statusCode, room: updatedRoom } = await this.roomService.updateRoomStatus(
room.roomId,
MeetRoomStatus.CLOSED
);
// Update room in the list
this.rooms.set(this.rooms().map((r) => (r.roomId === updatedRoom.roomId ? updatedRoom : r)));
if (statusCode === 202) {
this.notificationService.showSnackbar('Room scheduled to be closed when the meeting ends');
} else {
this.notificationService.showSnackbar('Room closed successfully');
}
} catch (error) {
this.notificationService.showSnackbar('Failed to close room');
this.log.e('Error closing room:', error);
}
}
private deleteRoom({ roomId }: MeetRoom) {
const deleteCallback = async () => {
try {

View File

@ -42,7 +42,7 @@ export class GlobalPreferencesService {
async saveSecurityPreferences(preferences: SecurityPreferences) {
const path = `${this.PREFERENCES_API}/security`;
await this.httpService.putRequest<SecurityPreferences>(path, preferences);
await this.httpService.putRequest(path, preferences);
this.securityPreferences = preferences;
}
@ -58,7 +58,7 @@ export class GlobalPreferencesService {
async saveWebhookPreferences(preferences: WebhookPreferences) {
const path = `${this.PREFERENCES_API}/webhooks`;
await this.httpService.putRequest<WebhookPreferences>(path, preferences);
await this.httpService.putRequest(path, preferences);
}
async testWebhookUrl(url: string): Promise<void> {

View File

@ -24,8 +24,14 @@ export class HttpService {
}
async putRequest<T>(path: string, body: any = {}, headers?: Record<string, string>): Promise<T> {
const options = headers ? { headers: new HttpHeaders(headers) } : {};
return lastValueFrom(this.http.put<T>(path, body, options));
const options = {
observe: 'response' as const,
...(headers ? { headers: new HttpHeaders(headers) } : {})
};
return lastValueFrom(this.http.put<T>(path, body, options)).then((response) => ({
...(response.body as T),
statusCode: response.status
}));
}
async patchRequest<T>(path: string, body: any = {}, headers?: Record<string, string>): Promise<T> {

View File

@ -5,7 +5,8 @@ import {
MeetRoomFilters,
MeetRoomOptions,
MeetRoomPreferences,
MeetRoomRoleAndPermissions
MeetRoomRoleAndPermissions,
MeetRoomStatus
} from '@lib/typings/ce';
import { LoggerService } from 'openvidu-components-angular';
@ -104,11 +105,23 @@ export class RoomService {
* @return A promise that resolves to the MeetRoom object
*/
async getRoom(roomId: string): Promise<MeetRoom> {
let path = `${this.ROOMS_API}/${roomId}`;
const path = `${this.ROOMS_API}/${roomId}`;
const headers = this.participantService.getParticipantRoleHeader();
return this.httpService.getRequest(path, headers);
}
/**
* Updates the status of a room.
*
* @param roomId - The unique identifier of the room
* @param status - The new status to be set
* @return A promise that resolves to an object containing the updated room and a status code
*/
async updateRoomStatus(roomId: string, status: MeetRoomStatus): Promise<{ statusCode: number; room: MeetRoom }> {
const path = `${this.ROOMS_API}/${roomId}/status`;
return this.httpService.putRequest(path, { status });
}
/**
* Deletes a room by its ID.
*