@memberjunction/ng-dashboards 2.48.0 → 2.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +105 -2
  2. package/dist/AI/ai-dashboard.component.d.ts +2 -0
  3. package/dist/AI/ai-dashboard.component.d.ts.map +1 -1
  4. package/dist/AI/ai-dashboard.component.js +66 -43
  5. package/dist/AI/ai-dashboard.component.js.map +1 -1
  6. package/dist/AI/components/agents/agent-configuration.component.js +45 -58
  7. package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
  8. package/dist/AI/components/agents/agent-editor.component.d.ts +6 -1
  9. package/dist/AI/components/agents/agent-editor.component.d.ts.map +1 -1
  10. package/dist/AI/components/agents/agent-editor.component.js +368 -366
  11. package/dist/AI/components/agents/agent-editor.component.js.map +1 -1
  12. package/dist/AI/components/agents/agent-filter-panel.component.js +83 -85
  13. package/dist/AI/components/agents/agent-filter-panel.component.js.map +1 -1
  14. package/dist/AI/components/charts/performance-heatmap.component.d.ts +66 -0
  15. package/dist/AI/components/charts/performance-heatmap.component.d.ts.map +1 -0
  16. package/dist/AI/components/charts/performance-heatmap.component.js +428 -0
  17. package/dist/AI/components/charts/performance-heatmap.component.js.map +1 -0
  18. package/dist/AI/components/charts/time-series-chart.component.d.ts +66 -0
  19. package/dist/AI/components/charts/time-series-chart.component.d.ts.map +1 -0
  20. package/dist/AI/components/charts/time-series-chart.component.js +547 -0
  21. package/dist/AI/components/charts/time-series-chart.component.js.map +1 -0
  22. package/dist/AI/components/execution-monitoring.component.d.ts +157 -5
  23. package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
  24. package/dist/AI/components/execution-monitoring.component.js +2032 -20
  25. package/dist/AI/components/execution-monitoring.component.js.map +1 -1
  26. package/dist/AI/components/models/model-management.component.js +211 -237
  27. package/dist/AI/components/models/model-management.component.js.map +1 -1
  28. package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +208 -226
  29. package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js.map +1 -1
  30. package/dist/AI/components/prompts/prompt-filter-panel.component.js +97 -99
  31. package/dist/AI/components/prompts/prompt-filter-panel.component.js.map +1 -1
  32. package/dist/AI/components/prompts/prompt-management.component.js +381 -424
  33. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
  34. package/dist/AI/components/prompts/prompt-version-control.component.js +173 -191
  35. package/dist/AI/components/prompts/prompt-version-control.component.js.map +1 -1
  36. package/dist/AI/components/system/system-config-filter-panel.component.js +85 -87
  37. package/dist/AI/components/system/system-config-filter-panel.component.js.map +1 -1
  38. package/dist/AI/components/system/system-configuration.component.js +86 -99
  39. package/dist/AI/components/system/system-configuration.component.js.map +1 -1
  40. package/dist/AI/components/widgets/kpi-card.component.d.ts +25 -0
  41. package/dist/AI/components/widgets/kpi-card.component.d.ts.map +1 -0
  42. package/dist/AI/components/widgets/kpi-card.component.js +163 -0
  43. package/dist/AI/components/widgets/kpi-card.component.js.map +1 -0
  44. package/dist/AI/components/widgets/live-execution-widget.component.d.ts +25 -0
  45. package/dist/AI/components/widgets/live-execution-widget.component.d.ts.map +1 -0
  46. package/dist/AI/components/widgets/live-execution-widget.component.js +298 -0
  47. package/dist/AI/components/widgets/live-execution-widget.component.js.map +1 -0
  48. package/dist/AI/index.d.ts +7 -0
  49. package/dist/AI/index.d.ts.map +1 -0
  50. package/dist/AI/index.js +9 -0
  51. package/dist/AI/index.js.map +1 -0
  52. package/dist/AI/services/ai-instrumentation.service.d.ts +109 -0
  53. package/dist/AI/services/ai-instrumentation.service.d.ts.map +1 -0
  54. package/dist/AI/services/ai-instrumentation.service.js +490 -0
  55. package/dist/AI/services/ai-instrumentation.service.js.map +1 -0
  56. package/dist/Actions/actions-management-dashboard.component.js +40 -41
  57. package/dist/Actions/actions-management-dashboard.component.js.map +1 -1
  58. package/dist/Actions/components/actions-list-view.component.js +117 -134
  59. package/dist/Actions/components/actions-list-view.component.js.map +1 -1
  60. package/dist/Actions/components/actions-overview.component.js +274 -296
  61. package/dist/Actions/components/actions-overview.component.js.map +1 -1
  62. package/dist/Actions/components/categories-list-view.component.js +12 -14
  63. package/dist/Actions/components/categories-list-view.component.js.map +1 -1
  64. package/dist/Actions/components/code-management.component.js +12 -14
  65. package/dist/Actions/components/code-management.component.js.map +1 -1
  66. package/dist/Actions/components/entity-integration.component.js +12 -14
  67. package/dist/Actions/components/entity-integration.component.js.map +1 -1
  68. package/dist/Actions/components/execution-monitoring.component.js +238 -256
  69. package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
  70. package/dist/Actions/components/executions-list-view.component.js +12 -14
  71. package/dist/Actions/components/executions-list-view.component.js.map +1 -1
  72. package/dist/Actions/components/scheduled-actions.component.js +12 -14
  73. package/dist/Actions/components/scheduled-actions.component.js.map +1 -1
  74. package/dist/Actions/components/security-permissions.component.js +12 -14
  75. package/dist/Actions/components/security-permissions.component.js.map +1 -1
  76. package/dist/EntityAdmin/components/entity-details.component.js +105 -107
  77. package/dist/EntityAdmin/components/entity-details.component.js.map +1 -1
  78. package/dist/EntityAdmin/components/entity-filter-panel.component.js +100 -102
  79. package/dist/EntityAdmin/components/entity-filter-panel.component.js.map +1 -1
  80. package/dist/EntityAdmin/components/erd-composite.component.js +84 -100
  81. package/dist/EntityAdmin/components/erd-composite.component.js.map +1 -1
  82. package/dist/EntityAdmin/components/erd-diagram.component.js +50 -50
  83. package/dist/EntityAdmin/components/erd-diagram.component.js.map +1 -1
  84. package/dist/EntityAdmin/entity-admin-dashboard.component.js +45 -49
  85. package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
  86. package/dist/generic/base-dashboard.js +28 -40
  87. package/dist/generic/base-dashboard.js.map +1 -1
  88. package/dist/module.d.ts +16 -12
  89. package/dist/module.d.ts.map +1 -1
  90. package/dist/module.js +36 -15
  91. package/dist/module.js.map +1 -1
  92. package/package.json +6 -6
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { Component, Output, EventEmitter } from '@angular/core';
11
2
  import { RunView, Metadata, LogError, LogStatus } from '@memberjunction/core';
12
3
  import { Subject, combineLatest, BehaviorSubject } from 'rxjs';
