@if (config.showCancel) {
@@ -24,7 +24,6 @@
mat-stroked-button
class="prev-btn"
id="wizard-previous-btn"
- [disabled]="config.isPreviousDisabled"
(click)="onPrevious()"
[attr.aria-label]="'Go to previous step'"
>
@@ -33,50 +32,46 @@
}
- @if (config.showQuickCreate) {
+ @if (config.showSkipAndFinish) {
}
-
+
@if (config.showNext) {
}
-
+
@if (config.showFinish) {
}
diff --git a/frontend/projects/shared-meet-components/src/lib/components/wizard-nav/wizard-nav.component.ts b/frontend/projects/shared-meet-components/src/lib/components/wizard-nav/wizard-nav.component.ts
index d5c08ab..181247c 100644
--- a/frontend/projects/shared-meet-components/src/lib/components/wizard-nav/wizard-nav.component.ts
+++ b/frontend/projects/shared-meet-components/src/lib/components/wizard-nav/wizard-nav.component.ts
@@ -1,5 +1,4 @@
-import { CommonModule } from '@angular/common';
-import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
+import { Component, EventEmitter, Input, Output } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { MatIcon } from '@angular/material/icon';
import type { WizardNavigationConfig, WizardNavigationEvent } from '@lib/models';
@@ -7,30 +6,25 @@ import type { WizardNavigationConfig, WizardNavigationEvent } from '@lib/models'
@Component({
selector: 'ov-wizard-nav',
standalone: true,
- imports: [CommonModule, MatButton, MatIcon],
+ imports: [MatButton, MatIcon],
templateUrl: './wizard-nav.component.html',
styleUrl: './wizard-nav.component.scss'
})
-export class WizardNavComponent implements OnInit, OnChanges {
+export class WizardNavComponent {
/**
* Navigation configuration with default values
*/
@Input() config: WizardNavigationConfig = {
- showPrevious: true,
+ showPrevious: false,
showNext: true,
showCancel: true,
showFinish: false,
- showQuickCreate: true,
+ showSkipAndFinish: false,
+ disableFinish: false,
nextLabel: 'Next',
previousLabel: 'Previous',
cancelLabel: 'Cancel',
- finishLabel: 'Finish',
- isNextDisabled: false,
- isPreviousDisabled: false,
- isFinishDisabled: false,
- isLoading: false,
- isCompact: false,
- ariaLabel: 'Wizard navigation'
+ finishLabel: 'Finish'
};
/**
@@ -51,90 +45,56 @@ export class WizardNavComponent implements OnInit, OnChanges {
*/
@Output() navigate = new EventEmitter
();
- ngOnInit() {
- this.validateConfig();
- }
-
- ngOnChanges(changes: SimpleChanges) {
- if (changes['config']) {
- this.validateConfig();
- }
- }
-
- /**
- * Validates navigation configuration
- */
- private validateConfig() {
- if (!this.config.nextLabel) this.config.nextLabel = 'Next';
- if (!this.config.previousLabel) this.config.previousLabel = 'Previous';
- if (!this.config.cancelLabel) this.config.cancelLabel = 'Cancel';
- if (!this.config.finishLabel) this.config.finishLabel = 'Finish';
- }
-
- /**
- * Handle previous step navigation
- */
onPrevious() {
- if (!this.config.isPreviousDisabled && !this.config.isLoading) {
- const event: WizardNavigationEvent = {
- action: 'previous',
- currentStepId: this.currentStepId
- };
+ if (!this.config.showPrevious) return;
- this.previous.emit(event);
- this.navigate.emit(event);
- }
+ const event: WizardNavigationEvent = {
+ action: 'previous',
+ currentStepIndex: this.currentStepId
+ };
+ this.previous.emit(event);
+ this.navigate.emit(event);
}
- /**
- * Handle next step navigation
- */
onNext() {
- if (!this.config.isNextDisabled && !this.config.isLoading) {
- const event: WizardNavigationEvent = {
- action: 'next',
- currentStepId: this.currentStepId
- };
+ if (!this.config.showNext) return;
- this.next.emit(event);
- this.navigate.emit(event);
- }
+ const event: WizardNavigationEvent = {
+ action: 'next',
+ currentStepIndex: this.currentStepId
+ };
+ this.next.emit(event);
+ this.navigate.emit(event);
}
- /**
- * Handle wizard cancellation
- */
onCancel() {
- if (!this.config.isLoading) {
- const event: WizardNavigationEvent = {
- action: 'cancel',
- currentStepId: this.currentStepId
- };
+ if (!this.config.showCancel) return;
- this.cancel.emit(event);
- this.navigate.emit(event);
- }
+ const event: WizardNavigationEvent = {
+ action: 'cancel',
+ currentStepIndex: this.currentStepId
+ };
+ this.cancel.emit(event);
+ this.navigate.emit(event);
}
- /**
- * Handle wizard completion
- */
onFinish() {
- if (!this.config.isFinishDisabled && !this.config.isLoading) {
- const event: WizardNavigationEvent = {
- action: 'finish',
- currentStepId: this.currentStepId
- };
+ if (!this.config.showFinish) return;
- this.finish.emit(event);
- this.navigate.emit(event);
- }
+ const event: WizardNavigationEvent = {
+ action: 'finish',
+ currentStepIndex: this.currentStepId
+ };
+ this.finish.emit(event);
+ this.navigate.emit(event);
}
skipAndFinish() {
+ if (!this.config.showSkipAndFinish) return;
+
const event: WizardNavigationEvent = {
action: 'finish',
- currentStepId: this.currentStepId
+ currentStepIndex: this.currentStepId
};
this.finish.emit(event);
this.navigate.emit(event);
diff --git a/frontend/projects/shared-meet-components/src/lib/models/wizard.model.ts b/frontend/projects/shared-meet-components/src/lib/models/wizard.model.ts
index 6a13ae6..ef83d36 100644
--- a/frontend/projects/shared-meet-components/src/lib/models/wizard.model.ts
+++ b/frontend/projects/shared-meet-components/src/lib/models/wizard.model.ts
@@ -9,42 +9,27 @@ export interface WizardStep {
isCompleted: boolean;
isActive: boolean;
isVisible: boolean;
- isOptional?: boolean;
- order: number;
- validationFormGroup: FormGroup;
- description?: string;
- icon?: string;
+ formGroup: FormGroup;
}
/**
* Configuration interface for wizard navigation controls
- * Supports theming and responsive behavior
*/
export interface WizardNavigationConfig {
- // Button visibility
+ // Button visibility flags
showPrevious: boolean;
showNext: boolean;
showCancel: boolean;
showFinish: boolean;
- showQuickCreate: boolean; // Optional for quick create functionality
+ showSkipAndFinish: boolean; // Used for quick create actions
+ disableFinish?: boolean;
- // Button labels (customizable)
+ // Button labels
nextLabel?: string;
previousLabel?: string;
cancelLabel?: string;
finishLabel?: string;
-
- // Button states
- isNextDisabled: boolean;
- isPreviousDisabled: boolean;
- isFinishDisabled?: boolean;
-
- // UI states
- isLoading?: boolean;
- isCompact?: boolean;
-
- // Accessibility
- ariaLabel?: string;
+ skipAndFinishLabel?: string;
}
/**
@@ -52,7 +37,5 @@ export interface WizardNavigationConfig {
*/
export interface WizardNavigationEvent {
action: 'next' | 'previous' | 'cancel' | 'finish';
- currentStepId?: number;
- targetStepId?: string;
- data?: any;
+ currentStepIndex?: number;
}
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/room-wizard.component.html b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/room-wizard.component.html
index 7ee6a1f..b689b13 100644
--- a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/room-wizard.component.html
+++ b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/room-wizard.component.html
@@ -1,52 +1,53 @@
-
+ @if (steps().length !== 0) {
+
-
-
- @switch (currentStep?.id) {
- @case ('basic') {
-
+
+
+ @switch (currentStep()?.id) {
+ @case ('basic') {
+
+ }
+ @case ('recording') {
+
+ }
+ @case ('recordingTrigger') {
+
+ }
+ @case ('recordingLayout') {
+
+ }
+ @case ('preferences') {
+
+ }
}
- @case ('recording') {
-
- }
- @case ('recordingTrigger') {
-
- }
- @case ('recordingLayout') {
-
- }
- @case ('preferences') {
-
- }
- }
-
-
+
+
-
+
+ }
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/room-wizard.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/room-wizard.component.ts
index 9e16cd6..c417c08 100644
--- a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/room-wizard.component.ts
+++ b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/room-wizard.component.ts
@@ -1,14 +1,13 @@
import { CommonModule } from '@angular/common';
-import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Component, computed, OnInit, Signal } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { ActivatedRoute } from '@angular/router';
import { StepIndicatorComponent, WizardNavComponent } from '@lib/components';
-import { WizardNavigationConfig, WizardNavigationEvent, WizardStep } from '@lib/models';
-import { NavigationService, RoomService, RoomWizardStateService } from '@lib/services';
-import { MeetRoom, MeetRoomOptions } from '@lib/typings/ce';
-import { Subject, takeUntil } from 'rxjs';
+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 { RecordingLayoutComponent } from './steps/recording-layout/recording-layout.component';
import { RecordingPreferencesComponent } from './steps/recording-preferences/recording-preferences.component';
@@ -34,79 +33,40 @@ import { RoomPreferencesComponent } from './steps/room-preferences/room-preferen
templateUrl: './room-wizard.component.html',
styleUrl: './room-wizard.component.scss'
})
-export class RoomWizardComponent implements OnInit, OnDestroy {
+export class RoomWizardComponent implements OnInit {
editMode: boolean = false;
- roomId: string | null = null;
- existingRoomData: MeetRoomOptions | null = null;
+ roomId?: string;
+ existingRoomData?: MeetRoomOptions;
- private destroy$ = new Subject();
-
- steps: WizardStep[] = [];
- currentStep: WizardStep | null = null;
- currentStepIndex: number = 0;
- currentLayout: 'vertical-sidebar' | 'horizontal-compact' | 'vertical-compact' = 'horizontal-compact';
- navigationConfig: WizardNavigationConfig = {
- showPrevious: false,
- showNext: true,
- showCancel: true,
- showFinish: false,
- showQuickCreate: true,
- nextLabel: 'Next',
- previousLabel: 'Previous',
- finishLabel: 'Create Room',
- isNextDisabled: false,
- isPreviousDisabled: true
- };
- wizardData: MeetRoomOptions = {};
+ steps: Signal;
+ currentStep: Signal;
+ currentStepIndex: Signal;
+ navigationConfig: Signal;
constructor(
- private wizardState: RoomWizardStateService,
+ private wizardService: RoomWizardStateService,
protected roomService: RoomService,
+ protected notificationService: NotificationService,
private navigationService: NavigationService,
private route: ActivatedRoute
- ) {}
+ ) {
+ this.steps = this.wizardService.steps;
+ this.currentStep = this.wizardService.currentStep;
+ this.currentStepIndex = this.wizardService.currentStepIndex;
+ this.navigationConfig = computed(() => this.wizardService.getNavigationConfig());
+ }
async ngOnInit() {
- console.log('RoomWizard ngOnInit - starting');
-
// Detect edit mode from route
this.detectEditMode();
// If in edit mode, load room data
if (this.editMode && this.roomId) {
- this.navigationConfig.showQuickCreate = false;
await this.loadRoomData();
}
// Initialize wizard with edit mode and existing data
- this.wizardState.initializeWizard(this.editMode, this.existingRoomData || undefined);
-
- this.wizardState.steps$.pipe(takeUntil(this.destroy$)).subscribe((steps) => {
- // Only update current step info after steps are available
-
- if (steps.length > 0) {
- this.steps = steps;
- this.currentStep = this.wizardState.getCurrentStep();
- this.currentStepIndex = this.wizardState.getCurrentStepIndex();
- this.navigationConfig = this.wizardState.getNavigationConfig();
-
- // Update navigation config for edit mode
- if (this.editMode) {
- this.navigationConfig.finishLabel = 'Update Room';
- }
- }
- });
-
- this.wizardState.roomOptions$.pipe(takeUntil(this.destroy$)).subscribe((options) => {
- this.wizardData = options;
- });
-
- this.wizardState.currentStepIndex$.pipe(takeUntil(this.destroy$)).subscribe((index) => {
- // Only update if we have visible steps
- if (this.steps.filter((s) => s.isVisible).length > 0) {
- this.currentStepIndex = index;
- }
- });
+ this.wizardService.initializeWizard(this.editMode, this.existingRoomData);
}
private detectEditMode() {
@@ -116,7 +76,7 @@ export class RoomWizardComponent implements OnInit, OnDestroy {
// Get roomId from route parameters when in edit mode
if (this.editMode) {
- this.roomId = this.route.snapshot.paramMap.get('roomId');
+ this.roomId = this.route.snapshot.paramMap.get('roomId') || undefined;
}
}
@@ -124,15 +84,8 @@ export class RoomWizardComponent implements OnInit, OnDestroy {
if (!this.roomId) return;
try {
- // Fetch room data from the service
- const room: MeetRoom = await this.roomService.getRoom(this.roomId);
-
- // Convert MeetRoom to MeetRoomOptions
- this.existingRoomData = {
- roomIdPrefix: room.roomIdPrefix,
- autoDeletionDate: room.autoDeletionDate,
- preferences: room.preferences
- };
+ const { roomIdPrefix, autoDeletionDate, preferences } = await this.roomService.getRoom(this.roomId);
+ this.existingRoomData = { roomIdPrefix, autoDeletionDate, preferences };
} catch (error) {
console.error('Error loading room data:', error);
// Navigate back to rooms list if room not found
@@ -140,56 +93,42 @@ export class RoomWizardComponent implements OnInit, OnDestroy {
}
}
- ngOnDestroy() {
- this.destroy$.next();
- this.destroy$.complete();
- }
-
onPrevious() {
- this.wizardState.goToPreviousStep();
- this.currentStep = this.wizardState.getCurrentStep();
- this.navigationConfig = this.wizardState.getNavigationConfig();
+ this.wizardService.goToPreviousStep();
}
onNext() {
- this.wizardState.goToNextStep();
- this.currentStep = this.wizardState.getCurrentStep();
- this.navigationConfig = this.wizardState.getNavigationConfig();
+ this.wizardService.goToNextStep();
}
- onCancel() {
- this.navigationService.navigateTo('rooms', undefined, true);
- this.wizardState.resetWizard();
+ onStepClick(event: { index: number; step: WizardStep }) {
+ this.wizardService.goToStep(event.index);
}
- onStepClick(event: { step: WizardStep; index: number }) {
- this.wizardState.goToStep(event.index);
- this.currentStep = this.wizardState.getCurrentStep();
- this.navigationConfig = this.wizardState.getNavigationConfig();
+ async onCancel() {
+ this.wizardService.resetWizard();
+ await this.navigationService.navigateTo('rooms', undefined, true);
}
- onLayoutChange(layout: 'vertical-sidebar' | 'horizontal-compact' | 'vertical-compact') {
- this.currentLayout = layout;
- }
-
- async onFinish(event: WizardNavigationEvent) {
- const roomOptions = this.wizardState.getRoomOptions();
- console.log('Wizard completed with data:', event, roomOptions);
+ async onFinish() {
+ const roomOptions = this.wizardService.roomOptions();
+ console.log('Wizard completed with data:', roomOptions);
try {
if (this.editMode && this.roomId && roomOptions.preferences) {
await this.roomService.updateRoom(this.roomId, roomOptions.preferences);
- //TODO: Show success notification
+ this.notificationService.showSnackbar('Room updated successfully');
} else {
// Create new room
await this.roomService.createRoom(roomOptions);
- console.log('Room created successfully');
- // TODO: Show error notification
+ this.notificationService.showSnackbar('Room created successfully');
}
await this.navigationService.navigateTo('rooms', undefined, true);
} catch (error) {
- console.error(`Failed to ${this.editMode ? 'update' : 'create'} room:`, error);
+ const errorMessage = `Failed to ${this.editMode ? 'update' : 'create'} room`;
+ this.notificationService.showSnackbar(errorMessage);
+ console.error(errorMessage, error);
}
}
}
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/steps/basic-info/basic-info.component.html b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/steps/basic-info/basic-info.component.html
index 978d30a..4e1ad78 100644
--- a/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/steps/basic-info/basic-info.component.html
+++ b/frontend/projects/shared-meet-components/src/lib/pages/console/rooms/room-wizard/steps/basic-info/basic-info.component.html
@@ -16,9 +16,12 @@
Room Name Prefix
-
+
label
Optional prefix for room names. Leave empty for default naming.
+ @if (basicInfoForm.get('roomIdPrefix')?.hasError('maxlength')) {
+ Room name prefix cannot exceed 50 characters
+ }
@@ -39,6 +42,7 @@
matSuffix
mat-icon-button
type="button"
+ [disabled]="basicInfoForm.get('autoDeletionDate')?.disabled"
(click)="clearDeletionDate()"
matTooltip="Clear date selection"
class="clear-date-button"
@@ -85,10 +89,14 @@
access_time
-