feat: integrate Tailwind CSS and implement dark mode functionality
- Added Tailwind CSS dependencies and configured PostCSS. - Updated app component to wrap router outlet with a styled div for dark mode support. - Modified routing to include a default route and added auth guards. - Implemented dark mode toggle functionality in content component. - Enhanced login component with improved styling and lifecycle management. - Created items component with basic structure and routing. - Added global styles for body and dark mode variants. - Updated tests for new items component.
This commit is contained in:
@@ -1 +1,3 @@
|
||||
<router-outlet />
|
||||
<div class="dark:bg-amber-950 bg-amber-50 text-amber-950 dark:text-amber-50 flex flex-col grow">
|
||||
<router-outlet />
|
||||
</div>
|
||||
@@ -1,20 +1,27 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { Login } from './login/login';
|
||||
import { authGuard } from './app.route.guard';
|
||||
import { App } from './app';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: App
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
component: Login,
|
||||
canActivate: [authGuard]
|
||||
},
|
||||
{
|
||||
path: 'content',
|
||||
loadComponent: () => import('./content/content').then(m => m.Content),
|
||||
canActivate: [authGuard],
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadComponent: () => import('./content/content').then(m => m.Content),
|
||||
path: 'items',
|
||||
loadComponent: () => import('./content/items/items').then(m => m.Items),
|
||||
canActivate: [authGuard],
|
||||
}
|
||||
],
|
||||
}]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { APP_INITIALIZER, Component, inject, OnInit, provideAppInitializer } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, RouterOutlet } from '@angular/router';
|
||||
import { DefaultOAuthInterceptor, OAuthService } from 'angular-oauth2-oidc';
|
||||
import { AppConfigService } from './services/config.service';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet],
|
||||
providers: [
|
||||
OAuthService,
|
||||
],
|
||||
],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.scss'
|
||||
})
|
||||
@@ -26,6 +24,22 @@ export class App implements OnInit {
|
||||
});
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.as.loadDiscoveryDocumentAndLogin().then(() => this.router.navigate(['login']));
|
||||
if (!sessionStorage.getItem('isDarkMode')) {
|
||||
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'true' : 'false';
|
||||
sessionStorage.setItem('isDarkMode', isDarkMode);
|
||||
setTheme();
|
||||
}
|
||||
|
||||
this.as.loadDiscoveryDocumentAndLogin()
|
||||
.then(() => this.router.navigate(['login']));
|
||||
}
|
||||
}
|
||||
|
||||
export function setTheme(): void {
|
||||
const isDarkMode = sessionStorage.getItem('isDarkMode');
|
||||
if (isDarkMode === 'true') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
@@ -1 +1,8 @@
|
||||
<span>{{data()|json}}</span>
|
||||
<span>{{data()|json}}</span>
|
||||
<router-outlet />
|
||||
<div>
|
||||
<a [routerLink]="['/']">Back to Home</a>
|
||||
</div>
|
||||
<div>
|
||||
<a (click)="toggleMode()" class="cursor-pointer">Change mode</a>
|
||||
</div>
|
||||
@@ -1,16 +1,18 @@
|
||||
import { JsonPipe } from '@angular/common';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Component, isDevMode, signal } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, OnInit, signal } from '@angular/core';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { AppConfigService } from '../services/config.service';
|
||||
import { RouterLink, RouterOutlet } from "@angular/router";
|
||||
import { setTheme } from '../app';
|
||||
|
||||
@Component({
|
||||
selector: 'app-content',
|
||||
imports: [JsonPipe],
|
||||
imports: [JsonPipe, RouterOutlet, RouterLink],
|
||||
templateUrl: './content.html',
|
||||
styleUrl: './content.scss'
|
||||
})
|
||||
export class Content {
|
||||
export class Content implements OnInit {
|
||||
data = signal({});
|
||||
|
||||
constructor(httpClient: HttpClient, readonly as: OAuthService, readonly cs: AppConfigService) {
|
||||
@@ -19,4 +21,16 @@ export class Content {
|
||||
this.data.set(data);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
setTheme();
|
||||
}
|
||||
|
||||
toggleMode() {
|
||||
sessionStorage.getItem('isDarkMode') === 'true' ?
|
||||
sessionStorage.setItem('isDarkMode', 'false') : sessionStorage.setItem('isDarkMode', 'true');
|
||||
setTheme();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
4
Web/src/app/content/items/items.html
Normal file
4
Web/src/app/content/items/items.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<p>items works!</p>
|
||||
<div>
|
||||
<a [routerLink]="['/','content']">Back to content</a>
|
||||
</div>
|
||||
0
Web/src/app/content/items/items.scss
Normal file
0
Web/src/app/content/items/items.scss
Normal file
23
Web/src/app/content/items/items.spec.ts
Normal file
23
Web/src/app/content/items/items.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Items } from './items';
|
||||
|
||||
describe('Items', () => {
|
||||
let component: Items;
|
||||
let fixture: ComponentFixture<Items>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Items]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Items);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
12
Web/src/app/content/items/items.ts
Normal file
12
Web/src/app/content/items/items.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-items',
|
||||
imports: [RouterLink],
|
||||
templateUrl: './items.html',
|
||||
styleUrl: './items.scss'
|
||||
})
|
||||
export class Items {
|
||||
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
|
||||
Logging in...
|
||||
<div class="flex grow flex-col justify-center text-center text-xl">Logging in...</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.html',
|
||||
styleUrl: './login.scss'
|
||||
})
|
||||
export class Login implements OnInit {
|
||||
export class Login implements OnInit, OnDestroy {
|
||||
|
||||
private readonly _destroy: Subject<void> = new Subject<void>();
|
||||
|
||||
constructor(readonly as: OAuthService, readonly router: Router) { }
|
||||
|
||||
@@ -15,19 +18,27 @@ export class Login implements OnInit {
|
||||
if (this.as.hasValidAccessToken() && this.as.hasValidIdToken()) {
|
||||
this.getUserInfo();
|
||||
} else {
|
||||
this.as.events.subscribe(event => {
|
||||
if (event.type === 'token_received')
|
||||
if (this.as.hasValidIdToken()) {
|
||||
this.getUserInfo();
|
||||
}
|
||||
})
|
||||
this.as.events
|
||||
.pipe(
|
||||
takeUntil(this._destroy))
|
||||
.subscribe(event => {
|
||||
if (event.type === 'token_received')
|
||||
if (this.as.hasValidIdToken()) {
|
||||
this.getUserInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._destroy.next();
|
||||
this._destroy.complete();
|
||||
}
|
||||
getUserInfo(): void {
|
||||
this.as.loadUserProfile().then(value => {
|
||||
console.log('User profile loaded:', value);
|
||||
this.router.navigate(['/content']);
|
||||
});
|
||||
this.as.loadUserProfile()
|
||||
.then(_ => {
|
||||
this.router.navigate(['content', 'items']);
|
||||
})
|
||||
.catch(_ => this.router.navigate(['content', 'items']));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user