@@ -274,50 +265,48 @@ function ModelManagementComponent_Conditional_17_Template(rf, ctx) { if (rf & 1)
274
265
  i0.ɵɵconditional(ctx_r0.filteredModels.length === 0 ? 46 : 47);
275
266
  } }
276
267
  export class ModelManagementComponent {
277
- constructor() {
278
- this.openEntityRecord = new EventEmitter();
279
- // Data properties
280
- this.models = [];
281
- this.vendors = [];
282
- this.modelTypes = [];
283
- this.filteredModels = [];
284
- // UI state
285
- this.isLoading = false;
286
- this.loadingMessage = 'Loading AI models...';
287
- this.error = null;
288
- this.filtersVisible = false;
289
- // Filter state
290
- this.filter = {
291
- searchTerm: '',
292
- vendorId: null,
293
- modelTypeId: null,
294
- powerRankMin: null,
295
- powerRankMax: null,
296
- speedRankMin: null,
297
- speedRankMax: null,
298
- costRankMin: null,
299
- costRankMax: null,
300
- isActive: null
301
- };
302
- // Sort options
303
- this.sortOptions = [
304
- { field: 'Name', direction: 'asc', label: 'Name (A-Z)' },
305
- { field: 'Name', direction: 'desc', label: 'Name (Z-A)' },
306
- { field: 'PowerRank', direction: 'desc', label: 'Power Rank (High to Low)' },
307
- { field: 'PowerRank', direction: 'asc', label: 'Power Rank (Low to High)' },
308
- { field: 'SpeedRank', direction: 'desc', label: 'Speed Rank (High to Low)' },
309
- { field: 'SpeedRank', direction: 'asc', label: 'Speed Rank (Low to High)' },
310
- { field: 'CostRank', direction: 'desc', label: 'Cost Rank (High to Low)' },
311
- { field: 'CostRank', direction: 'asc', label: 'Cost Rank (Low to High)' },
312
- { field: '__mj_CreatedAt', direction: 'desc', label: 'Recently Added' },
313
- { field: '__mj_UpdatedAt', direction: 'desc', label: 'Recently Updated' }
314
- ];
315
- this.selectedSort = this.sortOptions[0];
316
- // Search and filter subjects
317
- this.searchSubject = new BehaviorSubject('');
318
- this.filterSubject = new BehaviorSubject(this.filter);
319
- this.destroy$ = new Subject();
320
- }
268
+ openEntityRecord = new EventEmitter();
269
+ // Data properties
270
+ models = [];
271
+ vendors = [];
272
+ modelTypes = [];
273
+ filteredModels = [];
274
+ // UI state
275
+ isLoading = false;
276
+ loadingMessage = 'Loading AI models...';
277
+ error = null;
278
+ filtersVisible = false;
279
+ // Filter state
280
+ filter = {
281
+ searchTerm: '',
282
+ vendorId: null,
283
+ modelTypeId: null,
284
+ powerRankMin: null,
285
+ powerRankMax: null,
286
+ speedRankMin: null,
287
+ speedRankMax: null,
288
+ costRankMin: null,
289
+ costRankMax: null,
290
+ isActive: null
291
+ };
292
+ // Sort options
293
+ sortOptions = [
294
+ { field: 'Name', direction: 'asc', label: 'Name (A-Z)' },
295
+ { field: 'Name', direction: 'desc', label: 'Name (Z-A)' },
296
+ { field: 'PowerRank', direction: 'desc', label: 'Power Rank (High to Low)' },
297
+ { field: 'PowerRank', direction: 'asc', label: 'Power Rank (Low to High)' },
298
+ { field: 'SpeedRank', direction: 'desc', label: 'Speed Rank (High to Low)' },
299
+ { field: 'SpeedRank', direction: 'asc', label: 'Speed Rank (Low to High)' },
300
+ { field: 'CostRank', direction: 'desc', label: 'Cost Rank (High to Low)' },
301
+ { field: 'CostRank', direction: 'asc', label: 'Cost Rank (Low to High)' },
302
+ { field: '__mj_CreatedAt', direction: 'desc', label: 'Recently Added' },
303
+ { field: '__mj_UpdatedAt', direction: 'desc', label: 'Recently Updated' }
304
+ ];
305
+ selectedSort = this.sortOptions[0];
306
+ // Search and filter subjects
307
+ searchSubject = new BehaviorSubject('');
308
+ filterSubject = new BehaviorSubject(this.filter);
309
+ destroy$ = new Subject();
321
310
  ngOnInit() {
322
311
  this.setupFilterSubscriptions();
323
312
  this.loadData();
@@ -340,119 +329,111 @@ export class ModelManagementComponent {
340
329
  this.applyFilters();
341
330
  });
342
331
  }
