diff --git a/talent-management/src/app/routes/dashboard/dashboard.html b/talent-management/src/app/routes/dashboard/dashboard.html index 05e3f4f..ab217ca 100644 --- a/talent-management/src/app/routes/dashboard/dashboard.html +++ b/talent-management/src/app/routes/dashboard/dashboard.html @@ -1,34 +1,5 @@ - - - - smart_toy - AI Workforce Insights - Generated from live dashboard metrics - - - - -
- - Analyzing workforce data… -
- - -

- {{ aiInsight }} -

- - -

- warning - {{ aiInsightError }} -

- -
-
-
diff --git a/talent-management/src/app/routes/dashboard/dashboard.ts b/talent-management/src/app/routes/dashboard/dashboard.ts index 00b5c37..02280ce 100644 --- a/talent-management/src/app/routes/dashboard/dashboard.ts +++ b/talent-management/src/app/routes/dashboard/dashboard.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, inject } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { Router } from '@angular/router'; import { CommonModule } from '@angular/common'; import { MatCardModule } from '@angular/material/card'; @@ -9,13 +9,10 @@ import { MatListModule } from '@angular/material/list'; import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; import { BaseChartDirective } from 'ng2-charts'; import { ChartConfiguration } from 'chart.js'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { PageHeader } from '@shared'; import { HasRoleDirective } from '../../shared/directives/has-role.directive'; import { DashboardMetrics, DepartmentMetric, PositionMetric, SalaryRangeMetric } from '../../models'; -import { DashboardService, AiService } from '../../services/api'; -import { environment } from '../../../environments/environment'; +import { DashboardService } from '../../services/api'; @Component({ selector: 'app-dashboard', @@ -35,12 +32,10 @@ import { environment } from '../../../environments/environment'; HasRoleDirective, ], }) -export class Dashboard implements OnInit, OnDestroy { +export class Dashboard implements OnInit { private dashboardService = inject(DashboardService); - private aiService = inject(AiService); private router = inject(Router); private snackBar = inject(MatSnackBar); - private destroy$ = new Subject(); // Loading state loading = true; @@ -48,12 +43,6 @@ export class Dashboard implements OnInit, OnDestroy { // Dashboard metrics metrics: DashboardMetrics | null = null; - // AI Insights state - aiEnabled = environment.aiEnabled; - aiInsight = ''; - aiInsightLoading = false; - aiInsightError = ''; - // Chart configurations departmentChartData: ChartConfiguration<'pie'>['data'] | null = null; departmentChartOptions: ChartConfiguration<'pie'>['options'] = { @@ -152,11 +141,6 @@ export class Dashboard implements OnInit, OnDestroy { this.loadDashboardMetrics(); } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - loadDashboardMetrics(): void { this.loading = true; @@ -165,9 +149,6 @@ export class Dashboard implements OnInit, OnDestroy { this.metrics = metrics; this.prepareCharts(metrics); this.loading = false; - if (this.aiEnabled) { - this.loadAiInsight(metrics); - } }, error: error => { console.error('Error loading dashboard metrics:', error); @@ -177,34 +158,6 @@ export class Dashboard implements OnInit, OnDestroy { }); } - private loadAiInsight(metrics: DashboardMetrics): void { - this.aiInsightLoading = true; - this.aiInsightError = ''; - this.aiInsight = ''; - - const systemPrompt = `You are an HR analytics assistant. Analyze the following workforce metrics and provide a concise executive summary (3-4 sentences) highlighting key observations, any notable patterns, and one actionable recommendation. Be specific — reference the actual numbers. - -Workforce Metrics: -${JSON.stringify(metrics, null, 2)}`; - - const question = 'Provide a brief executive summary of the current workforce.'; - - this.aiService - .chat(question, systemPrompt) - .pipe(takeUntil(this.destroy$)) - .subscribe({ - next: response => { - this.aiInsight = response.reply; - this.aiInsightLoading = false; - }, - error: err => { - this.aiInsightError = - err?.error?.detail ?? 'AI insights unavailable. Is the API running with AiEnabled: true?'; - this.aiInsightLoading = false; - }, - }); - } - private prepareCharts(metrics: DashboardMetrics): void { this.prepareDepartmentChart(metrics.employeesByDepartment); this.preparePositionChart(metrics.employeesByPosition); diff --git a/talent-management/src/app/routes/employees/employee-list.component.html b/talent-management/src/app/routes/employees/employee-list.component.html index 3cc62cf..9cfaa0c 100644 --- a/talent-management/src/app/routes/employees/employee-list.component.html +++ b/talent-management/src/app/routes/employees/employee-list.component.html @@ -1,41 +1,5 @@ - - - - - - smart_toy - Search in plain English - - - search - - - - -
- auto_awesome - AI interpreted: {{ nlParsedExpression }} -
- -
- warning - {{ nlError }} -
-
-
- Employee Directory diff --git a/talent-management/src/app/routes/employees/employee-list.component.ts b/talent-management/src/app/routes/employees/employee-list.component.ts index c8a8475..658f89c 100644 --- a/talent-management/src/app/routes/employees/employee-list.component.ts +++ b/talent-management/src/app/routes/employees/employee-list.component.ts @@ -18,12 +18,10 @@ import { PageHeader } from '@shared/components/page-header/page-header'; import { ConfirmDialogComponent, ConfirmDialogData } from '../../shared/components/confirm-dialog/confirm-dialog'; import { Employee } from '../../models'; import { EmployeeService } from '../../services/api'; -import { AiService, NlEmployeeFilter } from '../../services/api/ai.service'; import { OidcAuthService } from '../../core/authentication/oidc-auth.service'; import { HasRoleDirective } from '../../shared/directives/has-role.directive'; import { Observable, Subject, of } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap, map, startWith, catchError, takeUntil } from 'rxjs/operators'; -import { environment } from '../../../environments/environment'; @Component({ selector: 'app-employee-list', @@ -51,7 +49,6 @@ import { environment } from '../../../environments/environment'; }) export class EmployeeListComponent implements OnInit, OnDestroy { private employeeService = inject(EmployeeService); - private aiService = inject(AiService); private authService = inject(OidcAuthService); private router = inject(Router); private fb = inject(FormBuilder); @@ -75,14 +72,6 @@ export class EmployeeListComponent implements OnInit, OnDestroy { filteredEmails$!: Observable; filteredPositionTitles$!: Observable; - // AI Natural Language Search - aiEnabled = environment.aiEnabled; - nlQuery = ''; - nlLoading = false; - nlError = ''; - nlParsedExpression = ''; - private nlSearch$ = new Subject(); - private destroy$ = new Subject(); // Table columns @@ -100,7 +89,6 @@ export class EmployeeListComponent implements OnInit, OnDestroy { this.setupAutocomplete(); this.setupAutoSubmit(); this.loadEmployees(); - this.setupNlSearch(); } ngOnDestroy(): void { @@ -318,61 +306,4 @@ export class EmployeeListComponent implements OnInit, OnDestroy { return this.authService.isHRAdmin() || this.authService.isManager(); } - // NL Search --------------------------------------------------------------- - - onNlQueryChange(value: string): void { - this.nlQuery = value; - this.nlSearch$.next(value); - } - - clearNlSearch(): void { - this.nlQuery = ''; - this.nlParsedExpression = ''; - this.nlError = ''; - this.searchForm.reset(); - this.pageNumber = 1; - this.loadEmployees(); - } - - private setupNlSearch(): void { - this.nlSearch$ - .pipe( - debounceTime(600), - distinctUntilChanged(), - switchMap(query => { - if (!query || query.length < 3) { - this.nlParsedExpression = ''; - this.nlError = ''; - return of(null); - } - this.nlLoading = true; - this.nlError = ''; - return this.aiService.nlEmployeeSearch(query).pipe( - catchError(err => { - this.nlLoading = false; - this.nlError = err?.error?.detail ?? 'Could not parse query. Try rephrasing.'; - return of(null); - }) - ); - }), - takeUntil(this.destroy$) - ) - .subscribe(filter => { - if (filter) { - this.nlLoading = false; - this.nlParsedExpression = filter.parsedExpression; - this.applyNlFilter(filter); - } - }); - } - - private applyNlFilter(filter: NlEmployeeFilter): void { - this.searchForm.patchValue({ - FirstName: filter.firstName, - LastName: filter.lastName, - Email: filter.email, - EmployeeNumber: filter.employeeNumber, - PositionTitle: filter.positionTitle, - }); - } } diff --git a/talent-management/src/app/routes/positions/position-list.component.html b/talent-management/src/app/routes/positions/position-list.component.html index 8c82bb2..a755d23 100644 --- a/talent-management/src/app/routes/positions/position-list.component.html +++ b/talent-management/src/app/routes/positions/position-list.component.html @@ -1,74 +1,5 @@ - - - - - auto_awesome - Semantic Position Search - - - - - Describe the position you're looking for… - - search - - -
- - Searching… -
- -
{{ semanticError }}
- -
-
- {{ semanticResults.length }} result(s) found - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Score{{ r.score | number:'1.3-3' }}Position #{{ r.positionNumber }}Title{{ r.positionTitle }}Department{{ r.departmentName }}Salary Range{{ r.salaryRangeName }}
-
-
-
- Positions diff --git a/talent-management/src/app/routes/positions/position-list.component.ts b/talent-management/src/app/routes/positions/position-list.component.ts index 081937d..934e77b 100644 --- a/talent-management/src/app/routes/positions/position-list.component.ts +++ b/talent-management/src/app/routes/positions/position-list.component.ts @@ -18,10 +18,9 @@ import { MatSort, MatSortModule, Sort } from '@angular/material/sort'; import { PageHeader } from '@shared/components/page-header/page-header'; import { HasRoleDirective } from '../../shared/directives/has-role.directive'; import { Position, PagedResponse, QueryParams } from '../../models'; -import { PositionService, AiService, SemanticPositionResult } from '../../services/api'; +import { PositionService } from '../../services/api'; import { OidcAuthService } from '../../core/authentication/oidc-auth.service'; -import { environment } from '../../../environments/environment'; -import { debounceTime, Subject, switchMap, catchError, of, takeUntil } from 'rxjs'; +import { debounceTime, Subject } from 'rxjs'; @Component({ selector: 'app-position-list', @@ -49,7 +48,6 @@ import { debounceTime, Subject, switchMap, catchError, of, takeUntil } from 'rxj }) export class PositionListComponent implements OnInit, AfterViewInit, OnDestroy { private positionService = inject(PositionService); - private aiService = inject(AiService); private authService = inject(OidcAuthService); private router = inject(Router); private dialog = inject(MatDialog); @@ -61,14 +59,6 @@ export class PositionListComponent implements OnInit, AfterViewInit, OnDestroy { @ViewChild(MatSort) sort!: MatSort; positions: Position[] = []; - - // Semantic search - aiEnabled = environment.aiEnabled; - semanticQuery = ''; - semanticLoading = false; - semanticError = ''; - semanticResults: SemanticPositionResult[] | null = null; - private semanticSearch$ = new Subject(); loading = false; displayedColumns: string[] = ['positionNumber', 'positionTitle', 'departmentId', 'salaryRangeId', 'actions']; @@ -90,7 +80,6 @@ export class PositionListComponent implements OnInit, AfterViewInit, OnDestroy { this.loadPositions(); }); - this.setupSemanticSearch(); this.loadPositions(); } @@ -103,46 +92,6 @@ export class PositionListComponent implements OnInit, AfterViewInit, OnDestroy { this.destroy$.complete(); } - private setupSemanticSearch(): void { - this.semanticSearch$ - .pipe( - debounceTime(600), - switchMap(query => { - if (!query || query.trim().length < 3) { - this.semanticResults = null; - this.semanticError = ''; - return of(null); - } - this.semanticLoading = true; - this.semanticError = ''; - return this.aiService.semanticPositionSearch(query.trim()).pipe( - catchError(err => { - this.semanticError = err?.error?.detail ?? 'Semantic search failed. Please try again.'; - return of(null); - }) - ); - }), - takeUntil(this.destroy$) - ) - .subscribe(results => { - this.semanticLoading = false; - if (results !== null) { - this.semanticResults = results; - } - }); - } - - onSemanticQueryChange(value: string): void { - this.semanticQuery = value; - this.semanticSearch$.next(value); - } - - clearSemanticSearch(): void { - this.semanticQuery = ''; - this.semanticResults = null; - this.semanticError = ''; - } - loadPositions(): void { this.loading = true;