@memberjunction/ng-entity-viewer 5.10.0 → 5.11.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.
- package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts +5 -4
- package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts.map +1 -1
- package/dist/lib/quick-save-dialog/quick-save-dialog.component.js +8 -3
- package/dist/lib/quick-save-dialog/quick-save-dialog.component.js.map +1 -1
- package/dist/lib/types.d.ts +12 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/view-config-panel/view-config-panel.component.d.ts +15 -2
- package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
- package/dist/lib/view-config-panel/view-config-panel.component.js +64 -21
- package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
- package/package.json +9 -9
|
@@ -1100,13 +1100,34 @@ function ViewConfigPanelComponent_Conditional_38_Conditional_1_Template(rf, ctx)
|
|
|
1100
1100
|
i0.ɵɵelement(0, "i", 228);
|
|
1101
1101
|
} }
|
|
1102
1102
|
function ViewConfigPanelComponent_Conditional_38_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
1103
|
-
i0.ɵɵelement(0, "i",
|
|
1103
|
+
i0.ɵɵelement(0, "i", 59);
|
|
1104
1104
|
} }
|
|
1105
1105
|
function ViewConfigPanelComponent_Conditional_38_Template(rf, ctx) { if (rf & 1) {
|
|
1106
1106
|
const _r33 = i0.ɵɵgetCurrentView();
|
|
1107
1107
|
i0.ɵɵelementStart(0, "button", 227);
|
|
1108
|
-
i0.ɵɵlistener("click", function ViewConfigPanelComponent_Conditional_38_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r33); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.
|
|
1109
|
-
i0.ɵɵconditionalCreate(1, ViewConfigPanelComponent_Conditional_38_Conditional_1_Template, 1, 0, "i", 228)(2, ViewConfigPanelComponent_Conditional_38_Conditional_2_Template, 1, 0, "i",
|
|
1108
|
+
i0.ɵɵlistener("click", function ViewConfigPanelComponent_Conditional_38_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r33); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSaveAsNew()); });
|
|
1109
|
+
i0.ɵɵconditionalCreate(1, ViewConfigPanelComponent_Conditional_38_Conditional_1_Template, 1, 0, "i", 228)(2, ViewConfigPanelComponent_Conditional_38_Conditional_2_Template, 1, 0, "i", 59);
|
|
1110
|
+
i0.ɵɵtext(3);
|
|
1111
|
+
i0.ɵɵelementEnd();
|
|
1112
|
+
} if (rf & 2) {
|
|
1113
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
1114
|
+
i0.ɵɵproperty("disabled", ctx_r1.isSaving || !ctx_r1.IsValidForSaveAsNew);
|
|
1115
|
+
i0.ɵɵadvance();
|
|
1116
|
+
i0.ɵɵconditional(ctx_r1.isSaving ? 1 : 2);
|
|
1117
|
+
i0.ɵɵadvance(2);
|
|
1118
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.isSaving ? "Saving..." : "Create View", " ");
|
|
1119
|
+
} }
|
|
1120
|
+
function ViewConfigPanelComponent_Conditional_39_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
1121
|
+
i0.ɵɵelement(0, "i", 228);
|
|
1122
|
+
} }
|
|
1123
|
+
function ViewConfigPanelComponent_Conditional_39_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
1124
|
+
i0.ɵɵelement(0, "i", 229);
|
|
1125
|
+
} }
|
|
1126
|
+
function ViewConfigPanelComponent_Conditional_39_Template(rf, ctx) { if (rf & 1) {
|
|
1127
|
+
const _r34 = i0.ɵɵgetCurrentView();
|
|
1128
|
+
i0.ɵɵelementStart(0, "button", 227);
|
|
1129
|
+
i0.ɵɵlistener("click", function ViewConfigPanelComponent_Conditional_39_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r34); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSaveDefaults()); });
|
|
1130
|
+
i0.ɵɵconditionalCreate(1, ViewConfigPanelComponent_Conditional_39_Conditional_1_Template, 1, 0, "i", 228)(2, ViewConfigPanelComponent_Conditional_39_Conditional_2_Template, 1, 0, "i", 229);
|
|
1110
1131
|
i0.ɵɵtext(3);
|
|
1111
1132
|
i0.ɵɵelementEnd();
|
|
1112
1133
|
} if (rf & 2) {
|
|
@@ -1177,9 +1198,22 @@ export class ViewConfigPanelComponent {
|
|
|
1177
1198
|
*/
|
|
1178
1199
|
externalFilterState = null;
|
|
1179
1200
|
/**
|
|
1180
|
-
* When true,
|
|
1201
|
+
* When true, the panel is in "create new view" mode — shows the Settings tab
|
|
1202
|
+
* and a "Create View" button even without a viewEntity.
|
|
1181
1203
|
*/
|
|
1182
1204
|
DefaultSaveAsNew = false;
|
|
1205
|
+
/**
|
|
1206
|
+
* Pre-populated view name from the quick save dialog (used when DefaultSaveAsNew is true)
|
|
1207
|
+
*/
|
|
1208
|
+
PendingNewViewName = '';
|
|
1209
|
+
/**
|
|
1210
|
+
* Pre-populated description from the quick save dialog (used when DefaultSaveAsNew is true)
|
|
1211
|
+
*/
|
|
1212
|
+
PendingNewViewDescription = '';
|
|
1213
|
+
/**
|
|
1214
|
+
* Pre-populated sharing preference from the quick save dialog (used when DefaultSaveAsNew is true)
|
|
1215
|
+
*/
|
|
1216
|
+
PendingNewViewIsShared = false;
|
|
1183
1217
|
/**
|
|
1184
1218
|
* Emitted when user wants to duplicate the current view (F-005)
|
|
1185
1219
|
*/
|
|
@@ -1482,10 +1516,10 @@ export class ViewConfigPanelComponent {
|
|
|
1482
1516
|
}
|
|
1483
1517
|
}
|
|
1484
1518
|
else {
|
|
1485
|
-
// Default view
|
|
1486
|
-
this.viewName = '';
|
|
1487
|
-
this.viewDescription = '';
|
|
1488
|
-
this.isShared = false;
|
|
1519
|
+
// Default view or pending new view — use pending values if creating a new view
|
|
1520
|
+
this.viewName = this.DefaultSaveAsNew ? this.PendingNewViewName : '';
|
|
1521
|
+
this.viewDescription = this.DefaultSaveAsNew ? this.PendingNewViewDescription : '';
|
|
1522
|
+
this.isShared = this.DefaultSaveAsNew ? this.PendingNewViewIsShared : false;
|
|
1489
1523
|
if (!this.currentGridState?.sortSettings?.length) {
|
|
1490
1524
|
this.sortField = null;
|
|
1491
1525
|
this.sortDirection = 'asc';
|
|
@@ -2689,7 +2723,7 @@ export class ViewConfigPanelComponent {
|
|
|
2689
2723
|
static ɵfac = function ViewConfigPanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ViewConfigPanelComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
2690
2724
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ViewConfigPanelComponent, selectors: [["mj-view-config-panel"]], hostBindings: function ViewConfigPanelComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
2691
2725
|
i0.ɵɵlistener("keydown.escape", function ViewConfigPanelComponent_keydown_escape_HostBindingHandler() { return ctx.handleEscape(); }, i0.ɵɵresolveDocument);
|
|
2692
|
-
} }, inputs: { entity: "entity", viewEntity: "viewEntity", isOpen: "isOpen", currentGridState: "currentGridState", sampleData: "sampleData", externalFilterState: "externalFilterState", DefaultSaveAsNew: "DefaultSaveAsNew", isSaving: "isSaving" }, outputs: { close: "close", save: "save", saveDefaults: "saveDefaults", delete: "delete", openFilterDialogRequest: "openFilterDialogRequest", duplicate: "duplicate" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls:
|
|
2726
|
+
} }, inputs: { entity: "entity", viewEntity: "viewEntity", isOpen: "isOpen", currentGridState: "currentGridState", sampleData: "sampleData", externalFilterState: "externalFilterState", DefaultSaveAsNew: "DefaultSaveAsNew", PendingNewViewName: "PendingNewViewName", PendingNewViewDescription: "PendingNewViewDescription", PendingNewViewIsShared: "PendingNewViewIsShared", isSaving: "isSaving" }, outputs: { close: "close", save: "save", saveDefaults: "saveDefaults", delete: "delete", openFilterDialogRequest: "openFilterDialogRequest", duplicate: "duplicate" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 45, vars: 46, consts: [[1, "panel-backdrop"], [1, "config-panel"], ["title", "Drag to resize", 1, "resize-handle", 3, "mousedown"], [1, "resize-grip"], [1, "panel-header"], [1, "header-title"], [1, "fa-solid", "fa-sliders-h"], ["title", "Close", 1, "close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "tab-nav"], [1, "tab-btn", 3, "click", "title"], [1, "fa-solid", "fa-columns"], [1, "fa-solid", "fa-arrow-up-wide-short"], [1, "tab-badge"], [1, "fa-solid", "fa-filter"], [1, "fa-solid", "fa-chart-simple"], [1, "tab-btn", 3, "active", "title"], [1, "panel-content"], [1, "tab-content"], [1, "format-editor"], [1, "validation-banner"], [1, "panel-footer"], [1, "footer-left"], [1, "footer-btn", "save-btn", "primary", 3, "disabled"], [1, "footer-btn", "cancel-btn", 3, "click"], [3, "Close", "Save", "Entity", "Aggregate", "IsOpen"], ["Title", "Delete View", "DetailMessage", "This action cannot be undone. All users who have access to this view will lose it.", "ConfirmText", "Delete", "ConfirmStyle", "danger", "Icon", "fa-solid fa-trash", 3, "Confirmed", "Cancelled", "IsOpen", "Message"], ["Title", "Switch Filter Mode", "DetailMessage", "You can switch back later, but the current filter data will be lost.", "ConfirmText", "Switch", "ConfirmStyle", "primary", "Icon", "fa-solid fa-exchange-alt", 3, "Confirmed", "Cancelled", "IsOpen", "Message"], [1, "panel-backdrop", 3, "click"], [1, "fa-solid", "fa-cog"], [1, "config-section"], [1, "section-header"], [1, "fa-solid", "fa-eye"], [1, "column-count"], [1, "column-list", "visible-columns"], [1, "empty-list"], [1, "fa-solid", "fa-eye-slash"], [1, "column-search"], [1, "column-list", "hidden-columns"], [1, "column-item", "hidden"], [1, "drop-indicator"], ["draggable", "true", 1, "column-item", 3, "dragstart", "dragover", "dragleave", "drop", "dragend"], [1, "drag-handle"], [1, "fa-solid", "fa-grip-vertical"], [1, "column-name"], [1, "fa-solid", "fa-pen-nib", "alias-indicator", 3, "title"], ["title", "Custom formatting applied", 1, "fa-solid", "fa-paintbrush", "format-indicator"], [1, "column-actions"], ["title", "Format column", 1, "action-btn", "format-btn", 3, "click"], [1, "fa-solid", "fa-paintbrush"], ["title", "Move up", 1, "action-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-chevron-up"], ["title", "Move down", 1, "action-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-chevron-down"], ["title", "Hide column", 1, "action-btn", "hide-btn", 3, "click"], [1, "fa-solid", "fa-info-circle"], [1, "fa-solid", "fa-search"], ["type", "text", "placeholder", "Search columns...", 3, "ngModelChange", "ngModel"], ["title", "Show column", 1, "action-btn", "show-btn", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "fa-solid", "fa-check-circle"], [1, "format-editor-header"], [1, "back-btn", 3, "click"], [1, "fa-solid", "fa-arrow-left"], [1, "format-editor-title"], [1, "format-section"], [1, "format-section-header"], [1, "fa-solid", "fa-pen-nib"], [1, "format-row"], ["type", "text", 1, "format-input", 3, "input", "value", "placeholder"], [1, "format-row", "alias-info"], [1, "format-preview-section"], [1, "preview-header"], [1, "preview-table"], [1, "preview-header-cell"], [1, "preview-cell", 3, "text-align", "font-weight", "font-style", "text-decoration", "color", "background-color"], [1, "fa-solid", "fa-hashtag"], [1, "format-select", 3, "ngModelChange", "ngModel"], ["value", "auto"], ["value", "text"], ["value", "number"], ["value", "currency"], ["value", "percent"], ["value", "date"], ["value", "datetime"], ["value", "boolean"], [1, "fa-solid", "fa-align-left"], [1, "alignment-toggle"], ["title", "Left", 1, "align-btn", 3, "click"], ["title", "Center", 1, "align-btn", 3, "click"], [1, "fa-solid", "fa-align-center"], ["title", "Right", 1, "align-btn", 3, "click"], [1, "fa-solid", "fa-align-right"], [1, "fa-solid", "fa-heading"], [1, "style-buttons"], ["title", "Bold", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-bold"], ["title", "Italic", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-italic"], ["title", "Underline", 1, "style-btn", 3, "click"], [1, "fa-solid", "fa-underline"], ["type", "color", 1, "color-input", 3, "input", "value"], [1, "fa-solid", "fa-table-cells"], [1, "format-actions"], [1, "clear-format-btn", 3, "click"], [1, "fa-solid", "fa-eraser"], [1, "muted-text"], ["title", "Clear alias", 1, "clear-alias-btn", 3, "click"], [1, "preview-cell"], [1, "fa-solid", "fa-sliders"], ["type", "number", "min", "0", "max", "10", 1, "format-input", "small", 3, "ngModelChange", "ngModel"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], ["value", "USD"], ["value", "EUR"], ["value", "GBP"], ["value", "JPY"], ["value", "CAD"], ["value", "AUD"], [1, "fa-solid", "fa-calendar"], ["value", "short"], ["value", "short-weekday"], ["value", "medium"], ["value", "medium-weekday"], ["value", "long"], ["value", "long-weekday"], [1, "fa-solid", "fa-toggle-on"], ["type", "text", "placeholder", "Yes", 1, "format-input", 3, "ngModelChange", "ngModel"], ["type", "text", "placeholder", "No", 1, "format-input", 3, "ngModelChange", "ngModel"], ["value", "checkbox"], ["value", "icon"], [1, "config-section", "sorting-section"], [1, "sorting-header"], [1, "sorting-description"], ["title", "Add another sort level", 1, "add-sort-btn", 3, "click", "disabled"], [1, "sort-empty-state"], [1, "sort-items-list"], [1, "sort-hint"], [1, "sort-drop-indicator"], ["draggable", "true", 1, "sort-item", 3, "dragstart", "dragover", "dragleave", "drop", "dragend"], [1, "sort-priority-badge"], ["title", "Drag to reorder", 1, "sort-drag-handle"], [1, "sort-field-dropdown", 3, "ngModelChange", "ngModel", "disabled"], [3, "value"], [1, "sort-direction-toggle"], ["title", "Ascending (A-Z, 1-9)", 1, "direction-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-up"], ["title", "Descending (Z-A, 9-1)", 1, "direction-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-down"], ["title", "Remove sort level", 1, "sort-remove-btn", 3, "click", "disabled"], [1, "sort-empty-hint"], [1, "filter-mode-selector"], [1, "filter-mode-btn", 3, "click", "disabled"], [1, "mode-icon"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "mode-content"], [1, "mode-title"], [1, "mode-subtitle"], [1, "fa-solid", "fa-check", "mode-check"], [1, "config-section", "smart-filter-section"], [1, "config-section", "traditional-filter-section"], [1, "smart-filter-input-container"], [1, "smart-filter-icon"], ["id", "smartFilterPrompt", "placeholder", "Describe what you're looking for...", "rows", "3", 1, "smart-filter-textarea", 3, "ngModelChange", "ngModel", "disabled"], [1, "smart-filter-explanation"], [1, "smart-filter-examples"], [1, "examples-header"], [1, "fa-solid", "fa-lightbulb"], [1, "example-chips"], [1, "example-chip", 3, "click", "disabled"], [1, "fa-regular", "fa-calendar"], [1, "fa-solid", "fa-circle-check"], [1, "fa-solid", "fa-star"], [1, "fa-solid", "fa-clock-rotate-left"], [1, "smart-filter-tip"], [1, "fa-solid", "fa-robot"], [1, "filter-summary-container"], [1, "filter-summary"], [1, "summary-info"], [1, "summary-text", "no-filters"], [1, "summary-actions"], ["title", "Clear all filters", 1, "summary-btn", "clear-btn"], ["title", "Edit filters", 1, "summary-btn", "edit-btn", "primary", 3, "click", "disabled"], [1, "fa-solid", "fa-pen"], [1, "traditional-filter-tip"], [1, "filter-badge"], [1, "summary-text"], ["title", "Clear all filters", 1, "summary-btn", "clear-btn", 3, "click"], [1, "config-section", "aggregates-section"], [1, "aggregates-header"], [1, "aggregates-description"], ["title", "Add a new aggregate", 1, "add-aggregate-btn", 3, "click", "disabled"], [1, "aggregates-empty-state"], [1, "aggregates-tip"], [1, "aggregates-list"], [1, "aggregate-item", 3, "disabled"], [1, "aggregates-summary"], [1, "summary-item"], [1, "fa-solid", "fa-id-card"], [1, "fa-solid", "fa-table-columns"], [1, "aggregate-item"], [1, "agg-icon"], [1, "agg-content"], [1, "agg-label"], [1, "agg-details"], [1, "agg-type"], ["title", "AI-generated", 1, "agg-smart-badge"], [1, "agg-actions"], ["title", "Move up", 1, "agg-action-btn", 3, "click", "disabled"], ["title", "Move down", 1, "agg-action-btn", 3, "click", "disabled"], [1, "agg-action-btn", "toggle-btn", 3, "click", "disabled", "title"], ["title", "Edit", 1, "agg-action-btn", "edit-btn", 3, "click", "disabled"], ["title", "Remove", 1, "agg-action-btn", "remove-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-trash"], [1, "empty-hint"], [1, "form-group"], ["for", "viewName"], ["id", "viewName", "type", "text", "placeholder", "Enter view name...", 1, "form-input", 3, "ngModelChange", "ngModel", "disabled"], [1, "validation-error"], ["for", "viewDescription"], ["id", "viewDescription", "placeholder", "Describe this view...", "rows", "3", 1, "form-input", "form-textarea", 3, "ngModelChange", "ngModel", "disabled"], [1, "fa-solid", "fa-share-alt"], [1, "checkbox-label"], ["type", "checkbox", 3, "ngModelChange", "ngModel", "disabled"], [1, "checkbox-text"], [1, "config-section", "danger-zone"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "delete-btn", 3, "click"], [1, "footer-btn", "save-btn", "primary", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-save"]], template: function ViewConfigPanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
2693
2727
|
i0.ɵɵconditionalCreate(0, ViewConfigPanelComponent_Conditional_0_Template, 1, 0, "div", 0);
|
|
2694
2728
|
i0.ɵɵelementStart(1, "div", 1)(2, "div", 2);
|
|
2695
2729
|
i0.ɵɵlistener("mousedown", function ViewConfigPanelComponent_Template_div_mousedown_2_listener($event) { return ctx.onResizeStart($event); });
|
|
@@ -2740,19 +2774,20 @@ export class ViewConfigPanelComponent {
|
|
|
2740
2774
|
i0.ɵɵelementStart(35, "div", 21)(36, "div", 22);
|
|
2741
2775
|
i0.ɵɵconditionalCreate(37, ViewConfigPanelComponent_Conditional_37_Template, 4, 3, "button", 23);
|
|
2742
2776
|
i0.ɵɵconditionalCreate(38, ViewConfigPanelComponent_Conditional_38_Template, 4, 3, "button", 23);
|
|
2777
|
+
i0.ɵɵconditionalCreate(39, ViewConfigPanelComponent_Conditional_39_Template, 4, 3, "button", 23);
|
|
2743
2778
|
i0.ɵɵelementEnd();
|
|
2744
|
-
i0.ɵɵelementStart(
|
|
2745
|
-
i0.ɵɵlistener("click", function
|
|
2746
|
-
i0.ɵɵtext(
|
|
2779
|
+
i0.ɵɵelementStart(40, "button", 24);
|
|
2780
|
+
i0.ɵɵlistener("click", function ViewConfigPanelComponent_Template_button_click_40_listener() { return ctx.onClose(); });
|
|
2781
|
+
i0.ɵɵtext(41, " Cancel ");
|
|
2747
2782
|
i0.ɵɵelementEnd()()();
|
|
2748
|
-
i0.ɵɵelementStart(
|
|
2749
|
-
i0.ɵɵlistener("Close", function
|
|
2783
|
+
i0.ɵɵelementStart(42, "mj-aggregate-setup-dialog", 25);
|
|
2784
|
+
i0.ɵɵlistener("Close", function ViewConfigPanelComponent_Template_mj_aggregate_setup_dialog_Close_42_listener() { return ctx.closeAggregateDialog(); })("Save", function ViewConfigPanelComponent_Template_mj_aggregate_setup_dialog_Save_42_listener($event) { return ctx.onAggregateSave($event); });
|
|
2750
2785
|
i0.ɵɵelementEnd();
|
|
2751
|
-
i0.ɵɵelementStart(
|
|
2752
|
-
i0.ɵɵlistener("Confirmed", function
|
|
2786
|
+
i0.ɵɵelementStart(43, "mj-ev-confirm-dialog", 26);
|
|
2787
|
+
i0.ɵɵlistener("Confirmed", function ViewConfigPanelComponent_Template_mj_ev_confirm_dialog_Confirmed_43_listener() { return ctx.OnDeleteConfirmed(); })("Cancelled", function ViewConfigPanelComponent_Template_mj_ev_confirm_dialog_Cancelled_43_listener() { return ctx.OnDeleteCancelled(); });
|
|
2753
2788
|
i0.ɵɵelementEnd();
|
|
2754
|
-
i0.ɵɵelementStart(
|
|
2755
|
-
i0.ɵɵlistener("Confirmed", function
|
|
2789
|
+
i0.ɵɵelementStart(44, "mj-ev-confirm-dialog", 27);
|
|
2790
|
+
i0.ɵɵlistener("Confirmed", function ViewConfigPanelComponent_Template_mj_ev_confirm_dialog_Confirmed_44_listener() { return ctx.OnFilterModeSwitchConfirmed(); })("Cancelled", function ViewConfigPanelComponent_Template_mj_ev_confirm_dialog_Cancelled_44_listener() { return ctx.OnFilterModeSwitchCancelled(); });
|
|
2756
2791
|
i0.ɵɵelementEnd();
|
|
2757
2792
|
} if (rf & 2) {
|
|
2758
2793
|
i0.ɵɵconditional(ctx.isOpen ? 0 : -1);
|
|
@@ -2788,7 +2823,7 @@ export class ViewConfigPanelComponent {
|
|
|
2788
2823
|
i0.ɵɵadvance();
|
|
2789
2824
|
i0.ɵɵconditional(ctx.enabledAggregatesCount > 0 ? 25 : -1);
|
|
2790
2825
|
i0.ɵɵadvance();
|
|
2791
|
-
i0.ɵɵconditional(ctx.viewEntity ? 26 : -1);
|
|
2826
|
+
i0.ɵɵconditional(ctx.viewEntity || ctx.DefaultSaveAsNew ? 26 : -1);
|
|
2792
2827
|
i0.ɵɵadvance(2);
|
|
2793
2828
|
i0.ɵɵconditional(ctx.activeTab === "columns" && !ctx.formatEditingColumn ? 28 : -1);
|
|
2794
2829
|
i0.ɵɵadvance();
|
|
@@ -2806,7 +2841,9 @@ export class ViewConfigPanelComponent {
|
|
|
2806
2841
|
i0.ɵɵadvance(3);
|
|
2807
2842
|
i0.ɵɵconditional(ctx.viewEntity && ctx.canEdit ? 37 : -1);
|
|
2808
2843
|
i0.ɵɵadvance();
|
|
2809
|
-
i0.ɵɵconditional(!ctx.viewEntity ? 38 : -1);
|
|
2844
|
+
i0.ɵɵconditional(!ctx.viewEntity && ctx.DefaultSaveAsNew ? 38 : -1);
|
|
2845
|
+
i0.ɵɵadvance();
|
|
2846
|
+
i0.ɵɵconditional(!ctx.viewEntity && !ctx.DefaultSaveAsNew ? 39 : -1);
|
|
2810
2847
|
i0.ɵɵadvance(3);
|
|
2811
2848
|
i0.ɵɵproperty("Entity", ctx.entity)("Aggregate", ctx.editingAggregate)("IsOpen", ctx.showAggregateDialog);
|
|
2812
2849
|
i0.ɵɵadvance();
|
|
@@ -2817,7 +2854,7 @@ export class ViewConfigPanelComponent {
|
|
|
2817
2854
|
}
|
|
2818
2855
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewConfigPanelComponent, [{
|
|
2819
2856
|
type: Component,
|
|
2820
|
-
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\">\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) {\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 <div class=\"empty-list\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>No columns visible. Add columns from below.</span>\n </div>\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 <div class=\"empty-list\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <span>All columns are visible</span>\n </div>\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\">\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 <div class=\"sort-hint\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Drag items to change priority. Records sort by first level, then second, etc.</span>\n </div>\n }\n } @else {\n <div class=\"sort-empty-state\">\n <i class=\"fa-solid fa-arrow-up-wide-short\"></i>\n <span>No sorting configured</span>\n <p class=\"sort-empty-hint\">Click \"Add Sort Level\" to define how records should be ordered</p>\n </div>\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 <div class=\"smart-filter-explanation\">\n <i class=\"fa-solid fa-robot\"></i>\n <span>{{ smartFilterExplanation }}</span>\n </div>\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 <div class=\"smart-filter-tip\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>The AI will interpret your description and create the appropriate filter when you save the view.</span>\n </div>\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 <div class=\"traditional-filter-tip\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Build precise filters by selecting fields, operators, and values. Use groups for complex AND/OR logic.</span>\n </div>\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 <div class=\"aggregates-empty-state\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n <span>No aggregates configured</span>\n <p class=\"empty-hint\">Click \"Add Aggregate\" to create summary calculations</p>\n </div>\n }\n\n <!-- Info Tip -->\n <div class=\"aggregates-tip\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Aggregates are calculated server-side for accuracy with filtered/paginated data.</span>\n </div>\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 <div class=\"validation-banner\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>At least one column must be visible</span>\n </div>\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) {\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: 1000;\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: 0;\n right: 0;\n width: 450px;\n min-width: 360px;\n max-width: min(800px, 100vw);\n height: 100%;\n background: white;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);\n z-index: 1001;\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: linear-gradient(90deg, rgba(25, 118, 210, 0.1) 0%, transparent 100%);\n}\n\n.resize-grip {\n width: 3px;\n height: 40px;\n background: #cbd5e1;\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: #1976d2;\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 #e0e0e0;\n background: #fafafa;\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: #333;\n}\n\n.header-title i {\n color: #1976d2;\n}\n\n.close-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: #666;\n transition: all 0.15s ease;\n}\n\n.close-btn:hover {\n background: #e0e0e0;\n color: #333;\n}\n\n/* Tab Navigation */\n.tab-nav {\n display: flex;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\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 color: #666;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 2px solid transparent;\n}\n\n.tab-btn:hover {\n background: #f5f5f5;\n color: #333;\n}\n\n.tab-btn.active {\n color: #1976d2;\n border-bottom-color: #1976d2;\n background: transparent;\n}\n\n.tab-btn i {\n font-size: 12px;\n}\n\n.tab-badge {\n background: #1976d2;\n color: white;\n font-size: 10px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 8px;\n margin-left: 4px;\n}\n\n/* Icon-only mode for narrow panels */\n.tab-nav.icon-only .tab-btn {\n padding: 12px 8px;\n gap: 4px;\n}\n\n.tab-nav.icon-only .tab-btn i {\n font-size: 14px;\n}\n\n.tab-nav.icon-only .tab-badge {\n margin-left: 0;\n padding: 2px 4px;\n font-size: 9px;\n}\n\n/* Panel Content */\n.panel-content {\n flex: 1;\n overflow: hidden;\n padding: 16px 0;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.tab-content {\n padding: 0 20px;\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n}\n\n/* Config Sections */\n.config-section {\n margin-bottom: 24px;\n}\n\n.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: #333;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.section-header i {\n color: #1976d2;\n font-size: 12px;\n}\n\n.column-count {\n margin-left: auto;\n background: #e3f2fd;\n color: #1976d2;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n}\n\n/* Sort Configuration */\n.sort-config {\n display: flex;\n gap: 8px;\n}\n\n.sort-field-select {\n flex: 1;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n background: white;\n cursor: pointer;\n}\n\n.sort-field-select:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.sort-direction-toggle {\n display: flex;\n background: #f0f0f0;\n border-radius: 6px;\n padding: 2px;\n}\n\n.direction-btn {\n width: 36px;\n height: 36px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n color: #666;\n transition: all 0.15s ease;\n}\n\n.direction-btn:hover {\n color: #333;\n}\n\n.direction-btn.active {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n/* ========================================\n MULTI-SORT CONFIGURATION\n ======================================== */\n\n.add-sort-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n border: 1px dashed #ccc;\n border-radius: 8px;\n background: transparent;\n color: #666;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n width: 100%;\n justify-content: center;\n margin-bottom: 12px;\n}\n\n.add-sort-btn:hover:not(:disabled) {\n border-color: #1976d2;\n background: #e3f2fd;\n color: #1976d2;\n}\n\n.add-sort-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.add-sort-btn i {\n font-size: 12px;\n}\n\n/* Sort Items List */\n.sort-items-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n/* Sort Item */\n.sort-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n transition: all 0.15s ease;\n}\n\n.sort-item:hover {\n border-color: #bdbdbd;\n background: #fafafa;\n}\n\n.sort-item.dragging {\n opacity: 0.5;\n background: #e3f2fd;\n border-color: #1976d2;\n}\n\n.sort-item.drop-target {\n border-color: #1976d2;\n}\n\n/* Priority Badge */\n.sort-priority-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 6px;\n font-size: 12px;\n font-weight: 700;\n color: white;\n background: linear-gradient(135deg, #1976d2 0%, #42a5f5 100%);\n border-radius: 12px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n flex-shrink: 0;\n}\n\n/* Drag Handle */\n.sort-drag-handle {\n color: #bdbdbd;\n cursor: grab;\n padding: 4px;\n transition: color 0.15s ease;\n flex-shrink: 0;\n}\n\n.sort-drag-handle:hover {\n color: #757575;\n}\n\n.sort-item.dragging .sort-drag-handle {\n cursor: grabbing;\n color: #1976d2;\n}\n\n/* Sort Field Dropdown */\n.sort-field-dropdown {\n flex: 1;\n min-width: 120px;\n padding: 8px 10px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 13px;\n background: white;\n cursor: pointer;\n}\n\n.sort-field-dropdown:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.sort-field-dropdown:disabled {\n background: #f5f5f5;\n color: #999;\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: 4px;\n cursor: pointer;\n color: #bdbdbd;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.sort-remove-btn:hover:not(:disabled) {\n background: #ffebee;\n color: #c62828;\n}\n\n.sort-remove-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n/* Sort Drop Indicator */\n.sort-drop-indicator {\n height: 3px;\n background: linear-gradient(90deg, #1976d2 0%, #42a5f5 50%, #1976d2 100%);\n border-radius: 2px;\n margin: 0 12px;\n box-shadow: 0 0 8px rgba(25, 118, 210, 0.5);\n animation: sortDropIndicatorPulse 0.8s ease-in-out infinite;\n}\n\n@keyframes sortDropIndicatorPulse {\n 0%, 100% { opacity: 1; transform: scaleX(1); }\n 50% { opacity: 0.7; transform: scaleX(0.98); }\n}\n\n/* Sort Hint */\n.sort-hint {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 10px 12px;\n background: #f5f7fa;\n border-radius: 6px;\n font-size: 12px;\n color: #666;\n}\n\n.sort-hint i {\n color: #1976d2;\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n/* Sorting Section Styles */\n.sorting-section {\n padding-top: 0;\n}\n\n.sorting-header {\n margin-bottom: 16px;\n}\n\n.sorting-description {\n font-size: 13px;\n color: #666;\n line-height: 1.5;\n margin: 0;\n}\n\n/* Sort Empty State */\n.sort-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px 20px;\n color: #999;\n font-size: 14px;\n text-align: center;\n}\n\n.sort-empty-state i {\n font-size: 48px;\n opacity: 0.4;\n margin-bottom: 8px;\n}\n\n.sort-empty-hint {\n font-size: 12px;\n color: #bbb;\n margin: 0;\n}\n\n/* Column Lists */\n.column-list {\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n background: #fafafa;\n}\n\n.column-list.visible-columns {\n max-height: 300px;\n overflow-y: auto;\n}\n\n.column-list.hidden-columns {\n flex: 1;\n min-height: 100px;\n overflow-y: auto;\n}\n\n/* Hidden columns section should expand to fill available space */\n.config-section:last-child {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n margin-bottom: 0;\n}\n\n.column-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n background: white;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.1s ease;\n position: relative;\n}\n\n.column-item:last-child {\n border-bottom: none;\n}\n\n.column-item:hover {\n background: #f5f7fa;\n}\n\n.column-item.hidden {\n background: #fafafa;\n}\n\n.column-item.hidden:hover {\n background: #f0f0f0;\n}\n\n/* ========================================\n DRAG & DROP INDICATOR STYLES\n ======================================== */\n\n/* Drop indicator line element - shows where the column will be placed */\n.drop-indicator {\n height: 3px;\n background: linear-gradient(90deg, #1976d2 0%, #42a5f5 50%, #1976d2 100%);\n border-radius: 2px;\n margin: 0 12px;\n box-shadow: 0 0 8px rgba(25, 118, 210, 0.5);\n animation: dropIndicatorPulse 0.8s ease-in-out infinite;\n}\n\n@keyframes dropIndicatorPulse {\n 0%, 100% { opacity: 1; transform: scaleX(1); }\n 50% { opacity: 0.7; transform: scaleX(0.98); }\n}\n\n/* Highlight the column being dragged */\n.column-item.dragging {\n opacity: 0.5;\n background: #e3f2fd;\n}\n\n/* Visual feedback during drag over */\n.column-item.drag-over {\n background: #f5f7fa;\n}\n\n.drag-handle {\n cursor: grab;\n color: #999;\n padding: 4px;\n transition: color 0.15s ease;\n}\n\n.drag-handle:hover {\n color: #666;\n}\n\n.drag-handle:active {\n cursor: grabbing;\n color: #1976d2;\n}\n\n.column-name {\n flex: 1;\n font-size: 14px;\n color: #333;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.column-name .format-indicator {\n font-size: 10px;\n color: #1976d2;\n opacity: 0.7;\n}\n\n.column-name .alias-indicator {\n font-size: 10px;\n color: #7b1fa2;\n opacity: 0.8;\n}\n\n.column-actions {\n display: flex;\n gap: 4px;\n}\n\n.action-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n color: #999;\n transition: all 0.15s ease;\n}\n\n.action-btn:hover:not(:disabled) {\n background: #e0e0e0;\n color: #333;\n}\n\n.action-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n.action-btn.hide-btn:hover {\n background: #ffebee;\n color: #c62828;\n}\n\n.action-btn.show-btn:hover {\n background: #e8f5e9;\n color: #2e7d32;\n}\n\n.action-btn.format-btn:hover {\n background: #e3f2fd;\n color: #1976d2;\n}\n\n.action-btn.format-btn.has-format {\n color: #1976d2;\n background: #e3f2fd;\n}\n\n.empty-list {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 20px;\n color: #999;\n font-size: 13px;\n}\n\n/* Column Search */\n.column-search {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n margin-bottom: 8px;\n}\n\n.column-search i {\n color: #999;\n font-size: 12px;\n}\n\n.column-search input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 14px;\n}\n\n/* ========================================\n COLUMN FORMAT EDITOR SUB-PANEL\n ======================================== */\n\n.format-editor-panel {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: white;\n z-index: 100;\n display: flex;\n flex-direction: column;\n animation: slideInFromRight 0.2s ease;\n}\n\n@keyframes slideInFromRight {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n.format-editor-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n border-bottom: 1px solid #e0e0e0;\n background: #fafafa;\n flex-shrink: 0;\n}\n\n.format-back-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: #666;\n transition: all 0.15s ease;\n}\n\n.format-back-btn:hover {\n background: #e0e0e0;\n color: #333;\n}\n\n.format-header-title {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.format-header-title span:first-child {\n font-size: 14px;\n font-weight: 600;\n color: #333;\n}\n\n.format-header-title span:last-child {\n font-size: 12px;\n color: #666;\n}\n\n.format-editor-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n/* Format Preview Section */\n.format-preview-section {\n margin-bottom: 24px;\n padding: 16px;\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n}\n\n.preview-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 12px;\n font-weight: 600;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.preview-header i {\n color: #3b82f6;\n}\n\n.preview-samples {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.preview-sample {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 14px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n.preview-label {\n font-size: 12px;\n color: #94a3b8;\n min-width: 60px;\n}\n\n.preview-value {\n flex: 1;\n font-size: 14px;\n font-family: 'SF Mono', 'Monaco', monospace;\n color: #1e293b;\n}\n\n/* Format Type Selection */\n.format-type-section {\n margin-bottom: 24px;\n}\n\n.format-type-section .section-label {\n display: block;\n margin-bottom: 10px;\n font-size: 13px;\n font-weight: 600;\n color: #333;\n}\n\n.format-type-grid {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 8px;\n}\n\n.format-type-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 12px 8px;\n border: 2px solid #e5e7eb;\n border-radius: 10px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.format-type-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.format-type-btn.active {\n border-color: #3b82f6;\n background: #eff6ff;\n}\n\n.format-type-btn i {\n font-size: 18px;\n color: #64748b;\n}\n\n.format-type-btn.active i {\n color: #3b82f6;\n}\n\n.format-type-btn span {\n font-size: 11px;\n font-weight: 500;\n color: #64748b;\n}\n\n.format-type-btn.active span {\n color: #1d4ed8;\n}\n\n/* Format Options Section */\n.format-options-section {\n margin-bottom: 24px;\n}\n\n.format-options-section .section-label {\n display: block;\n margin-bottom: 12px;\n font-size: 13px;\n font-weight: 600;\n color: #333;\n}\n\n.format-option-group {\n margin-bottom: 16px;\n}\n\n.format-option-group label {\n display: block;\n margin-bottom: 6px;\n font-size: 13px;\n font-weight: 500;\n color: #333;\n}\n\n.format-option-group input,\n.format-option-group select {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n transition: border-color 0.15s ease;\n}\n\n.format-option-group input:focus,\n.format-option-group select:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.format-option-row {\n display: flex;\n gap: 12px;\n}\n\n.format-option-row .format-option-group {\n flex: 1;\n}\n\n.format-checkbox {\n display: flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n}\n\n.format-checkbox input[type=\"checkbox\"] {\n width: 18px;\n height: 18px;\n cursor: pointer;\n}\n\n.format-checkbox span {\n font-size: 14px;\n color: #333;\n}\n\n/* Alignment Options */\n.alignment-options {\n display: flex;\n gap: 8px;\n}\n\n.align-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 10px;\n border: 2px solid #e5e7eb;\n border-radius: 8px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.align-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.align-btn.active {\n border-color: #3b82f6;\n background: #eff6ff;\n color: #3b82f6;\n}\n\n.align-btn i {\n font-size: 16px;\n color: #64748b;\n}\n\n.align-btn.active i {\n color: #3b82f6;\n}\n\n/* Style Section */\n.style-section {\n margin-bottom: 24px;\n}\n\n.style-section .section-label {\n display: block;\n margin-bottom: 12px;\n font-size: 13px;\n font-weight: 600;\n color: #333;\n}\n\n.style-toggle-group {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.style-toggle-btn {\n width: 40px;\n height: 40px;\n border: 2px solid #e5e7eb;\n border-radius: 8px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.style-toggle-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.style-toggle-btn.active {\n border-color: #3b82f6;\n background: #eff6ff;\n}\n\n.style-toggle-btn i {\n font-size: 16px;\n color: #64748b;\n}\n\n.style-toggle-btn.active i {\n color: #3b82f6;\n}\n\n.color-picker-row {\n display: flex;\n gap: 12px;\n align-items: center;\n}\n\n.color-picker-group {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.color-picker-group label {\n font-size: 13px;\n color: #64748b;\n min-width: 80px;\n}\n\n.color-picker-group input[type=\"color\"] {\n width: 40px;\n height: 32px;\n padding: 2px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.color-clear-btn {\n width: 32px;\n height: 32px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n color: #999;\n transition: all 0.15s ease;\n}\n\n.color-clear-btn:hover {\n background: #f5f5f5;\n color: #666;\n}\n\n/* Format Editor Footer */\n.format-editor-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n background: #fafafa;\n flex-shrink: 0;\n}\n\n.format-footer-left {\n display: flex;\n gap: 8px;\n}\n\n.format-footer-right {\n display: flex;\n gap: 8px;\n}\n\n.format-footer-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n border: 1px solid #e0e0e0;\n background: white;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.format-footer-btn:hover:not(:disabled) {\n background: #f0f0f0;\n border-color: #ccc;\n}\n\n.format-footer-btn.primary {\n background: #1976d2;\n border-color: #1976d2;\n color: white;\n}\n\n.format-footer-btn.primary:hover:not(:disabled) {\n background: #1565c0;\n border-color: #1565c0;\n}\n\n.format-footer-btn.danger {\n color: #c62828;\n border-color: #c62828;\n}\n\n.format-footer-btn.danger:hover:not(:disabled) {\n background: #c62828;\n color: white;\n}\n\n/* Filter Summary Container */\n.filter-summary-container {\n padding: 4px 0;\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n background: #f5f7fa;\n border-radius: 8px;\n border: 1px solid #e8e8e8;\n}\n\n.summary-info {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-badge {\n background: #1976d2;\n color: white;\n font-size: 12px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 10px;\n min-width: 20px;\n text-align: center;\n}\n\n.summary-text {\n font-size: 14px;\n color: #333;\n}\n\n.summary-text.no-filters {\n color: #999;\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: 4px;\n padding: 6px 12px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.summary-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.summary-btn i {\n font-size: 11px;\n}\n\n.summary-btn.clear-btn {\n background: transparent;\n color: #c62828;\n border: 1px solid #e0e0e0;\n}\n\n.summary-btn.clear-btn:hover:not(:disabled) {\n background: #ffebee;\n border-color: #c62828;\n}\n\n.summary-btn.edit-btn {\n background: #1976d2;\n color: white;\n}\n\n.summary-btn.edit-btn:hover:not(:disabled) {\n background: #1565c0;\n}\n\n/* ========================================\n FILTER MODE SELECTOR\n ======================================== */\n\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: 14px;\n padding: 14px 16px;\n border: 2px solid #e5e7eb;\n border-radius: 12px;\n background: white;\n cursor: pointer;\n transition: all 0.2s ease;\n text-align: left;\n}\n\n.filter-mode-btn:hover:not(:disabled) {\n border-color: #d1d5db;\n background: #f9fafb;\n}\n\n.filter-mode-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.filter-mode-btn.active {\n border-color: #3b82f6;\n background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n}\n\n.filter-mode-btn .mode-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 10px;\n background: #f3f4f6;\n color: #6b7280;\n font-size: 16px;\n flex-shrink: 0;\n transition: all 0.2s ease;\n}\n\n.filter-mode-btn.active .mode-icon {\n background: #3b82f6;\n color: white;\n}\n\n.filter-mode-btn .mode-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.filter-mode-btn .mode-title {\n font-size: 14px;\n font-weight: 600;\n color: #1f2937;\n}\n\n.filter-mode-btn .mode-subtitle {\n font-size: 12px;\n color: #6b7280;\n}\n\n.filter-mode-btn.active .mode-title {\n color: #1d4ed8;\n}\n\n.filter-mode-btn .mode-check {\n color: #3b82f6;\n font-size: 16px;\n flex-shrink: 0;\n}\n\n/* ========================================\n SMART FILTER SECTION\n ======================================== */\n\n.smart-filter-section {\n padding: 0;\n border: none;\n background: transparent;\n}\n\n.smart-filter-input-container {\n display: flex;\n gap: 12px;\n padding: 16px;\n background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);\n border: 1px solid #bae6fd;\n border-radius: 12px;\n margin-bottom: 16px;\n}\n\n.smart-filter-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);\n border-radius: 10px;\n color: white;\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.smart-filter-input-container .smart-filter-textarea {\n flex: 1;\n border: none;\n background: white;\n border-radius: 8px;\n padding: 12px;\n font-size: 14px;\n line-height: 1.5;\n resize: none;\n min-height: 60px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n.smart-filter-input-container .smart-filter-textarea:focus {\n outline: none;\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3), 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n.smart-filter-input-container .smart-filter-textarea::placeholder {\n color: #9ca3af;\n}\n\n.smart-filter-explanation {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 12px;\n background: #e3f2fd;\n border-radius: 6px;\n margin-bottom: 12px;\n font-size: 13px;\n color: #1565c0;\n}\n\n.smart-filter-explanation i {\n color: #1976d2;\n font-size: 14px;\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n/* Smart Filter Examples */\n.smart-filter-examples {\n margin-bottom: 16px;\n}\n\n.examples-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 10px;\n font-size: 13px;\n font-weight: 500;\n color: #6b7280;\n}\n\n.examples-header i {\n color: #f59e0b;\n}\n\n.example-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.example-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n border: 1px solid #e5e7eb;\n border-radius: 20px;\n background: white;\n font-size: 12px;\n font-weight: 500;\n color: #4b5563;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.example-chip:hover:not(:disabled) {\n border-color: #3b82f6;\n background: #eff6ff;\n color: #1d4ed8;\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 color: #9ca3af;\n}\n\n.example-chip:hover:not(:disabled) i {\n color: #3b82f6;\n}\n\n/* Smart Filter Tip */\n.smart-filter-tip {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 12px 14px;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n font-size: 12px;\n color: #64748b;\n line-height: 1.5;\n}\n\n.smart-filter-tip i {\n color: #94a3b8;\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n/* ========================================\n TRADITIONAL FILTER SECTION\n ======================================== */\n\n.traditional-filter-section {\n padding: 0;\n border: none;\n background: transparent;\n}\n\n.traditional-filter-tip {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 12px 14px;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n font-size: 12px;\n color: #64748b;\n line-height: 1.5;\n margin-top: 12px;\n}\n\n.traditional-filter-tip i {\n color: #94a3b8;\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n.summary-btn.edit-btn.primary {\n background: #3b82f6;\n color: white;\n border-color: #3b82f6;\n}\n\n.summary-btn.edit-btn.primary:hover:not(:disabled) {\n background: #2563eb;\n border-color: #2563eb;\n}\n\n/* Form Elements */\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: #333;\n}\n\n.form-input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n transition: border-color 0.15s ease;\n}\n\n.form-input:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.form-input:disabled {\n background: #f5f5f5;\n color: #999;\n}\n\n.form-textarea {\n resize: vertical;\n min-height: 80px;\n}\n\n/* Checkbox */\n.checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n cursor: pointer;\n}\n\n.checkbox-label input[type=\"checkbox\"] {\n width: 18px;\n height: 18px;\n margin-top: 2px;\n cursor: pointer;\n}\n\n.checkbox-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.checkbox-text strong {\n font-size: 14px;\n color: #333;\n}\n\n.checkbox-text small {\n font-size: 12px;\n color: #666;\n}\n\n/* Danger Zone */\n.danger-zone {\n padding-top: 16px;\n border-top: 1px solid #ffcdd2;\n}\n\n.danger-zone .section-header {\n color: #c62828;\n}\n\n.danger-zone .section-header i {\n color: #c62828;\n}\n\n.delete-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n border: 1px solid #c62828;\n background: transparent;\n border-radius: 6px;\n color: #c62828;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.delete-btn:hover {\n background: #c62828;\n color: white;\n}\n\n/* Panel Footer */\n.panel-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n background: #fafafa;\n flex-shrink: 0;\n}\n\n.footer-left {\n display: flex;\n gap: 8px;\n}\n\n.footer-right {\n display: flex;\n gap: 8px;\n}\n\n.footer-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n border: 1px solid #e0e0e0;\n background: white;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.footer-btn:hover:not(:disabled) {\n background: #f0f0f0;\n border-color: #ccc;\n}\n\n.footer-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.footer-btn.primary {\n background: #1976d2;\n border-color: #1976d2;\n color: white;\n}\n\n.footer-btn.primary:hover:not(:disabled) {\n background: #1565c0;\n border-color: #1565c0;\n}\n\n.footer-btn i {\n font-size: 12px;\n}\n\n/* ========================================\n FORMAT EDITOR INLINE STYLES\n ======================================== */\n\n.format-editor {\n padding: 0 20px;\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n}\n\n.format-editor .format-editor-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 0 16px 0;\n border-bottom: 1px solid #e0e0e0;\n margin-bottom: 16px;\n}\n\n.format-editor .back-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: #666;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.format-editor .back-btn:hover {\n background: #e0e0e0;\n color: #333;\n}\n\n.format-editor .format-editor-title {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n}\n\n/* Preview Table */\n.format-editor .preview-table {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 12px;\n}\n\n.format-editor .preview-header-cell {\n padding: 10px 14px;\n background: #f1f5f9;\n border-radius: 6px;\n font-weight: 600;\n font-size: 13px;\n color: #333;\n}\n\n.format-editor .preview-cell {\n padding: 8px 14px;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n font-size: 14px;\n color: #333;\n}\n\n/* Format Section */\n.format-section {\n margin-bottom: 20px;\n padding-bottom: 16px;\n border-bottom: 1px solid #f0f0f0;\n}\n\n.format-section:last-child {\n border-bottom: none;\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: #333;\n}\n\n.format-section-header i {\n color: #1976d2;\n font-size: 12px;\n}\n\n/* Format Row */\n.format-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 10px;\n}\n\n.format-row:last-child {\n margin-bottom: 0;\n}\n\n.format-row label {\n font-size: 13px;\n color: #555;\n flex-shrink: 0;\n}\n\n/* Format Inputs */\n.format-select {\n flex: 1;\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n background: white;\n cursor: pointer;\n min-width: 120px;\n}\n\n.format-select:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.format-input {\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n background: white;\n}\n\n.format-input:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.format-input.small {\n width: 80px;\n}\n\n/* Alias Info Row */\n.format-row.alias-info {\n justify-content: flex-start;\n gap: 8px;\n}\n\n.format-row.alias-info .muted-text {\n color: #888;\n font-size: 12px;\n}\n\n.clear-alias-btn {\n width: 20px;\n height: 20px;\n border: none;\n background: #f0f0f0;\n border-radius: 50%;\n color: #888;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n transition: all 0.15s ease;\n}\n\n.clear-alias-btn:hover {\n background: #e0e0e0;\n color: #c62828;\n}\n\n/* Color Input */\n.color-input {\n width: 50px;\n height: 32px;\n padding: 2px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n background: white;\n}\n\n.color-input:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n/* Alignment Toggle */\n.alignment-toggle {\n display: flex;\n gap: 8px;\n}\n\n.align-btn {\n width: 40px;\n height: 36px;\n border: 2px solid #e5e7eb;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.align-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.align-btn.active {\n border-color: #1976d2;\n background: #e3f2fd;\n}\n\n.align-btn i {\n font-size: 14px;\n color: #64748b;\n}\n\n.align-btn.active i {\n color: #1976d2;\n}\n\n/* Style Buttons */\n.style-buttons {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.style-btn {\n width: 36px;\n height: 36px;\n border: 2px solid #e5e7eb;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.style-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.style-btn.active {\n border-color: #1976d2;\n background: #e3f2fd;\n}\n\n.style-btn i {\n font-size: 14px;\n color: #64748b;\n}\n\n.style-btn.active i {\n color: #1976d2;\n}\n\n/* Collapsible Format Sections */\n.format-section-collapsible {\n margin-bottom: 12px;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.format-section-collapsible summary {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 14px;\n background: #f8fafc;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: #333;\n list-style: none;\n}\n\n.format-section-collapsible summary::-webkit-details-marker {\n display: none;\n}\n\n.format-section-collapsible summary::before {\n content: '\\f054';\n font-family: 'Font Awesome 6 Free';\n font-weight: 900;\n font-size: 10px;\n color: #999;\n transition: transform 0.2s ease;\n}\n\n.format-section-collapsible[open] summary::before {\n transform: rotate(90deg);\n}\n\n.format-section-collapsible summary i {\n color: #1976d2;\n font-size: 12px;\n}\n\n.format-section-collapsible summary span {\n flex: 1;\n}\n\n.format-section-content {\n padding: 14px;\n background: white;\n border-top: 1px solid #e0e0e0;\n}\n\n/* Format Actions */\n.format-actions {\n margin-top: 20px;\n padding-top: 16px;\n border-top: 1px solid #e0e0e0;\n}\n\n.clear-format-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n border: 1px solid #e0e0e0;\n background: white;\n border-radius: 6px;\n color: #666;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.clear-format-btn:hover {\n background: #fff5f5;\n border-color: #f87171;\n color: #dc2626;\n}\n\n.clear-format-btn i {\n font-size: 12px;\n}\n\n/* ========================================\n AGGREGATES TAB STYLES\n ======================================== */\n\n.aggregates-section {\n padding-top: 0;\n}\n\n.aggregates-header {\n margin-bottom: 16px;\n}\n\n.aggregates-description {\n font-size: 13px;\n color: #666;\n line-height: 1.5;\n margin: 0;\n}\n\n/* Add Aggregate Button */\n.add-aggregate-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n border: 1px dashed #ccc;\n border-radius: 8px;\n background: transparent;\n color: #666;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n width: 100%;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.add-aggregate-btn:hover:not(:disabled) {\n border-color: #1976d2;\n background: #e3f2fd;\n color: #1976d2;\n}\n\n.add-aggregate-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.add-aggregate-btn i {\n font-size: 12px;\n}\n\n/* Aggregates List */\n.aggregates-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n/* Aggregate Item */\n.aggregate-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 14px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n transition: all 0.15s ease;\n}\n\n.aggregate-item:hover {\n border-color: #bdbdbd;\n background: #fafafa;\n}\n\n.aggregate-item.disabled {\n opacity: 0.6;\n background: #f5f5f5;\n}\n\n/* Aggregate Icon */\n.agg-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);\n border-radius: 8px;\n color: #1976d2;\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.aggregate-item.disabled .agg-icon {\n background: #f0f0f0;\n color: #999;\n}\n\n/* Aggregate Content */\n.agg-content {\n flex: 1;\n min-width: 0;\n}\n\n.agg-label {\n font-size: 14px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin-bottom: 2px;\n}\n\n.aggregate-item.disabled .agg-label {\n color: #888;\n}\n\n.agg-details {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #888;\n}\n\n.agg-type {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.agg-type i {\n font-size: 10px;\n}\n\n.agg-smart-badge {\n display: flex;\n align-items: center;\n padding: 2px 6px;\n background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n border-radius: 4px;\n color: #3b82f6;\n font-size: 10px;\n}\n\n/* Aggregate Actions */\n.agg-actions {\n display: flex;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.agg-action-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n color: #999;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.agg-action-btn:hover:not(:disabled) {\n background: #e0e0e0;\n color: #333;\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: #2e7d32;\n}\n\n.agg-action-btn.toggle-btn:not(.enabled) {\n color: #bdbdbd;\n}\n\n.agg-action-btn.edit-btn:hover:not(:disabled) {\n background: #e3f2fd;\n color: #1976d2;\n}\n\n.agg-action-btn.remove-btn:hover:not(:disabled) {\n background: #ffebee;\n color: #c62828;\n}\n\n/* Aggregates Summary */\n.aggregates-summary {\n display: flex;\n gap: 16px;\n padding: 12px 14px;\n background: #f5f7fa;\n border-radius: 6px;\n margin-bottom: 16px;\n}\n\n.summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: #666;\n}\n\n.summary-item i {\n color: #1976d2;\n font-size: 11px;\n}\n\n/* Aggregates Empty State */\n.aggregates-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px 20px;\n color: #999;\n font-size: 14px;\n text-align: center;\n}\n\n.aggregates-empty-state i {\n font-size: 48px;\n opacity: 0.4;\n margin-bottom: 8px;\n}\n\n.empty-hint {\n font-size: 12px;\n color: #bbb;\n margin: 0;\n}\n\n/* Aggregates Tip */\n.aggregates-tip {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 12px 14px;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n font-size: 12px;\n color: #64748b;\n line-height: 1.5;\n}\n\n.aggregates-tip i {\n color: #94a3b8;\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .config-panel {\n width: 100% !important;\n max-width: 100vw !important;\n }\n\n /* Hide resize handle on mobile - panel is full width */\n .resize-handle {\n display: none;\n }\n\n .tab-btn span {\n display: none;\n }\n\n .tab-btn {\n padding: 14px;\n }\n\n .tab-btn i {\n font-size: 16px;\n }\n\n .format-type-grid {\n grid-template-columns: repeat(3, 1fr);\n }\n}\n\n/* Medium screens - limit max resize width */\n@media (min-width: 769px) and (max-width: 1200px) {\n .config-panel {\n max-width: min(600px, 80vw);\n }\n}\n\n/* Validation Styles (BUG-004) */\n.form-input.invalid {\n border-color: #ef4444;\n}\n\n.validation-error {\n display: block;\n font-size: 12px;\n color: #ef4444;\n margin-top: 4px;\n}\n\n.validation-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: #fef2f2;\n border-top: 1px solid #fecaca;\n color: #dc2626;\n font-size: 13px;\n}\n\n.validation-banner i {\n flex-shrink: 0;\n}\n\n/* Duplicate Button (F-005) */\n.footer-btn.duplicate-btn {\n background: #f3f4f6;\n color: #374151;\n border: 1px solid #d1d5db;\n}\n\n.footer-btn.duplicate-btn:hover {\n background: #e5e7eb;\n border-color: #9ca3af;\n}\n"] }]
|
|
2857
|
+
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\">\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 <div class=\"empty-list\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>No columns visible. Add columns from below.</span>\n </div>\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 <div class=\"empty-list\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <span>All columns are visible</span>\n </div>\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\">\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 <div class=\"sort-hint\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Drag items to change priority. Records sort by first level, then second, etc.</span>\n </div>\n }\n } @else {\n <div class=\"sort-empty-state\">\n <i class=\"fa-solid fa-arrow-up-wide-short\"></i>\n <span>No sorting configured</span>\n <p class=\"sort-empty-hint\">Click \"Add Sort Level\" to define how records should be ordered</p>\n </div>\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 <div class=\"smart-filter-explanation\">\n <i class=\"fa-solid fa-robot\"></i>\n <span>{{ smartFilterExplanation }}</span>\n </div>\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 <div class=\"smart-filter-tip\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>The AI will interpret your description and create the appropriate filter when you save the view.</span>\n </div>\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 <div class=\"traditional-filter-tip\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Build precise filters by selecting fields, operators, and values. Use groups for complex AND/OR logic.</span>\n </div>\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 <div class=\"aggregates-empty-state\">\n <i class=\"fa-solid fa-chart-simple\"></i>\n <span>No aggregates configured</span>\n <p class=\"empty-hint\">Click \"Add Aggregate\" to create summary calculations</p>\n </div>\n }\n\n <!-- Info Tip -->\n <div class=\"aggregates-tip\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Aggregates are calculated server-side for accuracy with filtered/paginated data.</span>\n </div>\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 <div class=\"validation-banner\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>At least one column must be visible</span>\n </div>\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: 1000;\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: 0;\n right: 0;\n width: 450px;\n min-width: 360px;\n max-width: min(800px, 100vw);\n height: 100%;\n background: white;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);\n z-index: 1001;\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: linear-gradient(90deg, rgba(25, 118, 210, 0.1) 0%, transparent 100%);\n}\n\n.resize-grip {\n width: 3px;\n height: 40px;\n background: #cbd5e1;\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: #1976d2;\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 #e0e0e0;\n background: #fafafa;\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: #333;\n}\n\n.header-title i {\n color: #1976d2;\n}\n\n.close-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: #666;\n transition: all 0.15s ease;\n}\n\n.close-btn:hover {\n background: #e0e0e0;\n color: #333;\n}\n\n/* Tab Navigation */\n.tab-nav {\n display: flex;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\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 color: #666;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 2px solid transparent;\n}\n\n.tab-btn:hover {\n background: #f5f5f5;\n color: #333;\n}\n\n.tab-btn.active {\n color: #1976d2;\n border-bottom-color: #1976d2;\n background: transparent;\n}\n\n.tab-btn i {\n font-size: 12px;\n}\n\n.tab-badge {\n background: #1976d2;\n color: white;\n font-size: 10px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 8px;\n margin-left: 4px;\n}\n\n/* Icon-only mode for narrow panels */\n.tab-nav.icon-only .tab-btn {\n padding: 12px 8px;\n gap: 4px;\n}\n\n.tab-nav.icon-only .tab-btn i {\n font-size: 14px;\n}\n\n.tab-nav.icon-only .tab-badge {\n margin-left: 0;\n padding: 2px 4px;\n font-size: 9px;\n}\n\n/* Panel Content */\n.panel-content {\n flex: 1;\n overflow: hidden;\n padding: 16px 0;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.tab-content {\n padding: 0 20px;\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n}\n\n/* Config Sections */\n.config-section {\n margin-bottom: 24px;\n}\n\n.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: #333;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.section-header i {\n color: #1976d2;\n font-size: 12px;\n}\n\n.column-count {\n margin-left: auto;\n background: #e3f2fd;\n color: #1976d2;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n}\n\n/* Sort Configuration */\n.sort-config {\n display: flex;\n gap: 8px;\n}\n\n.sort-field-select {\n flex: 1;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n background: white;\n cursor: pointer;\n}\n\n.sort-field-select:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.sort-direction-toggle {\n display: flex;\n background: #f0f0f0;\n border-radius: 6px;\n padding: 2px;\n}\n\n.direction-btn {\n width: 36px;\n height: 36px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n color: #666;\n transition: all 0.15s ease;\n}\n\n.direction-btn:hover {\n color: #333;\n}\n\n.direction-btn.active {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n/* ========================================\n MULTI-SORT CONFIGURATION\n ======================================== */\n\n.add-sort-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n border: 1px dashed #ccc;\n border-radius: 8px;\n background: transparent;\n color: #666;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n width: 100%;\n justify-content: center;\n margin-bottom: 12px;\n}\n\n.add-sort-btn:hover:not(:disabled) {\n border-color: #1976d2;\n background: #e3f2fd;\n color: #1976d2;\n}\n\n.add-sort-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.add-sort-btn i {\n font-size: 12px;\n}\n\n/* Sort Items List */\n.sort-items-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n/* Sort Item */\n.sort-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n transition: all 0.15s ease;\n}\n\n.sort-item:hover {\n border-color: #bdbdbd;\n background: #fafafa;\n}\n\n.sort-item.dragging {\n opacity: 0.5;\n background: #e3f2fd;\n border-color: #1976d2;\n}\n\n.sort-item.drop-target {\n border-color: #1976d2;\n}\n\n/* Priority Badge */\n.sort-priority-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n height: 24px;\n padding: 0 6px;\n font-size: 12px;\n font-weight: 700;\n color: white;\n background: linear-gradient(135deg, #1976d2 0%, #42a5f5 100%);\n border-radius: 12px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n flex-shrink: 0;\n}\n\n/* Drag Handle */\n.sort-drag-handle {\n color: #bdbdbd;\n cursor: grab;\n padding: 4px;\n transition: color 0.15s ease;\n flex-shrink: 0;\n}\n\n.sort-drag-handle:hover {\n color: #757575;\n}\n\n.sort-item.dragging .sort-drag-handle {\n cursor: grabbing;\n color: #1976d2;\n}\n\n/* Sort Field Dropdown */\n.sort-field-dropdown {\n flex: 1;\n min-width: 120px;\n padding: 8px 10px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 13px;\n background: white;\n cursor: pointer;\n}\n\n.sort-field-dropdown:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.sort-field-dropdown:disabled {\n background: #f5f5f5;\n color: #999;\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: 4px;\n cursor: pointer;\n color: #bdbdbd;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.sort-remove-btn:hover:not(:disabled) {\n background: #ffebee;\n color: #c62828;\n}\n\n.sort-remove-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n/* Sort Drop Indicator */\n.sort-drop-indicator {\n height: 3px;\n background: linear-gradient(90deg, #1976d2 0%, #42a5f5 50%, #1976d2 100%);\n border-radius: 2px;\n margin: 0 12px;\n box-shadow: 0 0 8px rgba(25, 118, 210, 0.5);\n animation: sortDropIndicatorPulse 0.8s ease-in-out infinite;\n}\n\n@keyframes sortDropIndicatorPulse {\n 0%, 100% { opacity: 1; transform: scaleX(1); }\n 50% { opacity: 0.7; transform: scaleX(0.98); }\n}\n\n/* Sort Hint */\n.sort-hint {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 10px 12px;\n background: #f5f7fa;\n border-radius: 6px;\n font-size: 12px;\n color: #666;\n}\n\n.sort-hint i {\n color: #1976d2;\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n/* Sorting Section Styles */\n.sorting-section {\n padding-top: 0;\n}\n\n.sorting-header {\n margin-bottom: 16px;\n}\n\n.sorting-description {\n font-size: 13px;\n color: #666;\n line-height: 1.5;\n margin: 0;\n}\n\n/* Sort Empty State */\n.sort-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px 20px;\n color: #999;\n font-size: 14px;\n text-align: center;\n}\n\n.sort-empty-state i {\n font-size: 48px;\n opacity: 0.4;\n margin-bottom: 8px;\n}\n\n.sort-empty-hint {\n font-size: 12px;\n color: #bbb;\n margin: 0;\n}\n\n/* Column Lists */\n.column-list {\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n background: #fafafa;\n}\n\n.column-list.visible-columns {\n max-height: 300px;\n overflow-y: auto;\n}\n\n.column-list.hidden-columns {\n flex: 1;\n min-height: 100px;\n overflow-y: auto;\n}\n\n/* Hidden columns section should expand to fill available space */\n.config-section:last-child {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n margin-bottom: 0;\n}\n\n.column-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n background: white;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.1s ease;\n position: relative;\n}\n\n.column-item:last-child {\n border-bottom: none;\n}\n\n.column-item:hover {\n background: #f5f7fa;\n}\n\n.column-item.hidden {\n background: #fafafa;\n}\n\n.column-item.hidden:hover {\n background: #f0f0f0;\n}\n\n/* ========================================\n DRAG & DROP INDICATOR STYLES\n ======================================== */\n\n/* Drop indicator line element - shows where the column will be placed */\n.drop-indicator {\n height: 3px;\n background: linear-gradient(90deg, #1976d2 0%, #42a5f5 50%, #1976d2 100%);\n border-radius: 2px;\n margin: 0 12px;\n box-shadow: 0 0 8px rgba(25, 118, 210, 0.5);\n animation: dropIndicatorPulse 0.8s ease-in-out infinite;\n}\n\n@keyframes dropIndicatorPulse {\n 0%, 100% { opacity: 1; transform: scaleX(1); }\n 50% { opacity: 0.7; transform: scaleX(0.98); }\n}\n\n/* Highlight the column being dragged */\n.column-item.dragging {\n opacity: 0.5;\n background: #e3f2fd;\n}\n\n/* Visual feedback during drag over */\n.column-item.drag-over {\n background: #f5f7fa;\n}\n\n.drag-handle {\n cursor: grab;\n color: #999;\n padding: 4px;\n transition: color 0.15s ease;\n}\n\n.drag-handle:hover {\n color: #666;\n}\n\n.drag-handle:active {\n cursor: grabbing;\n color: #1976d2;\n}\n\n.column-name {\n flex: 1;\n font-size: 14px;\n color: #333;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.column-name .format-indicator {\n font-size: 10px;\n color: #1976d2;\n opacity: 0.7;\n}\n\n.column-name .alias-indicator {\n font-size: 10px;\n color: #7b1fa2;\n opacity: 0.8;\n}\n\n.column-actions {\n display: flex;\n gap: 4px;\n}\n\n.action-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n color: #999;\n transition: all 0.15s ease;\n}\n\n.action-btn:hover:not(:disabled) {\n background: #e0e0e0;\n color: #333;\n}\n\n.action-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n\n.action-btn.hide-btn:hover {\n background: #ffebee;\n color: #c62828;\n}\n\n.action-btn.show-btn:hover {\n background: #e8f5e9;\n color: #2e7d32;\n}\n\n.action-btn.format-btn:hover {\n background: #e3f2fd;\n color: #1976d2;\n}\n\n.action-btn.format-btn.has-format {\n color: #1976d2;\n background: #e3f2fd;\n}\n\n.empty-list {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 20px;\n color: #999;\n font-size: 13px;\n}\n\n/* Column Search */\n.column-search {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n margin-bottom: 8px;\n}\n\n.column-search i {\n color: #999;\n font-size: 12px;\n}\n\n.column-search input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 14px;\n}\n\n/* ========================================\n COLUMN FORMAT EDITOR SUB-PANEL\n ======================================== */\n\n.format-editor-panel {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: white;\n z-index: 100;\n display: flex;\n flex-direction: column;\n animation: slideInFromRight 0.2s ease;\n}\n\n@keyframes slideInFromRight {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n.format-editor-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n border-bottom: 1px solid #e0e0e0;\n background: #fafafa;\n flex-shrink: 0;\n}\n\n.format-back-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: #666;\n transition: all 0.15s ease;\n}\n\n.format-back-btn:hover {\n background: #e0e0e0;\n color: #333;\n}\n\n.format-header-title {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.format-header-title span:first-child {\n font-size: 14px;\n font-weight: 600;\n color: #333;\n}\n\n.format-header-title span:last-child {\n font-size: 12px;\n color: #666;\n}\n\n.format-editor-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n/* Format Preview Section */\n.format-preview-section {\n margin-bottom: 24px;\n padding: 16px;\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n}\n\n.preview-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 12px;\n font-weight: 600;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.preview-header i {\n color: #3b82f6;\n}\n\n.preview-samples {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.preview-sample {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 14px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n.preview-label {\n font-size: 12px;\n color: #94a3b8;\n min-width: 60px;\n}\n\n.preview-value {\n flex: 1;\n font-size: 14px;\n font-family: 'SF Mono', 'Monaco', monospace;\n color: #1e293b;\n}\n\n/* Format Type Selection */\n.format-type-section {\n margin-bottom: 24px;\n}\n\n.format-type-section .section-label {\n display: block;\n margin-bottom: 10px;\n font-size: 13px;\n font-weight: 600;\n color: #333;\n}\n\n.format-type-grid {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 8px;\n}\n\n.format-type-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 12px 8px;\n border: 2px solid #e5e7eb;\n border-radius: 10px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.format-type-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.format-type-btn.active {\n border-color: #3b82f6;\n background: #eff6ff;\n}\n\n.format-type-btn i {\n font-size: 18px;\n color: #64748b;\n}\n\n.format-type-btn.active i {\n color: #3b82f6;\n}\n\n.format-type-btn span {\n font-size: 11px;\n font-weight: 500;\n color: #64748b;\n}\n\n.format-type-btn.active span {\n color: #1d4ed8;\n}\n\n/* Format Options Section */\n.format-options-section {\n margin-bottom: 24px;\n}\n\n.format-options-section .section-label {\n display: block;\n margin-bottom: 12px;\n font-size: 13px;\n font-weight: 600;\n color: #333;\n}\n\n.format-option-group {\n margin-bottom: 16px;\n}\n\n.format-option-group label {\n display: block;\n margin-bottom: 6px;\n font-size: 13px;\n font-weight: 500;\n color: #333;\n}\n\n.format-option-group input,\n.format-option-group select {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n transition: border-color 0.15s ease;\n}\n\n.format-option-group input:focus,\n.format-option-group select:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.format-option-row {\n display: flex;\n gap: 12px;\n}\n\n.format-option-row .format-option-group {\n flex: 1;\n}\n\n.format-checkbox {\n display: flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n}\n\n.format-checkbox input[type=\"checkbox\"] {\n width: 18px;\n height: 18px;\n cursor: pointer;\n}\n\n.format-checkbox span {\n font-size: 14px;\n color: #333;\n}\n\n/* Alignment Options */\n.alignment-options {\n display: flex;\n gap: 8px;\n}\n\n.align-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 10px;\n border: 2px solid #e5e7eb;\n border-radius: 8px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.align-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.align-btn.active {\n border-color: #3b82f6;\n background: #eff6ff;\n color: #3b82f6;\n}\n\n.align-btn i {\n font-size: 16px;\n color: #64748b;\n}\n\n.align-btn.active i {\n color: #3b82f6;\n}\n\n/* Style Section */\n.style-section {\n margin-bottom: 24px;\n}\n\n.style-section .section-label {\n display: block;\n margin-bottom: 12px;\n font-size: 13px;\n font-weight: 600;\n color: #333;\n}\n\n.style-toggle-group {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.style-toggle-btn {\n width: 40px;\n height: 40px;\n border: 2px solid #e5e7eb;\n border-radius: 8px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.style-toggle-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.style-toggle-btn.active {\n border-color: #3b82f6;\n background: #eff6ff;\n}\n\n.style-toggle-btn i {\n font-size: 16px;\n color: #64748b;\n}\n\n.style-toggle-btn.active i {\n color: #3b82f6;\n}\n\n.color-picker-row {\n display: flex;\n gap: 12px;\n align-items: center;\n}\n\n.color-picker-group {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.color-picker-group label {\n font-size: 13px;\n color: #64748b;\n min-width: 80px;\n}\n\n.color-picker-group input[type=\"color\"] {\n width: 40px;\n height: 32px;\n padding: 2px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.color-clear-btn {\n width: 32px;\n height: 32px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n color: #999;\n transition: all 0.15s ease;\n}\n\n.color-clear-btn:hover {\n background: #f5f5f5;\n color: #666;\n}\n\n/* Format Editor Footer */\n.format-editor-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n background: #fafafa;\n flex-shrink: 0;\n}\n\n.format-footer-left {\n display: flex;\n gap: 8px;\n}\n\n.format-footer-right {\n display: flex;\n gap: 8px;\n}\n\n.format-footer-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n border: 1px solid #e0e0e0;\n background: white;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.format-footer-btn:hover:not(:disabled) {\n background: #f0f0f0;\n border-color: #ccc;\n}\n\n.format-footer-btn.primary {\n background: #1976d2;\n border-color: #1976d2;\n color: white;\n}\n\n.format-footer-btn.primary:hover:not(:disabled) {\n background: #1565c0;\n border-color: #1565c0;\n}\n\n.format-footer-btn.danger {\n color: #c62828;\n border-color: #c62828;\n}\n\n.format-footer-btn.danger:hover:not(:disabled) {\n background: #c62828;\n color: white;\n}\n\n/* Filter Summary Container */\n.filter-summary-container {\n padding: 4px 0;\n}\n\n.filter-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n background: #f5f7fa;\n border-radius: 8px;\n border: 1px solid #e8e8e8;\n}\n\n.summary-info {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.filter-badge {\n background: #1976d2;\n color: white;\n font-size: 12px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 10px;\n min-width: 20px;\n text-align: center;\n}\n\n.summary-text {\n font-size: 14px;\n color: #333;\n}\n\n.summary-text.no-filters {\n color: #999;\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: 4px;\n padding: 6px 12px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.summary-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.summary-btn i {\n font-size: 11px;\n}\n\n.summary-btn.clear-btn {\n background: transparent;\n color: #c62828;\n border: 1px solid #e0e0e0;\n}\n\n.summary-btn.clear-btn:hover:not(:disabled) {\n background: #ffebee;\n border-color: #c62828;\n}\n\n.summary-btn.edit-btn {\n background: #1976d2;\n color: white;\n}\n\n.summary-btn.edit-btn:hover:not(:disabled) {\n background: #1565c0;\n}\n\n/* ========================================\n FILTER MODE SELECTOR\n ======================================== */\n\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: 14px;\n padding: 14px 16px;\n border: 2px solid #e5e7eb;\n border-radius: 12px;\n background: white;\n cursor: pointer;\n transition: all 0.2s ease;\n text-align: left;\n}\n\n.filter-mode-btn:hover:not(:disabled) {\n border-color: #d1d5db;\n background: #f9fafb;\n}\n\n.filter-mode-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.filter-mode-btn.active {\n border-color: #3b82f6;\n background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n}\n\n.filter-mode-btn .mode-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 10px;\n background: #f3f4f6;\n color: #6b7280;\n font-size: 16px;\n flex-shrink: 0;\n transition: all 0.2s ease;\n}\n\n.filter-mode-btn.active .mode-icon {\n background: #3b82f6;\n color: white;\n}\n\n.filter-mode-btn .mode-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.filter-mode-btn .mode-title {\n font-size: 14px;\n font-weight: 600;\n color: #1f2937;\n}\n\n.filter-mode-btn .mode-subtitle {\n font-size: 12px;\n color: #6b7280;\n}\n\n.filter-mode-btn.active .mode-title {\n color: #1d4ed8;\n}\n\n.filter-mode-btn .mode-check {\n color: #3b82f6;\n font-size: 16px;\n flex-shrink: 0;\n}\n\n/* ========================================\n SMART FILTER SECTION\n ======================================== */\n\n.smart-filter-section {\n padding: 0;\n border: none;\n background: transparent;\n}\n\n.smart-filter-input-container {\n display: flex;\n gap: 12px;\n padding: 16px;\n background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);\n border: 1px solid #bae6fd;\n border-radius: 12px;\n margin-bottom: 16px;\n}\n\n.smart-filter-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);\n border-radius: 10px;\n color: white;\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.smart-filter-input-container .smart-filter-textarea {\n flex: 1;\n border: none;\n background: white;\n border-radius: 8px;\n padding: 12px;\n font-size: 14px;\n line-height: 1.5;\n resize: none;\n min-height: 60px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n.smart-filter-input-container .smart-filter-textarea:focus {\n outline: none;\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3), 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n.smart-filter-input-container .smart-filter-textarea::placeholder {\n color: #9ca3af;\n}\n\n.smart-filter-explanation {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 12px;\n background: #e3f2fd;\n border-radius: 6px;\n margin-bottom: 12px;\n font-size: 13px;\n color: #1565c0;\n}\n\n.smart-filter-explanation i {\n color: #1976d2;\n font-size: 14px;\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n/* Smart Filter Examples */\n.smart-filter-examples {\n margin-bottom: 16px;\n}\n\n.examples-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 10px;\n font-size: 13px;\n font-weight: 500;\n color: #6b7280;\n}\n\n.examples-header i {\n color: #f59e0b;\n}\n\n.example-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.example-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n border: 1px solid #e5e7eb;\n border-radius: 20px;\n background: white;\n font-size: 12px;\n font-weight: 500;\n color: #4b5563;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.example-chip:hover:not(:disabled) {\n border-color: #3b82f6;\n background: #eff6ff;\n color: #1d4ed8;\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 color: #9ca3af;\n}\n\n.example-chip:hover:not(:disabled) i {\n color: #3b82f6;\n}\n\n/* Smart Filter Tip */\n.smart-filter-tip {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 12px 14px;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n font-size: 12px;\n color: #64748b;\n line-height: 1.5;\n}\n\n.smart-filter-tip i {\n color: #94a3b8;\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n/* ========================================\n TRADITIONAL FILTER SECTION\n ======================================== */\n\n.traditional-filter-section {\n padding: 0;\n border: none;\n background: transparent;\n}\n\n.traditional-filter-tip {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 12px 14px;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n font-size: 12px;\n color: #64748b;\n line-height: 1.5;\n margin-top: 12px;\n}\n\n.traditional-filter-tip i {\n color: #94a3b8;\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n.summary-btn.edit-btn.primary {\n background: #3b82f6;\n color: white;\n border-color: #3b82f6;\n}\n\n.summary-btn.edit-btn.primary:hover:not(:disabled) {\n background: #2563eb;\n border-color: #2563eb;\n}\n\n/* Form Elements */\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: #333;\n}\n\n.form-input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n transition: border-color 0.15s ease;\n}\n\n.form-input:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.form-input:disabled {\n background: #f5f5f5;\n color: #999;\n}\n\n.form-textarea {\n resize: vertical;\n min-height: 80px;\n}\n\n/* Checkbox */\n.checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n cursor: pointer;\n}\n\n.checkbox-label input[type=\"checkbox\"] {\n width: 18px;\n height: 18px;\n margin-top: 2px;\n cursor: pointer;\n}\n\n.checkbox-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.checkbox-text strong {\n font-size: 14px;\n color: #333;\n}\n\n.checkbox-text small {\n font-size: 12px;\n color: #666;\n}\n\n/* Danger Zone */\n.danger-zone {\n padding-top: 16px;\n border-top: 1px solid #ffcdd2;\n}\n\n.danger-zone .section-header {\n color: #c62828;\n}\n\n.danger-zone .section-header i {\n color: #c62828;\n}\n\n.delete-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n border: 1px solid #c62828;\n background: transparent;\n border-radius: 6px;\n color: #c62828;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.delete-btn:hover {\n background: #c62828;\n color: white;\n}\n\n/* Panel Footer */\n.panel-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n background: #fafafa;\n flex-shrink: 0;\n}\n\n.footer-left {\n display: flex;\n gap: 8px;\n}\n\n.footer-right {\n display: flex;\n gap: 8px;\n}\n\n.footer-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n border: 1px solid #e0e0e0;\n background: white;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.footer-btn:hover:not(:disabled) {\n background: #f0f0f0;\n border-color: #ccc;\n}\n\n.footer-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.footer-btn.primary {\n background: #1976d2;\n border-color: #1976d2;\n color: white;\n}\n\n.footer-btn.primary:hover:not(:disabled) {\n background: #1565c0;\n border-color: #1565c0;\n}\n\n.footer-btn i {\n font-size: 12px;\n}\n\n/* ========================================\n FORMAT EDITOR INLINE STYLES\n ======================================== */\n\n.format-editor {\n padding: 0 20px;\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n}\n\n.format-editor .format-editor-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 0 16px 0;\n border-bottom: 1px solid #e0e0e0;\n margin-bottom: 16px;\n}\n\n.format-editor .back-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: #666;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.format-editor .back-btn:hover {\n background: #e0e0e0;\n color: #333;\n}\n\n.format-editor .format-editor-title {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n}\n\n/* Preview Table */\n.format-editor .preview-table {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 12px;\n}\n\n.format-editor .preview-header-cell {\n padding: 10px 14px;\n background: #f1f5f9;\n border-radius: 6px;\n font-weight: 600;\n font-size: 13px;\n color: #333;\n}\n\n.format-editor .preview-cell {\n padding: 8px 14px;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n font-size: 14px;\n color: #333;\n}\n\n/* Format Section */\n.format-section {\n margin-bottom: 20px;\n padding-bottom: 16px;\n border-bottom: 1px solid #f0f0f0;\n}\n\n.format-section:last-child {\n border-bottom: none;\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: #333;\n}\n\n.format-section-header i {\n color: #1976d2;\n font-size: 12px;\n}\n\n/* Format Row */\n.format-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 10px;\n}\n\n.format-row:last-child {\n margin-bottom: 0;\n}\n\n.format-row label {\n font-size: 13px;\n color: #555;\n flex-shrink: 0;\n}\n\n/* Format Inputs */\n.format-select {\n flex: 1;\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n background: white;\n cursor: pointer;\n min-width: 120px;\n}\n\n.format-select:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.format-input {\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 14px;\n background: white;\n}\n\n.format-input:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n.format-input.small {\n width: 80px;\n}\n\n/* Alias Info Row */\n.format-row.alias-info {\n justify-content: flex-start;\n gap: 8px;\n}\n\n.format-row.alias-info .muted-text {\n color: #888;\n font-size: 12px;\n}\n\n.clear-alias-btn {\n width: 20px;\n height: 20px;\n border: none;\n background: #f0f0f0;\n border-radius: 50%;\n color: #888;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n transition: all 0.15s ease;\n}\n\n.clear-alias-btn:hover {\n background: #e0e0e0;\n color: #c62828;\n}\n\n/* Color Input */\n.color-input {\n width: 50px;\n height: 32px;\n padding: 2px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n background: white;\n}\n\n.color-input:focus {\n outline: none;\n border-color: #1976d2;\n}\n\n/* Alignment Toggle */\n.alignment-toggle {\n display: flex;\n gap: 8px;\n}\n\n.align-btn {\n width: 40px;\n height: 36px;\n border: 2px solid #e5e7eb;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.align-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.align-btn.active {\n border-color: #1976d2;\n background: #e3f2fd;\n}\n\n.align-btn i {\n font-size: 14px;\n color: #64748b;\n}\n\n.align-btn.active i {\n color: #1976d2;\n}\n\n/* Style Buttons */\n.style-buttons {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.style-btn {\n width: 36px;\n height: 36px;\n border: 2px solid #e5e7eb;\n border-radius: 6px;\n background: white;\n cursor: pointer;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.style-btn:hover {\n border-color: #cbd5e1;\n background: #f8fafc;\n}\n\n.style-btn.active {\n border-color: #1976d2;\n background: #e3f2fd;\n}\n\n.style-btn i {\n font-size: 14px;\n color: #64748b;\n}\n\n.style-btn.active i {\n color: #1976d2;\n}\n\n/* Collapsible Format Sections */\n.format-section-collapsible {\n margin-bottom: 12px;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.format-section-collapsible summary {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 14px;\n background: #f8fafc;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: #333;\n list-style: none;\n}\n\n.format-section-collapsible summary::-webkit-details-marker {\n display: none;\n}\n\n.format-section-collapsible summary::before {\n content: '\\f054';\n font-family: 'Font Awesome 6 Free';\n font-weight: 900;\n font-size: 10px;\n color: #999;\n transition: transform 0.2s ease;\n}\n\n.format-section-collapsible[open] summary::before {\n transform: rotate(90deg);\n}\n\n.format-section-collapsible summary i {\n color: #1976d2;\n font-size: 12px;\n}\n\n.format-section-collapsible summary span {\n flex: 1;\n}\n\n.format-section-content {\n padding: 14px;\n background: white;\n border-top: 1px solid #e0e0e0;\n}\n\n/* Format Actions */\n.format-actions {\n margin-top: 20px;\n padding-top: 16px;\n border-top: 1px solid #e0e0e0;\n}\n\n.clear-format-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n border: 1px solid #e0e0e0;\n background: white;\n border-radius: 6px;\n color: #666;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.clear-format-btn:hover {\n background: #fff5f5;\n border-color: #f87171;\n color: #dc2626;\n}\n\n.clear-format-btn i {\n font-size: 12px;\n}\n\n/* ========================================\n AGGREGATES TAB STYLES\n ======================================== */\n\n.aggregates-section {\n padding-top: 0;\n}\n\n.aggregates-header {\n margin-bottom: 16px;\n}\n\n.aggregates-description {\n font-size: 13px;\n color: #666;\n line-height: 1.5;\n margin: 0;\n}\n\n/* Add Aggregate Button */\n.add-aggregate-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n border: 1px dashed #ccc;\n border-radius: 8px;\n background: transparent;\n color: #666;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n width: 100%;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.add-aggregate-btn:hover:not(:disabled) {\n border-color: #1976d2;\n background: #e3f2fd;\n color: #1976d2;\n}\n\n.add-aggregate-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.add-aggregate-btn i {\n font-size: 12px;\n}\n\n/* Aggregates List */\n.aggregates-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n/* Aggregate Item */\n.aggregate-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 14px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n transition: all 0.15s ease;\n}\n\n.aggregate-item:hover {\n border-color: #bdbdbd;\n background: #fafafa;\n}\n\n.aggregate-item.disabled {\n opacity: 0.6;\n background: #f5f5f5;\n}\n\n/* Aggregate Icon */\n.agg-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);\n border-radius: 8px;\n color: #1976d2;\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.aggregate-item.disabled .agg-icon {\n background: #f0f0f0;\n color: #999;\n}\n\n/* Aggregate Content */\n.agg-content {\n flex: 1;\n min-width: 0;\n}\n\n.agg-label {\n font-size: 14px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin-bottom: 2px;\n}\n\n.aggregate-item.disabled .agg-label {\n color: #888;\n}\n\n.agg-details {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #888;\n}\n\n.agg-type {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.agg-type i {\n font-size: 10px;\n}\n\n.agg-smart-badge {\n display: flex;\n align-items: center;\n padding: 2px 6px;\n background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);\n border-radius: 4px;\n color: #3b82f6;\n font-size: 10px;\n}\n\n/* Aggregate Actions */\n.agg-actions {\n display: flex;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.agg-action-btn {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n color: #999;\n transition: all 0.15s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.agg-action-btn:hover:not(:disabled) {\n background: #e0e0e0;\n color: #333;\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: #2e7d32;\n}\n\n.agg-action-btn.toggle-btn:not(.enabled) {\n color: #bdbdbd;\n}\n\n.agg-action-btn.edit-btn:hover:not(:disabled) {\n background: #e3f2fd;\n color: #1976d2;\n}\n\n.agg-action-btn.remove-btn:hover:not(:disabled) {\n background: #ffebee;\n color: #c62828;\n}\n\n/* Aggregates Summary */\n.aggregates-summary {\n display: flex;\n gap: 16px;\n padding: 12px 14px;\n background: #f5f7fa;\n border-radius: 6px;\n margin-bottom: 16px;\n}\n\n.summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: #666;\n}\n\n.summary-item i {\n color: #1976d2;\n font-size: 11px;\n}\n\n/* Aggregates Empty State */\n.aggregates-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px 20px;\n color: #999;\n font-size: 14px;\n text-align: center;\n}\n\n.aggregates-empty-state i {\n font-size: 48px;\n opacity: 0.4;\n margin-bottom: 8px;\n}\n\n.empty-hint {\n font-size: 12px;\n color: #bbb;\n margin: 0;\n}\n\n/* Aggregates Tip */\n.aggregates-tip {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 12px 14px;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n font-size: 12px;\n color: #64748b;\n line-height: 1.5;\n}\n\n.aggregates-tip i {\n color: #94a3b8;\n margin-top: 1px;\n flex-shrink: 0;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .config-panel {\n width: 100% !important;\n max-width: 100vw !important;\n }\n\n /* Hide resize handle on mobile - panel is full width */\n .resize-handle {\n display: none;\n }\n\n .tab-btn span {\n display: none;\n }\n\n .tab-btn {\n padding: 14px;\n }\n\n .tab-btn i {\n font-size: 16px;\n }\n\n .format-type-grid {\n grid-template-columns: repeat(3, 1fr);\n }\n}\n\n/* Medium screens - limit max resize width */\n@media (min-width: 769px) and (max-width: 1200px) {\n .config-panel {\n max-width: min(600px, 80vw);\n }\n}\n\n/* Validation Styles (BUG-004) */\n.form-input.invalid {\n border-color: #ef4444;\n}\n\n.validation-error {\n display: block;\n font-size: 12px;\n color: #ef4444;\n margin-top: 4px;\n}\n\n.validation-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: #fef2f2;\n border-top: 1px solid #fecaca;\n color: #dc2626;\n font-size: 13px;\n}\n\n.validation-banner i {\n flex-shrink: 0;\n}\n\n/* Duplicate Button (F-005) */\n.footer-btn.duplicate-btn {\n background: #f3f4f6;\n color: #374151;\n border: 1px solid #d1d5db;\n}\n\n.footer-btn.duplicate-btn:hover {\n background: #e5e7eb;\n border-color: #9ca3af;\n}\n"] }]
|
|
2821
2858
|
}], () => [{ type: i0.ChangeDetectorRef }], { entity: [{
|
|
2822
2859
|
type: Input
|
|
2823
2860
|
}], viewEntity: [{
|
|
@@ -2842,6 +2879,12 @@ export class ViewConfigPanelComponent {
|
|
|
2842
2879
|
type: Input
|
|
2843
2880
|
}], DefaultSaveAsNew: [{
|
|
2844
2881
|
type: Input
|
|
2882
|
+
}], PendingNewViewName: [{
|
|
2883
|
+
type: Input
|
|
2884
|
+
}], PendingNewViewDescription: [{
|
|
2885
|
+
type: Input
|
|
2886
|
+
}], PendingNewViewIsShared: [{
|
|
2887
|
+
type: Input
|
|
2845
2888
|
}], duplicate: [{
|
|
2846
2889
|
type: Output
|
|
2847
2890
|
}], isSaving: [{
|