@memberjunction/ng-entity-viewer 6.1.4 → 6.2.0-edge.1

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 (81) hide show
  1. package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts +18 -2
  2. package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts.map +1 -1
  3. package/dist/lib/aggregate-panel/aggregate-panel.component.js +47 -18
  4. package/dist/lib/aggregate-panel/aggregate-panel.component.js.map +1 -1
  5. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts +22 -0
  6. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts.map +1 -1
  7. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js +57 -13
  8. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js.map +1 -1
  9. package/dist/lib/entity-cards/entity-cards.component.d.ts +68 -5
  10. package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -1
  11. package/dist/lib/entity-cards/entity-cards.component.js +213 -73
  12. package/dist/lib/entity-cards/entity-cards.component.js.map +1 -1
  13. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +156 -14
  14. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
  15. package/dist/lib/entity-data-grid/entity-data-grid.component.js +432 -138
  16. package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
  17. package/dist/lib/entity-data-grid/events/grid-events.js +67 -67
  18. package/dist/lib/entity-data-grid/events/grid-events.js.map +1 -1
  19. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts +112 -8
  20. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts.map +1 -1
  21. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js +255 -69
  22. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js.map +1 -1
  23. package/dist/lib/entity-viewer/entity-viewer.component.d.ts +11 -0
  24. package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
  25. package/dist/lib/entity-viewer/entity-viewer.component.js +45 -8
  26. package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
  27. package/dist/lib/pill/pill.component.d.ts +13 -3
  28. package/dist/lib/pill/pill.component.d.ts.map +1 -1
  29. package/dist/lib/pill/pill.component.js +34 -10
  30. package/dist/lib/pill/pill.component.js.map +1 -1
  31. package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts +29 -3
  32. package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts.map +1 -1
  33. package/dist/lib/quick-save-dialog/quick-save-dialog.component.js +173 -114
  34. package/dist/lib/quick-save-dialog/quick-save-dialog.component.js.map +1 -1
  35. package/dist/lib/recycle-bin/recycle-bin.component.d.ts +4 -0
  36. package/dist/lib/recycle-bin/recycle-bin.component.d.ts.map +1 -1
  37. package/dist/lib/recycle-bin/recycle-bin.component.js +10 -2
  38. package/dist/lib/recycle-bin/recycle-bin.component.js.map +1 -1
  39. package/dist/lib/utils/aggregate-field.util.d.ts +21 -0
  40. package/dist/lib/utils/aggregate-field.util.d.ts.map +1 -0
  41. package/dist/lib/utils/aggregate-field.util.js +40 -0
  42. package/dist/lib/utils/aggregate-field.util.js.map +1 -0
  43. package/dist/lib/utils/highlight.util.d.ts +6 -0
  44. package/dist/lib/utils/highlight.util.d.ts.map +1 -1
  45. package/dist/lib/utils/highlight.util.js +23 -11
  46. package/dist/lib/utils/highlight.util.js.map +1 -1
  47. package/dist/lib/utils/record.util.d.ts +10 -0
  48. package/dist/lib/utils/record.util.d.ts.map +1 -1
  49. package/dist/lib/utils/record.util.js +20 -4
  50. package/dist/lib/utils/record.util.js.map +1 -1
  51. package/dist/lib/view-config-panel/view-config-panel.component.d.ts +22 -2
  52. package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
  53. package/dist/lib/view-config-panel/view-config-panel.component.js +54 -14
  54. package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
  55. package/dist/lib/view-selector/view-selector.component.js +2 -2
  56. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts +15 -3
  57. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts.map +1 -1
  58. package/dist/lib/view-types/renderers/cards-view-renderer.component.js +33 -5
  59. package/dist/lib/view-types/renderers/cards-view-renderer.component.js.map +1 -1
  60. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +160 -5
  61. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -1
  62. package/dist/lib/view-types/renderers/grid-view-renderer.component.js +576 -36
  63. package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -1
  64. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts +16 -2
  65. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts.map +1 -1
  66. package/dist/lib/view-types/renderers/map-view-renderer.component.js +37 -9
  67. package/dist/lib/view-types/renderers/map-view-renderer.component.js.map +1 -1
  68. package/dist/lib/view-types/view-type.contracts.js.map +1 -1
  69. package/dist/lib/view-workspace/view-workspace.component.d.ts +235 -24
  70. package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -1
  71. package/dist/lib/view-workspace/view-workspace.component.js +600 -174
  72. package/dist/lib/view-workspace/view-workspace.component.js.map +1 -1
  73. package/dist/module.d.ts +5 -4
  74. package/dist/module.d.ts.map +1 -1
  75. package/dist/module.js +8 -1
  76. package/dist/module.js.map +1 -1
  77. package/dist/public-api.d.ts +1 -0
  78. package/dist/public-api.d.ts.map +1 -1
  79. package/dist/public-api.js +1 -0
  80. package/dist/public-api.js.map +1 -1
  81. package/package.json +21 -20
@@ -1,5 +1,6 @@
1
1
  import { Component, Input, Output, EventEmitter, HostListener } from '@angular/core';
2
2
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
3
+ import { IsDateOnlySQLType, FormatDateOnly } from '@memberjunction/core';
3
4
  import { DEFAULT_AGGREGATE_DISPLAY, UserInfoEngine } from '@memberjunction/core-entities';
4
5
  import { CreateEmptyFilter } from '@memberjunction/ng-filter-builder';
5
6
  import * as i0 from "@angular/core";
@@ -236,7 +237,7 @@ function ViewConfigPanelComponent_Conditional_29_For_26_Template(rf, ctx) { if (
236
237
  const ctx_r1 = i0.ɵɵnextContext(2);
237
238
  i0.ɵɵstyleProp("text-align", (ctx_r1.FormatEditingColumn.format == null ? null : ctx_r1.FormatEditingColumn.format.align) || "left")("font-weight", (ctx_r1.FormatEditingColumn.format == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle.bold) ? "bold" : "normal")("font-style", (ctx_r1.FormatEditingColumn.format == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle.italic) ? "italic" : "normal")("text-decoration", (ctx_r1.FormatEditingColumn.format == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle.underline) ? "underline" : "none")("color", ctx_r1.FormatEditingColumn.format == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle.color)("background-color", ctx_r1.FormatEditingColumn.format == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle == null ? null : ctx_r1.FormatEditingColumn.format.cellStyle.backgroundColor);
238
239
  i0.ɵɵadvance();
239
- i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatPreviewValue(value_r12, ctx_r1.FormatEditingColumn.format), " ");
240
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatPreviewValue(value_r12, ctx_r1.FormatEditingColumn.format, ctx_r1.FormatEditingColumn.field), " ");
240
241
  } }
241
242
  function ViewConfigPanelComponent_Conditional_29_Conditional_49_Conditional_9_Template(rf, ctx) { if (rf & 1) {
242
243
  const _r14 = i0.ɵɵgetCurrentView();
@@ -1181,6 +1182,22 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
1181
1182
  * Pre-populated sharing preference from the quick save dialog (used when DefaultSaveAsNew is true)
1182
1183
  */
1183
1184
  PendingNewViewIsShared = false;
1185
+ /**
1186
+ * Pre-populated traditional filter from the quick save dialog (used when DefaultSaveAsNew is
1187
+ * true). Without it, reopening this panel from the dialog would reset the filter the user had
1188
+ * already configured. {@link ExternalFilterState} can't carry it back: the staged filter is the
1189
+ * very object this panel is already bound to, so that input never registers a change.
1190
+ */
1191
+ PendingNewViewFilterState = null;
1192
+ /**
1193
+ * Pre-populated smart-filter toggle from the quick save dialog (used when DefaultSaveAsNew is
1194
+ * true) — the smart-mode counterpart of {@link PendingNewViewFilterState}.
1195
+ */
1196
+ PendingNewViewSmartFilterEnabled = false;
1197
+ /**
1198
+ * Pre-populated smart-filter prompt from the quick save dialog (used when DefaultSaveAsNew is true)
1199
+ */
1200
+ PendingNewViewSmartFilterPrompt = '';
1184
1201
  /**
1185
1202
  * Emitted when user wants to duplicate the current view (F-005)
1186
1203
  */
@@ -1517,12 +1534,23 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
1517
1534
  this.SortDirection = 'asc';
1518
1535
  this.SortItems = [];
1519
1536
  }
1520
- this.SmartFilterPrompt = '';
1537
+ // Carry a filter back from the quick-save dialog when continuing a new-view flow; otherwise
1538
+ // start clean. Smart takes precedence, matching how a saved view's filter mode is chosen.
1539
+ const carryingSmartFilter = this.DefaultSaveAsNew && this.PendingNewViewSmartFilterEnabled;
1540
+ const carriedFilter = this.DefaultSaveAsNew && !carryingSmartFilter ? this.PendingNewViewFilterState : null;
1541
+ this.SmartFilterPrompt = carryingSmartFilter ? this.PendingNewViewSmartFilterPrompt : '';
1521
1542
  this.SmartFilterExplanation = '';
1522
- this.FilterState = CreateEmptyFilter();
1523
- // Default to smart mode (promote AI filtering)
1524
- this.FilterMode = 'smart';
1525
- this.SmartFilterEnabled = true;
1543
+ if (carriedFilter && this.countFilters(carriedFilter) > 0) {
1544
+ this.FilterState = carriedFilter;
1545
+ this.FilterMode = 'traditional';
1546
+ this.SmartFilterEnabled = false;
1547
+ }
1548
+ else {
1549
+ this.FilterState = CreateEmptyFilter();
1550
+ // Default to smart mode (promote AI filtering)
1551
+ this.FilterMode = 'smart';
1552
+ this.SmartFilterEnabled = true;
1553
+ }
1526
1554
  }
1527
1555
  // Load aggregates from currentGridState if available
