@memberjunction/ng-entity-viewer 5.38.0 → 5.40.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 (93) hide show
  1. package/README.md +1 -1
  2. package/dist/__tests__/view-types.test.d.ts +2 -0
  3. package/dist/__tests__/view-types.test.d.ts.map +1 -0
  4. package/dist/__tests__/view-types.test.js +102 -0
  5. package/dist/__tests__/view-types.test.js.map +1 -0
  6. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js +2 -2
  7. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js.map +1 -1
  8. package/dist/lib/confirm-dialog/confirm-dialog.component.js +2 -2
  9. package/dist/lib/confirm-dialog/confirm-dialog.component.js.map +1 -1
  10. package/dist/lib/duplicate-view-dialog/duplicate-view-dialog.component.js +2 -2
  11. package/dist/lib/duplicate-view-dialog/duplicate-view-dialog.component.js.map +1 -1
  12. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
  13. package/dist/lib/entity-data-grid/entity-data-grid.component.js +10 -2
  14. package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
  15. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js +2 -2
  16. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js.map +1 -1
  17. package/dist/lib/entity-viewer/entity-viewer.component.d.ts +356 -341
  18. package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
  19. package/dist/lib/entity-viewer/entity-viewer.component.js +993 -1097
  20. package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
  21. package/dist/lib/quick-save-dialog/quick-save-dialog.component.js +2 -2
  22. package/dist/lib/quick-save-dialog/quick-save-dialog.component.js.map +1 -1
  23. package/dist/lib/recycle-bin/recycle-bin.component.js +1 -1
  24. package/dist/lib/shared-view-warning-dialog/shared-view-warning-dialog.component.js +2 -2
  25. package/dist/lib/shared-view-warning-dialog/shared-view-warning-dialog.component.js.map +1 -1
  26. package/dist/lib/view-config-panel/view-config-panel.component.d.ts +126 -126
  27. package/dist/lib/view-config-panel/view-config-panel.component.js +635 -635
  28. package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
  29. package/dist/lib/view-selector/view-selector.component.d.ts +226 -0
  30. package/dist/lib/view-selector/view-selector.component.d.ts.map +1 -0
  31. package/dist/lib/view-selector/view-selector.component.js +861 -0
  32. package/dist/lib/view-selector/view-selector.component.js.map +1 -0
  33. package/dist/lib/view-type-switcher/view-type-switcher.component.d.ts +114 -0
  34. package/dist/lib/view-type-switcher/view-type-switcher.component.d.ts.map +1 -0
  35. package/dist/lib/view-type-switcher/view-type-switcher.component.js +209 -0
  36. package/dist/lib/view-type-switcher/view-type-switcher.component.js.map +1 -0
  37. package/dist/lib/view-types/descriptors/cards-view-type.d.ts +18 -0
  38. package/dist/lib/view-types/descriptors/cards-view-type.d.ts.map +1 -0
  39. package/dist/lib/view-types/descriptors/cards-view-type.js +31 -0
  40. package/dist/lib/view-types/descriptors/cards-view-type.js.map +1 -0
  41. package/dist/lib/view-types/descriptors/grid-view-type.d.ts +17 -0
  42. package/dist/lib/view-types/descriptors/grid-view-type.d.ts.map +1 -0
  43. package/dist/lib/view-types/descriptors/grid-view-type.js +30 -0
  44. package/dist/lib/view-types/descriptors/grid-view-type.js.map +1 -0
  45. package/dist/lib/view-types/descriptors/map-view-type.d.ts +21 -0
  46. package/dist/lib/view-types/descriptors/map-view-type.d.ts.map +1 -0
  47. package/dist/lib/view-types/descriptors/map-view-type.js +35 -0
  48. package/dist/lib/view-types/descriptors/map-view-type.js.map +1 -0
  49. package/dist/lib/view-types/descriptors/timeline-view-type.d.ts +22 -0
  50. package/dist/lib/view-types/descriptors/timeline-view-type.d.ts.map +1 -0
  51. package/dist/lib/view-types/descriptors/timeline-view-type.js +40 -0
  52. package/dist/lib/view-types/descriptors/timeline-view-type.js.map +1 -0
  53. package/dist/lib/view-types/index.d.ts +20 -0
  54. package/dist/lib/view-types/index.d.ts.map +1 -0
  55. package/dist/lib/view-types/index.js +29 -0
  56. package/dist/lib/view-types/index.js.map +1 -0
  57. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts +93 -0
  58. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts.map +1 -0
  59. package/dist/lib/view-types/renderers/cards-view-renderer.component.js +144 -0
  60. package/dist/lib/view-types/renderers/cards-view-renderer.component.js.map +1 -0
  61. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +273 -0
  62. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -0
  63. package/dist/lib/view-types/renderers/grid-view-renderer.component.js +558 -0
  64. package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -0
  65. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts +135 -0
  66. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts.map +1 -0
  67. package/dist/lib/view-types/renderers/map-view-renderer.component.js +216 -0
  68. package/dist/lib/view-types/renderers/map-view-renderer.component.js.map +1 -0
  69. package/dist/lib/view-types/renderers/timeline-view-renderer.component.d.ts +176 -0
  70. package/dist/lib/view-types/renderers/timeline-view-renderer.component.d.ts.map +1 -0
  71. package/dist/lib/view-types/renderers/timeline-view-renderer.component.js +535 -0
  72. package/dist/lib/view-types/renderers/timeline-view-renderer.component.js.map +1 -0
  73. package/dist/lib/view-types/view-type.contracts.d.ts +235 -0
  74. package/dist/lib/view-types/view-type.contracts.d.ts.map +1 -0
  75. package/dist/lib/view-types/view-type.contracts.js +51 -0
  76. package/dist/lib/view-types/view-type.contracts.js.map +1 -0
  77. package/dist/lib/view-types/view-type.engine.d.ts +76 -0
  78. package/dist/lib/view-types/view-type.engine.d.ts.map +1 -0
  79. package/dist/lib/view-types/view-type.engine.js +138 -0
  80. package/dist/lib/view-types/view-type.engine.js.map +1 -0
  81. package/dist/lib/view-workspace/view-workspace.component.d.ts +451 -0
  82. package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -0
  83. package/dist/lib/view-workspace/view-workspace.component.js +1212 -0
  84. package/dist/lib/view-workspace/view-workspace.component.js.map +1 -0
  85. package/dist/module.d.ts +20 -11
  86. package/dist/module.d.ts.map +1 -1
  87. package/dist/module.js +50 -8
  88. package/dist/module.js.map +1 -1
  89. package/dist/public-api.d.ts +8 -0
  90. package/dist/public-api.d.ts.map +1 -1
  91. package/dist/public-api.js +14 -0
  92. package/dist/public-api.js.map +1 -1
  93. package/package.json +19 -18