343
- loadData() {
344
- return __awaiter(this, void 0, void 0, function* () {
345
- try {
346
- this.isLoading = true;
347
- this.error = null;
348
- this.loadingMessage = 'Loading AI models...';
349
- // Load all required data in parallel
350
- const [modelsResult, vendorsResult, modelTypesResult] = yield Promise.all([
351
- this.loadModels(),
352
- this.loadVendors(),
353
- this.loadModelTypes()
354
- ]);
355
- if (modelsResult && vendorsResult && modelTypesResult) {
356
- this.applyFilters();
357
- LogStatus('Model management data loaded successfully');
358
- }
332
+ async loadData() {
333
+ try {
334
+ this.isLoading = true;
335
+ this.error = null;
336
+ this.loadingMessage = 'Loading AI models...';
337
+ // Load all required data in parallel
338
+ const [modelsResult, vendorsResult, modelTypesResult] = await Promise.all([
339
+ this.loadModels(),
340
+ this.loadVendors(),
341
+ this.loadModelTypes()
342
+ ]);
343
+ if (modelsResult && vendorsResult && modelTypesResult) {
344
+ this.applyFilters();
345
+ LogStatus('Model management data loaded successfully');
359
346
  }
360
- catch (error) {
361
- this.error = 'Failed to load model data. Please try again.';
362
- LogError('Error loading model management data: ' + String(error));
363
- }
364
- finally {
365
- this.isLoading = false;
366
- }
367
- });
347
+ }
348
+ catch (error) {
349
+ this.error = 'Failed to load model data. Please try again.';
350
+ LogError('Error loading model management data: ' + String(error));
351
+ }
352
+ finally {
353
+ this.isLoading = false;
354
+ }
368
355
  }
369
- loadModels() {
370
- return __awaiter(this, void 0, void 0, function* () {
371
- try {
372
- const md = Metadata.Provider;
373
- if (!md) {
374
- throw new Error('Metadata provider not available');
375
- }
376
- const rv = new RunView();
377
- const result = yield rv.RunView({
378
- EntityName: 'AI Models',
379
- ExtraFilter: '', // Load all models, we'll filter in UI
380
- OrderBy: 'Name',
381
- UserSearchString: '',
382
- OverrideExcludeFilter: '',
383
- SaveViewResults: false
384
- });
385
- if (result && result.Success && result.Results) {
386
- this.models = result.Results;
387
- return true;
388
- }
389
- else {
390
- throw new Error('Failed to load AI models');
391
- }
356
+ async loadModels() {
357
+ try {
358
+ const md = Metadata.Provider;
359
+ if (!md) {
360
+ throw new Error('Metadata provider not available');
392
361
  }
393
- catch (error) {
394
- LogError('Error loading AI models: ' + String(error));
395
- return false;
362
+ const rv = new RunView();
363
+ const result = await rv.RunView({
364
+ EntityName: 'AI Models',
365
+ ExtraFilter: '', // Load all models, we'll filter in UI
366
+ OrderBy: 'Name',
367
+ UserSearchString: '',
368
+ OverrideExcludeFilter: '',
369
+ SaveViewResults: false
370
+ });
371
+ if (result && result.Success && result.Results) {
372
+ this.models = result.Results;
373
+ return true;
396
374
  }
397
- });
375
+ else {
376
+ throw new Error('Failed to load AI models');
377
+ }
378
+ }
379
+ catch (error) {
380
+ LogError('Error loading AI models: ' + String(error));
381
+ return false;
382
+ }
398
383
  }
399
- loadVendors() {
400
- return __awaiter(this, void 0, void 0, function* () {
401
- try {
402
- const rv = new RunView();
403
- const result = yield rv.RunView({
404
- EntityName: 'MJ: AI Vendors',
405
- ExtraFilter: '',
406
- OrderBy: 'Name',
407
- UserSearchString: '',
408
- OverrideExcludeFilter: '',
409
- SaveViewResults: false
410
- });
411
- if (result && result.Success && result.Results) {
412
- this.vendors = result.Results;
413
- return true;
414
- }
415
- else {
416
- throw new Error('Failed to load AI vendors');
417
- }
384
+ async loadVendors() {
385
+ try {
386
+ const rv = new RunView();
387
+ const result = await rv.RunView({
388
+ EntityName: 'MJ: AI Vendors',
389
+ ExtraFilter: '',
390
+ OrderBy: 'Name',
391
+ UserSearchString: '',
392
+ OverrideExcludeFilter: '',
393
+ SaveViewResults: false
394
+ });
395
+ if (result && result.Success && result.Results) {
396
+ this.vendors = result.Results;
397
+ return true;
418
398
  }
419
- catch (error) {
420
- LogError('Error loading AI vendors: ' + String(error));
421
- return false;
399
+ else {
400
+ throw new Error('Failed to load AI vendors');
422
401
  }
423
- });
402
+ }
403
+ catch (error) {
404
+ LogError('Error loading AI vendors: ' + String(error));
405
+ return false;
406
+ }
424
407
  }
425
- loadModelTypes() {
426
- return __awaiter(this, void 0, void 0, function* () {
427
- try {
428
- const rv = new RunView();
429
- const result = yield rv.RunView({
430
- EntityName: 'AI Model Types',
431
- ExtraFilter: '',
432
- OrderBy: 'Name',
433
- UserSearchString: '',
434
- OverrideExcludeFilter: '',
435
- SaveViewResults: false
436
- });
437
- if (result && result.Success && result.Results) {
438
- this.modelTypes = result.Results;
439
- return true;
440
- }
441
- else {
442
- throw new Error('Failed to load AI model types');
443
- }
408
+ async loadModelTypes() {
409
+ try {
410
+ const rv = new RunView();
411
+ const result = await rv.RunView({
412
+ EntityName: 'AI Model Types',
413
+ ExtraFilter: '',
414
+ OrderBy: 'Name',
415
+ UserSearchString: '',
416
+ OverrideExcludeFilter: '',
417
+ SaveViewResults: false
418
+ });
419
+ if (result && result.Success && result.Results) {
420
+ this.modelTypes = result.Results;
421
+ return true;
444
422
  }
445
- catch (error) {
446
- LogError('Error loading AI model types: ' + String(error));
447
- return false;
423
+ else {
424
+ throw new Error('Failed to load AI model types');
448
425
  }
449
- });
426
+ }
427
+ catch (error) {
428
+ LogError('Error loading AI model types: ' + String(error));
429
+ return false;
430
+ }
450
431
  }
451
432
  onSearchChange(searchTerm) {
452
433
  this.searchSubject.next(searchTerm);
453
434
  }
454
435
  onFilterChange() {
455
- this.filterSubject.next(Object.assign({}, this.filter));
436
+ this.filterSubject.next({ ...this.filter });
456
437
  }
457
438
  onSortChange(sort) {
458
439
  this.selectedSort = sort;
@@ -482,12 +463,9 @@ export class ModelManagementComponent {
482
463
  // Apply search filter
483
464
  if (this.filter.searchTerm) {
484
465
  const searchLower = this.filter.searchTerm.toLowerCase();
485
- filtered = filtered.filter(model => {
486
- var _a, _b, _c;
487
- return ((_a = model.Name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(searchLower)) ||
488
- ((_b = model.Description) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(searchLower)) ||
489
- ((_c = this.getVendorName(model)) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes(searchLower));
490
- });
466
+ filtered = filtered.filter(model => model.Name?.toLowerCase().includes(searchLower) ||
467
+ model.Description?.toLowerCase().includes(searchLower) ||
468
+ this.getVendorName(model)?.toLowerCase().includes(searchLower));
491
469
  }
492
470
  // Apply vendor filter
493
471
  if (this.filter.vendorId) {
@@ -558,7 +536,7 @@ export class ModelManagementComponent {
558
536
  }
559
537
  getVendorName(model) {
560
538
  const vendor = this.getVendor(model);
561
- return (vendor === null || vendor === void 0 ? void 0 : vendor.Name) || null;
539
+ return vendor?.Name || null;
562
540
  }
563
541
  getModelType(model) {
564
542
  return this.modelTypes.find(mt => mt.ID === model.AIModelTypeID) || null;
@@ -574,91 +552,87 @@ export class ModelManagementComponent {
574
552
  recordId: model.ID
575
553
  });
576
554
  }
577
- createNewModel() {
578
- return __awaiter(this, void 0, void 0, function* () {
579
- try {
580
- const md = Metadata.Provider;
581
- if (!md) {
582
- throw new Error('Metadata provider not available');
583
- }
584
- const newModel = yield md.GetEntityObject('AI Models', md.CurrentUser);
585
- // Pre-populate some defaults
586
- newModel.IsActive = true;
587
- newModel.PowerRank = 5;
588
- newModel.SpeedRank = 5;
589
- newModel.CostRank = 5;
590
- this.openEntityRecord.emit({
591
- entityName: 'AI Models',
592
- recordId: 'new'
593
- });
555
+ async createNewModel() {
556
+ try {
557
+ const md = Metadata.Provider;
558
+ if (!md) {
559
+ throw new Error('Metadata provider not available');
594
560
  }
595
- catch (error) {
596
- LogError('Error creating new AI model: ' + String(error));
597
- }
598
- });
561
+ const newModel = await md.GetEntityObject('AI Models', md.CurrentUser);
562
+ // Pre-populate some defaults
563
+ newModel.IsActive = true;
564
+ newModel.PowerRank = 5;
565
+ newModel.SpeedRank = 5;
566
+ newModel.CostRank = 5;
567
+ this.openEntityRecord.emit({
568
+ entityName: 'AI Models',
569
+ recordId: 'new'
570
+ });
571
+ }
572
+ catch (error) {
573
+ LogError('Error creating new AI model: ' + String(error));
574
+ }
599
575
  }
600
- toggleModelStatus(model) {
601
- return __awaiter(this, void 0, void 0, function* () {
602
- try {
603
- const md = Metadata.Provider;
604
- if (!md) {
605
- throw new Error('Metadata provider not available');
606
- }
607
- const modelToUpdate = yield md.GetEntityObject('AI Models', md.CurrentUser);
608
- yield modelToUpdate.Load(model.ID);
609
- modelToUpdate.IsActive = !modelToUpdate.IsActive;
610
- const result = yield modelToUpdate.Save();
611
- if (result) {
612
- // Update local data
613
- const index = this.models.findIndex(m => m.ID === model.ID);
614
- if (index >= 0) {
615
- this.models[index] = Object.assign({}, this.models[index], { IsActive: modelToUpdate.IsActive });
616
- this.applyFilters();
617
- }
618
- LogStatus(`Model ${modelToUpdate.Name} ${modelToUpdate.IsActive ? 'activated' : 'deactivated'}`);
619
- }
576
+ async toggleModelStatus(model) {
577
+ try {
578
+ const md = Metadata.Provider;
579
+ if (!md) {
580
+ throw new Error('Metadata provider not available');
620
581
  }
621
- catch (error) {
622
- LogError('Error updating model status: ' + String(error));
582
+ const modelToUpdate = await md.GetEntityObject('AI Models', md.CurrentUser);
583
+ await modelToUpdate.Load(model.ID);
584
+ modelToUpdate.IsActive = !modelToUpdate.IsActive;
585
+ const result = await modelToUpdate.Save();
586
+ if (result) {
587
+ // Update local data
588
+ const index = this.models.findIndex(m => m.ID === model.ID);
589
+ if (index >= 0) {
590
+ this.models[index] = Object.assign({}, this.models[index], { IsActive: modelToUpdate.IsActive });
591
+ this.applyFilters();
592
+ }
593
+ LogStatus(`Model ${modelToUpdate.Name} ${modelToUpdate.IsActive ? 'activated' : 'deactivated'}`);
623
594
  }
624
- });
595
+ }
596
+ catch (error) {
597
+ LogError('Error updating model status: ' + String(error));
598
+ }
625
599
  }
600
+ static ɵfac = function ModelManagementComponent_Factory(t) { return new (t || ModelManagementComponent)(); };
601
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ModelManagementComponent, selectors: [["app-model-management"]], outputs: { openEntityRecord: "openEntityRecord" }, decls: 18, vars: 8, consts: [["mjFillContainer", "", 1, "model-management-container", 3, "rightMargin", "bottomMargin"], [1, "dashboard-header"], [1, "header-info"], [1, "model-count"], [1, "header-controls"], ["title", "Toggle Filters", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-filter"], ["title", "Add New Model", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-plus"], ["title", "Refresh", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-refresh"], [1, "loading-container"], [1, "error-container"], [1, "dashboard-content"], [1, "loading-content"], [1, "loading-spinner"], [1, "spinner-ring"], [1, "loading-text"], [1, "error-message"], ["orientation", "horizontal", "mjFillContainer", "", 1, "main-splitter"], [3, "size", "collapsible", "resizable", "scrollable", "hidden"], [1, "filter-panel"], [1, "filter-header"], [1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "filter-content"], [1, "search-section"], ["type", "text", "placeholder", "Search models...", 1, "search-input", 3, "ngModelChange", "ngModel"], [1, "filter-group"], [3, "ngModelChange", "ngModel"], [3, "value"], [1, "filter-actions"], [1, "clear-btn", 3, "click"], [3, "resizable", "scrollable"], ["mjFillContainer", "", 1, "models-pane-container"], [1, "empty-state"], [1, "models-list"], [1, "fa-solid", "fa-microchip", "empty-icon"], [1, "control-btn", 3, "click"], [1, "model-item"], [1, "model-item", 3, "click"], [1, "model-header"], [1, "model-title"], [1, "model-vendor"], [1, "model-status"], [1, "model-details"], [1, "detail-item"], [1, "label"], [1, "value"], [1, "model-description"], [1, "model-actions"], [1, "action-btn", 3, "click", "title"], ["title", "Edit Model", 1, "action-btn", 3, "click"], [1, "fa-solid", "fa-edit"]], template: function ModelManagementComponent_Template(rf, ctx) { if (rf & 1) {
602
+ i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "span", 3);
603
+ i0.ɵɵtext(4);
604
+ i0.ɵɵelementEnd()();
605
+ i0.ɵɵelementStart(5, "div", 4)(6, "button", 5);
606
+ i0.ɵɵlistener("click", function ModelManagementComponent_Template_button_click_6_listener() { return ctx.toggleFiltersVisible(); });
607
+ i0.ɵɵelement(7, "span", 6);
608
+ i0.ɵɵtext(8, " Filters ");
609
+ i0.ɵɵelementEnd();
610
+ i0.ɵɵelementStart(9, "button", 7);
611
+ i0.ɵɵlistener("click", function ModelManagementComponent_Template_button_click_9_listener() { return ctx.createNewModel(); });
612
+ i0.ɵɵelement(10, "span", 8);
613
+ i0.ɵɵtext(11, " Add Model ");
614
+ i0.ɵɵelementEnd();
615
+ i0.ɵɵelementStart(12, "button", 9);
616
+ i0.ɵɵlistener("click", function ModelManagementComponent_Template_button_click_12_listener() { return ctx.loadData(); });
617
+ i0.ɵɵelement(13, "span", 10);
618
+ i0.ɵɵtext(14, " Refresh ");
619
+ i0.ɵɵelementEnd()()();
620
+ i0.ɵɵtemplate(15, ModelManagementComponent_Conditional_15_Template, 8, 1, "div", 11)(16, ModelManagementComponent_Conditional_16_Template, 3, 1, "div", 12)(17, ModelManagementComponent_Conditional_17_Template, 48, 17, "div", 13);
621
+ i0.ɵɵelementEnd();
622
+ } if (rf & 2) {
623
+ i0.ɵɵproperty("rightMargin", 8)("bottomMargin", 8);
624
+ i0.ɵɵadvance(4);
625
+ i0.ɵɵtextInterpolate1("", ctx.filteredModels.length, " AI models");
626
+ i0.ɵɵadvance(2);
627
+ i0.ɵɵclassProp("active", ctx.filtersVisible);
628
+ i0.ɵɵadvance(9);
629
+ i0.ɵɵconditional(ctx.isLoading ? 15 : -1);
630
+ i0.ɵɵadvance();
631
+ i0.ɵɵconditional(ctx.error ? 16 : -1);
632
+ i0.ɵɵadvance();
633
+ i0.ɵɵconditional(!ctx.isLoading && !ctx.error ? 17 : -1);
634
+ } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.SplitterComponent, i2.SplitterPaneComponent, i3.FillContainer], styles: [".model-management-container[_ngcontent-%COMP%] {\n overflow: hidden;\n padding: 4px;\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.dashboard-header[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n \n .header-info {\n flex: 1;\n display: flex;\n align-items: center;\n }\n \n .model-count {\n font-size: 12px;\n color: #2196f3;\n font-weight: 600;\n background: rgba(33, 150, 243, 0.1);\n padding: 4px 8px;\n border-radius: 4px;\n border: 1px solid rgba(33, 150, 243, 0.2);\n }\n \n .header-controls {\n display: flex;\n gap: 8px;\n \n .control-btn {\n padding: 8px 12px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: all 0.2s;\n \n &:hover {\n background-color: #f0f0f0;\n border-color: #2196f3;\n }\n \n &.active {\n background-color: #2196f3;\n border-color: #2196f3;\n color: white;\n }\n \n &:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n \n &:hover {\n background-color: white;\n border-color: #ccc;\n }\n }\n \n .fa-solid {\n font-size: 14px;\n }\n }\n }\n}\n\n.loading-container[_ngcontent-%COMP%], .error-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: #fafafa;\n flex: 1;\n \n p {\n color: #666;\n font-size: 14px;\n }\n}\n\n.error-message[_ngcontent-%COMP%] {\n color: #d32f2f;\n font-weight: 500;\n}\n\n.dashboard-content[_ngcontent-%COMP%] {\n overflow: hidden;\n flex: 1;\n}\n\n.loading-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n}\n\n.loading-spinner[_ngcontent-%COMP%] {\n position: relative;\n width: 60px;\n height: 60px;\n}\n\n.spinner-ring[_ngcontent-%COMP%] {\n position: absolute;\n width: 100%;\n height: 100%;\n border: 3px solid transparent;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_spin 1.5s linear infinite;\n \n &:nth-child(1) {\n border-top-color: #2196f3;\n animation-delay: 0s;\n }\n \n &:nth-child(2) {\n border-top-color: #9c27b0;\n animation-delay: 0.3s;\n transform: scale(0.8);\n }\n \n &:nth-child(3) {\n border-top-color: #ff6b35;\n animation-delay: 0.6s;\n transform: scale(0.6);\n }\n}\n\n.loading-text[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #666;\n font-weight: 500;\n text-align: center;\n}\n\n//[_ngcontent-%COMP%] Filter[_ngcontent-%COMP%] Panel\n.filter-panel[_ngcontent-%COMP%] {\n background: #fafafa;\n border-right: 1px solid #e0e0e0;\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n \n .filter-header {\n padding: 16px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: white;\n \n h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n }\n \n .close-btn {\n padding: 4px;\n border: none;\n background: none;\n cursor: pointer;\n color: #666;\n border-radius: 2px;\n \n &:hover {\n background: #f0f0f0;\n color: #333;\n }\n }\n }\n \n .filter-content {\n padding: 12px;\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n \n .search-section {\n margin-bottom: 16px;\n }\n \n .filter-group {\n margin-bottom: 16px;\n }\n \n label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: #333;\n margin-bottom: 4px;\n }\n \n .search-input, select {\n width: calc(100% - 4px);\n max-width: 100%;\n padding: 8px;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-size: 12px;\n box-sizing: border-box;\n \n &:focus {\n outline: none;\n border-color: #2196f3;\n }\n }\n \n .filter-actions {\n margin-top: 20px;\n \n .clear-btn {\n width: calc(100% - 4px);\n max-width: 100%;\n padding: 8px 12px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.2s;\n box-sizing: border-box;\n \n &:hover {\n background-color: #f0f0f0;\n border-color: #999;\n }\n }\n }\n }\n}\n\n//[_ngcontent-%COMP%] Models[_ngcontent-%COMP%] List\n.models-pane-container[_ngcontent-%COMP%] {\n background: white;\n position: relative;\n}\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 40px;\n text-align: center;\n \n .empty-icon {\n font-size: 48px;\n color: #ccc;\n margin-bottom: 16px;\n }\n \n h3 {\n margin: 0 0 8px 0;\n color: #666;\n font-size: 18px;\n }\n \n p {\n margin: 0 0 20px 0;\n color: #999;\n font-size: 14px;\n }\n}\n\n.models-list[_ngcontent-%COMP%] {\n padding: 16px;\n overflow-y: auto;\n height: 100%;\n \n .model-item {\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 16px;\n margin-bottom: 12px;\n cursor: pointer;\n transition: all 0.2s;\n position: relative;\n \n &:hover {\n border-color: #2196f3;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n \n &:last-child {\n margin-bottom: 0;\n }\n \n .model-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 12px;\n \n .model-title {\n flex: 1;\n \n h4 {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: #333;\n }\n \n .model-vendor {\n font-size: 12px;\n color: #2196f3;\n font-weight: 500;\n }\n }\n \n .model-status {\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: #ffc107;\n color: #000;\n text-transform: uppercase;\n \n &.active {\n background: #4caf50;\n color: white;\n }\n }\n }\n \n .model-details {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-bottom: 12px;\n \n .detail-item {\n display: flex;\n align-items: center;\n gap: 4px;\n \n .label {\n font-size: 12px;\n color: #666;\n font-weight: 500;\n }\n \n .value {\n font-size: 12px;\n color: #333;\n font-weight: 600;\n }\n }\n }\n \n .model-description {\n font-size: 12px;\n color: #666;\n line-height: 1.4;\n margin-bottom: 12px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n \n .model-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n \n .action-btn {\n padding: 6px 8px;\n border: 1px solid #e0e0e0;\n border-radius: 3px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n color: #666;\n transition: all 0.2s;\n \n &:hover {\n background: #f0f0f0;\n border-color: #2196f3;\n color: #2196f3;\n }\n }\n }\n }\n}\n\n@keyframes _ngcontent-%COMP%_spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}"] });
626
635
  }
627
- ModelManagementComponent.ɵfac = function ModelManagementComponent_Factory(t) { return new (t || ModelManagementComponent)(); };
628
- ModelManagementComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ModelManagementComponent, selectors: [["app-model-management"]], outputs: { openEntityRecord: "openEntityRecord" }, decls: 18, vars: 8, consts: [["mjFillContainer", "", 1, "model-management-container", 3, "rightMargin", "bottomMargin"], [1, "dashboard-header"], [1, "header-info"], [1, "model-count"], [1, "header-controls"], ["title", "Toggle Filters", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-filter"], ["title", "Add New Model", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-plus"], ["title", "Refresh", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-refresh"], [1, "loading-container"], [1, "error-container"], [1, "dashboard-content"], [1, "loading-content"], [1, "loading-spinner"], [1, "spinner-ring"], [1, "loading-text"], [1, "error-message"], ["orientation", "horizontal", "mjFillContainer", "", 1, "main-splitter"], [3, "size", "collapsible", "resizable", "scrollable", "hidden"], [1, "filter-panel"], [1, "filter-header"], [1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "filter-content"], [1, "search-section"], ["type", "text", "placeholder", "Search models...", 1, "search-input", 3, "ngModelChange", "ngModel"], [1, "filter-group"], [3, "ngModelChange", "ngModel"], [3, "value"], [1, "filter-actions"], [1, "clear-btn", 3, "click"], [3, "resizable", "scrollable"], ["mjFillContainer", "", 1, "models-pane-container"], [1, "empty-state"], [1, "models-list"], [1, "fa-solid", "fa-microchip", "empty-icon"], [1, "control-btn", 3, "click"], [1, "model-item"], [1, "model-item", 3, "click"], [1, "model-header"], [1, "model-title"], [1, "model-vendor"], [1, "model-status"], [1, "model-details"], [1, "detail-item"], [1, "label"], [1, "value"], [1, "model-description"], [1, "model-actions"], [1, "action-btn", 3, "click", "title"], ["title", "Edit Model", 1, "action-btn", 3, "click"], [1, "fa-solid", "fa-edit"]], template: function ModelManagementComponent_Template(rf, ctx) { if (rf & 1) {
629
- i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "span", 3);
630
- i0.ɵɵtext(4);
631
- i0.ɵɵelementEnd()();
632
- i0.ɵɵelementStart(5, "div", 4)(6, "button", 5);
633
- i0.ɵɵlistener("click", function ModelManagementComponent_Template_button_click_6_listener() { return ctx.toggleFiltersVisible(); });
634
- i0.ɵɵelement(7, "span", 6);
635
- i0.ɵɵtext(8, " Filters ");
636
- i0.ɵɵelementEnd();
637
- i0.ɵɵelementStart(9, "button", 7);
638
- i0.ɵɵlistener("click", function ModelManagementComponent_Template_button_click_9_listener() { return ctx.createNewModel(); });
639
- i0.ɵɵelement(10, "span", 8);
640
- i0.ɵɵtext(11, " Add Model ");
641
- i0.ɵɵelementEnd();
642
- i0.ɵɵelementStart(12, "button", 9);
643
- i0.ɵɵlistener("click", function ModelManagementComponent_Template_button_click_12_listener() { return ctx.loadData(); });
644
- i0.ɵɵelement(13, "span", 10);
645
- i0.ɵɵtext(14, " Refresh ");
646
- i0.ɵɵelementEnd()()();
647
- i0.ɵɵtemplate(15, ModelManagementComponent_Conditional_15_Template, 8, 1, "div", 11)(16, ModelManagementComponent_Conditional_16_Template, 3, 1, "div", 12)(17, ModelManagementComponent_Conditional_17_Template, 48, 17, "div", 13);
648
- i0.ɵɵelementEnd();
649
- } if (rf & 2) {
650
- i0.ɵɵproperty("rightMargin", 8)("bottomMargin", 8);
651
- i0.ɵɵadvance(4);
652
- i0.ɵɵtextInterpolate1("", ctx.filteredModels.length, " AI models");
653
- i0.ɵɵadvance(2);
654
- i0.ɵɵclassProp("active", ctx.filtersVisible);
655
- i0.ɵɵadvance(9);
656
- i0.ɵɵconditional(ctx.isLoading ? 15 : -1);
657
- i0.ɵɵadvance();
658
- i0.ɵɵconditional(ctx.error ? 16 : -1);
659
- i0.ɵɵadvance();
660
- i0.ɵɵconditional(!ctx.isLoading && !ctx.error ? 17 : -1);
661
- } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.SplitterComponent, i2.SplitterPaneComponent, i3.FillContainer], styles: [".model-management-container[_ngcontent-%COMP%] {\n overflow: hidden;\n padding: 4px;\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.dashboard-header[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n \n .header-info {\n flex: 1;\n display: flex;\n align-items: center;\n }\n \n .model-count {\n font-size: 12px;\n color: #2196f3;\n font-weight: 600;\n background: rgba(33, 150, 243, 0.1);\n padding: 4px 8px;\n border-radius: 4px;\n border: 1px solid rgba(33, 150, 243, 0.2);\n }\n \n .header-controls {\n display: flex;\n gap: 8px;\n \n .control-btn {\n padding: 8px 12px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: all 0.2s;\n \n &:hover {\n background-color: #f0f0f0;\n border-color: #2196f3;\n }\n \n &.active {\n background-color: #2196f3;\n border-color: #2196f3;\n color: white;\n }\n \n &:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n \n &:hover {\n background-color: white;\n border-color: #ccc;\n }\n }\n \n .fa-solid {\n font-size: 14px;\n }\n }\n }\n}\n\n.loading-container[_ngcontent-%COMP%], .error-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: #fafafa;\n flex: 1;\n \n p {\n color: #666;\n font-size: 14px;\n }\n}\n\n.error-message[_ngcontent-%COMP%] {\n color: #d32f2f;\n font-weight: 500;\n}\n\n.dashboard-content[_ngcontent-%COMP%] {\n overflow: hidden;\n flex: 1;\n}\n\n.loading-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n}\n\n.loading-spinner[_ngcontent-%COMP%] {\n position: relative;\n width: 60px;\n height: 60px;\n}\n\n.spinner-ring[_ngcontent-%COMP%] {\n position: absolute;\n width: 100%;\n height: 100%;\n border: 3px solid transparent;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_spin 1.5s linear infinite;\n \n &:nth-child(1) {\n border-top-color: #2196f3;\n animation-delay: 0s;\n }\n \n &:nth-child(2) {\n border-top-color: #9c27b0;\n animation-delay: 0.3s;\n transform: scale(0.8);\n }\n \n &:nth-child(3) {\n border-top-color: #ff6b35;\n animation-delay: 0.6s;\n transform: scale(0.6);\n }\n}\n\n.loading-text[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #666;\n font-weight: 500;\n text-align: center;\n}\n\n//[_ngcontent-%COMP%] Filter[_ngcontent-%COMP%] Panel\n.filter-panel[_ngcontent-%COMP%] {\n background: #fafafa;\n border-right: 1px solid #e0e0e0;\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n \n .filter-header {\n padding: 16px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: white;\n \n h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n }\n \n .close-btn {\n padding: 4px;\n border: none;\n background: none;\n cursor: pointer;\n color: #666;\n border-radius: 2px;\n \n &:hover {\n background: #f0f0f0;\n color: #333;\n }\n }\n }\n \n .filter-content {\n padding: 12px;\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n \n .search-section {\n margin-bottom: 16px;\n }\n \n .filter-group {\n margin-bottom: 16px;\n }\n \n label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: #333;\n margin-bottom: 4px;\n }\n \n .search-input, select {\n width: calc(100% - 4px);\n max-width: 100%;\n padding: 8px;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-size: 12px;\n box-sizing: border-box;\n \n &:focus {\n outline: none;\n border-color: #2196f3;\n }\n }\n \n .filter-actions {\n margin-top: 20px;\n \n .clear-btn {\n width: calc(100% - 4px);\n max-width: 100%;\n padding: 8px 12px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.2s;\n box-sizing: border-box;\n \n &:hover {\n background-color: #f0f0f0;\n border-color: #999;\n }\n }\n }\n }\n}\n\n//[_ngcontent-%COMP%] Models[_ngcontent-%COMP%] List\n.models-pane-container[_ngcontent-%COMP%] {\n background: white;\n position: relative;\n}\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 40px;\n text-align: center;\n \n .empty-icon {\n font-size: 48px;\n color: #ccc;\n margin-bottom: 16px;\n }\n \n h3 {\n margin: 0 0 8px 0;\n color: #666;\n font-size: 18px;\n }\n \n p {\n margin: 0 0 20px 0;\n color: #999;\n font-size: 14px;\n }\n}\n\n.models-list[_ngcontent-%COMP%] {\n padding: 16px;\n overflow-y: auto;\n height: 100%;\n \n .model-item {\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 16px;\n margin-bottom: 12px;\n cursor: pointer;\n transition: all 0.2s;\n position: relative;\n \n &:hover {\n border-color: #2196f3;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n \n &:last-child {\n margin-bottom: 0;\n }\n \n .model-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 12px;\n \n .model-title {\n flex: 1;\n \n h4 {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: #333;\n }\n \n .model-vendor {\n font-size: 12px;\n color: #2196f3;\n font-weight: 500;\n }\n }\n \n .model-status {\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: #ffc107;\n color: #000;\n text-transform: uppercase;\n \n &.active {\n background: #4caf50;\n color: white;\n }\n }\n }\n \n .model-details {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-bottom: 12px;\n \n .detail-item {\n display: flex;\n align-items: center;\n gap: 4px;\n \n .label {\n font-size: 12px;\n color: #666;\n font-weight: 500;\n }\n \n .value {\n font-size: 12px;\n color: #333;\n font-weight: 600;\n }\n }\n }\n \n .model-description {\n font-size: 12px;\n color: #666;\n line-height: 1.4;\n margin-bottom: 12px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n \n .model-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n \n .action-btn {\n padding: 6px 8px;\n border: 1px solid #e0e0e0;\n border-radius: 3px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n color: #666;\n transition: all 0.2s;\n \n &:hover {\n background: #f0f0f0;\n border-color: #2196f3;\n color: #2196f3;\n }\n }\n }\n }\n}\n\n@keyframes _ngcontent-%COMP%_spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}"] });
662
636
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ModelManagementComponent, [{
663
637
  type: Component,
664
638
  args: [{ selector: 'app-model-management', template: "<div class=\"model-management-container\" mjFillContainer [rightMargin]=\"8\" [bottomMargin]=\"8\">\n <div class=\"dashboard-header\">\n <div class=\"header-info\">\n <span class=\"model-count\">{{ filteredModels.length }} AI models</span>\n </div>\n <div class=\"header-controls\">\n <button \n class=\"control-btn\" \n (click)=\"toggleFiltersVisible()\" \n [class.active]=\"filtersVisible\"\n title=\"Toggle Filters\">\n <span class=\"fa-solid fa-filter\"></span>\n Filters\n </button>\n <button \n class=\"control-btn\" \n (click)=\"createNewModel()\"\n title=\"Add New Model\">\n <span class=\"fa-solid fa-plus\"></span>\n Add Model\n </button>\n <button \n class=\"control-btn\" \n (click)=\"loadData()\"\n title=\"Refresh\">\n <span class=\"fa-solid fa-refresh\"></span>\n Refresh\n </button>\n </div>\n </div>\n \n @if (isLoading) {\n <div class=\"loading-container\">\n <div class=\"loading-content\">\n <div class=\"loading-spinner\">\n <div class=\"spinner-ring\"></div>\n <div class=\"spinner-ring\"></div>\n <div class=\"spinner-ring\"></div>\n </div>\n <div class=\"loading-text\">{{ loadingMessage }}</div>\n </div>\n </div>\n }\n \n @if (error) {\n <div class=\"error-container\">\n <p class=\"error-message\">{{ error }}</p>\n </div>\n }\n \n @if (!isLoading && !error) {\n <div class=\"dashboard-content\">\n <kendo-splitter \n class=\"main-splitter\"\n orientation=\"horizontal\"\n mjFillContainer>\n \n <!-- Filter Panel (Left) -->\n <kendo-splitter-pane \n [size]=\"filtersVisible ? '280px' : '0px'\"\n [collapsible]=\"false\"\n [resizable]=\"filtersVisible\"\n [scrollable]=\"false\"\n [hidden]=\"!filtersVisible\">\n <div class=\"filter-panel\">\n <div class=\"filter-header\">\n <h3>Filters</h3>\n <button class=\"close-btn\" (click)=\"toggleFiltersVisible()\">\n <span class=\"fa-solid fa-times\"></span>\n </button>\n </div>\n \n <div class=\"filter-content\">\n <div class=\"search-section\">\n <label>Search</label>\n <input \n type=\"text\" \n placeholder=\"Search models...\"\n [(ngModel)]=\"filter.searchTerm\"\n (ngModelChange)=\"onSearchChange($event)\"\n class=\"search-input\">\n </div>\n \n <div class=\"filter-group\">\n <label>Vendor</label>\n <select [(ngModel)]=\"filter.vendorId\" (ngModelChange)=\"onFilterChange()\">\n <option [value]=\"null\">All Vendors</option>\n @for (vendor of vendors; track vendor.ID) {\n <option [value]=\"vendor.ID\">{{ vendor.Name }}</option>\n }\n </select>\n </div>\n \n <div class=\"filter-group\">\n <label>Type</label>\n <select [(ngModel)]=\"filter.modelTypeId\" (ngModelChange)=\"onFilterChange()\">\n <option [value]=\"null\">All Types</option>\n @for (type of modelTypes; track type.ID) {\n <option [value]=\"type.ID\">{{ type.Name }}</option>\n }\n </select>\n </div>\n \n <div class=\"filter-group\">\n <label>Status</label>\n <select [(ngModel)]=\"filter.isActive\" (ngModelChange)=\"onFilterChange()\">\n <option [value]=\"null\">All</option>\n <option [value]=\"true\">Active</option>\n <option [value]=\"false\">Inactive</option>\n </select>\n </div>\n \n <div class=\"filter-actions\">\n <button class=\"clear-btn\" (click)=\"clearFilters()\">\n <span class=\"fa-solid fa-times\"></span>\n Clear All\n </button>\n </div>\n </div>\n </div>\n </kendo-splitter-pane>\n \n <!-- Models List (Right) -->\n <kendo-splitter-pane \n [resizable]=\"true\"\n [scrollable]=\"false\">\n <div class=\"models-pane-container\" mjFillContainer>\n @if (filteredModels.length === 0) {\n <div class=\"empty-state\">\n <span class=\"fa-solid fa-microchip empty-icon\"></span>\n <h3>No Models Found</h3>\n @if (models.length === 0) {\n <p>No AI models have been configured yet.</p>\n <button class=\"control-btn\" (click)=\"createNewModel()\">\n <span class=\"fa-solid fa-plus\"></span>\n Add First Model\n </button>\n } @else {\n <p>No models match your current filters.</p>\n <button class=\"control-btn\" (click)=\"clearFilters()\">Clear Filters</button>\n }\n </div>\n } @else {\n <div class=\"models-list\">\n @for (displayData of filteredModels; track displayData.model.ID) {\n <div class=\"model-item\" (click)=\"onOpenModel(displayData.model)\">\n <div class=\"model-header\">\n <div class=\"model-title\">\n <h4>{{ displayData.model.Name }}</h4>\n <span class=\"model-vendor\">{{ displayData.vendor?.Name || 'Unknown Vendor' }}</span>\n </div>\n <div class=\"model-status\" [class.active]=\"displayData.model.IsActive\">\n {{ displayData.statusDisplay }}\n </div>\n </div>\n \n <div class=\"model-details\">\n <div class=\"detail-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ displayData.modelType?.Name || 'Unknown' }}</span>\n </div>\n <div class=\"detail-item\">\n <span class=\"label\">Power:</span>\n <span class=\"value\">{{ displayData.powerRankDisplay }}</span>\n </div>\n <div class=\"detail-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ displayData.speedRankDisplay }}</span>\n </div>\n <div class=\"detail-item\">\n <span class=\"label\">Cost:</span>\n <span class=\"value\">{{ displayData.costRankDisplay }}</span>\n </div>\n </div>\n \n @if (displayData.model.Description) {\n <div class=\"model-description\">\n {{ displayData.model.Description }}\n </div>\n }\n \n <div class=\"model-actions\">\n <button \n class=\"action-btn\"\n (click)=\"$event.stopPropagation(); toggleModelStatus(displayData.model)\"\n [title]=\"displayData.model.IsActive ? 'Deactivate' : 'Activate'\">\n <span [class]=\"displayData.model.IsActive ? 'fa-solid fa-pause' : 'fa-solid fa-play'\"></span>\n </button>\n <button \n class=\"action-btn\"\n (click)=\"$event.stopPropagation(); onOpenModel(displayData.model)\"\n title=\"Edit Model\">\n <span class=\"fa-solid fa-edit\"></span>\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </kendo-splitter-pane>\n </kendo-splitter>\n </div>\n }\n</div>", styles: [".model-management-container {\n overflow: hidden;\n padding: 4px;\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.dashboard-header {\n margin-bottom: 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n \n .header-info {\n flex: 1;\n display: flex;\n align-items: center;\n }\n \n .model-count {\n font-size: 12px;\n color: #2196f3;\n font-weight: 600;\n background: rgba(33, 150, 243, 0.1);\n padding: 4px 8px;\n border-radius: 4px;\n border: 1px solid rgba(33, 150, 243, 0.2);\n }\n \n .header-controls {\n display: flex;\n gap: 8px;\n \n .control-btn {\n padding: 8px 12px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: all 0.2s;\n \n &:hover {\n background-color: #f0f0f0;\n border-color: #2196f3;\n }\n \n &.active {\n background-color: #2196f3;\n border-color: #2196f3;\n color: white;\n }\n \n &:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n \n &:hover {\n background-color: white;\n border-color: #ccc;\n }\n }\n \n .fa-solid {\n font-size: 14px;\n }\n }\n }\n}\n\n.loading-container, .error-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: #fafafa;\n flex: 1;\n \n p {\n color: #666;\n font-size: 14px;\n }\n}\n\n.error-message {\n color: #d32f2f;\n font-weight: 500;\n}\n\n.dashboard-content {\n overflow: hidden;\n flex: 1;\n}\n\n.loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n}\n\n.loading-spinner {\n position: relative;\n width: 60px;\n height: 60px;\n}\n\n.spinner-ring {\n position: absolute;\n width: 100%;\n height: 100%;\n border: 3px solid transparent;\n border-radius: 50%;\n animation: spin 1.5s linear infinite;\n \n &:nth-child(1) {\n border-top-color: #2196f3;\n animation-delay: 0s;\n }\n \n &:nth-child(2) {\n border-top-color: #9c27b0;\n animation-delay: 0.3s;\n transform: scale(0.8);\n }\n \n &:nth-child(3) {\n border-top-color: #ff6b35;\n animation-delay: 0.6s;\n transform: scale(0.6);\n }\n}\n\n.loading-text {\n font-size: 14px;\n color: #666;\n font-weight: 500;\n text-align: center;\n}\n\n// Filter Panel\n.filter-panel {\n background: #fafafa;\n border-right: 1px solid #e0e0e0;\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n \n .filter-header {\n padding: 16px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: white;\n \n h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n }\n \n .close-btn {\n padding: 4px;\n border: none;\n background: none;\n cursor: pointer;\n color: #666;\n border-radius: 2px;\n \n &:hover {\n background: #f0f0f0;\n color: #333;\n }\n }\n }\n \n .filter-content {\n padding: 12px;\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n \n .search-section {\n margin-bottom: 16px;\n }\n \n .filter-group {\n margin-bottom: 16px;\n }\n \n label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: #333;\n margin-bottom: 4px;\n }\n \n .search-input, select {\n width: calc(100% - 4px);\n max-width: 100%;\n padding: 8px;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-size: 12px;\n box-sizing: border-box;\n \n &:focus {\n outline: none;\n border-color: #2196f3;\n }\n }\n \n .filter-actions {\n margin-top: 20px;\n \n .clear-btn {\n width: calc(100% - 4px);\n max-width: 100%;\n padding: 8px 12px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.2s;\n box-sizing: border-box;\n \n &:hover {\n background-color: #f0f0f0;\n border-color: #999;\n }\n }\n }\n }\n}\n\n// Models List\n.models-pane-container {\n background: white;\n position: relative;\n}\n\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 40px;\n text-align: center;\n \n .empty-icon {\n font-size: 48px;\n color: #ccc;\n margin-bottom: 16px;\n }\n \n h3 {\n margin: 0 0 8px 0;\n color: #666;\n font-size: 18px;\n }\n \n p {\n margin: 0 0 20px 0;\n color: #999;\n font-size: 14px;\n }\n}\n\n.models-list {\n padding: 16px;\n overflow-y: auto;\n height: 100%;\n \n .model-item {\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 16px;\n margin-bottom: 12px;\n cursor: pointer;\n transition: all 0.2s;\n position: relative;\n \n &:hover {\n border-color: #2196f3;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n \n &:last-child {\n margin-bottom: 0;\n }\n \n .model-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 12px;\n \n .model-title {\n flex: 1;\n \n h4 {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: #333;\n }\n \n .model-vendor {\n font-size: 12px;\n color: #2196f3;\n font-weight: 500;\n }\n }\n \n .model-status {\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n background: #ffc107;\n color: #000;\n text-transform: uppercase;\n \n &.active {\n background: #4caf50;\n color: white;\n }\n }\n }\n \n .model-details {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-bottom: 12px;\n \n .detail-item {\n display: flex;\n align-items: center;\n gap: 4px;\n \n .label {\n font-size: 12px;\n color: #666;\n font-weight: 500;\n }\n \n .value {\n font-size: 12px;\n color: #333;\n font-weight: 600;\n }\n }\n }\n \n .model-description {\n font-size: 12px;\n color: #666;\n line-height: 1.4;\n margin-bottom: 12px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n \n .model-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n \n .action-btn {\n padding: 6px 8px;\n border: 1px solid #e0e0e0;\n border-radius: 3px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n color: #666;\n transition: all 0.2s;\n \n &:hover {\n background: #f0f0f0;\n border-color: #2196f3;\n color: #2196f3;\n }\n }\n }\n }\n}\n\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}"] }]