1528
1556
  if (this.CurrentGridState?.aggregates?.expressions) {
@@ -2165,7 +2193,11 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2165
2193
  /**
2166
2194
  * Format a value for preview display
2167
2195
  */
2168
- FormatPreviewValue(value, format) {
2196
+ /**
2197
+ * @param field The column's field, so a SQL `date` previews as its stored calendar day rather
2198
+ * than shifting into the reader's zone (MJ#4210).
2199
+ */
2200
+ FormatPreviewValue(value, format, field) {
2169
2201
  if (value == null)
2170
2202
  return '—';
2171
2203
  if (!format || format.type === 'auto')
@@ -2179,7 +2211,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2179
2211
  return this.formatPercent(value, format);
2180
2212
  case 'date':
2181
2213
  case 'datetime':
2182
- return this.formatDate(value, format);
2214
+ return this.formatDate(value, format, IsDateOnlySQLType(field?.Type));
2183
2215
  case 'boolean':
2184
2216
  return this.formatBoolean(value, format);
2185
2217
  default:
@@ -2212,7 +2244,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2212
2244
  // Assume value is already a percentage (e.g., 50 = 50%), divide by 100
2213
2245
  return new Intl.NumberFormat('en-US', options).format(value / 100);
2214
2246
  }
2215
- formatDate(value, format) {
2247
+ formatDate(value, format, dateOnly) {
2216
2248
  const date = value instanceof Date ? value : new Date(value);
2217
2249
  if (isNaN(date.getTime()))
2218
2250
  return String(value);
@@ -2220,6 +2252,8 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2220
2252
  const formatStr = format.dateFormat || 'medium';
2221
2253
  const includeWeekday = formatStr.includes('-weekday');
2222
2254
  const baseFormat = formatStr.replace('-weekday', '');
2255
+ // A `date` column has no time to show and must not shift into the reader's zone.
2256
+ const withTime = format.type === 'datetime' && !dateOnly;
2223
2257
  let options;
2224
2258
  // Intl.DateTimeFormat doesn't allow combining dateStyle with weekday
2225
2259
  // So we must use individual components when weekday is requested
@@ -2234,7 +2268,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2234
2268
  // medium
2235
2269
  options = { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' };
2236
2270
  }
2237
- if (format.type === 'datetime') {
2271
+ if (withTime) {
2238
2272
  options.hour = 'numeric';
2239
2273
  options.minute = '2-digit';
2240
2274
  }
@@ -2244,11 +2278,11 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2244
2278
  options = {
2245
2279
  dateStyle: baseFormat === 'short' ? 'short' : baseFormat === 'long' ? 'long' : 'medium'
2246
2280
  };
2247
- if (format.type === 'datetime') {
2281
+ if (withTime) {
2248
2282
  options.timeStyle = 'short';
2249
2283
  }
2250
2284
  }
2251
- return new Intl.DateTimeFormat('en-US', options).format(date);
2285
+ return dateOnly ? FormatDateOnly(date, options, 'en-US') : new Intl.DateTimeFormat('en-US', options).format(date);
2252
2286
  }
2253
2287
  formatBoolean(value, format) {
2254
2288
  return value ? (format.trueLabel || 'Yes') : (format.falseLabel || 'No');
@@ -2715,7 +2749,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2715
2749
  static ɵfac = function ViewConfigPanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ViewConfigPanelComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
2716
2750
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ViewConfigPanelComponent, selectors: [["mj-view-config-panel"]], hostBindings: function ViewConfigPanelComponent_HostBindings(rf, ctx) { if (rf & 1) {
2717
2751
  i0.ɵɵlistener("keydown.escape", function ViewConfigPanelComponent_keydown_escape_HostBindingHandler() { return ctx.HandleEscape(); }, i0.ɵɵresolveDocument);
2718
- } }, inputs: { Entity: "Entity", ViewEntity: "ViewEntity", IsOpen: "IsOpen", CurrentGridState: "CurrentGridState", SampleData: "SampleData", ExternalFilterState: "ExternalFilterState", DefaultSaveAsNew: "DefaultSaveAsNew", PendingNewViewName: "PendingNewViewName", PendingNewViewDescription: "PendingNewViewDescription", PendingNewViewIsShared: "PendingNewViewIsShared", IsSaving: "IsSaving" }, outputs: { Close: "Close", Save: "Save", SaveDefaults: "SaveDefaults", Delete: "Delete", OpenFilterDialogRequest: "OpenFilterDialogRequest", Duplicate: "Duplicate" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 45, vars: 46, consts: [[1, "panel-backdrop"], [1, "config-panel"], ["title", "Drag to resize", 1, "resize-handle", 3, "mousedown"], [1, "resize-grip"], [1, "panel-header"], [1, "header-title"], [1, "fa-solid", "fa-sliders-h"], ["title", "Close", "aria-label", "Close panel", 1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "tab-nav"], [1, "tab-btn", 3, "click", "title"], [1, "fa-solid", "fa-columns"], [1, "fa-solid", "fa-arrow-up-wide-short"], [1, "tab-badge"], [1, "fa-solid", "fa-filter"], [1, "fa-solid", "fa-chart-simple"], [1, "tab-btn", 3, "active", "title"], [1, "panel-content"], [1, "tab-content"], [1, "format-editor"], ["Variant", "error", "Size", "sm", 1, "view-config-validation-pos"], [1, "panel-footer"], [1, "footer-left"], [1, "footer-btn", "save-btn", "primary", 3, "disabled"], [1, "footer-btn", "cancel-btn", 3, "click"], [3, "Close", "Save", "Entity", "Aggregate", "IsOpen"], ["Title", "Delete View", "DetailMessage", "This action cannot be undone. All users who have access to this view will lose it.", "ConfirmText", "Delete", "ConfirmStyle", "danger", "Icon", "fa-solid fa-trash", 3, "Confirmed", "Cancelled", "IsOpen", "Message"], ["Title", "Switch Filter Mode", "DetailMessage", "You can switch back later, but the current filter data will be lost.", "ConfirmText", "Switch", "ConfirmStyle", "primary", "Icon", "fa-solid fa-exchange-alt", 3, "Confirmed", "Cancelled", "IsOpen", "Message"], [1, "panel-backdrop", 3, "click"], [1, "fa-solid", "fa-cog"], [1, "config-section"], [1, "section-header"], [1, "fa-solid", "fa-eye"], [1, "column-count"], [1, "column-list", "visible-columns"], ["Size", "compact", "Variant", "warning", "Title", "No columns visible. Add columns from below."], [1, "fa-solid", "fa-eye-slash"], [1, "column-search"], [1, "column-list", "hidden-columns"], [1, "column-item", "hidden"], ["Size", "compact", "Variant", "success", "Title", "All columns are visible"], [1, "drop-indicator"], ["draggable", "true", 1, "column-item", 3, "dragstart", "dragover", "dragleave", "drop", "dragend"], [1, "drag-handle"], [1, "fa-solid", "fa-grip-vertical"], [1, "column-name"], [1, "fa-solid", "fa-pen-nib", "alias-indicator", 3, "title"], ["title", "Custom formatting applied", 1, "fa-solid", "fa-paintbrush", "format-indicator"], [1, "column-actions"], ["title", "Format column", 1, "action-btn", "format-btn", 3, "click"], [1, "fa-solid", "fa-paintbrush"], ["title", "Move up", 1, "action-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-chevron-up"], ["title", "Move down", 1, "action-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-chevron-down"], ["title", "Hide column", 1, "action-btn", "hide-btn", 3, "click"], [1, "fa-solid", "fa-search"], ["type", "text", "placeholder", "Search columns...", 3, "ngModelChange", "ngModel"], ["title", "Show column", 1, "action-btn", "show-btn", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "format-editor-header"], [1, "back-btn", 3, "click"], [1, "fa-solid", "fa-arrow-left"], [1, "format-editor-title"], [1, "format-section"], [1, "format-section-header"], [1, "fa-solid", "fa-pen-nib"], [1, "format-row"], ["type", "text", 1, "format-input", 3, "input", "value", "placeholder"], [1, "format-row", "alias-info"], [1, "format-preview-section"], [1, "preview-header"], [1, "preview-table"], [1, "preview-header-cell"], [1, "preview-cell", 3, "text-align", "font-weight", "font-style", "text-decoration", "color", "background-color"], [1, "fa-solid", "fa-hashtag"], [1, "format-select", 3, "ngModelChange", "ngModel"], ["value", "auto"], ["value", "text"], ["value", "number"], ["value", "currency"], ["value", "percent"], ["value", "date"], ["value", "datetime"], ["value", "boolean"], [1, "fa-solid", "fa-align-left"], [1, "alignment-toggle"], ["title", "Left", 1, "align-btn", 3, "click"], ["title", "Center", 1, "align-btn", 3, "click"], [1, "fa-solid", "fa-align-center"], ["title", "Right", 1, "align-btn", 3, "click"], [1, "fa-solid", "fa-align-right"], [1, "fa-solid", "fa-heading"], [1, "style-buttons"], ["title", "Bold", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-bold"], ["title", "Italic", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-italic"], ["title", "Underline", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-underline"], ["type", "color", 1, "color-input", 3, "input", "value"], [1, "fa-solid", "fa-table-cells"], [1, "format-actions"], [1, "clear-format-btn", 3, "click"], [1, "fa-solid", "fa-eraser"], [1, "muted-text"], ["title", "Clear alias", "aria-label", "Clear alias", 1, "clear-alias-btn", 3, "click"], [1, "preview-cell"], [1, "fa-solid", "fa-sliders"], ["type", "number", "min", "0", "max", "10", 1, "format-input", "small", 3, "ngModelChange", "ngModel"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], ["value", "USD"], ["value", "EUR"], ["value", "GBP"], ["value", "JPY"], ["value", "CAD"], ["value", "AUD"], [1, "fa-solid", "fa-calendar"], ["value", "short"], ["value", "short-weekday"], ["value", "medium"], ["value", "medium-weekday"], ["value", "long"], ["value", "long-weekday"], [1, "fa-solid", "fa-toggle-on"], ["type", "text", "placeholder", "Yes", 1, "format-input", 3, "ngModelChange", "ngModel"], ["type", "text", "placeholder", "No", 1, "format-input", 3, "ngModelChange", "ngModel"], ["value", "checkbox"], ["value", "icon"], [1, "config-section", "sorting-section"], [1, "sorting-header"], [1, "sorting-description"], ["title", "Add another sort level", 1, "add-sort-btn", 3, "click", "disabled"], ["Size", "compact", "Icon", "fa-solid fa-arrow-up-wide-short", "Title", "No sorting configured", "Message", "Click \"Add Sort Level\" to define how records should be ordered"], [1, "sort-items-list"], ["Variant", "info", "Size", "sm"], [1, "sort-drop-indicator"], ["draggable", "true", 1, "sort-item", 3, "dragstart", "dragover", "dragleave", "drop", "dragend"], [1, "sort-priority-badge"], ["title", "Drag to reorder", 1, "sort-drag-handle"], [1, "sort-field-dropdown", 3, "ngModelChange", "ngModel", "disabled"], [3, "value"], [1, "sort-direction-toggle"], ["title", "Ascending (A-Z, 1-9)", 1, "direction-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-up"], ["title", "Descending (Z-A, 9-1)", 1, "direction-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-down"], ["title", "Remove sort level", 1, "sort-remove-btn", 3, "click", "disabled"], [1, "filter-mode-selector"], [1, "filter-mode-btn", 3, "click", "disabled"], [1, "mode-icon"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "mode-content"], [1, "mode-title"], [1, "mode-subtitle"], [1, "fa-solid", "fa-check", "mode-check"], [1, "config-section", "smart-filter-section"], [1, "config-section", "traditional-filter-section"], [1, "smart-filter-input-container"], [1, "smart-filter-icon"], ["id", "SmartFilterPrompt", "placeholder", "Describe what you're looking for...", "rows", "3", 1, "smart-filter-textarea", 3, "ngModelChange", "ngModel", "disabled"], ["Variant", "info", "Size", "sm", "Icon", "fa-solid fa-robot", 1, "view-config-alert-pos-mb"], [1, "smart-filter-examples"], [1, "examples-header"], [1, "fa-solid", "fa-lightbulb"], [1, "example-chips"], [1, "example-chip", 3, "click", "disabled"], [1, "fa-regular", "fa-calendar"], [1, "fa-solid", "fa-circle-check"], [1, "fa-solid", "fa-star"], [1, "fa-solid", "fa-clock-rotate-left"], ["Variant", "info", "Size", "sm", 1, "view-config-alert-pos"], [1, "filter-summary-container"], [1, "filter-summary"], [1, "summary-info"], [1, "summary-text", "no-filters"], [1, "summary-actions"], ["title", "Clear all filters", 1, "summary-btn", "clear-btn"], ["title", "Edit filters", 1, "summary-btn", "edit-btn", "primary", 3, "click", "disabled"], [1, "fa-solid", "fa-pen"], [1, "filter-badge"], [1, "summary-text"], ["title", "Clear all filters", 1, "summary-btn", "clear-btn", 3, "click"], [1, "config-section", "aggregates-section"], [1, "aggregates-header"], [1, "aggregates-description"], ["title", "Add a new aggregate", 1, "add-aggregate-btn", 3, "click", "disabled"], ["Size", "compact", "Icon", "fa-solid fa-chart-simple", "Title", "No aggregates configured", "Message", "Click \"Add Aggregate\" to create summary calculations"], [1, "aggregates-list"], [1, "aggregate-item", 3, "disabled"], [1, "aggregates-summary"], [1, "summary-item"], [1, "fa-solid", "fa-id-card"], [1, "fa-solid", "fa-table-columns"], [1, "aggregate-item"], [1, "agg-icon"], [1, "agg-content"], [1, "agg-label"], [1, "agg-details"], [1, "agg-type"], ["title", "AI-generated", 1, "agg-smart-badge"], [1, "agg-actions"], ["title", "Move up", 1, "agg-action-btn", 3, "click", "disabled"], ["title", "Move down", 1, "agg-action-btn", 3, "click", "disabled"], [1, "agg-action-btn", "toggle-btn", 3, "click", "disabled", "title"], ["title", "Edit", 1, "agg-action-btn", "edit-btn", 3, "click", "disabled"], ["title", "Remove", 1, "agg-action-btn", "remove-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-trash"], [1, "fa-solid", "fa-info-circle"], [1, "form-group"], ["for", "ViewName"], ["id", "ViewName", "type", "text", "placeholder", "Enter view name...", 1, "form-input", 3, "ngModelChange", "ngModel", "disabled"], [1, "validation-error"], ["for", "ViewDescription"], ["id", "ViewDescription", "placeholder", "Describe this view...", "rows", "3", 1, "form-input", "form-textarea", 3, "ngModelChange", "ngModel", "disabled"], [1, "fa-solid", "fa-share-alt"], [1, "checkbox-label"], ["type", "checkbox", 3, "ngModelChange", "ngModel", "disabled"], [1, "checkbox-text"], [1, "config-section", "danger-zone"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "delete-btn", 3, "click"], [1, "footer-btn", "save-btn", "primary", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-save"]], template: function ViewConfigPanelComponent_Template(rf, ctx) { if (rf & 1) {
2752
+ } }, inputs: { Entity: "Entity", ViewEntity: "ViewEntity", IsOpen: "IsOpen", CurrentGridState: "CurrentGridState", SampleData: "SampleData", ExternalFilterState: "ExternalFilterState", DefaultSaveAsNew: "DefaultSaveAsNew", PendingNewViewName: "PendingNewViewName", PendingNewViewDescription: "PendingNewViewDescription", PendingNewViewIsShared: "PendingNewViewIsShared", PendingNewViewFilterState: "PendingNewViewFilterState", PendingNewViewSmartFilterEnabled: "PendingNewViewSmartFilterEnabled", PendingNewViewSmartFilterPrompt: "PendingNewViewSmartFilterPrompt", IsSaving: "IsSaving" }, outputs: { Close: "Close", Save: "Save", SaveDefaults: "SaveDefaults", Delete: "Delete", OpenFilterDialogRequest: "OpenFilterDialogRequest", Duplicate: "Duplicate" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 45, vars: 46, consts: [[1, "panel-backdrop"], [1, "config-panel"], ["title", "Drag to resize", 1, "resize-handle", 3, "mousedown"], [1, "resize-grip"], [1, "panel-header"], [1, "header-title"], [1, "fa-solid", "fa-sliders-h"], ["title", "Close", "aria-label", "Close panel", 1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "tab-nav"], [1, "tab-btn", 3, "click", "title"], [1, "fa-solid", "fa-columns"], [1, "fa-solid", "fa-arrow-up-wide-short"], [1, "tab-badge"], [1, "fa-solid", "fa-filter"], [1, "fa-solid", "fa-chart-simple"], [1, "tab-btn", 3, "active", "title"], [1, "panel-content"], [1, "tab-content"], [1, "format-editor"], ["Variant", "error", "Size", "sm", 1, "view-config-validation-pos"], [1, "panel-footer"], [1, "footer-left"], [1, "footer-btn", "save-btn", "primary", 3, "disabled"], [1, "footer-btn", "cancel-btn", 3, "click"], [3, "Close", "Save", "Entity", "Aggregate", "IsOpen"], ["Title", "Delete View", "DetailMessage", "This action cannot be undone. All users who have access to this view will lose it.", "ConfirmText", "Delete", "ConfirmStyle", "danger", "Icon", "fa-solid fa-trash", 3, "Confirmed", "Cancelled", "IsOpen", "Message"], ["Title", "Switch Filter Mode", "DetailMessage", "You can switch back later, but the current filter data will be lost.", "ConfirmText", "Switch", "ConfirmStyle", "primary", "Icon", "fa-solid fa-exchange-alt", 3, "Confirmed", "Cancelled", "IsOpen", "Message"], [1, "panel-backdrop", 3, "click"], [1, "fa-solid", "fa-cog"], [1, "config-section"], [1, "section-header"], [1, "fa-solid", "fa-eye"], [1, "column-count"], [1, "column-list", "visible-columns"], ["Size", "compact", "Variant", "warning", "Title", "No columns visible. Add columns from below."], [1, "fa-solid", "fa-eye-slash"], [1, "column-search"], [1, "column-list", "hidden-columns"], [1, "column-item", "hidden"], ["Size", "compact", "Variant", "success", "Title", "All columns are visible"], [1, "drop-indicator"], ["draggable", "true", 1, "column-item", 3, "dragstart", "dragover", "dragleave", "drop", "dragend"], [1, "drag-handle"], [1, "fa-solid", "fa-grip-vertical"], [1, "column-name"], [1, "fa-solid", "fa-pen-nib", "alias-indicator", 3, "title"], ["title", "Custom formatting applied", 1, "fa-solid", "fa-paintbrush", "format-indicator"], [1, "column-actions"], ["title", "Format column", 1, "action-btn", "format-btn", 3, "click"], [1, "fa-solid", "fa-paintbrush"], ["title", "Move up", 1, "action-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-chevron-up"], ["title", "Move down", 1, "action-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-chevron-down"], ["title", "Hide column", 1, "action-btn", "hide-btn", 3, "click"], [1, "fa-solid", "fa-search"], ["type", "text", "placeholder", "Search columns...", 3, "ngModelChange", "ngModel"], ["title", "Show column", 1, "action-btn", "show-btn", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "format-editor-header"], [1, "back-btn", 3, "click"], [1, "fa-solid", "fa-arrow-left"], [1, "format-editor-title"], [1, "format-section"], [1, "format-section-header"], [1, "fa-solid", "fa-pen-nib"], [1, "format-row"], ["type", "text", 1, "format-input", 3, "input", "value", "placeholder"], [1, "format-row", "alias-info"], [1, "format-preview-section"], [1, "preview-header"], [1, "preview-table"], [1, "preview-header-cell"], [1, "preview-cell", 3, "text-align", "font-weight", "font-style", "text-decoration", "color", "background-color"], [1, "fa-solid", "fa-hashtag"], [1, "format-select", 3, "ngModelChange", "ngModel"], ["value", "auto"], ["value", "text"], ["value", "number"], ["value", "currency"], ["value", "percent"], ["value", "date"], ["value", "datetime"], ["value", "boolean"], [1, "fa-solid", "fa-align-left"], [1, "alignment-toggle"], ["title", "Left", 1, "align-btn", 3, "click"], ["title", "Center", 1, "align-btn", 3, "click"], [1, "fa-solid", "fa-align-center"], ["title", "Right", 1, "align-btn", 3, "click"], [1, "fa-solid", "fa-align-right"], [1, "fa-solid", "fa-heading"], [1, "style-buttons"], ["title", "Bold", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-bold"], ["title", "Italic", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-italic"], ["title", "Underline", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-underline"], ["type", "color", 1, "color-input", 3, "input", "value"], [1, "fa-solid", "fa-table-cells"], [1, "format-actions"], [1, "clear-format-btn", 3, "click"], [1, "fa-solid", "fa-eraser"], [1, "muted-text"], ["title", "Clear alias", "aria-label", "Clear alias", 1, "clear-alias-btn", 3, "click"], [1, "preview-cell"], [1, "fa-solid", "fa-sliders"], ["type", "number", "min", "0", "max", "10", 1, "format-input", "small", 3, "ngModelChange", "ngModel"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], ["value", "USD"], ["value", "EUR"], ["value", "GBP"], ["value", "JPY"], ["value", "CAD"], ["value", "AUD"], [1, "fa-solid", "fa-calendar"], ["value", "short"], ["value", "short-weekday"], ["value", "medium"], ["value", "medium-weekday"], ["value", "long"], ["value", "long-weekday"], [1, "fa-solid", "fa-toggle-on"], ["type", "text", "placeholder", "Yes", 1, "format-input", 3, "ngModelChange", "ngModel"], ["type", "text", "placeholder", "No", 1, "format-input", 3, "ngModelChange", "ngModel"], ["value", "checkbox"], ["value", "icon"], [1, "config-section", "sorting-section"], [1, "sorting-header"], [1, "sorting-description"], ["title", "Add another sort level", 1, "add-sort-btn", 3, "click", "disabled"], ["Size", "compact", "Icon", "fa-solid fa-arrow-up-wide-short", "Title", "No sorting configured", "Message", "Click \"Add Sort Level\" to define how records should be ordered"], [1, "sort-items-list"], ["Variant", "info", "Size", "sm"], [1, "sort-drop-indicator"], ["draggable", "true", 1, "sort-item", 3, "dragstart", "dragover", "dragleave", "drop", "dragend"], [1, "sort-priority-badge"], ["title", "Drag to reorder", 1, "sort-drag-handle"], [1, "sort-field-dropdown", 3, "ngModelChange", "ngModel", "disabled"], [3, "value"], [1, "sort-direction-toggle"], ["title", "Ascending (A-Z, 1-9)", 1, "direction-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-up"], ["title", "Descending (Z-A, 9-1)", 1, "direction-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-down"], ["title", "Remove sort level", 1, "sort-remove-btn", 3, "click", "disabled"], [1, "filter-mode-selector"], [1, "filter-mode-btn", 3, "click", "disabled"], [1, "mode-icon"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "mode-content"], [1, "mode-title"], [1, "mode-subtitle"], [1, "fa-solid", "fa-check", "mode-check"], [1, "config-section", "smart-filter-section"], [1, "config-section", "traditional-filter-section"], [1, "smart-filter-input-container"], [1, "smart-filter-icon"], ["id", "SmartFilterPrompt", "placeholder", "Describe what you're looking for...", "rows", "3", 1, "smart-filter-textarea", 3, "ngModelChange", "ngModel", "disabled"], ["Variant", "info", "Size", "sm", "Icon", "fa-solid fa-robot", 1, "view-config-alert-pos-mb"], [1, "smart-filter-examples"], [1, "examples-header"], [1, "fa-solid", "fa-lightbulb"], [1, "example-chips"], [1, "example-chip", 3, "click", "disabled"], [1, "fa-regular", "fa-calendar"], [1, "fa-solid", "fa-circle-check"], [1, "fa-solid", "fa-star"], [1, "fa-solid", "fa-clock-rotate-left"], ["Variant", "info", "Size", "sm", 1, "view-config-alert-pos"], [1, "filter-summary-container"], [1, "filter-summary"], [1, "summary-info"], [1, "summary-text", "no-filters"], [1, "summary-actions"], ["title", "Clear all filters", 1, "summary-btn", "clear-btn"], ["title", "Edit filters", 1, "summary-btn", "edit-btn", "primary", 3, "click", "disabled"], [1, "fa-solid", "fa-pen"], [1, "filter-badge"], [1, "summary-text"], ["title", "Clear all filters", 1, "summary-btn", "clear-btn", 3, "click"], [1, "config-section", "aggregates-section"], [1, "aggregates-header"], [1, "aggregates-description"], ["title", "Add a new aggregate", 1, "add-aggregate-btn", 3, "click", "disabled"], ["Size", "compact", "Icon", "fa-solid fa-chart-simple", "Title", "No aggregates configured", "Message", "Click \"Add Aggregate\" to create summary calculations"], [1, "aggregates-list"], [1, "aggregate-item", 3, "disabled"], [1, "aggregates-summary"], [1, "summary-item"], [1, "fa-solid", "fa-id-card"], [1, "fa-solid", "fa-table-columns"], [1, "aggregate-item"], [1, "agg-icon"], [1, "agg-content"], [1, "agg-label"], [1, "agg-details"], [1, "agg-type"], ["title", "AI-generated", 1, "agg-smart-badge"], [1, "agg-actions"], ["title", "Move up", 1, "agg-action-btn", 3, "click", "disabled"], ["title", "Move down", 1, "agg-action-btn", 3, "click", "disabled"], [1, "agg-action-btn", "toggle-btn", 3, "click", "disabled", "title"], ["title", "Edit", 1, "agg-action-btn", "edit-btn", 3, "click", "disabled"], ["title", "Remove", 1, "agg-action-btn", "remove-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-trash"], [1, "fa-solid", "fa-info-circle"], [1, "form-group"], ["for", "ViewName"], ["id", "ViewName", "type", "text", "placeholder", "Enter view name...", 1, "form-input", 3, "ngModelChange", "ngModel", "disabled"], [1, "validation-error"], ["for", "ViewDescription"], ["id", "ViewDescription", "placeholder", "Describe this view...", "rows", "3", 1, "form-input", "form-textarea", 3, "ngModelChange", "ngModel", "disabled"], [1, "fa-solid", "fa-share-alt"], [1, "checkbox-label"], ["type", "checkbox", 3, "ngModelChange", "ngModel", "disabled"], [1, "checkbox-text"], [1, "config-section", "danger-zone"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "delete-btn", 3, "click"], [1, "footer-btn", "save-btn", "primary", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-save"]], template: function ViewConfigPanelComponent_Template(rf, ctx) { if (rf & 1) {
2719
2753
  i0.ɵɵconditionalCreate(0, ViewConfigPanelComponent_Conditional_0_Template, 1, 0, "div", 0);
2720
2754
  i0.ɵɵelementStart(1, "div", 1)(2, "div", 2);
2721
2755
  i0.ɵɵlistener("mousedown", function ViewConfigPanelComponent_Template_div_mousedown_2_listener($event) { return ctx.OnResizeStart($event); });
@@ -2846,7 +2880,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2846
2880
  }
2847
2881
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewConfigPanelComponent, [{
2848
2882
  type: Component,
2849
- args: [{ standalone: false, selector: 'mj-view-config-panel', template: "<!-- Backdrop -->\n@if (IsOpen) {\n <div class=\"panel-backdrop\" (click)=\"OnClose()\"></div>\n}\n\n<!-- Sliding Panel -->\n<div class=\"config-panel\"\n [class.open]=\"IsOpen\"\n [class.resizing]=\"IsResizing\"\n [style.width.px]=\"PanelWidth\">\n <!-- Resize Handle -->\n <div class=\"resize-handle\"\n (mousedown)=\"OnResizeStart($event)\"\n title=\"Drag to resize\">\n <div class=\"resize-grip\"></div>\n </div>\n\n <!-- Panel Header -->\n <div class=\"panel-header\">\n <div class=\"header-title\">\n <i class=\"fa-solid fa-sliders-h\"></i>\n <span>{{ ViewEntity ? 'Edit View' : 'Configure View' }}</span>\n </div>\n <button class=\"close-btn\" (click)=\"OnClose()\" title=\"Close\" aria-label=\"Close panel\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Tab Navigation -->\n <div class=\"tab-nav\" [class.icon-only]=\"IsIconOnlyMode\">\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'columns'\"\n (click)=\"SetActiveTab('columns')\"\n [title]=\"IsIconOnlyMode ? 'Columns' : ''\">\n <i class=\"fa-solid fa-columns\"></i>\n @if (!IsIconOnlyMode) {\n <span>Columns</span>\n }\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'sorting'\"\n (click)=\"SetActiveTab('sorting')\"\n [title]=\"IsIconOnlyMode ? 'Sorting' + (SortItems.length > 0 ? ' (' + SortItems.length + ')' : '') : ''\">\n <i class=\"fa-solid fa-arrow-up-wide-short\"></i>\n @if (!IsIconOnlyMode) {\n <span>Sorting</span>\n }\n @if (SortItems.length > 0) {\n <span class=\"tab-badge\">{{ SortItems.length }}</span>\n }\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'filters'\"\n (click)=\"SetActiveTab('filters')\"\n [title]=\"IsIconOnlyMode ? 'Filters' : ''\">\n <i class=\"fa-solid fa-filter\"></i>\n @if (!IsIconOnlyMode) {\n <span>Filters</span>\n }\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'aggregates'\"\n (click)=\"SetActiveTab('aggregates')\"\n [title]=\"IsIconOnlyMode ? 'Aggregates' + (EnabledAggregatesCount > 0 ? ' (' + EnabledAggregatesCount + ')' : '') : ''\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n @if (!IsIconOnlyMode) {\n <span>Aggregates</span>\n }\n @if (EnabledAggregatesCount > 0) {\n <span class=\"tab-badge\">{{ EnabledAggregatesCount }}</span>\n }\n </button>\n @if (ViewEntity || DefaultSaveAsNew) {\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'settings'\"\n (click)=\"SetActiveTab('settings')\"\n [title]=\"IsIconOnlyMode ? 'Settings' : ''\">\n <i class=\"fa-solid fa-cog\"></i>\n @if (!IsIconOnlyMode) {\n <span>Settings</span>\n }\n </button>\n }\n </div>\n\n <!-- Panel Content -->\n <div class=\"panel-content\">\n <!-- Columns Tab -->\n @if (ActiveTab === 'columns' && !FormatEditingColumn) {\n <div class=\"tab-content\">\n <!-- Visible Columns -->\n <div class=\"config-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-eye\"></i>\n <span>Visible Columns</span>\n <span class=\"column-count\">{{ VisibleColumns.length }}</span>\n </div>\n <div class=\"column-list visible-columns\">\n @for (column of VisibleColumns; track column.fieldId; let i = $index) {\n <!-- Drop indicator before -->\n @if (IsDropBefore(column)) {\n <div class=\"drop-indicator\"></div>\n }\n <div\n class=\"column-item\"\n [class.dragging]=\"DraggedColumn === column\"\n [class.drop-target]=\"DropTargetColumn === column\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event, column)\"\n (dragover)=\"OnDragOver($event, column)\"\n (dragleave)=\"OnDragLeave($event)\"\n (drop)=\"OnDrop($event, column)\"\n (dragend)=\"OnDragEnd($event)\">\n <div class=\"drag-handle\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n <span class=\"column-name\">\n {{ column.userDisplayName || column.displayName }}\n @if (column.userDisplayName) {\n <i class=\"fa-solid fa-pen-nib alias-indicator\" title=\"Custom name: {{ column.userDisplayName }}\"></i>\n }\n @if (HasCustomFormat(column)) {\n <i class=\"fa-solid fa-paintbrush format-indicator\" title=\"Custom formatting applied\"></i>\n }\n </span>\n <div class=\"column-actions\">\n <button\n class=\"action-btn format-btn\"\n (click)=\"OpenFormatEditor(column)\"\n title=\"Format column\">\n <i class=\"fa-solid fa-paintbrush\"></i>\n </button>\n <button\n class=\"action-btn\"\n [disabled]=\"i === 0\"\n (click)=\"MoveColumnUp(column)\"\n title=\"Move up\">\n <i class=\"fa-solid fa-chevron-up\"></i>\n </button>\n <button\n class=\"action-btn\"\n [disabled]=\"i === VisibleColumns.length - 1\"\n (click)=\"MoveColumnDown(column)\"\n title=\"Move down\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n <button\n class=\"action-btn hide-btn\"\n (click)=\"ToggleColumnVisibility(column)\"\n title=\"Hide column\">\n <i class=\"fa-solid fa-eye-slash\"></i>\n </button>\n </div>\n </div>\n <!-- Drop indicator after (only for last item) -->\n @if (IsDropAfter(column) && i === VisibleColumns.length - 1) {\n <div class=\"drop-indicator\"></div>\n }\n }\n @if (VisibleColumns.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Variant=\"warning\"\n Title=\"No columns visible. Add columns from below.\" />\n }\n </div>\n </div>\n\n <!-- Hidden Columns -->\n <div class=\"config-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-eye-slash\"></i>\n <span>Hidden Columns</span>\n <span class=\"column-count\">{{ HiddenColumns.length }}</span>\n </div>\n @if (HiddenColumns.length > 5) {\n <div class=\"column-search\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n type=\"text\"\n placeholder=\"Search columns...\"\n [(ngModel)]=\"ColumnSearchText\"\n />\n </div>\n }\n <div class=\"column-list hidden-columns\">\n @for (column of FilteredHiddenColumns; track column.fieldId) {\n <div class=\"column-item hidden\">\n <span class=\"column-name\">{{ column.displayName }}</span>\n <button\n class=\"action-btn show-btn\"\n (click)=\"ToggleColumnVisibility(column)\"\n title=\"Show column\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n </div>\n }\n @if (HiddenColumns.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Variant=\"success\"\n Title=\"All columns are visible\" />\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Column Format Editor Sub-Panel -->\n @if (ActiveTab === 'columns' && FormatEditingColumn) {\n <div class=\"format-editor\">\n <!-- Format Editor Header -->\n <div class=\"format-editor-header\">\n <button class=\"back-btn\" (click)=\"CloseFormatEditor()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <div class=\"format-editor-title\">\n <span>Format: {{ FormatEditingColumn.userDisplayName || FormatEditingColumn.displayName }}</span>\n </div>\n </div>\n\n <!-- Column Alias Section -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-pen-nib\"></i>\n <span>Column Name</span>\n </div>\n <div class=\"format-row\">\n <label>Display As</label>\n <input type=\"text\" class=\"format-input\"\n [value]=\"FormatEditingColumn.userDisplayName || ''\"\n (input)=\"UpdateUserDisplayName($any($event.target).value)\"\n [placeholder]=\"FormatEditingColumn.displayName\" />\n </div>\n @if (FormatEditingColumn.userDisplayName) {\n <div class=\"format-row alias-info\">\n <small class=\"muted-text\">Original: {{ FormatEditingColumn.displayName }}</small>\n <button class=\"clear-alias-btn\" (click)=\"FormatEditingColumn.userDisplayName = undefined\" title=\"Clear alias\" aria-label=\"Clear alias\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Preview Section -->\n <div class=\"format-preview-section\">\n <div class=\"preview-header\">\n <i class=\"fa-solid fa-eye\"></i>\n <span>Preview</span>\n </div>\n <div class=\"preview-table\">\n <div class=\"preview-header-cell\"\n [style.font-weight]=\"FormatEditingColumn.format?.headerStyle?.bold ? 'bold' : 'normal'\"\n [style.font-style]=\"FormatEditingColumn.format?.headerStyle?.italic ? 'italic' : 'normal'\"\n [style.text-decoration]=\"FormatEditingColumn.format?.headerStyle?.underline ? 'underline' : 'none'\"\n [style.color]=\"FormatEditingColumn.format?.headerStyle?.color\"\n [style.background-color]=\"FormatEditingColumn.format?.headerStyle?.backgroundColor\">\n {{ FormatEditingColumn.userDisplayName || FormatEditingColumn.displayName }}\n </div>\n @for (value of GetSampleValues(FormatEditingColumn); track $index) {\n <div class=\"preview-cell\"\n [style.text-align]=\"FormatEditingColumn.format?.align || 'left'\"\n [style.font-weight]=\"FormatEditingColumn.format?.cellStyle?.bold ? 'bold' : 'normal'\"\n [style.font-style]=\"FormatEditingColumn.format?.cellStyle?.italic ? 'italic' : 'normal'\"\n [style.text-decoration]=\"FormatEditingColumn.format?.cellStyle?.underline ? 'underline' : 'none'\"\n [style.color]=\"FormatEditingColumn.format?.cellStyle?.color\"\n [style.background-color]=\"FormatEditingColumn.format?.cellStyle?.backgroundColor\">\n {{ FormatPreviewValue(value, FormatEditingColumn.format) }}\n </div>\n }\n </div>\n </div>\n\n <!-- Format Type -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-hashtag\"></i>\n <span>Format Type</span>\n </div>\n <select class=\"format-select\" [(ngModel)]=\"FormatEditingColumn.format!.type\">\n <option value=\"auto\">Auto (Smart Default)</option>\n <option value=\"text\">Text</option>\n <option value=\"number\">Number</option>\n <option value=\"currency\">Currency</option>\n <option value=\"percent\">Percent</option>\n <option value=\"date\">Date</option>\n <option value=\"datetime\">Date & Time</option>\n <option value=\"boolean\">Boolean</option>\n </select>\n </div>\n\n <!-- Type-Specific Options -->\n @if (FormatEditingColumn.format?.type === 'number' || FormatEditingColumn.format?.type === 'currency' || FormatEditingColumn.format?.type === 'percent') {\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-sliders\"></i>\n <span>Number Options</span>\n </div>\n <div class=\"format-row\">\n <label>Decimal Places</label>\n <input type=\"number\" class=\"format-input small\" min=\"0\" max=\"10\"\n [(ngModel)]=\"FormatEditingColumn.format!.decimals\" />\n </div>\n @if (FormatEditingColumn.format?.type === 'currency') {\n <div class=\"format-row\">\n <label>Currency</label>\n <select class=\"format-select\" [(ngModel)]=\"FormatEditingColumn.format!.currencyCode\">\n <option value=\"USD\">USD ($)</option>\n <option value=\"EUR\">EUR (\u20AC)</option>\n <option value=\"GBP\">GBP (\u00A3)</option>\n <option value=\"JPY\">JPY (\u00A5)</option>\n <option value=\"CAD\">CAD ($)</option>\n <option value=\"AUD\">AUD ($)</option>\n </select>\n </div>\n }\n <div class=\"format-row\">\n <label>Thousands Separator</label>\n <input type=\"checkbox\" [(ngModel)]=\"FormatEditingColumn.format!.thousandsSeparator\" />\n </div>\n </div>\n }\n\n @if (FormatEditingColumn.format?.type === 'date' || FormatEditingColumn.format?.type === 'datetime') {\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-calendar\"></i>\n <span>Date Options</span>\n </div>\n <div class=\"format-row\">\n <label>Format</label>\n <select class=\"format-select\" [(ngModel)]=\"FormatEditingColumn.format!.dateFormat\">\n <option value=\"short\">Short (1/15/25)</option>\n <option value=\"short-weekday\">Short + Day (Wed, 1/15/25)</option>\n <option value=\"medium\">Medium (Jan 15, 2025)</option>\n <option value=\"medium-weekday\">Medium + Day (Wed, Jan 15, 2025)</option>\n <option value=\"long\">Long (January 15, 2025)</option>\n <option value=\"long-weekday\">Long + Day (Wednesday, January 15, 2025)</option>\n </select>\n </div>\n </div>\n }\n\n @if (FormatEditingColumn.format?.type === 'boolean') {\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-toggle-on\"></i>\n <span>Boolean Options</span>\n </div>\n <div class=\"format-row\">\n <label>True Label</label>\n <input type=\"text\" class=\"format-input\" [(ngModel)]=\"FormatEditingColumn.format!.trueLabel\" placeholder=\"Yes\" />\n </div>\n <div class=\"format-row\">\n <label>False Label</label>\n <input type=\"text\" class=\"format-input\" [(ngModel)]=\"FormatEditingColumn.format!.falseLabel\" placeholder=\"No\" />\n </div>\n <div class=\"format-row\">\n <label>Display As</label>\n <select class=\"format-select\" [(ngModel)]=\"FormatEditingColumn.format!.booleanDisplay\">\n <option value=\"text\">Text</option>\n <option value=\"checkbox\">Checkbox</option>\n <option value=\"icon\">Icon</option>\n </select>\n </div>\n </div>\n }\n\n <!-- Alignment -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-align-left\"></i>\n <span>Alignment</span>\n </div>\n <div class=\"alignment-toggle\">\n <button class=\"align-btn\" [class.active]=\"FormatEditingColumn.format?.align === 'left' || !FormatEditingColumn.format?.align\"\n (click)=\"FormatEditingColumn.format!.align = 'left'\" title=\"Left\">\n <i class=\"fa-solid fa-align-left\"></i>\n </button>\n <button class=\"align-btn\" [class.active]=\"FormatEditingColumn.format?.align === 'center'\"\n (click)=\"FormatEditingColumn.format!.align = 'center'\" title=\"Center\">\n <i class=\"fa-solid fa-align-center\"></i>\n </button>\n <button class=\"align-btn\" [class.active]=\"FormatEditingColumn.format?.align === 'right'\"\n (click)=\"FormatEditingColumn.format!.align = 'right'\" title=\"Right\">\n <i class=\"fa-solid fa-align-right\"></i>\n </button>\n </div>\n </div>\n\n <!-- Header Style -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-heading\"></i>\n <span>Header Style</span>\n </div>\n <div class=\"style-buttons\">\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.headerStyle?.bold\"\n (click)=\"ToggleHeaderStyle('bold')\"\n title=\"Bold\">\n <i class=\"fa-solid fa-bold\"></i>\n </button>\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.headerStyle?.italic\"\n (click)=\"ToggleHeaderStyle('italic')\"\n title=\"Italic\">\n <i class=\"fa-solid fa-italic\"></i>\n </button>\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.headerStyle?.underline\"\n (click)=\"ToggleHeaderStyle('underline')\"\n title=\"Underline\">\n <i class=\"fa-solid fa-underline\"></i>\n </button>\n </div>\n <div class=\"format-row\">\n <label>Text Color</label>\n <input type=\"color\" class=\"color-input\"\n [value]=\"FormatEditingColumn.format?.headerStyle?.color || '#000000'\"\n (input)=\"UpdateHeaderColor('color', $any($event.target).value)\" />\n </div>\n <div class=\"format-row\">\n <label>Background</label>\n <input type=\"color\" class=\"color-input\"\n [value]=\"FormatEditingColumn.format?.headerStyle?.backgroundColor || '#ffffff'\"\n (input)=\"UpdateHeaderColor('backgroundColor', $any($event.target).value)\" />\n </div>\n </div>\n\n <!-- Cell Style -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-table-cells\"></i>\n <span>Cell Style</span>\n </div>\n <div class=\"style-buttons\">\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.cellStyle?.bold\"\n (click)=\"ToggleCellStyle('bold')\"\n title=\"Bold\">\n <i class=\"fa-solid fa-bold\"></i>\n </button>\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.cellStyle?.italic\"\n (click)=\"ToggleCellStyle('italic')\"\n title=\"Italic\">\n <i class=\"fa-solid fa-italic\"></i>\n </button>\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.cellStyle?.underline\"\n (click)=\"ToggleCellStyle('underline')\"\n title=\"Underline\">\n <i class=\"fa-solid fa-underline\"></i>\n </button>\n </div>\n <div class=\"format-row\">\n <label>Text Color</label>\n <input type=\"color\" class=\"color-input\"\n [value]=\"FormatEditingColumn.format?.cellStyle?.color || '#000000'\"\n (input)=\"UpdateCellColor('color', $any($event.target).value)\" />\n </div>\n <div class=\"format-row\">\n <label>Background</label>\n <input type=\"color\" class=\"color-input\"\n [value]=\"FormatEditingColumn.format?.cellStyle?.backgroundColor || '#ffffff'\"\n (input)=\"UpdateCellColor('backgroundColor', $any($event.target).value)\" />\n </div>\n </div>\n\n <!-- Clear Format Button -->\n <div class=\"format-actions\">\n <button class=\"clear-format-btn\" (click)=\"ClearColumnFormat(FormatEditingColumn); CloseFormatEditor()\">\n <i class=\"fa-solid fa-eraser\"></i>\n Clear Formatting\n </button>\n </div>\n </div>\n }\n\n <!-- Sorting Tab -->\n @if (ActiveTab === 'sorting') {\n <div class=\"tab-content\">\n <div class=\"config-section sorting-section\">\n <div class=\"sorting-header\">\n <p class=\"sorting-description\">\n Define how records should be ordered. Add multiple levels to sort by secondary fields when values are equal.\n </p>\n </div>\n\n <!-- Add Sort Button -->\n <button\n class=\"add-sort-btn\"\n (click)=\"AddSortLevel()\"\n [disabled]=\"!CanEdit || SortItems.length >= SortableFields.length\"\n title=\"Add another sort level\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add Sort Level</span>\n </button>\n\n <!-- Sort Items List -->\n @if (SortItems.length > 0) {\n <div class=\"sort-items-list\">\n @for (sortItem of SortItems; track $index; let i = $index) {\n <!-- Drop indicator before -->\n @if (IsSortDropBefore(sortItem)) {\n <div class=\"sort-drop-indicator\"></div>\n }\n <div\n class=\"sort-item\"\n [class.dragging]=\"DraggedSortItem === sortItem\"\n [class.drop-target]=\"DropTargetSortItem === sortItem\"\n draggable=\"true\"\n (dragstart)=\"OnSortDragStart($event, sortItem)\"\n (dragover)=\"OnSortDragOver($event, sortItem)\"\n (dragleave)=\"OnSortDragLeave($event)\"\n (drop)=\"OnSortDrop($event, sortItem)\"\n (dragend)=\"OnSortDragEnd($event)\">\n <!-- Priority Badge -->\n <div class=\"sort-priority-badge\">{{ i + 1 }}</div>\n <!-- Drag Handle -->\n <div class=\"sort-drag-handle\" title=\"Drag to reorder\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n <!-- Field Dropdown -->\n <select\n class=\"sort-field-dropdown\"\n [(ngModel)]=\"sortItem.field\"\n (ngModelChange)=\"OnSortFieldChange(sortItem, $event)\"\n [disabled]=\"!CanEdit\">\n @for (field of SortableFields; track field.ID) {\n <option [value]=\"field.Name\">{{ field.DisplayNameOrName }}</option>\n }\n </select>\n <!-- Direction Toggle -->\n <div class=\"sort-direction-toggle\">\n <button\n class=\"direction-btn\"\n [class.active]=\"sortItem.direction === 'asc'\"\n (click)=\"OnSortDirectionChange(sortItem, 'asc')\"\n [disabled]=\"!CanEdit\"\n title=\"Ascending (A-Z, 1-9)\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n </button>\n <button\n class=\"direction-btn\"\n [class.active]=\"sortItem.direction === 'desc'\"\n (click)=\"OnSortDirectionChange(sortItem, 'desc')\"\n [disabled]=\"!CanEdit\"\n title=\"Descending (Z-A, 9-1)\">\n <i class=\"fa-solid fa-arrow-down\"></i>\n </button>\n </div>\n <!-- Remove Button -->\n <button\n class=\"sort-remove-btn\"\n (click)=\"RemoveSortLevel(sortItem)\"\n [disabled]=\"!CanEdit\"\n title=\"Remove sort level\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Drop indicator after (only for last item) -->\n @if (IsSortDropAfter(sortItem) && i === SortItems.length - 1) {\n <div class=\"sort-drop-indicator\"></div>\n }\n }\n </div>\n <!-- Multi-sort hint -->\n @if (SortItems.length > 1) {\n <mj-alert Variant=\"info\" Size=\"sm\">Drag items to change priority. Records sort by first level, then second, etc.</mj-alert>\n }\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-arrow-up-wide-short\"\n Title=\"No sorting configured\"\n Message='Click \"Add Sort Level\" to define how records should be ordered' />\n }\n </div>\n </div>\n }\n\n <!-- Filters Tab -->\n @if (ActiveTab === 'filters') {\n <div class=\"tab-content\">\n <!-- Filter Mode Selection -->\n <div class=\"filter-mode-selector\">\n <button\n class=\"filter-mode-btn\"\n [class.active]=\"FilterMode === 'smart'\"\n (click)=\"SetFilterMode('smart')\"\n [disabled]=\"!CanEdit\">\n <div class=\"mode-icon\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n </div>\n <div class=\"mode-content\">\n <span class=\"mode-title\">Smart Filter</span>\n <span class=\"mode-subtitle\">Use AI to filter with natural language</span>\n </div>\n @if (FilterMode === 'smart') {\n <i class=\"fa-solid fa-check mode-check\"></i>\n }\n </button>\n <button\n class=\"filter-mode-btn\"\n [class.active]=\"FilterMode === 'traditional'\"\n (click)=\"SetFilterMode('traditional')\"\n [disabled]=\"!CanEdit\">\n <div class=\"mode-icon\">\n <i class=\"fa-solid fa-sliders\"></i>\n </div>\n <div class=\"mode-content\">\n <span class=\"mode-title\">Traditional Filter</span>\n <span class=\"mode-subtitle\">Build filters with field/operator/value</span>\n </div>\n @if (FilterMode === 'traditional') {\n <i class=\"fa-solid fa-check mode-check\"></i>\n }\n </button>\n </div>\n\n <!-- Smart Filter Section -->\n @if (FilterMode === 'smart') {\n <div class=\"config-section smart-filter-section\">\n <div class=\"smart-filter-input-container\">\n <div class=\"smart-filter-icon\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n </div>\n <textarea\n id=\"SmartFilterPrompt\"\n class=\"smart-filter-textarea\"\n placeholder=\"Describe what you're looking for...\"\n [(ngModel)]=\"SmartFilterPrompt\"\n [disabled]=\"!CanEdit\"\n rows=\"3\"\n ></textarea>\n </div>\n\n @if (SmartFilterExplanation) {\n <mj-alert Variant=\"info\" Size=\"sm\" Icon=\"fa-solid fa-robot\" class=\"view-config-alert-pos-mb\">{{ SmartFilterExplanation }}</mj-alert>\n }\n\n <!-- Example Prompts -->\n <div class=\"smart-filter-examples\">\n <div class=\"examples-header\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Try these examples:</span>\n </div>\n <div class=\"example-chips\">\n <button\n class=\"example-chip\"\n (click)=\"ApplySmartFilterExample('Show records created in the last 30 days')\"\n [disabled]=\"!CanEdit\">\n <i class=\"fa-regular fa-calendar\"></i>\n Last 30 days\n </button>\n <button\n class=\"example-chip\"\n (click)=\"ApplySmartFilterExample('Active records only')\"\n [disabled]=\"!CanEdit\">\n <i class=\"fa-solid fa-circle-check\"></i>\n Active only\n </button>\n <button\n class=\"example-chip\"\n (click)=\"ApplySmartFilterExample('Records with high priority')\"\n [disabled]=\"!CanEdit\">\n <i class=\"fa-solid fa-star\"></i>\n High priority\n </button>\n <button\n class=\"example-chip\"\n (click)=\"ApplySmartFilterExample('Records modified this week')\"\n [disabled]=\"!CanEdit\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n Modified this week\n </button>\n </div>\n </div>\n\n <mj-alert Variant=\"info\" Size=\"sm\" class=\"view-config-alert-pos\">The AI will interpret your description and create the appropriate filter when you save the view.</mj-alert>\n </div>\n }\n\n <!-- Traditional Filter Section -->\n @if (FilterMode === 'traditional') {\n <div class=\"config-section traditional-filter-section\">\n <div class=\"filter-summary-container\">\n <div class=\"filter-summary\">\n <div class=\"summary-info\">\n @if (GetFilterCount() > 0) {\n <span class=\"filter-badge\">{{ GetFilterCount() }}</span>\n <span class=\"summary-text\">{{ GetFilterSummary() }}</span>\n } @else {\n <span class=\"summary-text no-filters\">No filters configured</span>\n }\n </div>\n <div class=\"summary-actions\">\n @if (GetFilterCount() > 0 && CanEdit) {\n <button\n class=\"summary-btn clear-btn\"\n (click)=\"ClearFilters()\"\n title=\"Clear all filters\">\n <i class=\"fa-solid fa-times\"></i>\n Clear\n </button>\n }\n <button\n class=\"summary-btn edit-btn primary\"\n (click)=\"OpenFilterDialog()\"\n [disabled]=\"!CanEdit && GetFilterCount() === 0\"\n title=\"Edit filters\">\n <i class=\"fa-solid fa-pen\"></i>\n {{ GetFilterCount() > 0 ? 'Edit Filters' : 'Add Filters' }}\n </button>\n </div>\n </div>\n </div>\n\n <mj-alert Variant=\"info\" Size=\"sm\" class=\"view-config-alert-pos\">Build precise filters by selecting fields, operators, and values. Use groups for complex AND/OR logic.</mj-alert>\n </div>\n }\n </div>\n }\n\n <!-- Aggregates Tab -->\n @if (ActiveTab === 'aggregates') {\n <div class=\"tab-content\">\n <div class=\"config-section aggregates-section\">\n <div class=\"aggregates-header\">\n <p class=\"aggregates-description\">\n Add summary calculations like totals, averages, and counts. Display them in cards or as column footers.\n </p>\n </div>\n\n <!-- Add Aggregate Button -->\n <button\n class=\"add-aggregate-btn\"\n (click)=\"OpenAddAggregateDialog()\"\n [disabled]=\"!CanEdit\"\n title=\"Add a new aggregate\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add Aggregate</span>\n </button>\n\n <!-- Aggregates List -->\n @if (Aggregates.length > 0) {\n <div class=\"aggregates-list\">\n @for (agg of Aggregates; track agg.id; let i = $index) {\n <div class=\"aggregate-item\" [class.disabled]=\"agg.enabled === false\">\n <!-- Icon -->\n <div class=\"agg-icon\">\n <i [class]=\"agg.icon || 'fa-solid fa-chart-simple'\"></i>\n </div>\n\n <!-- Content -->\n <div class=\"agg-content\">\n <div class=\"agg-label\">{{ agg.label }}</div>\n <div class=\"agg-details\">\n <span class=\"agg-type\">\n <i [class]=\"agg.displayType === 'card' ? 'fa-solid fa-id-card' : 'fa-solid fa-table-columns'\"></i>\n {{ agg.displayType === 'card' ? 'Card' : 'Column Footer' }}\n </span>\n @if (agg.smartPrompt) {\n <span class=\"agg-smart-badge\" title=\"AI-generated\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n </span>\n }\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"agg-actions\">\n <button\n class=\"agg-action-btn\"\n [disabled]=\"i === 0 || !CanEdit\"\n (click)=\"MoveAggregateUp(agg)\"\n title=\"Move up\">\n <i class=\"fa-solid fa-chevron-up\"></i>\n </button>\n <button\n class=\"agg-action-btn\"\n [disabled]=\"i === Aggregates.length - 1 || !CanEdit\"\n (click)=\"MoveAggregateDown(agg)\"\n title=\"Move down\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n <button\n class=\"agg-action-btn toggle-btn\"\n [class.enabled]=\"agg.enabled !== false\"\n (click)=\"ToggleAggregateEnabled(agg, $event)\"\n [disabled]=\"!CanEdit\"\n [title]=\"agg.enabled !== false ? 'Disable' : 'Enable'\">\n <i [class]=\"agg.enabled !== false ? 'fa-solid fa-eye' : 'fa-solid fa-eye-slash'\"></i>\n </button>\n <button\n class=\"agg-action-btn edit-btn\"\n (click)=\"EditAggregate(agg)\"\n [disabled]=\"!CanEdit\"\n title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button\n class=\"agg-action-btn remove-btn\"\n (click)=\"RemoveAggregate(agg)\"\n [disabled]=\"!CanEdit\"\n title=\"Remove\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n </div>\n }\n </div>\n\n <!-- Summary -->\n <div class=\"aggregates-summary\">\n <div class=\"summary-item\">\n <i class=\"fa-solid fa-id-card\"></i>\n <span>{{ CardAggregates.length }} card{{ CardAggregates.length !== 1 ? 's' : '' }}</span>\n </div>\n <div class=\"summary-item\">\n <i class=\"fa-solid fa-table-columns\"></i>\n <span>{{ ColumnAggregates.length }} column footer{{ ColumnAggregates.length !== 1 ? 's' : '' }}</span>\n </div>\n </div>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-chart-simple\"\n Title=\"No aggregates configured\"\n Message='Click \"Add Aggregate\" to create summary calculations' />\n }\n\n <!-- Info Tip -->\n <mj-alert Variant=\"info\" Size=\"sm\" class=\"view-config-alert-pos\">Aggregates are calculated server-side for accuracy with filtered/paginated data.</mj-alert>\n </div>\n </div>\n }\n\n <!-- Settings Tab -->\n @if (ActiveTab === 'settings') {\n <div class=\"tab-content\">\n <!-- View Name & Description -->\n <div class=\"config-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>View Details</span>\n </div>\n <div class=\"form-group\">\n <label for=\"ViewName\">Name</label>\n <input\n id=\"ViewName\"\n type=\"text\"\n class=\"form-input\"\n [class.invalid]=\"!ViewName || !ViewName.trim()\"\n placeholder=\"Enter view name...\"\n [(ngModel)]=\"ViewName\"\n [disabled]=\"!CanEdit\"\n />\n @if (!ViewName || !ViewName.trim()) {\n <span class=\"validation-error\">View name is required</span>\n }\n </div>\n <div class=\"form-group\">\n <label for=\"ViewDescription\">Description</label>\n <textarea\n id=\"ViewDescription\"\n class=\"form-input form-textarea\"\n placeholder=\"Describe this view...\"\n [(ngModel)]=\"ViewDescription\"\n [disabled]=\"!CanEdit\"\n rows=\"3\"\n ></textarea>\n </div>\n </div>\n\n <!-- Sharing -->\n <div class=\"config-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-share-alt\"></i>\n <span>Sharing</span>\n </div>\n <label class=\"checkbox-label\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"IsShared\"\n [disabled]=\"!CanEdit\"\n />\n <span class=\"checkbox-text\">\n <strong>Share with others</strong>\n <small>Allow other users to use this view</small>\n </span>\n </label>\n </div>\n\n <!-- Danger Zone -->\n @if (ViewEntity && CanDelete) {\n <div class=\"config-section danger-zone\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>Danger Zone</span>\n </div>\n <button class=\"delete-btn\" (click)=\"OnDelete()\">\n <i class=\"fa-solid fa-trash\"></i>\n <span>Delete View</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Validation Errors Banner -->\n @if (ValidationErrors.length > 0 && VisibleColumns.length === 0) {\n <mj-alert Variant=\"error\" Size=\"sm\" class=\"view-config-validation-pos\">At least one column must be visible</mj-alert>\n }\n\n <!-- Panel Footer -->\n <div class=\"panel-footer\">\n <!-- Action buttons on LEFT -->\n <div class=\"footer-left\">\n @if (ViewEntity && CanEdit) {\n <!-- Editing an existing saved view -->\n <button\n class=\"footer-btn save-btn primary\"\n (click)=\"OnSave()\"\n [disabled]=\"IsSaving || !IsValid\">\n @if (IsSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n }\n {{ IsSaving ? 'Saving...' : 'Save' }}\n </button>\n }\n @if (!ViewEntity && DefaultSaveAsNew) {\n <!-- Creating a new view (from quick Save dialog) -->\n <button\n class=\"footer-btn save-btn primary\"\n (click)=\"OnSaveAsNew()\"\n [disabled]=\"IsSaving || !IsValidForSaveAsNew\">\n @if (IsSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-plus\"></i>\n }\n {{ IsSaving ? 'Saving...' : 'Create View' }}\n </button>\n }\n @if (!ViewEntity && !DefaultSaveAsNew) {\n <!-- Default/Dynamic view - Save to user settings -->\n <button\n class=\"footer-btn save-btn primary\"\n (click)=\"OnSaveDefaults()\"\n [disabled]=\"IsSaving\">\n @if (IsSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n }\n {{ IsSaving ? 'Saving...' : 'Save' }}\n </button>\n }\n </div>\n <!-- Cancel button on RIGHT -->\n <button class=\"footer-btn cancel-btn\" (click)=\"OnClose()\">\n Cancel\n </button>\n </div>\n</div>\n\n<!-- Aggregate Setup Dialog -->\n<mj-aggregate-setup-dialog\n [Entity]=\"Entity\"\n [Aggregate]=\"EditingAggregate\"\n [IsOpen]=\"ShowAggregateDialog\"\n (Close)=\"CloseAggregateDialog()\"\n (Save)=\"OnAggregateSave($event)\">\n</mj-aggregate-setup-dialog>\n\n<!-- Delete Confirmation Dialog -->\n<mj-ev-confirm-dialog\n [IsOpen]=\"ShowDeleteConfirm\"\n Title=\"Delete View\"\n [Message]=\"'Are you sure you want to delete \\'' + ViewName + '\\'?'\"\n DetailMessage=\"This action cannot be undone. All users who have access to this view will lose it.\"\n ConfirmText=\"Delete\"\n ConfirmStyle=\"danger\"\n Icon=\"fa-solid fa-trash\"\n (Confirmed)=\"OnDeleteConfirmed()\"\n (Cancelled)=\"OnDeleteCancelled()\">\n</mj-ev-confirm-dialog>\n\n<!-- Filter Mode Switch Confirmation Dialog (BUG-006) -->\n<mj-ev-confirm-dialog\n [IsOpen]=\"ShowFilterModeSwitchConfirm\"\n Title=\"Switch Filter Mode\"\n [Message]=\"FilterMode === 'smart' ? 'Switching to Traditional mode will clear your smart filter prompt.' : 'Switching to Smart mode will clear your traditional filter rules.'\"\n DetailMessage=\"You can switch back later, but the current filter data will be lost.\"\n ConfirmText=\"Switch\"\n ConfirmStyle=\"primary\"\n Icon=\"fa-solid fa-exchange-alt\"\n (Confirmed)=\"OnFilterModeSwitchConfirmed()\"\n (Cancelled)=\"OnFilterModeSwitchCancelled()\">\n</mj-ev-confirm-dialog>\n", styles: ["/* Backdrop */\n.panel-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.3);\n z-index: 1200; /* Above Leaflet map (1000), content-header (1100) */\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Sliding Panel */\n.config-panel {\n position: fixed;\n top: 60px;\n right: 0;\n width: 450px;\n min-width: 360px;\n max-width: min(800px, 100vw);\n height: calc(100% - 60px);\n background: var(--mj-bg-surface);\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);\n z-index: 1201; /* Above backdrop (1200) */\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.25s ease, width 0s;\n}\n\n.config-panel.open {\n transform: translateX(0);\n}\n\n.config-panel.resizing {\n transition: none;\n user-select: none;\n}\n\n/* Resize Handle */\n.resize-handle {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 6px;\n cursor: ew-resize;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.resize-handle:hover,\n.config-panel.resizing .resize-handle {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.resize-grip {\n width: 3px;\n height: 40px;\n background: var(--mj-border-strong);\n border-radius: 3px;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.resize-handle:hover .resize-grip,\n.config-panel.resizing .resize-grip {\n opacity: 1;\n background: var(--mj-brand-primary);\n}\n\n/* Panel Header */\n.panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n flex-shrink: 0;\n}\n\n.header-title {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.header-title i {\n color: var(--mj-brand-primary);\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-hover);\n color: var(--mj-text-primary);\n}\n\n/* Tab Navigation */\n.tab-nav {\n display: flex;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n flex-shrink: 0;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.tab-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 12px 16px;\n border: none;\n background: transparent;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 13px;\n font-weight: 500;\n transition: all 0.15s ease;\n position: relative;\n white-space: nowrap;\n}\n\n.tab-btn:hover {\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-hover);\n}\n\n.tab-btn.active {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.tab-btn.active::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 8px;\n right: 8px;\n height: 2px;\n background: var(--mj-brand-primary);\n border-radius: 2px 2px 0 0;\n}\n\n.tab-btn i {\n font-size: 14px;\n}\n\n.tab-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-radius: 9px;\n font-size: 11px;\n font-weight: 600;\n}\n\n/* Icon-only mode for narrow panel */\n.tab-nav.icon-only .tab-btn {\n padding: 12px 8px;\n}\n\n/* Panel Content */\n.panel-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.tab-content {\n padding: 16px;\n}\n\n/* Config Sections */\n.config-section {\n margin-bottom: 20px;\n}\n\n.section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 0;\n margin-bottom: 8px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.section-header i {\n color: var(--mj-text-muted);\n font-size: 14px;\n}\n\n.column-count {\n margin-left: auto;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 12px;\n font-weight: 500;\n}\n\n/* Column Search */\n.column-search {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-bottom: 8px;\n}\n\n.column-search i {\n color: var(--mj-text-disabled);\n font-size: 12px;\n}\n\n.column-search input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 13px;\n background: transparent;\n color: var(--mj-text-primary);\n}\n\n.column-search input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n/* Column List */\n.column-list {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n/* Column Item */\n.column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-radius: 8px;\n transition: background 0.15s ease;\n cursor: default;\n}\n\n.column-item:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.column-item.hidden {\n opacity: 0.7;\n}\n\n.column-item.hidden:hover {\n opacity: 1;\n}\n\n.column-item.dragging {\n opacity: 0.4;\n}\n\n.column-item.drop-target {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n/* Drop Indicator */\n.drop-indicator {\n height: 2px;\n background: var(--mj-brand-primary);\n border-radius: 1px;\n margin: 0 10px;\n}\n\n/* Drag Handle */\n.drag-handle {\n display: flex;\n align-items: center;\n cursor: grab;\n color: var(--mj-text-disabled);\n font-size: 12px;\n padding: 4px 2px;\n}\n\n.drag-handle:active {\n cursor: grabbing;\n}\n\n.column-item:hover .drag-handle {\n color: var(--mj-text-muted);\n}\n\n/* Column Name */\n.column-name {\n flex: 1;\n font-size: 13px;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.alias-indicator {\n font-size: 10px;\n color: var(--mj-brand-primary);\n margin-left: 4px;\n}\n\n.format-indicator {\n font-size: 10px;\n color: var(--mj-status-warning);\n margin-left: 4px;\n}\n\n/* Column Actions */\n.column-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.column-item:hover .column-actions {\n opacity: 1;\n}\n\n.action-btn {\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.action-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.action-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n.action-btn.hide-btn:hover {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n.action-btn.show-btn {\n opacity: 0;\n}\n\n.column-item:hover .action-btn.show-btn {\n opacity: 1;\n}\n\n.action-btn.show-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.action-btn.format-btn:hover {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning);\n}\n\n.action-btn i {\n font-size: 12px;\n}\n\n/* Format Editor */\n.format-editor {\n padding: 0;\n}\n\n.format-editor-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.back-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.back-btn:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n\n.format-editor-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n/* Format Sections */\n.format-section {\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.format-section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.format-section-header i {\n color: var(--mj-text-muted);\n}\n\n/* Format Rows */\n.format-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 0;\n}\n\n.format-row label {\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.format-row.alias-info {\n padding: 4px 0;\n}\n\n.muted-text {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.clear-alias-btn {\n width: 24px;\n height: 24px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-disabled);\n transition: all 0.15s ease;\n}\n\n.clear-alias-btn:hover {\n color: var(--mj-status-error);\n}\n\n/* Format Input */\n.format-input {\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease;\n}\n\n.format-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.format-input.small {\n width: 60px;\n text-align: center;\n}\n\n.format-select {\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n}\n\n.format-select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n/* Color Input */\n.color-input {\n width: 36px;\n height: 28px;\n padding: 2px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n background: var(--mj-bg-surface);\n}\n\n/* Alignment Toggle */\n.alignment-toggle {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n padding: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\n.align-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.align-btn:hover {\n color: var(--mj-text-secondary);\n}\n\n.align-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);\n}\n\n/* Style Buttons */\n.style-buttons {\n display: flex;\n gap: 4px;\n margin-bottom: 12px;\n}\n\n.style-btn {\n width: 32px;\n height: 32px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.style-btn:hover {\n border-color: var(--mj-border-strong);\n color: var(--mj-text-secondary);\n}\n\n.style-btn.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n/* Format Preview */\n.format-preview-section {\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.preview-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.preview-header i {\n color: var(--mj-text-muted);\n}\n\n.preview-table {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.preview-header-cell {\n padding: 8px 12px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.preview-cell {\n padding: 8px 12px;\n font-size: 13px;\n color: var(--mj-text-primary);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.preview-cell:last-child {\n border-bottom: none;\n}\n\n/* Format Actions */\n.format-actions {\n padding: 16px;\n}\n\n.clear-format-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.clear-format-btn:hover {\n background: var(--mj-status-error-bg);\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n/* Sorting Section */\n.sorting-section {\n padding: 0;\n}\n\n.sorting-header {\n margin-bottom: 12px;\n}\n\n.sorting-description,\n.aggregates-description {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-muted);\n line-height: 1.5;\n}\n\n/* Add Sort Button */\n.add-sort-btn,\n.add-aggregate-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n width: 100%;\n border: 2px dashed var(--mj-border-default);\n border-radius: 10px;\n background: transparent;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n margin-bottom: 12px;\n}\n\n.add-sort-btn:hover:not(:disabled),\n.add-aggregate-btn:hover:not(:disabled) {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.add-sort-btn:disabled,\n.add-aggregate-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Sort Items List */\n.sort-items-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-bottom: 12px;\n}\n\n.sort-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n transition: all 0.15s ease;\n}\n\n.sort-item:hover {\n border-color: var(--mj-border-strong);\n}\n\n.sort-item.dragging {\n opacity: 0.4;\n}\n\n.sort-item.drop-target {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n/* Sort Drop Indicator */\n.sort-drop-indicator {\n height: 2px;\n background: var(--mj-brand-primary);\n border-radius: 1px;\n margin: 0 10px;\n}\n\n/* Sort Priority Badge */\n.sort-priority-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-radius: 6px;\n font-size: 11px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n/* Sort Drag Handle */\n.sort-drag-handle {\n cursor: grab;\n color: var(--mj-text-disabled);\n padding: 2px;\n font-size: 12px;\n}\n\n.sort-drag-handle:active {\n cursor: grabbing;\n}\n\n.sort-item:hover .sort-drag-handle {\n color: var(--mj-text-muted);\n}\n\n/* Sort Field Dropdown */\n.sort-field-dropdown {\n flex: 1;\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n min-width: 0;\n}\n\n.sort-field-dropdown:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n/* Sort Direction Toggle */\n.sort-direction-toggle {\n display: flex;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n padding: 2px;\n gap: 2px;\n}\n\n.direction-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 5px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.direction-btn:hover:not(:disabled) {\n color: var(--mj-text-secondary);\n}\n\n.direction-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);\n}\n\n.direction-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n/* Sort Remove Button */\n.sort-remove-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-disabled);\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.sort-remove-btn:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n.sort-remove-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n/* Positioning-only helpers for migrated <mj-alert> callouts. Box chrome\n (bg/border/radius/padding/icon) is owned by the component; only the\n surrounding margins from the bespoke banners are preserved here. */\n.view-config-alert-pos { margin-top: 12px; }\n.view-config-alert-pos-mb { margin-bottom: 12px; }\n.view-config-validation-pos { margin: 0 16px 12px; }\n\n/* Filter Mode Selector */\n.filter-mode-selector {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.filter-mode-btn {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 14px;\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 text-align: left;\n}\n\n.filter-mode-btn:hover:not(:disabled) {\n border-color: var(--mj-border-strong);\n}\n\n.filter-mode-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.filter-mode-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.mode-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n font-size: 16px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n.filter-mode-btn.active .mode-icon {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mode-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mode-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mode-subtitle {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.mode-check {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n/* Smart Filter Section */\n.smart-filter-section {\n padding: 0;\n}\n\n.smart-filter-input-container {\n display: flex;\n gap: 12px;\n padding: 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\n.smart-filter-icon {\n display: flex;\n align-items: flex-start;\n padding-top: 4px;\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.smart-filter-textarea {\n flex: 1;\n border: none;\n outline: none;\n resize: vertical;\n font-size: 14px;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: transparent;\n min-height: 60px;\n}\n\n.smart-filter-textarea::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.smart-filter-textarea:disabled {\n color: var(--mj-text-muted);\n}\n\n/* Smart Filter Explanation */\n/* Smart Filter Examples */\n.smart-filter-examples {\n margin-bottom: 12px;\n}\n\n.examples-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 8px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n}\n\n.examples-header i {\n color: var(--mj-status-warning);\n}\n\n.example-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.example-chip {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 20px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 12px;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.example-chip:hover:not(:disabled) {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.example-chip:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.example-chip i {\n font-size: 11px;\n}\n\n/* Traditional Filter Section */\n.traditional-filter-section {\n padding: 0;\n}\n\n.filter-summary-container {\n margin-bottom: 12px;\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 14px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n}\n\n.summary-info {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n height: 22px;\n padding: 0 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-radius: 11px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.summary-text {\n font-size: 13px;\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.summary-text.no-filters {\n color: var(--mj-text-muted);\n font-weight: 400;\n}\n\n.summary-actions {\n display: flex;\n gap: 8px;\n}\n\n.summary-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.summary-btn:hover:not(:disabled) {\n border-color: var(--mj-border-strong);\n}\n\n.summary-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.summary-btn.clear-btn:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n.summary-btn.primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.summary-btn.primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n}\n\n/* Aggregates Section */\n.aggregates-section {\n padding: 0;\n}\n\n.aggregates-header {\n margin-bottom: 12px;\n}\n\n/* Aggregates List */\n.aggregates-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-bottom: 12px;\n}\n\n.aggregate-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n transition: all 0.15s ease;\n}\n\n.aggregate-item:hover {\n border-color: var(--mj-border-strong);\n}\n\n.aggregate-item.disabled {\n opacity: 0.5;\n}\n\n/* Aggregate Icon */\n.agg-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-radius: 8px;\n color: var(--mj-brand-primary);\n font-size: 14px;\n flex-shrink: 0;\n}\n\n/* Aggregate Content */\n.agg-content {\n flex: 1;\n min-width: 0;\n}\n\n.agg-label {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.agg-details {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 2px;\n}\n\n.agg-type {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.agg-smart-badge {\n color: var(--mj-brand-primary);\n font-size: 11px;\n}\n\n/* Aggregate Actions */\n.agg-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.aggregate-item:hover .agg-actions {\n opacity: 1;\n}\n\n.agg-action-btn {\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.agg-action-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.agg-action-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n.agg-action-btn.toggle-btn.enabled {\n color: var(--mj-brand-primary);\n}\n\n.agg-action-btn.remove-btn:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n.agg-action-btn.edit-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n/* Aggregates Summary */\n.aggregates-summary {\n display: flex;\n gap: 16px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n margin-bottom: 12px;\n}\n\n.aggregates-summary .summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.aggregates-summary .summary-item i {\n color: var(--mj-text-disabled);\n}\n\n/* Settings Tab */\n.form-group {\n margin-bottom: 16px;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.form-input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 14px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease;\n box-sizing: border-box;\n}\n\n.form-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.form-input.invalid {\n border-color: var(--mj-status-error);\n}\n\n.form-textarea {\n resize: vertical;\n min-height: 60px;\n font-family: inherit;\n}\n\n.validation-error {\n font-size: 12px;\n color: var(--mj-status-error);\n margin-top: 4px;\n}\n\n/* Checkbox */\n.checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: pointer;\n}\n\n.checkbox-label input[type=\"checkbox\"] {\n margin-top: 3px;\n flex-shrink: 0;\n}\n\n.checkbox-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.checkbox-text strong {\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.checkbox-text small {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n/* Danger Zone */\n.danger-zone .section-header {\n color: var(--mj-status-error);\n}\n\n.danger-zone .section-header i {\n color: var(--mj-status-error);\n}\n\n.delete-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n border: 1px solid var(--mj-status-error);\n border-radius: 8px;\n background: var(--mj-status-error-bg);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-status-error);\n transition: all 0.15s ease;\n}\n\n.delete-btn:hover {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n/* Panel Footer */\n.panel-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n flex-shrink: 0;\n}\n\n.footer-left {\n display: flex;\n gap: 8px;\n}\n\n.footer-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 9px 18px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.footer-btn:hover:not(:disabled) {\n border-color: var(--mj-border-strong);\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-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n}\n\n.footer-btn.cancel-btn {\n background: var(--mj-bg-surface-card);\n}\n\n.footer-btn.cancel-btn:hover {\n background: var(--mj-bg-surface-active);\n}\n\n/* Responsive */\n@media (max-width: 480px) {\n .config-panel {\n width: 100vw;\n min-width: auto;\n }\n\n .resize-handle {\n display: none;\n }\n}\n"] }]
2883
+ args: [{ standalone: false, selector: 'mj-view-config-panel', template: "<!-- Backdrop -->\n@if (IsOpen) {\n <div class=\"panel-backdrop\" (click)=\"OnClose()\"></div>\n}\n\n<!-- Sliding Panel -->\n<div class=\"config-panel\"\n [class.open]=\"IsOpen\"\n [class.resizing]=\"IsResizing\"\n [style.width.px]=\"PanelWidth\">\n <!-- Resize Handle -->\n <div class=\"resize-handle\"\n (mousedown)=\"OnResizeStart($event)\"\n title=\"Drag to resize\">\n <div class=\"resize-grip\"></div>\n </div>\n\n <!-- Panel Header -->\n <div class=\"panel-header\">\n <div class=\"header-title\">\n <i class=\"fa-solid fa-sliders-h\"></i>\n <span>{{ ViewEntity ? 'Edit View' : 'Configure View' }}</span>\n </div>\n <button class=\"close-btn\" (click)=\"OnClose()\" title=\"Close\" aria-label=\"Close panel\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Tab Navigation -->\n <div class=\"tab-nav\" [class.icon-only]=\"IsIconOnlyMode\">\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'columns'\"\n (click)=\"SetActiveTab('columns')\"\n [title]=\"IsIconOnlyMode ? 'Columns' : ''\">\n <i class=\"fa-solid fa-columns\"></i>\n @if (!IsIconOnlyMode) {\n <span>Columns</span>\n }\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'sorting'\"\n (click)=\"SetActiveTab('sorting')\"\n [title]=\"IsIconOnlyMode ? 'Sorting' + (SortItems.length > 0 ? ' (' + SortItems.length + ')' : '') : ''\">\n <i class=\"fa-solid fa-arrow-up-wide-short\"></i>\n @if (!IsIconOnlyMode) {\n <span>Sorting</span>\n }\n @if (SortItems.length > 0) {\n <span class=\"tab-badge\">{{ SortItems.length }}</span>\n }\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'filters'\"\n (click)=\"SetActiveTab('filters')\"\n [title]=\"IsIconOnlyMode ? 'Filters' : ''\">\n <i class=\"fa-solid fa-filter\"></i>\n @if (!IsIconOnlyMode) {\n <span>Filters</span>\n }\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'aggregates'\"\n (click)=\"SetActiveTab('aggregates')\"\n [title]=\"IsIconOnlyMode ? 'Aggregates' + (EnabledAggregatesCount > 0 ? ' (' + EnabledAggregatesCount + ')' : '') : ''\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n @if (!IsIconOnlyMode) {\n <span>Aggregates</span>\n }\n @if (EnabledAggregatesCount > 0) {\n <span class=\"tab-badge\">{{ EnabledAggregatesCount }}</span>\n }\n </button>\n @if (ViewEntity || DefaultSaveAsNew) {\n <button\n class=\"tab-btn\"\n [class.active]=\"ActiveTab === 'settings'\"\n (click)=\"SetActiveTab('settings')\"\n [title]=\"IsIconOnlyMode ? 'Settings' : ''\">\n <i class=\"fa-solid fa-cog\"></i>\n @if (!IsIconOnlyMode) {\n <span>Settings</span>\n }\n </button>\n }\n </div>\n\n <!-- Panel Content -->\n <div class=\"panel-content\">\n <!-- Columns Tab -->\n @if (ActiveTab === 'columns' && !FormatEditingColumn) {\n <div class=\"tab-content\">\n <!-- Visible Columns -->\n <div class=\"config-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-eye\"></i>\n <span>Visible Columns</span>\n <span class=\"column-count\">{{ VisibleColumns.length }}</span>\n </div>\n <div class=\"column-list visible-columns\">\n @for (column of VisibleColumns; track column.fieldId; let i = $index) {\n <!-- Drop indicator before -->\n @if (IsDropBefore(column)) {\n <div class=\"drop-indicator\"></div>\n }\n <div\n class=\"column-item\"\n [class.dragging]=\"DraggedColumn === column\"\n [class.drop-target]=\"DropTargetColumn === column\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event, column)\"\n (dragover)=\"OnDragOver($event, column)\"\n (dragleave)=\"OnDragLeave($event)\"\n (drop)=\"OnDrop($event, column)\"\n (dragend)=\"OnDragEnd($event)\">\n <div class=\"drag-handle\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n <span class=\"column-name\">\n {{ column.userDisplayName || column.displayName }}\n @if (column.userDisplayName) {\n <i class=\"fa-solid fa-pen-nib alias-indicator\" title=\"Custom name: {{ column.userDisplayName }}\"></i>\n }\n @if (HasCustomFormat(column)) {\n <i class=\"fa-solid fa-paintbrush format-indicator\" title=\"Custom formatting applied\"></i>\n }\n </span>\n <div class=\"column-actions\">\n <button\n class=\"action-btn format-btn\"\n (click)=\"OpenFormatEditor(column)\"\n title=\"Format column\">\n <i class=\"fa-solid fa-paintbrush\"></i>\n </button>\n <button\n class=\"action-btn\"\n [disabled]=\"i === 0\"\n (click)=\"MoveColumnUp(column)\"\n title=\"Move up\">\n <i class=\"fa-solid fa-chevron-up\"></i>\n </button>\n <button\n class=\"action-btn\"\n [disabled]=\"i === VisibleColumns.length - 1\"\n (click)=\"MoveColumnDown(column)\"\n title=\"Move down\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n <button\n class=\"action-btn hide-btn\"\n (click)=\"ToggleColumnVisibility(column)\"\n title=\"Hide column\">\n <i class=\"fa-solid fa-eye-slash\"></i>\n </button>\n </div>\n </div>\n <!-- Drop indicator after (only for last item) -->\n @if (IsDropAfter(column) && i === VisibleColumns.length - 1) {\n <div class=\"drop-indicator\"></div>\n }\n }\n @if (VisibleColumns.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Variant=\"warning\"\n Title=\"No columns visible. Add columns from below.\" />\n }\n </div>\n </div>\n\n <!-- Hidden Columns -->\n <div class=\"config-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-eye-slash\"></i>\n <span>Hidden Columns</span>\n <span class=\"column-count\">{{ HiddenColumns.length }}</span>\n </div>\n @if (HiddenColumns.length > 5) {\n <div class=\"column-search\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n type=\"text\"\n placeholder=\"Search columns...\"\n [(ngModel)]=\"ColumnSearchText\"\n />\n </div>\n }\n <div class=\"column-list hidden-columns\">\n @for (column of FilteredHiddenColumns; track column.fieldId) {\n <div class=\"column-item hidden\">\n <span class=\"column-name\">{{ column.displayName }}</span>\n <button\n class=\"action-btn show-btn\"\n (click)=\"ToggleColumnVisibility(column)\"\n title=\"Show column\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n </div>\n }\n @if (HiddenColumns.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Variant=\"success\"\n Title=\"All columns are visible\" />\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Column Format Editor Sub-Panel -->\n @if (ActiveTab === 'columns' && FormatEditingColumn) {\n <div class=\"format-editor\">\n <!-- Format Editor Header -->\n <div class=\"format-editor-header\">\n <button class=\"back-btn\" (click)=\"CloseFormatEditor()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <div class=\"format-editor-title\">\n <span>Format: {{ FormatEditingColumn.userDisplayName || FormatEditingColumn.displayName }}</span>\n </div>\n </div>\n\n <!-- Column Alias Section -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-pen-nib\"></i>\n <span>Column Name</span>\n </div>\n <div class=\"format-row\">\n <label>Display As</label>\n <input type=\"text\" class=\"format-input\"\n [value]=\"FormatEditingColumn.userDisplayName || ''\"\n (input)=\"UpdateUserDisplayName($any($event.target).value)\"\n [placeholder]=\"FormatEditingColumn.displayName\" />\n </div>\n @if (FormatEditingColumn.userDisplayName) {\n <div class=\"format-row alias-info\">\n <small class=\"muted-text\">Original: {{ FormatEditingColumn.displayName }}</small>\n <button class=\"clear-alias-btn\" (click)=\"FormatEditingColumn.userDisplayName = undefined\" title=\"Clear alias\" aria-label=\"Clear alias\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Preview Section -->\n <div class=\"format-preview-section\">\n <div class=\"preview-header\">\n <i class=\"fa-solid fa-eye\"></i>\n <span>Preview</span>\n </div>\n <div class=\"preview-table\">\n <div class=\"preview-header-cell\"\n [style.font-weight]=\"FormatEditingColumn.format?.headerStyle?.bold ? 'bold' : 'normal'\"\n [style.font-style]=\"FormatEditingColumn.format?.headerStyle?.italic ? 'italic' : 'normal'\"\n [style.text-decoration]=\"FormatEditingColumn.format?.headerStyle?.underline ? 'underline' : 'none'\"\n [style.color]=\"FormatEditingColumn.format?.headerStyle?.color\"\n [style.background-color]=\"FormatEditingColumn.format?.headerStyle?.backgroundColor\">\n {{ FormatEditingColumn.userDisplayName || FormatEditingColumn.displayName }}\n </div>\n @for (value of GetSampleValues(FormatEditingColumn); track $index) {\n <div class=\"preview-cell\"\n [style.text-align]=\"FormatEditingColumn.format?.align || 'left'\"\n [style.font-weight]=\"FormatEditingColumn.format?.cellStyle?.bold ? 'bold' : 'normal'\"\n [style.font-style]=\"FormatEditingColumn.format?.cellStyle?.italic ? 'italic' : 'normal'\"\n [style.text-decoration]=\"FormatEditingColumn.format?.cellStyle?.underline ? 'underline' : 'none'\"\n [style.color]=\"FormatEditingColumn.format?.cellStyle?.color\"\n [style.background-color]=\"FormatEditingColumn.format?.cellStyle?.backgroundColor\">\n {{ FormatPreviewValue(value, FormatEditingColumn.format, FormatEditingColumn.field) }}\n </div>\n }\n </div>\n </div>\n\n <!-- Format Type -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-hashtag\"></i>\n <span>Format Type</span>\n </div>\n <select class=\"format-select\" [(ngModel)]=\"FormatEditingColumn.format!.type\">\n <option value=\"auto\">Auto (Smart Default)</option>\n <option value=\"text\">Text</option>\n <option value=\"number\">Number</option>\n <option value=\"currency\">Currency</option>\n <option value=\"percent\">Percent</option>\n <option value=\"date\">Date</option>\n <option value=\"datetime\">Date & Time</option>\n <option value=\"boolean\">Boolean</option>\n </select>\n </div>\n\n <!-- Type-Specific Options -->\n @if (FormatEditingColumn.format?.type === 'number' || FormatEditingColumn.format?.type === 'currency' || FormatEditingColumn.format?.type === 'percent') {\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-sliders\"></i>\n <span>Number Options</span>\n </div>\n <div class=\"format-row\">\n <label>Decimal Places</label>\n <input type=\"number\" class=\"format-input small\" min=\"0\" max=\"10\"\n [(ngModel)]=\"FormatEditingColumn.format!.decimals\" />\n </div>\n @if (FormatEditingColumn.format?.type === 'currency') {\n <div class=\"format-row\">\n <label>Currency</label>\n <select class=\"format-select\" [(ngModel)]=\"FormatEditingColumn.format!.currencyCode\">\n <option value=\"USD\">USD ($)</option>\n <option value=\"EUR\">EUR (\u20AC)</option>\n <option value=\"GBP\">GBP (\u00A3)</option>\n <option value=\"JPY\">JPY (\u00A5)</option>\n <option value=\"CAD\">CAD ($)</option>\n <option value=\"AUD\">AUD ($)</option>\n </select>\n </div>\n }\n <div class=\"format-row\">\n <label>Thousands Separator</label>\n <input type=\"checkbox\" [(ngModel)]=\"FormatEditingColumn.format!.thousandsSeparator\" />\n </div>\n </div>\n }\n\n @if (FormatEditingColumn.format?.type === 'date' || FormatEditingColumn.format?.type === 'datetime') {\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-calendar\"></i>\n <span>Date Options</span>\n </div>\n <div class=\"format-row\">\n <label>Format</label>\n <select class=\"format-select\" [(ngModel)]=\"FormatEditingColumn.format!.dateFormat\">\n <option value=\"short\">Short (1/15/25)</option>\n <option value=\"short-weekday\">Short + Day (Wed, 1/15/25)</option>\n <option value=\"medium\">Medium (Jan 15, 2025)</option>\n <option value=\"medium-weekday\">Medium + Day (Wed, Jan 15, 2025)</option>\n <option value=\"long\">Long (January 15, 2025)</option>\n <option value=\"long-weekday\">Long + Day (Wednesday, January 15, 2025)</option>\n </select>\n </div>\n </div>\n }\n\n @if (FormatEditingColumn.format?.type === 'boolean') {\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-toggle-on\"></i>\n <span>Boolean Options</span>\n </div>\n <div class=\"format-row\">\n <label>True Label</label>\n <input type=\"text\" class=\"format-input\" [(ngModel)]=\"FormatEditingColumn.format!.trueLabel\" placeholder=\"Yes\" />\n </div>\n <div class=\"format-row\">\n <label>False Label</label>\n <input type=\"text\" class=\"format-input\" [(ngModel)]=\"FormatEditingColumn.format!.falseLabel\" placeholder=\"No\" />\n </div>\n <div class=\"format-row\">\n <label>Display As</label>\n <select class=\"format-select\" [(ngModel)]=\"FormatEditingColumn.format!.booleanDisplay\">\n <option value=\"text\">Text</option>\n <option value=\"checkbox\">Checkbox</option>\n <option value=\"icon\">Icon</option>\n </select>\n </div>\n </div>\n }\n\n <!-- Alignment -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-align-left\"></i>\n <span>Alignment</span>\n </div>\n <div class=\"alignment-toggle\">\n <button class=\"align-btn\" [class.active]=\"FormatEditingColumn.format?.align === 'left' || !FormatEditingColumn.format?.align\"\n (click)=\"FormatEditingColumn.format!.align = 'left'\" title=\"Left\">\n <i class=\"fa-solid fa-align-left\"></i>\n </button>\n <button class=\"align-btn\" [class.active]=\"FormatEditingColumn.format?.align === 'center'\"\n (click)=\"FormatEditingColumn.format!.align = 'center'\" title=\"Center\">\n <i class=\"fa-solid fa-align-center\"></i>\n </button>\n <button class=\"align-btn\" [class.active]=\"FormatEditingColumn.format?.align === 'right'\"\n (click)=\"FormatEditingColumn.format!.align = 'right'\" title=\"Right\">\n <i class=\"fa-solid fa-align-right\"></i>\n </button>\n </div>\n </div>\n\n <!-- Header Style -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-heading\"></i>\n <span>Header Style</span>\n </div>\n <div class=\"style-buttons\">\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.headerStyle?.bold\"\n (click)=\"ToggleHeaderStyle('bold')\"\n title=\"Bold\">\n <i class=\"fa-solid fa-bold\"></i>\n </button>\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.headerStyle?.italic\"\n (click)=\"ToggleHeaderStyle('italic')\"\n title=\"Italic\">\n <i class=\"fa-solid fa-italic\"></i>\n </button>\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.headerStyle?.underline\"\n (click)=\"ToggleHeaderStyle('underline')\"\n title=\"Underline\">\n <i class=\"fa-solid fa-underline\"></i>\n </button>\n </div>\n <div class=\"format-row\">\n <label>Text Color</label>\n <input type=\"color\" class=\"color-input\"\n [value]=\"FormatEditingColumn.format?.headerStyle?.color || '#000000'\"\n (input)=\"UpdateHeaderColor('color', $any($event.target).value)\" />\n </div>\n <div class=\"format-row\">\n <label>Background</label>\n <input type=\"color\" class=\"color-input\"\n [value]=\"FormatEditingColumn.format?.headerStyle?.backgroundColor || '#ffffff'\"\n (input)=\"UpdateHeaderColor('backgroundColor', $any($event.target).value)\" />\n </div>\n </div>\n\n <!-- Cell Style -->\n <div class=\"format-section\">\n <div class=\"format-section-header\">\n <i class=\"fa-solid fa-table-cells\"></i>\n <span>Cell Style</span>\n </div>\n <div class=\"style-buttons\">\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.cellStyle?.bold\"\n (click)=\"ToggleCellStyle('bold')\"\n title=\"Bold\">\n <i class=\"fa-solid fa-bold\"></i>\n </button>\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.cellStyle?.italic\"\n (click)=\"ToggleCellStyle('italic')\"\n title=\"Italic\">\n <i class=\"fa-solid fa-italic\"></i>\n </button>\n <button class=\"style-btn\" [class.active]=\"FormatEditingColumn.format?.cellStyle?.underline\"\n (click)=\"ToggleCellStyle('underline')\"\n title=\"Underline\">\n <i class=\"fa-solid fa-underline\"></i>\n </button>\n </div>\n <div class=\"format-row\">\n <label>Text Color</label>\n <input type=\"color\" class=\"color-input\"\n [value]=\"FormatEditingColumn.format?.cellStyle?.color || '#000000'\"\n (input)=\"UpdateCellColor('color', $any($event.target).value)\" />\n </div>\n <div class=\"format-row\">\n <label>Background</label>\n <input type=\"color\" class=\"color-input\"\n [value]=\"FormatEditingColumn.format?.cellStyle?.backgroundColor || '#ffffff'\"\n (input)=\"UpdateCellColor('backgroundColor', $any($event.target).value)\" />\n </div>\n </div>\n\n <!-- Clear Format Button -->\n <div class=\"format-actions\">\n <button class=\"clear-format-btn\" (click)=\"ClearColumnFormat(FormatEditingColumn); CloseFormatEditor()\">\n <i class=\"fa-solid fa-eraser\"></i>\n Clear Formatting\n </button>\n </div>\n </div>\n }\n\n <!-- Sorting Tab -->\n @if (ActiveTab === 'sorting') {\n <div class=\"tab-content\">\n <div class=\"config-section sorting-section\">\n <div class=\"sorting-header\">\n <p class=\"sorting-description\">\n Define how records should be ordered. Add multiple levels to sort by secondary fields when values are equal.\n </p>\n </div>\n\n <!-- Add Sort Button -->\n <button\n class=\"add-sort-btn\"\n (click)=\"AddSortLevel()\"\n [disabled]=\"!CanEdit || SortItems.length >= SortableFields.length\"\n title=\"Add another sort level\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add Sort Level</span>\n </button>\n\n <!-- Sort Items List -->\n @if (SortItems.length > 0) {\n <div class=\"sort-items-list\">\n @for (sortItem of SortItems; track $index; let i = $index) {\n <!-- Drop indicator before -->\n @if (IsSortDropBefore(sortItem)) {\n <div class=\"sort-drop-indicator\"></div>\n }\n <div\n class=\"sort-item\"\n [class.dragging]=\"DraggedSortItem === sortItem\"\n [class.drop-target]=\"DropTargetSortItem === sortItem\"\n draggable=\"true\"\n (dragstart)=\"OnSortDragStart($event, sortItem)\"\n (dragover)=\"OnSortDragOver($event, sortItem)\"\n (dragleave)=\"OnSortDragLeave($event)\"\n (drop)=\"OnSortDrop($event, sortItem)\"\n (dragend)=\"OnSortDragEnd($event)\">\n <!-- Priority Badge -->\n <div class=\"sort-priority-badge\">{{ i + 1 }}</div>\n <!-- Drag Handle -->\n <div class=\"sort-drag-handle\" title=\"Drag to reorder\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n <!-- Field Dropdown -->\n <select\n class=\"sort-field-dropdown\"\n [(ngModel)]=\"sortItem.field\"\n (ngModelChange)=\"OnSortFieldChange(sortItem, $event)\"\n [disabled]=\"!CanEdit\">\n @for (field of SortableFields; track field.ID) {\n <option [value]=\"field.Name\">{{ field.DisplayNameOrName }}</option>\n }\n </select>\n <!-- Direction Toggle -->\n <div class=\"sort-direction-toggle\">\n <button\n class=\"direction-btn\"\n [class.active]=\"sortItem.direction === 'asc'\"\n (click)=\"OnSortDirectionChange(sortItem, 'asc')\"\n [disabled]=\"!CanEdit\"\n title=\"Ascending (A-Z, 1-9)\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n </button>\n <button\n class=\"direction-btn\"\n [class.active]=\"sortItem.direction === 'desc'\"\n (click)=\"OnSortDirectionChange(sortItem, 'desc')\"\n [disabled]=\"!CanEdit\"\n title=\"Descending (Z-A, 9-1)\">\n <i class=\"fa-solid fa-arrow-down\"></i>\n </button>\n </div>\n <!-- Remove Button -->\n <button\n class=\"sort-remove-btn\"\n (click)=\"RemoveSortLevel(sortItem)\"\n [disabled]=\"!CanEdit\"\n title=\"Remove sort level\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Drop indicator after (only for last item) -->\n @if (IsSortDropAfter(sortItem) && i === SortItems.length - 1) {\n <div class=\"sort-drop-indicator\"></div>\n }\n }\n </div>\n <!-- Multi-sort hint -->\n @if (SortItems.length > 1) {\n <mj-alert Variant=\"info\" Size=\"sm\">Drag items to change priority. Records sort by first level, then second, etc.</mj-alert>\n }\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-arrow-up-wide-short\"\n Title=\"No sorting configured\"\n Message='Click \"Add Sort Level\" to define how records should be ordered' />\n }\n </div>\n </div>\n }\n\n <!-- Filters Tab -->\n @if (ActiveTab === 'filters') {\n <div class=\"tab-content\">\n <!-- Filter Mode Selection -->\n <div class=\"filter-mode-selector\">\n <button\n class=\"filter-mode-btn\"\n [class.active]=\"FilterMode === 'smart'\"\n (click)=\"SetFilterMode('smart')\"\n [disabled]=\"!CanEdit\">\n <div class=\"mode-icon\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n </div>\n <div class=\"mode-content\">\n <span class=\"mode-title\">Smart Filter</span>\n <span class=\"mode-subtitle\">Use AI to filter with natural language</span>\n </div>\n @if (FilterMode === 'smart') {\n <i class=\"fa-solid fa-check mode-check\"></i>\n }\n </button>\n <button\n class=\"filter-mode-btn\"\n [class.active]=\"FilterMode === 'traditional'\"\n (click)=\"SetFilterMode('traditional')\"\n [disabled]=\"!CanEdit\">\n <div class=\"mode-icon\">\n <i class=\"fa-solid fa-sliders\"></i>\n </div>\n <div class=\"mode-content\">\n <span class=\"mode-title\">Traditional Filter</span>\n <span class=\"mode-subtitle\">Build filters with field/operator/value</span>\n </div>\n @if (FilterMode === 'traditional') {\n <i class=\"fa-solid fa-check mode-check\"></i>\n }\n </button>\n </div>\n\n <!-- Smart Filter Section -->\n @if (FilterMode === 'smart') {\n <div class=\"config-section smart-filter-section\">\n <div class=\"smart-filter-input-container\">\n <div class=\"smart-filter-icon\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n </div>\n <textarea\n id=\"SmartFilterPrompt\"\n class=\"smart-filter-textarea\"\n placeholder=\"Describe what you're looking for...\"\n [(ngModel)]=\"SmartFilterPrompt\"\n [disabled]=\"!CanEdit\"\n rows=\"3\"\n ></textarea>\n </div>\n\n @if (SmartFilterExplanation) {\n <mj-alert Variant=\"info\" Size=\"sm\" Icon=\"fa-solid fa-robot\" class=\"view-config-alert-pos-mb\">{{ SmartFilterExplanation }}</mj-alert>\n }\n\n <!-- Example Prompts -->\n <div class=\"smart-filter-examples\">\n <div class=\"examples-header\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Try these examples:</span>\n </div>\n <div class=\"example-chips\">\n <button\n class=\"example-chip\"\n (click)=\"ApplySmartFilterExample('Show records created in the last 30 days')\"\n [disabled]=\"!CanEdit\">\n <i class=\"fa-regular fa-calendar\"></i>\n Last 30 days\n </button>\n <button\n class=\"example-chip\"\n (click)=\"ApplySmartFilterExample('Active records only')\"\n [disabled]=\"!CanEdit\">\n <i class=\"fa-solid fa-circle-check\"></i>\n Active only\n </button>\n <button\n class=\"example-chip\"\n (click)=\"ApplySmartFilterExample('Records with high priority')\"\n [disabled]=\"!CanEdit\">\n <i class=\"fa-solid fa-star\"></i>\n High priority\n </button>\n <button\n class=\"example-chip\"\n (click)=\"ApplySmartFilterExample('Records modified this week')\"\n [disabled]=\"!CanEdit\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n Modified this week\n </button>\n </div>\n </div>\n\n <mj-alert Variant=\"info\" Size=\"sm\" class=\"view-config-alert-pos\">The AI will interpret your description and create the appropriate filter when you save the view.</mj-alert>\n </div>\n }\n\n <!-- Traditional Filter Section -->\n @if (FilterMode === 'traditional') {\n <div class=\"config-section traditional-filter-section\">\n <div class=\"filter-summary-container\">\n <div class=\"filter-summary\">\n <div class=\"summary-info\">\n @if (GetFilterCount() > 0) {\n <span class=\"filter-badge\">{{ GetFilterCount() }}</span>\n <span class=\"summary-text\">{{ GetFilterSummary() }}</span>\n } @else {\n <span class=\"summary-text no-filters\">No filters configured</span>\n }\n </div>\n <div class=\"summary-actions\">\n @if (GetFilterCount() > 0 && CanEdit) {\n <button\n class=\"summary-btn clear-btn\"\n (click)=\"ClearFilters()\"\n title=\"Clear all filters\">\n <i class=\"fa-solid fa-times\"></i>\n Clear\n </button>\n }\n <button\n class=\"summary-btn edit-btn primary\"\n (click)=\"OpenFilterDialog()\"\n [disabled]=\"!CanEdit && GetFilterCount() === 0\"\n title=\"Edit filters\">\n <i class=\"fa-solid fa-pen\"></i>\n {{ GetFilterCount() > 0 ? 'Edit Filters' : 'Add Filters' }}\n </button>\n </div>\n </div>\n </div>\n\n <mj-alert Variant=\"info\" Size=\"sm\" class=\"view-config-alert-pos\">Build precise filters by selecting fields, operators, and values. Use groups for complex AND/OR logic.</mj-alert>\n </div>\n }\n </div>\n }\n\n <!-- Aggregates Tab -->\n @if (ActiveTab === 'aggregates') {\n <div class=\"tab-content\">\n <div class=\"config-section aggregates-section\">\n <div class=\"aggregates-header\">\n <p class=\"aggregates-description\">\n Add summary calculations like totals, averages, and counts. Display them in cards or as column footers.\n </p>\n </div>\n\n <!-- Add Aggregate Button -->\n <button\n class=\"add-aggregate-btn\"\n (click)=\"OpenAddAggregateDialog()\"\n [disabled]=\"!CanEdit\"\n title=\"Add a new aggregate\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Add Aggregate</span>\n </button>\n\n <!-- Aggregates List -->\n @if (Aggregates.length > 0) {\n <div class=\"aggregates-list\">\n @for (agg of Aggregates; track agg.id; let i = $index) {\n <div class=\"aggregate-item\" [class.disabled]=\"agg.enabled === false\">\n <!-- Icon -->\n <div class=\"agg-icon\">\n <i [class]=\"agg.icon || 'fa-solid fa-chart-simple'\"></i>\n </div>\n\n <!-- Content -->\n <div class=\"agg-content\">\n <div class=\"agg-label\">{{ agg.label }}</div>\n <div class=\"agg-details\">\n <span class=\"agg-type\">\n <i [class]=\"agg.displayType === 'card' ? 'fa-solid fa-id-card' : 'fa-solid fa-table-columns'\"></i>\n {{ agg.displayType === 'card' ? 'Card' : 'Column Footer' }}\n </span>\n @if (agg.smartPrompt) {\n <span class=\"agg-smart-badge\" title=\"AI-generated\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n </span>\n }\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"agg-actions\">\n <button\n class=\"agg-action-btn\"\n [disabled]=\"i === 0 || !CanEdit\"\n (click)=\"MoveAggregateUp(agg)\"\n title=\"Move up\">\n <i class=\"fa-solid fa-chevron-up\"></i>\n </button>\n <button\n class=\"agg-action-btn\"\n [disabled]=\"i === Aggregates.length - 1 || !CanEdit\"\n (click)=\"MoveAggregateDown(agg)\"\n title=\"Move down\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n <button\n class=\"agg-action-btn toggle-btn\"\n [class.enabled]=\"agg.enabled !== false\"\n (click)=\"ToggleAggregateEnabled(agg, $event)\"\n [disabled]=\"!CanEdit\"\n [title]=\"agg.enabled !== false ? 'Disable' : 'Enable'\">\n <i [class]=\"agg.enabled !== false ? 'fa-solid fa-eye' : 'fa-solid fa-eye-slash'\"></i>\n </button>\n <button\n class=\"agg-action-btn edit-btn\"\n (click)=\"EditAggregate(agg)\"\n [disabled]=\"!CanEdit\"\n title=\"Edit\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button\n class=\"agg-action-btn remove-btn\"\n (click)=\"RemoveAggregate(agg)\"\n [disabled]=\"!CanEdit\"\n title=\"Remove\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n </div>\n }\n </div>\n\n <!-- Summary -->\n <div class=\"aggregates-summary\">\n <div class=\"summary-item\">\n <i class=\"fa-solid fa-id-card\"></i>\n <span>{{ CardAggregates.length }} card{{ CardAggregates.length !== 1 ? 's' : '' }}</span>\n </div>\n <div class=\"summary-item\">\n <i class=\"fa-solid fa-table-columns\"></i>\n <span>{{ ColumnAggregates.length }} column footer{{ ColumnAggregates.length !== 1 ? 's' : '' }}</span>\n </div>\n </div>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-chart-simple\"\n Title=\"No aggregates configured\"\n Message='Click \"Add Aggregate\" to create summary calculations' />\n }\n\n <!-- Info Tip -->\n <mj-alert Variant=\"info\" Size=\"sm\" class=\"view-config-alert-pos\">Aggregates are calculated server-side for accuracy with filtered/paginated data.</mj-alert>\n </div>\n </div>\n }\n\n <!-- Settings Tab -->\n @if (ActiveTab === 'settings') {\n <div class=\"tab-content\">\n <!-- View Name & Description -->\n <div class=\"config-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>View Details</span>\n </div>\n <div class=\"form-group\">\n <label for=\"ViewName\">Name</label>\n <input\n id=\"ViewName\"\n type=\"text\"\n class=\"form-input\"\n [class.invalid]=\"!ViewName || !ViewName.trim()\"\n placeholder=\"Enter view name...\"\n [(ngModel)]=\"ViewName\"\n [disabled]=\"!CanEdit\"\n />\n @if (!ViewName || !ViewName.trim()) {\n <span class=\"validation-error\">View name is required</span>\n }\n </div>\n <div class=\"form-group\">\n <label for=\"ViewDescription\">Description</label>\n <textarea\n id=\"ViewDescription\"\n class=\"form-input form-textarea\"\n placeholder=\"Describe this view...\"\n [(ngModel)]=\"ViewDescription\"\n [disabled]=\"!CanEdit\"\n rows=\"3\"\n ></textarea>\n </div>\n </div>\n\n <!-- Sharing -->\n <div class=\"config-section\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-share-alt\"></i>\n <span>Sharing</span>\n </div>\n <label class=\"checkbox-label\">\n <input\n type=\"checkbox\"\n [(ngModel)]=\"IsShared\"\n [disabled]=\"!CanEdit\"\n />\n <span class=\"checkbox-text\">\n <strong>Share with others</strong>\n <small>Allow other users to use this view</small>\n </span>\n </label>\n </div>\n\n <!-- Danger Zone -->\n @if (ViewEntity && CanDelete) {\n <div class=\"config-section danger-zone\">\n <div class=\"section-header\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>Danger Zone</span>\n </div>\n <button class=\"delete-btn\" (click)=\"OnDelete()\">\n <i class=\"fa-solid fa-trash\"></i>\n <span>Delete View</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Validation Errors Banner -->\n @if (ValidationErrors.length > 0 && VisibleColumns.length === 0) {\n <mj-alert Variant=\"error\" Size=\"sm\" class=\"view-config-validation-pos\">At least one column must be visible</mj-alert>\n }\n\n <!-- Panel Footer -->\n <div class=\"panel-footer\">\n <!-- Action buttons on LEFT -->\n <div class=\"footer-left\">\n @if (ViewEntity && CanEdit) {\n <!-- Editing an existing saved view -->\n <button\n class=\"footer-btn save-btn primary\"\n (click)=\"OnSave()\"\n [disabled]=\"IsSaving || !IsValid\">\n @if (IsSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n }\n {{ IsSaving ? 'Saving...' : 'Save' }}\n </button>\n }\n @if (!ViewEntity && DefaultSaveAsNew) {\n <!-- Creating a new view (from quick Save dialog) -->\n <button\n class=\"footer-btn save-btn primary\"\n (click)=\"OnSaveAsNew()\"\n [disabled]=\"IsSaving || !IsValidForSaveAsNew\">\n @if (IsSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-plus\"></i>\n }\n {{ IsSaving ? 'Saving...' : 'Create View' }}\n </button>\n }\n @if (!ViewEntity && !DefaultSaveAsNew) {\n <!-- Default/Dynamic view - Save to user settings -->\n <button\n class=\"footer-btn save-btn primary\"\n (click)=\"OnSaveDefaults()\"\n [disabled]=\"IsSaving\">\n @if (IsSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n }\n {{ IsSaving ? 'Saving...' : 'Save' }}\n </button>\n }\n </div>\n <!-- Cancel button on RIGHT -->\n <button class=\"footer-btn cancel-btn\" (click)=\"OnClose()\">\n Cancel\n </button>\n </div>\n</div>\n\n<!-- Aggregate Setup Dialog -->\n<mj-aggregate-setup-dialog\n [Entity]=\"Entity\"\n [Aggregate]=\"EditingAggregate\"\n [IsOpen]=\"ShowAggregateDialog\"\n (Close)=\"CloseAggregateDialog()\"\n (Save)=\"OnAggregateSave($event)\">\n</mj-aggregate-setup-dialog>\n\n<!-- Delete Confirmation Dialog -->\n<mj-ev-confirm-dialog\n [IsOpen]=\"ShowDeleteConfirm\"\n Title=\"Delete View\"\n [Message]=\"'Are you sure you want to delete \\'' + ViewName + '\\'?'\"\n DetailMessage=\"This action cannot be undone. All users who have access to this view will lose it.\"\n ConfirmText=\"Delete\"\n ConfirmStyle=\"danger\"\n Icon=\"fa-solid fa-trash\"\n (Confirmed)=\"OnDeleteConfirmed()\"\n (Cancelled)=\"OnDeleteCancelled()\">\n</mj-ev-confirm-dialog>\n\n<!-- Filter Mode Switch Confirmation Dialog (BUG-006) -->\n<mj-ev-confirm-dialog\n [IsOpen]=\"ShowFilterModeSwitchConfirm\"\n Title=\"Switch Filter Mode\"\n [Message]=\"FilterMode === 'smart' ? 'Switching to Traditional mode will clear your smart filter prompt.' : 'Switching to Smart mode will clear your traditional filter rules.'\"\n DetailMessage=\"You can switch back later, but the current filter data will be lost.\"\n ConfirmText=\"Switch\"\n ConfirmStyle=\"primary\"\n Icon=\"fa-solid fa-exchange-alt\"\n (Confirmed)=\"OnFilterModeSwitchConfirmed()\"\n (Cancelled)=\"OnFilterModeSwitchCancelled()\">\n</mj-ev-confirm-dialog>\n", styles: ["/* Backdrop */\n.panel-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.3);\n z-index: 1200; /* Above Leaflet map (1000), content-header (1100) */\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Sliding Panel */\n.config-panel {\n position: fixed;\n top: 60px;\n right: 0;\n width: 450px;\n min-width: 360px;\n max-width: min(800px, 100vw);\n height: calc(100% - 60px);\n background: var(--mj-bg-surface);\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);\n z-index: 1201; /* Above backdrop (1200) */\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.25s ease, width 0s;\n}\n\n.config-panel.open {\n transform: translateX(0);\n}\n\n.config-panel.resizing {\n transition: none;\n user-select: none;\n}\n\n/* Resize Handle */\n.resize-handle {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 6px;\n cursor: ew-resize;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.resize-handle:hover,\n.config-panel.resizing .resize-handle {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.resize-grip {\n width: 3px;\n height: 40px;\n background: var(--mj-border-strong);\n border-radius: 3px;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.resize-handle:hover .resize-grip,\n.config-panel.resizing .resize-grip {\n opacity: 1;\n background: var(--mj-brand-primary);\n}\n\n/* Panel Header */\n.panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n flex-shrink: 0;\n}\n\n.header-title {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.header-title i {\n color: var(--mj-brand-primary);\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-hover);\n color: var(--mj-text-primary);\n}\n\n/* Tab Navigation */\n.tab-nav {\n display: flex;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n flex-shrink: 0;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.tab-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 12px 16px;\n border: none;\n background: transparent;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 13px;\n font-weight: 500;\n transition: all 0.15s ease;\n position: relative;\n white-space: nowrap;\n}\n\n.tab-btn:hover {\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-hover);\n}\n\n.tab-btn.active {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.tab-btn.active::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 8px;\n right: 8px;\n height: 2px;\n background: var(--mj-brand-primary);\n border-radius: 2px 2px 0 0;\n}\n\n.tab-btn i {\n font-size: 14px;\n}\n\n.tab-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-radius: 9px;\n font-size: 11px;\n font-weight: 600;\n}\n\n/* Icon-only mode for narrow panel */\n.tab-nav.icon-only .tab-btn {\n padding: 12px 8px;\n}\n\n/* Panel Content */\n.panel-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.tab-content {\n padding: 16px;\n}\n\n/* Config Sections */\n.config-section {\n margin-bottom: 20px;\n}\n\n.section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 0;\n margin-bottom: 8px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.section-header i {\n color: var(--mj-text-muted);\n font-size: 14px;\n}\n\n.column-count {\n margin-left: auto;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 12px;\n font-weight: 500;\n}\n\n/* Column Search */\n.column-search {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-bottom: 8px;\n}\n\n.column-search i {\n color: var(--mj-text-disabled);\n font-size: 12px;\n}\n\n.column-search input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 13px;\n background: transparent;\n color: var(--mj-text-primary);\n}\n\n.column-search input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n/* Column List */\n.column-list {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n/* Column Item */\n.column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-radius: 8px;\n transition: background 0.15s ease;\n cursor: default;\n}\n\n.column-item:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.column-item.hidden {\n opacity: 0.7;\n}\n\n.column-item.hidden:hover {\n opacity: 1;\n}\n\n.column-item.dragging {\n opacity: 0.4;\n}\n\n.column-item.drop-target {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n/* Drop Indicator */\n.drop-indicator {\n height: 2px;\n background: var(--mj-brand-primary);\n border-radius: 1px;\n margin: 0 10px;\n}\n\n/* Drag Handle */\n.drag-handle {\n display: flex;\n align-items: center;\n cursor: grab;\n color: var(--mj-text-disabled);\n font-size: 12px;\n padding: 4px 2px;\n}\n\n.drag-handle:active {\n cursor: grabbing;\n}\n\n.column-item:hover .drag-handle {\n color: var(--mj-text-muted);\n}\n\n/* Column Name */\n.column-name {\n flex: 1;\n font-size: 13px;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.alias-indicator {\n font-size: 10px;\n color: var(--mj-brand-primary);\n margin-left: 4px;\n}\n\n.format-indicator {\n font-size: 10px;\n color: var(--mj-status-warning);\n margin-left: 4px;\n}\n\n/* Column Actions */\n.column-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.column-item:hover .column-actions {\n opacity: 1;\n}\n\n.action-btn {\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.action-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.action-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n.action-btn.hide-btn:hover {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n.action-btn.show-btn {\n opacity: 0;\n}\n\n.column-item:hover .action-btn.show-btn {\n opacity: 1;\n}\n\n.action-btn.show-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.action-btn.format-btn:hover {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning);\n}\n\n.action-btn i {\n font-size: 12px;\n}\n\n/* Format Editor */\n.format-editor {\n padding: 0;\n}\n\n.format-editor-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.back-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.back-btn:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n\n.format-editor-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n/* Format Sections */\n.format-section {\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.format-section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.format-section-header i {\n color: var(--mj-text-muted);\n}\n\n/* Format Rows */\n.format-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 0;\n}\n\n.format-row label {\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.format-row.alias-info {\n padding: 4px 0;\n}\n\n.muted-text {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.clear-alias-btn {\n width: 24px;\n height: 24px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-disabled);\n transition: all 0.15s ease;\n}\n\n.clear-alias-btn:hover {\n color: var(--mj-status-error);\n}\n\n/* Format Input */\n.format-input {\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease;\n}\n\n.format-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.format-input.small {\n width: 60px;\n text-align: center;\n}\n\n.format-select {\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n}\n\n.format-select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n/* Color Input */\n.color-input {\n width: 36px;\n height: 28px;\n padding: 2px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n background: var(--mj-bg-surface);\n}\n\n/* Alignment Toggle */\n.alignment-toggle {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n padding: 3px;\n border: 1px solid var(--mj-border-default);\n}\n\n.align-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.align-btn:hover {\n color: var(--mj-text-secondary);\n}\n\n.align-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);\n}\n\n/* Style Buttons */\n.style-buttons {\n display: flex;\n gap: 4px;\n margin-bottom: 12px;\n}\n\n.style-btn {\n width: 32px;\n height: 32px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.style-btn:hover {\n border-color: var(--mj-border-strong);\n color: var(--mj-text-secondary);\n}\n\n.style-btn.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n/* Format Preview */\n.format-preview-section {\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.preview-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.preview-header i {\n color: var(--mj-text-muted);\n}\n\n.preview-table {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.preview-header-cell {\n padding: 8px 12px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.preview-cell {\n padding: 8px 12px;\n font-size: 13px;\n color: var(--mj-text-primary);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.preview-cell:last-child {\n border-bottom: none;\n}\n\n/* Format Actions */\n.format-actions {\n padding: 16px;\n}\n\n.clear-format-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.clear-format-btn:hover {\n background: var(--mj-status-error-bg);\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n/* Sorting Section */\n.sorting-section {\n padding: 0;\n}\n\n.sorting-header {\n margin-bottom: 12px;\n}\n\n.sorting-description,\n.aggregates-description {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-muted);\n line-height: 1.5;\n}\n\n/* Add Sort Button */\n.add-sort-btn,\n.add-aggregate-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n width: 100%;\n border: 2px dashed var(--mj-border-default);\n border-radius: 10px;\n background: transparent;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n margin-bottom: 12px;\n}\n\n.add-sort-btn:hover:not(:disabled),\n.add-aggregate-btn:hover:not(:disabled) {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.add-sort-btn:disabled,\n.add-aggregate-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Sort Items List */\n.sort-items-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-bottom: 12px;\n}\n\n.sort-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n transition: all 0.15s ease;\n}\n\n.sort-item:hover {\n border-color: var(--mj-border-strong);\n}\n\n.sort-item.dragging {\n opacity: 0.4;\n}\n\n.sort-item.drop-target {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n/* Sort Drop Indicator */\n.sort-drop-indicator {\n height: 2px;\n background: var(--mj-brand-primary);\n border-radius: 1px;\n margin: 0 10px;\n}\n\n/* Sort Priority Badge */\n.sort-priority-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-radius: 6px;\n font-size: 11px;\n font-weight: 700;\n flex-shrink: 0;\n}\n\n/* Sort Drag Handle */\n.sort-drag-handle {\n cursor: grab;\n color: var(--mj-text-disabled);\n padding: 2px;\n font-size: 12px;\n}\n\n.sort-drag-handle:active {\n cursor: grabbing;\n}\n\n.sort-item:hover .sort-drag-handle {\n color: var(--mj-text-muted);\n}\n\n/* Sort Field Dropdown */\n.sort-field-dropdown {\n flex: 1;\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n min-width: 0;\n}\n\n.sort-field-dropdown:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n/* Sort Direction Toggle */\n.sort-direction-toggle {\n display: flex;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n padding: 2px;\n gap: 2px;\n}\n\n.direction-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 5px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.direction-btn:hover:not(:disabled) {\n color: var(--mj-text-secondary);\n}\n\n.direction-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);\n}\n\n.direction-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n/* Sort Remove Button */\n.sort-remove-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-disabled);\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.sort-remove-btn:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n.sort-remove-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n/* Positioning-only helpers for migrated <mj-alert> callouts. Box chrome\n (bg/border/radius/padding/icon) is owned by the component; only the\n surrounding margins from the bespoke banners are preserved here. */\n.view-config-alert-pos { margin-top: 12px; }\n.view-config-alert-pos-mb { margin-bottom: 12px; }\n.view-config-validation-pos { margin: 0 16px 12px; }\n\n/* Filter Mode Selector */\n.filter-mode-selector {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.filter-mode-btn {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 14px;\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 text-align: left;\n}\n\n.filter-mode-btn:hover:not(:disabled) {\n border-color: var(--mj-border-strong);\n}\n\n.filter-mode-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.filter-mode-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.mode-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n font-size: 16px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n.filter-mode-btn.active .mode-icon {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mode-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mode-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mode-subtitle {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.mode-check {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n/* Smart Filter Section */\n.smart-filter-section {\n padding: 0;\n}\n\n.smart-filter-input-container {\n display: flex;\n gap: 12px;\n padding: 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\n.smart-filter-icon {\n display: flex;\n align-items: flex-start;\n padding-top: 4px;\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.smart-filter-textarea {\n flex: 1;\n border: none;\n outline: none;\n resize: vertical;\n font-size: 14px;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: transparent;\n min-height: 60px;\n}\n\n.smart-filter-textarea::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.smart-filter-textarea:disabled {\n color: var(--mj-text-muted);\n}\n\n/* Smart Filter Explanation */\n/* Smart Filter Examples */\n.smart-filter-examples {\n margin-bottom: 12px;\n}\n\n.examples-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 8px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n}\n\n.examples-header i {\n color: var(--mj-status-warning);\n}\n\n.example-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.example-chip {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 20px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 12px;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.example-chip:hover:not(:disabled) {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.example-chip:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.example-chip i {\n font-size: 11px;\n}\n\n/* Traditional Filter Section */\n.traditional-filter-section {\n padding: 0;\n}\n\n.filter-summary-container {\n margin-bottom: 12px;\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 14px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n}\n\n.summary-info {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 22px;\n height: 22px;\n padding: 0 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-radius: 11px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.summary-text {\n font-size: 13px;\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.summary-text.no-filters {\n color: var(--mj-text-muted);\n font-weight: 400;\n}\n\n.summary-actions {\n display: flex;\n gap: 8px;\n}\n\n.summary-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.summary-btn:hover:not(:disabled) {\n border-color: var(--mj-border-strong);\n}\n\n.summary-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.summary-btn.clear-btn:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n.summary-btn.primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.summary-btn.primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n}\n\n/* Aggregates Section */\n.aggregates-section {\n padding: 0;\n}\n\n.aggregates-header {\n margin-bottom: 12px;\n}\n\n/* Aggregates List */\n.aggregates-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-bottom: 12px;\n}\n\n.aggregate-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n transition: all 0.15s ease;\n}\n\n.aggregate-item:hover {\n border-color: var(--mj-border-strong);\n}\n\n.aggregate-item.disabled {\n opacity: 0.5;\n}\n\n/* Aggregate Icon */\n.agg-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-radius: 8px;\n color: var(--mj-brand-primary);\n font-size: 14px;\n flex-shrink: 0;\n}\n\n/* Aggregate Content */\n.agg-content {\n flex: 1;\n min-width: 0;\n}\n\n.agg-label {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.agg-details {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 2px;\n}\n\n.agg-type {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.agg-smart-badge {\n color: var(--mj-brand-primary);\n font-size: 11px;\n}\n\n/* Aggregate Actions */\n.agg-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.aggregate-item:hover .agg-actions {\n opacity: 1;\n}\n\n.agg-action-btn {\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.agg-action-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.agg-action-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n.agg-action-btn.toggle-btn.enabled {\n color: var(--mj-brand-primary);\n}\n\n.agg-action-btn.remove-btn:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n.agg-action-btn.edit-btn:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n/* Aggregates Summary */\n.aggregates-summary {\n display: flex;\n gap: 16px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n margin-bottom: 12px;\n}\n\n.aggregates-summary .summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.aggregates-summary .summary-item i {\n color: var(--mj-text-disabled);\n}\n\n/* Settings Tab */\n.form-group {\n margin-bottom: 16px;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.form-input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 14px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease;\n box-sizing: border-box;\n}\n\n.form-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.form-input.invalid {\n border-color: var(--mj-status-error);\n}\n\n.form-textarea {\n resize: vertical;\n min-height: 60px;\n font-family: inherit;\n}\n\n.validation-error {\n font-size: 12px;\n color: var(--mj-status-error);\n margin-top: 4px;\n}\n\n/* Checkbox */\n.checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: pointer;\n}\n\n.checkbox-label input[type=\"checkbox\"] {\n margin-top: 3px;\n flex-shrink: 0;\n}\n\n.checkbox-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.checkbox-text strong {\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.checkbox-text small {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n/* Danger Zone */\n.danger-zone .section-header {\n color: var(--mj-status-error);\n}\n\n.danger-zone .section-header i {\n color: var(--mj-status-error);\n}\n\n.delete-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n border: 1px solid var(--mj-status-error);\n border-radius: 8px;\n background: var(--mj-status-error-bg);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-status-error);\n transition: all 0.15s ease;\n}\n\n.delete-btn:hover {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n/* Panel Footer */\n.panel-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n flex-shrink: 0;\n}\n\n.footer-left {\n display: flex;\n gap: 8px;\n}\n\n.footer-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 9px 18px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.footer-btn:hover:not(:disabled) {\n border-color: var(--mj-border-strong);\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-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n}\n\n.footer-btn.cancel-btn {\n background: var(--mj-bg-surface-card);\n}\n\n.footer-btn.cancel-btn:hover {\n background: var(--mj-bg-surface-active);\n}\n\n/* Responsive */\n@media (max-width: 480px) {\n .config-panel {\n width: 100vw;\n min-width: auto;\n }\n\n .resize-handle {\n display: none;\n }\n}\n"] }]
2850
2884
  }], () => [{ type: i0.ChangeDetectorRef }], { Entity: [{
2851
2885
  type: Input
2852
2886
  }], ViewEntity: [{
@@ -2877,6 +2911,12 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2877
2911
  type: Input
2878
2912
  }], PendingNewViewIsShared: [{
2879
2913
  type: Input
2914
+ }], PendingNewViewFilterState: [{
2915
+ type: Input
2916
+ }], PendingNewViewSmartFilterEnabled: [{
2917
+ type: Input
2918
+ }], PendingNewViewSmartFilterPrompt: [{
2919
+ type: Input
2880
2920
  }], Duplicate: [{
2881
2921
  type: Output
2882
2922
  }], IsSaving: [{