package/README.md CHANGED
@@ -458,7 +458,7 @@ import {
458
458
  | `@memberjunction/ng-filter-builder` | Filter builder component |
459
459
  | `@memberjunction/ng-export-service` | Export service and dialog |
460
460
  | `@memberjunction/ng-record-changes` | Reusable restore preview panel for the Recycle Bin |
461
- | `@memberjunction/ng-versions` | Provides `mj-slide-panel` for the Recycle Bin slide-in |
461
+ | `@memberjunction/ng-ui-components` | Provides `mj-slide-panel` for the Recycle Bin slide-in |
462
462
 
463
463
  ### Peer Dependencies
464
464
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=view-types.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-types.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/view-types.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,102 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { describe, it, expect, beforeEach } from 'vitest';
8
+ import { RegisterClass } from '@memberjunction/global';
9
+ import { BaseViewTypeDescriptor } from '../lib/view-types/view-type.contracts';
10
+ import { ViewTypeEngine } from '../lib/view-types/view-type.engine';
11
+ // NOTE: We test the framework (base descriptor + engine) with an in-test descriptor rather than
12
+ // importing the four built-in descriptors — those pull the Angular component graph (grid/cards/
13
+ // timeline/map) which needs the JIT compiler in a unit-test env. The built-in renderers + the
14
+ // dynamic-mount host are exercised by the live Playwright pass.
15
+ // Module-level hooks so the test can observe the descriptor the ClassFactory instantiates.
16
+ let availableFlag = true;
17
+ let ensureCalls = 0;
18
+ let TestAvailViewType = class TestAvailViewType extends BaseViewTypeDescriptor {
19
+ Name = 'TestAvailViewType';
20
+ DisplayName = 'Test';
21
+ Icon = 'fa-solid fa-flask';
22
+ RendererComponent = class {
23
+ };
24
+ IsAvailableFor() {
25
+ return availableFlag;
26
+ }
27
+ async EnsureAvailabilityData() {
28
+ ensureCalls++;
29
+ }
30
+ };
31
+ TestAvailViewType = __decorate([
32
+ RegisterClass(BaseViewTypeDescriptor, 'TestAvailViewType')
33
+ ], TestAvailViewType);
34
+ function entity() {
35
+ return { ID: 'E1', Name: 'Test Entity' };
36
+ }
37
+ function seedViewTypes(rows) {
38
+ ViewTypeEngine.Instance._viewTypes =
39
+ rows;
40
+ }
41
+ describe('BaseViewTypeDescriptor defaults', () => {
42
+ class PlainViewType extends BaseViewTypeDescriptor {
43
+ Name = 'PlainViewType';
44
+ DisplayName = 'Plain';
45
+ Icon = 'fa-solid fa-shapes';
46
+ RendererComponent = class {
47
+ };
48
+ }
49
+ it('defaults IsAvailableFor to true', () => {
50
+ expect(new PlainViewType().IsAvailableFor(entity())).toBe(true);
51
+ });
52
+ it('defaults EnsureAvailabilityData to a resolved no-op', async () => {
53
+ await expect(new PlainViewType().EnsureAvailabilityData()).resolves.toBeUndefined();
54
+ });
55
+ });
56
+ describe('ViewTypeEngine', () => {
57
+ beforeEach(() => {
58
+ availableFlag = true;
59
+ ensureCalls = 0;
60
+ seedViewTypes([{ ID: 'VT1', DriverClass: 'TestAvailViewType', Name: 'Test' }]);
61
+ });
62
+ it('GetDescriptor resolves a registered descriptor by DriverClass', () => {
63
+ const d = ViewTypeEngine.Instance.GetDescriptor('TestAvailViewType');
64
+ expect(d).toBeTruthy();
65
+ expect(d?.Name).toBe('TestAvailViewType');
66
+ });
67
+ it('GetDescriptor returns null for an unregistered DriverClass (no blank-item fallback)', () => {
68
+ // A seeded MJ: View Types row whose descriptor class was never built (e.g. TagCloudViewType)
69
+ // must NOT resolve to the abstract base fallback — that would render a blank switcher item.
70
+ expect(ViewTypeEngine.Instance.GetDescriptor('TotallyUnregisteredViewType')).toBeNull();
71
+ });
72
+ it('GetAvailableViewTypeRows omits rows whose descriptor is unregistered', () => {
73
+ seedViewTypes([
74
+ { ID: 'VT1', DriverClass: 'TestAvailViewType', Name: 'Test' },
75
+ { ID: 'VT2', DriverClass: 'TotallyUnregisteredViewType', Name: 'Ghost' },
76
+ ]);
77
+ const rows = ViewTypeEngine.Instance.GetAvailableViewTypeRows(entity());
78
+ expect(rows.map(r => r.ViewType.ID)).toEqual(['VT1']);
79
+ });
80
+ it('GetAvailableViewTypeRows pairs the row with its descriptor when available', () => {
81
+ const rows = ViewTypeEngine.Instance.GetAvailableViewTypeRows(entity());
82
+ expect(rows).toHaveLength(1);
83
+ expect(rows[0].ViewType.ID).toBe('VT1');
84
+ expect(rows[0].Descriptor.Name).toBe('TestAvailViewType');
85
+ });
86
+ it('GetAvailableViewTypeRows excludes view types whose predicate is false', () => {
87
+ availableFlag = false;
88
+ expect(ViewTypeEngine.Instance.GetAvailableViewTypeRows(entity())).toHaveLength(0);
89
+ });
90
+ it('GetAvailableViewTypes returns just the descriptors', () => {
91
+ const descriptors = ViewTypeEngine.Instance.GetAvailableViewTypes(entity());
92
+ expect(descriptors.map(d => d.Name)).toEqual(['TestAvailViewType']);
93
+ });
94
+ it('EnsureAvailabilityData invokes each descriptor hook once', async () => {
95
+ await ViewTypeEngine.Instance.EnsureAvailabilityData();
96
+ expect(ensureCalls).toBe(1);
97
+ });
98
+ it('returns nothing for a null entity', () => {
99
+ expect(ViewTypeEngine.Instance.GetAvailableViewTypeRows(null)).toHaveLength(0);
100
+ });
101
+ });
102
+ //# sourceMappingURL=view-types.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-types.test.js","sourceRoot":"","sources":["../../src/__tests__/view-types.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, beforeEach } from 'vitest';\nimport { RegisterClass } from '@memberjunction/global';\nimport type { EntityInfo } from '@memberjunction/core';\nimport type { MJViewTypeEntity } from '@memberjunction/core-entities';\nimport { BaseViewTypeDescriptor } from '../lib/view-types/view-type.contracts';\nimport { ViewTypeEngine } from '../lib/view-types/view-type.engine';\n\n// NOTE: We test the framework (base descriptor + engine) with an in-test descriptor rather than\n// importing the four built-in descriptors — those pull the Angular component graph (grid/cards/\n// timeline/map) which needs the JIT compiler in a unit-test env. The built-in renderers + the\n// dynamic-mount host are exercised by the live Playwright pass.\n\n// Module-level hooks so the test can observe the descriptor the ClassFactory instantiates.\nlet availableFlag = true;\nlet ensureCalls = 0;\n\n@RegisterClass(BaseViewTypeDescriptor, 'TestAvailViewType')\nclass TestAvailViewType extends BaseViewTypeDescriptor {\n readonly Name = 'TestAvailViewType';\n readonly DisplayName = 'Test';\n readonly Icon = 'fa-solid fa-flask';\n readonly RendererComponent = class {};\n override IsAvailableFor(): boolean {\n return availableFlag;\n }\n override async EnsureAvailabilityData(): Promise<void> {\n ensureCalls++;\n }\n}\n\nfunction entity(): EntityInfo {\n return { ID: 'E1', Name: 'Test Entity' } as unknown as EntityInfo;\n}\n\nfunction seedViewTypes(rows: Array<{ ID: string; DriverClass: string; Name: string }>): void {\n (ViewTypeEngine.Instance as unknown as { _viewTypes: MJViewTypeEntity[] })._viewTypes =\n rows as unknown as MJViewTypeEntity[];\n}\n\ndescribe('BaseViewTypeDescriptor defaults', () => {\n class PlainViewType extends BaseViewTypeDescriptor {\n readonly Name = 'PlainViewType';\n readonly DisplayName = 'Plain';\n readonly Icon = 'fa-solid fa-shapes';\n readonly RendererComponent = class {};\n }\n\n it('defaults IsAvailableFor to true', () => {\n expect(new PlainViewType().IsAvailableFor(entity())).toBe(true);\n });\n\n it('defaults EnsureAvailabilityData to a resolved no-op', async () => {\n await expect(new PlainViewType().EnsureAvailabilityData()).resolves.toBeUndefined();\n });\n});\n\ndescribe('ViewTypeEngine', () => {\n beforeEach(() => {\n availableFlag = true;\n ensureCalls = 0;\n seedViewTypes([{ ID: 'VT1', DriverClass: 'TestAvailViewType', Name: 'Test' }]);\n });\n\n it('GetDescriptor resolves a registered descriptor by DriverClass', () => {\n const d = ViewTypeEngine.Instance.GetDescriptor('TestAvailViewType');\n expect(d).toBeTruthy();\n expect(d?.Name).toBe('TestAvailViewType');\n });\n\n it('GetDescriptor returns null for an unregistered DriverClass (no blank-item fallback)', () => {\n // A seeded MJ: View Types row whose descriptor class was never built (e.g. TagCloudViewType)\n // must NOT resolve to the abstract base fallback — that would render a blank switcher item.\n expect(ViewTypeEngine.Instance.GetDescriptor('TotallyUnregisteredViewType')).toBeNull();\n });\n\n it('GetAvailableViewTypeRows omits rows whose descriptor is unregistered', () => {\n seedViewTypes([\n { ID: 'VT1', DriverClass: 'TestAvailViewType', Name: 'Test' },\n { ID: 'VT2', DriverClass: 'TotallyUnregisteredViewType', Name: 'Ghost' },\n ]);\n const rows = ViewTypeEngine.Instance.GetAvailableViewTypeRows(entity());\n expect(rows.map(r => r.ViewType.ID)).toEqual(['VT1']);\n });\n\n it('GetAvailableViewTypeRows pairs the row with its descriptor when available', () => {\n const rows = ViewTypeEngine.Instance.GetAvailableViewTypeRows(entity());\n expect(rows).toHaveLength(1);\n expect(rows[0].ViewType.ID).toBe('VT1');\n expect(rows[0].Descriptor.Name).toBe('TestAvailViewType');\n });\n\n it('GetAvailableViewTypeRows excludes view types whose predicate is false', () => {\n availableFlag = false;\n expect(ViewTypeEngine.Instance.GetAvailableViewTypeRows(entity())).toHaveLength(0);\n });\n\n it('GetAvailableViewTypes returns just the descriptors', () => {\n const descriptors = ViewTypeEngine.Instance.GetAvailableViewTypes(entity());\n expect(descriptors.map(d => d.Name)).toEqual(['TestAvailViewType']);\n });\n\n it('EnsureAvailabilityData invokes each descriptor hook once', async () => {\n await ViewTypeEngine.Instance.EnsureAvailabilityData();\n expect(ensureCalls).toBe(1);\n });\n\n it('returns nothing for a null entity', () => {\n expect(ViewTypeEngine.Instance.GetAvailableViewTypeRows(null as unknown as EntityInfo)).toHaveLength(0);\n });\n});\n"]}
@@ -802,7 +802,7 @@ export class AggregateSetupDialogComponent {
802
802
  return field.Name;
803
803
  }
804
804
  static ɵfac = function AggregateSetupDialogComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AggregateSetupDialogComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
805
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AggregateSetupDialogComponent, selectors: [["mj-aggregate-setup-dialog"]], inputs: { Entity: "Entity", Aggregate: "Aggregate", IsOpen: "IsOpen" }, outputs: { Close: "Close", Save: "Save" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 75, vars: 23, consts: [[1, "dialog-backdrop"], [1, "dialog-panel"], [1, "dialog-header"], [1, "header-title"], [1, "fa-solid", "fa-chart-simple"], ["title", "Close", 1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "mode-selector"], ["title", "Pick a column and function", 1, "mode-btn", 3, "click"], [1, "fa-solid", "fa-wand-sparkles"], ["title", "Write custom SQL expression", 1, "mode-btn", 3, "click"], [1, "fa-solid", "fa-code"], ["title", "Describe in natural language", 1, "mode-btn", 3, "click"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "dialog-content"], [1, "mode-content", "simple-mode"], [1, "mode-content", "advanced-mode"], [1, "mode-content", "smart-mode"], [1, "config-section"], [1, "section-divider"], [1, "form-section"], [1, "form-label", "required"], ["type", "text", "placeholder", "e.g., Total Revenue, Average Order Value", 1, "form-input", 3, "ngModelChange", "ngModel"], [1, "form-label"], [1, "optional"], ["type", "text", "placeholder", "Brief explanation of what this shows", 1, "form-input", 3, "ngModelChange", "ngModel"], [1, "display-type-toggle"], [1, "display-type-btn", 3, "click"], [1, "fa-solid", "fa-id-card"], [1, "fa-solid", "fa-table-columns"], [1, "icon-selector"], [1, "selected-icon"], [1, "icon-grid"], [1, "icon-btn", 3, "active", "title"], [1, "dialog-footer"], [1, "footer-left"], [1, "footer-btn", "save-btn", "primary", 3, "click", "disabled"], [1, "fa-solid", "fa-check"], [1, "footer-btn", "cancel-btn", 3, "click"], [1, "validation-message"], [1, "dialog-backdrop", 3, "click"], [1, "mode-description"], [1, "fa-solid", "fa-info-circle"], [1, "function-grid"], [1, "function-btn", 3, "active", "title"], [1, "form-select", 3, "ngModelChange", "ngModel"], ["value", ""], [3, "value"], [1, "field-hint", "warning"], [1, "field-hint", "info"], [1, "expression-preview"], [1, "function-btn", 3, "click", "title"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "preview-label"], [1, "preview-code"], ["placeholder", "e.g., SUM(Amount * Quantity) or COUNT(CASE WHEN Status = 'Active' THEN 1 END)", "rows", "3", 1, "form-textarea", "code-input", 3, "ngModelChange", "ngModel"], [1, "field-hint"], [1, "fa-solid", "fa-lightbulb"], [1, "form-label", "examples-label"], [1, "example-chips"], [1, "example-chip", 3, "click"], [1, "smart-input-container"], ["placeholder", "e.g., 'Total revenue from completed orders' or 'Average order value for premium customers'", "rows", "3", 1, "form-textarea", "smart-input", 3, "ngModelChange", "ngModel", "disabled"], [1, "generate-btn", 3, "disabled"], [1, "generated-section"], [1, "smart-tips"], [1, "tips-header"], [1, "tips-list"], [1, "generate-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "generated-header"], ["title", "Edit prompt", 1, "clear-generated-btn", 3, "click"], [1, "fa-solid", "fa-pen"], [1, "generated-code"], [1, "generated-info"], ["title", "Remove icon", 1, "clear-icon-btn", 3, "click"], [1, "icon-btn", 3, "click", "title"], [1, "fa-solid", "fa-exclamation-circle"]], template: function AggregateSetupDialogComponent_Template(rf, ctx) { if (rf & 1) {
805
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AggregateSetupDialogComponent, selectors: [["mj-aggregate-setup-dialog"]], inputs: { Entity: "Entity", Aggregate: "Aggregate", IsOpen: "IsOpen" }, outputs: { Close: "Close", Save: "Save" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 75, vars: 23, consts: [[1, "dialog-backdrop"], [1, "dialog-panel"], [1, "dialog-header"], [1, "header-title"], [1, "fa-solid", "fa-chart-simple"], ["title", "Close", "aria-label", "Close dialog", 1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "mode-selector"], ["title", "Pick a column and function", 1, "mode-btn", 3, "click"], [1, "fa-solid", "fa-wand-sparkles"], ["title", "Write custom SQL expression", 1, "mode-btn", 3, "click"], [1, "fa-solid", "fa-code"], ["title", "Describe in natural language", 1, "mode-btn", 3, "click"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "dialog-content"], [1, "mode-content", "simple-mode"], [1, "mode-content", "advanced-mode"], [1, "mode-content", "smart-mode"], [1, "config-section"], [1, "section-divider"], [1, "form-section"], [1, "form-label", "required"], ["type", "text", "placeholder", "e.g., Total Revenue, Average Order Value", 1, "form-input", 3, "ngModelChange", "ngModel"], [1, "form-label"], [1, "optional"], ["type", "text", "placeholder", "Brief explanation of what this shows", 1, "form-input", 3, "ngModelChange", "ngModel"], [1, "display-type-toggle"], [1, "display-type-btn", 3, "click"], [1, "fa-solid", "fa-id-card"], [1, "fa-solid", "fa-table-columns"], [1, "icon-selector"], [1, "selected-icon"], [1, "icon-grid"], [1, "icon-btn", 3, "active", "title"], [1, "dialog-footer"], [1, "footer-left"], [1, "footer-btn", "save-btn", "primary", 3, "click", "disabled"], [1, "fa-solid", "fa-check"], [1, "footer-btn", "cancel-btn", 3, "click"], [1, "validation-message"], [1, "dialog-backdrop", 3, "click"], [1, "mode-description"], [1, "fa-solid", "fa-info-circle"], [1, "function-grid"], [1, "function-btn", 3, "active", "title"], [1, "form-select", 3, "ngModelChange", "ngModel"], ["value", ""], [3, "value"], [1, "field-hint", "warning"], [1, "field-hint", "info"], [1, "expression-preview"], [1, "function-btn", 3, "click", "title"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "preview-label"], [1, "preview-code"], ["placeholder", "e.g., SUM(Amount * Quantity) or COUNT(CASE WHEN Status = 'Active' THEN 1 END)", "rows", "3", 1, "form-textarea", "code-input", 3, "ngModelChange", "ngModel"], [1, "field-hint"], [1, "fa-solid", "fa-lightbulb"], [1, "form-label", "examples-label"], [1, "example-chips"], [1, "example-chip", 3, "click"], [1, "smart-input-container"], ["placeholder", "e.g., 'Total revenue from completed orders' or 'Average order value for premium customers'", "rows", "3", 1, "form-textarea", "smart-input", 3, "ngModelChange", "ngModel", "disabled"], [1, "generate-btn", 3, "disabled"], [1, "generated-section"], [1, "smart-tips"], [1, "tips-header"], [1, "tips-list"], [1, "generate-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "generated-header"], ["title", "Edit prompt", 1, "clear-generated-btn", 3, "click"], [1, "fa-solid", "fa-pen"], [1, "generated-code"], [1, "generated-info"], ["title", "Remove icon", "aria-label", "Remove icon", 1, "clear-icon-btn", 3, "click"], [1, "icon-btn", 3, "click", "title"], [1, "fa-solid", "fa-exclamation-circle"]], template: function AggregateSetupDialogComponent_Template(rf, ctx) { if (rf & 1) {
806
806
  i0.ɵɵconditionalCreate(0, AggregateSetupDialogComponent_Conditional_0_Template, 1, 0, "div", 0);
807
807
  i0.ɵɵelementStart(1, "div", 1)(2, "div", 2)(3, "div", 3);
808
808
  i0.ɵɵelement(4, "i", 4);
@@ -935,7 +935,7 @@ export class AggregateSetupDialogComponent {
935
935
  }
936
936
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AggregateSetupDialogComponent, [{
937
937
  type: Component,
938
- args: [{ standalone: false, selector: 'mj-aggregate-setup-dialog', template: "<!-- Dialog Backdrop -->\n@if (IsOpen) {\n <div class=\"dialog-backdrop\" (click)=\"onClose()\"></div>\n}\n\n<!-- Dialog Panel -->\n<div class=\"dialog-panel\" [class.open]=\"IsOpen\">\n <!-- Header -->\n <div class=\"dialog-header\">\n <div class=\"header-title\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n <span>{{ Aggregate ? 'Edit Aggregate' : 'Add Aggregate' }}</span>\n </div>\n <button class=\"close-btn\" (click)=\"onClose()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Mode Selector -->\n <div class=\"mode-selector\">\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'simple'\"\n (click)=\"setMode('simple')\"\n title=\"Pick a column and function\">\n <i class=\"fa-solid fa-wand-sparkles\"></i>\n <span>Simple</span>\n </button>\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'advanced'\"\n (click)=\"setMode('advanced')\"\n title=\"Write custom SQL expression\">\n <i class=\"fa-solid fa-code\"></i>\n <span>Advanced</span>\n </button>\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'smart'\"\n (click)=\"setMode('smart')\"\n title=\"Describe in natural language\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Smart</span>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"dialog-content\">\n <!-- Simple Mode -->\n @if (Mode === 'simple') {\n <div class=\"mode-content simple-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Select a column and an aggregate function to calculate</span>\n </div>\n\n <!-- Function Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Function</label>\n <div class=\"function-grid\">\n @for (func of AggregateFunctions; track func.value) {\n <button\n class=\"function-btn\"\n [class.active]=\"SelectedFunction === func.value\"\n (click)=\"selectFunction(func.value)\"\n [title]=\"func.label\">\n <i [class]=\"func.icon\"></i>\n <span>{{ func.label }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Column Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Column</label>\n <select\n class=\"form-select\"\n [(ngModel)]=\"SelectedColumn\"\n (ngModelChange)=\"onColumnSelected($event)\">\n <option value=\"\">Select a column...</option>\n @for (field of AvailableFields; track field.ID) {\n <option [value]=\"field.Name\">{{ getFieldDisplayLabel(field) }}</option>\n }\n </select>\n @if (SelectedFunction !== 'COUNT' && SelectedFunction !== 'COUNT_DISTINCT' && NumericFields.length === 0) {\n <div class=\"field-hint warning\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>No numeric fields available. Use COUNT or switch to Advanced mode.</span>\n </div>\n }\n @if (SelectedFunction === 'COUNT' && !SelectedColumn) {\n <div class=\"field-hint info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>No column selected - will count all records (COUNT(*))</span>\n </div>\n }\n </div>\n\n <!-- Preview -->\n @if (SelectedFunction && (SelectedColumn || SelectedFunction === 'COUNT')) {\n <div class=\"expression-preview\">\n <label class=\"preview-label\">Expression Preview</label>\n <code class=\"preview-code\">{{\n !SelectedColumn && SelectedFunction === 'COUNT' ? 'COUNT(*)' :\n SelectedFunction === 'COUNT_DISTINCT' ? 'COUNT(DISTINCT ' + SelectedColumn + ')' :\n SelectedFunction + '(' + SelectedColumn + ')'\n }}</code>\n </div>\n }\n </div>\n }\n\n <!-- Advanced Mode -->\n @if (Mode === 'advanced') {\n <div class=\"mode-content advanced-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Write a custom SQL aggregate expression. Use field names as they appear in the database.</span>\n </div>\n\n <div class=\"form-section\">\n <label class=\"form-label\">SQL Expression</label>\n <textarea\n class=\"form-textarea code-input\"\n [(ngModel)]=\"Expression\"\n placeholder=\"e.g., SUM(Amount * Quantity) or COUNT(CASE WHEN Status = 'Active' THEN 1 END)\"\n rows=\"3\"\n ></textarea>\n <div class=\"field-hint\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tip: Use brackets for column names with spaces, e.g., [Total Amount]</span>\n </div>\n </div>\n\n <!-- Quick Examples -->\n <div class=\"form-section\">\n <label class=\"form-label examples-label\">Quick Examples</label>\n <div class=\"example-chips\">\n <button class=\"example-chip\" (click)=\"Expression = 'SUM(Amount)'\">\n SUM(Amount)\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'AVG(Price * Quantity)'\">\n AVG(Price * Quantity)\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'COUNT(CASE WHEN Status = \\'Active\\' THEN 1 END)'\">\n COUNT with condition\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'SUM(CASE WHEN Type = \\'Credit\\' THEN Amount ELSE -Amount END)'\">\n Conditional SUM\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Smart Mode -->\n @if (Mode === 'smart') {\n <div class=\"mode-content smart-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Describe what you want to calculate in plain English. AI will generate the expression.</span>\n </div>\n\n <div class=\"form-section\">\n <label class=\"form-label\">What would you like to calculate?</label>\n <div class=\"smart-input-container\">\n <textarea\n class=\"form-textarea smart-input\"\n [(ngModel)]=\"SmartPrompt\"\n [disabled]=\"!!GeneratedExpression\"\n placeholder=\"e.g., 'Total revenue from completed orders' or 'Average order value for premium customers'\"\n rows=\"3\"\n ></textarea>\n @if (!GeneratedExpression) {\n <button\n class=\"generate-btn\"\n [disabled]=\"!SmartPrompt.trim() || IsGenerating\"\n (click)=\"onGenerateFromPrompt()\">\n @if (IsGenerating) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Generating...</span>\n } @else {\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Generate</span>\n }\n </button>\n }\n </div>\n </div>\n\n <!-- Generated Expression -->\n @if (GeneratedExpression) {\n <div class=\"generated-section\">\n <div class=\"generated-header\">\n <label class=\"form-label\">Generated Expression</label>\n <button class=\"clear-generated-btn\" (click)=\"clearGeneratedExpression()\" title=\"Edit prompt\">\n <i class=\"fa-solid fa-pen\"></i>\n <span>Edit Prompt</span>\n </button>\n </div>\n <code class=\"generated-code\">{{ GeneratedExpression }}</code>\n <div class=\"generated-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>This expression was generated from your description. Clear it to edit the prompt.</span>\n </div>\n </div>\n }\n\n <!-- Smart Mode Tips -->\n <div class=\"smart-tips\">\n <div class=\"tips-header\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tips for better results:</span>\n </div>\n <ul class=\"tips-list\">\n <li>Be specific about which fields to use</li>\n <li>Mention any conditions or filters</li>\n <li>Specify the type of calculation (sum, average, count, etc.)</li>\n </ul>\n </div>\n </div>\n }\n\n <!-- Common Configuration -->\n <div class=\"config-section\">\n <div class=\"section-divider\">\n <span>Display Options</span>\n </div>\n\n <!-- Label -->\n <div class=\"form-section\">\n <label class=\"form-label required\">Label</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"Label\"\n placeholder=\"e.g., Total Revenue, Average Order Value\"\n />\n </div>\n\n <!-- Description (optional) -->\n <div class=\"form-section\">\n <label class=\"form-label\">Description <span class=\"optional\">(optional)</span></label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"Description\"\n placeholder=\"Brief explanation of what this shows\"\n />\n </div>\n\n <!-- Display Type -->\n <div class=\"form-section\">\n <label class=\"form-label\">Display As</label>\n <div class=\"display-type-toggle\">\n <button\n class=\"display-type-btn\"\n [class.active]=\"DisplayType === 'card'\"\n (click)=\"DisplayType = 'card'\">\n <i class=\"fa-solid fa-id-card\"></i>\n <span>Card</span>\n <small>Shows in summary panel</small>\n </button>\n <button\n class=\"display-type-btn\"\n [class.active]=\"DisplayType === 'column'\"\n (click)=\"DisplayType = 'column'\">\n <i class=\"fa-solid fa-table-columns\"></i>\n <span>Column Footer</span>\n <small>Shows at bottom of grid</small>\n </button>\n </div>\n </div>\n\n <!-- Icon Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Icon <span class=\"optional\">(optional)</span></label>\n <div class=\"icon-selector\">\n @if (Icon) {\n <div class=\"selected-icon\">\n <i [class]=\"Icon\"></i>\n <button class=\"clear-icon-btn\" (click)=\"clearIcon()\" title=\"Remove icon\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n <div class=\"icon-grid\">\n @for (icon of CommonIcons; track icon) {\n <button\n class=\"icon-btn\"\n [class.active]=\"Icon === icon\"\n (click)=\"selectIcon(icon)\"\n [title]=\"icon\">\n <i [class]=\"icon\"></i>\n </button>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <div class=\"footer-left\">\n <button\n class=\"footer-btn save-btn primary\"\n [disabled]=\"!IsValid\"\n (click)=\"onSave()\">\n <i class=\"fa-solid fa-check\"></i>\n <span>{{ Aggregate ? 'Update' : 'Add' }} Aggregate</span>\n </button>\n </div>\n <button class=\"footer-btn cancel-btn\" (click)=\"onClose()\">\n Cancel\n </button>\n </div>\n\n <!-- Validation Message -->\n @if (ValidationMessage) {\n <div class=\"validation-message\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>{{ ValidationMessage }}</span>\n </div>\n }\n</div>\n", styles: ["/* Dialog Backdrop */\n.dialog-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n z-index: 2000;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Dialog Panel */\n.dialog-panel {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0.95);\n width: 520px;\n max-width: calc(100vw - 40px);\n max-height: calc(100vh - 60px);\n background: var(--mj-bg-surface);\n border-radius: 16px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);\n z-index: 2001;\n display: flex;\n flex-direction: column;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.2s ease, transform 0.2s ease;\n}\n\n.dialog-panel.open {\n opacity: 1;\n pointer-events: auto;\n transform: translate(-50%, -50%) scale(1);\n}\n\n/* Dialog Header */\n.dialog-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 18px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 16px 16px 0 0;\n}\n\n.header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 17px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.header-title i {\n color: var(--mj-brand-primary);\n font-size: 18px;\n}\n\n.close-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 8px;\n cursor: pointer;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.close-btn:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n\n/* Mode Selector */\n.mode-selector {\n display: flex;\n gap: 8px;\n padding: 16px 24px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.mode-btn {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 14px 16px;\n border: 2px solid var(--mj-border-default);\n border-radius: 12px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mode-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n}\n\n.mode-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.mode-btn i {\n font-size: 18px;\n color: var(--mj-text-muted);\n}\n\n.mode-btn.active i {\n color: var(--mj-brand-primary);\n}\n\n.mode-btn span {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mode-btn.active span {\n color: var(--mj-color-info-700);\n}\n\n/* Dialog Content */\n.dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n}\n\n/* Mode Content */\n.mode-content {\n margin-bottom: 20px;\n}\n\n.mode-description {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 12px 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, var(--mj-bg-surface));\n border-radius: 10px;\n margin-bottom: 20px;\n font-size: 13px;\n color: var(--mj-color-info-700);\n line-height: 1.5;\n}\n\n.mode-description i {\n color: var(--mj-brand-primary);\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n/* Form Sections */\n.form-section {\n margin-bottom: 18px;\n}\n\n.form-label {\n display: block;\n margin-bottom: 8px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.form-label.required::after {\n content: ' *';\n color: var(--mj-status-error);\n}\n\n.form-label .optional {\n font-weight: 400;\n color: var(--mj-text-disabled);\n}\n\n.form-label.examples-label {\n color: var(--mj-text-muted);\n}\n\n/* Form Inputs */\n.form-input,\n.form-select,\n.form-textarea {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n background: var(--mj-bg-surface);\n}\n\n.form-input:focus,\n.form-select:focus,\n.form-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.form-textarea {\n resize: vertical;\n min-height: 80px;\n}\n\n.form-textarea.code-input {\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 13px;\n}\n\n/* Function Grid */\n.function-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 8px;\n}\n\n.function-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 14px 10px;\n border: 2px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.function-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n}\n\n.function-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.function-btn i {\n font-size: 18px;\n color: var(--mj-text-muted);\n}\n\n.function-btn.active i {\n color: var(--mj-brand-primary);\n}\n\n.function-btn span {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.function-btn.active span {\n color: var(--mj-color-info-700);\n}\n\n/* Field Hint */\n.field-hint {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n margin-top: 8px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.field-hint.warning {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}\n\n.field-hint.warning i {\n color: var(--mj-status-warning);\n}\n\n.field-hint.info {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.field-hint.info i {\n color: var(--mj-brand-primary);\n}\n\n.field-hint i {\n color: var(--mj-text-disabled);\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n/* Expression Preview */\n.expression-preview {\n margin-top: 16px;\n padding: 14px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n}\n\n.preview-label {\n display: block;\n margin-bottom: 8px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.preview-code {\n display: block;\n padding: 12px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n/* Example Chips */\n.example-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.example-chip {\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 11px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.example-chip:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-color-info-700);\n}\n\n/* Smart Mode Input */\n.smart-input-container {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.smart-input {\n min-height: 100px;\n}\n\n.smart-input:disabled {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.generate-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 12px 20px;\n background: var(--mj-brand-primary);\n border: none;\n border-radius: 10px;\n color: var(--mj-text-inverse);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n align-self: flex-start;\n}\n\n.generate-btn:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.generate-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none;\n}\n\n/* Generated Section */\n.generated-section {\n margin-top: 16px;\n padding: 16px;\n background: var(--mj-status-success-bg);\n border: 1px solid var(--mj-status-success-border);\n border-radius: 10px;\n}\n\n.generated-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.generated-header .form-label {\n margin-bottom: 0;\n color: var(--mj-status-success-text);\n}\n\n.clear-generated-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-status-success-border);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-status-success-text);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.clear-generated-btn:hover {\n background: var(--mj-status-success-text);\n color: var(--mj-text-inverse);\n}\n\n.generated-code {\n display: block;\n padding: 12px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-status-success-border);\n border-radius: 6px;\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 13px;\n color: var(--mj-status-success-text);\n margin-bottom: 10px;\n}\n\n.generated-info {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-status-success-text);\n}\n\n.generated-info i {\n margin-top: 1px;\n}\n\n/* Smart Tips */\n.smart-tips {\n margin-top: 20px;\n padding: 14px;\n background: var(--mj-status-warning-bg);\n border: 1px solid var(--mj-status-warning-border);\n border-radius: 10px;\n}\n\n.tips-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 10px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-status-warning-text);\n}\n\n.tips-header i {\n color: var(--mj-status-warning);\n}\n\n.tips-list {\n margin: 0;\n padding-left: 20px;\n font-size: 12px;\n color: var(--mj-status-warning-text);\n line-height: 1.6;\n}\n\n/* Config Section */\n.config-section {\n padding-top: 16px;\n}\n\n.section-divider {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 20px;\n}\n\n.section-divider::before,\n.section-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--mj-border-default);\n}\n\n.section-divider span {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n/* Display Type Toggle */\n.display-type-toggle {\n display: flex;\n gap: 12px;\n}\n\n.display-type-btn {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 16px 14px;\n border: 2px solid var(--mj-border-default);\n border-radius: 12px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.display-type-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n}\n\n.display-type-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.display-type-btn i {\n font-size: 22px;\n color: var(--mj-text-muted);\n}\n\n.display-type-btn.active i {\n color: var(--mj-brand-primary);\n}\n\n.display-type-btn span {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.display-type-btn.active span {\n color: var(--mj-color-info-700);\n}\n\n.display-type-btn small {\n font-size: 11px;\n color: var(--mj-text-disabled);\n}\n\n.display-type-btn.active small {\n color: var(--mj-brand-primary);\n}\n\n/* Icon Selector */\n.icon-selector {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.selected-icon {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n border-radius: 8px;\n width: fit-content;\n}\n\n.selected-icon i:first-child {\n font-size: 20px;\n color: var(--mj-brand-primary);\n}\n\n.clear-icon-btn {\n width: 24px;\n height: 24px;\n border: none;\n background: var(--mj-bg-surface);\n border-radius: 50%;\n cursor: pointer;\n color: var(--mj-text-disabled);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 11px;\n transition: all 0.15s ease;\n}\n\n.clear-icon-btn:hover {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n.icon-grid {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.icon-btn {\n width: 40px;\n height: 40px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n}\n\n.icon-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n}\n\n.icon-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.icon-btn i {\n font-size: 16px;\n color: var(--mj-text-muted);\n}\n\n.icon-btn.active i {\n color: var(--mj-brand-primary);\n}\n\n/* Dialog Footer */\n.dialog-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 18px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 0 0 16px 16px;\n}\n\n.footer-left {\n display: flex;\n gap: 10px;\n}\n\n.footer-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 11px 18px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.footer-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-text-disabled);\n}\n\n.footer-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.footer-btn.primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.footer-btn.primary:hover:not(:disabled) {\n background: var(--mj-color-info-600);\n border-color: var(--mj-color-info-600);\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n/* Validation Message */\n.validation-message {\n position: absolute;\n bottom: 80px;\n left: 24px;\n right: 24px;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n background: var(--mj-status-error-bg);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.validation-message i {\n flex-shrink: 0;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n .dialog-panel {\n width: calc(100vw - 24px);\n max-height: calc(100vh - 40px);\n border-radius: 12px;\n }\n\n .dialog-header,\n .dialog-footer {\n border-radius: 0;\n }\n\n .dialog-header {\n border-radius: 12px 12px 0 0;\n }\n\n .dialog-footer {\n border-radius: 0 0 12px 12px;\n }\n\n .mode-btn {\n padding: 10px 8px;\n }\n\n .mode-btn span {\n font-size: 11px;\n }\n\n .function-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .display-type-toggle {\n flex-direction: column;\n }\n}\n"] }]
938
+ args: [{ standalone: false, selector: 'mj-aggregate-setup-dialog', template: "<!-- Dialog Backdrop -->\n@if (IsOpen) {\n <div class=\"dialog-backdrop\" (click)=\"onClose()\"></div>\n}\n\n<!-- Dialog Panel -->\n<div class=\"dialog-panel\" [class.open]=\"IsOpen\">\n <!-- Header -->\n <div class=\"dialog-header\">\n <div class=\"header-title\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n <span>{{ Aggregate ? 'Edit Aggregate' : 'Add Aggregate' }}</span>\n </div>\n <button class=\"close-btn\" (click)=\"onClose()\" title=\"Close\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Mode Selector -->\n <div class=\"mode-selector\">\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'simple'\"\n (click)=\"setMode('simple')\"\n title=\"Pick a column and function\">\n <i class=\"fa-solid fa-wand-sparkles\"></i>\n <span>Simple</span>\n </button>\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'advanced'\"\n (click)=\"setMode('advanced')\"\n title=\"Write custom SQL expression\">\n <i class=\"fa-solid fa-code\"></i>\n <span>Advanced</span>\n </button>\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'smart'\"\n (click)=\"setMode('smart')\"\n title=\"Describe in natural language\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Smart</span>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"dialog-content\">\n <!-- Simple Mode -->\n @if (Mode === 'simple') {\n <div class=\"mode-content simple-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Select a column and an aggregate function to calculate</span>\n </div>\n\n <!-- Function Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Function</label>\n <div class=\"function-grid\">\n @for (func of AggregateFunctions; track func.value) {\n <button\n class=\"function-btn\"\n [class.active]=\"SelectedFunction === func.value\"\n (click)=\"selectFunction(func.value)\"\n [title]=\"func.label\">\n <i [class]=\"func.icon\"></i>\n <span>{{ func.label }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Column Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Column</label>\n <select\n class=\"form-select\"\n [(ngModel)]=\"SelectedColumn\"\n (ngModelChange)=\"onColumnSelected($event)\">\n <option value=\"\">Select a column...</option>\n @for (field of AvailableFields; track field.ID) {\n <option [value]=\"field.Name\">{{ getFieldDisplayLabel(field) }}</option>\n }\n </select>\n @if (SelectedFunction !== 'COUNT' && SelectedFunction !== 'COUNT_DISTINCT' && NumericFields.length === 0) {\n <div class=\"field-hint warning\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>No numeric fields available. Use COUNT or switch to Advanced mode.</span>\n </div>\n }\n @if (SelectedFunction === 'COUNT' && !SelectedColumn) {\n <div class=\"field-hint info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>No column selected - will count all records (COUNT(*))</span>\n </div>\n }\n </div>\n\n <!-- Preview -->\n @if (SelectedFunction && (SelectedColumn || SelectedFunction === 'COUNT')) {\n <div class=\"expression-preview\">\n <label class=\"preview-label\">Expression Preview</label>\n <code class=\"preview-code\">{{\n !SelectedColumn && SelectedFunction === 'COUNT' ? 'COUNT(*)' :\n SelectedFunction === 'COUNT_DISTINCT' ? 'COUNT(DISTINCT ' + SelectedColumn + ')' :\n SelectedFunction + '(' + SelectedColumn + ')'\n }}</code>\n </div>\n }\n </div>\n }\n\n <!-- Advanced Mode -->\n @if (Mode === 'advanced') {\n <div class=\"mode-content advanced-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Write a custom SQL aggregate expression. Use field names as they appear in the database.</span>\n </div>\n\n <div class=\"form-section\">\n <label class=\"form-label\">SQL Expression</label>\n <textarea\n class=\"form-textarea code-input\"\n [(ngModel)]=\"Expression\"\n placeholder=\"e.g., SUM(Amount * Quantity) or COUNT(CASE WHEN Status = 'Active' THEN 1 END)\"\n rows=\"3\"\n ></textarea>\n <div class=\"field-hint\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tip: Use brackets for column names with spaces, e.g., [Total Amount]</span>\n </div>\n </div>\n\n <!-- Quick Examples -->\n <div class=\"form-section\">\n <label class=\"form-label examples-label\">Quick Examples</label>\n <div class=\"example-chips\">\n <button class=\"example-chip\" (click)=\"Expression = 'SUM(Amount)'\">\n SUM(Amount)\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'AVG(Price * Quantity)'\">\n AVG(Price * Quantity)\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'COUNT(CASE WHEN Status = \\'Active\\' THEN 1 END)'\">\n COUNT with condition\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'SUM(CASE WHEN Type = \\'Credit\\' THEN Amount ELSE -Amount END)'\">\n Conditional SUM\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Smart Mode -->\n @if (Mode === 'smart') {\n <div class=\"mode-content smart-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Describe what you want to calculate in plain English. AI will generate the expression.</span>\n </div>\n\n <div class=\"form-section\">\n <label class=\"form-label\">What would you like to calculate?</label>\n <div class=\"smart-input-container\">\n <textarea\n class=\"form-textarea smart-input\"\n [(ngModel)]=\"SmartPrompt\"\n [disabled]=\"!!GeneratedExpression\"\n placeholder=\"e.g., 'Total revenue from completed orders' or 'Average order value for premium customers'\"\n rows=\"3\"\n ></textarea>\n @if (!GeneratedExpression) {\n <button\n class=\"generate-btn\"\n [disabled]=\"!SmartPrompt.trim() || IsGenerating\"\n (click)=\"onGenerateFromPrompt()\">\n @if (IsGenerating) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Generating...</span>\n } @else {\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Generate</span>\n }\n </button>\n }\n </div>\n </div>\n\n <!-- Generated Expression -->\n @if (GeneratedExpression) {\n <div class=\"generated-section\">\n <div class=\"generated-header\">\n <label class=\"form-label\">Generated Expression</label>\n <button class=\"clear-generated-btn\" (click)=\"clearGeneratedExpression()\" title=\"Edit prompt\">\n <i class=\"fa-solid fa-pen\"></i>\n <span>Edit Prompt</span>\n </button>\n </div>\n <code class=\"generated-code\">{{ GeneratedExpression }}</code>\n <div class=\"generated-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>This expression was generated from your description. Clear it to edit the prompt.</span>\n </div>\n </div>\n }\n\n <!-- Smart Mode Tips -->\n <div class=\"smart-tips\">\n <div class=\"tips-header\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tips for better results:</span>\n </div>\n <ul class=\"tips-list\">\n <li>Be specific about which fields to use</li>\n <li>Mention any conditions or filters</li>\n <li>Specify the type of calculation (sum, average, count, etc.)</li>\n </ul>\n </div>\n </div>\n }\n\n <!-- Common Configuration -->\n <div class=\"config-section\">\n <div class=\"section-divider\">\n <span>Display Options</span>\n </div>\n\n <!-- Label -->\n <div class=\"form-section\">\n <label class=\"form-label required\">Label</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"Label\"\n placeholder=\"e.g., Total Revenue, Average Order Value\"\n />\n </div>\n\n <!-- Description (optional) -->\n <div class=\"form-section\">\n <label class=\"form-label\">Description <span class=\"optional\">(optional)</span></label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"Description\"\n placeholder=\"Brief explanation of what this shows\"\n />\n </div>\n\n <!-- Display Type -->\n <div class=\"form-section\">\n <label class=\"form-label\">Display As</label>\n <div class=\"display-type-toggle\">\n <button\n class=\"display-type-btn\"\n [class.active]=\"DisplayType === 'card'\"\n (click)=\"DisplayType = 'card'\">\n <i class=\"fa-solid fa-id-card\"></i>\n <span>Card</span>\n <small>Shows in summary panel</small>\n </button>\n <button\n class=\"display-type-btn\"\n [class.active]=\"DisplayType === 'column'\"\n (click)=\"DisplayType = 'column'\">\n <i class=\"fa-solid fa-table-columns\"></i>\n <span>Column Footer</span>\n <small>Shows at bottom of grid</small>\n </button>\n </div>\n </div>\n\n <!-- Icon Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Icon <span class=\"optional\">(optional)</span></label>\n <div class=\"icon-selector\">\n @if (Icon) {\n <div class=\"selected-icon\">\n <i [class]=\"Icon\"></i>\n <button class=\"clear-icon-btn\" (click)=\"clearIcon()\" title=\"Remove icon\" aria-label=\"Remove icon\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n <div class=\"icon-grid\">\n @for (icon of CommonIcons; track icon) {\n <button\n class=\"icon-btn\"\n [class.active]=\"Icon === icon\"\n (click)=\"selectIcon(icon)\"\n [title]=\"icon\">\n <i [class]=\"icon\"></i>\n </button>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <div class=\"footer-left\">\n <button\n class=\"footer-btn save-btn primary\"\n [disabled]=\"!IsValid\"\n (click)=\"onSave()\">\n <i class=\"fa-solid fa-check\"></i>\n <span>{{ Aggregate ? 'Update' : 'Add' }} Aggregate</span>\n </button>\n </div>\n <button class=\"footer-btn cancel-btn\" (click)=\"onClose()\">\n Cancel\n </button>\n </div>\n\n <!-- Validation Message -->\n @if (ValidationMessage) {\n <div class=\"validation-message\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>{{ ValidationMessage }}</span>\n </div>\n }\n</div>\n", styles: ["/* Dialog Backdrop */\n.dialog-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n z-index: 2000;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Dialog Panel */\n.dialog-panel {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0.95);\n width: 520px;\n max-width: calc(100vw - 40px);\n max-height: calc(100vh - 60px);\n background: var(--mj-bg-surface);\n border-radius: 16px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);\n z-index: 2001;\n display: flex;\n flex-direction: column;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.2s ease, transform 0.2s ease;\n}\n\n.dialog-panel.open {\n opacity: 1;\n pointer-events: auto;\n transform: translate(-50%, -50%) scale(1);\n}\n\n/* Dialog Header */\n.dialog-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 18px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 16px 16px 0 0;\n}\n\n.header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 17px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.header-title i {\n color: var(--mj-brand-primary);\n font-size: 18px;\n}\n\n.close-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 8px;\n cursor: pointer;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.close-btn:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n\n/* Mode Selector */\n.mode-selector {\n display: flex;\n gap: 8px;\n padding: 16px 24px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.mode-btn {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 14px 16px;\n border: 2px solid var(--mj-border-default);\n border-radius: 12px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mode-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n}\n\n.mode-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.mode-btn i {\n font-size: 18px;\n color: var(--mj-text-muted);\n}\n\n.mode-btn.active i {\n color: var(--mj-brand-primary);\n}\n\n.mode-btn span {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mode-btn.active span {\n color: var(--mj-color-info-700);\n}\n\n/* Dialog Content */\n.dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n}\n\n/* Mode Content */\n.mode-content {\n margin-bottom: 20px;\n}\n\n.mode-description {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 12px 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, var(--mj-bg-surface));\n border-radius: 10px;\n margin-bottom: 20px;\n font-size: 13px;\n color: var(--mj-color-info-700);\n line-height: 1.5;\n}\n\n.mode-description i {\n color: var(--mj-brand-primary);\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n/* Form Sections */\n.form-section {\n margin-bottom: 18px;\n}\n\n.form-label {\n display: block;\n margin-bottom: 8px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.form-label.required::after {\n content: ' *';\n color: var(--mj-status-error);\n}\n\n.form-label .optional {\n font-weight: 400;\n color: var(--mj-text-disabled);\n}\n\n.form-label.examples-label {\n color: var(--mj-text-muted);\n}\n\n/* Form Inputs */\n.form-input,\n.form-select,\n.form-textarea {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n background: var(--mj-bg-surface);\n}\n\n.form-input:focus,\n.form-select:focus,\n.form-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.form-textarea {\n resize: vertical;\n min-height: 80px;\n}\n\n.form-textarea.code-input {\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 13px;\n}\n\n/* Function Grid */\n.function-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 8px;\n}\n\n.function-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 14px 10px;\n border: 2px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.function-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n}\n\n.function-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.function-btn i {\n font-size: 18px;\n color: var(--mj-text-muted);\n}\n\n.function-btn.active i {\n color: var(--mj-brand-primary);\n}\n\n.function-btn span {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.function-btn.active span {\n color: var(--mj-color-info-700);\n}\n\n/* Field Hint */\n.field-hint {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n margin-top: 8px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.field-hint.warning {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}\n\n.field-hint.warning i {\n color: var(--mj-status-warning);\n}\n\n.field-hint.info {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.field-hint.info i {\n color: var(--mj-brand-primary);\n}\n\n.field-hint i {\n color: var(--mj-text-disabled);\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n/* Expression Preview */\n.expression-preview {\n margin-top: 16px;\n padding: 14px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n}\n\n.preview-label {\n display: block;\n margin-bottom: 8px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.preview-code {\n display: block;\n padding: 12px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n/* Example Chips */\n.example-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.example-chip {\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 11px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.example-chip:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-color-info-700);\n}\n\n/* Smart Mode Input */\n.smart-input-container {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.smart-input {\n min-height: 100px;\n}\n\n.smart-input:disabled {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.generate-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 12px 20px;\n background: var(--mj-brand-primary);\n border: none;\n border-radius: 10px;\n color: var(--mj-text-inverse);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n align-self: flex-start;\n}\n\n.generate-btn:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.generate-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none;\n}\n\n/* Generated Section */\n.generated-section {\n margin-top: 16px;\n padding: 16px;\n background: var(--mj-status-success-bg);\n border: 1px solid var(--mj-status-success-border);\n border-radius: 10px;\n}\n\n.generated-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.generated-header .form-label {\n margin-bottom: 0;\n color: var(--mj-status-success-text);\n}\n\n.clear-generated-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-status-success-border);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-status-success-text);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.clear-generated-btn:hover {\n background: var(--mj-status-success-text);\n color: var(--mj-text-inverse);\n}\n\n.generated-code {\n display: block;\n padding: 12px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-status-success-border);\n border-radius: 6px;\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 13px;\n color: var(--mj-status-success-text);\n margin-bottom: 10px;\n}\n\n.generated-info {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-status-success-text);\n}\n\n.generated-info i {\n margin-top: 1px;\n}\n\n/* Smart Tips */\n.smart-tips {\n margin-top: 20px;\n padding: 14px;\n background: var(--mj-status-warning-bg);\n border: 1px solid var(--mj-status-warning-border);\n border-radius: 10px;\n}\n\n.tips-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 10px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-status-warning-text);\n}\n\n.tips-header i {\n color: var(--mj-status-warning);\n}\n\n.tips-list {\n margin: 0;\n padding-left: 20px;\n font-size: 12px;\n color: var(--mj-status-warning-text);\n line-height: 1.6;\n}\n\n/* Config Section */\n.config-section {\n padding-top: 16px;\n}\n\n.section-divider {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 20px;\n}\n\n.section-divider::before,\n.section-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--mj-border-default);\n}\n\n.section-divider span {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n/* Display Type Toggle */\n.display-type-toggle {\n display: flex;\n gap: 12px;\n}\n\n.display-type-btn {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 16px 14px;\n border: 2px solid var(--mj-border-default);\n border-radius: 12px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.display-type-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n}\n\n.display-type-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.display-type-btn i {\n font-size: 22px;\n color: var(--mj-text-muted);\n}\n\n.display-type-btn.active i {\n color: var(--mj-brand-primary);\n}\n\n.display-type-btn span {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.display-type-btn.active span {\n color: var(--mj-color-info-700);\n}\n\n.display-type-btn small {\n font-size: 11px;\n color: var(--mj-text-disabled);\n}\n\n.display-type-btn.active small {\n color: var(--mj-brand-primary);\n}\n\n/* Icon Selector */\n.icon-selector {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.selected-icon {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n border-radius: 8px;\n width: fit-content;\n}\n\n.selected-icon i:first-child {\n font-size: 20px;\n color: var(--mj-brand-primary);\n}\n\n.clear-icon-btn {\n width: 24px;\n height: 24px;\n border: none;\n background: var(--mj-bg-surface);\n border-radius: 50%;\n cursor: pointer;\n color: var(--mj-text-disabled);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 11px;\n transition: all 0.15s ease;\n}\n\n.clear-icon-btn:hover {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n.icon-grid {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.icon-btn {\n width: 40px;\n height: 40px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n}\n\n.icon-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n}\n\n.icon-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.icon-btn i {\n font-size: 16px;\n color: var(--mj-text-muted);\n}\n\n.icon-btn.active i {\n color: var(--mj-brand-primary);\n}\n\n/* Dialog Footer */\n.dialog-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 18px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 0 0 16px 16px;\n}\n\n.footer-left {\n display: flex;\n gap: 10px;\n}\n\n.footer-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 11px 18px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.footer-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-text-disabled);\n}\n\n.footer-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.footer-btn.primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.footer-btn.primary:hover:not(:disabled) {\n background: var(--mj-color-info-600);\n border-color: var(--mj-color-info-600);\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n/* Validation Message */\n.validation-message {\n position: absolute;\n bottom: 80px;\n left: 24px;\n right: 24px;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n background: var(--mj-status-error-bg);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.validation-message i {\n flex-shrink: 0;\n}\n\n/* Responsive */\n@media (max-width: 600px) {\n .dialog-panel {\n width: calc(100vw - 24px);\n max-height: calc(100vh - 40px);\n border-radius: 12px;\n }\n\n .dialog-header,\n .dialog-footer {\n border-radius: 0;\n }\n\n .dialog-header {\n border-radius: 12px 12px 0 0;\n }\n\n .dialog-footer {\n border-radius: 0 0 12px 12px;\n }\n\n .mode-btn {\n padding: 10px 8px;\n }\n\n .mode-btn span {\n font-size: 11px;\n }\n\n .function-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .display-type-toggle {\n flex-direction: column;\n }\n}\n"] }]
939
939
  }], () => [{ type: i0.ChangeDetectorRef }], { Entity: [{
940
940
  type: Input
941
941
  }], Aggregate: [{
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-setup-dialog.component.js","sourceRoot":"","sources":["../../../src/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.ts","../../../src/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, ChangeDetectorRef } from '@angular/core';\nimport { EntityInfo, EntityFieldInfo } from '@memberjunction/core';\nimport { MJUserViewEntity_IGridAggregate as ViewGridAggregate } from '@memberjunction/core-entities';\n\ntype AggregateDisplayType = 'column' | 'card';\n\n/**\n * Aggregate function types for simple mode\n */\nexport type AggregateFunctionType = 'SUM' | 'AVG' | 'COUNT' | 'MIN' | 'MAX' | 'COUNT_DISTINCT';\n\n/**\n * Aggregate setup mode\n */\nexport type AggregateSetupMode = 'simple' | 'advanced' | 'smart';\n\n/**\n * Default icons for aggregate functions\n */\nconst FUNCTION_ICONS: Record<AggregateFunctionType, string> = {\n 'SUM': 'fa-solid fa-plus',\n 'AVG': 'fa-solid fa-divide',\n 'COUNT': 'fa-solid fa-hashtag',\n 'MIN': 'fa-solid fa-arrow-down',\n 'MAX': 'fa-solid fa-arrow-up',\n 'COUNT_DISTINCT': 'fa-solid fa-fingerprint'\n};\n\n/**\n * Friendly labels for aggregate functions\n */\nconst FUNCTION_LABELS: Record<AggregateFunctionType, string> = {\n 'SUM': 'Sum',\n 'AVG': 'Average',\n 'COUNT': 'Count',\n 'MIN': 'Minimum',\n 'MAX': 'Maximum',\n 'COUNT_DISTINCT': 'Count Distinct'\n};\n\n/**\n * AggregateSetupDialogComponent - Dialog for creating/editing aggregate configurations\n *\n * Features:\n * - Simple mode: Pick column + aggregate function (SUM, AVG, COUNT, etc.)\n * - Advanced mode: Write custom SQL expression\n * - Smart mode: Describe in natural language (AI generates expression)\n * - Progressive disclosure - simple mode covers 80% of use cases\n * - Display type selection (card or column)\n * - Optional label and icon customization\n */\n@Component({\n standalone: false,\n selector: 'mj-aggregate-setup-dialog',\n templateUrl: './aggregate-setup-dialog.component.html',\n styleUrls: ['./aggregate-setup-dialog.component.css']\n})\nexport class AggregateSetupDialogComponent implements OnInit, OnChanges {\n /**\n * The entity being viewed - provides field information\n */\n @Input() Entity: EntityInfo | null = null;\n\n /**\n * Existing aggregate to edit (null for new)\n */\n @Input() Aggregate: ViewGridAggregate | null = null;\n\n /**\n * Whether the dialog is open\n */\n @Input() IsOpen: boolean = false;\n\n /**\n * Emitted when the dialog is closed\n */\n @Output() Close = new EventEmitter<void>();\n\n /**\n * Emitted when an aggregate is saved\n */\n @Output() Save = new EventEmitter<ViewGridAggregate>();\n\n // Setup mode\n Mode: AggregateSetupMode = 'simple';\n\n // Simple mode state\n SelectedColumn: string = '';\n SelectedFunction: AggregateFunctionType = 'SUM';\n\n // Advanced mode state\n Expression: string = '';\n\n // Smart mode state\n SmartPrompt: string = '';\n GeneratedExpression: string = '';\n IsGenerating: boolean = false;\n\n // Common configuration\n Label: string = '';\n DisplayType: AggregateDisplayType = 'card';\n Icon: string = '';\n Description: string = '';\n\n // Available functions\n AggregateFunctions: { value: AggregateFunctionType; label: string; icon: string }[] = [\n { value: 'SUM', label: 'Sum', icon: 'fa-solid fa-plus' },\n { value: 'AVG', label: 'Average', icon: 'fa-solid fa-divide' },\n { value: 'COUNT', label: 'Count', icon: 'fa-solid fa-hashtag' },\n { value: 'MIN', label: 'Minimum', icon: 'fa-solid fa-arrow-down' },\n { value: 'MAX', label: 'Maximum', icon: 'fa-solid fa-arrow-up' },\n { value: 'COUNT_DISTINCT', label: 'Count Distinct', icon: 'fa-solid fa-fingerprint' }\n ];\n\n // Common icons for selection\n CommonIcons: string[] = [\n 'fa-solid fa-chart-line',\n 'fa-solid fa-chart-bar',\n 'fa-solid fa-dollar-sign',\n 'fa-solid fa-users',\n 'fa-solid fa-shopping-cart',\n 'fa-solid fa-box',\n 'fa-solid fa-clock',\n 'fa-solid fa-calendar',\n 'fa-solid fa-percent',\n 'fa-solid fa-star',\n 'fa-solid fa-check-circle',\n 'fa-solid fa-exclamation-triangle'\n ];\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n ngOnInit(): void {\n this.initializeFromAggregate();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n // Re-initialize when dialog opens or aggregate changes\n if (changes['IsOpen'] && this.IsOpen) {\n this.initializeFromAggregate();\n }\n if (changes['Aggregate'] && !changes['Aggregate'].firstChange) {\n this.initializeFromAggregate();\n }\n }\n\n /**\n * Initialize form state from existing aggregate (if editing)\n */\n private initializeFromAggregate(): void {\n if (!this.Aggregate) {\n this.resetForm();\n return;\n }\n\n const agg = this.Aggregate;\n\n // Set common fields\n this.Label = agg.label || '';\n this.DisplayType = agg.displayType || 'card';\n this.Icon = agg.icon || '';\n this.Description = agg.description || '';\n\n // Determine mode and populate fields\n if (agg.smartPrompt) {\n // Smart mode - has AI prompt\n this.Mode = 'smart';\n this.SmartPrompt = agg.smartPrompt;\n this.GeneratedExpression = agg.expression;\n this.Expression = agg.expression;\n } else if (this.isSimpleExpression(agg.expression)) {\n // Simple mode - can be represented as function(column)\n this.Mode = 'simple';\n const parsed = this.parseSimpleExpression(agg.expression);\n if (parsed) {\n this.SelectedFunction = parsed.func;\n this.SelectedColumn = parsed.column;\n this._previousColumn = parsed.column; // Track for auto-label updates\n }\n } else {\n // Advanced mode - custom expression\n this.Mode = 'advanced';\n this.Expression = agg.expression;\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Reset form to default state\n */\n private resetForm(): void {\n this.Mode = 'simple';\n this.SelectedColumn = '';\n this._previousColumn = ''; // Reset previous column tracking\n this.SelectedFunction = 'SUM';\n this.Expression = '';\n this.SmartPrompt = '';\n this.GeneratedExpression = '';\n this.Label = '';\n this.DisplayType = 'card';\n this.Icon = '';\n this.Description = '';\n }\n\n /**\n * Check if an expression matches simple function(column) pattern\n * Also matches COUNT(*) for row counting\n */\n private isSimpleExpression(expression: string): boolean {\n const pattern = /^(SUM|AVG|COUNT|MIN|MAX|COUNT_DISTINCT)\\s*\\(\\s*(\\*|\\[?[\\w\\s]+\\]?)\\s*\\)$/i;\n return pattern.test(expression.trim());\n }\n\n /**\n * Parse a simple expression into function and column\n * Returns empty string for column if COUNT(*)\n */\n private parseSimpleExpression(expression: string): { func: AggregateFunctionType; column: string } | null {\n const match = expression.trim().match(/^(SUM|AVG|COUNT|MIN|MAX|COUNT_DISTINCT)\\s*\\(\\s*(\\*|\\[?([\\w\\s]+)\\]?)\\s*\\)$/i);\n if (!match) return null;\n\n const funcName = match[1].toUpperCase().replace(' ', '_') as AggregateFunctionType;\n // If it's COUNT(*), column will be empty string\n const column = match[2] === '*' ? '' : (match[3] || match[2]).trim();\n\n return { func: funcName, column };\n }\n\n /**\n * Get numeric fields for SUM/AVG operations\n */\n get NumericFields(): EntityFieldInfo[] {\n if (!this.Entity) return [];\n return this.Entity.Fields.filter(f => {\n const sqlType = f.Type.toLowerCase();\n return sqlType.includes('int') ||\n sqlType.includes('decimal') ||\n sqlType.includes('numeric') ||\n sqlType.includes('float') ||\n sqlType.includes('real') ||\n sqlType.includes('money');\n });\n }\n\n /**\n * Get date/datetime fields for MIN/MAX operations\n */\n get DateFields(): EntityFieldInfo[] {\n if (!this.Entity) return [];\n return this.Entity.Fields.filter(f => {\n const sqlType = f.Type.toLowerCase();\n return sqlType.includes('date') || sqlType.includes('time');\n });\n }\n\n /**\n * Get string fields for MIN/MAX operations\n */\n get StringFields(): EntityFieldInfo[] {\n if (!this.Entity) return [];\n return this.Entity.Fields.filter(f => {\n const sqlType = f.Type.toLowerCase();\n return (sqlType.includes('char') || sqlType.includes('text')) && !f.IsBinaryFieldType;\n });\n }\n\n /**\n * Get all fields for COUNT operations\n */\n get AllFields(): EntityFieldInfo[] {\n if (!this.Entity) return [];\n return this.Entity.Fields.filter(f => !f.IsBinaryFieldType);\n }\n\n /**\n * Get fields available for current function\n */\n get AvailableFields(): EntityFieldInfo[] {\n // COUNT and COUNT_DISTINCT can use any field\n if (this.SelectedFunction === 'COUNT' || this.SelectedFunction === 'COUNT_DISTINCT') {\n return this.AllFields;\n }\n // SUM and AVG only work with numeric fields\n if (this.SelectedFunction === 'SUM' || this.SelectedFunction === 'AVG') {\n return this.NumericFields;\n }\n // MIN and MAX work with numeric and date fields only (not text/varchar)\n if (this.SelectedFunction === 'MIN' || this.SelectedFunction === 'MAX') {\n // Combine numeric and date fields (avoiding duplicates)\n const combined = [...this.NumericFields, ...this.DateFields];\n // Remove duplicates by field ID\n const seen = new Set<string>();\n return combined.filter(f => {\n if (seen.has(f.ID)) return false;\n seen.add(f.ID);\n return true;\n });\n }\n // Default to all fields\n return this.AllFields;\n }\n\n /**\n * Set the setup mode\n */\n setMode(mode: AggregateSetupMode): void {\n this.Mode = mode;\n\n // If switching from smart mode with generated expression, populate advanced mode\n if (mode === 'advanced' && this.GeneratedExpression) {\n this.Expression = this.GeneratedExpression;\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Handle function button click - captures old value before changing\n * Called from template when user clicks a function button\n */\n selectFunction(newFunction: AggregateFunctionType): void {\n if (newFunction === this.SelectedFunction) return;\n\n const previousFunction = this.SelectedFunction;\n this.SelectedFunction = newFunction;\n this.onFunctionChange(previousFunction);\n }\n\n /**\n * Handle column selection from dropdown\n * Called from template with the NEW value from ngModelChange\n * We track the previous value internally\n */\n private _previousColumn: string = '';\n\n onColumnSelected(newColumn: string): void {\n const previousColumn = this._previousColumn;\n this._previousColumn = newColumn;\n this.onColumnChange(previousColumn);\n }\n\n /**\n * Handle function selection change (internal)\n */\n private onFunctionChange(previousFunction: AggregateFunctionType): void {\n // Check if label matches what we would have auto-generated for the OLD function\n const shouldUpdateLabel = this.shouldAutoUpdateLabel(previousFunction, this.SelectedColumn);\n\n // If current column is not valid for new function, clear it\n const validFields = this.AvailableFields;\n if (this.SelectedColumn && !validFields.find(f => f.Name === this.SelectedColumn)) {\n this.SelectedColumn = '';\n }\n\n // Update label if it was auto-generated (matches old pattern) or is empty\n // For COUNT, we can set auto-label even without a column (COUNT(*))\n if (shouldUpdateLabel && (this.SelectedColumn || this.SelectedFunction === 'COUNT')) {\n this.setAutoLabel();\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Handle column selection change\n */\n onColumnChange(previousColumn: string): void {\n // Check if label matches what we would have auto-generated for the OLD column\n const shouldUpdateLabel = this.shouldAutoUpdateLabel(this.SelectedFunction, previousColumn);\n\n // Update label if it was auto-generated (matches old pattern) or is empty\n if (shouldUpdateLabel) {\n this.setAutoLabel();\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Check if the current label matches what we would have auto-generated\n * for the given function and column. If so, we should update the label\n * when either changes. This allows auto-updating while preserving manual edits.\n */\n private shouldAutoUpdateLabel(func: AggregateFunctionType, column: string): boolean {\n // Always update if label is empty\n if (!this.Label) return true;\n\n // Check if current label matches the auto-generated pattern for the old values\n const expectedOldLabel = this.generateAutoLabel(func, column);\n return expectedOldLabel !== null && this.Label === expectedOldLabel;\n }\n\n /**\n * Generate what the auto-label would be for a given function and column\n * Returns null if we can't generate a label (missing data)\n * Handles COUNT(*) case with \"Record Count\" label\n */\n private generateAutoLabel(func: AggregateFunctionType, column: string): string | null {\n if (!func) return null;\n\n // Handle COUNT(*) case - no column needed\n if (!column) {\n if (func === 'COUNT') {\n return 'Record Count';\n }\n return null; // Other functions require a column\n }\n\n const field = this.Entity?.Fields.find(f => f.Name === column);\n if (!field) return null;\n\n const funcLabel = FUNCTION_LABELS[func] || func;\n return `${funcLabel} of ${field.DisplayNameOrName}`;\n }\n\n /**\n * Set the auto-generated label based on current function and column\n */\n private setAutoLabel(): void {\n const label = this.generateAutoLabel(this.SelectedFunction, this.SelectedColumn);\n if (label) {\n this.Label = label;\n }\n }\n\n /**\n * Select an icon\n */\n selectIcon(icon: string): void {\n this.Icon = icon;\n this.cdr.detectChanges();\n }\n\n /**\n * Clear the icon\n */\n clearIcon(): void {\n this.Icon = '';\n this.cdr.detectChanges();\n }\n\n /**\n * Build the expression based on current mode\n */\n private buildExpression(): string {\n switch (this.Mode) {\n case 'simple':\n if (!this.SelectedFunction) return '';\n // COUNT can work without a column (uses COUNT(*))\n // COUNT_DISTINCT requires a column\n if (!this.SelectedColumn) {\n if (this.SelectedFunction === 'COUNT') {\n return 'COUNT(*)';\n }\n return ''; // Other functions require a column\n }\n // Use brackets for column names with spaces\n const columnRef = this.SelectedColumn.includes(' ')\n ? `[${this.SelectedColumn}]`\n : this.SelectedColumn;\n if (this.SelectedFunction === 'COUNT_DISTINCT') {\n return `COUNT(DISTINCT ${columnRef})`;\n }\n return `${this.SelectedFunction}(${columnRef})`;\n\n case 'advanced':\n return this.Expression;\n\n case 'smart':\n return this.GeneratedExpression || '';\n\n default:\n return '';\n }\n }\n\n /**\n * Check if the form is valid\n */\n get IsValid(): boolean {\n const hasExpression = !!this.buildExpression();\n const hasLabel = !!this.Label.trim();\n return hasExpression && hasLabel;\n }\n\n /**\n * Get validation message\n */\n get ValidationMessage(): string {\n if (!this.Label.trim()) {\n return 'Please enter a label for this aggregate';\n }\n\n switch (this.Mode) {\n case 'simple':\n // COUNT can work without a column (uses COUNT(*))\n // All other functions require a column\n if (!this.SelectedColumn && this.SelectedFunction !== 'COUNT') {\n return 'Please select a column';\n }\n break;\n case 'advanced':\n if (!this.Expression.trim()) return 'Please enter an expression';\n break;\n case 'smart':\n if (!this.GeneratedExpression) return 'Please generate an expression from your prompt';\n break;\n }\n\n return '';\n }\n\n /**\n * Save the aggregate\n */\n onSave(): void {\n if (!this.IsValid) return;\n\n const aggregate: ViewGridAggregate = {\n id: this.Aggregate?.id || this.generateId(),\n expression: this.buildExpression(),\n displayType: this.DisplayType,\n label: this.Label.trim(),\n description: this.Description.trim() || undefined,\n icon: this.Icon || this.getDefaultIcon(),\n enabled: this.Aggregate?.enabled ?? true, // Preserve enabled state when editing, default to true for new\n order: this.Aggregate?.order || 0\n };\n\n // Only include smartPrompt if in smart mode\n if (this.Mode === 'smart' && this.SmartPrompt) {\n aggregate.smartPrompt = this.SmartPrompt;\n }\n\n this.Save.emit(aggregate);\n this.onClose();\n }\n\n /**\n * Get default icon based on function\n */\n private getDefaultIcon(): string {\n if (this.Mode === 'simple' && this.SelectedFunction) {\n return FUNCTION_ICONS[this.SelectedFunction] || 'fa-solid fa-chart-simple';\n }\n return 'fa-solid fa-chart-simple';\n }\n\n /**\n * Generate a unique ID for new aggregates\n */\n private generateId(): string {\n return `agg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n }\n\n /**\n * Close the dialog\n */\n onClose(): void {\n this.Close.emit();\n }\n\n /**\n * Handle smart prompt generation (placeholder - actual AI call would be made by parent)\n */\n onGenerateFromPrompt(): void {\n if (!this.SmartPrompt.trim()) return;\n\n this.IsGenerating = true;\n this.cdr.detectChanges();\n\n // This would typically call an AI service\n // For now, we'll show a placeholder message\n // The actual implementation would be handled by the parent component or a service\n setTimeout(() => {\n this.IsGenerating = false;\n // Placeholder: In real implementation, this would be replaced with AI-generated expression\n this.GeneratedExpression = '/* AI-generated expression will appear here */';\n this.cdr.detectChanges();\n }, 500);\n }\n\n /**\n * Clear the generated expression and allow editing prompt\n */\n clearGeneratedExpression(): void {\n this.GeneratedExpression = '';\n this.cdr.detectChanges();\n }\n\n /**\n * Get display label for field dropdown showing \"Name (DisplayName)\" format\n * If DisplayName equals Name or is not set, just show Name\n */\n getFieldDisplayLabel(field: EntityFieldInfo): string {\n if (field.DisplayName && field.DisplayName !== field.Name) {\n return `${field.Name} (${field.DisplayName})`;\n }\n return field.Name;\n }\n}\n","<!-- Dialog Backdrop -->\n@if (IsOpen) {\n <div class=\"dialog-backdrop\" (click)=\"onClose()\"></div>\n}\n\n<!-- Dialog Panel -->\n<div class=\"dialog-panel\" [class.open]=\"IsOpen\">\n <!-- Header -->\n <div class=\"dialog-header\">\n <div class=\"header-title\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n <span>{{ Aggregate ? 'Edit Aggregate' : 'Add Aggregate' }}</span>\n </div>\n <button class=\"close-btn\" (click)=\"onClose()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Mode Selector -->\n <div class=\"mode-selector\">\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'simple'\"\n (click)=\"setMode('simple')\"\n title=\"Pick a column and function\">\n <i class=\"fa-solid fa-wand-sparkles\"></i>\n <span>Simple</span>\n </button>\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'advanced'\"\n (click)=\"setMode('advanced')\"\n title=\"Write custom SQL expression\">\n <i class=\"fa-solid fa-code\"></i>\n <span>Advanced</span>\n </button>\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'smart'\"\n (click)=\"setMode('smart')\"\n title=\"Describe in natural language\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Smart</span>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"dialog-content\">\n <!-- Simple Mode -->\n @if (Mode === 'simple') {\n <div class=\"mode-content simple-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Select a column and an aggregate function to calculate</span>\n </div>\n\n <!-- Function Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Function</label>\n <div class=\"function-grid\">\n @for (func of AggregateFunctions; track func.value) {\n <button\n class=\"function-btn\"\n [class.active]=\"SelectedFunction === func.value\"\n (click)=\"selectFunction(func.value)\"\n [title]=\"func.label\">\n <i [class]=\"func.icon\"></i>\n <span>{{ func.label }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Column Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Column</label>\n <select\n class=\"form-select\"\n [(ngModel)]=\"SelectedColumn\"\n (ngModelChange)=\"onColumnSelected($event)\">\n <option value=\"\">Select a column...</option>\n @for (field of AvailableFields; track field.ID) {\n <option [value]=\"field.Name\">{{ getFieldDisplayLabel(field) }}</option>\n }\n </select>\n @if (SelectedFunction !== 'COUNT' && SelectedFunction !== 'COUNT_DISTINCT' && NumericFields.length === 0) {\n <div class=\"field-hint warning\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>No numeric fields available. Use COUNT or switch to Advanced mode.</span>\n </div>\n }\n @if (SelectedFunction === 'COUNT' && !SelectedColumn) {\n <div class=\"field-hint info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>No column selected - will count all records (COUNT(*))</span>\n </div>\n }\n </div>\n\n <!-- Preview -->\n @if (SelectedFunction && (SelectedColumn || SelectedFunction === 'COUNT')) {\n <div class=\"expression-preview\">\n <label class=\"preview-label\">Expression Preview</label>\n <code class=\"preview-code\">{{\n !SelectedColumn && SelectedFunction === 'COUNT' ? 'COUNT(*)' :\n SelectedFunction === 'COUNT_DISTINCT' ? 'COUNT(DISTINCT ' + SelectedColumn + ')' :\n SelectedFunction + '(' + SelectedColumn + ')'\n }}</code>\n </div>\n }\n </div>\n }\n\n <!-- Advanced Mode -->\n @if (Mode === 'advanced') {\n <div class=\"mode-content advanced-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Write a custom SQL aggregate expression. Use field names as they appear in the database.</span>\n </div>\n\n <div class=\"form-section\">\n <label class=\"form-label\">SQL Expression</label>\n <textarea\n class=\"form-textarea code-input\"\n [(ngModel)]=\"Expression\"\n placeholder=\"e.g., SUM(Amount * Quantity) or COUNT(CASE WHEN Status = 'Active' THEN 1 END)\"\n rows=\"3\"\n ></textarea>\n <div class=\"field-hint\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tip: Use brackets for column names with spaces, e.g., [Total Amount]</span>\n </div>\n </div>\n\n <!-- Quick Examples -->\n <div class=\"form-section\">\n <label class=\"form-label examples-label\">Quick Examples</label>\n <div class=\"example-chips\">\n <button class=\"example-chip\" (click)=\"Expression = 'SUM(Amount)'\">\n SUM(Amount)\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'AVG(Price * Quantity)'\">\n AVG(Price * Quantity)\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'COUNT(CASE WHEN Status = \\'Active\\' THEN 1 END)'\">\n COUNT with condition\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'SUM(CASE WHEN Type = \\'Credit\\' THEN Amount ELSE -Amount END)'\">\n Conditional SUM\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Smart Mode -->\n @if (Mode === 'smart') {\n <div class=\"mode-content smart-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Describe what you want to calculate in plain English. AI will generate the expression.</span>\n </div>\n\n <div class=\"form-section\">\n <label class=\"form-label\">What would you like to calculate?</label>\n <div class=\"smart-input-container\">\n <textarea\n class=\"form-textarea smart-input\"\n [(ngModel)]=\"SmartPrompt\"\n [disabled]=\"!!GeneratedExpression\"\n placeholder=\"e.g., 'Total revenue from completed orders' or 'Average order value for premium customers'\"\n rows=\"3\"\n ></textarea>\n @if (!GeneratedExpression) {\n <button\n class=\"generate-btn\"\n [disabled]=\"!SmartPrompt.trim() || IsGenerating\"\n (click)=\"onGenerateFromPrompt()\">\n @if (IsGenerating) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Generating...</span>\n } @else {\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Generate</span>\n }\n </button>\n }\n </div>\n </div>\n\n <!-- Generated Expression -->\n @if (GeneratedExpression) {\n <div class=\"generated-section\">\n <div class=\"generated-header\">\n <label class=\"form-label\">Generated Expression</label>\n <button class=\"clear-generated-btn\" (click)=\"clearGeneratedExpression()\" title=\"Edit prompt\">\n <i class=\"fa-solid fa-pen\"></i>\n <span>Edit Prompt</span>\n </button>\n </div>\n <code class=\"generated-code\">{{ GeneratedExpression }}</code>\n <div class=\"generated-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>This expression was generated from your description. Clear it to edit the prompt.</span>\n </div>\n </div>\n }\n\n <!-- Smart Mode Tips -->\n <div class=\"smart-tips\">\n <div class=\"tips-header\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tips for better results:</span>\n </div>\n <ul class=\"tips-list\">\n <li>Be specific about which fields to use</li>\n <li>Mention any conditions or filters</li>\n <li>Specify the type of calculation (sum, average, count, etc.)</li>\n </ul>\n </div>\n </div>\n }\n\n <!-- Common Configuration -->\n <div class=\"config-section\">\n <div class=\"section-divider\">\n <span>Display Options</span>\n </div>\n\n <!-- Label -->\n <div class=\"form-section\">\n <label class=\"form-label required\">Label</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"Label\"\n placeholder=\"e.g., Total Revenue, Average Order Value\"\n />\n </div>\n\n <!-- Description (optional) -->\n <div class=\"form-section\">\n <label class=\"form-label\">Description <span class=\"optional\">(optional)</span></label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"Description\"\n placeholder=\"Brief explanation of what this shows\"\n />\n </div>\n\n <!-- Display Type -->\n <div class=\"form-section\">\n <label class=\"form-label\">Display As</label>\n <div class=\"display-type-toggle\">\n <button\n class=\"display-type-btn\"\n [class.active]=\"DisplayType === 'card'\"\n (click)=\"DisplayType = 'card'\">\n <i class=\"fa-solid fa-id-card\"></i>\n <span>Card</span>\n <small>Shows in summary panel</small>\n </button>\n <button\n class=\"display-type-btn\"\n [class.active]=\"DisplayType === 'column'\"\n (click)=\"DisplayType = 'column'\">\n <i class=\"fa-solid fa-table-columns\"></i>\n <span>Column Footer</span>\n <small>Shows at bottom of grid</small>\n </button>\n </div>\n </div>\n\n <!-- Icon Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Icon <span class=\"optional\">(optional)</span></label>\n <div class=\"icon-selector\">\n @if (Icon) {\n <div class=\"selected-icon\">\n <i [class]=\"Icon\"></i>\n <button class=\"clear-icon-btn\" (click)=\"clearIcon()\" title=\"Remove icon\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n <div class=\"icon-grid\">\n @for (icon of CommonIcons; track icon) {\n <button\n class=\"icon-btn\"\n [class.active]=\"Icon === icon\"\n (click)=\"selectIcon(icon)\"\n [title]=\"icon\">\n <i [class]=\"icon\"></i>\n </button>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <div class=\"footer-left\">\n <button\n class=\"footer-btn save-btn primary\"\n [disabled]=\"!IsValid\"\n (click)=\"onSave()\">\n <i class=\"fa-solid fa-check\"></i>\n <span>{{ Aggregate ? 'Update' : 'Add' }} Aggregate</span>\n </button>\n </div>\n <button class=\"footer-btn cancel-btn\" (click)=\"onClose()\">\n Cancel\n </button>\n </div>\n\n <!-- Validation Message -->\n @if (ValidationMessage) {\n <div class=\"validation-message\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>{{ ValidationMessage }}</span>\n </div>\n }\n</div>\n"]}
1
+ {"version":3,"file":"aggregate-setup-dialog.component.js","sourceRoot":"","sources":["../../../src/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.ts","../../../src/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, ChangeDetectorRef } from '@angular/core';\nimport { EntityInfo, EntityFieldInfo } from '@memberjunction/core';\nimport { MJUserViewEntity_IGridAggregate as ViewGridAggregate } from '@memberjunction/core-entities';\n\ntype AggregateDisplayType = 'column' | 'card';\n\n/**\n * Aggregate function types for simple mode\n */\nexport type AggregateFunctionType = 'SUM' | 'AVG' | 'COUNT' | 'MIN' | 'MAX' | 'COUNT_DISTINCT';\n\n/**\n * Aggregate setup mode\n */\nexport type AggregateSetupMode = 'simple' | 'advanced' | 'smart';\n\n/**\n * Default icons for aggregate functions\n */\nconst FUNCTION_ICONS: Record<AggregateFunctionType, string> = {\n 'SUM': 'fa-solid fa-plus',\n 'AVG': 'fa-solid fa-divide',\n 'COUNT': 'fa-solid fa-hashtag',\n 'MIN': 'fa-solid fa-arrow-down',\n 'MAX': 'fa-solid fa-arrow-up',\n 'COUNT_DISTINCT': 'fa-solid fa-fingerprint'\n};\n\n/**\n * Friendly labels for aggregate functions\n */\nconst FUNCTION_LABELS: Record<AggregateFunctionType, string> = {\n 'SUM': 'Sum',\n 'AVG': 'Average',\n 'COUNT': 'Count',\n 'MIN': 'Minimum',\n 'MAX': 'Maximum',\n 'COUNT_DISTINCT': 'Count Distinct'\n};\n\n/**\n * AggregateSetupDialogComponent - Dialog for creating/editing aggregate configurations\n *\n * Features:\n * - Simple mode: Pick column + aggregate function (SUM, AVG, COUNT, etc.)\n * - Advanced mode: Write custom SQL expression\n * - Smart mode: Describe in natural language (AI generates expression)\n * - Progressive disclosure - simple mode covers 80% of use cases\n * - Display type selection (card or column)\n * - Optional label and icon customization\n */\n@Component({\n standalone: false,\n selector: 'mj-aggregate-setup-dialog',\n templateUrl: './aggregate-setup-dialog.component.html',\n styleUrls: ['./aggregate-setup-dialog.component.css']\n})\nexport class AggregateSetupDialogComponent implements OnInit, OnChanges {\n /**\n * The entity being viewed - provides field information\n */\n @Input() Entity: EntityInfo | null = null;\n\n /**\n * Existing aggregate to edit (null for new)\n */\n @Input() Aggregate: ViewGridAggregate | null = null;\n\n /**\n * Whether the dialog is open\n */\n @Input() IsOpen: boolean = false;\n\n /**\n * Emitted when the dialog is closed\n */\n @Output() Close = new EventEmitter<void>();\n\n /**\n * Emitted when an aggregate is saved\n */\n @Output() Save = new EventEmitter<ViewGridAggregate>();\n\n // Setup mode\n Mode: AggregateSetupMode = 'simple';\n\n // Simple mode state\n SelectedColumn: string = '';\n SelectedFunction: AggregateFunctionType = 'SUM';\n\n // Advanced mode state\n Expression: string = '';\n\n // Smart mode state\n SmartPrompt: string = '';\n GeneratedExpression: string = '';\n IsGenerating: boolean = false;\n\n // Common configuration\n Label: string = '';\n DisplayType: AggregateDisplayType = 'card';\n Icon: string = '';\n Description: string = '';\n\n // Available functions\n AggregateFunctions: { value: AggregateFunctionType; label: string; icon: string }[] = [\n { value: 'SUM', label: 'Sum', icon: 'fa-solid fa-plus' },\n { value: 'AVG', label: 'Average', icon: 'fa-solid fa-divide' },\n { value: 'COUNT', label: 'Count', icon: 'fa-solid fa-hashtag' },\n { value: 'MIN', label: 'Minimum', icon: 'fa-solid fa-arrow-down' },\n { value: 'MAX', label: 'Maximum', icon: 'fa-solid fa-arrow-up' },\n { value: 'COUNT_DISTINCT', label: 'Count Distinct', icon: 'fa-solid fa-fingerprint' }\n ];\n\n // Common icons for selection\n CommonIcons: string[] = [\n 'fa-solid fa-chart-line',\n 'fa-solid fa-chart-bar',\n 'fa-solid fa-dollar-sign',\n 'fa-solid fa-users',\n 'fa-solid fa-shopping-cart',\n 'fa-solid fa-box',\n 'fa-solid fa-clock',\n 'fa-solid fa-calendar',\n 'fa-solid fa-percent',\n 'fa-solid fa-star',\n 'fa-solid fa-check-circle',\n 'fa-solid fa-exclamation-triangle'\n ];\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n ngOnInit(): void {\n this.initializeFromAggregate();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n // Re-initialize when dialog opens or aggregate changes\n if (changes['IsOpen'] && this.IsOpen) {\n this.initializeFromAggregate();\n }\n if (changes['Aggregate'] && !changes['Aggregate'].firstChange) {\n this.initializeFromAggregate();\n }\n }\n\n /**\n * Initialize form state from existing aggregate (if editing)\n */\n private initializeFromAggregate(): void {\n if (!this.Aggregate) {\n this.resetForm();\n return;\n }\n\n const agg = this.Aggregate;\n\n // Set common fields\n this.Label = agg.label || '';\n this.DisplayType = agg.displayType || 'card';\n this.Icon = agg.icon || '';\n this.Description = agg.description || '';\n\n // Determine mode and populate fields\n if (agg.smartPrompt) {\n // Smart mode - has AI prompt\n this.Mode = 'smart';\n this.SmartPrompt = agg.smartPrompt;\n this.GeneratedExpression = agg.expression;\n this.Expression = agg.expression;\n } else if (this.isSimpleExpression(agg.expression)) {\n // Simple mode - can be represented as function(column)\n this.Mode = 'simple';\n const parsed = this.parseSimpleExpression(agg.expression);\n if (parsed) {\n this.SelectedFunction = parsed.func;\n this.SelectedColumn = parsed.column;\n this._previousColumn = parsed.column; // Track for auto-label updates\n }\n } else {\n // Advanced mode - custom expression\n this.Mode = 'advanced';\n this.Expression = agg.expression;\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Reset form to default state\n */\n private resetForm(): void {\n this.Mode = 'simple';\n this.SelectedColumn = '';\n this._previousColumn = ''; // Reset previous column tracking\n this.SelectedFunction = 'SUM';\n this.Expression = '';\n this.SmartPrompt = '';\n this.GeneratedExpression = '';\n this.Label = '';\n this.DisplayType = 'card';\n this.Icon = '';\n this.Description = '';\n }\n\n /**\n * Check if an expression matches simple function(column) pattern\n * Also matches COUNT(*) for row counting\n */\n private isSimpleExpression(expression: string): boolean {\n const pattern = /^(SUM|AVG|COUNT|MIN|MAX|COUNT_DISTINCT)\\s*\\(\\s*(\\*|\\[?[\\w\\s]+\\]?)\\s*\\)$/i;\n return pattern.test(expression.trim());\n }\n\n /**\n * Parse a simple expression into function and column\n * Returns empty string for column if COUNT(*)\n */\n private parseSimpleExpression(expression: string): { func: AggregateFunctionType; column: string } | null {\n const match = expression.trim().match(/^(SUM|AVG|COUNT|MIN|MAX|COUNT_DISTINCT)\\s*\\(\\s*(\\*|\\[?([\\w\\s]+)\\]?)\\s*\\)$/i);\n if (!match) return null;\n\n const funcName = match[1].toUpperCase().replace(' ', '_') as AggregateFunctionType;\n // If it's COUNT(*), column will be empty string\n const column = match[2] === '*' ? '' : (match[3] || match[2]).trim();\n\n return { func: funcName, column };\n }\n\n /**\n * Get numeric fields for SUM/AVG operations\n */\n get NumericFields(): EntityFieldInfo[] {\n if (!this.Entity) return [];\n return this.Entity.Fields.filter(f => {\n const sqlType = f.Type.toLowerCase();\n return sqlType.includes('int') ||\n sqlType.includes('decimal') ||\n sqlType.includes('numeric') ||\n sqlType.includes('float') ||\n sqlType.includes('real') ||\n sqlType.includes('money');\n });\n }\n\n /**\n * Get date/datetime fields for MIN/MAX operations\n */\n get DateFields(): EntityFieldInfo[] {\n if (!this.Entity) return [];\n return this.Entity.Fields.filter(f => {\n const sqlType = f.Type.toLowerCase();\n return sqlType.includes('date') || sqlType.includes('time');\n });\n }\n\n /**\n * Get string fields for MIN/MAX operations\n */\n get StringFields(): EntityFieldInfo[] {\n if (!this.Entity) return [];\n return this.Entity.Fields.filter(f => {\n const sqlType = f.Type.toLowerCase();\n return (sqlType.includes('char') || sqlType.includes('text')) && !f.IsBinaryFieldType;\n });\n }\n\n /**\n * Get all fields for COUNT operations\n */\n get AllFields(): EntityFieldInfo[] {\n if (!this.Entity) return [];\n return this.Entity.Fields.filter(f => !f.IsBinaryFieldType);\n }\n\n /**\n * Get fields available for current function\n */\n get AvailableFields(): EntityFieldInfo[] {\n // COUNT and COUNT_DISTINCT can use any field\n if (this.SelectedFunction === 'COUNT' || this.SelectedFunction === 'COUNT_DISTINCT') {\n return this.AllFields;\n }\n // SUM and AVG only work with numeric fields\n if (this.SelectedFunction === 'SUM' || this.SelectedFunction === 'AVG') {\n return this.NumericFields;\n }\n // MIN and MAX work with numeric and date fields only (not text/varchar)\n if (this.SelectedFunction === 'MIN' || this.SelectedFunction === 'MAX') {\n // Combine numeric and date fields (avoiding duplicates)\n const combined = [...this.NumericFields, ...this.DateFields];\n // Remove duplicates by field ID\n const seen = new Set<string>();\n return combined.filter(f => {\n if (seen.has(f.ID)) return false;\n seen.add(f.ID);\n return true;\n });\n }\n // Default to all fields\n return this.AllFields;\n }\n\n /**\n * Set the setup mode\n */\n setMode(mode: AggregateSetupMode): void {\n this.Mode = mode;\n\n // If switching from smart mode with generated expression, populate advanced mode\n if (mode === 'advanced' && this.GeneratedExpression) {\n this.Expression = this.GeneratedExpression;\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Handle function button click - captures old value before changing\n * Called from template when user clicks a function button\n */\n selectFunction(newFunction: AggregateFunctionType): void {\n if (newFunction === this.SelectedFunction) return;\n\n const previousFunction = this.SelectedFunction;\n this.SelectedFunction = newFunction;\n this.onFunctionChange(previousFunction);\n }\n\n /**\n * Handle column selection from dropdown\n * Called from template with the NEW value from ngModelChange\n * We track the previous value internally\n */\n private _previousColumn: string = '';\n\n onColumnSelected(newColumn: string): void {\n const previousColumn = this._previousColumn;\n this._previousColumn = newColumn;\n this.onColumnChange(previousColumn);\n }\n\n /**\n * Handle function selection change (internal)\n */\n private onFunctionChange(previousFunction: AggregateFunctionType): void {\n // Check if label matches what we would have auto-generated for the OLD function\n const shouldUpdateLabel = this.shouldAutoUpdateLabel(previousFunction, this.SelectedColumn);\n\n // If current column is not valid for new function, clear it\n const validFields = this.AvailableFields;\n if (this.SelectedColumn && !validFields.find(f => f.Name === this.SelectedColumn)) {\n this.SelectedColumn = '';\n }\n\n // Update label if it was auto-generated (matches old pattern) or is empty\n // For COUNT, we can set auto-label even without a column (COUNT(*))\n if (shouldUpdateLabel && (this.SelectedColumn || this.SelectedFunction === 'COUNT')) {\n this.setAutoLabel();\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Handle column selection change\n */\n onColumnChange(previousColumn: string): void {\n // Check if label matches what we would have auto-generated for the OLD column\n const shouldUpdateLabel = this.shouldAutoUpdateLabel(this.SelectedFunction, previousColumn);\n\n // Update label if it was auto-generated (matches old pattern) or is empty\n if (shouldUpdateLabel) {\n this.setAutoLabel();\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Check if the current label matches what we would have auto-generated\n * for the given function and column. If so, we should update the label\n * when either changes. This allows auto-updating while preserving manual edits.\n */\n private shouldAutoUpdateLabel(func: AggregateFunctionType, column: string): boolean {\n // Always update if label is empty\n if (!this.Label) return true;\n\n // Check if current label matches the auto-generated pattern for the old values\n const expectedOldLabel = this.generateAutoLabel(func, column);\n return expectedOldLabel !== null && this.Label === expectedOldLabel;\n }\n\n /**\n * Generate what the auto-label would be for a given function and column\n * Returns null if we can't generate a label (missing data)\n * Handles COUNT(*) case with \"Record Count\" label\n */\n private generateAutoLabel(func: AggregateFunctionType, column: string): string | null {\n if (!func) return null;\n\n // Handle COUNT(*) case - no column needed\n if (!column) {\n if (func === 'COUNT') {\n return 'Record Count';\n }\n return null; // Other functions require a column\n }\n\n const field = this.Entity?.Fields.find(f => f.Name === column);\n if (!field) return null;\n\n const funcLabel = FUNCTION_LABELS[func] || func;\n return `${funcLabel} of ${field.DisplayNameOrName}`;\n }\n\n /**\n * Set the auto-generated label based on current function and column\n */\n private setAutoLabel(): void {\n const label = this.generateAutoLabel(this.SelectedFunction, this.SelectedColumn);\n if (label) {\n this.Label = label;\n }\n }\n\n /**\n * Select an icon\n */\n selectIcon(icon: string): void {\n this.Icon = icon;\n this.cdr.detectChanges();\n }\n\n /**\n * Clear the icon\n */\n clearIcon(): void {\n this.Icon = '';\n this.cdr.detectChanges();\n }\n\n /**\n * Build the expression based on current mode\n */\n private buildExpression(): string {\n switch (this.Mode) {\n case 'simple':\n if (!this.SelectedFunction) return '';\n // COUNT can work without a column (uses COUNT(*))\n // COUNT_DISTINCT requires a column\n if (!this.SelectedColumn) {\n if (this.SelectedFunction === 'COUNT') {\n return 'COUNT(*)';\n }\n return ''; // Other functions require a column\n }\n // Use brackets for column names with spaces\n const columnRef = this.SelectedColumn.includes(' ')\n ? `[${this.SelectedColumn}]`\n : this.SelectedColumn;\n if (this.SelectedFunction === 'COUNT_DISTINCT') {\n return `COUNT(DISTINCT ${columnRef})`;\n }\n return `${this.SelectedFunction}(${columnRef})`;\n\n case 'advanced':\n return this.Expression;\n\n case 'smart':\n return this.GeneratedExpression || '';\n\n default:\n return '';\n }\n }\n\n /**\n * Check if the form is valid\n */\n get IsValid(): boolean {\n const hasExpression = !!this.buildExpression();\n const hasLabel = !!this.Label.trim();\n return hasExpression && hasLabel;\n }\n\n /**\n * Get validation message\n */\n get ValidationMessage(): string {\n if (!this.Label.trim()) {\n return 'Please enter a label for this aggregate';\n }\n\n switch (this.Mode) {\n case 'simple':\n // COUNT can work without a column (uses COUNT(*))\n // All other functions require a column\n if (!this.SelectedColumn && this.SelectedFunction !== 'COUNT') {\n return 'Please select a column';\n }\n break;\n case 'advanced':\n if (!this.Expression.trim()) return 'Please enter an expression';\n break;\n case 'smart':\n if (!this.GeneratedExpression) return 'Please generate an expression from your prompt';\n break;\n }\n\n return '';\n }\n\n /**\n * Save the aggregate\n */\n onSave(): void {\n if (!this.IsValid) return;\n\n const aggregate: ViewGridAggregate = {\n id: this.Aggregate?.id || this.generateId(),\n expression: this.buildExpression(),\n displayType: this.DisplayType,\n label: this.Label.trim(),\n description: this.Description.trim() || undefined,\n icon: this.Icon || this.getDefaultIcon(),\n enabled: this.Aggregate?.enabled ?? true, // Preserve enabled state when editing, default to true for new\n order: this.Aggregate?.order || 0\n };\n\n // Only include smartPrompt if in smart mode\n if (this.Mode === 'smart' && this.SmartPrompt) {\n aggregate.smartPrompt = this.SmartPrompt;\n }\n\n this.Save.emit(aggregate);\n this.onClose();\n }\n\n /**\n * Get default icon based on function\n */\n private getDefaultIcon(): string {\n if (this.Mode === 'simple' && this.SelectedFunction) {\n return FUNCTION_ICONS[this.SelectedFunction] || 'fa-solid fa-chart-simple';\n }\n return 'fa-solid fa-chart-simple';\n }\n\n /**\n * Generate a unique ID for new aggregates\n */\n private generateId(): string {\n return `agg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n }\n\n /**\n * Close the dialog\n */\n onClose(): void {\n this.Close.emit();\n }\n\n /**\n * Handle smart prompt generation (placeholder - actual AI call would be made by parent)\n */\n onGenerateFromPrompt(): void {\n if (!this.SmartPrompt.trim()) return;\n\n this.IsGenerating = true;\n this.cdr.detectChanges();\n\n // This would typically call an AI service\n // For now, we'll show a placeholder message\n // The actual implementation would be handled by the parent component or a service\n setTimeout(() => {\n this.IsGenerating = false;\n // Placeholder: In real implementation, this would be replaced with AI-generated expression\n this.GeneratedExpression = '/* AI-generated expression will appear here */';\n this.cdr.detectChanges();\n }, 500);\n }\n\n /**\n * Clear the generated expression and allow editing prompt\n */\n clearGeneratedExpression(): void {\n this.GeneratedExpression = '';\n this.cdr.detectChanges();\n }\n\n /**\n * Get display label for field dropdown showing \"Name (DisplayName)\" format\n * If DisplayName equals Name or is not set, just show Name\n */\n getFieldDisplayLabel(field: EntityFieldInfo): string {\n if (field.DisplayName && field.DisplayName !== field.Name) {\n return `${field.Name} (${field.DisplayName})`;\n }\n return field.Name;\n }\n}\n","<!-- Dialog Backdrop -->\n@if (IsOpen) {\n <div class=\"dialog-backdrop\" (click)=\"onClose()\"></div>\n}\n\n<!-- Dialog Panel -->\n<div class=\"dialog-panel\" [class.open]=\"IsOpen\">\n <!-- Header -->\n <div class=\"dialog-header\">\n <div class=\"header-title\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n <span>{{ Aggregate ? 'Edit Aggregate' : 'Add Aggregate' }}</span>\n </div>\n <button class=\"close-btn\" (click)=\"onClose()\" title=\"Close\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Mode Selector -->\n <div class=\"mode-selector\">\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'simple'\"\n (click)=\"setMode('simple')\"\n title=\"Pick a column and function\">\n <i class=\"fa-solid fa-wand-sparkles\"></i>\n <span>Simple</span>\n </button>\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'advanced'\"\n (click)=\"setMode('advanced')\"\n title=\"Write custom SQL expression\">\n <i class=\"fa-solid fa-code\"></i>\n <span>Advanced</span>\n </button>\n <button\n class=\"mode-btn\"\n [class.active]=\"Mode === 'smart'\"\n (click)=\"setMode('smart')\"\n title=\"Describe in natural language\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Smart</span>\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"dialog-content\">\n <!-- Simple Mode -->\n @if (Mode === 'simple') {\n <div class=\"mode-content simple-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Select a column and an aggregate function to calculate</span>\n </div>\n\n <!-- Function Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Function</label>\n <div class=\"function-grid\">\n @for (func of AggregateFunctions; track func.value) {\n <button\n class=\"function-btn\"\n [class.active]=\"SelectedFunction === func.value\"\n (click)=\"selectFunction(func.value)\"\n [title]=\"func.label\">\n <i [class]=\"func.icon\"></i>\n <span>{{ func.label }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Column Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Column</label>\n <select\n class=\"form-select\"\n [(ngModel)]=\"SelectedColumn\"\n (ngModelChange)=\"onColumnSelected($event)\">\n <option value=\"\">Select a column...</option>\n @for (field of AvailableFields; track field.ID) {\n <option [value]=\"field.Name\">{{ getFieldDisplayLabel(field) }}</option>\n }\n </select>\n @if (SelectedFunction !== 'COUNT' && SelectedFunction !== 'COUNT_DISTINCT' && NumericFields.length === 0) {\n <div class=\"field-hint warning\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>No numeric fields available. Use COUNT or switch to Advanced mode.</span>\n </div>\n }\n @if (SelectedFunction === 'COUNT' && !SelectedColumn) {\n <div class=\"field-hint info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>No column selected - will count all records (COUNT(*))</span>\n </div>\n }\n </div>\n\n <!-- Preview -->\n @if (SelectedFunction && (SelectedColumn || SelectedFunction === 'COUNT')) {\n <div class=\"expression-preview\">\n <label class=\"preview-label\">Expression Preview</label>\n <code class=\"preview-code\">{{\n !SelectedColumn && SelectedFunction === 'COUNT' ? 'COUNT(*)' :\n SelectedFunction === 'COUNT_DISTINCT' ? 'COUNT(DISTINCT ' + SelectedColumn + ')' :\n SelectedFunction + '(' + SelectedColumn + ')'\n }}</code>\n </div>\n }\n </div>\n }\n\n <!-- Advanced Mode -->\n @if (Mode === 'advanced') {\n <div class=\"mode-content advanced-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Write a custom SQL aggregate expression. Use field names as they appear in the database.</span>\n </div>\n\n <div class=\"form-section\">\n <label class=\"form-label\">SQL Expression</label>\n <textarea\n class=\"form-textarea code-input\"\n [(ngModel)]=\"Expression\"\n placeholder=\"e.g., SUM(Amount * Quantity) or COUNT(CASE WHEN Status = 'Active' THEN 1 END)\"\n rows=\"3\"\n ></textarea>\n <div class=\"field-hint\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tip: Use brackets for column names with spaces, e.g., [Total Amount]</span>\n </div>\n </div>\n\n <!-- Quick Examples -->\n <div class=\"form-section\">\n <label class=\"form-label examples-label\">Quick Examples</label>\n <div class=\"example-chips\">\n <button class=\"example-chip\" (click)=\"Expression = 'SUM(Amount)'\">\n SUM(Amount)\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'AVG(Price * Quantity)'\">\n AVG(Price * Quantity)\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'COUNT(CASE WHEN Status = \\'Active\\' THEN 1 END)'\">\n COUNT with condition\n </button>\n <button class=\"example-chip\" (click)=\"Expression = 'SUM(CASE WHEN Type = \\'Credit\\' THEN Amount ELSE -Amount END)'\">\n Conditional SUM\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Smart Mode -->\n @if (Mode === 'smart') {\n <div class=\"mode-content smart-mode\">\n <div class=\"mode-description\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Describe what you want to calculate in plain English. AI will generate the expression.</span>\n </div>\n\n <div class=\"form-section\">\n <label class=\"form-label\">What would you like to calculate?</label>\n <div class=\"smart-input-container\">\n <textarea\n class=\"form-textarea smart-input\"\n [(ngModel)]=\"SmartPrompt\"\n [disabled]=\"!!GeneratedExpression\"\n placeholder=\"e.g., 'Total revenue from completed orders' or 'Average order value for premium customers'\"\n rows=\"3\"\n ></textarea>\n @if (!GeneratedExpression) {\n <button\n class=\"generate-btn\"\n [disabled]=\"!SmartPrompt.trim() || IsGenerating\"\n (click)=\"onGenerateFromPrompt()\">\n @if (IsGenerating) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Generating...</span>\n } @else {\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Generate</span>\n }\n </button>\n }\n </div>\n </div>\n\n <!-- Generated Expression -->\n @if (GeneratedExpression) {\n <div class=\"generated-section\">\n <div class=\"generated-header\">\n <label class=\"form-label\">Generated Expression</label>\n <button class=\"clear-generated-btn\" (click)=\"clearGeneratedExpression()\" title=\"Edit prompt\">\n <i class=\"fa-solid fa-pen\"></i>\n <span>Edit Prompt</span>\n </button>\n </div>\n <code class=\"generated-code\">{{ GeneratedExpression }}</code>\n <div class=\"generated-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>This expression was generated from your description. Clear it to edit the prompt.</span>\n </div>\n </div>\n }\n\n <!-- Smart Mode Tips -->\n <div class=\"smart-tips\">\n <div class=\"tips-header\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tips for better results:</span>\n </div>\n <ul class=\"tips-list\">\n <li>Be specific about which fields to use</li>\n <li>Mention any conditions or filters</li>\n <li>Specify the type of calculation (sum, average, count, etc.)</li>\n </ul>\n </div>\n </div>\n }\n\n <!-- Common Configuration -->\n <div class=\"config-section\">\n <div class=\"section-divider\">\n <span>Display Options</span>\n </div>\n\n <!-- Label -->\n <div class=\"form-section\">\n <label class=\"form-label required\">Label</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"Label\"\n placeholder=\"e.g., Total Revenue, Average Order Value\"\n />\n </div>\n\n <!-- Description (optional) -->\n <div class=\"form-section\">\n <label class=\"form-label\">Description <span class=\"optional\">(optional)</span></label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"Description\"\n placeholder=\"Brief explanation of what this shows\"\n />\n </div>\n\n <!-- Display Type -->\n <div class=\"form-section\">\n <label class=\"form-label\">Display As</label>\n <div class=\"display-type-toggle\">\n <button\n class=\"display-type-btn\"\n [class.active]=\"DisplayType === 'card'\"\n (click)=\"DisplayType = 'card'\">\n <i class=\"fa-solid fa-id-card\"></i>\n <span>Card</span>\n <small>Shows in summary panel</small>\n </button>\n <button\n class=\"display-type-btn\"\n [class.active]=\"DisplayType === 'column'\"\n (click)=\"DisplayType = 'column'\">\n <i class=\"fa-solid fa-table-columns\"></i>\n <span>Column Footer</span>\n <small>Shows at bottom of grid</small>\n </button>\n </div>\n </div>\n\n <!-- Icon Selection -->\n <div class=\"form-section\">\n <label class=\"form-label\">Icon <span class=\"optional\">(optional)</span></label>\n <div class=\"icon-selector\">\n @if (Icon) {\n <div class=\"selected-icon\">\n <i [class]=\"Icon\"></i>\n <button class=\"clear-icon-btn\" (click)=\"clearIcon()\" title=\"Remove icon\" aria-label=\"Remove icon\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n <div class=\"icon-grid\">\n @for (icon of CommonIcons; track icon) {\n <button\n class=\"icon-btn\"\n [class.active]=\"Icon === icon\"\n (click)=\"selectIcon(icon)\"\n [title]=\"icon\">\n <i [class]=\"icon\"></i>\n </button>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <div class=\"footer-left\">\n <button\n class=\"footer-btn save-btn primary\"\n [disabled]=\"!IsValid\"\n (click)=\"onSave()\">\n <i class=\"fa-solid fa-check\"></i>\n <span>{{ Aggregate ? 'Update' : 'Add' }} Aggregate</span>\n </button>\n </div>\n <button class=\"footer-btn cancel-btn\" (click)=\"onClose()\">\n Cancel\n </button>\n </div>\n\n <!-- Validation Message -->\n @if (ValidationMessage) {\n <div class=\"validation-message\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <span>{{ ValidationMessage }}</span>\n </div>\n }\n</div>\n"]}