diff --git a/angular.json b/angular.json index d7cc641..6f2761e 100644 --- a/angular.json +++ b/angular.json @@ -23,6 +23,11 @@ { "glob": "**/*", "input": "public" + }, + { + "glob": "**/*", + "input": "src/assets", + "output": "assets" } ], "styles": [ diff --git a/package-lock.json b/package-lock.json index 20e7ffc..ad85746 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bonsai-webapp", - "version": "0.0.0", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bonsai-webapp", - "version": "0.0.0", + "version": "0.1.0", "dependencies": { "@angular/common": "^21.2.0", "@angular/compiler": "^21.2.0", @@ -15,6 +15,7 @@ "@angular/platform-browser": "^21.2.0", "@angular/router": "^21.2.0", "bootstrap": "^5.3.3", + "keycloak-js": "^26.2.4", "marked": "^18.0.4", "rxjs": "~7.8.0", "tslib": "^2.3.0" @@ -6072,6 +6073,15 @@ ], "license": "MIT" }, + "node_modules/keycloak-js": { + "version": "26.2.4", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-26.2.4.tgz", + "integrity": "sha512-PnXpR3ubETGOt0B/Qt2lxmPbkZr5bc3vlQsOqDoTPPQsZRp7JjhTKxlJ187uWh8qJhvBab6Gsjb06a8ayOPfuw==", + "license": "Apache-2.0", + "workspaces": [ + "test" + ] + }, "node_modules/listr2": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", diff --git a/package.json b/package.json index 6bdc09e..5f2c9b6 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@angular/platform-browser": "^21.2.0", "@angular/router": "^21.2.0", "bootstrap": "^5.3.3", + "keycloak-js": "^26.2.4", "marked": "^18.0.4", "rxjs": "~7.8.0", "tslib": "^2.3.0" diff --git a/src/app/app.config.ts b/src/app/app.config.ts index e75614a..164478f 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,8 +1,13 @@ -import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; +import { ApplicationConfig, inject, provideBrowserGlobalErrorListeners, provideAppInitializer } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; +import { KeycloakService } from './auth/keycloak.service'; export const appConfig: ApplicationConfig = { - providers: [provideBrowserGlobalErrorListeners(), provideRouter(routes)], + providers: [ + provideBrowserGlobalErrorListeners(), + provideRouter(routes), + provideAppInitializer(() => inject(KeycloakService).init()), + ], }; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 2ff2cd7..71ef07f 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -3,14 +3,15 @@ import { Home } from './home/home'; import { IssueDetail } from './issues/issue-detail/issue-detail'; import { Issues } from './issues/issues'; import { Projects } from './projects/projects'; +import { authGuard } from './auth/auth.guard'; export const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: 'home' }, { path: 'home', component: Home }, - { path: 'project', component: Projects }, + { path: 'project', component: Projects, canActivate: [authGuard] }, { path: 'projects', redirectTo: 'project' }, - { path: 'issues/new', component: IssueDetail }, - { path: 'issues/:id', component: IssueDetail }, - { path: 'issues', component: Issues }, + { path: 'issues/new', component: IssueDetail, canActivate: [authGuard] }, + { path: 'issues/:id', component: IssueDetail, canActivate: [authGuard] }, + { path: 'issues', component: Issues, canActivate: [authGuard] }, { path: '**', redirectTo: 'home' }, ]; diff --git a/src/app/menu/menu.css b/src/app/menu/menu.css index 7801aab..e6e172a 100644 --- a/src/app/menu/menu.css +++ b/src/app/menu/menu.css @@ -64,8 +64,44 @@ font-weight: 600; } -.sidebar-footer { +.sidebar-user { + display: flex; + flex-direction: column; + gap: 0.4rem; + padding: 0.6rem 0.75rem; + border-top: 1px solid #e5e7eb; margin-top: auto; +} + +.sidebar-user-name { + font-size: 0.85rem; + font-weight: 600; + color: #374151; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.sidebar-logout { + width: 100%; + padding: 0.35rem 0.5rem; + font-size: 0.8rem; + font-weight: 500; + color: #6b7280; + background: transparent; + border: 1px solid #d1d5db; + border-radius: 0.375rem; + cursor: pointer; + transition: background 0.1s, color 0.1s, border-color 0.1s; +} + +.sidebar-logout:hover { + background: #fee2e2; + color: #dc2626; + border-color: #fca5a5; +} + +.sidebar-footer { padding: 0 0.5rem; font-size: 0.72rem; color: #9ca3af; diff --git a/src/app/menu/menu.html b/src/app/menu/menu.html index 0acc1ff..84b5e77 100644 --- a/src/app/menu/menu.html +++ b/src/app/menu/menu.html @@ -40,5 +40,12 @@ } + @if (keycloak.isAuthenticated()) { + + } + diff --git a/src/app/menu/menu.ts b/src/app/menu/menu.ts index 89709cb..ea4735c 100644 --- a/src/app/menu/menu.ts +++ b/src/app/menu/menu.ts @@ -1,6 +1,7 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { RouterLink, RouterLinkActive } from '@angular/router'; import { version } from '../../../package.json'; +import { KeycloakService } from '../auth/keycloak.service'; @Component({ selector: 'app-menu', @@ -10,10 +11,15 @@ import { version } from '../../../package.json'; }) export class Menu { protected readonly version = version; + protected readonly keycloak = inject(KeycloakService); protected readonly menuItems = [ { label: 'Accueil', path: '/home' }, { label: 'Projet', path: '/project' }, { label: 'Issues', path: '/issues' }, ]; + + protected logout(): void { + this.keycloak.logout(); + } }