diff --git a/frontend/package.json b/frontend/package.json
index fdc61cc..eae7c8e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -17,6 +17,7 @@
"@angular/platform-browser": "^19.2.0",
"@angular/platform-browser-dynamic": "^19.2.0",
"@angular/router": "^19.2.0",
+ "ngx-cookie-service": "^19.1.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index 8148fa7..2c12a4f 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -5,23 +5,26 @@ import {RouterModule, Routes} from '@angular/router';
import { HeaderComponent } from './header/header.component';
import { HomeComponent } from './home/home.component';
import {NgOptimizedImage} from '@angular/common';
+import {ThemeComponent} from "./theme/theme.component";
+import {CookieService} from 'ngx-cookie-service';
const routes: Routes = [
{ path: '', component: HomeComponent }
];
@NgModule({
- declarations: [
- AppComponent,
- HeaderComponent,
- HomeComponent
- ],
+ declarations: [
+ AppComponent,
+ HeaderComponent,
+ HomeComponent,
+ ThemeComponent
+ ],
imports: [
BrowserModule,
RouterModule.forRoot(routes),
NgOptimizedImage,
],
- providers: [],
+ providers: [CookieService],
bootstrap: [AppComponent]
})
export class AppModule { }
diff --git a/frontend/src/app/constant.ts b/frontend/src/app/constant.ts
index 6fb8a8c..9fa462d 100644
--- a/frontend/src/app/constant.ts
+++ b/frontend/src/app/constant.ts
@@ -1 +1,5 @@
export const ALTITUDE_VERSION = "1.21"
+export const enum THEME_MODE {
+ LIGHT= 'theme-light',
+ DARK = 'theme-dark'
+}
diff --git a/frontend/src/app/header/header.component.html b/frontend/src/app/header/header.component.html
index 4a92abb..42c9ebd 100644
--- a/frontend/src/app/header/header.component.html
+++ b/frontend/src/app/header/header.component.html
@@ -126,12 +126,7 @@
-
-
-
+
diff --git a/frontend/src/app/header/header.component.scss b/frontend/src/app/header/header.component.scss
index e653188..ed04791 100644
--- a/frontend/src/app/header/header.component.scss
+++ b/frontend/src/app/header/header.component.scss
@@ -251,86 +251,6 @@ nav img {
display: none;
}
-/* darkmode switch start */
-
-/* The switch - the box around the slider */
-.switch {
- position: relative;
- display: inline-block;
- width: 60px;
- height: 25px;
-}
-
-.switch-div{
- position: absolute;
- right: 40px;
-}
-
-/* Hide default HTML checkbox */
-.switch input {
- opacity: 0;
- width: 0;
- height: 0;
-}
-
-/* The slider */
-.slider {
- position: absolute;
- cursor: pointer;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: #ccc;
- -webkit-transition: 0.4s;
- transition: 0.4s;
-}
-
-.slider:before {
- position: absolute;
- content: "";
- height: 37px;
- width: 37px;
- left: 0;
- top: 0;
- bottom: 0;
- margin: auto 0;
- -webkit-transition: 0.4s;
- transition: 0.4s;
- box-shadow: 0 0 15px #2020203d;
- background: var(--switch) url('https://i.ibb.co/7JfqXxB/sunny.png');
- background-repeat: no-repeat;
- background-position: center;
-}
-
-input:checked + .slider {
- background-color: var(--link-color);
-}
-
-input:focus + .slider {
- box-shadow: 0 0 1px var(--link-color);
-}
-
-input:checked + .slider:before {
- -webkit-transform: translateX(24px);
- -ms-transform: translateX(24px);
- transform: translateX(24px);
- background: var(--switch) url('https://i.ibb.co/FxzBYR9/night.png');
- background-repeat: no-repeat;
- background-position: center;
-}
-
-/* Rounded sliders */
-.slider.round {
- border-radius: 34px;
-}
-
-.slider.round:before {
- border-radius: 50%;
-}
-
-/* darkmode switch end */
-
@-moz-document url-prefix() {
.dropdown2{
top: 12px;
diff --git a/frontend/src/app/theme/theme.component.html b/frontend/src/app/theme/theme.component.html
new file mode 100644
index 0000000..8ae21cf
--- /dev/null
+++ b/frontend/src/app/theme/theme.component.html
@@ -0,0 +1,6 @@
+
+
+
diff --git a/frontend/src/app/theme/theme.component.scss b/frontend/src/app/theme/theme.component.scss
new file mode 100644
index 0000000..dce2175
--- /dev/null
+++ b/frontend/src/app/theme/theme.component.scss
@@ -0,0 +1,75 @@
+
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 60px;
+ height: 25px;
+}
+
+.switch-div{
+ position: absolute;
+ right: 40px;
+}
+
+/* Hide default HTML checkbox */
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+/* The slider */
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ -webkit-transition: 0.4s;
+ transition: 0.4s;
+}
+
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 37px;
+ width: 37px;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ margin: auto 0;
+ -webkit-transition: 0.4s;
+ transition: 0.4s;
+ box-shadow: 0 0 15px #2020203d;
+ background: var(--switch) url('https://i.ibb.co/7JfqXxB/sunny.png');
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+input:checked + .slider {
+ background-color: var(--link-color);
+}
+
+input:focus + .slider {
+ box-shadow: 0 0 1px var(--link-color);
+}
+
+input:checked + .slider:before {
+ -webkit-transform: translateX(24px);
+ -ms-transform: translateX(24px);
+ transform: translateX(24px);
+ background: var(--switch) url('https://i.ibb.co/FxzBYR9/night.png');
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+/* Rounded sliders */
+.slider.round {
+ border-radius: 34px;
+}
+
+.slider.round:before {
+ border-radius: 50%;
+}
diff --git a/frontend/src/app/theme/theme.component.spec.ts b/frontend/src/app/theme/theme.component.spec.ts
new file mode 100644
index 0000000..7472879
--- /dev/null
+++ b/frontend/src/app/theme/theme.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ThemeComponent } from './theme.component';
+
+describe('ThemeComponent', () => {
+ let component: ThemeComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ThemeComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(ThemeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/theme/theme.component.ts b/frontend/src/app/theme/theme.component.ts
new file mode 100644
index 0000000..97feab4
--- /dev/null
+++ b/frontend/src/app/theme/theme.component.ts
@@ -0,0 +1,35 @@
+import {Component, OnDestroy, OnInit} from '@angular/core';
+import {Subscription} from 'rxjs';
+import {ThemeService} from './theme.service';
+import {THEME_MODE} from '../constant';
+
+@Component({
+ standalone: false,
+ selector: 'app-theme',
+ templateUrl: './theme.component.html',
+ styleUrl: './theme.component.scss'
+})
+export class ThemeComponent implements OnInit, OnDestroy {
+ isDarkMode: boolean = false;
+ private themeSubscription: Subscription | null = null;
+
+ constructor(private themeService: ThemeService) {}
+
+ ngOnInit(): void {
+ this.themeSubscription = this.themeService.theme$.subscribe(theme => {
+ this.isDarkMode = theme === THEME_MODE.DARK;
+ });
+ }
+
+ toggleTheme(): void {
+ this.isDarkMode = this.themeService.toggleTheme() === THEME_MODE.DARK;
+ }
+
+ ngOnDestroy(): void {
+ if (this.themeSubscription) {
+ this.themeSubscription.unsubscribe();
+ }
+ }
+
+
+}
diff --git a/frontend/src/app/theme/theme.service.spec.ts b/frontend/src/app/theme/theme.service.spec.ts
new file mode 100644
index 0000000..1c2957b
--- /dev/null
+++ b/frontend/src/app/theme/theme.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { ThemeService } from './theme.service';
+
+describe('ThemeService', () => {
+ let service: ThemeService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(ThemeService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/theme/theme.service.ts b/frontend/src/app/theme/theme.service.ts
new file mode 100644
index 0000000..a5c0e34
--- /dev/null
+++ b/frontend/src/app/theme/theme.service.ts
@@ -0,0 +1,44 @@
+import { Injectable } from '@angular/core';
+import { CookieService } from 'ngx-cookie-service';
+import { BehaviorSubject } from 'rxjs';
+import {THEME_MODE} from '../constant';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ThemeService {
+ private readonly THEME_KEY = 'theme';
+
+ private themeSubject: BehaviorSubject = new BehaviorSubject(THEME_MODE.DARK);
+ public theme$ = this.themeSubject.asObservable();
+
+ constructor(private cookieService: CookieService) {
+ this.initializeTheme();
+ }
+
+ public initializeTheme(): void {
+ const savedTheme = this.cookieService.check(this.THEME_KEY)
+ ? this.cookieService.get(this.THEME_KEY)
+ : THEME_MODE.DARK;
+ let currentTheme: THEME_MODE;
+ switch (savedTheme) {
+ case THEME_MODE.LIGHT: currentTheme = THEME_MODE.LIGHT; break;
+ case THEME_MODE.DARK:
+ default: currentTheme = THEME_MODE.DARK;
+ }
+ this.setTheme(currentTheme);
+ }
+
+ public setTheme(themeName: THEME_MODE): void {
+ this.cookieService.set(this.THEME_KEY, themeName, 365);
+ document.body.className = themeName;
+ this.themeSubject.next(themeName);
+ }
+
+ public toggleTheme(): THEME_MODE {
+ const currentTheme = this.themeSubject.getValue();
+ const newTheme = currentTheme === THEME_MODE.LIGHT ? THEME_MODE.DARK : THEME_MODE.LIGHT;
+ this.setTheme(newTheme);
+ return newTheme
+ }
+}