diff --git a/src/app/app.component.html b/src/app/app.component.html
index d1b27e6..d324a65 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,40 +1 @@
-@if (this.isLoggedIn === null) {
-
-
-} @else {
-
-@if (sharedService.isNewContactDialogOpen) {
-
-} @if (sharedService.isEditContactDialogOpen) {
-
-} @if (sharedService.isDeleteContactDialogOpen) {
-
-}
-
-}
+
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 37d9f9f..e69de29 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -1,80 +0,0 @@
-.container {
- display: flex;
-}
-
-app-header {
- position: fixed;
- top: 0;
- left: 232px;
- right: 0;
- height: 96px;
- width: 100%;
- background-color: var(--white);
- z-index: 2;
-}
-
-app-sidebar {
- width: 232px;
- height: 100vh;
-}
-
-app-sidebar-mobile {
- display: none;
- position: fixed;
- bottom: 0;
- width: 100%;
- height: 80px;
-}
-
-.main-content {
- position: fixed;
- top: 96px;
- left: 232px;
- bottom: 0;
- right: 0;
- padding: 64px;
- background-color: var(--bgContent);
- overflow-y: auto;
- z-index: 1;
-}
-
-.blur-background {
- filter: blur(5px);
- overflow: hidden;
-}
-
-.d-none {
- display: none;
-}
-
-@media screen and (max-width: 910px) {
- .main-content {
- padding: 32px;
- left: 0;
- bottom: 80px;
- }
-
- app-header {
- left: 0;
- }
-
- app-sidebar-mobile {
- display: unset;
- }
-}
-
-@media screen and (max-width: 700px) {
- .main-content {
- top: 80px;
- }
-
- app-header {
- height: 80px;
- }
-}
-
-@media screen and (max-width: 450px) {
- .main-content {
- padding: 16px;
- }
-}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 35e73d0..05cffae 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,104 +1,14 @@
import { Component } from '@angular/core';
-import { NavigationEnd, Router, RouterOutlet } from '@angular/router';
-import { HeaderComponent } from './shared/components/header/header.component';
-import { SidebarComponent } from './shared/components/sidebar/sidebar.component';
-import { LanguageService } from './services/language.service';
-import { SidebarMobileComponent } from './shared/components/sidebar/sidebar-mobile/sidebar-mobile.component';
-import { ContactEditNewComponent } from './components/contacts/contact-edit-new/contact-edit-new.component';
-import { CommonModule } from '@angular/common';
-import { SharedService } from './services/shared.service';
-import { ContactDeleteComponent } from './components/contacts/contact-delete/contact-delete.component';
+import { RouterOutlet } from '@angular/router';
import { OverlayComponent } from './shared/components/overlay/overlay.component';
-import { FirebaseService } from './services/firebase.service';
-import { filter } from 'rxjs';
@Component({
selector: 'app-root',
standalone: true,
- imports: [
- RouterOutlet,
- HeaderComponent,
- SidebarComponent,
- SidebarMobileComponent,
- ContactEditNewComponent,
- ContactDeleteComponent,
- CommonModule,
- OverlayComponent,
- ],
+ imports: [RouterOutlet, OverlayComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
})
export class AppComponent {
title = 'join';
- isLoggedIn: string = '';
-
- constructor(
- public langService: LanguageService,
- public sharedService: SharedService,
- private firebaseService: FirebaseService,
- private router: Router
- ) {
- this.checkAndClearLocalStorage();
- this.isLoggedIn = this.firebaseService.getCurrentUserId();
- }
-
- /**
- * Lifecycle hook that is called after the component has been initialized.
- *
- * Checks if the user is logged in by verifying the `isLoggedIn` property.
- */
- ngOnInit() {
- if (this.isLoggedIn === null) {
- this.checkPwResetRoute();
- } else {
- this.router.navigate(['/summary']);
- this.sharedService.isBtnDisabled = false;
- }
- }
-
- /**
- * Clears the local storage after a certain time period (12h) has
- * passed since the last login.
- */
- checkAndClearLocalStorage() {
- const startTime = localStorage.getItem('sessionTimeJOIN');
-
- if (startTime) {
- const startTimeMills = parseInt(startTime);
- const currentTime = new Date().getTime();
- const timeDifference = 12 * 60 * 60 * 1000; // 12h
- if (currentTime - startTimeMills > timeDifference) {
- localStorage.clear();
- }
- }
- }
-
- /**
- * Listens to router events and checks if the current route is one of the
- * allowed routes for password reset, registration, forgotten password,
- * or login. If it is not, it navigates to the login page.
- */
- checkPwResetRoute() {
- this.router.events
- .pipe(filter((event) => event instanceof NavigationEnd))
- .subscribe(() => {
- const urlTree = this.router.parseUrl(this.router.url);
- const firstSegment = this.getFirstSegment(urlTree);
- const allowedRoutes = ['pw-reset', 'register', 'forgot-pw', 'login'];
-
- if (!allowedRoutes.includes(firstSegment)) {
- this.router.navigate(['/login']);
- }
- });
- }
-
- /**
- * Returns the first segment of the current URL or an empty string if it does not exist.
- *
- * @param urlTree The current URL tree.
- * @returns The first segment of the current URL or an empty string if it does not exist.
- */
- private getFirstSegment(urlTree: any): string {
- return urlTree.root.children['primary']?.segments[0]?.path || '';
- }
}
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index 9da3b6e..41d58d9 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -12,41 +12,42 @@ import { LoginComponent } from './components/login/login.component';
import { RegisterComponent } from './components/login/register/register.component';
import { ForgotPwComponent } from './components/login/forgot-pw/forgot-pw.component';
import { PwResetComponent } from './components/login/forgot-pw/pw-reset/pw-reset.component';
-import { AuthGuard } from './auth.guard';
+import { AuthenticatedGuard } from './guards/authenticated.guard';
+import { RedirectIfAuthenticatedGuard } from './guards/redirect-if-authenticated.guard';
+import { MainLayoutComponent } from './components/main-layout/main-layout.component';
export const routes: Routes = [
- { path: '', component: LoginComponent },
- { path: 'login', component: LoginComponent },
- { path: 'login/notice/:id', component: LoginComponent },
- { path: 'login/imprint', component: ImprintComponent },
- { path: 'login/privacy-policy', component: PrivacyPolicyComponent },
- { path: 'register', component: RegisterComponent },
- { path: 'forgot-pw', component: ForgotPwComponent },
- { path: 'pw-reset', component: PwResetComponent },
- { path: 'summary', component: SummaryComponent, canActivate: [AuthGuard] },
- { path: 'add-task', component: AddTaskComponent, canActivate: [AuthGuard] },
+ // Parent route for all pages that do not require authenticated access
{
- path: 'add-task/:id',
- component: AddTaskComponent,
- canActivate: [AuthGuard],
+ path: '',
+ canActivate: [RedirectIfAuthenticatedGuard],
+ children: [
+ { path: '', component: LoginComponent, pathMatch: 'full' },
+ { path: 'login', component: LoginComponent },
+ { path: 'login/notice/:id', component: LoginComponent },
+ { path: 'register', component: RegisterComponent },
+ { path: 'forgot-pw', component: ForgotPwComponent },
+ { path: 'pw-reset', component: PwResetComponent },
+ { path: 'login/imprint', component: ImprintComponent },
+ { path: 'login/privacy-policy', component: PrivacyPolicyComponent },
+ ],
},
- { path: 'board', component: BoardComponent, canActivate: [AuthGuard] },
+ // Protected routes with MainLayout
{
- path: 'task/:id',
- component: TaskOverlayComponent,
- canActivate: [AuthGuard],
- },
- {
- path: 'task-edit/:id',
- component: TaskEditOverlayComponent,
- canActivate: [AuthGuard],
- },
- { path: 'contacts', component: ContactsComponent, canActivate: [AuthGuard] },
- { path: 'help', component: HelpComponent, canActivate: [AuthGuard] },
- { path: 'imprint', component: ImprintComponent, canActivate: [AuthGuard] },
- {
- path: 'privacy-policy',
- component: PrivacyPolicyComponent,
- canActivate: [AuthGuard],
+ path: '',
+ component: MainLayoutComponent,
+ canActivate: [AuthenticatedGuard],
+ children: [
+ { path: 'summary', component: SummaryComponent },
+ { path: 'add-task', component: AddTaskComponent },
+ { path: 'add-task/:id', component: AddTaskComponent },
+ { path: 'board', component: BoardComponent },
+ { path: 'task/:id', component: TaskOverlayComponent },
+ { path: 'task-edit/:id', component: TaskEditOverlayComponent },
+ { path: 'contacts', component: ContactsComponent },
+ { path: 'help', component: HelpComponent },
+ { path: 'imprint', component: ImprintComponent },
+ { path: 'privacy-policy', component: PrivacyPolicyComponent },
+ ],
},
];
diff --git a/src/app/auth.guard.spec.ts b/src/app/auth.guard.spec.ts
deleted file mode 100644
index 4ae275e..0000000
--- a/src/app/auth.guard.spec.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { CanActivateFn } from '@angular/router';
-
-import { authGuard } from './auth.guard';
-
-describe('authGuard', () => {
- const executeGuard: CanActivateFn = (...guardParameters) =>
- TestBed.runInInjectionContext(() => authGuard(...guardParameters));
-
- beforeEach(() => {
- TestBed.configureTestingModule({});
- });
-
- it('should be created', () => {
- expect(executeGuard).toBeTruthy();
- });
-});
diff --git a/src/app/components/login/login.component.ts b/src/app/components/login/login.component.ts
index 6973b44..ce9ab51 100644
--- a/src/app/components/login/login.component.ts
+++ b/src/app/components/login/login.component.ts
@@ -46,6 +46,9 @@ export class LoginComponent {
) {}
ngOnInit() {
+ if (this.loginService.checkAuthUser()) {
+ this.router.navigate(['/summary']);
+ }
this.routeParams();
}
diff --git a/src/app/components/main-layout/main-layout.component.html b/src/app/components/main-layout/main-layout.component.html
new file mode 100644
index 0000000..2a0e3e4
--- /dev/null
+++ b/src/app/components/main-layout/main-layout.component.html
@@ -0,0 +1,34 @@
+
+@if (sharedService.isNewContactDialogOpen) {
+
+} @if (sharedService.isEditContactDialogOpen) {
+
+} @if (sharedService.isDeleteContactDialogOpen) {
+
+}
diff --git a/src/app/components/main-layout/main-layout.component.scss b/src/app/components/main-layout/main-layout.component.scss
new file mode 100644
index 0000000..37d9f9f
--- /dev/null
+++ b/src/app/components/main-layout/main-layout.component.scss
@@ -0,0 +1,80 @@
+.container {
+ display: flex;
+}
+
+app-header {
+ position: fixed;
+ top: 0;
+ left: 232px;
+ right: 0;
+ height: 96px;
+ width: 100%;
+ background-color: var(--white);
+ z-index: 2;
+}
+
+app-sidebar {
+ width: 232px;
+ height: 100vh;
+}
+
+app-sidebar-mobile {
+ display: none;
+ position: fixed;
+ bottom: 0;
+ width: 100%;
+ height: 80px;
+}
+
+.main-content {
+ position: fixed;
+ top: 96px;
+ left: 232px;
+ bottom: 0;
+ right: 0;
+ padding: 64px;
+ background-color: var(--bgContent);
+ overflow-y: auto;
+ z-index: 1;
+}
+
+.blur-background {
+ filter: blur(5px);
+ overflow: hidden;
+}
+
+.d-none {
+ display: none;
+}
+
+@media screen and (max-width: 910px) {
+ .main-content {
+ padding: 32px;
+ left: 0;
+ bottom: 80px;
+ }
+
+ app-header {
+ left: 0;
+ }
+
+ app-sidebar-mobile {
+ display: unset;
+ }
+}
+
+@media screen and (max-width: 700px) {
+ .main-content {
+ top: 80px;
+ }
+
+ app-header {
+ height: 80px;
+ }
+}
+
+@media screen and (max-width: 450px) {
+ .main-content {
+ padding: 16px;
+ }
+}
diff --git a/src/app/components/main-layout/main-layout.component.spec.ts b/src/app/components/main-layout/main-layout.component.spec.ts
new file mode 100644
index 0000000..c577470
--- /dev/null
+++ b/src/app/components/main-layout/main-layout.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MainLayoutComponent } from './main-layout.component';
+
+describe('MainLayoutComponent', () => {
+ let component: MainLayoutComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MainLayoutComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(MainLayoutComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/components/main-layout/main-layout.component.ts b/src/app/components/main-layout/main-layout.component.ts
new file mode 100644
index 0000000..0deaaa0
--- /dev/null
+++ b/src/app/components/main-layout/main-layout.component.ts
@@ -0,0 +1,101 @@
+import { Component } from '@angular/core';
+import { NavigationEnd, Router, RouterOutlet } from '@angular/router';
+import { CommonModule } from '@angular/common';
+import { filter } from 'rxjs';
+import { FirebaseService } from '../../services/firebase.service';
+import { LanguageService } from '../../services/language.service';
+import { SharedService } from '../../services/shared.service';
+import { SidebarMobileComponent } from '../../shared/components/sidebar/sidebar-mobile/sidebar-mobile.component';
+import { SidebarComponent } from '../../shared/components/sidebar/sidebar.component';
+import { ContactDeleteComponent } from '../contacts/contact-delete/contact-delete.component';
+import { ContactEditNewComponent } from '../contacts/contact-edit-new/contact-edit-new.component';
+import { HeaderComponent } from '../../shared/components/header/header.component';
+
+@Component({
+ selector: 'app-main-layout',
+ standalone: true,
+ imports: [
+ RouterOutlet,
+ HeaderComponent,
+ SidebarComponent,
+ SidebarMobileComponent,
+ ContactEditNewComponent,
+ ContactDeleteComponent,
+ CommonModule,
+ ],
+ templateUrl: './main-layout.component.html',
+ styleUrl: './main-layout.component.scss',
+})
+export class MainLayoutComponent {
+ isLoggedIn: string = '';
+
+ constructor(
+ public langService: LanguageService,
+ public sharedService: SharedService,
+ private firebaseService: FirebaseService,
+ private router: Router
+ ) {
+ this.checkAndClearLocalStorage();
+ this.isLoggedIn = this.firebaseService.getCurrentUserId();
+ }
+
+ /**
+ * Lifecycle hook that is called after the component has been initialized.
+ *
+ * Checks if the user is logged in by verifying the `isLoggedIn` property.
+ */
+ ngOnInit() {
+ if (this.isLoggedIn === null) {
+ this.checkPwResetRoute();
+ } else {
+ this.router.navigate(['/summary']);
+ this.sharedService.isBtnDisabled = false;
+ }
+ }
+
+ /**
+ * Clears the local storage after a certain time period (12h) has
+ * passed since the last login.
+ */
+ checkAndClearLocalStorage() {
+ const startTime = localStorage.getItem('sessionTimeJOIN');
+
+ if (startTime) {
+ const startTimeMills = parseInt(startTime);
+ const currentTime = new Date().getTime();
+ const timeDifference = 12 * 60 * 60 * 1000; // 12h
+ if (currentTime - startTimeMills > timeDifference) {
+ localStorage.clear();
+ }
+ }
+ }
+
+ /**
+ * Listens to router events and checks if the current route is one of the
+ * allowed routes for password reset, registration, forgotten password,
+ * or login. If it is not, it navigates to the login page.
+ */
+ checkPwResetRoute() {
+ this.router.events
+ .pipe(filter((event) => event instanceof NavigationEnd))
+ .subscribe(() => {
+ const urlTree = this.router.parseUrl(this.router.url);
+ const firstSegment = this.getFirstSegment(urlTree);
+ const allowedRoutes = ['pw-reset', 'register', 'forgot-pw', 'login'];
+
+ if (!allowedRoutes.includes(firstSegment)) {
+ this.router.navigate(['/login']);
+ }
+ });
+ }
+
+ /**
+ * Returns the first segment of the current URL or an empty string if it does not exist.
+ *
+ * @param urlTree The current URL tree.
+ * @returns The first segment of the current URL or an empty string if it does not exist.
+ */
+ private getFirstSegment(urlTree: any): string {
+ return urlTree.root.children['primary']?.segments[0]?.path || '';
+ }
+}
diff --git a/src/app/guards/authenticated.guard.ts b/src/app/guards/authenticated.guard.ts
new file mode 100644
index 0000000..6c1fe01
--- /dev/null
+++ b/src/app/guards/authenticated.guard.ts
@@ -0,0 +1,28 @@
+import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+import { catchError, map, Observable, of } from 'rxjs';
+import { LoginService } from '../services/auth.service';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class AuthenticatedGuard {
+ constructor(private loginService: LoginService, private router: Router) {}
+
+ canActivate(): Observable | Promise | boolean {
+ return this.loginService.checkAuthUser().pipe(
+ map((isAuthenticated) => {
+ if (isAuthenticated) {
+ return true;
+ } else {
+ this.router.navigate(['/login']);
+ return false;
+ }
+ }),
+ catchError(() => {
+ this.router.navigate(['/login']);
+ return of(false);
+ })
+ );
+ }
+}
diff --git a/src/app/auth.guard.ts b/src/app/guards/redirect-if-authenticated.guard.ts
similarity index 52%
rename from src/app/auth.guard.ts
rename to src/app/guards/redirect-if-authenticated.guard.ts
index 104699a..3ddb4e0 100644
--- a/src/app/auth.guard.ts
+++ b/src/app/guards/redirect-if-authenticated.guard.ts
@@ -2,32 +2,25 @@ import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
-import { LoginService } from './services/auth.service';
+import { LoginService } from '../services/auth.service';
@Injectable({
providedIn: 'root',
})
-export class AuthGuard {
+export class RedirectIfAuthenticatedGuard {
constructor(private loginService: LoginService, private router: Router) {}
- /**
- * The canActivate guard checks if the user is authenticated.
- * If the user is authenticated, the guard returns true.
- * If the user is not authenticated, the guard navigates to the root route and returns false.
- * If an error occurs during the authentication check, the guard navigates to the root route and returns false.
- */
canActivate(): Observable | Promise | boolean {
return this.loginService.checkAuthUser().pipe(
map((isAuthenticated) => {
if (isAuthenticated) {
- return true;
- } else {
- this.router.navigate(['/']);
+ this.router.navigate(['/summary']);
return false;
}
+ return true;
}),
catchError(() => {
- this.router.navigate(['/']);
+ this.router.navigate(['/login']);
return of(false);
})
);
diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts
index 5479199..bbdfd1d 100644
--- a/src/app/services/auth.service.ts
+++ b/src/app/services/auth.service.ts
@@ -50,8 +50,16 @@ export class LoginService {
*/
checkAuthUser(): Observable {
return this.firebaseService.getAuthUser().pipe(
- map((user) => !!user),
- catchError(() => of(false))
+ map((user) => {
+ if (user) {
+ return true;
+ } else {
+ return false;
+ }
+ }),
+ catchError(() => {
+ return of(false);
+ })
);
}