Empty
       
Scroll Bottom
diff --git a/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts b/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts
index 9e3b6d42f..01e8217ee 100644
--- a/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts
+++ b/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts
@@ -1,10 +1,13 @@
 /* eslint-disable @angular-eslint/component-selector */
 import { Component, Input } from '@angular/core';
+import { RouterLink } from '@angular/router';
+
 @Component({
   selector: 'nav-button',
   standalone: true,
+  imports: [RouterLink],
   template: `
-    
+    
       
     
   `,
@@ -14,4 +17,18 @@ import { Component, Input } from '@angular/core';
 })
 export class NavButtonComponent {
   @Input() href = '';
+  fragment = '';
+
+  ngOnInit() {
+    const [path, fragment] = this.href.split('#');
+    this.href = path || '';
+    this.fragment = fragment || '';
+  }
+
+  handleClick() {
+    if (this.fragment) {
+      const element = document.getElementById(this.fragment);
+      element?.scrollIntoView({ behavior: 'smooth' });
+    }
+  }
 }
diff --git a/apps/angular/21-anchor-navigation/src/styles.scss b/apps/angular/21-anchor-navigation/src/styles.scss
index 77e408aa8..d1e86f4b1 100644
--- a/apps/angular/21-anchor-navigation/src/styles.scss
+++ b/apps/angular/21-anchor-navigation/src/styles.scss
@@ -3,3 +3,7 @@
 @tailwind utilities;
 
 /* You can add global styles to this file, and also import other style files */
+
+html {
+  scroll-behavior: smooth;
+}
diff --git a/apps/angular/22-router-input/src/app/app.component.ts b/apps/angular/22-router-input/src/app/app.component.ts
index 9dfc11200..8f6d5974a 100644
--- a/apps/angular/22-router-input/src/app/app.component.ts
+++ b/apps/angular/22-router-input/src/app/app.component.ts
@@ -6,20 +6,81 @@ import { RouterLink, RouterModule } from '@angular/router';
   imports: [RouterLink, RouterModule, ReactiveFormsModule],
   selector: 'app-root',
   template: `
-    
-    
-    
-    
-    
-    
-    
+    
+      
+        
+        
+      
+      
+        
+        
+      
+      
+        
+        
+      
+      
+    
   `,
+  styles: [
+    `
+      .container {
+        max-width: 600px;
+        margin: 2rem auto;
+        padding: 2rem;
+        background: #f8f9fa;
+        border-radius: 8px;
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+      }
+      .form-group {
+        margin-bottom: 1rem;
+      }
+      label {
+        display: block;
+        margin-bottom: 0.5rem;
+        font-weight: 500;
+        color: #495057;
+      }
+      input {
+        width: 100%;
+        padding: 0.5rem;
+        border: 1px solid #ced4da;
+        border-radius: 4px;
+        font-size: 1rem;
+      }
+      .button-group {
+        margin-top: 1.5rem;
+        display: flex;
+        gap: 1rem;
+      }
+      button {
+        padding: 0.5rem 1rem;
+        border: none;
+        border-radius: 4px;
+        cursor: pointer;
+        font-weight: 500;
+        transition: opacity 0.2s;
+      }
+      button:hover {
+        opacity: 0.9;
+      }
+      .primary {
+        background: #0d6efd;
+        color: white;
+      }
+      .secondary {
+        background: #6c757d;
+        color: white;
+      }
+    `,
+  ],
 })
 export class AppComponent {
-  userName = new FormControl();
-  testId = new FormControl();
+  userName = new FormControl('');
+  testId = new FormControl(0);
 }
