frontend: Implement basic room creation component and integrate with room wizard

- Added RoomBasicCreationComponent for creating a room with optional name prefix.
- Integrated basic creation into RoomWizardComponent, allowing users to switch to advanced mode.
- Updated wizard navigation to include 'back' functionality and adjusted visibility of navigation buttons.
- Created RoomDetailsComponent for configuring room details, including auto-deletion date and time.
- Enhanced styling for new components and ensured responsive design.
- Updated wizard state service to manage new steps and handle edit mode appropriately.
This commit is contained in:
Carlos Santos 2025-07-28 14:59:56 +02:00
parent 2c87a3b8d8
commit 91c9690953
16 changed files with 479 additions and 299 deletions

View File

@ -14,6 +14,20 @@
</button>
}
@if (config.showBack) {
<!-- Back Button -->
<button
mat-stroked-button
class="back-btn"
id="wizard-back-btn"
(click)="onBack()"
[attr.aria-label]="'Go back'"
>
<mat-icon class="leading-icon">arrow_back</mat-icon>
{{ backButtonText }}
</button>
}
<div class="spacer"></div>
<!-- Navigation Group -->

View File

@ -15,7 +15,7 @@
flex: 1;
}
.cancel-btn {
.cancel-btn, .back-btn {
margin-right: auto;
}
@ -38,7 +38,7 @@
margin-right: 0;
}
&.cancel-btn {
&.cancel-btn, &.back-btn {
color: var(--ov-meet-text-secondary);
border-color: var(--ov-meet-border-color-strong);
@ -127,7 +127,7 @@
flex-direction: column;
gap: var(--ov-meet-spacing-md);
.cancel-btn {
.cancel-btn, .back-btn {
margin-right: 0;
order: 3;
width: 100%;
@ -171,7 +171,7 @@
}
&.loading {
button:not(.cancel-btn) {
button:not(.cancel-btn):not(.back-btn) {
pointer-events: none;
opacity: 0.7;

View File

@ -17,7 +17,8 @@ export class WizardNavComponent {
@Input() config: WizardNavigationConfig = {
showPrevious: false,
showNext: true,
showCancel: true,
showCancel: false,
showBack: true,
showFinish: false,
showSkipAndFinish: false,
disableFinish: false,
@ -27,6 +28,8 @@ export class WizardNavComponent {
finishLabel: 'Finish'
};
@Input() backButtonText: string = 'Back';
/**
* Current step identifier for context
*/
@ -38,6 +41,7 @@ export class WizardNavComponent {
@Output() previous = new EventEmitter<WizardNavigationEvent>();
@Output() next = new EventEmitter<WizardNavigationEvent>();
@Output() cancel = new EventEmitter<WizardNavigationEvent>();
@Output() back = new EventEmitter<WizardNavigationEvent>();
@Output() finish = new EventEmitter<WizardNavigationEvent>();
/**
@ -78,6 +82,16 @@ export class WizardNavComponent {
this.navigate.emit(event);
}
onBack() {
if (!this.config.showBack) return;
const event: WizardNavigationEvent = {
action: 'back',
currentStepIndex: this.currentStepId
};
this.back.emit(event);
this.navigate.emit(event);
}
onFinish() {
if (!this.config.showFinish) return;

View File

@ -19,7 +19,10 @@ export interface WizardNavigationConfig {
// Button visibility flags
showPrevious: boolean;
showNext: boolean;
// Cancel button visibility
showCancel: boolean;
// Used for going back to the previous page
showBack: boolean;
showFinish: boolean;
showSkipAndFinish: boolean; // Used for quick create actions
disableFinish?: boolean;
@ -36,6 +39,6 @@ export interface WizardNavigationConfig {
* Event interface for wizard navigation actions
*/
export interface WizardNavigationEvent {
action: 'next' | 'previous' | 'cancel' | 'finish';
action: 'next' | 'previous' | 'cancel' | 'finish' | 'back';
currentStepIndex?: number;
}

View File

@ -0,0 +1,53 @@
<div class="room-basic-creation-page fade-in">
<!-- Header Section -->
<header class="page-header">
<mat-icon class="ov-room-icon page-icon">video_chat</mat-icon>
<div class="page-title-group">
<h2 class="page-title">Create Room</h2>
<p class="page-description">
Create a new video room with optional name prefix. For advanced options, use the room wizard.
</p>
</div>
</header>
<!-- Form Section -->
<main class="page-content">
<form [formGroup]="roomCreationForm" class="room-basic-creation-form" (ngSubmit)="onCreateRoom()">
<!-- Room Prefix Field -->
<mat-form-field appearance="outline" class="form-field">
<mat-label>Room Name Prefix</mat-label>
<input matInput formControlName="roomIdPrefix" placeholder="e.g. demo-room" />
<mat-icon matSuffix class="ov-settings-icon">label</mat-icon>
<mat-hint>Optional prefix for room names. Leave empty for default naming.</mat-hint>
@if (roomCreationForm.get('roomIdPrefix')?.hasError('maxlength')) {
<mat-error> Room name prefix cannot exceed 50 characters </mat-error>
}
</mat-form-field>
<!-- Action Buttons -->
<div class="action-buttons">
<button
mat-button
color="primary"
type="submit"
[disabled]="!isFormValid"
class="create-button"
>
<mat-icon>add</mat-icon>
<span>Create Room</span>
</button>
<button
mat-stroked-button
type="button"
(click)="onOpenAdvancedMode()"
class="advanced-button"
matTooltip="Open room wizard for advanced configuration"
>
<mat-icon>handyman</mat-icon>
<span>Advanced Setup</span>
</button>
</div>
</form>
</main>
</div>

View File

@ -0,0 +1,174 @@
@import '../../../../../../../../src/assets/styles/design-tokens';
.room-basic-creation-page {
@include ov-page-content;
@include ov-container;
padding: var(--ov-meet-spacing-xl);
max-width: 600px;
margin: 0 auto;
.page-header {
display: flex;
align-items: flex-start;
gap: var(--ov-meet-spacing-md);
margin-bottom: var(--ov-meet-spacing-xl);
.page-icon {
@include ov-icon(xxl);
color: var(--ov-meet-icon-rooms);
margin-top: var(--ov-meet-spacing-xs);
}
.page-title-group {
flex: 1;
.page-title {
margin: 0 0 var(--ov-meet-spacing-sm) 0;
font-size: var(--ov-meet-font-size-xxl);
font-weight: var(--ov-meet-font-weight-medium);
color: var(--ov-meet-text-primary);
line-height: var(--ov-meet-line-height-tight);
}
.page-description {
margin: 0;
font-size: var(--ov-meet-font-size-lg);
color: var(--ov-meet-text-secondary);
line-height: var(--ov-meet-line-height-normal);
}
}
}
.page-content {
.room-basic-creation-form {
display: flex;
flex-direction: column;
gap: var(--ov-meet-spacing-xl);
.form-field {
width: 100%;
// Material form field customization using existing system
::ng-deep {
.mat-mdc-form-field-outline {
border-radius: var(--ov-meet-radius-sm);
}
.mat-mdc-form-field-label {
color: var(--ov-meet-text-secondary);
}
.mat-mdc-form-field-hint {
color: var(--ov-meet-text-hint);
font-size: var(--ov-meet-font-size-xs);
}
// Icon styling in form fields
.mat-mdc-form-field-icon-suffix {
mat-icon {
@include ov-icon(sm);
}
}
}
}
.action-buttons {
display: flex;
gap: var(--ov-meet-spacing-md);
justify-content: center;
align-items: center;
margin-top: var(--ov-meet-spacing-lg);
.create-button {
@include ov-button-base;
flex: 1;
max-width: 200px;
background-color: var(--ov-meet-color-success);
color: var(--ov-meet-text-on-primary);
box-shadow: var(--ov-meet-shadow-sm);
mat-icon {
@include ov-icon(sm);
margin-right: var(--ov-meet-spacing-xs);
}
span {
font-size: var(--ov-meet-font-size-md);
font-weight: var(--ov-meet-font-weight-medium);
}
}
.advanced-button {
@include ov-button-base;
flex: 1;
max-width: 200px;
mat-icon {
@include ov-icon(sm);
margin-right: var(--ov-meet-spacing-xs);
}
span {
font-size: var(--ov-meet-font-size-md);
font-weight: var(--ov-meet-font-weight-normal);
}
}
}
}
}
@include ov-mobile-down {
padding: var(--ov-meet-spacing-lg);
.page-header {
flex-direction: column;
text-align: center;
gap: var(--ov-meet-spacing-sm);
.page-icon {
align-self: center;
margin-top: 0;
}
.page-title-group {
.page-title {
font-size: var(--ov-meet-font-size-xl);
}
.page-description {
font-size: var(--ov-meet-font-size-md);
}
}
}
.page-content {
.room-basic-creation-form {
.action-buttons {
flex-direction: column;
.create-button,
.advanced-button {
max-width: none;
width: 100%;
}
}
}
}
}
@include ov-tablet-down {
.page-content {
.room-basic-creation-form {
.action-buttons {
flex-direction: column;
.create-button,
.advanced-button {
max-width: none;
}
}
}
}
}
}

View File

@ -0,0 +1,61 @@
import { Component, OnDestroy, Output, EventEmitter } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatTooltipModule } from '@angular/material/tooltip';
import { Subject, takeUntil } from 'rxjs';
@Component({
selector: 'ov-room-basic-creation',
standalone: true,
imports: [
ReactiveFormsModule,
MatButtonModule,
MatIconModule,
MatInputModule,
MatFormFieldModule,
MatTooltipModule
],
templateUrl: './room-basic-creation.component.html',
styleUrl: './room-basic-creation.component.scss'
})
export class RoomBasicCreationComponent implements OnDestroy {
@Output() createRoom = new EventEmitter<string | undefined>();
@Output() openAdvancedMode = new EventEmitter<void>();
roomCreationForm: FormGroup;
private destroy$ = new Subject<void>();
constructor(private fb: FormBuilder) {
this.roomCreationForm = this.fb.group({
roomIdPrefix: ['', [Validators.maxLength(50)]]
});
this.roomCreationForm.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
// Optional: Save form data to local storage or service if needed
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
onCreateRoom() {
if (this.roomCreationForm.valid) {
const formValue = this.roomCreationForm.value;
this.createRoom.emit(formValue.roomIdPrefix || undefined);
}
}
onOpenAdvancedMode() {
this.openAdvancedMode.emit();
}
get isFormValid(): boolean {
return this.roomCreationForm.valid && !this.roomCreationForm.pending;
}
}

View File

@ -6,14 +6,16 @@
editMode ? 'Edit your room settings' : 'Create and configure your video room in a few simple steps'
}}
</p>
<ov-step-indicator
[steps]="steps()"
[currentStepIndex]="currentStepIndex()"
[allowNavigation]="true"
[editMode]="editMode"
(stepClick)="onStepClick($event)"
>
</ov-step-indicator>
@if (!isBasicCreation()) {
<ov-step-indicator
[steps]="steps()"
[currentStepIndex]="currentStepIndex()"
[allowNavigation]="true"
[editMode]="editMode"
(stepClick)="onStepClick($event)"
>
</ov-step-indicator>
}
</header>
<main class="wizard-content">
@ -41,21 +43,30 @@
</div>
</div>
} @else {
@switch (currentStep()?.id) {
@case ('basic') {
<ov-room-wizard-basic-info></ov-room-wizard-basic-info>
}
@case ('recording') {
<ov-recording-preferences></ov-recording-preferences>
}
@case ('recordingTrigger') {
<ov-recording-trigger></ov-recording-trigger>
}
@case ('recordingLayout') {
<ov-recording-layout></ov-recording-layout>
}
@case ('preferences') {
<ov-room-preferences></ov-room-preferences>
@if (isBasicCreation()) {
<!-- Basic Room Creation -->
<ov-room-basic-creation
(createRoom)="createRoom($event)"
(openAdvancedMode)="onOpenAdvancedMode()"
></ov-room-basic-creation>
} @else {
<!-- Room Wizard Steps -->
@switch (currentStep()?.id) {
@case ('roomDetails') {
<ov-room-wizard-room-details></ov-room-wizard-room-details>
}
@case ('recording') {
<ov-recording-preferences></ov-recording-preferences>
}
@case ('recordingTrigger') {
<ov-recording-trigger></ov-recording-trigger>
}
@case ('recordingLayout') {
<ov-recording-layout></ov-recording-layout>
}
@case ('preferences') {
<ov-room-preferences></ov-room-preferences>
}
}
}
}
@ -63,15 +74,29 @@
</main>
<footer class="wizard-footer">
<ov-wizard-nav
[config]="navigationConfig()"
[currentStepId]="currentStepIndex()"
(previous)="onPrevious()"
(next)="onNext()"
(cancel)="onCancel()"
(finish)="onFinish()"
>
</ov-wizard-nav>
@if (isBasicCreation()) {
<div class="basic-nav">
<nav class="wizard-navigation">
<button mat-stroked-button class="cancel-button" (click)="onCancel()">
<mat-icon>close</mat-icon>
<span>Cancel</span>
</button>
</nav>
</div>
} @else {
<ov-wizard-nav
class="wizard-nav"
[config]="navigationConfig()"
[currentStepId]="currentStepIndex()"
[backButtonText]="'Back to simple mode'"
(previous)="onPrevious()"
(next)="onNext()"
(cancel)="onCancel()"
(back)="onBack()"
(finish)="onFinish()"
>
</ov-wizard-nav>
}
</footer>
}
</div>

View File

@ -5,6 +5,7 @@
@include ov-page-content;
min-height: 600px;
gap: 0;
padding-top: 0;
}
.wizard-header {
@ -37,7 +38,30 @@
}
.wizard-footer {
margin-top: auto;
flex: 1;
display: flex;
justify-content: center;
.basic-nav,
.wizard-nav {
width: 100%;
max-width: 650px;
display: flex;
flex-direction: column;
}
.basic-nav .wizard-navigation {
padding: var(--ov-meet-spacing-md) 0 0 0;
}
.cancel-button {
@include ov-button-base;
border-radius: var(--ov-meet-radius-sm);
min-width: 120px;
color: var(--ov-meet-text-secondary);
border-color: var(--ov-meet-border-color-strong);
&:hover {
background-color: var(--ov-meet-surface-hover);
}
}
}
.toggle-label {

View File

@ -9,11 +9,12 @@ import { StepIndicatorComponent, WizardNavComponent } from '@lib/components';
import { WizardNavigationConfig, WizardStep } from '@lib/models';
import { NavigationService, NotificationService, RoomService, RoomWizardStateService } from '@lib/services';
import { MeetRoomOptions } from '@lib/typings/ce';
import { RoomWizardBasicInfoComponent } from './steps/basic-info/basic-info.component';
import { RoomWizardRoomDetailsComponent } from './steps/room-details/room-details.component';
import { RecordingLayoutComponent } from './steps/recording-layout/recording-layout.component';
import { RecordingPreferencesComponent } from './steps/recording-preferences/recording-preferences.component';
import { RecordingTriggerComponent } from './steps/recording-trigger/recording-trigger.component';
import { RoomPreferencesComponent } from './steps/room-preferences/room-preferences.component';
import { RoomBasicCreationComponent } from '../room-basic-creation/room-basic-creation.component';
@Component({
selector: 'ov-room-wizard',
@ -26,7 +27,8 @@ import { RoomPreferencesComponent } from './steps/room-preferences/room-preferen
MatIconModule,
MatProgressSpinnerModule,
MatSlideToggleModule,
RoomWizardBasicInfoComponent,
RoomBasicCreationComponent,
RoomWizardRoomDetailsComponent,
RecordingPreferencesComponent,
RecordingTriggerComponent,
RecordingLayoutComponent,
@ -38,9 +40,9 @@ import { RoomPreferencesComponent } from './steps/room-preferences/room-preferen
export class RoomWizardComponent implements OnInit {
editMode: boolean = false;
roomId?: string;
existingRoomData?: MeetRoomOptions;
existingRoomData?: MeetRoomOptions; // Edit mode
isCreatingRoom = signal(false);
isBasicCreation = signal(true);
steps: Signal<WizardStep[]>;
currentStep: Signal<WizardStep | undefined>;
currentStepIndex: Signal<number>;
@ -89,6 +91,9 @@ export class RoomWizardComponent implements OnInit {
try {
const { roomIdPrefix, autoDeletionDate, preferences } = await this.roomService.getRoom(this.roomId);
this.existingRoomData = { roomIdPrefix, autoDeletionDate, preferences };
if (this.existingRoomData) {
this.isBasicCreation.set(false);
}
} catch (error) {
console.error('Error loading room data:', error);
// Navigate back to rooms list if room not found
@ -96,10 +101,19 @@ export class RoomWizardComponent implements OnInit {
}
}
onOpenAdvancedMode() {
this.isBasicCreation.set(false);
this.wizardService.goToStep(0); // Reset to first step
}
onPrevious() {
this.wizardService.goToPreviousStep();
}
onBack() {
this.isBasicCreation.set(true);
}
onNext() {
this.wizardService.goToNextStep();
}
@ -113,6 +127,22 @@ export class RoomWizardComponent implements OnInit {
await this.navigationService.navigateTo('rooms', undefined, true);
}
async createRoom(roomIdPrefix?: string) {
try {
// Call the room service to create a new room
const { moderatorRoomUrl } = await this.roomService.createRoom({ roomIdPrefix });
await this.navigationService.redirectTo(moderatorRoomUrl);
} catch (error) {
const errorMessage = `Failed to create room ${roomIdPrefix}`;
this.notificationService.showSnackbar(errorMessage);
console.error(errorMessage, error);
} finally {
this.wizardService.resetWizard();
// Deactivate loading state
this.isCreatingRoom.set(false);
}
}
async onFinish() {
const roomOptions = this.wizardService.roomOptions();
console.log('Wizard completed with data:', roomOptions);

View File

@ -1,25 +1,25 @@
<div class="basic-info-step fade-in">
<div class="room-details-step fade-in">
<!-- Header Section -->
<header class="step-header">
<mat-icon class="ov-room-icon step-icon">video_chat</mat-icon>
<div class="step-title-group">
<h3 class="step-title">Basic Information</h3>
<h3 class="step-title">Room Details</h3>
<p class="step-description">
Configure your room's basic settings including name prefix and automatic deletion date
Configure your room's details including name prefix and automatic deletion date
</p>
</div>
</header>
<!-- Form Section -->
<main class="step-content">
<form [formGroup]="basicInfoForm" class="basic-info-form">
<form [formGroup]="roomDetailsForm" class="room-details-form">
<!-- Room Prefix Field -->
<mat-form-field appearance="outline" class="form-field">
<mat-label>Room Name Prefix</mat-label>
<input matInput formControlName="roomIdPrefix" placeholder="e.g. demo-room" />
<mat-icon matSuffix class="ov-settings-icon">label</mat-icon>
<mat-hint>Optional prefix for room names. Leave empty for default naming.</mat-hint>
@if (basicInfoForm.get('roomIdPrefix')?.hasError('maxlength')) {
@if (roomDetailsForm.get('roomIdPrefix')?.hasError('maxlength')) {
<mat-error> Room name prefix cannot exceed 50 characters </mat-error>
}
</mat-form-field>
@ -42,7 +42,7 @@
matSuffix
mat-icon-button
type="button"
[disabled]="basicInfoForm.get('autoDeletionDate')?.disabled"
[disabled]="roomDetailsForm.get('autoDeletionDate')?.disabled"
(click)="clearDeletionDate()"
matTooltip="Clear date selection"
class="clear-date-button"
@ -60,7 +60,7 @@
</mat-form-field>
<!-- Time Selection (only shown when date is selected) -->
@if (basicInfoForm.get('autoDeletionDate')?.value) {
@if (roomDetailsForm.get('autoDeletionDate')?.value) {
<div class="time-selection-container">
<div class="time-selection-row">
<mat-form-field appearance="outline" class="time-field">
@ -89,7 +89,7 @@
<mat-icon matSuffix class="ov-settings-icon">access_time</mat-icon>
</mat-form-field>
</div>
@if (!basicInfoForm.hasError('minFutureDateTime')) {
@if (!roomDetailsForm.hasError('minFutureDateTime')) {
<div class="time-hint">
<mat-icon class="hint-icon material-symbols-outlined material-icons">auto_delete</mat-icon>
<span>Room will be deleted at {{ getFormattedDateTime() }}</span>

View File

@ -1,6 +1,6 @@
@import '../../../../../../../../../../src/assets/styles/design-tokens';
.basic-info-step {
.room-details-step {
@include ov-page-content;
@include ov-container;
@ -41,7 +41,7 @@
.step-content {
// margin-bottom: var(--ov-meet-spacing-xl);
.basic-info-form {
.room-details-form {
@include ov-grid-responsive(280px);
gap: var(--ov-meet-spacing-lg);
@ -144,7 +144,7 @@
}
.step-content {
.basic-info-form {
.room-details-form {
display: flex;
flex-direction: column;
gap: var(--ov-meet-spacing-md);
@ -170,7 +170,7 @@
@include ov-tablet-down {
.step-content {
.basic-info-form {
.room-details-form {
grid-template-columns: 1fr;
}
}

View File

@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RoomWizardBasicInfoComponent } from './basic-info.component';
import { RoomWizardRoomDetailsComponent } from './room-details.component';
describe('BasicInfoComponent', () => {
let component: RoomWizardBasicInfoComponent;
let fixture: ComponentFixture<RoomWizardBasicInfoComponent>;
let component: RoomWizardRoomDetailsComponent;
let fixture: ComponentFixture<RoomWizardRoomDetailsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RoomWizardBasicInfoComponent]
imports: [RoomWizardRoomDetailsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(RoomWizardBasicInfoComponent);
fixture = TestBed.createComponent(RoomWizardRoomDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -13,7 +13,7 @@ import { MeetRoomOptions } from '@lib/typings/ce';
import { Subject, takeUntil } from 'rxjs';
@Component({
selector: 'ov-room-wizard-basic-info',
selector: 'ov-room-wizard-room-details',
standalone: true,
imports: [
ReactiveFormsModule,
@ -26,11 +26,11 @@ import { Subject, takeUntil } from 'rxjs';
MatSelectModule,
MatTooltipModule
],
templateUrl: './basic-info.component.html',
styleUrl: './basic-info.component.scss'
templateUrl: './room-details.component.html',
styleUrl: './room-details.component.scss'
})
export class RoomWizardBasicInfoComponent implements OnDestroy {
basicInfoForm: FormGroup;
export class RoomWizardRoomDetailsComponent implements OnDestroy {
roomDetailsForm: FormGroup;
// Arrays for time selection
hours = Array.from({ length: 24 }, (_, i) => ({ value: i, display: i.toString().padStart(2, '0') }));
@ -40,9 +40,9 @@ export class RoomWizardBasicInfoComponent implements OnDestroy {
constructor(private wizardService: RoomWizardStateService) {
const currentStep = this.wizardService.currentStep();
this.basicInfoForm = currentStep!.formGroup;
this.roomDetailsForm = currentStep!.formGroup;
this.basicInfoForm.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.roomDetailsForm.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.saveFormData(value);
});
}
@ -71,7 +71,7 @@ export class RoomWizardBasicInfoComponent implements OnDestroy {
};
// Always save to wizard state (including when values are cleared)
this.wizardService.updateStepData('basic', stepData);
this.wizardService.updateStepData('roomDetails', stepData);
}
get minDate(): Date {
@ -81,11 +81,11 @@ export class RoomWizardBasicInfoComponent implements OnDestroy {
}
get hasDateSelected(): boolean {
return !!this.basicInfoForm.get('autoDeletionDate')?.value;
return !!this.roomDetailsForm.get('autoDeletionDate')?.value;
}
getFormattedDateTime(): string {
const formValue = this.basicInfoForm.value;
const formValue = this.roomDetailsForm.value;
if (!formValue.autoDeletionDate) {
return '';
}
@ -109,7 +109,7 @@ export class RoomWizardBasicInfoComponent implements OnDestroy {
}
clearDeletionDate() {
this.basicInfoForm.patchValue({
this.roomDetailsForm.patchValue({
autoDeletionDate: null,
autoDeletionHour: 23,
autoDeletionMinute: 59

View File

@ -1,219 +0,0 @@
// @import '../../../../../../../src/assets/styles/design-tokens';
// Use page loading utility
// .loading-container {
// @extend .ov-page-loading;
// .loading-content .loading-header .loading-title .loading-icon {
// color: var(--ov-meet-icon-rooms);
// }
// }
// Use table page actions utility
// .rooms-actions {
// @extend .ov-table-page-actions;
// .create-room-btn {
// @include ov-button-base;
// mat-icon {
// @include ov-icon(md);
// margin-right: var(--ov-meet-spacing-sm);
// }
// }
// }
// Use search field utility
// .search-bar {
// .search-field {
// @extend .ov-search-field;
// min-width: 400px;
// max-width: 500px;
// }
// }
// Use table page container utility
// .rooms-table-container {
// @extend .ov-table-page-container;
// }
// Responsive table utilities
// .table-wrapper {
// &.desktop-view {
// display: block;
// @include ov-tablet-down {
// display: none;
// }
// }
// &.mobile-view {
// display: none;
// @include ov-tablet-down {
// display: block;
// }
// }
// }
// // Use data table utility
// .rooms-table {
// @extend .ov-data-table;
// .mat-mdc-header-cell {
// &.room-header {
// @extend .primary-header;
// }
// &.actions-header {
// @extend .actions-header;
// }
// }
// .mat-mdc-cell {
// &.room-cell {
// @extend .primary-cell;
// }
// &.actions-cell {
// @extend .actions-cell;
// }
// }
// }
// Use mobile card utilities
// .mobile-rooms-list {
// display: flex;
// flex-direction: column;
// gap: var(--ov-meet-spacing-md);
// .room-mobile-card {
// @include ov-card;
// @include ov-theme-transition;
// &:hover {
// @include ov-hover-lift(-2px);
// }
// .room-card-header {
// display: flex;
// justify-content: space-between;
// align-items: flex-start;
// margin-bottom: var(--ov-meet-spacing-sm);
// .room-title {
// @extend .primary-text;
// }
// .room-status {
// @extend .ov-status-badge;
// }
// }
// .room-card-content {
// display: flex;
// flex-direction: column;
// gap: var(--ov-meet-spacing-xs);
// .room-detail {
// display: flex;
// justify-content: space-between;
// font-size: var(--ov-meet-font-size-sm);
// .detail-label {
// color: var(--ov-meet-text-secondary);
// }
// .detail-value {
// color: var(--ov-meet-text-primary);
// }
// }
// }
// .room-card-actions {
// @extend .ov-action-buttons;
// margin-top: var(--ov-meet-spacing-md);
// padding-top: var(--ov-meet-spacing-md);
// border-top: 1px solid var(--ov-meet-border-color-light);
// }
// }
// }
// // Use info display and status utilities
// .room-info {
// @extend .ov-info-display;
// }
// .status-badge {
// @extend .ov-status-badge;
// }
// .participant-count {
// @extend .ov-date-info;
// }
// .creation-date {
// @extend .ov-date-info;
// }
// // Use action buttons utility
// .action-buttons {
// @extend .ov-action-buttons;
// .mat-mdc-icon-button {
// &.primary-action {
// color: var(--ov-meet-color-primary);
// }
// &.room-preferences-btn {
// color: var(--ov-meet-icon-settings);
// }
// &.copy-link-btn {
// color: var(--ov-meet-text-secondary);
// }
// &.view-recordings-btn {
// color: var(--ov-meet-icon-recordings);
// }
// &.delete-room-btn {
// color: var(--ov-meet-color-error);
// }
// }
// }
// // Use empty state utility
// .no-rooms-state {
// @extend .ov-empty-state;
// .empty-icon {
// @include ov-icon(xl);
// color: var(--ov-meet-text-hint);
// margin-bottom: var(--ov-meet-spacing-lg);
// display: block;
// }
// .getting-started-actions {
// display: flex;
// flex-direction: column;
// gap: var(--ov-meet-spacing-md);
// align-items: center;
// button {
// @include ov-button-base;
// mat-icon {
// @include ov-icon(md);
// margin-right: var(--ov-meet-spacing-sm);
// }
// }
// }
// }
// // Use focus utilities
// .mat-mdc-checkbox,
// .mat-mdc-icon-button,
// .mat-mdc-button {
// @extend .ov-focus-visible;
// }

View File

@ -65,10 +65,10 @@ export class RoomWizardStateService {
// Define wizard steps
const baseSteps: WizardStep[] = [
{
id: 'basic',
id: 'roomDetails',
label: 'Room Details',
isCompleted: editMode, // In edit mode, mark as completed but not editable
isActive: !editMode, // Start with basic step active in create mode
isActive: !editMode, // Start with roomDetails step active in create mode
isVisible: true,
formGroup: this.formBuilder.group(
{
@ -174,7 +174,7 @@ export class RoomWizardStateService {
];
this._steps.set(baseSteps);
const initialStepIndex = editMode ? 1 : 0; // Skip basic step in edit mode
const initialStepIndex = editMode ? 1 : 0; // Skip roomDetails step in edit mode
this._currentStepIndex.set(initialStepIndex);
// Update step visibility after index is set
@ -192,7 +192,7 @@ export class RoomWizardStateService {
let updatedOptions: MeetRoomOptions;
switch (stepId) {
case 'basic':
case 'roomDetails':
updatedOptions = {
...currentOptions
};
@ -360,9 +360,10 @@ export class RoomWizardStateService {
return {
showPrevious: !isFirstStep,
showNext: !isLastStep,
showCancel: true,
showCancel: false,
showBack: true,
showFinish: isLastStep,
showSkipAndFinish: !isEditMode && isFirstStep,
showSkipAndFinish: false, // Skip and finish is not used in this wizard
disableFinish: isSomeStepInvalid,
nextLabel: 'Next',
previousLabel: 'Previous',
@ -373,13 +374,13 @@ export class RoomWizardStateService {
/**
* Checks if the wizard is in edit mode.
* Edit mode is determined by whether the basic step is completed and its form is disabled.
* Edit mode is determined by whether the roomDetails step is completed and its form is disabled.
* @returns True if in edit mode, false otherwise
*/
private isEditMode(): boolean {
const visibleSteps = this._visibleSteps();
const basicStep = visibleSteps.find((step) => step.id === 'basic');
const isEditMode = !!basicStep && basicStep.isCompleted && basicStep.formGroup.disabled;
const roomDetailsStep = visibleSteps.find((step) => step.id === 'roomDetails');
const isEditMode = !!roomDetailsStep && roomDetailsStep.isCompleted && roomDetailsStep.formGroup.disabled;
return isEditMode;
}