frontend: add auto-deletion policies selection when creating new rooms
This commit is contained in:
parent
87594dfe17
commit
0b3c0e9e42
@ -32,9 +32,16 @@
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
min-height: 450px;
|
min-height: 450px;
|
||||||
max-height: 450px;
|
max-height: 450px;
|
||||||
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content > :first-child {
|
||||||
|
margin: auto 0;
|
||||||
|
width: 100%;
|
||||||
|
min-height: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-footer {
|
.wizard-footer {
|
||||||
|
|||||||
@ -4,9 +4,7 @@
|
|||||||
<mat-icon class="ov-room-icon step-icon">video_chat</mat-icon>
|
<mat-icon class="ov-room-icon step-icon">video_chat</mat-icon>
|
||||||
<div class="step-title-group">
|
<div class="step-title-group">
|
||||||
<h3 class="step-title">Room Details</h3>
|
<h3 class="step-title">Room Details</h3>
|
||||||
<p class="step-description">
|
<p class="step-description">Configure your room's details including name and automatic deletion date</p>
|
||||||
Configure your room's details including name and automatic deletion date
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@ -59,8 +57,8 @@
|
|||||||
<mat-hint>Optional. Room will be automatically deleted on this date and time.</mat-hint>
|
<mat-hint>Optional. Room will be automatically deleted on this date and time.</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- Time Selection (only shown when date is selected) -->
|
@if (hasDateSelected) {
|
||||||
@if (roomDetailsForm.get('autoDeletionDate')?.value) {
|
<!-- Time Selection (only shown when date is selected) -->
|
||||||
<div class="time-selection-container">
|
<div class="time-selection-container">
|
||||||
<div class="time-selection-row">
|
<div class="time-selection-row">
|
||||||
<mat-form-field appearance="outline" class="time-field">
|
<mat-form-field appearance="outline" class="time-field">
|
||||||
@ -89,6 +87,7 @@
|
|||||||
<mat-icon matSuffix class="ov-settings-icon">access_time</mat-icon>
|
<mat-icon matSuffix class="ov-settings-icon">access_time</mat-icon>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (!roomDetailsForm.hasError('minFutureDateTime')) {
|
@if (!roomDetailsForm.hasError('minFutureDateTime')) {
|
||||||
<div class="time-hint">
|
<div class="time-hint">
|
||||||
<mat-icon class="hint-icon material-symbols-outlined material-icons">auto_delete</mat-icon>
|
<mat-icon class="hint-icon material-symbols-outlined material-icons">auto_delete</mat-icon>
|
||||||
@ -97,6 +96,46 @@
|
|||||||
} @else {
|
} @else {
|
||||||
<mat-error> Deletion date and time must be at least one hour in the future </mat-error>
|
<mat-error> Deletion date and time must be at least one hour in the future </mat-error>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<!-- Auto-deletion Policies Section -->
|
||||||
|
<div class="auto-deletion-policies">
|
||||||
|
<h4 class="policies-title">
|
||||||
|
<mat-icon class="policies-icon">policy</mat-icon>
|
||||||
|
Auto-deletion Policies
|
||||||
|
</h4>
|
||||||
|
<p class="policies-description">
|
||||||
|
Configure how the room should be handled during auto-deletion if there are active meetings
|
||||||
|
or recordings.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="policies-row">
|
||||||
|
<mat-form-field appearance="outline" class="policy-field">
|
||||||
|
<mat-label>With Active Meeting Policy</mat-label>
|
||||||
|
<mat-select formControlName="autoDeletionPolicyWithMeeting">
|
||||||
|
@for (option of meetingPolicyOptions; track option.value) {
|
||||||
|
<mat-option [value]="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-icon matSuffix class="ov-settings-icon">groups</mat-icon>
|
||||||
|
<mat-hint>{{ getMeetingPolicyDescription() }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="policy-field">
|
||||||
|
<mat-label>With Recordings Policy</mat-label>
|
||||||
|
<mat-select formControlName="autoDeletionPolicyWithRecordings">
|
||||||
|
@for (option of recordingPolicyOptions; track option.value) {
|
||||||
|
<mat-option [value]="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
<mat-icon matSuffix class="ov-settings-icon">video_library</mat-icon>
|
||||||
|
<mat-hint>{{ getRecordingPolicyDescription() }}</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -108,6 +108,51 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-deletion policies styling
|
||||||
|
.auto-deletion-policies {
|
||||||
|
margin-top: var(--ov-meet-spacing-lg);
|
||||||
|
|
||||||
|
.policies-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--ov-meet-spacing-xs);
|
||||||
|
margin: 0 0 var(--ov-meet-spacing-xs) 0;
|
||||||
|
font-size: var(--ov-meet-font-size-md);
|
||||||
|
font-weight: var(--ov-meet-font-weight-medium);
|
||||||
|
color: var(--ov-meet-text-primary);
|
||||||
|
|
||||||
|
.policies-icon {
|
||||||
|
@include ov-icon(sm);
|
||||||
|
color: var(--ov-meet-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.policies-description {
|
||||||
|
margin: 0 0 var(--ov-meet-spacing-md) 0;
|
||||||
|
font-size: var(--ov-meet-font-size-sm);
|
||||||
|
color: var(--ov-meet-text-secondary);
|
||||||
|
line-height: var(--ov-meet-line-height-relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
.policies-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: var(--ov-meet-spacing-md);
|
||||||
|
|
||||||
|
.policy-field {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
::ng-deep {
|
||||||
|
.mat-mdc-form-field-hint {
|
||||||
|
font-size: var(--ov-meet-font-size-xs);
|
||||||
|
color: var(--ov-meet-text-hint);
|
||||||
|
line-height: var(--ov-meet-line-height-tight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.time-hint {
|
.time-hint {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -163,6 +208,13 @@
|
|||||||
display: none; // Hide separator in mobile vertical layout
|
display: none; // Hide separator in mobile vertical layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auto-deletion-policies {
|
||||||
|
.policies-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: var(--ov-meet-spacing-sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,12 @@ import { MatInputModule } from '@angular/material/input';
|
|||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { RoomWizardStateService } from '@lib/services';
|
import { RoomWizardStateService } from '@lib/services';
|
||||||
import { MeetRoomOptions } from '@lib/typings/ce';
|
import {
|
||||||
|
MeetRoomAutoDeletionPolicy,
|
||||||
|
MeetRoomDeletionPolicyWithMeeting,
|
||||||
|
MeetRoomDeletionPolicyWithRecordings,
|
||||||
|
MeetRoomOptions
|
||||||
|
} from '@lib/typings/ce';
|
||||||
import { Subject, takeUntil } from 'rxjs';
|
import { Subject, takeUntil } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -36,6 +41,32 @@ export class RoomWizardRoomDetailsComponent implements OnDestroy {
|
|||||||
hours = Array.from({ length: 24 }, (_, i) => ({ value: i, display: i.toString().padStart(2, '0') }));
|
hours = Array.from({ length: 24 }, (_, i) => ({ value: i, display: i.toString().padStart(2, '0') }));
|
||||||
minutes = Array.from({ length: 60 }, (_, i) => ({ value: i, display: i.toString().padStart(2, '0') }));
|
minutes = Array.from({ length: 60 }, (_, i) => ({ value: i, display: i.toString().padStart(2, '0') }));
|
||||||
|
|
||||||
|
meetingPolicyOptions = [
|
||||||
|
{
|
||||||
|
value: MeetRoomDeletionPolicyWithMeeting.FORCE,
|
||||||
|
label: 'Force',
|
||||||
|
description:
|
||||||
|
'The meeting will be ended, and the room will be deleted without waiting for participants to leave.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
|
||||||
|
label: 'When meeting ends',
|
||||||
|
description: 'The room will be deleted when the meeting ends.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
recordingPolicyOptions = [
|
||||||
|
{
|
||||||
|
value: MeetRoomDeletionPolicyWithRecordings.FORCE,
|
||||||
|
label: 'Force',
|
||||||
|
description: 'The room and its recordings will be deleted.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MeetRoomDeletionPolicyWithRecordings.CLOSE,
|
||||||
|
label: 'Close',
|
||||||
|
description: 'The room will be closed instead of deleted, maintaining its recordings.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(private wizardService: RoomWizardStateService) {
|
constructor(private wizardService: RoomWizardStateService) {
|
||||||
@ -54,20 +85,29 @@ export class RoomWizardRoomDetailsComponent implements OnDestroy {
|
|||||||
|
|
||||||
private saveFormData(formValue: any) {
|
private saveFormData(formValue: any) {
|
||||||
let autoDeletionDateTime: number | undefined = undefined;
|
let autoDeletionDateTime: number | undefined = undefined;
|
||||||
|
let autoDeletionPolicy: MeetRoomAutoDeletionPolicy | undefined = undefined;
|
||||||
|
|
||||||
// If date is selected, combine it with time
|
// If date is selected
|
||||||
if (formValue.autoDeletionDate) {
|
if (formValue.autoDeletionDate) {
|
||||||
|
// Combine date with time
|
||||||
const date = new Date(formValue.autoDeletionDate);
|
const date = new Date(formValue.autoDeletionDate);
|
||||||
date.setHours(formValue.autoDeletionHour || 23);
|
date.setHours(formValue.autoDeletionHour || 23);
|
||||||
date.setMinutes(formValue.autoDeletionMinute || 59);
|
date.setMinutes(formValue.autoDeletionMinute || 59);
|
||||||
date.setSeconds(0);
|
date.setSeconds(0);
|
||||||
date.setMilliseconds(0);
|
date.setMilliseconds(0);
|
||||||
autoDeletionDateTime = date.getTime();
|
autoDeletionDateTime = date.getTime();
|
||||||
|
|
||||||
|
// Set auto deletion policy
|
||||||
|
autoDeletionPolicy = {
|
||||||
|
withMeeting: formValue.autoDeletionPolicyWithMeeting,
|
||||||
|
withRecordings: formValue.autoDeletionPolicyWithRecordings
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const stepData: Partial<MeetRoomOptions> = {
|
const stepData: Partial<MeetRoomOptions> = {
|
||||||
roomName: formValue.roomName,
|
roomName: formValue.roomName,
|
||||||
autoDeletionDate: autoDeletionDateTime
|
autoDeletionDate: autoDeletionDateTime,
|
||||||
|
autoDeletionPolicy
|
||||||
};
|
};
|
||||||
|
|
||||||
// Always save to wizard state (including when values are cleared)
|
// Always save to wizard state (including when values are cleared)
|
||||||
@ -115,4 +155,16 @@ export class RoomWizardRoomDetailsComponent implements OnDestroy {
|
|||||||
autoDeletionMinute: 59
|
autoDeletionMinute: 59
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMeetingPolicyDescription(): string {
|
||||||
|
const selectedValue = this.roomDetailsForm.get('autoDeletionPolicyWithMeeting')?.value;
|
||||||
|
const option = this.meetingPolicyOptions.find((opt) => opt.value === selectedValue);
|
||||||
|
return option?.description || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getRecordingPolicyDescription(): string {
|
||||||
|
const selectedValue = this.roomDetailsForm.get('autoDeletionPolicyWithRecordings')?.value;
|
||||||
|
const option = this.recordingPolicyOptions.find((opt) => opt.value === selectedValue);
|
||||||
|
return option?.description || '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
import { computed, Injectable, signal } from '@angular/core';
|
import { computed, Injectable, signal } from '@angular/core';
|
||||||
import { AbstractControl, FormBuilder, ValidationErrors, Validators } from '@angular/forms';
|
import { AbstractControl, FormBuilder, ValidationErrors, Validators } from '@angular/forms';
|
||||||
import { WizardNavigationConfig, WizardStep } from '@lib/models';
|
import { WizardNavigationConfig, WizardStep } from '@lib/models';
|
||||||
import { MeetRecordingAccess, MeetRoomOptions, MeetRoomPreferences } from '@lib/typings/ce';
|
import {
|
||||||
|
MeetRecordingAccess,
|
||||||
|
MeetRoomDeletionPolicyWithMeeting,
|
||||||
|
MeetRoomDeletionPolicyWithRecordings,
|
||||||
|
MeetRoomOptions,
|
||||||
|
MeetRoomPreferences
|
||||||
|
} from '@lib/typings/ce';
|
||||||
|
|
||||||
// Default room preferences following the app's defaults
|
// Default room preferences following the app's defaults
|
||||||
const DEFAULT_PREFERENCES: MeetRoomPreferences = {
|
const DEFAULT_PREFERENCES: MeetRoomPreferences = {
|
||||||
@ -101,6 +107,22 @@ export class RoomWizardStateService {
|
|||||||
disabled: editMode
|
disabled: editMode
|
||||||
},
|
},
|
||||||
editMode ? [] : [Validators.min(0), Validators.max(59)]
|
editMode ? [] : [Validators.min(0), Validators.max(59)]
|
||||||
|
],
|
||||||
|
autoDeletionPolicyWithMeeting: [
|
||||||
|
{
|
||||||
|
value:
|
||||||
|
initialRoomOptions.autoDeletionPolicy?.withMeeting ||
|
||||||
|
MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
|
||||||
|
disabled: editMode
|
||||||
|
}
|
||||||
|
],
|
||||||
|
autoDeletionPolicyWithRecordings: [
|
||||||
|
{
|
||||||
|
value:
|
||||||
|
initialRoomOptions.autoDeletionPolicy?.withRecordings ||
|
||||||
|
MeetRoomDeletionPolicyWithRecordings.CLOSE,
|
||||||
|
disabled: editMode
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -204,6 +226,9 @@ export class RoomWizardStateService {
|
|||||||
if ('autoDeletionDate' in stepData) {
|
if ('autoDeletionDate' in stepData) {
|
||||||
updatedOptions.autoDeletionDate = stepData.autoDeletionDate;
|
updatedOptions.autoDeletionDate = stepData.autoDeletionDate;
|
||||||
}
|
}
|
||||||
|
if ('autoDeletionPolicy' in stepData) {
|
||||||
|
updatedOptions.autoDeletionPolicy = stepData.autoDeletionPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'recording':
|
case 'recording':
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user