diff --git a/apps/angular/22-router-input/src/app/app.routes.ts b/apps/angular/22-router-input/src/app/app.routes.ts
index f5d3487c4..160d10e95 100644
--- a/apps/angular/22-router-input/src/app/app.routes.ts
+++ b/apps/angular/22-router-input/src/app/app.routes.ts
@@ -8,8 +8,8 @@ export const appRoutes: Route[] = [
   {
     path: 'subscription/:testId',
     loadComponent: () => import('./test.component'),
-    data: {
-      permission: 'admin',
+    resolve: {
+      permission: () => 'admin',
     },
   },
 ];
diff --git a/apps/angular/22-router-input/src/app/home.component.ts b/apps/angular/22-router-input/src/app/home.component.ts
index 0ddc1501d..293fda8ea 100644
--- a/apps/angular/22-router-input/src/app/home.component.ts
+++ b/apps/angular/22-router-input/src/app/home.component.ts
@@ -1,9 +1,235 @@
+import { CommonModule } from '@angular/common';
 import { Component } from '@angular/core';
+import { RouterLink } from '@angular/router';
+
 @Component({
   selector: 'app-home',
-  imports: [],
+  standalone: true,
+  imports: [RouterLink, CommonModule],
   template: `
-    
Home
+    
+      
+
+      
+        Quick Actions
+        
+          
+            
📊
+            
View Tests
+            
Access and manage your existing test subscriptions
+            
+          
+          
+            
🔍
+            
Track Progress
+            
Monitor your test completion and results
+            
+          
+          
+            
📅
+            
Schedule Tests
+            
Plan and organize upcoming test sessions
+            
+          
+        
 
+      
+
+      
+        Getting Started
+        
+          
+            
1
+            
+              
Create Account
+              
Set up your user profile to begin
+            
+          
+          
+            
2
+            
+              
Select Test
+              
Choose from available test options
+            
+          
+          
+            
3
+            
+              
Start Testing
+              
Begin your assessment journey
+            
+          
+        
 
+      
+    
   `,
+  styles: [
+    `
+      .home-container {
+        max-width: 1200px;
+        margin: 0 auto;
+        padding: 2rem;
+      }
+
+      .hero {
+        text-align: center;
+        padding: 3rem 0;
+        background: linear-gradient(to right, #f8f9fa, #e9ecef);
+        border-radius: 12px;
+        margin-bottom: 3rem;
+      }
+
+      .hero h1 {
+        font-size: 2.5rem;
+        color: #212529;
+        margin-bottom: 1rem;
+      }
+
+      .subtitle {
+        font-size: 1.25rem;
+        color: #6c757d;
+      }
+
+      .features {
+        margin-bottom: 3rem;
+      }
+
+      h2 {
+        font-size: 1.75rem;
+        color: #343a40;
+        margin-bottom: 1.5rem;
+      }
+
+      .cards-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+        gap: 1.5rem;
+        margin-bottom: 2rem;
+      }
+
+      .card {
+        background: white;
+        padding: 1.5rem;
+        border-radius: 8px;
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+        transition: transform 0.2s;
+        display: flex;
+        flex-direction: column;
+      }
+
+      .card:hover {
+        transform: translateY(-5px);
+      }
+
+      .card-icon {
+        font-size: 2rem;
+        margin-bottom: 1rem;
+      }
+
+      .card h3 {
+        font-size: 1.25rem;
+        color: #343a40;
+        margin-bottom: 0.5rem;
+      }
+
+      .card p {
+        color: #6c757d;
+        line-height: 1.5;
+        margin-bottom: 1rem;
+        flex-grow: 1;
+      }
+
+      .action-button {
+        background: #0d6efd;
+        color: white;
+        border: none;
+        padding: 0.5rem 1rem;
+        border-radius: 4px;
+        cursor: pointer;
+        font-weight: 500;
+        transition: background-color 0.2s;
+      }
+
+      .action-button:hover {
+        background: #0b5ed7;
+      }
+
+      .steps {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+        gap: 1.5rem;
+      }
+
+      .step {
+        display: flex;
+        align-items: center;
+        gap: 1rem;
+        padding: 1rem;
+        background: white;
+        border-radius: 8px;
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+      }
+
+      .step-number {
+        width: 40px;
+        height: 40px;
+        background: #0d6efd;
+        color: white;
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-weight: bold;
+        font-size: 1.25rem;
+      }
+
+      .step-content h3 {
+        font-size: 1.1rem;
+        color: #343a40;
+        margin-bottom: 0.25rem;
+      }
+
+      .step-content p {
+        color: #6c757d;
+        font-size: 0.9rem;
+      }
+
+      @media (max-width: 768px) {
+        .home-container {
+          padding: 1rem;
+        }
+
+        .hero {
+          padding: 2rem 1rem;
+        }
+
+        .hero h1 {
+          font-size: 2rem;
+        }
+
+        .cards-grid,
+        .steps {
+          grid-template-columns: 1fr;
+        }
+      }
+    `,
+  ],
 })
 export default class HomeComponent {}
diff --git a/apps/angular/22-router-input/src/app/test.component.ts b/apps/angular/22-router-input/src/app/test.component.ts
index 747ab4483..539a3c7e9 100644
--- a/apps/angular/22-router-input/src/app/test.component.ts
+++ b/apps/angular/22-router-input/src/app/test.component.ts
@@ -1,21 +1,77 @@
-import { AsyncPipe } from '@angular/common';
-import { Component, inject } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
-import { map } from 'rxjs';
 
 @Component({
   selector: 'app-subscription',
-  imports: [AsyncPipe],
+  standalone: true,
+  imports: [CommonModule],
   template: `
-    
TestId: {{ testId$ | async }}
-    
Permission: {{ permission$ | async }}
-    
User: {{ user$ | async }}
+    
+      
+        TestId:
+        {{ testId }}
+      
+      
+        Permission:
+        {{ permission }}
+      
+      
+        User:
+        {{ user }}
+      
+    
   `,
+  styles: [
+    `
+      .info-container {
+        margin-top: 2rem;
+        padding: 1.5rem;
+        background: white;
+        border-radius: 6px;
+        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+      }
+      .info-item {
+        display: flex;
+        align-items: center;
+        padding: 0.75rem 0;
+        border-bottom: 1px solid #e9ecef;
+      }
+      .info-item:last-child {
+        border-bottom: none;
+      }
+      .label {
+        font-weight: 600;
+        color: #495057;
+        width: 120px;
+      }
+      .value {
+        color: #212529;
+      }
+    `,
+  ],
 })
-export default class TestComponent {
-  private activatedRoute = inject(ActivatedRoute);
+export default class TestComponent implements OnInit {
+  testId: string = '';
+  permission: string = '';
+  user: string = '';
 
-  testId$ = this.activatedRoute.params.pipe(map((p) => p['testId']));
-  permission$ = this.activatedRoute.data.pipe(map((d) => d['permission']));
-  user$ = this.activatedRoute.queryParams.pipe(map((q) => q['user']));
+  constructor(private route: ActivatedRoute) {}
+
+  ngOnInit() {
+    // Get route parameters
+    this.route.params.subscribe((params) => {
+      this.testId = params['testId'];
+    });
+
+    // Get query parameters
+    this.route.queryParams.subscribe((queryParams) => {
+      this.user = queryParams['user'];
+    });
+
+    // Get resolved data
+    this.route.data.subscribe((data) => {
+      this.permission = data['permission'];
+    });
+  }
 }
diff --git a/apps/angular/3-directive-enhancement/src/app/app.component.ts b/apps/angular/3-directive-enhancement/src/app/app.component.ts
index 8d37369a1..9091d2992 100644
--- a/apps/angular/3-directive-enhancement/src/app/app.component.ts
+++ b/apps/angular/3-directive-enhancement/src/app/app.component.ts
@@ -1,24 +1,122 @@
-import { NgFor, NgIf } from '@angular/common';
 import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { EnhancedNgForDirective } from './directives/enhanced-ngfor.directive';
 
 interface Person {
   name: string;
 }
 
 @Component({
-  imports: [NgFor, NgIf],
   selector: 'app-root',
   template: `
-    
 0; else emptyList">
-      
-        {{ person.name }}
+    
+      
Person List
+
+      
+        
+          👤
+          {{ person.name }}
+        
+        
+          
+            
👥
+            
The list is empty !!
+          
 
+        
+      
+
+      
+        
+        
       
-    
-    
The list is empty !!
+    
   `,
-  styles: [],
+  styles: [
+    `
+      .container {
+        @apply mx-auto mt-8 max-w-2xl rounded-lg bg-white p-8 shadow-lg;
+      }
+
+      h1 {
+        @apply mb-6 text-2xl font-bold text-gray-800;
+      }
+
+      .list-container {
+        @apply min-h-[200px] rounded-lg bg-gray-50 p-4;
+      }
+
+      .person-item {
+        @apply mb-2 flex items-center gap-2 rounded-md bg-white p-4 shadow-sm;
+        @apply border border-gray-100;
+        @apply transition-all duration-200;
+        @apply hover:border-blue-200 hover:shadow-md;
+        span {
+          @apply text-xl;
+        }
+      }
+
+      .empty-state {
+        @apply flex h-[200px] flex-col items-center justify-center;
+        @apply text-gray-500;
+        span {
+          @apply mb-2 text-4xl;
+        }
+        p {
+          @apply text-lg;
+        }
+      }
+
+      .controls {
+        @apply mt-6 flex justify-between gap-4;
+      }
+
+      .btn {
+        @apply flex items-center gap-2 rounded-lg px-6 py-3 font-medium;
+        @apply transform transition-all duration-200;
+        @apply disabled:cursor-not-allowed disabled:opacity-50;
+        @apply hover:scale-105 active:scale-95;
+        @apply shadow-sm;
+
+        .icon {
+          @apply text-lg;
+        }
+      }
+
+      .add {
+        @apply bg-gradient-to-r from-blue-500 to-blue-600 text-white;
+        @apply hover:from-blue-600 hover:to-blue-700;
+        @apply focus:ring-2 focus:ring-blue-500 focus:ring-offset-2;
+      }
+
+      .clear {
+        @apply bg-gradient-to-r from-red-50 to-red-100 text-red-600;
+        @apply hover:from-red-100 hover:to-red-200;
+        @apply focus:ring-2 focus:ring-red-500 focus:ring-offset-2;
+      }
+    `,
+  ],
   changeDetection: ChangeDetectionStrategy.OnPush,
+  imports: [EnhancedNgForDirective],
+  standalone: true,
 })
 export class AppComponent {
   persons: Person[] = [];
+
+  addPerson() {
+    this.persons = [
+      ...this.persons,
+      { name: `Person ${this.persons.length + 1}` },
+    ];
+  }
+
+  clearPersons() {
+    this.persons = [];
+  }
 }
diff --git a/apps/angular/3-directive-enhancement/src/app/directives/enhanced-ngfor.directive.ts b/apps/angular/3-directive-enhancement/src/app/directives/enhanced-ngfor.directive.ts
new file mode 100644
index 000000000..a3d39c58d
--- /dev/null
+++ b/apps/angular/3-directive-enhancement/src/app/directives/enhanced-ngfor.directive.ts
@@ -0,0 +1,39 @@
+import { NgForOf } from '@angular/common';
+import {
+  Directive,
+  Input,
+  IterableDiffers,
+  TemplateRef,
+  ViewContainerRef,
+} from '@angular/core';
+
+@Directive({
+  selector: '[ngFor][ngForOf][ngForEmpty]',
+  standalone: true,
+})
+export class EnhancedNgForDirective
 extends NgForOf {
+  @Input('ngForEmpty') empty!: TemplateRef;
+  private vcRef: ViewContainerRef;
+
+  constructor(
+    viewContainer: ViewContainerRef,
+    template: TemplateRef,
+    differs: IterableDiffers,
+  ) {
+    super(viewContainer, template, differs);
+    this.vcRef = viewContainer;
+  }
+
+  override ngDoCheck(): void {
+    if (
+      this.ngForOf &&
+      Array.isArray(this.ngForOf) &&
+      this.ngForOf.length === 0
+    ) {
+      this.vcRef.clear();
+      this.vcRef.createEmbeddedView(this.empty);
+    } else {
+      super.ngDoCheck();
+    }
+  }
+}
diff --git a/apps/angular/31-module-to-standalone/src/app/app.component.ts b/apps/angular/31-module-to-standalone/src/app/app.component.ts
index 986df84b5..c4c7ecbfe 100644
--- a/apps/angular/31-module-to-standalone/src/app/app.component.ts
+++ b/apps/angular/31-module-to-standalone/src/app/app.component.ts
@@ -1,4 +1,5 @@
 import { Component } from '@angular/core';
+import { RouterLink, RouterOutlet } from '@angular/router';
 
 @Component({
   selector: 'app-root',
@@ -25,6 +26,7 @@ import { Component } from '@angular/core';
   host: {
     class: 'flex flex-col p-4 gap-3',
   },
-  standalone: false,
+  standalone: true,
+  imports: [RouterLink, RouterOutlet],
 })
 export class AppComponent {}
diff --git a/apps/angular/31-module-to-standalone/src/app/app.config.ts b/apps/angular/31-module-to-standalone/src/app/app.config.ts
new file mode 100644
index 000000000..0700d1c43
--- /dev/null
+++ b/apps/angular/31-module-to-standalone/src/app/app.config.ts
@@ -0,0 +1,7 @@
+import { ApplicationConfig } from '@angular/core';
+import { provideRouter } from '@angular/router';
+import { routes } from './app.routes';
+
+export const appConfig: ApplicationConfig = {
+  providers: [provideRouter(routes)],
+};
diff --git a/apps/angular/31-module-to-standalone/src/app/app.routes.ts b/apps/angular/31-module-to-standalone/src/app/app.routes.ts
new file mode 100644
index 000000000..0517bc539
--- /dev/null
+++ b/apps/angular/31-module-to-standalone/src/app/app.routes.ts
@@ -0,0 +1,35 @@
+import { IsAuthorizedGuard } from '@angular-challenges/module-to-standalone/admin/shared';
+import { Routes } from '@angular/router';
+
+export const routes: Routes = [
+  { path: '', redirectTo: 'home', pathMatch: 'full' },
+  {
+    path: 'home',
+    loadChildren: () =>
+      import('@angular-challenges/module-to-standalone/home').then(
+        (m) => m.ModuleToStandaloneHomeModule,
+      ),
+  },
+  {
+    path: 'admin',
+    canActivate: [IsAuthorizedGuard],
+    loadChildren: () =>
+      import('@angular-challenges/module-to-standalone/admin/feature').then(
+        (m) => m.AdminFeatureModule,
+      ),
+  },
+  {
+    path: 'user',
+    loadChildren: () =>
+      import('@angular-challenges/module-to-standalone/user/shell').then(
+        (m) => m.UserShellModule,
+      ),
+  },
+  {
+    path: 'forbidden',
+    loadChildren: () =>
+      import('@angular-challenges/module-to-standalone/forbidden').then(
+        (m) => m.ForbiddenModule,
+      ),
+  },
+];
diff --git a/apps/angular/31-module-to-standalone/src/main.ts b/apps/angular/31-module-to-standalone/src/main.ts
index 16de2365d..f3a7223da 100644
--- a/apps/angular/31-module-to-standalone/src/main.ts
+++ b/apps/angular/31-module-to-standalone/src/main.ts
@@ -1,6 +1,7 @@
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-import { AppModule } from './app/app.module';
+import { bootstrapApplication } from '@angular/platform-browser';
+import { AppComponent } from './app/app.component';
+import { appConfig } from './app/app.config';
 
-platformBrowserDynamic()
-  .bootstrapModule(AppModule)
-  .catch((err) => console.error(err));
+bootstrapApplication(AppComponent, appConfig).catch((err) =>
+  console.error(err),
+);
diff --git a/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts b/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts
index 3d5ce20f8..b88aa9455 100644
--- a/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts
+++ b/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts
@@ -10,6 +10,7 @@ interface MenuItem {
 
 @Component({
   selector: 'app-nav',
+  standalone: true,
   imports: [RouterLink, RouterLinkActive, NgFor],
   template: `
     
@@ -37,29 +38,20 @@ export class NavigationComponent {
 }
 
 @Component({
+  standalone: true,
   imports: [NavigationComponent, NgIf, AsyncPipe],
   template: `
     
-      
-        
-      
+      
     
-
-    
-      
-    
   `,
-  host: {},
 })
 export class MainNavigationComponent {
   private fakeBackend = inject(FakeServiceService);
-
   readonly info$ = this.fakeBackend.getInfoFromBackend();
 
-  getMenu(prop: string) {
-    return [
-      { path: '/foo', name: `Foo ${prop}` },
-      { path: '/bar', name: `Bar ${prop}` },
-    ];
-  }
+  readonly menus: MenuItem[] = [
+    { path: '/foo', name: 'Foo' },
+    { path: '/bar', name: 'Bar' },
+  ];
 }
diff --git a/apps/angular/39-injection-token/src/app/phone.component.ts b/apps/angular/39-injection-token/src/app/phone.component.ts
index 41ee3cfc0..6b63fe239 100644
--- a/apps/angular/39-injection-token/src/app/phone.component.ts
+++ b/apps/angular/39-injection-token/src/app/phone.component.ts
@@ -1,9 +1,11 @@
 import { Component } from '@angular/core';
 import { TimerContainerComponent } from './timer-container.component';
+import { getTimerProvider } from './timer-token';
 
 @Component({
   selector: 'app-phone',
   imports: [TimerContainerComponent],
+  providers: [getTimerProvider(2000)],
   template: `
     
       Phone Call Timer:
diff --git a/apps/angular/39-injection-token/src/app/timer-container.component.ts b/apps/angular/39-injection-token/src/app/timer-container.component.ts
index 67db6059a..817026377 100644
--- a/apps/angular/39-injection-token/src/app/timer-container.component.ts
+++ b/apps/angular/39-injection-token/src/app/timer-container.component.ts
@@ -1,6 +1,7 @@
-import { Component } from '@angular/core';
-import { DEFAULT_TIMER } from './data';
+import { Component, inject } from '@angular/core';
+import { TIMER_VALUE } from './timer-token';
 import { TimerComponent } from './timer.component';
+
 @Component({
   selector: 'timer-container',
   imports: [TimerComponent],
@@ -16,5 +17,5 @@ import { TimerComponent } from './timer.component';
   },
 })
 export class TimerContainerComponent {
-  timer = DEFAULT_TIMER;
+  timer = inject(TIMER_VALUE, { optional: true }) ?? 1000;
 }
diff --git a/apps/angular/39-injection-token/src/app/timer-token.ts b/apps/angular/39-injection-token/src/app/timer-token.ts
new file mode 100644
index 000000000..5cfffe6dc
--- /dev/null
+++ b/apps/angular/39-injection-token/src/app/timer-token.ts
@@ -0,0 +1,8 @@
+import { InjectionToken } from '@angular/core';
+
+export const TIMER_VALUE = new InjectionToken
('TIMER_VALUE');
+
+export const getTimerProvider = (value: number) => ({
+  provide: TIMER_VALUE,
+  useValue: value,
+});
diff --git a/apps/angular/39-injection-token/src/app/timer.component.ts b/apps/angular/39-injection-token/src/app/timer.component.ts
index 95707ec61..b529b3790 100644
--- a/apps/angular/39-injection-token/src/app/timer.component.ts
+++ b/apps/angular/39-injection-token/src/app/timer.component.ts
@@ -1,7 +1,7 @@
-import { Component } from '@angular/core';
+import { Component, inject } from '@angular/core';
 import { toSignal } from '@angular/core/rxjs-interop';
 import { interval } from 'rxjs';
-import { DEFAULT_TIMER } from './data';
+import { TIMER_VALUE } from './timer-token';
 
 @Component({
   selector: 'timer',
@@ -11,5 +11,6 @@ import { DEFAULT_TIMER } from './data';
   `,
 })
 export class TimerComponent {
-  timer = toSignal(interval(DEFAULT_TIMER));
+  private timerValue = inject(TIMER_VALUE, { optional: true }) ?? 1000;
+  timer = toSignal(interval(this.timerValue));
 }
diff --git a/apps/angular/39-injection-token/src/app/video.component.ts b/apps/angular/39-injection-token/src/app/video.component.ts
index ba0a218b4..7f44d9b36 100644
--- a/apps/angular/39-injection-token/src/app/video.component.ts
+++ b/apps/angular/39-injection-token/src/app/video.component.ts
@@ -1,9 +1,11 @@
 import { Component } from '@angular/core';
 import { TimerContainerComponent } from './timer-container.component';
+import { getTimerProvider } from './timer-token';
 
 @Component({
   selector: 'app-video',
   imports: [TimerContainerComponent],
+  providers: [getTimerProvider(1000)],
   template: `
     
       Video Call Timer:
diff --git a/apps/angular/4-typed-context-outlet/src/app/app.component.ts b/apps/angular/4-typed-context-outlet/src/app/app.component.ts
index 23be9dac6..303e9c16d 100644
--- a/apps/angular/4-typed-context-outlet/src/app/app.component.ts
+++ b/apps/angular/4-typed-context-outlet/src/app/app.component.ts
@@ -1,10 +1,19 @@
-import { NgTemplateOutlet } from '@angular/common';
 import { ChangeDetectionStrategy, Component } from '@angular/core';
 import { ListComponent } from './list.component';
 import { PersonComponent } from './person.component';
 
+interface Student {
+  name: string;
+  age: number;
+}
+
+interface City {
+  name: string;
+  country: string;
+}
+
 @Component({
-  imports: [NgTemplateOutlet, PersonComponent, ListComponent],
+  imports: [PersonComponent, ListComponent],
   selector: 'app-root',
   template: `
     
@@ -26,6 +35,7 @@ import { PersonComponent } from './person.component';
     
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
+  standalone: true,
 })
 export class AppComponent {
   person = {
@@ -33,12 +43,12 @@ export class AppComponent {
     age: 3,
   };
 
-  students = [
+  students: Student[] = [
     { name: 'toto', age: 3 },
     { name: 'titi', age: 4 },
   ];
 
-  cities = [
+  cities: City[] = [
     { name: 'Paris', country: 'France' },
     { name: 'Berlin', country: 'Germany' },
   ];
diff --git a/apps/angular/4-typed-context-outlet/src/app/list.component.ts b/apps/angular/4-typed-context-outlet/src/app/list.component.ts
index b9946e428..44e5d4667 100644
--- a/apps/angular/4-typed-context-outlet/src/app/list.component.ts
+++ b/apps/angular/4-typed-context-outlet/src/app/list.component.ts
@@ -7,6 +7,11 @@ import {
   TemplateRef,
 } from '@angular/core';
 
+interface ListContext {
+  $implicit: T;
+  index: number;
+}
+
 @Component({
   selector: 'list',
   imports: [CommonModule],
@@ -15,17 +20,25 @@ import {
       
      
 
     No Template
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
+  standalone: true,
 })
 export class ListComponent {
   @Input() list!: TItem[];
 
   @ContentChild('listRef', { read: TemplateRef })
-  listTemplateRef!: TemplateRef;
+  listTemplateRef!: TemplateRef>;
+
+  static ngTemplateContextGuard(
+    dir: ListComponent,
+    ctx: unknown,
+  ): ctx is ListContext {
+    return true;
+  }
 }
diff --git a/apps/angular/4-typed-context-outlet/src/app/person.component.ts b/apps/angular/4-typed-context-outlet/src/app/person.component.ts
index 59eb00ab1..1d3e82800 100644
--- a/apps/angular/4-typed-context-outlet/src/app/person.component.ts
+++ b/apps/angular/4-typed-context-outlet/src/app/person.component.ts
@@ -6,6 +6,11 @@ interface Person {
   age: number;
 }
 
+interface PersonContext {
+  $implicit: string; // for the name
+  age: number;
+}
+
 @Component({
   imports: [NgTemplateOutlet],
   selector: 'person',
@@ -18,10 +23,18 @@ interface Person {
 
     No Template
   `,
+  standalone: true,
 })
 export class PersonComponent {
   @Input() person!: Person;
 
-  @ContentChild('#personRef', { read: TemplateRef })
-  personTemplateRef!: TemplateRef;
+  @ContentChild('personRef', { read: TemplateRef })
+  personTemplateRef!: TemplateRef;
+
+  static ngTemplateContextGuard(
+    dir: PersonComponent,
+    ctx: unknown,
+  ): ctx is PersonContext {
+    return true;
+  }
 }
diff --git a/apps/angular/44-view-transition/src/app/app.config.ts b/apps/angular/44-view-transition/src/app/app.config.ts
index 4c128f040..59e0c71b0 100644
--- a/apps/angular/44-view-transition/src/app/app.config.ts
+++ b/apps/angular/44-view-transition/src/app/app.config.ts
@@ -1,5 +1,9 @@
 import { ApplicationConfig } from '@angular/core';
-import { provideRouter, withComponentInputBinding } from '@angular/router';
+import {
+  provideRouter,
+  withComponentInputBinding,
+  withViewTransitions,
+} from '@angular/router';
 
 export const appConfig: ApplicationConfig = {
   providers: [
@@ -12,6 +16,7 @@ export const appConfig: ApplicationConfig = {
         },
       ],
       withComponentInputBinding(),
+      withViewTransitions(), // Enable view transitions
     ),
   ],
 };
diff --git a/apps/angular/44-view-transition/src/app/blog/blog.component.ts b/apps/angular/44-view-transition/src/app/blog/blog.component.ts
index 29291d21e..01370b234 100644
--- a/apps/angular/44-view-transition/src/app/blog/blog.component.ts
+++ b/apps/angular/44-view-transition/src/app/blog/blog.component.ts
@@ -1,4 +1,11 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
+import {
+  AfterViewInit,
+  ChangeDetectionStrategy,
+  Component,
+  ElementRef,
+  QueryList,
+  ViewChildren,
+} from '@angular/core';
 import { posts } from '../data';
 import { ThumbnailComponent } from './thumbnail.component';
 
@@ -7,17 +14,42 @@ import { ThumbnailComponent } from './thumbnail.component';
   imports: [ThumbnailComponent],
   template: `
     
+      class="fixed left-0 right-0 top-0 z-50 flex h-20 items-center justify-center border-b-2 bg-white text-4xl shadow-md"
+      style="view-transition-name: page-header">
       Blog List
     
-    
+    
       @for (post of posts; track post.id) {
-        
+        
       }
     
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export default class BlogComponent {
+export default class BlogComponent implements AfterViewInit {
   posts = posts;
+  @ViewChildren('thumbnails') thumbnails!: QueryList
;
+
+  ngAfterViewInit() {
+    // Store thumbnail positions in sessionStorage
+    this.thumbnails.forEach((thumbnail) => {
+      const element = thumbnail.nativeElement;
+      const postId = element.getAttribute('data-post-id');
+      const rect = element.getBoundingClientRect();
+      sessionStorage.setItem(
+        `post-position-${postId}`,
+        JSON.stringify({
+          top: rect.top + window.scrollY,
+          left: rect.left,
+          width: rect.width,
+          height: rect.height,
+        }),
+      );
+    });
+  }
 }
diff --git a/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts b/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts
index dd2e25e26..68fef3c28 100644
--- a/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts
+++ b/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts
@@ -15,14 +15,21 @@ import { ThumbnailHeaderComponent } from './thumbnail-header.component';
         width="960"
         height="540"
         class="rounded-t-3xl"
+        [style]="{ 'view-transition-name': 'post-image-' + post().id }"
         [priority]="post().id === '1'" />
-      {{ post().title }}
+      
+        {{ post().title }}
+      
       {{ post().description }}
-      
+      
     
   `,
   host: {
-    class: 'w-full  max-w-[600px] rounded-3xl border-none shadow-lg',
+    class: 'w-full max-w-[600px] rounded-3xl border-none shadow-lg',
   },
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
diff --git a/apps/angular/44-view-transition/src/app/post/post.component.ts b/apps/angular/44-view-transition/src/app/post/post.component.ts
index edb87f780..a59b08bdb 100644
--- a/apps/angular/44-view-transition/src/app/post/post.component.ts
+++ b/apps/angular/44-view-transition/src/app/post/post.component.ts
@@ -1,33 +1,43 @@
 import { NgOptimizedImage } from '@angular/common';
 import {
+  AfterViewInit,
   ChangeDetectionStrategy,
   Component,
   computed,
+  ElementRef,
   input,
+  ViewChild,
 } from '@angular/core';
 import { RouterLink } from '@angular/router';
-import { ThumbnailHeaderComponent } from '../blog/thumbnail-header.component';
 import { fakeTextChapters, posts } from '../data';
 import { PostHeaderComponent } from './post-header.component';
 
 @Component({
   selector: 'post',
-  imports: [
-    ThumbnailHeaderComponent,
-    NgOptimizedImage,
-    PostHeaderComponent,
-    RouterLink,
-  ],
+  imports: [NgOptimizedImage, PostHeaderComponent, RouterLink],
   template: `
-    
+    
       
-      
![]()
-      
{{ post().title }}
-      
+      
![]()
+      
+        {{ post().title }}
+      
+      
       @for (chapter of fakeTextChapter; track $index) {
         
{{ chapter }}
       }
@@ -38,9 +48,48 @@ import { PostHeaderComponent } from './post-header.component';
   },
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export default class PostComponent {
+export default class PostComponent implements AfterViewInit {
   id = input.required
();
   post = computed(() => posts.filter((p) => p.id === this.id())[0]);
-
   fakeTextChapter = fakeTextChapters;
+
+  @ViewChild('postImage') postImage!: ElementRef;
+  @ViewChild('postContainer') postContainer!: ElementRef;
+
+  private previousPosition: any = null;
+
+  ngAfterViewInit() {
+    // Get the stored position
+    const storedPosition = sessionStorage.getItem(`post-position-${this.id()}`);
+    if (storedPosition) {
+      this.previousPosition = JSON.parse(storedPosition);
+      this.applyInitialPosition();
+    }
+
+    // Animate to final position
+    requestAnimationFrame(() => {
+      this.postContainer.nativeElement.style.transform = 'none';
+      this.postContainer.nativeElement.style.transition =
+        'transform 0.3s ease-out';
+    });
+  }
+
+  getImageTransitionStyle() {
+    return {
+      'view-transition-name': `post-image-${this.id()}`,
+      'transform-origin': 'top left',
+    };
+  }
+
+  private applyInitialPosition() {
+    if (this.previousPosition) {
+      const currentRect = this.postImage.nativeElement.getBoundingClientRect();
+      const scaleX = this.previousPosition.width / currentRect.width;
+      const scaleY = this.previousPosition.height / currentRect.height;
+      const translateX = this.previousPosition.left - currentRect.left;
+      const translateY = this.previousPosition.top - currentRect.top;
+
+      this.postContainer.nativeElement.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`;
+    }
+  }
 }
diff --git a/apps/angular/44-view-transition/src/styles.scss b/apps/angular/44-view-transition/src/styles.scss
index b5c61c956..54db592ed 100644
--- a/apps/angular/44-view-transition/src/styles.scss
+++ b/apps/angular/44-view-transition/src/styles.scss
@@ -1,3 +1,80 @@
 @tailwind base;
 @tailwind components;
 @tailwind utilities;
+
+/* Add this to your global styles.css */
+
+::view-transition-old(*),
+::view-transition-new(*) {
+  animation: none;
+  mix-blend-mode: normal;
+  transition: transform 0.3s ease-out;
+}
+
+/* Page header transition */
+.page-header {
+  view-transition-name: page-header;
+}
+
+::view-transition-old(page-header) {
+  animation: slide-out 0.3s ease-out;
+}
+
+::view-transition-new(page-header) {
+  animation: slide-in 0.3s ease-in;
+}
+
+/* Shared transitions */
+.shared-element {
+  transform-origin: top left;
+  will-change: transform;
+}
+
+@keyframes fade-out {
+  from {
+    opacity: 1;
+    transform: scale(1) translateY(0);
+  }
+  to {
+    opacity: 0;
+    transform: scale(0.95) translateY(20px);
+  }
+}
+
+@keyframes fade-in {
+  from {
+    opacity: 0;
+    transform: scale(1.05) translateY(-20px);
+  }
+  to {
+    opacity: 1;
+    transform: scale(1) translateY(0);
+  }
+}
+
+@keyframes slide-out {
+  from {
+    transform: translateX(0);
+    opacity: 1;
+  }
+  to {
+    transform: translateX(-100%);
+    opacity: 0;
+  }
+}
+
+@keyframes slide-in {
+  from {
+    transform: translateX(100%);
+    opacity: 0;
+  }
+  to {
+    transform: translateX(0);
+    opacity: 1;
+  }
+}
+
+/* Handle scrolling */
+html.no-scroll {
+  overflow: hidden;
+}
diff --git a/apps/angular/45-react-in-angular/src/app/app.component.ts b/apps/angular/45-react-in-angular/src/app/app.component.ts
index 87b9675cc..bc0c45366 100644
--- a/apps/angular/45-react-in-angular/src/app/app.component.ts
+++ b/apps/angular/45-react-in-angular/src/app/app.component.ts
@@ -1,61 +1,58 @@
-import { Component, signal } from '@angular/core';
-import { PostComponent } from './react/post.component';
+import { CommonModule } from '@angular/common';
+import { Component } from '@angular/core';
+import { ReactWrapperDirective } from './react-wrapper.directive';
 
-type Post = { title: string; description: string };
+interface Post {
+  id: number;
+  title: string;
+  content: string;
+  pictureLink: string;
+}
 
 @Component({
-  imports: [PostComponent],
   selector: 'app-root',
+  standalone: true,
+  imports: [ReactWrapperDirective, CommonModule],
   template: `
-    
-      
-        @for (post of posts; track post.title) {
-          
-        }
-      
-      
-        
Selected Post:
-        
-          {{ selectedPost()?.title ?? '-' }}
-        
+    
   `,
-  styles: [''],
 })
 export class AppComponent {
-  readonly posts = [
+  selectedId?: number;
+
+  posts: Post[] = [
     {
-      title: 'A Deep Dive into Angular',
-      description:
-        "Explore Angular's core features, its evolution, and best practices in development for creating dynamic, efficient web applications in our comprehensive guide.",
-      pictureLink:
-        'https://images.unsplash.com/photo-1471958680802-1345a694ba6d',
+      id: 1,
+      title: 'First Post',
+      content: 'This is the first post content',
+      pictureLink: '../assets/bird.jpg',
     },
     {
-      title: 'The Perfect Combination',
-      description:
-        'Unveil the power of combining Angular & React in web development, maximizing efficiency and flexibility for building scalable, sophisticated applications.',
-      pictureLink:
-        'https://images.unsplash.com/photo-1518717202715-9fa9d099f58a',
+      id: 2,
+      title: 'Second Post',
+      content: 'This is the second post content',
+      pictureLink: '../assets/bird.jpg',
     },
     {
-      title: 'Taking Angular to the Next Level',
-      description:
-        "Discover how integrating React with Angular elevates web development, blending Angular's structure with React's UI prowess for advanced applications.",
-      pictureLink:
-        'https://images.unsplash.com/photo-1532103050105-860af53bc6aa',
+      id: 3,
+      title: 'Third Post',
+      content: 'This is the third post content',
+      pictureLink: '../assets/bird.jpg',
     },
   ];
 
-  readonly selectedPost = signal
(null);
-
-  selectPost(post: Post) {
-    this.selectedPost.set(post);
+  selectPost(id: number) {
+    this.selectedId = id;
   }
 }
diff --git a/apps/angular/45-react-in-angular/src/app/react-wrapper.directive.ts b/apps/angular/45-react-in-angular/src/app/react-wrapper.directive.ts
new file mode 100644
index 000000000..a428aeaa4
--- /dev/null
+++ b/apps/angular/45-react-in-angular/src/app/react-wrapper.directive.ts
@@ -0,0 +1,42 @@
+import {
+  Directive,
+  ElementRef,
+  Input,
+  OnChanges,
+  OnDestroy,
+} from '@angular/core';
+import * as React from 'react';
+import { createRoot } from 'react-dom/client';
+import ReactPost from './react/ReactPost';
+
+@Directive({
+  selector: '[reactPost]',
+  standalone: true,
+})
+export class ReactWrapperDirective implements OnChanges, OnDestroy {
+  @Input() title = '';
+  @Input() content = '';
+  @Input() selected = false;
+  @Input() pictureLink = '';
+  private root = createRoot(this.el.nativeElement);
+
+  constructor(private el: ElementRef) {}
+
+  ngOnChanges() {
+    this.root.render(
+      React.createElement(ReactPost, {
+        title: this.title,
+        description: this.content,
+        selected: this.selected,
+        pictureLink: this.pictureLink,
+        handleClick: () => {
+          console.log('clicked');
+        },
+      }),
+    );
+  }
+
+  ngOnDestroy() {
+    this.root.unmount();
+  }
+}
diff --git a/apps/angular/45-react-in-angular/src/app/react/ReactPost.tsx b/apps/angular/45-react-in-angular/src/app/react/ReactPost.tsx
index 3f6b9e4cd..c5ab83beb 100644
--- a/apps/angular/45-react-in-angular/src/app/react/ReactPost.tsx
+++ b/apps/angular/45-react-in-angular/src/app/react/ReactPost.tsx
@@ -1,4 +1,4 @@
-// import React from 'react';
+import * as React from 'react';
 
 export default function ReactPost(props: {
   title?: string;
@@ -6,6 +6,7 @@ export default function ReactPost(props: {
   pictureLink?: string;
   selected?: boolean;
   handleClick: () => void;
+
 }) {
   return (
     
diff --git a/apps/angular/45-react-in-angular/src/assets/bird.jpg b/apps/angular/45-react-in-angular/src/assets/bird.jpg
new file mode 100644
index 000000000..392ea5eb6
Binary files /dev/null and b/apps/angular/45-react-in-angular/src/assets/bird.jpg differ
diff --git a/apps/angular/45-react-in-angular/tailwind.config.js b/apps/angular/45-react-in-angular/tailwind.config.js
index 38183db2c..d896cb505 100644
--- a/apps/angular/45-react-in-angular/tailwind.config.js
+++ b/apps/angular/45-react-in-angular/tailwind.config.js
@@ -1,11 +1,11 @@
-const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind');
-const { join } = require('path');
+const { createGlobPatternsForDependencies } = require( '@nx/angular/tailwind' );
+const { join } = require( 'path' );
 
 /** @type {import('tailwindcss').Config} */
 module.exports = {
   content: [
-    join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'),
-    ...createGlobPatternsForDependencies(__dirname),
+    join( __dirname, 'src/**/!(*.stories|*.spec).{ts,tsx,html}' ),
+    ...createGlobPatternsForDependencies( __dirname ),
   ],
   theme: {
     extend: {},
diff --git a/apps/angular/45-react-in-angular/tsconfig.json b/apps/angular/45-react-in-angular/tsconfig.json
index 25ca437b4..993512aac 100644
--- a/apps/angular/45-react-in-angular/tsconfig.json
+++ b/apps/angular/45-react-in-angular/tsconfig.json
@@ -1,8 +1,9 @@
 {
+  "extends": "../../../tsconfig.base.json",
   "compilerOptions": {
+    "jsx": "react",
     "target": "es2022",
     "useDefineForClassFields": false,
-    "esModuleInterop": true,
     "forceConsistentCasingInFileNames": true,
     "strict": true,
     "noImplicitOverride": true,
@@ -23,7 +24,6 @@
       "path": "./tsconfig.editor.json"
     }
   ],
-  "extends": "../../../tsconfig.base.json",
   "angularCompilerOptions": {
     "enableI18nLegacyMessageIdFormat": false,
     "strictInjectionParameters": true,
diff --git a/apps/angular/46-simple-animations/src/app/app.component.ts b/apps/angular/46-simple-animations/src/app/app.component.ts
index ae63db419..37211450d 100644
--- a/apps/angular/46-simple-animations/src/app/app.component.ts
+++ b/apps/angular/46-simple-animations/src/app/app.component.ts
@@ -1,86 +1,113 @@
+import {
+  animate,
+  query,
+  stagger,
+  style,
+  transition,
+  trigger,
+} from '@angular/animations';
+import { CommonModule } from '@angular/common';
 import { Component } from '@angular/core';
 
 @Component({
-  imports: [],
+  standalone: true,
+  imports: [CommonModule],
   selector: 'app-root',
-  styles: `
-    section {
-      @apply flex flex-1 flex-col gap-5;
-    }
-
-    .list-item {
-      @apply flex flex-row border-b px-5 pb-2;
-
-      span {
-        @apply flex-1;
-      }
-    }
-  `,
+  animations: [
+    trigger('fadeInParagraphs', [
+      transition(':enter', [
+        query('.timeline-item', [
+          style({ opacity: 0, transform: 'translateX(-100px)' }),
+          stagger(200, [
+            animate(
+              '600ms ease',
+              style({ opacity: 1, transform: 'translateX(0)' }),
+            ),
+          ]),
+        ]),
+      ]),
+    ]),
+    trigger('listAnimation', [
+      transition(':enter', [
+        query('.list-item', [
+          style({ opacity: 0, transform: 'translateX(-20px)' }),
+          stagger(100, [
+            animate(
+              '300ms ease',
+              style({ opacity: 1, transform: 'translateX(0)' }),
+            ),
+          ]),
+        ]),
+      ]),
+    ]),
+  ],
   template: `
-    
-      
-        
-          
2008
-          
-            Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae
-            mollitia sequi accusantium, distinctio similique laudantium eveniet
-            quidem sit placeat possimus tempore dolorum inventore corporis atque
-            quae ad, nobis explicabo delectus.
-          
-        
 
+    
+      
+        
+          
+            
2008
+            
+              Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae
+              mollitia sequi accusantium, distinctio similique laudantium
+              eveniet quidem sit placeat possimus tempore dolorum inventore
+              corporis atque quae ad, nobis explicabo delectus.
+            
+          
 
 
-        
-          
2010
-          
-            Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae
-            mollitia sequi accusantium, distinctio similique laudantium eveniet
-            quidem sit placeat possimus tempore dolorum inventore corporis atque
-            quae ad, nobis explicabo delectus.
-          
-        
 
+          
+            
2010
+            
+              Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae
+              mollitia sequi accusantium, distinctio similique laudantium
+              eveniet quidem sit placeat possimus tempore dolorum inventore
+              corporis atque quae ad, nobis explicabo delectus.
+            
+          
 
 
-        
-          
2012
-          
-            Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae
-            mollitia sequi accusantium, distinctio similique laudantium eveniet
-            quidem sit placeat possimus tempore dolorum inventore corporis atque
-            quae ad, nobis explicabo delectus.
-          
-        
 
-      
+          
+            
2012
+            
+              Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae
+              mollitia sequi accusantium, distinctio similique laudantium
+              eveniet quidem sit placeat possimus tempore dolorum inventore
+              corporis atque quae ad, nobis explicabo delectus.
+            
+          
+        
 
-      
-        
-          Name:
-          Samuel
-        
+        
+          
+            Name:
+            Samuel
+          
 
-        
-          Age:
-          28
-        
+          
+            Age:
+            28
+          
 
-        
-          Birthdate:
-          02.11.1995
-        
+          
+            Birthdate:
+            02.11.1995
+          
 
-        
-          City:
-          Berlin
-        
+          
+            City:
+            Berlin
+          
 
-        
-          Language:
-          English
-        
+          
+            Language:
+            English
+          
 
-        
-          Like Pizza:
-          Hell yeah
-        
-      
+          
+            Like Pizza:
+            Hell yeah
+          
+        
+      
 
      
   `,
 })
diff --git a/apps/angular/46-simple-animations/src/app/app.config.ts b/apps/angular/46-simple-animations/src/app/app.config.ts
index 81a6edde4..59198e627 100644
--- a/apps/angular/46-simple-animations/src/app/app.config.ts
+++ b/apps/angular/46-simple-animations/src/app/app.config.ts
@@ -1,5 +1,6 @@
 import { ApplicationConfig } from '@angular/core';
+import { provideAnimations } from '@angular/platform-browser/animations';
 
 export const appConfig: ApplicationConfig = {
-  providers: [],
+  providers: [provideAnimations()],
 };
diff --git a/apps/angular/46-simple-animations/src/styles.scss b/apps/angular/46-simple-animations/src/styles.scss
index 77e408aa8..86e55892e 100644
--- a/apps/angular/46-simple-animations/src/styles.scss
+++ b/apps/angular/46-simple-animations/src/styles.scss
@@ -3,3 +3,44 @@
 @tailwind utilities;
 
 /* You can add global styles to this file, and also import other style files */
+
+section {
+  @apply flex flex-1 flex-col gap-5;
+}
+
+.list-item {
+  @apply flex flex-row border-b px-5 pb-2;
+
+  span {
+    @apply flex-1;
+  }
+}
+
+.timeline-item {
+  @apply mb-8;
+
+  h3,
+  h4 {
+    @apply mb-2 text-xl font-bold;
+  }
+
+  p {
+    @apply leading-relaxed text-gray-600;
+  }
+}
+
+.container-wrapper {
+  @apply p-4 md:mx-20 md:my-40;
+}
+
+.content-container {
+  @apply flex flex-col gap-8 md:flex-row md:gap-12;
+}
+
+.timeline-section {
+  @apply w-full md:w-2/3;
+}
+
+.info-section {
+  @apply w-full md:w-1/3;
+}
diff --git a/apps/angular/5-crud-application/src/app/app.component.ts b/apps/angular/5-crud-application/src/app/app.component.ts
index 9152ff5e4..8cef4deae 100644
--- a/apps/angular/5-crud-application/src/app/app.component.ts
+++ b/apps/angular/5-crud-application/src/app/app.component.ts
@@ -1,50 +1,56 @@
 import { CommonModule } from '@angular/common';
-import { HttpClient } from '@angular/common/http';
 import { Component, OnInit } from '@angular/core';
-import { randText } from '@ngneat/falso';
+import { MatProgressSpinner } from '@angular/material/progress-spinner';
+import { TodoItemComponent } from './components/todo-item.component';
+import { TodoService } from './services/todo.service';
 
 @Component({
-  imports: [CommonModule],
   selector: 'app-root',
+  standalone: true,
+  imports: [CommonModule, TodoItemComponent, MatProgressSpinner],
   template: `
-    
-      {{ todo.title }}
-      
+    
+      
Todo List
+
+      @if (todoService.loading()) {
+        
+      }
+
+      @if (todoService.error()) {
+        
+          {{ todoService.error() }}
+        
+      }
+
+      @for (todo of todoService.todos(); track todo.id) {
+        
+      }
     
   `,
-  styles: [],
+  styles: [
+    `
+      .container {
+        max-width: 800px;
+        margin: 2rem auto;
+        padding: 0 1rem;
+      }
+      .loader {
+        margin: 2rem auto;
+      }
+      .error {
+        color: #ff4444;
+        padding: 1rem;
+        border: 1px solid #ff4444;
+        border-radius: 4px;
+        margin: 1rem 0;
+      }
+    `,
+  ],
 })
 export class AppComponent implements OnInit {
-  todos!: any[];
-
-  constructor(private http: HttpClient) {}
+  constructor(public todoService: TodoService) {}
 
   ngOnInit(): void {
-    this.http
-      .get
('https://jsonplaceholder.typicode.com/todos')
-      .subscribe((todos) => {
-        this.todos = todos;
-      });
-  }
-
-  update(todo: any) {
-    this.http
-      .put(
-        `https://jsonplaceholder.typicode.com/todos/${todo.id}`,
-        JSON.stringify({
-          todo: todo.id,
-          title: randText(),
-          body: todo.body,
-          userId: todo.userId,
-        }),
-        {
-          headers: {
-            'Content-type': 'application/json; charset=UTF-8',
-          },
-        },
-      )
-      .subscribe((todoUpdated: any) => {
-        this.todos[todoUpdated.id - 1] = todoUpdated;
-      });
+    this.todoService.getTodos().subscribe();
   }
 }
diff --git a/apps/angular/5-crud-application/src/app/components/todo-item.component.spec.ts b/apps/angular/5-crud-application/src/app/components/todo-item.component.spec.ts
new file mode 100644
index 000000000..76ce176ad
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/components/todo-item.component.spec.ts
@@ -0,0 +1,71 @@
+import {
+  ComponentFixture,
+  TestBed,
+  fakeAsync,
+  tick,
+} from '@angular/core/testing';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { of } from 'rxjs';
+import { TodoService } from '../services/todo.service';
+import { TodoItemComponent } from './todo-item.component';
+
+describe('TodoItemComponent', () => {
+  let component: TodoItemComponent;
+  let fixture: ComponentFixture;
+  let todoService: jest.Mocked;
+
+  const mockTodo = {
+    id: 1,
+    title: 'Test Todo',
+    completed: false,
+    userId: 1,
+  };
+
+  beforeEach(async () => {
+    const spy = {
+      updateTodo: jest.fn().mockReturnValue(of(mockTodo)),
+      deleteTodo: jest.fn().mockReturnValue(of(void 0)),
+      todos: jest.fn(),
+      loading: jest.fn(),
+      error: jest.fn(),
+      getTodos: jest.fn(),
+    } as unknown as jest.Mocked;
+
+    await TestBed.configureTestingModule({
+      imports: [TodoItemComponent, MatProgressSpinnerModule],
+      providers: [{ provide: TodoService, useValue: spy }],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(TodoItemComponent);
+    component = fixture.componentInstance;
+    component.todo = mockTodo;
+    todoService = TestBed.inject(TodoService) as jest.Mocked;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should show processing state during update', fakeAsync(() => {
+    component.updateTodo();
+    expect(component.processing).toBe(true);
+    tick();
+    expect(component.processing).toBe(false);
+  }));
+
+  it('should show processing state during delete', fakeAsync(() => {
+    component.deleteTodo();
+    expect(component.processing).toBe(true);
+    tick();
+    expect(component.processing).toBe(false);
+  }));
+
+  it('should call service methods', () => {
+    component.updateTodo();
+    expect(todoService.updateTodo).toHaveBeenCalled();
+
+    component.deleteTodo();
+    expect(todoService.deleteTodo).toHaveBeenCalled();
+  });
+});
diff --git a/apps/angular/5-crud-application/src/app/components/todo-item.component.ts b/apps/angular/5-crud-application/src/app/components/todo-item.component.ts
new file mode 100644
index 000000000..b1cbaa772
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/components/todo-item.component.ts
@@ -0,0 +1,78 @@
+import { CommonModule } from '@angular/common';
+import { Component, Input } from '@angular/core';
+import { MatProgressSpinner } from '@angular/material/progress-spinner';
+import { randText } from '@ngneat/falso';
+import { Todo } from '../interfaces/todo.interface';
+import { TodoService } from '../services/todo.service';
+
+@Component({
+  selector: 'app-todo-item',
+  standalone: true,
+  imports: [CommonModule, MatProgressSpinner],
+  template: `
+    
+      
{{ todo.title }}
+      
+        
+        
+        
+      
+    
 
+  `,
+  styles: [
+    `
+      .todo-item {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 1rem;
+        border-bottom: 1px solid #eee;
+      }
+      .completed {
+        text-decoration: line-through;
+        color: #888;
+      }
+      .processing {
+        opacity: 0.7;
+      }
+      .actions {
+        display: flex;
+        gap: 0.5rem;
+        align-items: center;
+      }
+      .delete {
+        background: #ff4444;
+        color: white;
+      }
+    `,
+  ],
+})
+export class TodoItemComponent {
+  @Input({ required: true }) todo!: Todo;
+  processing = false;
+
+  constructor(private todoService: TodoService) {}
+
+  updateTodo() {
+    this.processing = true;
+    this.todoService
+      .updateTodo(this.todo.id, {
+        title: randText(),
+        completed: this.todo.completed,
+      })
+      .subscribe({
+        next: () => (this.processing = false),
+        error: () => (this.processing = false),
+      });
+  }
+
+  deleteTodo() {
+    this.processing = true;
+    this.todoService.deleteTodo(this.todo.id).subscribe({
+      next: () => (this.processing = false),
+      error: () => (this.processing = false),
+    });
+  }
+}
diff --git a/apps/angular/5-crud-application/src/app/interfaces/todo.interface.ts b/apps/angular/5-crud-application/src/app/interfaces/todo.interface.ts
new file mode 100644
index 000000000..cf598b68c
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/interfaces/todo.interface.ts
@@ -0,0 +1,11 @@
+export interface Todo {
+  id: number;
+  title: string;
+  completed: boolean;
+  userId: number;
+}
+
+export interface TodoUpdate {
+  title: string;
+  completed: boolean;
+}
diff --git a/apps/angular/5-crud-application/src/app/services/todo.service.spec.ts b/apps/angular/5-crud-application/src/app/services/todo.service.spec.ts
new file mode 100644
index 000000000..4ec1140c8
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/services/todo.service.spec.ts
@@ -0,0 +1,72 @@
+import {
+  HttpClientTestingModule,
+  HttpTestingController,
+} from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { Todo } from '../interfaces/todo.interface';
+import { TodoService } from './todo.service';
+
+describe('TodoService', () => {
+  let service: TodoService;
+  let httpMock: HttpTestingController;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [HttpClientTestingModule],
+      providers: [TodoService],
+    });
+    service = TestBed.inject(TodoService);
+    httpMock = TestBed.inject(HttpTestingController);
+  });
+
+  afterEach(() => {
+    httpMock.verify();
+  });
+
+  const mockTodo: Todo = {
+    id: 1,
+    title: 'Test Todo',
+    completed: false,
+    userId: 1,
+  };
+
+  it('should fetch todos', () => {
+    const mockTodos = [mockTodo];
+
+    service.getTodos().subscribe((todos) => {
+      expect(todos).toEqual(mockTodos);
+      expect(service.todos()).toEqual(mockTodos);
+    });
+
+    const req = httpMock.expectOne(
+      'https://jsonplaceholder.typicode.com/todos',
+    );
+    expect(req.request.method).toBe('GET');
+    req.flush(mockTodos);
+  });
+
+  it('should update todo', () => {
+    const update = { title: 'Updated Todo', completed: true };
+    const updatedTodo = { ...mockTodo, ...update };
+
+    service.updateTodo(1, update).subscribe((todo) => {
+      expect(todo).toEqual(updatedTodo);
+    });
+
+    const req = httpMock.expectOne(
+      'https://jsonplaceholder.typicode.com/todos/1',
+    );
+    expect(req.request.method).toBe('PUT');
+    req.flush(updatedTodo);
+  });
+
+  it('should delete todo', () => {
+    service.deleteTodo(1).subscribe();
+
+    const req = httpMock.expectOne(
+      'https://jsonplaceholder.typicode.com/todos/1',
+    );
+    expect(req.request.method).toBe('DELETE');
+    req.flush({});
+  });
+});
diff --git a/apps/angular/5-crud-application/src/app/services/todo.service.ts b/apps/angular/5-crud-application/src/app/services/todo.service.ts
new file mode 100644
index 000000000..4b0cf08b9
--- /dev/null
+++ b/apps/angular/5-crud-application/src/app/services/todo.service.ts
@@ -0,0 +1,66 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable, signal } from '@angular/core';
+import { Observable, throwError } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
+import { Todo, TodoUpdate } from '../interfaces/todo.interface';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class TodoService {
+  private apiUrl = 'https://jsonplaceholder.typicode.com/todos';
+  todos = signal([]);
+  loading = signal(false);
+  error = signal(null);
+
+  constructor(private http: HttpClient) {}
+
+  getTodos(): Observable {
+    this.loading.set(true);
+    return this.http.get(this.apiUrl).pipe(
+      map((todos) => {
+        this.todos.set(todos);
+        this.loading.set(false);
+        return todos;
+      }),
+      catchError((error) => {
+        this.error.set('Failed to load todos');
+        this.loading.set(false);
+        return throwError(() => error);
+      }),
+    );
+  }
+
+  updateTodo(id: number, update: TodoUpdate): Observable {
+    return this.http
+      .put(`${this.apiUrl}/${id}`, update, {
+        headers: {
+          'Content-type': 'application/json; charset=UTF-8',
+        },
+      })
+      .pipe(
+        map((updatedTodo) => {
+          this.todos.update((todos) =>
+            todos.map((todo) => (todo.id === id ? updatedTodo : todo)),
+          );
+          return updatedTodo;
+        }),
+        catchError((error) => {
+          this.error.set(`Failed to update todo #${id}`);
+          return throwError(() => error);
+        }),
+      );
+  }
+
+  deleteTodo(id: number): Observable {
+    return this.http.delete(`${this.apiUrl}/${id}`).pipe(
+      map(() => {
+        this.todos.update((todos) => todos.filter((todo) => todo.id !== id));
+      }),
+      catchError((error) => {
+        this.error.set(`Failed to delete todo #${id}`);
+        return throwError(() => error);
+      }),
+    );
+  }
+}
diff --git a/apps/angular/52-lazy-load-component/src/app/app.component.ts b/apps/angular/52-lazy-load-component/src/app/app.component.ts
index 6d8c03d29..5ffffd1a0 100644
--- a/apps/angular/52-lazy-load-component/src/app/app.component.ts
+++ b/apps/angular/52-lazy-load-component/src/app/app.component.ts
@@ -1,23 +1,23 @@
-import { Component, signal } from '@angular/core';
-
+import { Component } from '@angular/core';
+import { PlaceholderComponent } from './placeholder.component';
+import { TopComponent } from './top.component';
 @Component({
   selector: 'app-root',
   template: `
     
-      @if (topLoaded()) {
+      @defer (on interaction(loadBtn)) {
         
-      } @else {
+      } @placeholder {
         
         
       }
     
 
   `,
-  standalone: false,
+  standalone: true,
+  imports: [PlaceholderComponent, TopComponent],
 })
-export class AppComponent {
-  topLoaded = signal(false);
-}
+export class AppComponent {}
diff --git a/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts b/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts
index cbb2b5fa6..3d1e31043 100644
--- a/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts
+++ b/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts
@@ -13,6 +13,6 @@ import { Component } from '@angular/core';
       height: 50%;
     }
   `,
-  standalone: false,
+  standalone: true,
 })
 export class PlaceholderComponent {}
diff --git a/apps/angular/52-lazy-load-component/src/app/top.component.ts b/apps/angular/52-lazy-load-component/src/app/top.component.ts
index e1ca9012c..ff2d80d4d 100644
--- a/apps/angular/52-lazy-load-component/src/app/top.component.ts
+++ b/apps/angular/52-lazy-load-component/src/app/top.component.ts
@@ -13,6 +13,6 @@ import { Component } from '@angular/core';
       height: 50%;
     }
   `,
-  standalone: false,
+  standalone: true,
 })
 export class TopComponent {}
diff --git a/apps/angular/52-lazy-load-component/src/main.ts b/apps/angular/52-lazy-load-component/src/main.ts
index 16de2365d..31c5da482 100644
--- a/apps/angular/52-lazy-load-component/src/main.ts
+++ b/apps/angular/52-lazy-load-component/src/main.ts
@@ -1,6 +1,4 @@
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-import { AppModule } from './app/app.module';
+import { bootstrapApplication } from '@angular/platform-browser';
+import { AppComponent } from './app/app.component';
 
-platformBrowserDynamic()
-  .bootstrapModule(AppModule)
-  .catch((err) => console.error(err));
+bootstrapApplication(AppComponent).catch((err) => console.error(err));
diff --git a/apps/angular/55-back-button-navigation/src/app/app.component.ts b/apps/angular/55-back-button-navigation/src/app/app.component.ts
index baffdae25..44b12a9e1 100644
--- a/apps/angular/55-back-button-navigation/src/app/app.component.ts
+++ b/apps/angular/55-back-button-navigation/src/app/app.component.ts
@@ -1,9 +1,10 @@
 import { Component } from '@angular/core';
-import { RouterLink, RouterOutlet } from '@angular/router';
+import { RouterOutlet } from '@angular/router';
 
 @Component({
-  imports: [RouterOutlet, RouterLink],
+  imports: [RouterOutlet],
   selector: 'app-root',
   templateUrl: './app.component.html',
+  standalone: true,
 })
 export class AppComponent {}
diff --git a/apps/angular/55-back-button-navigation/src/app/app.routes.ts b/apps/angular/55-back-button-navigation/src/app/app.routes.ts
index 7deecd57a..5e492e7d6 100644
--- a/apps/angular/55-back-button-navigation/src/app/app.routes.ts
+++ b/apps/angular/55-back-button-navigation/src/app/app.routes.ts
@@ -1,4 +1,5 @@
 import { Routes } from '@angular/router';
+import { DialogGuard } from './dialog/dialog.guard';
 import { HomeComponent } from './home/home.component';
 import { SensitiveActionComponent } from './sensitive-action/sensitive-action.component';
 import { SimpleActionComponent } from './simple-action/simple-action.component';
@@ -16,9 +17,11 @@ export const APP_ROUTES: Routes = [
   {
     path: 'simple-action',
     component: SimpleActionComponent,
+    canDeactivate: [DialogGuard],
   },
   {
     path: 'sensitive-action',
     component: SensitiveActionComponent,
+    canDeactivate: [DialogGuard],
   },
 ];
diff --git a/apps/angular/55-back-button-navigation/src/app/dialog/confirm-dialog.component.ts b/apps/angular/55-back-button-navigation/src/app/dialog/confirm-dialog.component.ts
new file mode 100644
index 000000000..247ccf0f3
--- /dev/null
+++ b/apps/angular/55-back-button-navigation/src/app/dialog/confirm-dialog.component.ts
@@ -0,0 +1,27 @@
+import { Component, Inject } from '@angular/core';
+import { MatButtonModule } from '@angular/material/button';
+import {
+  MAT_DIALOG_DATA,
+  MatDialogModule,
+  MatDialogRef,
+} from '@angular/material/dialog';
+
+@Component({
+  selector: 'app-confirm-dialog',
+  standalone: true,
+  imports: [MatDialogModule, MatButtonModule],
+  template: `
+    Confirm Navigation
+    {{ data.message }}
+    
+      
+      
+    
+  `,
+})
+export class ConfirmDialogComponent {
+  constructor(
+    public dialogRef: MatDialogRef,
+    @Inject(MAT_DIALOG_DATA) public data: { message: string },
+  ) {}
+}
diff --git a/apps/angular/55-back-button-navigation/src/app/dialog/dialog-strategy.interface.ts b/apps/angular/55-back-button-navigation/src/app/dialog/dialog-strategy.interface.ts
new file mode 100644
index 000000000..b793a992b
--- /dev/null
+++ b/apps/angular/55-back-button-navigation/src/app/dialog/dialog-strategy.interface.ts
@@ -0,0 +1,11 @@
+import { InjectionToken } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+import { Observable } from 'rxjs';
+
+export interface DialogBackButtonStrategy {
+  handleBackButton(dialogRef: MatDialogRef): Observable;
+}
+
+export const DIALOG_STRATEGY = new InjectionToken(
+  'DIALOG_STRATEGY',
+);
diff --git a/apps/angular/55-back-button-navigation/src/app/dialog/dialog.guard.ts b/apps/angular/55-back-button-navigation/src/app/dialog/dialog.guard.ts
new file mode 100644
index 000000000..f6b44b359
--- /dev/null
+++ b/apps/angular/55-back-button-navigation/src/app/dialog/dialog.guard.ts
@@ -0,0 +1,22 @@
+import { Injectable, inject } from '@angular/core';
+import { MatDialog } from '@angular/material/dialog';
+import { CanDeactivate } from '@angular/router';
+import { Observable, of } from 'rxjs';
+import { DIALOG_STRATEGY } from './dialog-strategy.interface';
+
+@Injectable({ providedIn: 'root' })
+export class DialogGuard implements CanDeactivate {
+  private dialog = inject(MatDialog);
+
+  canDeactivate(component: unknown): Observable {
+    const dialogRef =
+      this.dialog.openDialogs[this.dialog.openDialogs.length - 1];
+    const strategy = inject(DIALOG_STRATEGY, { optional: true });
+
+    if (dialogRef && strategy) {
+      return strategy.handleBackButton(dialogRef);
+    }
+
+    return of(true);
+  }
+}
diff --git a/apps/angular/55-back-button-navigation/src/app/dialog/strategies/sensitive-dialog.strategy.ts b/apps/angular/55-back-button-navigation/src/app/dialog/strategies/sensitive-dialog.strategy.ts
new file mode 100644
index 000000000..9be48a65a
--- /dev/null
+++ b/apps/angular/55-back-button-navigation/src/app/dialog/strategies/sensitive-dialog.strategy.ts
@@ -0,0 +1,28 @@
+import { Injectable, inject } from '@angular/core';
+import { MatDialog, MatDialogRef } from '@angular/material/dialog';
+import { Observable, from } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { ConfirmDialogComponent } from '../confirm-dialog.component';
+import { DialogBackButtonStrategy } from '../dialog-strategy.interface';
+
+@Injectable({ providedIn: 'root' })
+export class SensitiveDialogStrategy implements DialogBackButtonStrategy {
+  private dialog = inject(MatDialog);
+
+  handleBackButton(dialogRef: MatDialogRef): Observable {
+    const confirmRef = this.dialog.open(ConfirmDialogComponent, {
+      width: '300px',
+      data: { message: 'Are you sure you want to leave?' },
+    });
+
+    return from(confirmRef.afterClosed()).pipe(
+      map((result) => {
+        if (result) {
+          dialogRef.close();
+          return false;
+        }
+        return false;
+      }),
+    );
+  }
+}
diff --git a/apps/angular/55-back-button-navigation/src/app/dialog/strategies/simple-dialog.strategy.ts b/apps/angular/55-back-button-navigation/src/app/dialog/strategies/simple-dialog.strategy.ts
new file mode 100644
index 000000000..c8b53fbaa
--- /dev/null
+++ b/apps/angular/55-back-button-navigation/src/app/dialog/strategies/simple-dialog.strategy.ts
@@ -0,0 +1,12 @@
+import { Injectable } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+import { Observable, of } from 'rxjs';
+import { tap } from 'rxjs/operators';
+import { DialogBackButtonStrategy } from '../dialog-strategy.interface';
+
+@Injectable({ providedIn: 'root' })
+export class SimpleDialogStrategy implements DialogBackButtonStrategy {
+  handleBackButton(dialogRef: MatDialogRef): Observable {
+    return of(false).pipe(tap(() => dialogRef.close()));
+  }
+}
diff --git a/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts b/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts
index fe97e7368..7dac7b74e 100644
--- a/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts
+++ b/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts
@@ -1,12 +1,15 @@
 import { Component, inject } from '@angular/core';
 import { MatButtonModule } from '@angular/material/button';
 import { MatDialog } from '@angular/material/dialog';
+import { DIALOG_STRATEGY } from '../dialog/dialog-strategy.interface';
 import { DialogComponent } from '../dialog/dialog.component';
+import { SimpleDialogStrategy } from '../dialog/strategies/simple-dialog.strategy';
 
 @Component({
   imports: [MatButtonModule],
   selector: 'app-simple-action',
   templateUrl: './simple-action.component.html',
+  providers: [{ provide: DIALOG_STRATEGY, useClass: SimpleDialogStrategy }],
 })
 export class SimpleActionComponent {
   readonly #dialog = inject(MatDialog);
diff --git a/apps/angular/6-structural-directive/src/app/directives/has-role-super-admin.directive.ts b/apps/angular/6-structural-directive/src/app/directives/has-role-super-admin.directive.ts
new file mode 100644
index 000000000..a372e51fa
--- /dev/null
+++ b/apps/angular/6-structural-directive/src/app/directives/has-role-super-admin.directive.ts
@@ -0,0 +1,40 @@
+import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
+import { distinctUntilChanged, map } from 'rxjs/operators';
+import { UserStore } from '../user.store';
+
+@Directive({
+  selector: '[hasRoleSuperAdmin]',
+  standalone: true,
+})
+export class HasRoleSuperAdminDirective {
+  private hasView = false;
+
+  constructor(
+    private templateRef: TemplateRef,
+    private vcr: ViewContainerRef,
+    private userStore: UserStore,
+  ) {}
+
+  @Input() set hasRoleSuperAdmin(value: boolean) {
+    if (value) {
+      this.updateView();
+    }
+  }
+
+  private updateView() {
+    this.userStore.user$
+      .pipe(
+        map((user) => user?.isAdmin ?? false),
+        distinctUntilChanged(),
+      )
+      .subscribe((isAdmin) => {
+        if (isAdmin && !this.hasView) {
+          this.vcr.createEmbeddedView(this.templateRef);
+          this.hasView = true;
+        } else if (!isAdmin && this.hasView) {
+          this.vcr.clear();
+          this.hasView = false;
+        }
+      });
+  }
+}
diff --git a/apps/angular/6-structural-directive/src/app/directives/has-role.directive.ts b/apps/angular/6-structural-directive/src/app/directives/has-role.directive.ts
new file mode 100644
index 000000000..5071945c1
--- /dev/null
+++ b/apps/angular/6-structural-directive/src/app/directives/has-role.directive.ts
@@ -0,0 +1,47 @@
+import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
+import { distinctUntilChanged, map } from 'rxjs/operators';
+import { Role, User } from '../user.model';
+import { UserStore } from '../user.store';
+
+@Directive({
+  selector: '[hasRole]',
+  standalone: true,
+})
+export class HasRoleDirective {
+  private roles: Role[] = [];
+  private hasView = false;
+
+  constructor(
+    private templateRef: TemplateRef,
+    private vcr: ViewContainerRef,
+    private userStore: UserStore,
+  ) {}
+
+  @Input() set hasRole(roleOrRoles: Role | Role[]) {
+    this.roles = Array.isArray(roleOrRoles) ? roleOrRoles : [roleOrRoles];
+    this.updateView();
+  }
+
+  private updateView() {
+    this.userStore.user$
+      .pipe(
+        map((user) => this.matchRoles(user)),
+        distinctUntilChanged(),
+      )
+      .subscribe((hasRole) => {
+        if (hasRole && !this.hasView) {
+          this.vcr.createEmbeddedView(this.templateRef);
+          this.hasView = true;
+        } else if (!hasRole && this.hasView) {
+          this.vcr.clear();
+          this.hasView = false;
+        }
+      });
+  }
+
+  private matchRoles(user?: User): boolean {
+    if (!user) return false;
+    if (user.isAdmin) return true;
+    return this.roles.some((role) => user.roles.includes(role));
+  }
+}
diff --git a/apps/angular/6-structural-directive/src/app/guards/role.guard.ts b/apps/angular/6-structural-directive/src/app/guards/role.guard.ts
new file mode 100644
index 000000000..ab056e078
--- /dev/null
+++ b/apps/angular/6-structural-directive/src/app/guards/role.guard.ts
@@ -0,0 +1,19 @@
+import { inject } from '@angular/core';
+import { CanMatchFn } from '@angular/router';
+import { map } from 'rxjs';
+import { Role } from '../user.model';
+import { UserStore } from '../user.store';
+
+export function hasRole(roles: Role[]): CanMatchFn {
+  return () => {
+    const userStore = inject(UserStore);
+
+    return userStore.user$.pipe(
+      map((user) => {
+        if (!user) return false;
+        if (user.isAdmin) return true;
+        return roles.some((role) => user.roles.includes(role));
+      }),
+    );
+  };
+}
diff --git a/apps/angular/6-structural-directive/src/app/information.component.ts b/apps/angular/6-structural-directive/src/app/information.component.ts
index 81b339520..3399d1039 100644
--- a/apps/angular/6-structural-directive/src/app/information.component.ts
+++ b/apps/angular/6-structural-directive/src/app/information.component.ts
@@ -1,23 +1,20 @@
-import { CommonModule } from '@angular/common';
 import { ChangeDetectionStrategy, Component } from '@angular/core';
-import { UserStore } from './user.store';
+import { HasRoleSuperAdminDirective } from './directives/has-role-super-admin.directive';
+import { HasRoleDirective } from './directives/has-role.directive';
 
 @Component({
   selector: 'app-information',
-  imports: [CommonModule],
+  standalone: true,
+  imports: [HasRoleDirective, HasRoleSuperAdminDirective],
   template: `
     Information Panel
-    
-    visible only for super admin
-    visible if manager
-    visible if manager and/or reader
-    visible if manager and/or writer
-    visible if client
+    visible only for super admin
+    visible if manager
+    visible if manager and/or reader
+    visible if manager and/or writer
+    visible if client
     visible for everyone
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class InformationComponent {
-  user$ = this.userStore.user$;
-  constructor(private userStore: UserStore) {}
-}
+export class InformationComponent {}
diff --git a/apps/angular/6-structural-directive/src/app/routes.ts b/apps/angular/6-structural-directive/src/app/routes.ts
index 4db203f3b..3a00f1416 100644
--- a/apps/angular/6-structural-directive/src/app/routes.ts
+++ b/apps/angular/6-structural-directive/src/app/routes.ts
@@ -1,4 +1,7 @@
-export const APP_ROUTES = [
+import { Routes } from '@angular/router';
+import { hasRole } from './guards/role.guard';
+
+export const APP_ROUTES: Routes = [
   {
     path: '',
     loadComponent: () =>
@@ -10,5 +13,14 @@ export const APP_ROUTES = [
       import('./dashboard/admin.component').then(
         (m) => m.AdminDashboardComponent,
       ),
+    canMatch: [hasRole(['MANAGER'])],
+  },
+  {
+    path: 'enter',
+    loadComponent: () =>
+      import('./dashboard/manager.component').then(
+        (m) => m.ManagerDashboardComponent,
+      ),
+    canMatch: [hasRole(['READER', 'WRITER', 'CLIENT'])],
   },
 ];
diff --git a/apps/angular/8-pure-pipe/src/app/app.component.ts b/apps/angular/8-pure-pipe/src/app/app.component.ts
index 41dd38e25..264f8f971 100644
--- a/apps/angular/8-pure-pipe/src/app/app.component.ts
+++ b/apps/angular/8-pure-pipe/src/app/app.component.ts
@@ -1,20 +1,17 @@
 import { NgFor } from '@angular/common';
 import { Component } from '@angular/core';
+import { PersonDisplayPipe } from './pipes/person-display.pipe';
 
 @Component({
-  imports: [NgFor],
   selector: 'app-root',
+  standalone: true,
+  imports: [NgFor, PersonDisplayPipe],
   template: `
     
-      {{ heavyComputation(person, index) }}
+      {{ person | personDisplay: index }}
     
   `,
 })
 export class AppComponent {
   persons = ['toto', 'jack'];
-
-  heavyComputation(name: string, index: number) {
-    // very heavy computation
-    return `${name} - ${index}`;
-  }
 }
diff --git a/apps/angular/8-pure-pipe/src/app/pipes/person-display.pipe.ts b/apps/angular/8-pure-pipe/src/app/pipes/person-display.pipe.ts
new file mode 100644
index 000000000..53d9f80f8
--- /dev/null
+++ b/apps/angular/8-pure-pipe/src/app/pipes/person-display.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+  name: 'personDisplay',
+  standalone: true,
+})
+export class PersonDisplayPipe implements PipeTransform {
+  transform(name: string, index: number): string {
+    // very heavy computation
+    return `${name} - ${index}`;
+  }
+}
diff --git a/apps/angular/9-wrap-function-pipe/src/app/app.component.ts b/apps/angular/9-wrap-function-pipe/src/app/app.component.ts
index dd576ae49..991cdaf3e 100644
--- a/apps/angular/9-wrap-function-pipe/src/app/app.component.ts
+++ b/apps/angular/9-wrap-function-pipe/src/app/app.component.ts
@@ -1,33 +1,40 @@
 import { NgFor } from '@angular/common';
 import { Component } from '@angular/core';
+import { WrapFnPipe } from './pipes/wrap-fn.pipe';
+
+interface Person {
+  name: string;
+  age: number;
+}
 
 @Component({
-  imports: [NgFor],
   selector: 'app-root',
+  standalone: true,
+  imports: [NgFor, WrapFnPipe],
   template: `
     
-      {{ showName(person.name, index) }}
-      {{ isAllowed(person.age, isFirst) }}
+      {{ showName | wrapFn: person.name : index }}
+      {{ isAllowed | wrapFn: person.age : isFirst }}
     
   `,
 })
 export class AppComponent {
-  persons = [
+  persons: Person[] = [
     { name: 'Toto', age: 10 },
     { name: 'Jack', age: 15 },
     { name: 'John', age: 30 },
   ];
 
-  showName(name: string, index: number) {
+  showName = (name: string, index: number): string => {
     // very heavy computation
     return `${name} - ${index}`;
-  }
+  };
 
-  isAllowed(age: number, isFirst: boolean) {
+  isAllowed = (age: number, isFirst: boolean): string => {
     if (isFirst) {
       return 'always allowed';
     } else {
       return age > 25 ? 'allowed' : 'declined';
     }
-  }
+  };
 }
diff --git a/apps/angular/9-wrap-function-pipe/src/app/pipes/wrap-fn.pipe.ts b/apps/angular/9-wrap-function-pipe/src/app/pipes/wrap-fn.pipe.ts
new file mode 100644
index 000000000..782530b60
--- /dev/null
+++ b/apps/angular/9-wrap-function-pipe/src/app/pipes/wrap-fn.pipe.ts
@@ -0,0 +1,16 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+type AnyFunction = (...args: any[]) => any;
+
+@Pipe({
+  name: 'wrapFn',
+  standalone: true,
+})
+export class WrapFnPipe implements PipeTransform {
+  transform(
+    fn: TFn,
+    ...args: Parameters
+  ): ReturnType {
+    return fn(...args);
+  }
+}
diff --git a/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.html b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.html
index 9334b5bc9..f138e4b9c 100644
--- a/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.html
+++ b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.html
@@ -1,29 +1,30 @@
-
+
diff --git a/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts
index 4110d6cf7..4ce7809a7 100644
--- a/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts
+++ b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts
@@ -25,17 +25,16 @@ export class FeedbackFormComponent {
     email: new FormControl('', {
       validators: Validators.required,
     }),
+    rating: new FormControl(null, {
+      validators: Validators.required,
+    }),
     comment: new FormControl(),
   });
 
-  rating: string | null = null;
-
   submitForm(): void {
-    this.feedBackSubmit.emit({
-      ...this.feedbackForm.value,
-      rating: this.rating,
-    });
-
-    this.feedbackForm.reset();
+    if (this.feedbackForm.valid) {
+      this.feedBackSubmit.emit(this.feedbackForm.value);
+      this.feedbackForm.reset();
+    }
   }
 }
diff --git a/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts b/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts
index d6dc31631..bced6a211 100644
--- a/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts
+++ b/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts
@@ -1,20 +1,47 @@
-import { Component, EventEmitter, Output } from '@angular/core';
+import { Component } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
 
 @Component({
   standalone: true,
   selector: 'app-rating-control',
   templateUrl: 'rating-control.component.html',
   styleUrls: ['rating-control.component.scss'],
+  providers: [
+    {
+      provide: NG_VALUE_ACCESSOR,
+      useExisting: RatingControlComponent,
+      multi: true,
+    },
+  ],
 })
-export class RatingControlComponent {
-  @Output()
-  readonly ratingUpdated: EventEmitter = new EventEmitter();
-
+export class RatingControlComponent implements ControlValueAccessor {
   value: number | null = null;
+  disabled = false;
+  onChange: (value: string | null) => void = () => {};
+  onTouched: () => void = () => {};
+
+  writeValue(value: number | null): void {
+    this.value = value;
+  }
+
+  registerOnChange(fn: (value: string | null) => void): void {
+    this.onChange = fn;
+  }
+
+  registerOnTouched(fn: () => void): void {
+    this.onTouched = fn;
+  }
+
+  setDisabledState(isDisabled: boolean): void {
+    this.disabled = isDisabled;
+  }
 
   setRating(index: number): void {
+    if (this.disabled) return;
+
     this.value = index + 1;
-    this.ratingUpdated.emit(`${this.value}`);
+    this.onChange(this.value?.toString() ?? null);
+    this.onTouched();
   }
 
   isStarActive(index: number, value: number | null): boolean {
diff --git a/apps/forms/41-control-value-accessor/src/styles.scss b/apps/forms/41-control-value-accessor/src/styles.scss
index 77e408aa8..a3b886fe0 100644
--- a/apps/forms/41-control-value-accessor/src/styles.scss
+++ b/apps/forms/41-control-value-accessor/src/styles.scss
@@ -2,4 +2,63 @@
 @tailwind components;
 @tailwind utilities;
 
+body {
+  @apply min-h-screen bg-gray-100 p-4;
+  font-family: system-ui, -apple-system, sans-serif;
+}
+
+.container {
+  @apply mx-auto flex min-h-screen max-w-lg items-center justify-center;
+}
+
+.feedback-form {
+  @apply w-full rounded-lg bg-white p-8 shadow-lg;
+
+  &-title {
+    @apply mb-6 text-center text-2xl font-bold text-gray-800;
+  }
+
+  &-control {
+    @apply mb-4 w-full rounded-md border border-gray-300 px-4 py-2 text-gray-700 transition-all;
+
+    &:focus {
+      @apply border-blue-500 outline-none ring-2 ring-blue-200;
+    }
+  }
+
+  textarea.feedback-form-control {
+    @apply min-h-[120px] resize-none;
+  }
+
+  &-submit {
+    @apply mt-4 w-full rounded-md bg-blue-600 px-6 py-3 text-white transition-colors;
+
+    &:hover:not(:disabled) {
+      @apply bg-blue-700;
+    }
+
+    &:disabled {
+      @apply cursor-not-allowed bg-gray-400;
+    }
+  }
+}
+
+.rating {
+  @apply mb-4 flex justify-center gap-2;
+
+  .star {
+    @apply h-10 w-10 cursor-pointer transition-all;
+    fill: #d1d5db;
+
+    &:hover {
+      @apply scale-110;
+      fill: #fbbf24;
+    }
+
+    &-active {
+      fill: #fbbf24;
+    }
+  }
+}
+
 /* You can add global styles to this file, and also import other style files */
diff --git a/apps/forms/48-avoid-losing-form-data/src/app/app.config.ts b/apps/forms/48-avoid-losing-form-data/src/app/app.config.ts
index a7c1007b9..5f951cda6 100644
--- a/apps/forms/48-avoid-losing-form-data/src/app/app.config.ts
+++ b/apps/forms/48-avoid-losing-form-data/src/app/app.config.ts
@@ -1,7 +1,11 @@
 import { ApplicationConfig } from '@angular/core';
+import { provideAnimations } from '@angular/platform-browser/animations';
 import { provideRouter, withComponentInputBinding } from '@angular/router';
 import { appRoutes } from './app.routes';
 
 export const appConfig: ApplicationConfig = {
-  providers: [provideRouter(appRoutes, withComponentInputBinding())],
+  providers: [
+    provideRouter(appRoutes, withComponentInputBinding()),
+    provideAnimations(),
+  ],
 };
diff --git a/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts b/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts
index 84be34b9a..6be8a57fb 100644
--- a/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts
+++ b/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts
@@ -1,6 +1,7 @@
 import { Route } from '@angular/router';
-import { JoinComponent } from './pages/join.component';
+import { formGuard } from './guards/form.guard';
 import { PageComponent } from './pages/page.component';
+import { FormComponent } from './ui/form.component';
 
 export const appRoutes: Route[] = [
   {
@@ -10,20 +11,21 @@ export const appRoutes: Route[] = [
   },
   {
     path: 'form',
-    loadComponent: () => JoinComponent,
+    component: FormComponent,
+    canDeactivate: [formGuard],
   },
   {
     path: 'page-1',
     data: {
       title: 'Page 1',
     },
-    loadComponent: () => PageComponent,
+    component: PageComponent,
   },
   {
     path: 'page-2',
     data: {
       title: 'Page 2',
     },
-    loadComponent: () => PageComponent,
+    component: PageComponent,
   },
 ];
diff --git a/apps/forms/48-avoid-losing-form-data/src/app/guards/form.guard.ts b/apps/forms/48-avoid-losing-form-data/src/app/guards/form.guard.ts
new file mode 100644
index 000000000..d4ebad6ff
--- /dev/null
+++ b/apps/forms/48-avoid-losing-form-data/src/app/guards/form.guard.ts
@@ -0,0 +1,32 @@
+import { Dialog } from '@angular/cdk/dialog';
+import { inject } from '@angular/core';
+import { CanDeactivateFn } from '@angular/router';
+import { Observable, map } from 'rxjs';
+import { AlertDialogComponent } from '../ui/dialog.component';
+import { FormComponent } from '../ui/form.component';
+
+export const formGuard: CanDeactivateFn = (
+  component,
+): Observable | boolean => {
+  const dialog = inject(Dialog);
+
+  if (component.form.dirty) {
+    const dialogRef = dialog.open(AlertDialogComponent, {
+      disableClose: true,
+      ariaDescribedBy: 'alert-dialog-description',
+      ariaLabelledBy: 'alert-dialog-title',
+    });
+
+    return dialogRef.closed.pipe(
+      map((result) => {
+        if (result) {
+          component.form.reset();
+          return true;
+        }
+        return false;
+      }),
+    );
+  }
+
+  return true;
+};
diff --git a/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts
index 13f4e09c2..e952c567d 100644
--- a/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts
+++ b/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts
@@ -3,8 +3,46 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
 @Component({
   standalone: true,
   template: `
-    
-      {{ title() }}
+    
+      
+        
+          {{ title() }}
+        
+      
+
+      
+        
+          

+        
+
+        
+          
+            
+              Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aut qui
+              hic atque tenetur quis eius quos ea neque sunt, accusantium soluta
+              minus veniam tempora deserunt? Molestiae eius quidem quam
+              repellat.
+            
+
+            
+              Lorem ipsum dolor sit amet consectetur, adipisicing elit. Dolorum
+              explicabo quidem voluptatum voluptas illo accusantium ipsam quis,
+              vel mollitia? Vel provident culpa dignissimos possimus,
+              perferendis consectetur odit accusantium dolorem amet voluptates
+              aliquid, ducimus tempore incidunt quas.
+            
+          
+
+          
+            Learn More
+          
+        
+      
 
     
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
diff --git a/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts
index 661da9bcf..15bfcd66c 100644
--- a/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts
+++ b/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts
@@ -1,24 +1,34 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
-
-// NOTE : this is just the dialog content, you need to implement dialog logic
+import { DialogRef } from '@angular/cdk/dialog';
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
 
 @Component({
   standalone: true,
   template: `
-    
-      
+    
+      
         You have unsaved information!
       
 
-      
Do you want to continue and lose them?
+      
+        Do you want to continue and lose them?
+      
 
       
         
 
         
@@ -27,4 +37,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class AlertDialogComponent {}
+export class AlertDialogComponent {
+  dialogRef = inject(DialogRef);
+}
diff --git a/apps/forms/48-avoid-losing-form-data/src/app/ui/form.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/ui/form.component.ts
index f3190d517..a257b6f9a 100644
--- a/apps/forms/48-avoid-losing-form-data/src/app/ui/form.component.ts
+++ b/apps/forms/48-avoid-losing-form-data/src/app/ui/form.component.ts
@@ -1,4 +1,9 @@
-import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import {
+  ChangeDetectionStrategy,
+  Component,
+  HostListener,
+  inject,
+} from '@angular/core';
 import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
 
 @Component({
@@ -71,6 +76,14 @@ export class FormComponent {
     message: '',
   });
 
+  @HostListener('window:beforeunload', ['$event'])
+  handleBeforeUnload(event: BeforeUnloadEvent) {
+    if (this.form.dirty) {
+      event.preventDefault();
+      event.returnValue = '';
+    }
+  }
+
   onSubmit() {
     if (this.form.valid) this.form.reset();
   }
diff --git a/apps/forms/48-avoid-losing-form-data/src/styles.scss b/apps/forms/48-avoid-losing-form-data/src/styles.scss
index 77e408aa8..9d2533e3a 100644
--- a/apps/forms/48-avoid-losing-form-data/src/styles.scss
+++ b/apps/forms/48-avoid-losing-form-data/src/styles.scss
@@ -2,4 +2,6 @@
 @tailwind components;
 @tailwind utilities;
 
+@import '@angular/cdk/overlay-prebuilt.css';
+
 /* You can add global styles to this file, and also import other style files */
diff --git a/apps/performance/12-optimize-change-detection/src/app/app.component.ts b/apps/performance/12-optimize-change-detection/src/app/app.component.ts
index dd818cad5..a9d5c1950 100644
--- a/apps/performance/12-optimize-change-detection/src/app/app.component.ts
+++ b/apps/performance/12-optimize-change-detection/src/app/app.component.ts
@@ -1,45 +1,70 @@
 import { AsyncPipe, NgIf } from '@angular/common';
-import { Component, HostListener } from '@angular/core';
+import {
+  ChangeDetectorRef,
+  Component,
+  NgZone,
+  OnDestroy,
+  OnInit,
+} from '@angular/core';
 import { BehaviorSubject } from 'rxjs';
 
 @Component({
+  standalone: true,
   imports: [NgIf, AsyncPipe],
   selector: 'app-root',
   template: `
-    
Top
-    
Middle
-    
Bottom
-    
+    
Top
+    
Middle
+    
Bottom
+
+    
   `,
   styles: [
     `
       :host {
-        height: 1500px;
-        display: flex;
-        flex-direction: column;
-        justify-content: space-between;
-
-        button {
-          position: fixed;
-          bottom: 1rem;
-          left: 1rem;
-          z-index: 1;
-          padding: 1rem;
-        }
+        min-height: 150vh;
+        display: block;
       }
     `,
   ],
 })
-export class AppComponent {
-  title = 'scroll-cd';
-
+export class AppComponent implements OnInit, OnDestroy {
   private displayButtonSubject = new BehaviorSubject
(false);
   displayButton$ = this.displayButtonSubject.asObservable();
 
-  @HostListener('window:scroll', ['$event'])
-  onScroll() {
-    const pos = window.pageYOffset;
-    this.displayButtonSubject.next(pos > 50);
+  private scrollHandler: () => void;
+  private lastState = false;
+
+  constructor(
+    private ngZone: NgZone,
+    private cdr: ChangeDetectorRef,
+  ) {
+    this.scrollHandler = () => {
+      const shouldShow = window.pageYOffset > 50;
+      if (shouldShow !== this.lastState) {
+        this.lastState = shouldShow;
+        this.displayButtonSubject.next(shouldShow);
+        this.cdr.detectChanges();
+      }
+    };
+  }
+
+  ngOnInit() {
+    this.ngZone.runOutsideAngular(() => {
+      window.addEventListener('scroll', this.scrollHandler, { passive: true });
+    });
+  }
+
+  ngOnDestroy() {
+    window.removeEventListener('scroll', this.scrollHandler);
   }
 
   goToTop() {
diff --git a/apps/performance/34-default-vs-onpush/src/app/app.component.ts b/apps/performance/34-default-vs-onpush/src/app/app.component.ts
index 88b0a6571..7af7eaaa5 100644
--- a/apps/performance/34-default-vs-onpush/src/app/app.component.ts
+++ b/apps/performance/34-default-vs-onpush/src/app/app.component.ts
@@ -1,9 +1,10 @@
-import { Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
 import { randFirstName } from '@ngneat/falso';
 import { PersonListComponent } from './person-list.component';
 import { RandomComponent } from './random.component';
 
 @Component({
+  standalone: true,
   imports: [PersonListComponent, RandomComponent],
   selector: 'app-root',
   template: `
@@ -14,6 +15,7 @@ import { RandomComponent } from './random.component';
       
      
   `,
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class AppComponent {
   girlList = randFirstName({ gender: 'female', length: 10 });
diff --git a/apps/performance/34-default-vs-onpush/src/app/name-list.component.ts b/apps/performance/34-default-vs-onpush/src/app/name-list.component.ts
new file mode 100644
index 000000000..e4d49b524
--- /dev/null
+++ b/apps/performance/34-default-vs-onpush/src/app/name-list.component.ts
@@ -0,0 +1,27 @@
+import { CDFlashingDirective } from '@angular-challenges/shared/directives';
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { MatListModule } from '@angular/material/list';
+@Component({
+  selector: 'app-name-list',
+  standalone: true,
+  imports: [MatListModule, CDFlashingDirective, CommonModule],
+  template: `
+    
+      Empty list
+      
+        
+          
{{ name }}
+        
+      
+      
+    
+  `,
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NameListComponent {
+  @Input() names: string[] = [];
+}
diff --git a/apps/performance/34-default-vs-onpush/src/app/person-input.component.ts b/apps/performance/34-default-vs-onpush/src/app/person-input.component.ts
new file mode 100644
index 000000000..27f2ed0b1
--- /dev/null
+++ b/apps/performance/34-default-vs-onpush/src/app/person-input.component.ts
@@ -0,0 +1,37 @@
+import {
+  ChangeDetectionStrategy,
+  Component,
+  EventEmitter,
+  Output,
+} from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+
+@Component({
+  selector: 'app-person-input',
+  standalone: true,
+  imports: [FormsModule, MatFormFieldModule, MatInputModule],
+  template: `
+    
+      
+    
+  `,
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class PersonInputComponent {
+  @Output() addPerson = new EventEmitter
();
+  label = '';
+
+  handleKey(event: KeyboardEvent) {
+    if (event.key === 'Enter' && this.label.trim()) {
+      this.addPerson.emit(this.label);
+      this.label = '';
+    }
+  }
+}
diff --git a/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts b/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts
index 4cd92396a..c4ec4fd7b 100644
--- a/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts
+++ b/apps/performance/34-default-vs-onpush/src/app/person-list.component.ts
@@ -1,67 +1,35 @@
-import { Component, Input } from '@angular/core';
-
 import { CDFlashingDirective } from '@angular-challenges/shared/directives';
 import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { MatChipsModule } from '@angular/material/chips';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatListModule } from '@angular/material/list';
-
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { NameListComponent } from './name-list.component';
+import { PersonInputComponent } from './person-input.component';
 @Component({
   selector: 'app-person-list',
+  standalone: true,
   imports: [
-    CommonModule,
-    FormsModule,
-    MatListModule,
-    MatFormFieldModule,
-    MatInputModule,
-    MatChipsModule,
+    PersonInputComponent,
+    NameListComponent,
     CDFlashingDirective,
+    CommonModule,
   ],
   template: `
     
       {{ title | titlecase }}
     
 
-    
-      
-    
-
-    
-      Empty list
-      
-        
-          
-            {{ name }}
-          
-        
-      
-      
-    
+    
+    
   `,
   host: {
     class: 'w-full flex flex-col items-center',
   },
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class PersonListComponent {
   @Input() names: string[] = [];
   @Input() title = '';
 
-  label = '';
-
-  handleKey(event: KeyboardEvent) {
-    if (event.key === 'Enter') {
-      this.names?.unshift(this.label);
-      this.label = '';
-    }
+  addPerson(name: string) {
+    this.names = [name, ...this.names];
   }
 }
diff --git a/apps/performance/34-default-vs-onpush/src/app/random.component.ts b/apps/performance/34-default-vs-onpush/src/app/random.component.ts
index 71479e28d..435045ee5 100644
--- a/apps/performance/34-default-vs-onpush/src/app/random.component.ts
+++ b/apps/performance/34-default-vs-onpush/src/app/random.component.ts
@@ -1,11 +1,13 @@
 import { CDFlashingDirective } from '@angular-challenges/shared/directives';
-import { Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
 
 @Component({
   selector: 'app-random',
+  standalone: true,
+  imports: [CDFlashingDirective],
   template: `
     I do nothing but I'm here
   `,
-  imports: [CDFlashingDirective],
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class RandomComponent {}
diff --git a/apps/rxjs/11-high-order-operator-bug/src/app/app.component.ts b/apps/rxjs/11-high-order-operator-bug/src/app/app.component.ts
index fb80fb2b6..b07c26824 100644
--- a/apps/rxjs/11-high-order-operator-bug/src/app/app.component.ts
+++ b/apps/rxjs/11-high-order-operator-bug/src/app/app.component.ts
@@ -1,52 +1,221 @@
 /* eslint-disable @angular-eslint/component-selector */
+import { CommonModule } from '@angular/common';
 import { Component, inject, input, signal } from '@angular/core';
 import { take } from 'rxjs';
 import { AppService } from './app.service';
 import { TopicType } from './localDB.service';
-
 @Component({
   selector: 'button-delete-topic',
+  standalone: true,
+  imports: [CommonModule],
   template: `
-    
-    {{ message() }}
+    
+      
+      
+        {{ message() }}
+      
+    
 
   `,
+  styles: [
+    `
+      .delete-container {
+        display: flex;
+        flex-direction: column;
+        gap: 8px;
+        margin: 10px 0;
+      }
+
+      .delete-button {
+        padding: 8px 16px;
+        border: none;
+        border-radius: 4px;
+        cursor: pointer;
+        font-weight: 500;
+        transition: all 0.2s ease;
+      }
+
+      .delete-button:disabled {
+        opacity: 0.5;
+        cursor: not-allowed;
+      }
+
+      .delete-button.food {
+        background-color: #ff4d4d;
+        color: white;
+      }
+
+      .delete-button.sport {
+        background-color: #4caf50;
+        color: white;
+      }
+
+      .delete-button.book {
+        background-color: #2196f3;
+        color: white;
+      }
+
+      .message {
+        padding: 8px;
+        border-radius: 4px;
+        font-size: 14px;
+      }
+
+      .message.error {
+        background-color: #ffebee;
+        color: #c62828;
+      }
+
+      .message.success {
+        background-color: #e8f5e9;
+        color: #2e7d32;
+      }
+    `,
+  ],
 })
 export class ButtonDeleteComponent {
   readonly topic = input.required();
 
   message = signal('');
+  isDeleting = signal(false);
+  isError = signal(false);
 
   private service = inject(AppService);
 
   deleteTopic() {
+    this.isDeleting.set(true);
+    this.message.set('');
+
     this.service
       .deleteOldTopics(this.topic())
       .pipe(take(1))
-      .subscribe((result) =>
-        this.message.set(
-          result
-            ? `All ${this.topic()} have been deleted`
-            : `Error: deletion of some ${this.topic()} failed`,
-        ),
-      );
+      .subscribe({
+        next: (result) => {
+          this.isError.set(!result);
+          this.message.set(
+            result
+              ? `All ${this.topic()} have been deleted`
+              : `Error: deletion of some ${this.topic()} failed`,
+          );
+        },
+        complete: () => {
+          this.isDeleting.set(false);
+        },
+      });
   }
 }
 
 @Component({
-  imports: [ButtonDeleteComponent],
+  standalone: true,
+  imports: [ButtonDeleteComponent, CommonModule],
   selector: 'app-root',
   template: `
-    @for (info of allInfo(); track info.id) {
-      {{ info.id }} - {{ info.topic }}
-    }
+    
+      
Topic Management
+
+      
+        
Current Topics
+        @for (info of allInfo(); track info.id) {
+          
+            #{{ info.id }}
+            {{ info.topic }}
+          
+        }
+      
 
-    
Delete Food
-    
Delete Sport
-    
Delete Book
+      
+        
Delete Topics
+        
+          Delete Food Topics
+        
+        
+          Delete Sport Topics
+        
+        
+          Delete Book Topics
+        
+      
+    
 
   `,
+  styles: [
+    `
+      .container {
+        max-width: 800px;
+        margin: 0 auto;
+        padding: 20px;
+        font-family: Arial, sans-serif;
+      }
+
+      h1 {
+        color: #333;
+        text-align: center;
+        margin-bottom: 30px;
+      }
+
+      h2 {
+        color: #666;
+        margin-bottom: 20px;
+      }
+
+      .info-list {
+        background: white;
+        padding: 20px;
+        border-radius: 8px;
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+        margin-bottom: 30px;
+      }
+
+      .info-item {
+        display: flex;
+        align-items: center;
+        padding: 8px;
+        border-bottom: 1px solid #eee;
+      }
+
+      .info-id {
+        font-weight: bold;
+        margin-right: 10px;
+        color: #666;
+      }
+
+      .topic-badge {
+        padding: 4px 8px;
+        border-radius: 12px;
+        font-size: 12px;
+        text-transform: uppercase;
+      }
+
+      .topic-badge.food {
+        background-color: #ffebee;
+        color: #c62828;
+      }
+
+      .topic-badge.sport {
+        background-color: #e8f5e9;
+        color: #2e7d32;
+      }
+
+      .topic-badge.book {
+        background-color: #e3f2fd;
+        color: #1565c0;
+      }
+
+      .actions {
+        background: white;
+        padding: 20px;
+        border-radius: 8px;
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+      }
+    `,
+  ],
 })
 export class AppComponent {
   private service = inject(AppService);
-
   allInfo = this.service.getAllInfo;
 }
diff --git a/apps/rxjs/11-high-order-operator-bug/src/app/app.service.ts b/apps/rxjs/11-high-order-operator-bug/src/app/app.service.ts
index df2269a89..093a21e19 100644
--- a/apps/rxjs/11-high-order-operator-bug/src/app/app.service.ts
+++ b/apps/rxjs/11-high-order-operator-bug/src/app/app.service.ts
@@ -1,5 +1,5 @@
 import { inject, Injectable } from '@angular/core';
-import { merge, Observable, of } from 'rxjs';
+import { combineLatest, map, Observable, of } from 'rxjs';
 import { LocalDBService, TopicType } from './localDB.service';
 
 @Injectable({ providedIn: 'root' })
@@ -10,10 +10,14 @@ export class AppService {
 
   deleteOldTopics(type: TopicType): Observable {
     const infoByType = this.dbService.searchByType(type);
-    return infoByType.length > 0
-      ? infoByType
-          .map((t) => this.dbService.deleteOneTopic(t.id))
-          .reduce((acc, curr) => merge(acc, curr), of(true))
-      : of(true);
+
+    if (infoByType.length === 0) {
+      return of(true);
+    }
+
+    // Use combineLatest to wait for all deletion operations and check if they all succeeded
+    return combineLatest(
+      infoByType.map((info) => this.dbService.deleteOneTopic(info.id)),
+    ).pipe(map((results) => results.every((result) => result === true)));
   }
 }
diff --git a/apps/rxjs/11-high-order-operator-bug/src/styles.scss b/apps/rxjs/11-high-order-operator-bug/src/styles.scss
index 90d4ee007..0da5d120c 100644
--- a/apps/rxjs/11-high-order-operator-bug/src/styles.scss
+++ b/apps/rxjs/11-high-order-operator-bug/src/styles.scss
@@ -1 +1,42 @@
 /* You can add global styles to this file, and also import other style files */
+
+body {
+  margin: 0;
+  padding: 0;
+  background-color: #f5f5f5;
+  min-height: 100vh;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
+    Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+  line-height: 1.6;
+  color: #333;
+}
+
+* {
+  box-sizing: border-box;
+}
+
+button {
+  font-family: inherit;
+}
+
+/* Smooth transitions */
+* {
+  transition: all 0.2s ease-in-out;
+}
+
+/* Better focus states for accessibility */
+:focus {
+  outline: 2px solid #2196f3;
+  outline-offset: 2px;
+}
+
+/* Responsive font sizing */
+html {
+  font-size: 16px;
+}
+
+@media (max-width: 768px) {
+  html {
+    font-size: 14px;
+  }
+}
diff --git a/apps/rxjs/14-race-condition/src/app/app.component.ts b/apps/rxjs/14-race-condition/src/app/app.component.ts
index a7eb77710..2b115cb40 100644
--- a/apps/rxjs/14-race-condition/src/app/app.component.ts
+++ b/apps/rxjs/14-race-condition/src/app/app.component.ts
@@ -1,40 +1,53 @@
-import {
-  ChangeDetectionStrategy,
-  Component,
-  inject,
-  OnInit,
-} from '@angular/core';
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { MatButtonModule } from '@angular/material/button';
 import { MatDialog } from '@angular/material/dialog';
-import { take } from 'rxjs';
+import { switchMap, take } from 'rxjs';
 import { TopicModalComponent } from './topic-dialog.component';
-import { TopicService, TopicType } from './topic.service';
+import { TopicService } from './topic.service';
 
 @Component({
   standalone: true,
+  imports: [MatButtonModule],
   selector: 'app-root',
   template: `
-    
+    
+      
+    
   `,
+  styles: [
+    `
+      .container {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        min-height: 100vh;
+        padding: 20px;
+      }
+    `,
+  ],
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class AppComponent implements OnInit {
-  title = 'rxjs-race-condition';
-  dialog = inject(MatDialog);
-  topicService = inject(TopicService);
-  topics: TopicType[] = [];
+export class AppComponent {
+  private dialog = inject(MatDialog);
+  private topicService = inject(TopicService);
 
-  ngOnInit(): void {
+  openTopicModal() {
+    // Get fresh topics data and then open modal
     this.topicService
       .fakeGetHttpTopic()
-      .pipe(take(1))
-      .subscribe((topics) => (this.topics = topics));
-  }
-
-  openTopicModal() {
-    this.dialog.open(TopicModalComponent, {
-      data: {
-        topics: this.topics,
-      },
-    });
+      .pipe(
+        take(1),
+        switchMap((topics) =>
+          this.dialog
+            .open(TopicModalComponent, {
+              data: { topics },
+              width: '400px',
+            })
+            .afterClosed(),
+        ),
+      )
+      .subscribe();
   }
 }
diff --git a/apps/rxjs/14-race-condition/src/app/topic-dialog.component.ts b/apps/rxjs/14-race-condition/src/app/topic-dialog.component.ts
index e01a69a01..319b7487b 100644
--- a/apps/rxjs/14-race-condition/src/app/topic-dialog.component.ts
+++ b/apps/rxjs/14-race-condition/src/app/topic-dialog.component.ts
@@ -1,23 +1,67 @@
-import { NgFor } from '@angular/common';
 import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
 import { MatButtonModule } from '@angular/material/button';
 import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
 
 @Component({
+  standalone: true,
   template: `
-    Show all Topics
-    
-    
-      
+    
+      
Available Topics
+
+      
+        
+          @for (topic of data.topics; track topic) {
+            - 
+              📌
+              {{ topic }}
+            
 
+          }
+        
+      
+
+      
+        
+      
     
   `,
-  imports: [MatDialogModule, MatButtonModule, NgFor],
+  styles: [
+    `
+      .dialog-container {
+        padding: 20px;
+      }
+
+      .topic-list {
+        list-style: none;
+        padding: 0;
+        margin: 20px 0;
+      }
+
+      .topic-item {
+        display: flex;
+        align-items: center;
+        padding: 12px;
+        margin: 8px 0;
+        background-color: #f5f5f5;
+        border-radius: 8px;
+        transition: all 0.2s ease;
+
+        &:hover {
+          background-color: #eeeeee;
+          transform: translateX(5px);
+        }
+      }
+
+      .topic-icon {
+        margin-right: 12px;
+        font-size: 1.2em;
+      }
+
+      mat-dialog-actions {
+        margin-top: 20px;
+      }
+    `,
+  ],
+  imports: [MatDialogModule, MatButtonModule],
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class TopicModalComponent {
diff --git a/apps/rxjs/14-race-condition/tsconfig.json b/apps/rxjs/14-race-condition/tsconfig.json
index 8a0cb05fc..3879b94cd 100644
--- a/apps/rxjs/14-race-condition/tsconfig.json
+++ b/apps/rxjs/14-race-condition/tsconfig.json
@@ -22,7 +22,7 @@
       "path": "./tsconfig.editor.json"
     },
     {
-      "path": "./cypress/tsconfig.base.json"
+      "path": "./cypress/tsconfig.json"
     }
   ],
   "extends": "../../../tsconfig.base.json",
diff --git a/apps/rxjs/38-catch-error/src/app/app.component.css b/apps/rxjs/38-catch-error/src/app/app.component.css
index e85a8aed5..dfd76b25d 100644
--- a/apps/rxjs/38-catch-error/src/app/app.component.css
+++ b/apps/rxjs/38-catch-error/src/app/app.component.css
@@ -30,3 +30,134 @@ button {
   text-align: center;
   border: 1px solid #ccc;
 }
+
+.container {
+  min-height: 100vh;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-color: #f5f5f5;
+  padding: 20px;
+}
+
+.content {
+  background: white;
+  padding: 2rem;
+  border-radius: 12px;
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+  width: 100%;
+  max-width: 600px;
+}
+
+h1 {
+  text-align: center;
+  color: #333;
+  margin-bottom: 2rem;
+  font-size: 1.8rem;
+}
+
+.info-box {
+  background-color: #f8f9fa;
+  padding: 1rem;
+  border-radius: 8px;
+  margin-bottom: 2rem;
+}
+
+.info-box h3 {
+  margin: 0 0 0.5rem 0;
+  color: #666;
+  font-size: 1rem;
+}
+
+.endpoints {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 0.5rem;
+}
+
+.endpoint {
+  background-color: #e9ecef;
+  padding: 0.25rem 0.75rem;
+  border-radius: 16px;
+  font-size: 0.9rem;
+  color: #495057;
+}
+
+.form {
+  display: flex;
+  gap: 1rem;
+  margin-bottom: 2rem;
+}
+
+.input {
+  flex: 1;
+  padding: 0.75rem 1rem;
+  border: 2px solid #dee2e6;
+  border-radius: 8px;
+  font-size: 1rem;
+  transition: all 0.2s ease;
+}
+
+.input:focus {
+  outline: none;
+  border-color: #4dabf7;
+  box-shadow: 0 0 0 3px rgba(77, 171, 247, 0.2);
+}
+
+.input.error {
+  border-color: #ff6b6b;
+}
+
+.input.success {
+  border-color: #51cf66;
+}
+
+.button {
+  padding: 0.75rem 1.5rem;
+  background-color: #4dabf7;
+  color: white;
+  border: none;
+  border-radius: 8px;
+  font-size: 1rem;
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.button:hover:not(:disabled) {
+  background-color: #339af0;
+}
+
+.button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.response {
+  background-color: #f8f9fa;
+  padding: 1rem;
+  border-radius: 8px;
+  border-left: 4px solid #4dabf7;
+}
+
+.response.error {
+  border-left-color: #ff6b6b;
+  background-color: #fff5f5;
+}
+
+.response.success {
+  border-left-color: #51cf66;
+  background-color: #f4fce3;
+}
+
+.response h3 {
+  margin: 0 0 0.5rem 0;
+  color: #495057;
+}
+
+pre {
+  margin: 0;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+  color: #495057;
+  font-size: 0.9rem;
+}
diff --git a/apps/rxjs/38-catch-error/src/app/app.component.ts b/apps/rxjs/38-catch-error/src/app/app.component.ts
index 65e177567..fc722835d 100644
--- a/apps/rxjs/38-catch-error/src/app/app.component.ts
+++ b/apps/rxjs/38-catch-error/src/app/app.component.ts
@@ -1,60 +1,94 @@
 import { CommonModule } from '@angular/common';
-import { HttpClient } from '@angular/common/http';
-import { Component, DestroyRef, OnInit, inject } from '@angular/core';
+import { HttpClient, HttpErrorResponse } from '@angular/common/http';
+import { Component, DestroyRef, inject } from '@angular/core';
 import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
 import { FormsModule } from '@angular/forms';
-import { Subject, concatMap, map } from 'rxjs';
+import { Subject, catchError, concatMap, map, of } from 'rxjs';
+
+interface ApiResponse {
+  data?: unknown;
+  error?: string;
+  status: 'success' | 'error';
+}
 
 @Component({
+  standalone: true,
   imports: [CommonModule, FormsModule],
   selector: 'app-root',
   template: `
-    
-      
-        possible values: posts, comments, albums, photos, todos, users
-      
-    
-    
-    
-      {{ response | json }}
+    
+      
+        
API Data Fetcher
+
+        
+          
Available Endpoints:
+          
+            
+              {{ endpoint }}
+            
+          
+        
+
+        
+
+        
+          
{{ response.status === 'success' ? 'Response' : 'Error' }}
+          
{{ response.data || response.error | json }}
+        
+      
 
     
   `,
   styleUrls: ['./app.component.css'],
 })
-export class AppComponent implements OnInit {
+export class AppComponent {
   submit$ = new Subject
();
   input = '';
-  response: unknown;
+  response?: ApiResponse;
+
+  validEndpoints = ['posts', 'comments', 'albums', 'photos', 'todos', 'users'];
 
   private destroyRef = inject(DestroyRef);
   private http = inject(HttpClient);
 
-  ngOnInit() {
+  constructor() {
     this.submit$
       .pipe(
-        map(() => this.input),
+        map(() => this.input.trim()),
         concatMap((value) =>
-          this.http.get(`https://jsonplaceholder.typicode.com/${value}/1`),
+          this.http.get(`https://jsonplaceholder.typicode.com/${value}/1`).pipe(
+            map(
+              (data): ApiResponse => ({
+                data,
+                status: 'success',
+              }),
+            ),
+            catchError((error: HttpErrorResponse) =>
+              of({
+                error: `Failed to fetch data: ${error.message}`,
+                status: 'error' as const,
+              }),
+            ),
+          ),
         ),
         takeUntilDestroyed(this.destroyRef),
       )
-      .subscribe({
-        next: (value) => {
-          console.log(value);
-          this.response = value;
-        },
-        error: (error) => {
-          console.log(error);
-          this.response = error;
-        },
-        complete: () => console.log('done'),
+      .subscribe((response) => {
+        console.log(response);
+        this.response = response;
       });
   }
 }
diff --git a/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts b/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts
index 8f0dbbc70..4af1110fc 100644
--- a/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts
+++ b/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts
@@ -1,25 +1,72 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
+import { HoldableDirective } from './holdable.directive';
 
 @Component({
-  imports: [],
+  standalone: true,
+  imports: [HoldableDirective, CommonModule],
   selector: 'app-root',
   template: `
-    
+    
       
+        
+          
Hold to Save
+          
Hold the button for 5 seconds to save
+        
+
         
 
-        
+        
+          
+          
+            {{ progress().toFixed(0) }}% Complete
+          
+        
 
        
     
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class AppComponent {
-  onSend() {
-    console.log('Save it!');
+  progress = signal(0);
+  saving = signal(false);
+
+  buttonClasses = () => {
+    const base =
+      'rounded px-6 py-3 font-semibold text-white transition-all duration-200 transform';
+    const state = this.saving()
+      ? 'bg-green-500 cursor-not-allowed opacity-75'
+      : this.progress() > 0
+        ? 'bg-indigo-700 scale-95'
+        : 'bg-indigo-600 hover:bg-indigo-700 active:scale-95';
+
+    return `${base} ${state}`;
+  };
+
+  onSave() {
+    this.saving.set(true);
+    // Simulate API call
+    setTimeout(() => {
+      console.log('Saved successfully!');
+      this.saving.set(false);
+      this.progress.set(0);
+    }, 1000);
   }
 }
diff --git a/apps/rxjs/49-hold-to-save-button/src/app/holdable.directive.ts b/apps/rxjs/49-hold-to-save-button/src/app/holdable.directive.ts
new file mode 100644
index 000000000..999c69a7b
--- /dev/null
+++ b/apps/rxjs/49-hold-to-save-button/src/app/holdable.directive.ts
@@ -0,0 +1,62 @@
+import {
+  Directive,
+  EventEmitter,
+  HostListener,
+  Input,
+  Output,
+} from '@angular/core';
+import { Subject, interval, map, takeUntil, tap } from 'rxjs';
+
+@Directive({
+  selector: '[holdable]',
+  standalone: true,
+})
+export class HoldableDirective {
+  @Input() holdDuration = 1000; // Default 1 second
+  @Output() holdProgress = new EventEmitter();
+  @Output() holdComplete = new EventEmitter();
+
+  private holding = false;
+  private cancel$ = new Subject();
+
+  @HostListener('mousedown')
+  @HostListener('touchstart')
+  onHoldStart() {
+    this.holding = true;
+    this.startHoldTimer();
+  }
+
+  @HostListener('mouseup')
+  @HostListener('mouseleave')
+  @HostListener('touchend')
+  @HostListener('touchcancel')
+  onHoldEnd() {
+    this.holding = false;
+    this.cancel$.next();
+    this.holdProgress.emit(0);
+  }
+
+  private startHoldTimer() {
+    const startTime = Date.now();
+
+    interval(10)
+      .pipe(
+        takeUntil(this.cancel$),
+        map(() => {
+          const elapsedTime = Date.now() - startTime;
+          return (elapsedTime / this.holdDuration) * 100;
+        }),
+        tap((progress) => {
+          if (progress >= 100 && this.holding) {
+            this.holdComplete.emit();
+            this.cancel$.next();
+          }
+        }),
+      )
+      .subscribe((progress) => {
+        if (this.holding) {
+          this.holdProgress.emit(Math.min(progress, 100));
+        }
+      });
+  }
+}
diff --git a/apps/rxjs/49-hold-to-save-button/src/styles.scss b/apps/rxjs/49-hold-to-save-button/src/styles.scss
index c98dac907..b9456c94b 100644
--- a/apps/rxjs/49-hold-to-save-button/src/styles.scss
+++ b/apps/rxjs/49-hold-to-save-button/src/styles.scss
@@ -3,12 +3,41 @@
 @tailwind utilities;
 
 progress {
-  @apply h-4 w-full rounded-full bg-gray-200;
+  @apply h-2 w-full rounded-full bg-gray-200;
+  transition: all 0.1s ease-out;
 }
+
 progress::-webkit-progress-bar {
   @apply rounded-full bg-gray-200;
 }
 
 progress::-webkit-progress-value {
   @apply rounded-full bg-indigo-600;
+  transition: all 0.1s ease-out;
+}
+
+progress::-moz-progress-bar {
+  @apply rounded-full bg-indigo-600;
+  transition: all 0.1s ease-out;
+}
+
+/* Add smooth transitions */
+.transition-transform {
+  transition-property: transform;
+  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  transition-duration: 150ms;
+}
+
+/* Add pulsing animation for saving state */
+@keyframes pulse {
+  0%, 100% {
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.7;
+  }
+}
+
+.animate-pulse {
+  animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
 }
diff --git a/apps/signal/30-interop-rxjs-signal/src/app/list/photos.component.ts b/apps/signal/30-interop-rxjs-signal/src/app/list/photos.component.ts
index f81361957..7ffd519a5 100644
--- a/apps/signal/30-interop-rxjs-signal/src/app/list/photos.component.ts
+++ b/apps/signal/30-interop-rxjs-signal/src/app/list/photos.component.ts
@@ -1,18 +1,18 @@
 import { NgFor, NgIf } from '@angular/common';
-import { Component, OnInit, inject } from '@angular/core';
+import { Component, inject } from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
 import { FormControl, ReactiveFormsModule } from '@angular/forms';
 import { MatFormFieldModule } from '@angular/material/form-field';
 import { MatInputModule } from '@angular/material/input';
 import { MatProgressBarModule } from '@angular/material/progress-bar';
 import { RouterLinkWithHref } from '@angular/router';
-import { LetDirective } from '@ngrx/component';
-import { provideComponentStore } from '@ngrx/component-store';
-import { debounceTime, distinctUntilChanged, skipWhile, tap } from 'rxjs';
+import { debounceTime, distinctUntilChanged } from 'rxjs';
 import { Photo } from '../photo.model';
 import { PhotoStore } from './photos.store';
 
 @Component({
   selector: 'app-photos',
+  standalone: true,
   imports: [
     ReactiveFormsModule,
     MatFormFieldModule,
@@ -20,9 +20,9 @@ import { PhotoStore } from './photos.store';
     NgIf,
     NgFor,
     MatInputModule,
-    LetDirective,
     RouterLinkWithHref,
   ],
+  providers: [PhotoStore],
   template: `
     Photos
 
@@ -35,77 +35,66 @@ import { PhotoStore } from './photos.store';
         placeholder="find a photo" />
     
 
-    
-      
-        
-          
-          
-          Page :{{ vm.page }} / {{ vm.pages }}
-        
-        
-         0; else noPhoto">
-          - 
-            
-              
-            
-           
-        
-        
-          No Photos found. Type a search word.
-        
-        
+    
+      
+        
+        
+        Page: {{ store.page() }} / {{ store.pages() }}
       
-    
+
+      
+
+      
+
+      
+        No Photos found. Type a search word.
+      
+
+      
+    
   `,
-  providers: [provideComponentStore(PhotoStore)],
   host: {
     class: 'p-5 block',
   },
 })
-export default class PhotosComponent implements OnInit {
+export default class PhotosComponent {
   store = inject(PhotoStore);
-  readonly vm$ = this.store.vm$.pipe(
-    tap(({ search }) => {
-      if (!this.formInit) {
-        this.search.setValue(search);
-        this.formInit = true;
-      }
-    }),
-  );
-
-  private formInit = false;
-  search = new FormControl();
+  search = new FormControl(this.store.search());
 
-  ngOnInit(): void {
-    this.store.search(
-      this.search.valueChanges.pipe(
-        skipWhile(() => !this.formInit),
-        debounceTime(300),
-        distinctUntilChanged(),
-      ),
-    );
+  constructor() {
+    this.search.valueChanges
+      .pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed())
+      .subscribe((value) => {
+        if (value) this.store.updateSearch(value);
+      });
   }
 
   trackById(index: number, photo: Photo) {
diff --git a/apps/signal/30-interop-rxjs-signal/src/app/list/photos.store.ts b/apps/signal/30-interop-rxjs-signal/src/app/list/photos.store.ts
index f1315e87e..20a3439a6 100644
--- a/apps/signal/30-interop-rxjs-signal/src/app/list/photos.store.ts
+++ b/apps/signal/30-interop-rxjs-signal/src/app/list/photos.store.ts
@@ -1,18 +1,12 @@
-import { inject, Injectable } from '@angular/core';
-import {
-  ComponentStore,
-  OnStateInit,
-  OnStoreInit,
-} from '@ngrx/component-store';
+import { computed, inject, Injectable, signal } from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
 import { tapResponse } from '@ngrx/operators';
-import { pipe } from 'rxjs';
-import { filter, mergeMap, tap } from 'rxjs/operators';
 import { Photo } from '../photo.model';
 import { PhotoService } from '../photos.service';
 
 const PHOTO_STATE_KEY = 'photo_search';
 
-export interface PhotoState {
+interface PhotoState {
   photos: Photo[];
   search: string;
   page: number;
@@ -31,105 +25,97 @@ const initialState: PhotoState = {
 };
 
 @Injectable()
-export class PhotoStore
-  extends ComponentStore
-  implements OnStoreInit, OnStateInit
-{
+export class PhotoStore {
   private photoService = inject(PhotoService);
 
-  private readonly photos$ = this.select((s) => s.photos);
-  private readonly search$ = this.select((s) => s.search);
-  private readonly page$ = this.select((s) => s.page);
-  private readonly pages$ = this.select((s) => s.pages);
-  private readonly error$ = this.select((s) => s.error);
-  private readonly loading$ = this.select((s) => s.loading);
+  // Convert state to signals
+  private state = signal(this.loadInitialState());
 
-  private readonly endOfPage$ = this.select(
-    this.page$,
-    this.pages$,
-    (page, pages) => page === pages,
-  );
+  // Computed values
+  readonly photos = computed(() => this.state().photos);
+  readonly search = computed(() => this.state().search);
+  readonly page = computed(() => this.state().page);
+  readonly pages = computed(() => this.state().pages);
+  readonly loading = computed(() => this.state().loading);
+  readonly error = computed(() => this.state().error);
+  readonly endOfPage = computed(() => this.page() === this.pages());
 
-  readonly vm$ = this.select(
-    {
-      photos: this.photos$,
-      search: this.search$,
-      page: this.page$,
-      pages: this.pages$,
-      endOfPage: this.endOfPage$,
-      loading: this.loading$,
-      error: this.error$,
-    },
-    { debounce: true },
-  );
+  constructor() {
+    // Initialize search on startup
+    this.searchPhotos();
+  }
 
-  ngrxOnStoreInit() {
+  private loadInitialState(): PhotoState {
     const savedJSONState = localStorage.getItem(PHOTO_STATE_KEY);
-    if (savedJSONState === null) {
-      this.setState(initialState);
-    } else {
-      const savedState = JSON.parse(savedJSONState);
-      this.setState({
-        ...initialState,
-        search: savedState.search,
-        page: savedState.page,
-      });
-    }
-  }
+    if (!savedJSONState) return initialState;
 
-  ngrxOnStateInit() {
-    this.searchPhotos(
-      this.select({
-        search: this.search$,
-        page: this.page$,
-      }),
-    );
+    const savedState = JSON.parse(savedJSONState);
+    return {
+      ...initialState,
+      search: savedState.search,
+      page: savedState.page,
+    };
   }
 
-  readonly search = this.updater(
-    (state, search: string): PhotoState => ({
+  updateSearch(search: string) {
+    this.state.update((state) => ({
       ...state,
       search,
       page: 1,
-    }),
-  );
+    }));
+    this.searchPhotos();
+  }
 
-  readonly nextPage = this.updater(
-    (state): PhotoState => ({
+  nextPage() {
+    if (this.endOfPage()) return;
+    this.state.update((state) => ({
       ...state,
       page: state.page + 1,
-    }),
-  );
+    }));
+    this.searchPhotos();
+  }
 
-  readonly previousPage = this.updater(
-    (state): PhotoState => ({
+  previousPage() {
+    if (this.page() === 1) return;
+    this.state.update((state) => ({
       ...state,
       page: state.page - 1,
-    }),
-  );
+    }));
+    this.searchPhotos();
+  }
+
+  private searchPhotos() {
+    const { search, page } = this.state();
+    if (search.length < 3) return;
 
-  readonly searchPhotos = this.effect<{ search: string; page: number }>(
-    pipe(
-      filter(({ search }) => search.length >= 3),
-      tap(() => this.patchState({ loading: true, error: '' })),
-      mergeMap(({ search, page }) =>
-        this.photoService.searchPublicPhotos(search, page).pipe(
-          tapResponse(
-            ({ photos: { photo, pages } }) => {
-              this.patchState({
-                loading: false,
-                photos: photo,
-                pages,
-              });
-              localStorage.setItem(
-                PHOTO_STATE_KEY,
-                JSON.stringify({ search, page }),
-              );
-            },
-            (error: unknown) => this.patchState({ error, loading: false }),
-          ),
+    this.state.update((state) => ({ ...state, loading: true, error: '' }));
+
+    this.photoService
+      .searchPublicPhotos(search, page)
+      .pipe(
+        takeUntilDestroyed(),
+        tapResponse(
+          ({ photos: { photo, pages } }) => {
+            this.state.update((state) => ({
+              ...state,
+              loading: false,
+              photos: photo,
+              pages,
+            }));
+            localStorage.setItem(
+              PHOTO_STATE_KEY,
+              JSON.stringify({ search, page }),
+            );
+          },
+          (error: unknown) => {
+            this.state.update((state) => ({
+              ...state,
+              error,
+              loading: false,
+            }));
+          },
         ),
-      ),
-    ),
-  );
+      )
+      .subscribe();
+  }
 }
diff --git a/apps/signal/43-signal-input/src/app/app.component.ts b/apps/signal/43-signal-input/src/app/app.component.ts
index 5c2deb684..3709abe87 100644
--- a/apps/signal/43-signal-input/src/app/app.component.ts
+++ b/apps/signal/43-signal-input/src/app/app.component.ts
@@ -2,40 +2,55 @@ import { Component } from '@angular/core';
 import { UserComponent } from './user.component';
 
 @Component({
+  standalone: true,
   imports: [UserComponent],
   selector: 'app-root',
   template: `
-    
-      
-        Name:
-        
-        @if (showUser && !name.value) {
-          
name required
-        }
-      
-      
-        LastName:
-        
-      
-      
-        Age:
-        
-      
-      
+    
+      
+        Tennis Player Registration
+      
+
+      
+
+      @if (showUser && !!name.value) {
+        
+      }
     
-    @if (showUser && !!name.value) {
-      
-    }
   `,
   host: {
-    class: 'p-10 block flex flex-col gap-10',
+    class: 'block p-6 min-h-screen bg-gray-50',
   },
 })
 export class AppComponent {
diff --git a/apps/signal/43-signal-input/src/app/user.component.ts b/apps/signal/43-signal-input/src/app/user.component.ts
index 908f952c3..b5b9eb6bb 100644
--- a/apps/signal/43-signal-input/src/app/user.component.ts
+++ b/apps/signal/43-signal-input/src/app/user.component.ts
@@ -2,8 +2,8 @@ import { TitleCasePipe } from '@angular/common';
 import {
   ChangeDetectionStrategy,
   Component,
-  Input,
-  OnChanges,
+  computed,
+  input,
 } from '@angular/core';
 
 type Category = 'Youth' | 'Junior' | 'Open' | 'Senior';
@@ -16,25 +16,21 @@ const ageToCategory = (age: number): Category => {
 
 @Component({
   selector: 'app-user',
+  standalone: true,
   imports: [TitleCasePipe],
   template: `
-    {{ fullName | titlecase }} plays tennis in the {{ category }} category!!
+    {{ fullName() | titlecase }} plays tennis in the {{ category() }} category!!
   `,
   host: {
     class: 'text-xl text-green-800',
   },
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class UserComponent implements OnChanges {
-  @Input({ required: true }) name!: string;
-  @Input() lastName?: string;
-  @Input() age?: string;
+export class UserComponent {
+  name = input.required
();
+  lastName = input('');
+  age = input('0');
 
-  fullName = '';
-  category: Category = 'Junior';
-
-  ngOnChanges(): void {
-    this.fullName = `${this.name} ${this.lastName ?? ''}`;
-    this.category = ageToCategory(Number(this.age) ?? 0);
-  }
+  category = computed(() => ageToCategory(Number(this.age())));
+  fullName = computed(() => `${this.name()} ${this.lastName()}`);
 }
diff --git a/apps/signal/43-signal-input/src/styles.scss b/apps/signal/43-signal-input/src/styles.scss
index 77e408aa8..4fc952a59 100644
--- a/apps/signal/43-signal-input/src/styles.scss
+++ b/apps/signal/43-signal-input/src/styles.scss
@@ -3,3 +3,31 @@
 @tailwind utilities;
 
 /* You can add global styles to this file, and also import other style files */
+
+@layer components {
+  .form-input {
+    @apply mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 focus:border-blue-500 focus:ring-1 focus:ring-blue-500;
+  }
+
+  .form-label {
+    @apply block text-sm font-medium text-gray-700;
+  }
+
+  .form-group {
+    @apply space-y-1;
+  }
+
+  .btn-primary {
+    @apply w-full rounded-md bg-blue-600 px-4 py-2 text-white font-medium
+           hover:bg-blue-700 focus:outline-none focus:ring-2
+           focus:ring-blue-500 focus:ring-offset-2 transition-colors;
+  }
+
+  .card {
+    @apply max-w-md mx-auto bg-white p-8 rounded-lg shadow-md;
+  }
+
+  .success-box {
+    @apply mt-8 p-4 bg-green-50 rounded-md border border-green-200;
+  }
+}
diff --git a/apps/signal/50-bug-in-effect/src/app/app.component.ts b/apps/signal/50-bug-in-effect/src/app/app.component.ts
index ec6ba09b0..688dd4adc 100644
--- a/apps/signal/50-bug-in-effect/src/app/app.component.ts
+++ b/apps/signal/50-bug-in-effect/src/app/app.component.ts
@@ -1,37 +1,62 @@
 import {
   ChangeDetectionStrategy,
   Component,
+  computed,
   effect,
   model,
 } from '@angular/core';
 import { FormsModule } from '@angular/forms';
 
 @Component({
+  standalone: true,
   imports: [FormsModule],
   selector: 'app-root',
   template: `
-    
+    
   `,
+  host: {
+    class: 'block p-6 min-h-screen bg-gray-50',
+  },
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class AppComponent {
@@ -39,14 +64,31 @@ export class AppComponent {
   ram = model(false);
   gpu = model(false);
 
+  totalExtras = computed(() => {
+    return Number(this.drive()) + Number(this.ram()) + Number(this.gpu());
+  });
+
   constructor() {
-    /* 
-      Explain for your junior team mate why this bug occurs ...
-    */
+    // Fix: Track each signal independently
     effect(() => {
-      if (this.drive() || this.ram() || this.gpu()) {
+      // Force effect to track each signal individually
+      const hasExtras = [this.drive(), this.ram(), this.gpu()].some(Boolean);
+      if (hasExtras) {
         alert('Price increased!');
       }
     });
+
+    /* Bug explanation for junior dev:
+    The original implementation had a logical OR operation (||) which short-circuits.
+    This means if this.drive() is true, the other signals (ram, gpu) aren't tracked
+    by the effect because JavaScript stops evaluating the OR expression after finding
+    the first true value.
+
+    To fix this, we need to ensure all signals are tracked by the effect system.
+    We can do this by either:
+    1. Evaluating each signal separately before the logical operation
+    2. Using an array and some() method (current solution)
+    3. Using a computed signal (bonus solution)
+    */
   }
 }
diff --git a/apps/signal/50-bug-in-effect/src/styles.scss b/apps/signal/50-bug-in-effect/src/styles.scss
index 77e408aa8..f4462a483 100644
--- a/apps/signal/50-bug-in-effect/src/styles.scss
+++ b/apps/signal/50-bug-in-effect/src/styles.scss
@@ -3,3 +3,46 @@
 @tailwind utilities;
 
 /* You can add global styles to this file, and also import other style files */
+
+@layer components {
+  .card {
+    @apply max-w-md mx-auto bg-white p-8 rounded-lg shadow-md;
+  }
+
+  .product-header {
+    @apply flex justify-between items-center mb-6 pb-4 border-b border-gray-200;
+  }
+
+  .product-name {
+    @apply text-2xl font-bold text-gray-800;
+  }
+
+  .product-price {
+    @apply text-xl font-semibold text-blue-600;
+  }
+
+  .extras-section {
+    @apply space-y-4;
+  }
+
+  .extras-title {
+    @apply text-lg font-medium text-gray-700 mb-3;
+  }
+
+  .checkbox-group {
+    @apply flex items-center space-x-3 py-2 px-4 rounded-md hover:bg-gray-50 transition-colors;
+  }
+
+  .checkbox-input {
+    @apply w-4 h-4 text-blue-600 rounded border-gray-300
+           focus:ring-blue-500 focus:ring-2;
+  }
+
+  .checkbox-label {
+    @apply text-gray-700;
+  }
+
+  .total-extras {
+    @apply mt-6 pt-4 border-t border-gray-200 text-right text-sm text-gray-600;
+  }
+}
diff --git a/apps/signal/51-function-call-effect/src/app/action.component.ts b/apps/signal/51-function-call-effect/src/app/action.component.ts
index 22e0e7a4f..a3f7ecdc3 100644
--- a/apps/signal/51-function-call-effect/src/app/action.component.ts
+++ b/apps/signal/51-function-call-effect/src/app/action.component.ts
@@ -9,36 +9,43 @@ import { FormsModule } from '@angular/forms';
 import { UserService } from './user.service';
 
 @Component({
+  standalone: true,
   imports: [FormsModule],
   selector: 'app-actions',
   template: `
-    
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class ActionsComponent {
   private userService = inject(UserService);
-  protected action = signal(undefined);
-
+  protected action = signal(undefined);
   protected actions = ['Create', 'Read', 'Update', 'Delete'];
 
   constructor() {
     effect(() => {
-      this.userService.log(this.action() ?? 'No action selected');
+      const currentAction = this.action();
+      const currentUser = this.userService.name();
+
+      if (currentAction) {
+        console.log(`${currentUser}: ${currentAction}`);
+      }
     });
   }
 }
diff --git a/apps/signal/51-function-call-effect/src/app/app.component.ts b/apps/signal/51-function-call-effect/src/app/app.component.ts
index 5f9342eda..e1a312b28 100644
--- a/apps/signal/51-function-call-effect/src/app/app.component.ts
+++ b/apps/signal/51-function-call-effect/src/app/app.component.ts
@@ -4,22 +4,20 @@ import { ActionsComponent } from './action.component';
 import { UserService } from './user.service';
 
 @Component({
+  standalone: true,
   imports: [FormsModule, ActionsComponent],
   selector: 'app-root',
   template: `
-