@memberjunction/ng-entity-viewer 6.1.2 → 6.2.0-edge.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) 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/entity-cards/entity-cards.component.d.ts +1 -0
  6. package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -1
  7. package/dist/lib/entity-cards/entity-cards.component.js +11 -2
  8. package/dist/lib/entity-cards/entity-cards.component.js.map +1 -1
  9. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +18 -0
  10. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
  11. package/dist/lib/entity-data-grid/entity-data-grid.component.js +71 -25
  12. package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
  13. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts.map +1 -1
  14. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js +10 -8
  15. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js.map +1 -1
  16. package/dist/lib/entity-viewer/entity-viewer.component.d.ts +11 -0
  17. package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
  18. package/dist/lib/entity-viewer/entity-viewer.component.js +29 -0
  19. package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
  20. package/dist/lib/utils/aggregate-field.util.d.ts +21 -0
  21. package/dist/lib/utils/aggregate-field.util.d.ts.map +1 -0
  22. package/dist/lib/utils/aggregate-field.util.js +40 -0
  23. package/dist/lib/utils/aggregate-field.util.js.map +1 -0
  24. package/dist/lib/view-config-panel/view-config-panel.component.d.ts +5 -1
  25. package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
  26. package/dist/lib/view-config-panel/view-config-panel.component.js +15 -8
  27. package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
  28. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +93 -3
  29. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -1
  30. package/dist/lib/view-types/renderers/grid-view-renderer.component.js +432 -10
  31. package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -1
  32. package/dist/module.d.ts +5 -4
  33. package/dist/module.d.ts.map +1 -1
  34. package/dist/module.js +8 -1
  35. package/dist/module.js.map +1 -1
  36. package/dist/public-api.d.ts +1 -0
  37. package/dist/public-api.d.ts.map +1 -1
  38. package/dist/public-api.js +1 -0
  39. package/dist/public-api.js.map +1 -1
  40. package/package.json +20 -19
@@ -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();
@@ -2165,7 +2166,11 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2165
2166
  /**
2166
2167
  * Format a value for preview display
2167
2168
  */
2168
- FormatPreviewValue(value, format) {
2169
+ /**
2170
+ * @param field The column's field, so a SQL `date` previews as its stored calendar day rather
2171
+ * than shifting into the reader's zone (MJ#4210).
2172
+ */
2173
+ FormatPreviewValue(value, format, field) {
2169
2174
  if (value == null)
2170
2175
  return '—';
2171
2176
  if (!format || format.type === 'auto')
@@ -2179,7 +2184,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2179
2184
  return this.formatPercent(value, format);
2180
2185
  case 'date':
2181
2186
  case 'datetime':
2182
- return this.formatDate(value, format);
2187
+ return this.formatDate(value, format, IsDateOnlySQLType(field?.Type));
2183
2188
  case 'boolean':
2184
2189
  return this.formatBoolean(value, format);
2185
2190
  default:
@@ -2212,7 +2217,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2212
2217
  // Assume value is already a percentage (e.g., 50 = 50%), divide by 100
2213
2218
  return new Intl.NumberFormat('en-US', options).format(value / 100);
2214
2219
  }
2215
- formatDate(value, format) {
2220
+ formatDate(value, format, dateOnly) {
2216
2221
  const date = value instanceof Date ? value : new Date(value);
2217
2222
  if (isNaN(date.getTime()))
2218
2223
  return String(value);
@@ -2220,6 +2225,8 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2220
2225
  const formatStr = format.dateFormat || 'medium';
2221
2226
  const includeWeekday = formatStr.includes('-weekday');
2222
2227
  const baseFormat = formatStr.replace('-weekday', '');
2228
+ // A `date` column has no time to show and must not shift into the reader's zone.
2229
+ const withTime = format.type === 'datetime' && !dateOnly;
2223
2230
  let options;
2224
2231
  // Intl.DateTimeFormat doesn't allow combining dateStyle with weekday
2225
2232
  // So we must use individual components when weekday is requested
@@ -2234,7 +2241,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2234
2241
  // medium
2235
2242
  options = { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' };
2236
2243
  }
2237
- if (format.type === 'datetime') {
2244
+ if (withTime) {
2238
2245
  options.hour = 'numeric';
2239
2246
  options.minute = '2-digit';
2240
2247
  }
@@ -2244,11 +2251,11 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2244
2251
  options = {
2245
2252
  dateStyle: baseFormat === 'short' ? 'short' : baseFormat === 'long' ? 'long' : 'medium'
2246
2253
  };
2247
- if (format.type === 'datetime') {
2254
+ if (withTime) {
2248
2255
  options.timeStyle = 'short';
2249
2256
  }
2250
2257
  }
2251
- return new Intl.DateTimeFormat('en-US', options).format(date);
2258
+ return dateOnly ? FormatDateOnly(date, options, 'en-US') : new Intl.DateTimeFormat('en-US', options).format(date);
2252
2259
  }
2253
2260
  formatBoolean(value, format) {
2254
2261
  return value ? (format.trueLabel || 'Yes') : (format.falseLabel || 'No');
@@ -2846,7 +2853,7 @@ export class ViewConfigPanelComponent extends BaseAngularComponent {
2846
2853
  }
2847
2854
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewConfigPanelComponent, [{
2848
2855
  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"] }]
2856
+ 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
2857
  }], () => [{ type: i0.ChangeDetectorRef }], { Entity: [{
2851
2858
  type: Input
2852
2859
  }], ViewEntity: [{