frontend: add theme toggle functionality

This commit is contained in:
juancarmore 2025-07-21 11:40:34 +02:00
parent a0e23cd4a7
commit e2f5725fa5
6 changed files with 47 additions and 27 deletions

View File

@ -11,7 +11,16 @@
<mat-icon>account_circle</mat-icon> <mat-icon>account_circle</mat-icon>
</button> --> </button> -->
<button mat-icon-button (click)="onLogoutClicked.emit()" aria-label="Logout" id="logout-btn"> <button
mat-icon-button
(click)="toggleTheme()"
[matTooltip]="isDarkMode() ? 'Switch to light mode' : 'Switch to dark mode'"
aria-label="Toggle theme mode"
id="theme-toggle-btn"
>
<mat-icon>{{ isDarkMode() ? 'wb_sunny' : 'nights_stay' }}</mat-icon>
</button>
<button mat-icon-button (click)="onLogoutClicked.emit()" matTooltip="Logout" aria-label="Logout" id="logout-btn">
<mat-icon>logout</mat-icon> <mat-icon>logout</mat-icon>
</button> </button>
</mat-toolbar> </mat-toolbar>

View File

@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, Output, Signal, ViewChild } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list'; import { MatListModule } from '@angular/material/list';
@ -8,7 +8,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { ConsoleNavLink } from '@lib/models'; import { ConsoleNavLink } from '@lib/models';
import { AppDataService } from '@lib/services'; import { AppDataService, ThemeService } from '@lib/services';
@Component({ @Component({
selector: 'ov-console-nav', selector: 'ov-console-nav',
@ -33,11 +33,17 @@ export class ConsoleNavComponent {
isSideMenuCollapsed = false; isSideMenuCollapsed = false;
version = ''; version = '';
isDarkMode: Signal<boolean>;
@Input() navLinks: ConsoleNavLink[] = []; @Input() navLinks: ConsoleNavLink[] = [];
@Output() onLogoutClicked: EventEmitter<void> = new EventEmitter<void>(); @Output() onLogoutClicked: EventEmitter<void> = new EventEmitter<void>();
constructor(private appDataService: AppDataService) { constructor(
private appDataService: AppDataService,
private themeService: ThemeService
) {
this.version = `${this.appDataService.getVersion()} (${this.appDataService.getEdition()})`; this.version = `${this.appDataService.getVersion()} (${this.appDataService.getEdition()})`;
this.isDarkMode = this.themeService.isDark;
} }
async toggleSideMenu() { async toggleSideMenu() {
@ -49,4 +55,8 @@ export class ConsoleNavComponent {
await this.sidenav.open(); await this.sidenav.open();
} }
} }
toggleTheme() {
this.themeService.toggleTheme();
}
} }

View File

@ -21,7 +21,12 @@ export class ConsoleComponent {
icon: 'code_blocks', icon: 'code_blocks',
iconClass: 'material-symbols-outlined ov-developer-icon' iconClass: 'material-symbols-outlined ov-developer-icon'
}, },
{ label: 'Users & Permissions', route: 'users-permissions', icon: 'passkey', iconClass: 'ov-settings-icon material-symbols-outlined' } {
label: 'Users & Permissions',
route: 'users-permissions',
icon: 'passkey',
iconClass: 'ov-settings-icon material-symbols-outlined'
}
// { label: 'About', route: 'about', icon: 'info' } // { label: 'About', route: 'about', icon: 'info' }
]; ];

View File

@ -4,7 +4,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatGridListModule } from '@angular/material/grid-list'; import { MatGridListModule } from '@angular/material/grid-list';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { NavigationService, RecordingManagerService, RoomService, ThemeService } from '@lib/services'; import { NavigationService, RecordingManagerService, RoomService } from '@lib/services';
import { MeetRecordingStatus, MeetRoom } from '@lib/typings/ce'; import { MeetRecordingStatus, MeetRoom } from '@lib/typings/ce';
interface OverviewStats { interface OverviewStats {
@ -36,8 +36,7 @@ export class OverviewComponent implements OnInit {
constructor( constructor(
private roomService: RoomService, private roomService: RoomService,
private recordingService: RecordingManagerService, private recordingService: RecordingManagerService,
private navigationService: NavigationService, private navigationService: NavigationService
private themeService: ThemeService
) {} ) {}
async ngOnInit() { async ngOnInit() {
@ -59,7 +58,8 @@ export class OverviewComponent implements OnInit {
totalRooms: rooms.length, totalRooms: rooms.length,
activeRooms: rooms.filter((room: MeetRoom) => !room.markedForDeletion).length, activeRooms: rooms.filter((room: MeetRoom) => !room.markedForDeletion).length,
totalRecordings: recordings.length, totalRecordings: recordings.length,
playableRecordings: recordings.filter((recording) => recording.status === MeetRecordingStatus.COMPLETE).length, playableRecordings: recordings.filter((recording) => recording.status === MeetRecordingStatus.COMPLETE)
.length,
hasData: rooms.length > 0 || recordings.length > 0, hasData: rooms.length > 0 || recordings.length > 0,
isLoading: false isLoading: false
}; };

View File

@ -34,15 +34,6 @@ export class ThemeService {
this.listenToSystemChanges(); this.listenToSystemChanges();
} }
/**
* Changes the current theme
*/
public setTheme(theme: Theme): void {
this._currentTheme.set(theme);
this.applyThemeToDocument(theme);
this.saveThemePreference(theme);
}
/** /**
* Toggles between light and dark theme * Toggles between light and dark theme
*/ */
@ -51,6 +42,15 @@ export class ThemeService {
this.setTheme(newTheme); this.setTheme(newTheme);
} }
/**
* Changes the current theme
*/
private setTheme(theme: Theme): void {
this._currentTheme.set(theme);
this.applyThemeToDocument(theme);
this.saveThemePreference(theme);
}
/** /**
* Applies the theme to the document * Applies the theme to the document
*/ */
@ -127,11 +127,4 @@ export class ThemeService {
const systemTheme = this.getSystemPreference(); const systemTheme = this.getSystemPreference();
this.setTheme(systemTheme); this.setTheme(systemTheme);
} }
/**
* Gets the theme value as string for use in templates
*/
public getThemeValue(): Theme {
return this._currentTheme();
}
} }

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router'; import { RouterOutlet } from '@angular/router';
import { AppDataService } from '@lib/services'; import { AppDataService, ThemeService } from '@lib/services';
import packageInfo from '../../package.json'; import packageInfo from '../../package.json';
@Component({ @Component({
@ -11,7 +11,10 @@ import packageInfo from '../../package.json';
imports: [RouterOutlet] imports: [RouterOutlet]
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
constructor(private appDataService: AppDataService) {} constructor(
private appDataService: AppDataService,
private themeService: ThemeService
) {}
ngOnInit() { ngOnInit() {
this.appDataService.setVersion(packageInfo.version); this.appDataService.setVersion(packageInfo.version);