From ad911b7479ed732cd706f26203fb39cf240b234a Mon Sep 17 00:00:00 2001 From: khalil liloulah Date: Thu, 2 Apr 2026 14:00:50 +0200 Subject: [PATCH] feat(ui): configure routing with lazy-loaded components Closes #26 --- ui/src/app/app.component.html | 340 +----------------- ui/src/app/app.component.scss | 4 + ui/src/app/app.component.ts | 6 +- ui/src/app/app.routes.ts | 24 +- .../components/header/header.component.html | 8 + .../components/header/header.component.scss | 41 +++ .../app/components/header/header.component.ts | 15 + .../components/legend/legend.component.html | 10 + .../components/legend/legend.component.scss | 42 +++ .../app/components/legend/legend.component.ts | 28 ++ .../room-details-panel.component.html | 5 + .../room-details-panel.component.scss | 16 + .../room-details-panel.component.ts | 19 + .../room-map/room-map.component.html | 4 + .../room-map/room-map.component.scss | 9 + .../components/room-map/room-map.component.ts | 14 + 16 files changed, 246 insertions(+), 339 deletions(-) create mode 100644 ui/src/app/components/header/header.component.html create mode 100644 ui/src/app/components/header/header.component.scss create mode 100644 ui/src/app/components/header/header.component.ts create mode 100644 ui/src/app/components/legend/legend.component.html create mode 100644 ui/src/app/components/legend/legend.component.scss create mode 100644 ui/src/app/components/legend/legend.component.ts create mode 100644 ui/src/app/components/room-details-panel/room-details-panel.component.html create mode 100644 ui/src/app/components/room-details-panel/room-details-panel.component.scss create mode 100644 ui/src/app/components/room-details-panel/room-details-panel.component.ts create mode 100644 ui/src/app/components/room-map/room-map.component.html create mode 100644 ui/src/app/components/room-map/room-map.component.scss create mode 100644 ui/src/app/components/room-map/room-map.component.ts diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 36093e1..e45b994 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -1,336 +1,4 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - - - + +
+ +
\ No newline at end of file diff --git a/ui/src/app/app.component.scss b/ui/src/app/app.component.scss index e69de29..b5736d3 100644 --- a/ui/src/app/app.component.scss +++ b/ui/src/app/app.component.scss @@ -0,0 +1,4 @@ + main { + min-height: calc(100vh - 64px); + background: #fafafa; + } \ No newline at end of file diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 3a1913a..92337e0 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -1,13 +1,15 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { HeaderComponent } from './components/header/header.component'; @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet], + imports: [RouterOutlet, HeaderComponent], templateUrl: './app.component.html', styleUrl: './app.component.scss' }) export class AppComponent { - title = 'dashboard'; + title = 'PI_E2EEDA Dashboard'; + } diff --git a/ui/src/app/app.routes.ts b/ui/src/app/app.routes.ts index dc39edb..150891b 100644 --- a/ui/src/app/app.routes.ts +++ b/ui/src/app/app.routes.ts @@ -1,3 +1,25 @@ import { Routes } from '@angular/router'; -export const routes: Routes = []; +export const routes: Routes = [ + { + path: '', + redirectTo: '/dashboard', + pathMatch: 'full' + }, + { + path: 'dashboard', + loadComponent: () => + import('./components/room-map/room-map.component').then(m => m.RoomMapComponent), + title: 'Dashboard - PI_E2EEDA' + }, + { + path: 'room/:id', + loadComponent: () => + import('./components/room-details-panel/room-details-panel.component').then(m => m.RoomDetailsPanelComponent), + title: 'Room Details - PI_E2EEDA' + }, + { + path: '**', + redirectTo: '/dashboard' + } +]; diff --git a/ui/src/app/components/header/header.component.html b/ui/src/app/components/header/header.component.html new file mode 100644 index 0000000..9323fb5 --- /dev/null +++ b/ui/src/app/components/header/header.component.html @@ -0,0 +1,8 @@ +
+
+

🌡️ PI_E2EEDA

+ +
+
\ No newline at end of file diff --git a/ui/src/app/components/header/header.component.scss b/ui/src/app/components/header/header.component.scss new file mode 100644 index 0000000..a32322c --- /dev/null +++ b/ui/src/app/components/header/header.component.scss @@ -0,0 +1,41 @@ + header { + background: #00bcd4; + color: white; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + } + + .header-content { + max-width: 1200px; + margin: 0 auto; + padding: 16px 24px; + display: flex; + justify-content: space-between; + align-items: center; + } + + h1 { + margin: 0; + font-size: 24px; + cursor: pointer; + + &:hover { + opacity: 0.9; + } + } + + nav a { + color: white; + text-decoration: none; + margin-left: 24px; + padding: 8px 16px; + border-radius: 4px; + transition: background 0.3s; + + &:hover { + background: rgba(255,255,255,0.1); + } + + &.active { + background: rgba(255,255,255,0.2); + } + } \ No newline at end of file diff --git a/ui/src/app/components/header/header.component.ts b/ui/src/app/components/header/header.component.ts new file mode 100644 index 0000000..d4be90e --- /dev/null +++ b/ui/src/app/components/header/header.component.ts @@ -0,0 +1,15 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +@Component({ + selector: 'app-header', + standalone: true, + imports: [CommonModule, RouterModule], + templateUrl: './header.component.html', + styleUrl: './header.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class HeaderComponent { + +} diff --git a/ui/src/app/components/legend/legend.component.html b/ui/src/app/components/legend/legend.component.html new file mode 100644 index 0000000..0281e56 --- /dev/null +++ b/ui/src/app/components/legend/legend.component.html @@ -0,0 +1,10 @@ +
+

Air Quality (CO2)

+
+
+
+ {{ level.label }} + {{ level.range }} +
+
+
> diff --git a/ui/src/app/components/legend/legend.component.scss b/ui/src/app/components/legend/legend.component.scss new file mode 100644 index 0000000..5efa204 --- /dev/null +++ b/ui/src/app/components/legend/legend.component.scss @@ -0,0 +1,42 @@ + .legend { + background: white; + border-radius: 8px; + padding: 16px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + } + + h3 { + margin: 0 0 12px 0; + font-size: 14px; + font-weight: 600; + color: rgba(0,0,0,0.87); + } + + .legend-items { + display: flex; + flex-direction: column; + gap: 8px; + } + + .legend-item { + display: flex; + align-items: center; + gap: 12px; + font-size: 13px; + } + + .color-box { + width: 24px; + height: 24px; + border-radius: 4px; + flex-shrink: 0; + } + + .label { + font-weight: 500; + min-width: 100px; + } + + .range { + color: rgba(0,0,0,0.6); + } \ No newline at end of file diff --git a/ui/src/app/components/legend/legend.component.ts b/ui/src/app/components/legend/legend.component.ts new file mode 100644 index 0000000..5d26739 --- /dev/null +++ b/ui/src/app/components/legend/legend.component.ts @@ -0,0 +1,28 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +interface CO2Level { + label: string; + range: string; + color: string; +} + +@Component({ + selector: 'app-legend', + standalone: true, + imports: [], + templateUrl: './legend.component.html', + styleUrl: './legend.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class LegendComponent { + levels: CO2Level[] = [ + { label: 'Excellent', range: '< 800 ppm', color: '#4caf50' }, + { label: 'Good', range: '800-1000 ppm', color: '#8bc34a' }, + { label: 'Moderate', range: '1000-1200 ppm', color: '#ffc107' }, + { label: 'Poor', range: '1200-1500 ppm', color: '#ff9800' }, + { label: 'Very Poor', range: '1500-2000 ppm', color: '#ff5722' }, + { label: 'Critical', range: '> 2000 ppm', color: '#f44336' } + ]; + +} diff --git a/ui/src/app/components/room-details-panel/room-details-panel.component.html b/ui/src/app/components/room-details-panel/room-details-panel.component.html new file mode 100644 index 0000000..f9f7485 --- /dev/null +++ b/ui/src/app/components/room-details-panel/room-details-panel.component.html @@ -0,0 +1,5 @@ +
+

Room Details - {{ roomId }}

+

Detailed room information coming soon.

+ ← Back to Dashboard +
\ No newline at end of file diff --git a/ui/src/app/components/room-details-panel/room-details-panel.component.scss b/ui/src/app/components/room-details-panel/room-details-panel.component.scss new file mode 100644 index 0000000..85b77cf --- /dev/null +++ b/ui/src/app/components/room-details-panel/room-details-panel.component.scss @@ -0,0 +1,16 @@ + .room-details-container { + padding: 24px; + } + + h1 { + margin-bottom: 16px; + } + + a { + color: #00bcd4; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } \ No newline at end of file diff --git a/ui/src/app/components/room-details-panel/room-details-panel.component.ts b/ui/src/app/components/room-details-panel/room-details-panel.component.ts new file mode 100644 index 0000000..a0e4792 --- /dev/null +++ b/ui/src/app/components/room-details-panel/room-details-panel.component.ts @@ -0,0 +1,19 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ActivatedRoute, RouterModule } from '@angular/router'; +@Component({ + selector: 'app-room-details-panel', + standalone: true, + imports: [CommonModule, RouterModule], + templateUrl: './room-details-panel.component.html', + styleUrl: './room-details-panel.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RoomDetailsPanelComponent { + roomId: string | null = null; + + constructor(private route: ActivatedRoute) { + this.roomId = this.route.snapshot.paramMap.get('id'); + } + +} diff --git a/ui/src/app/components/room-map/room-map.component.html b/ui/src/app/components/room-map/room-map.component.html new file mode 100644 index 0000000..9ead5cc --- /dev/null +++ b/ui/src/app/components/room-map/room-map.component.html @@ -0,0 +1,4 @@ +
+

Room Map

+

Interactive 2D SVG map will be displayed here.

+
diff --git a/ui/src/app/components/room-map/room-map.component.scss b/ui/src/app/components/room-map/room-map.component.scss new file mode 100644 index 0000000..44af470 --- /dev/null +++ b/ui/src/app/components/room-map/room-map.component.scss @@ -0,0 +1,9 @@ +.room-map-container { + padding: 24px; + text-align: center; + } + + h1 { + color: #00bcd4; + margin-bottom: 16px; + } \ No newline at end of file diff --git a/ui/src/app/components/room-map/room-map.component.ts b/ui/src/app/components/room-map/room-map.component.ts new file mode 100644 index 0000000..36b89f7 --- /dev/null +++ b/ui/src/app/components/room-map/room-map.component.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +@Component({ + selector: 'app-room-map', + standalone: true, + imports: [CommonModule], + templateUrl: './room-map.component.html', + styleUrl: './room-map.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class RoomMapComponent { + +}