@memberjunction/ng-dashboards 5.39.0 → 5.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +128 -4
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +548 -145
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/autotagging/components/classify-item-drilldown.component.d.ts +56 -0
- package/dist/AI/components/autotagging/components/classify-item-drilldown.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-item-drilldown.component.js +423 -0
- package/dist/AI/components/autotagging/components/classify-item-drilldown.component.js.map +1 -0
- package/dist/AI/components/autotagging/components/classify-item-grid.component.d.ts +70 -0
- package/dist/AI/components/autotagging/components/classify-item-grid.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-item-grid.component.js +308 -0
- package/dist/AI/components/autotagging/components/classify-item-grid.component.js.map +1 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.d.ts +29 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.js +186 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.js.map +1 -0
- package/dist/AI/components/autotagging/components/classify-overview-analytics.component.d.ts +69 -0
- package/dist/AI/components/autotagging/components/classify-overview-analytics.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-overview-analytics.component.js +278 -0
- package/dist/AI/components/autotagging/components/classify-overview-analytics.component.js.map +1 -0
- package/dist/AI/components/autotagging/components/classify-seed-taxonomy.component.d.ts +73 -0
- package/dist/AI/components/autotagging/components/classify-seed-taxonomy.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-seed-taxonomy.component.js +393 -0
- package/dist/AI/components/autotagging/components/classify-seed-taxonomy.component.js.map +1 -0
- package/dist/AI/components/autotagging/dialogs/classify-setup-wizard.component.d.ts +122 -0
- package/dist/AI/components/autotagging/dialogs/classify-setup-wizard.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/dialogs/classify-setup-wizard.component.js +908 -0
- package/dist/AI/components/autotagging/dialogs/classify-setup-wizard.component.js.map +1 -0
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts +100 -2
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js +603 -213
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js.map +1 -1
- package/dist/AI/components/autotagging/shared/classify.format.d.ts +15 -0
- package/dist/AI/components/autotagging/shared/classify.format.d.ts.map +1 -1
- package/dist/AI/components/autotagging/shared/classify.format.js +51 -0
- package/dist/AI/components/autotagging/shared/classify.format.js.map +1 -1
- package/dist/AI/components/autotagging/shared/classify.types.d.ts +43 -0
- package/dist/AI/components/autotagging/shared/classify.types.d.ts.map +1 -1
- package/dist/AI/components/autotagging/shared/classify.types.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/history-tab.component.d.ts +38 -1
- package/dist/AI/components/autotagging/tabs/history-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/history-tab.component.js +185 -68
- package/dist/AI/components/autotagging/tabs/history-tab.component.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/pipeline-tab.component.d.ts +10 -1
- package/dist/AI/components/autotagging/tabs/pipeline-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/pipeline-tab.component.js +249 -188
- package/dist/AI/components/autotagging/tabs/pipeline-tab.component.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts +12 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js +377 -296
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/tags-tab.component.d.ts +8 -0
- package/dist/AI/components/autotagging/tabs/tags-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/tags-tab.component.js +112 -68
- package/dist/AI/components/autotagging/tabs/tags-tab.component.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/types-tab.component.d.ts +9 -0
- package/dist/AI/components/autotagging/tabs/types-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/types-tab.component.js +87 -36
- package/dist/AI/components/autotagging/tabs/types-tab.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts +3 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +15 -3
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +1 -1
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.d.ts +1 -0
- package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.js +28 -6
- package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +3 -0
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +330 -302
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.js +2 -2
- package/dist/APIKeys/api-key-create-dialog.component.js +2 -2
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +31 -340
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +468 -1958
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-resource.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-resource.component.js +10 -0
- package/dist/DataExplorer/data-explorer-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +12 -9
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts +27 -2
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js +244 -120
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/visualize/record-drilldown/record-drilldown.component.d.ts +65 -0
- package/dist/KnowledgeHub/components/visualize/record-drilldown/record-drilldown.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/visualize/record-drilldown/record-drilldown.component.js +176 -0
- package/dist/KnowledgeHub/components/visualize/record-drilldown/record-drilldown.component.js.map +1 -0
- package/dist/KnowledgeHub/components/visualize/tag-cloud/tag-cloud.component.d.ts +81 -0
- package/dist/KnowledgeHub/components/visualize/tag-cloud/tag-cloud.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/visualize/tag-cloud/tag-cloud.component.js +308 -0
- package/dist/KnowledgeHub/components/visualize/tag-cloud/tag-cloud.component.js.map +1 -0
- package/dist/KnowledgeHub/components/visualize/visualize-resource.component.d.ts +85 -0
- package/dist/KnowledgeHub/components/visualize/visualize-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/visualize/visualize-resource.component.js +362 -0
- package/dist/KnowledgeHub/components/visualize/visualize-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/index.d.ts +3 -0
- package/dist/KnowledgeHub/index.d.ts.map +1 -1
- package/dist/KnowledgeHub/index.js +3 -0
- package/dist/KnowledgeHub/index.js.map +1 -1
- package/dist/MCP/components/mcp-server-dialog.component.js +2 -2
- package/dist/QueryBrowser/query-browser-resource.component.js +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/ai-dashboards.module.d.ts +48 -38
- package/dist/ai-dashboards.module.d.ts.map +1 -1
- package/dist/ai-dashboards.module.js +41 -1
- package/dist/ai-dashboards.module.js.map +1 -1
- package/dist/data-explorer-dashboards.module.d.ts +12 -14
- package/dist/data-explorer-dashboards.module.d.ts.map +1 -1
- package/dist/data-explorer-dashboards.module.js +5 -14
- package/dist/data-explorer-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +3 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +57 -55
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.d.ts +0 -79
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.d.ts.map +0 -1
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.js +0 -195
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.js.map +0 -1
- package/dist/DataExplorer/components/view-selector/view-selector.component.d.ts +0 -226
- package/dist/DataExplorer/components/view-selector/view-selector.component.d.ts.map +0 -1
- package/dist/DataExplorer/components/view-selector/view-selector.component.js +0 -861
- package/dist/DataExplorer/components/view-selector/view-selector.component.js.map +0 -1
|
@@ -1057,11 +1057,11 @@ export class APIApplicationsPanelComponent extends BaseAngularComponent {
|
|
|
1057
1057
|
i0.ɵɵconditional(ctx.IsSaving ? 61 : -1);
|
|
1058
1058
|
i0.ɵɵadvance();
|
|
1059
1059
|
i0.ɵɵconditional(!ctx.IsSaving ? 62 : -1);
|
|
1060
|
-
} }, dependencies: [i1.NgClass, i2.DefaultValueAccessor, i2.CheckboxControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.MJButtonDirective, i3.MJDropdownComponent, i4.LoadingComponent], styles: [".applications-panel[_ngcontent-%COMP%] {\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n}\n\n.applications-panel.panel-open[_ngcontent-%COMP%] {\n overflow: hidden;\n}\n\n\n\n.panel-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 24px;\n}\n\n.header-left[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.panel-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.panel-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.panel-subtitle[_ngcontent-%COMP%] {\n font-size: 14px;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.btn-create[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 8px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.btn-create[_ngcontent-%COMP%]:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n\n\n.message[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 14px;\n}\n\n.message.success[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.message.error[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n border: 1px solid var(--mj-status-error-border);\n}\n\n\n\n.applications-grid[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n flex: 1;\n overflow-y: auto;\n}\n\n.app-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n transition: box-shadow 0.2s ease;\n}\n\n.app-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-lg);\n}\n\n.app-card.inactive[_ngcontent-%COMP%] {\n opacity: 0.7;\n}\n\n.app-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 16px 20px;\n gap: 12px;\n}\n\n.app-header-main[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n flex: 1;\n cursor: pointer;\n transition: background 0.2s ease;\n border-radius: 8px;\n padding: 4px;\n margin: -4px;\n}\n\n.app-header-main[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.app-edit-btn[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.app-edit-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.app-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n border-radius: 10px;\n margin-right: 16px;\n flex-shrink: 0;\n}\n\n.app-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-text-inverse);\n}\n\n.app-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.app-name[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.status-badge[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.active[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.status-badge.inactive[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.app-description[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-stats[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-left: 16px;\n}\n\n.scope-count[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.scope-count[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.expand-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n transition: transform 0.2s ease;\n}\n\n\n\n.app-details[_ngcontent-%COMP%] {\n padding: 0 20px 20px 20px;\n border-top: 1px solid var(--mj-border-default);\n margin-top: 0;\n}\n\n.details-section[_ngcontent-%COMP%] {\n margin-top: 16px;\n}\n\n.details-section[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 12px 0;\n}\n\n.scope-rules[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.scope-rule[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n padding: 10px 12px;\n background: var(--mj-bg-page);\n border-radius: 8px;\n}\n\n.rule-icon[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n margin-right: 12px;\n flex-shrink: 0;\n}\n\n.rule-icon.pattern-include[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-600);\n}\n\n.rule-icon.pattern-exclude[_ngcontent-%COMP%] {\n background: var(--mj-color-brand-100);\n color: var(--mj-brand-primary-hover);\n}\n\n.rule-icon.pattern-deny[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-color-error-600);\n}\n\n.rule-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.rule-scope[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.rule-pattern[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.rule-pattern[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n font-family: monospace;\n font-size: 12px;\n background: var(--mj-bg-surface-active);\n padding: 2px 6px;\n border-radius: 4px;\n color: var(--mj-text-primary);\n}\n\n.pattern-type[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.priority[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-brand-primary);\n}\n\n.empty-scopes[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 24px;\n color: var(--mj-text-secondary);\n text-align: center;\n background: var(--mj-bg-page);\n border-radius: 8px;\n}\n\n.empty-scopes[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n margin-bottom: 8px;\n opacity: 0.5;\n}\n\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px;\n color: var(--mj-text-secondary);\n text-align: center;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n.empty-state[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 500;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 14px;\n margin-top: 8px;\n}\n\n\n\n\n\n\n\n\n.slideout-backdrop[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 100;\n animation: _ngcontent-%COMP%_fadeIn 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n\n\n.slideout-panel[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n right: 0;\n width: 570px;\n height: 100%;\n max-height: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay);\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n transform: translateX(100%);\n}\n\n.slideout-panel.open[_ngcontent-%COMP%] {\n transform: translateX(0);\n}\n\n\n\n.slideout-resize-handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n width: 6px;\n height: 100%;\n cursor: ew-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s ease;\n}\n\n.slideout-resize-handle[_ngcontent-%COMP%]:hover, \n.slideout-resize-handle.resizing[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.slideout-resize-handle[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 2px;\n height: 40px;\n background: var(--mj-color-neutral-300);\n border-radius: 2px;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.slideout-resize-handle[_ngcontent-%COMP%]:hover::after {\n opacity: 1;\n}\n\n\n\n.slideout-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n font-weight: 600;\n font-size: 17px;\n color: var(--mj-text-primary);\n flex: 1;\n min-width: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:first-of-type {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.status-pill[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n padding: 3px 10px;\n border-radius: 12px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n flex-shrink: 0;\n}\n\n.status-pill.active[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.slideout-close[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.slideout-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n\n\n.slideout-tabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n padding: 12px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-tab[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 18px;\n background: transparent;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.slideout-tab[_ngcontent-%COMP%]:hover {\n background: var(--mj-border-default);\n color: var(--mj-text-secondary);\n}\n\n.slideout-tab.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.slideout-tab[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.tab-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.slideout-tab[_ngcontent-%COMP%]:not(.active) .tab-badge[_ngcontent-%COMP%] {\n background: var(--mj-border-default); \n\n color: var(--mj-text-secondary);\n}\n\n\n\n.slideout-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n}\n\n.tab-panel[_ngcontent-%COMP%] {\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.scopes-panel[_ngcontent-%COMP%] {\n height: 100%;\n}\n\n\n\n.form-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n\n\n.form-field[_ngcontent-%COMP%] {\n margin-bottom: 20px;\n}\n\n.form-field[_ngcontent-%COMP%]:last-of-type {\n margin-bottom: 0;\n}\n\n.form-field[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n letter-spacing: 0.01em;\n}\n\n.form-input[_ngcontent-%COMP%] {\n width: 100%;\n}\n\n.form-textarea[_ngcontent-%COMP%] {\n width: 100%;\n resize: vertical;\n min-height: 80px;\n}\n\n[_nghost-%COMP%] .form-input .k-input, \n[_nghost-%COMP%] .form-textarea .k-input-inner {\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 14px;\n border-color: var(--mj-border-default);\n transition: all 0.2s ease;\n}\n\n[_nghost-%COMP%] .form-input:focus-within, \n[_nghost-%COMP%] .form-textarea:focus-within {\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n\n\n.slideout-footer[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default); \n\n flex-shrink: 0;\n}\n\n[_nghost-%COMP%] .slideout-footer .k-button {\n min-width: 100px;\n padding: 10px 20px;\n font-weight: 600;\n border-radius: 8px;\n transition: all 0.2s ease;\n}\n\n[_nghost-%COMP%] .slideout-footer .k-button-solid-primary {\n background: var(--mj-brand-primary);\n border: none;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n[_nghost-%COMP%] .slideout-footer .k-button-solid-primary:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.checkbox-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n cursor: pointer;\n padding: 12px 16px;\n background: var(--mj-bg-page);\n border-radius: 10px;\n border: 1px solid var(--mj-border-default);\n transition: all 0.2s ease;\n}\n\n.checkbox-label[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-color-neutral-300);\n}\n\n.checkbox-label[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.checkbox-label[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:first-of-type {\n font-weight: 600;\n color: var(--mj-text-secondary);\n font-size: 14px;\n}\n\n.checkbox-hint[_ngcontent-%COMP%] {\n display: block;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n line-height: 1.4;\n}\n\n\n\n\n\n\n\n\n\n.scopes-intro[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-status-info-bg);\n border: 1px solid var(--mj-status-info-border);\n border-radius: 10px;\n font-size: 13px;\n color: var(--mj-status-info-text);\n margin-bottom: 16px;\n line-height: 1.4;\n}\n\n.scopes-intro[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-info);\n font-size: 14px;\n flex-shrink: 0;\n margin-top: 1px;\n}\n\n.scope-categories-list[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.scope-category-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-page);\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-left[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.category-count-badge[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-sunken);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n}\n\n.category-all-toggle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.category-chevron[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 11px;\n}\n\n.category-scopes-list[_ngcontent-%COMP%] {\n border-top: 1px solid var(--mj-border-default); \n\n padding: 8px;\n max-height: 280px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.scope-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--mj-bg-page);\n transition: all 0.2s ease;\n}\n\n.scope-row[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.scope-row.selected[_ngcontent-%COMP%] {\n background: var(--mj-color-violet-100);\n border: 1px solid var(--mj-color-violet-300);\n}\n\n.scope-select[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n flex: 1;\n min-width: 0;\n cursor: pointer;\n}\n\n.scope-select[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.scope-label[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.scope-path[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-primary);\n font-family: 'SF Mono', 'Consolas', monospace;\n word-break: break-word;\n}\n\n.scope-description[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n\n\n.scope-pattern-display[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.pattern-tag[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 4px;\n font-family: 'SF Mono', 'Consolas', monospace;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.pattern-tag.include[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.pattern-tag.exclude[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\n\n.pattern-tag[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.scope-pattern-config[_ngcontent-%COMP%] {\n display: none;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\n.scope-row[_ngcontent-%COMP%]:hover .scope-pattern-config[_ngcontent-%COMP%], \n.scope-row.editing[_ngcontent-%COMP%] .scope-pattern-config[_ngcontent-%COMP%] {\n display: flex;\n}\n\n.scope-row[_ngcontent-%COMP%]:hover .scope-pattern-display[_ngcontent-%COMP%], \n.scope-row.editing[_ngcontent-%COMP%] .scope-pattern-display[_ngcontent-%COMP%] {\n display: none;\n}\n\n.pattern-field[_ngcontent-%COMP%] {\n width: 60px;\n}\n\n[_nghost-%COMP%] .pattern-field .k-input {\n padding: 3px 6px;\n font-size: 11px;\n border-radius: 4px;\n height: 26px;\n}\n\n.type-field[_ngcontent-%COMP%] {\n width: 75px;\n}\n\n[_nghost-%COMP%] .type-field .k-dropdownlist {\n font-size: 11px;\n height: 26px;\n}\n\n[_nghost-%COMP%] .type-field .k-input-inner {\n padding: 2px 6px;\n font-size: 11px;\n}\n\n.empty-scopes-message[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 32px;\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n.empty-scopes-message[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n margin-bottom: 12px;\n opacity: 0.5;\n}\n\n\n"] });
|
|
1060
|
+
} }, dependencies: [i1.NgClass, i2.DefaultValueAccessor, i2.CheckboxControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.MJButtonDirective, i3.MJDropdownComponent, i4.LoadingComponent], styles: [".applications-panel[_ngcontent-%COMP%] {\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n}\n\n.applications-panel.panel-open[_ngcontent-%COMP%] {\n overflow: hidden;\n}\n\n\n\n.panel-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 24px;\n}\n\n.header-left[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.panel-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.panel-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.panel-subtitle[_ngcontent-%COMP%] {\n font-size: 14px;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.btn-create[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 8px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.btn-create[_ngcontent-%COMP%]:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n\n\n.message[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 14px;\n}\n\n.message.success[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.message.error[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n border: 1px solid var(--mj-status-error-border);\n}\n\n\n\n.applications-grid[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n flex: 1;\n overflow-y: auto;\n}\n\n.app-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n transition: box-shadow 0.2s ease;\n}\n\n.app-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-lg);\n}\n\n.app-card.inactive[_ngcontent-%COMP%] {\n opacity: 0.7;\n}\n\n.app-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 16px 20px;\n gap: 12px;\n}\n\n.app-header-main[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n flex: 1;\n cursor: pointer;\n transition: background 0.2s ease;\n border-radius: 8px;\n padding: 4px;\n margin: -4px;\n}\n\n.app-header-main[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.app-edit-btn[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.app-edit-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.app-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n border-radius: 10px;\n margin-right: 16px;\n flex-shrink: 0;\n}\n\n.app-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-text-inverse);\n}\n\n.app-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.app-name[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.status-badge[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.active[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.status-badge.inactive[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.app-description[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-stats[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-left: 16px;\n}\n\n.scope-count[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.scope-count[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.expand-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n transition: transform 0.2s ease;\n}\n\n\n\n.app-details[_ngcontent-%COMP%] {\n padding: 0 20px 20px 20px;\n border-top: 1px solid var(--mj-border-default);\n margin-top: 0;\n}\n\n.details-section[_ngcontent-%COMP%] {\n margin-top: 16px;\n}\n\n.details-section[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 12px 0;\n}\n\n.scope-rules[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.scope-rule[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n padding: 10px 12px;\n background: var(--mj-bg-page);\n border-radius: 8px;\n}\n\n.rule-icon[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n margin-right: 12px;\n flex-shrink: 0;\n}\n\n.rule-icon.pattern-include[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-600);\n}\n\n.rule-icon.pattern-exclude[_ngcontent-%COMP%] {\n background: var(--mj-color-brand-100);\n color: var(--mj-brand-primary-hover);\n}\n\n.rule-icon.pattern-deny[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-color-error-600);\n}\n\n.rule-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.rule-scope[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.rule-pattern[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.rule-pattern[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n font-family: monospace;\n font-size: 12px;\n background: var(--mj-bg-surface-active);\n padding: 2px 6px;\n border-radius: 4px;\n color: var(--mj-text-primary);\n}\n\n.pattern-type[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.priority[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-brand-primary);\n}\n\n.empty-scopes[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 24px;\n color: var(--mj-text-secondary);\n text-align: center;\n background: var(--mj-bg-page);\n border-radius: 8px;\n}\n\n.empty-scopes[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n margin-bottom: 8px;\n opacity: 0.5;\n}\n\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px;\n color: var(--mj-text-secondary);\n text-align: center;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n.empty-state[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 500;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 14px;\n margin-top: 8px;\n}\n\n\n\n\n\n\n\n\n.slideout-backdrop[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 100;\n animation: _ngcontent-%COMP%_fadeIn 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n\n\n.slideout-panel[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n right: 0;\n width: 570px;\n height: 100%;\n max-height: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay);\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n transform: translateX(100%);\n}\n\n.slideout-panel.open[_ngcontent-%COMP%] {\n transform: translateX(0);\n}\n\n\n\n.slideout-resize-handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n width: 6px;\n height: 100%;\n cursor: ew-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s ease;\n}\n\n.slideout-resize-handle[_ngcontent-%COMP%]:hover, \n.slideout-resize-handle.resizing[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.slideout-resize-handle[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 2px;\n height: 40px;\n background: var(--mj-color-neutral-300);\n border-radius: 2px;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.slideout-resize-handle[_ngcontent-%COMP%]:hover::after {\n opacity: 1;\n}\n\n\n\n.slideout-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n font-weight: 600;\n font-size: 17px;\n color: var(--mj-text-primary);\n flex: 1;\n min-width: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:first-of-type {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.status-pill[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n padding: 3px 10px;\n border-radius: 12px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n flex-shrink: 0;\n}\n\n.status-pill.active[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.slideout-close[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.slideout-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n\n\n.slideout-tabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n padding: 12px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-tab[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 18px;\n background: transparent;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.slideout-tab[_ngcontent-%COMP%]:hover {\n background: var(--mj-border-default);\n color: var(--mj-text-secondary);\n}\n\n.slideout-tab.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.slideout-tab[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.tab-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.slideout-tab[_ngcontent-%COMP%]:not(.active) .tab-badge[_ngcontent-%COMP%] {\n background: var(--mj-border-default); \n\n color: var(--mj-text-secondary);\n}\n\n\n\n.slideout-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n}\n\n.tab-panel[_ngcontent-%COMP%] {\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.scopes-panel[_ngcontent-%COMP%] {\n height: 100%;\n}\n\n\n\n.form-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n\n\n.form-field[_ngcontent-%COMP%] {\n margin-bottom: 20px;\n}\n\n.form-field[_ngcontent-%COMP%]:last-of-type {\n margin-bottom: 0;\n}\n\n.form-field[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n letter-spacing: 0.01em;\n}\n\n.form-input[_ngcontent-%COMP%] {\n width: 100%;\n}\n\n.form-textarea[_ngcontent-%COMP%] {\n width: 100%;\n resize: vertical;\n min-height: 80px;\n}\n\n[_nghost-%COMP%] .form-input:focus-within, \n[_nghost-%COMP%] .form-textarea:focus-within {\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n\n\n.slideout-footer[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default); \n\n flex-shrink: 0;\n}\n\n.checkbox-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n cursor: pointer;\n padding: 12px 16px;\n background: var(--mj-bg-page);\n border-radius: 10px;\n border: 1px solid var(--mj-border-default);\n transition: all 0.2s ease;\n}\n\n.checkbox-label[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-color-neutral-300);\n}\n\n.checkbox-label[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.checkbox-label[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:first-of-type {\n font-weight: 600;\n color: var(--mj-text-secondary);\n font-size: 14px;\n}\n\n.checkbox-hint[_ngcontent-%COMP%] {\n display: block;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n line-height: 1.4;\n}\n\n\n\n\n\n\n\n\n\n.scopes-intro[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-status-info-bg);\n border: 1px solid var(--mj-status-info-border);\n border-radius: 10px;\n font-size: 13px;\n color: var(--mj-status-info-text);\n margin-bottom: 16px;\n line-height: 1.4;\n}\n\n.scopes-intro[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-info);\n font-size: 14px;\n flex-shrink: 0;\n margin-top: 1px;\n}\n\n.scope-categories-list[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.scope-category-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-page);\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-left[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.category-count-badge[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-sunken);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.scope-category-card[_ngcontent-%COMP%] .category-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n}\n\n.category-all-toggle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.category-chevron[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 11px;\n}\n\n.category-scopes-list[_ngcontent-%COMP%] {\n border-top: 1px solid var(--mj-border-default); \n\n padding: 8px;\n max-height: 280px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.scope-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--mj-bg-page);\n transition: all 0.2s ease;\n}\n\n.scope-row[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.scope-row.selected[_ngcontent-%COMP%] {\n background: var(--mj-color-violet-100);\n border: 1px solid var(--mj-color-violet-300);\n}\n\n.scope-select[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n flex: 1;\n min-width: 0;\n cursor: pointer;\n}\n\n.scope-select[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.scope-label[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.scope-path[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-primary);\n font-family: 'SF Mono', 'Consolas', monospace;\n word-break: break-word;\n}\n\n.scope-description[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n\n\n.scope-pattern-display[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.pattern-tag[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 4px;\n font-family: 'SF Mono', 'Consolas', monospace;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.pattern-tag.include[_ngcontent-%COMP%] {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.pattern-tag.exclude[_ngcontent-%COMP%] {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\n\n.pattern-tag[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.scope-pattern-config[_ngcontent-%COMP%] {\n display: none;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\n.scope-row[_ngcontent-%COMP%]:hover .scope-pattern-config[_ngcontent-%COMP%], \n.scope-row.editing[_ngcontent-%COMP%] .scope-pattern-config[_ngcontent-%COMP%] {\n display: flex;\n}\n\n.scope-row[_ngcontent-%COMP%]:hover .scope-pattern-display[_ngcontent-%COMP%], \n.scope-row.editing[_ngcontent-%COMP%] .scope-pattern-display[_ngcontent-%COMP%] {\n display: none;\n}\n\n.pattern-field[_ngcontent-%COMP%] {\n width: 60px;\n}\n\n.type-field[_ngcontent-%COMP%] {\n width: 75px;\n}\n\n.empty-scopes-message[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 32px;\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n.empty-scopes-message[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n margin-bottom: 12px;\n opacity: 0.5;\n}\n\n\n"] });
|
|
1061
1061
|
}
|
|
1062
1062
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(APIApplicationsPanelComponent, [{
|
|
1063
1063
|
type: Component,
|
|
1064
|
-
args: [{ standalone: false, selector: 'mj-api-applications-panel', template: "<div class=\"applications-panel\" [class.panel-open]=\"ShowEditPanel || ShowCreatePanel\">\n @if (IsLoading) {\n <mj-loading text=\"Loading applications...\"></mj-loading>\n }\n\n @if (!IsLoading) {\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"header-left\">\n <h3 class=\"panel-title\">\n <i class=\"fa-solid fa-cube\"></i>\n API Applications\n </h3>\n <p class=\"panel-subtitle\">Manage applications and their default scope permissions</p>\n </div>\n <button class=\"btn-create\" (click)=\"openCreatePanel()\">\n <i class=\"fa-solid fa-plus\"></i>\n New Application\n </button>\n </div>\n <!-- Messages -->\n @if (SuccessMessage) {\n <div class=\"message success\">\n <i class=\"fa-solid fa-check-circle\"></i>\n {{SuccessMessage}}\n </div>\n }\n @if (ErrorMessage) {\n <div class=\"message error\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n {{ErrorMessage}}\n </div>\n }\n <!-- Applications Grid -->\n <div class=\"applications-grid\">\n @for (appItem of Applications; track appItem) {\n <div class=\"app-card\"\n [class.inactive]=\"!appItem.application.IsActive\"\n [class.expanded]=\"appItem.expanded\">\n <div class=\"app-header\">\n <div class=\"app-header-main\" (click)=\"toggleExpanded(appItem)\">\n <div class=\"app-icon\">\n <i class=\"fa-solid fa-cube\"></i>\n </div>\n <div class=\"app-info\">\n <div class=\"app-name\">\n {{appItem.application.Name}}\n <span class=\"status-badge\" [class.active]=\"appItem.application.IsActive\"\n [class.inactive]=\"!appItem.application.IsActive\">\n {{appItem.application.IsActive ? 'Active' : 'Inactive'}}\n </span>\n </div>\n @if (appItem.application.Description) {\n <div class=\"app-description\">\n {{appItem.application.Description}}\n </div>\n }\n </div>\n <div class=\"app-stats\">\n <div class=\"scope-count\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n {{appItem.scopeCount}} scopes\n </div>\n <i class=\"fa-solid expand-icon\"\n [class.fa-chevron-down]=\"!appItem.expanded\"\n [class.fa-chevron-up]=\"appItem.expanded\"></i>\n </div>\n </div>\n <button class=\"app-edit-btn\" (click)=\"openEditPanel(appItem); $event.stopPropagation()\" title=\"Edit Application\">\n <i class=\"fa-solid fa-pencil\"></i>\n </button>\n </div>\n <!-- Expanded Content -->\n @if (appItem.expanded) {\n <div class=\"app-details\">\n <div class=\"details-section\">\n <h4>Scope Ceiling Rules</h4>\n @if (appItem.scopes.length > 0) {\n <div class=\"scope-rules\">\n @for (scope of appItem.scopes; track scope) {\n <div class=\"scope-rule\">\n <div class=\"rule-icon\" [ngClass]=\"getPatternClass(scope.PatternType, scope.IsDeny)\">\n <i [class]=\"getPatternIcon(scope.PatternType, scope.IsDeny)\"></i>\n </div>\n <div class=\"rule-info\">\n <div class=\"rule-scope\">{{getScopeName(scope.ScopeID)}}</div>\n <div class=\"rule-pattern\">\n <code>{{scope.ResourcePattern}}</code>\n <span class=\"pattern-type\">{{scope.PatternType}}</span>\n @if (scope.Priority > 0) {\n <span class=\"priority\">\n Priority: {{scope.Priority}}\n </span>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n @if (appItem.scopes.length === 0) {\n <div class=\"empty-scopes\">\n <i class=\"fa-solid fa-shield-xmark\"></i>\n <span>No scope rules defined - all access denied by default</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (Applications.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>No applications configured</span>\n <p>Create an application to define scope ceilings for API key access</p>\n </div>\n }\n </div>\n }\n\n <!-- Slide-out Backdrop -->\n @if (ShowEditPanel || ShowCreatePanel) {\n <div class=\"slideout-backdrop\" (click)=\"closePanel()\"></div>\n }\n\n <!-- Create Panel (Slide-out) -->\n <div class=\"slideout-panel\" [class.open]=\"ShowCreatePanel\">\n <div class=\"slideout-header\">\n <div class=\"slideout-title\">\n <i class=\"fa-solid fa-plus-circle\"></i>\n <span>New Application</span>\n </div>\n <button class=\"slideout-close\" (click)=\"closePanel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <div class=\"slideout-content\">\n <div class=\"form-section\">\n <div class=\"form-field\">\n <label>Application Name *</label>\n <input class=\"mj-input\" [(ngModel)]=\"EditName\"\n placeholder=\"e.g., MJAPI, MCP Server, Portal\"\n class=\"form-input\" />\n </div>\n\n <div class=\"form-field\">\n <label>Description</label>\n <textarea class=\"mj-textarea\" [(ngModel)]=\"EditDescription\"\n placeholder=\"Describe the application's purpose...\"\n [rows]=\"3\"\n class=\"form-textarea\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" class=\"mj-checkbox\" [(ngModel)]=\"EditIsActive\" />\n <div>\n <span>Active</span>\n <span class=\"checkbox-hint\">Inactive applications cannot be used with API keys</span>\n </div>\n </label>\n </div>\n </div>\n </div>\n\n <div class=\"slideout-footer\">\n <button mjButton variant=\"primary\"\n [disabled]=\"!EditName.trim() || IsSaving\"\n (click)=\"saveApplication()\">\n @if (IsSaving) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n }\n @if (!IsSaving) {\n <span>\n <i class=\"fa-solid fa-save\"></i>\n Create Application\n </span>\n }\n </button>\n <button mjButton (click)=\"closePanel()\">Cancel</button>\n </div>\n </div>\n\n <!-- Edit Panel (Slide-out) -->\n <div class=\"slideout-panel\" [class.open]=\"ShowEditPanel\" [style.width.px]=\"PanelWidth\">\n <!-- Resize Handle -->\n <div class=\"slideout-resize-handle\"\n [class.resizing]=\"IsResizing\"\n (mousedown)=\"startResize($event)\"></div>\n <div class=\"slideout-header\">\n <div class=\"slideout-title\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>{{EditingApplication?.Name}}</span>\n <span class=\"status-pill\" [class.active]=\"EditIsActive\">\n {{EditIsActive ? 'Active' : 'Inactive'}}\n </span>\n </div>\n <button class=\"slideout-close\" (click)=\"closePanel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Tab Bar -->\n <div class=\"slideout-tabs\">\n <button class=\"slideout-tab\" [class.active]=\"EditTab === 'details'\"\n (click)=\"EditTab = 'details'\">\n <i class=\"fa-solid fa-info-circle\"></i>\n Details\n </button>\n <button class=\"slideout-tab\" [class.active]=\"EditTab === 'scopes'\"\n (click)=\"EditTab = 'scopes'\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n Scopes\n <span class=\"tab-badge\">{{getSelectedScopeCount()}}</span>\n </button>\n </div>\n\n <div class=\"slideout-content\">\n <!-- Details Tab -->\n @if (EditTab === 'details') {\n <div class=\"tab-panel\">\n <div class=\"form-section\">\n <div class=\"form-field\">\n <label>Application Name *</label>\n <input class=\"mj-input\" [(ngModel)]=\"EditName\"\n placeholder=\"e.g., MJAPI, MCP Server, Portal\"\n class=\"form-input\" />\n </div>\n <div class=\"form-field\">\n <label>Description</label>\n <textarea class=\"mj-textarea\" [(ngModel)]=\"EditDescription\"\n placeholder=\"Describe the application's purpose...\"\n [rows]=\"4\"\n class=\"form-textarea\"></textarea>\n </div>\n <div class=\"form-field\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" class=\"mj-checkbox\" [(ngModel)]=\"EditIsActive\" />\n <div>\n <span>Active</span>\n <span class=\"checkbox-hint\">Inactive applications cannot be used with API keys</span>\n </div>\n </label>\n </div>\n </div>\n </div>\n }\n\n <!-- Scopes Tab -->\n @if (EditTab === 'scopes') {\n <div class=\"tab-panel scopes-panel\">\n <div class=\"scopes-intro\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Define the maximum permissions this application can grant to API keys.</span>\n </div>\n <div class=\"scope-categories-list\">\n @for (category of ScopeCategories; track category) {\n <div class=\"scope-category-card\">\n <div class=\"category-header\" (click)=\"toggleScopeCategory(category)\">\n <div class=\"category-left\">\n <i [class]=\"category.icon\" [style.color]=\"category.color\"></i>\n <span class=\"category-name\">{{category.name}}</span>\n <span class=\"category-count-badge\">\n {{getCategorySelectedCount(category)}}/{{category.scopes.length}}\n </span>\n </div>\n <div class=\"category-right\">\n <label class=\"category-all-toggle\" (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [checked]=\"category.allSelected\"\n (change)=\"toggleCategoryAll(category)\" />\n <span>All</span>\n </label>\n <i class=\"fa-solid category-chevron\"\n [class.fa-chevron-down]=\"!category.expanded\"\n [class.fa-chevron-up]=\"category.expanded\"></i>\n </div>\n </div>\n @if (category.expanded) {\n <div class=\"category-scopes-list\">\n @for (selection of category.scopes; track selection) {\n <div class=\"scope-row\"\n [class.selected]=\"selection.selected\">\n <label class=\"scope-select\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [(ngModel)]=\"selection.selected\"\n (change)=\"updateCategoryState(category)\" />\n <div class=\"scope-label\">\n <span class=\"scope-path\">{{selection.displayName || '(unnamed scope)'}}</span>\n @if (selection.scope.Description) {\n <span class=\"scope-description\">\n {{selection.scope.Description}}\n </span>\n }\n </div>\n </label>\n <!-- Read-only pattern display -->\n @if (selection.selected) {\n <div class=\"scope-pattern-display\">\n <span class=\"pattern-tag\" [class.include]=\"selection.patternType === 'Include'\"\n [class.exclude]=\"selection.patternType === 'Exclude'\">\n <i class=\"fa-solid\" [class.fa-check]=\"selection.patternType === 'Include'\"\n [class.fa-minus]=\"selection.patternType === 'Exclude'\"></i>\n {{selection.pattern || '*'}}\n </span>\n </div>\n }\n <!-- Editable pattern config - shown on hover -->\n @if (selection.selected) {\n <div class=\"scope-pattern-config\">\n <input class=\"mj-input\" [(ngModel)]=\"selection.pattern\"\n placeholder=\"*\"\n class=\"pattern-field\"\n title=\"Resource Pattern\" />\n <mj-dropdown [(ngModel)]=\"selection.patternType\"\n [Data]=\"['Include', 'Exclude']\"\n [ValuePrimitive]=\"true\"\n class=\"type-field\"\n title=\"Pattern Type\">\n </mj-dropdown>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n @if (ScopeCategories.length === 0) {\n <div class=\"empty-scopes-message\">\n <i class=\"fa-solid fa-shield-xmark\"></i>\n <span>No scopes available</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <div class=\"slideout-footer\">\n <button mjButton variant=\"primary\"\n [disabled]=\"!EditName.trim() || IsSaving\"\n (click)=\"saveAll()\">\n @if (IsSaving) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n }\n @if (!IsSaving) {\n <span>\n <i class=\"fa-solid fa-save\"></i>\n Save Changes\n </span>\n }\n </button>\n <button mjButton (click)=\"closePanel()\">Cancel</button>\n </div>\n </div>\n</div>\n", styles: [".applications-panel {\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n}\n\n.applications-panel.panel-open {\n overflow: hidden;\n}\n\n/* Panel Header */\n.panel-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 24px;\n}\n\n.header-left {\n flex: 1;\n}\n\n.panel-title {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.panel-title i {\n color: var(--mj-brand-primary);\n}\n\n.panel-subtitle {\n font-size: 14px;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.btn-create {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 8px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.btn-create:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n/* Messages */\n.message {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 14px;\n}\n\n.message.success {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.message.error {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n border: 1px solid var(--mj-status-error-border);\n}\n\n/* Applications Grid */\n.applications-grid {\n display: flex;\n flex-direction: column;\n gap: 16px;\n flex: 1;\n overflow-y: auto;\n}\n\n.app-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n transition: box-shadow 0.2s ease;\n}\n\n.app-card:hover {\n box-shadow: var(--mj-shadow-lg);\n}\n\n.app-card.inactive {\n opacity: 0.7;\n}\n\n.app-header {\n display: flex;\n align-items: center;\n padding: 16px 20px;\n gap: 12px;\n}\n\n.app-header-main {\n display: flex;\n align-items: center;\n flex: 1;\n cursor: pointer;\n transition: background 0.2s ease;\n border-radius: 8px;\n padding: 4px;\n margin: -4px;\n}\n\n.app-header-main:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.app-edit-btn {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.app-edit-btn:hover {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.app-icon {\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n border-radius: 10px;\n margin-right: 16px;\n flex-shrink: 0;\n}\n\n.app-icon i {\n font-size: 20px;\n color: var(--mj-text-inverse);\n}\n\n.app-info {\n flex: 1;\n min-width: 0;\n}\n\n.app-name {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.status-badge {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.active {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.status-badge.inactive {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.app-description {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-stats {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-left: 16px;\n}\n\n.scope-count {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.scope-count i {\n color: var(--mj-brand-primary);\n}\n\n.expand-icon {\n color: var(--mj-text-muted);\n transition: transform 0.2s ease;\n}\n\n/* Expanded Details */\n.app-details {\n padding: 0 20px 20px 20px;\n border-top: 1px solid var(--mj-border-default);\n margin-top: 0;\n}\n\n.details-section {\n margin-top: 16px;\n}\n\n.details-section h4 {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 12px 0;\n}\n\n.scope-rules {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.scope-rule {\n display: flex;\n align-items: flex-start;\n padding: 10px 12px;\n background: var(--mj-bg-page);\n border-radius: 8px;\n}\n\n.rule-icon {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n margin-right: 12px;\n flex-shrink: 0;\n}\n\n.rule-icon.pattern-include {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-600);\n}\n\n.rule-icon.pattern-exclude {\n background: var(--mj-color-brand-100);\n color: var(--mj-brand-primary-hover);\n}\n\n.rule-icon.pattern-deny {\n background: var(--mj-color-error-100);\n color: var(--mj-color-error-600);\n}\n\n.rule-info {\n flex: 1;\n min-width: 0;\n}\n\n.rule-scope {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.rule-pattern {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.rule-pattern code {\n font-family: monospace;\n font-size: 12px;\n background: var(--mj-bg-surface-active);\n padding: 2px 6px;\n border-radius: 4px;\n color: var(--mj-text-primary);\n}\n\n.pattern-type {\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.priority {\n font-size: 11px;\n color: var(--mj-brand-primary);\n}\n\n.empty-scopes {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 24px;\n color: var(--mj-text-secondary);\n text-align: center;\n background: var(--mj-bg-page);\n border-radius: 8px;\n}\n\n.empty-scopes i {\n font-size: 24px;\n margin-bottom: 8px;\n opacity: 0.5;\n}\n\n/* Legacy .details-actions removed - buttons now in header */\n\n/* Empty State */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px;\n color: var(--mj-text-secondary);\n text-align: center;\n}\n\n.empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n.empty-state span {\n font-size: 16px;\n font-weight: 500;\n}\n\n.empty-state p {\n font-size: 14px;\n margin-top: 8px;\n}\n\n/* ========================================\n Slide-out Panel Styles\n ======================================== */\n\n/* Backdrop */\n.slideout-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 100;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Slide-out Panel */\n.slideout-panel {\n position: absolute;\n top: 0;\n right: 0;\n width: 570px;\n height: 100%;\n max-height: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay);\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n transform: translateX(100%);\n}\n\n.slideout-panel.open {\n transform: translateX(0);\n}\n\n/* Resize handle */\n.slideout-resize-handle {\n position: absolute;\n top: 0;\n left: 0;\n width: 6px;\n height: 100%;\n cursor: ew-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s ease;\n}\n\n.slideout-resize-handle:hover,\n.slideout-resize-handle.resizing {\n background: color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.slideout-resize-handle::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 2px;\n height: 40px;\n background: var(--mj-color-neutral-300);\n border-radius: 2px;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.slideout-resize-handle:hover::after {\n opacity: 1;\n}\n\n/* Panel Header */\n.slideout-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-title {\n display: flex;\n align-items: center;\n gap: 12px;\n font-weight: 600;\n font-size: 17px;\n color: var(--mj-text-primary);\n flex: 1;\n min-width: 0;\n}\n\n.slideout-title i {\n color: var(--mj-brand-primary);\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.slideout-title span:first-of-type {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.status-pill {\n font-size: 11px;\n font-weight: 500;\n padding: 3px 10px;\n border-radius: 12px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n flex-shrink: 0;\n}\n\n.status-pill.active {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.slideout-close {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.slideout-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n/* Panel Tabs */\n.slideout-tabs {\n display: flex;\n gap: 4px;\n padding: 12px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-tab {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 18px;\n background: transparent;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.slideout-tab:hover {\n background: var(--mj-border-default);\n color: var(--mj-text-secondary);\n}\n\n.slideout-tab.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.slideout-tab i {\n font-size: 14px;\n}\n\n.tab-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.slideout-tab:not(.active) .tab-badge {\n background: var(--mj-border-default); /* tab badge neutral bg */\n color: var(--mj-text-secondary);\n}\n\n/* Panel Content */\n.slideout-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n}\n\n.tab-panel {\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.scopes-panel {\n height: 100%;\n}\n\n/* Form Section */\n.form-section {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n/* Form Fields */\n.form-field {\n margin-bottom: 20px;\n}\n\n.form-field:last-of-type {\n margin-bottom: 0;\n}\n\n.form-field label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n letter-spacing: 0.01em;\n}\n\n.form-input {\n width: 100%;\n}\n\n.form-textarea {\n width: 100%;\n resize: vertical;\n min-height: 80px;\n}\n\n:host ::ng-deep .form-input .k-input,\n:host ::ng-deep .form-textarea .k-input-inner {\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 14px;\n border-color: var(--mj-border-default);\n transition: all 0.2s ease;\n}\n\n:host ::ng-deep .form-input:focus-within,\n:host ::ng-deep .form-textarea:focus-within {\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n/* Slideout Footer */\n.slideout-footer {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default); /* category/slideout separator */\n flex-shrink: 0;\n}\n\n:host ::ng-deep .slideout-footer .k-button {\n min-width: 100px;\n padding: 10px 20px;\n font-weight: 600;\n border-radius: 8px;\n transition: all 0.2s ease;\n}\n\n:host ::ng-deep .slideout-footer .k-button-solid-primary {\n background: var(--mj-brand-primary);\n border: none;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n:host ::ng-deep .slideout-footer .k-button-solid-primary:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n cursor: pointer;\n padding: 12px 16px;\n background: var(--mj-bg-page);\n border-radius: 10px;\n border: 1px solid var(--mj-border-default);\n transition: all 0.2s ease;\n}\n\n.checkbox-label:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-color-neutral-300);\n}\n\n.checkbox-label input {\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.checkbox-label span:first-of-type {\n font-weight: 600;\n color: var(--mj-text-secondary);\n font-size: 14px;\n}\n\n.checkbox-hint {\n display: block;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n line-height: 1.4;\n}\n\n/* Legacy dialog-actions removed - now using slideout-footer */\n\n/* ========================================\n Scopes Panel - Slide-out Layout\n ======================================== */\n\n.scopes-intro {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-status-info-bg);\n border: 1px solid var(--mj-status-info-border);\n border-radius: 10px;\n font-size: 13px;\n color: var(--mj-status-info-text);\n margin-bottom: 16px;\n line-height: 1.4;\n}\n\n.scopes-intro i {\n color: var(--mj-status-info);\n font-size: 14px;\n flex-shrink: 0;\n margin-top: 1px;\n}\n\n.scope-categories-list {\n flex: 1;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.scope-category-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.scope-category-card .category-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.scope-category-card .category-header:hover {\n background: var(--mj-bg-page);\n}\n\n.scope-category-card .category-left {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.scope-category-card .category-left i {\n font-size: 14px;\n}\n\n.scope-category-card .category-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.category-count-badge {\n font-size: 11px;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-sunken);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.scope-category-card .category-right {\n display: flex;\n align-items: center;\n gap: 14px;\n}\n\n.category-all-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.category-chevron {\n color: var(--mj-text-muted);\n font-size: 11px;\n}\n\n.category-scopes-list {\n border-top: 1px solid var(--mj-border-default); /* category/slideout separator */\n padding: 8px;\n max-height: 280px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.scope-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--mj-bg-page);\n transition: all 0.2s ease;\n}\n\n.scope-row:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.scope-row.selected {\n background: var(--mj-color-violet-100);\n border: 1px solid var(--mj-color-violet-300);\n}\n\n.scope-select {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n flex: 1;\n min-width: 0;\n cursor: pointer;\n}\n\n.scope-select input {\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.scope-label {\n flex: 1;\n min-width: 0;\n}\n\n.scope-path {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-primary);\n font-family: 'SF Mono', 'Consolas', monospace;\n word-break: break-word;\n}\n\n.scope-description {\n display: block;\n font-size: 11px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Pattern config for selected scopes - read-only display */\n.scope-pattern-display {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.pattern-tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 4px;\n font-family: 'SF Mono', 'Consolas', monospace;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.pattern-tag.include {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.pattern-tag.exclude {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\n\n.pattern-tag i {\n font-size: 10px;\n}\n\n/* Editable pattern config - shown on hover/edit */\n.scope-pattern-config {\n display: none;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\n.scope-row:hover .scope-pattern-config,\n.scope-row.editing .scope-pattern-config {\n display: flex;\n}\n\n.scope-row:hover .scope-pattern-display,\n.scope-row.editing .scope-pattern-display {\n display: none;\n}\n\n.pattern-field {\n width: 60px;\n}\n\n:host ::ng-deep .pattern-field .k-input {\n padding: 3px 6px;\n font-size: 11px;\n border-radius: 4px;\n height: 26px;\n}\n\n.type-field {\n width: 75px;\n}\n\n:host ::ng-deep .type-field .k-dropdownlist {\n font-size: 11px;\n height: 26px;\n}\n\n:host ::ng-deep .type-field .k-input-inner {\n padding: 2px 6px;\n font-size: 11px;\n}\n\n.empty-scopes-message {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 32px;\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n.empty-scopes-message i {\n font-size: 32px;\n margin-bottom: 12px;\n opacity: 0.5;\n}\n\n/* Legacy dialog styles removed - now using slide-out panels */\n"] }]
|
|
1064
|
+
args: [{ standalone: false, selector: 'mj-api-applications-panel', template: "<div class=\"applications-panel\" [class.panel-open]=\"ShowEditPanel || ShowCreatePanel\">\n @if (IsLoading) {\n <mj-loading text=\"Loading applications...\"></mj-loading>\n }\n\n @if (!IsLoading) {\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"header-left\">\n <h3 class=\"panel-title\">\n <i class=\"fa-solid fa-cube\"></i>\n API Applications\n </h3>\n <p class=\"panel-subtitle\">Manage applications and their default scope permissions</p>\n </div>\n <button class=\"btn-create\" (click)=\"openCreatePanel()\">\n <i class=\"fa-solid fa-plus\"></i>\n New Application\n </button>\n </div>\n <!-- Messages -->\n @if (SuccessMessage) {\n <div class=\"message success\">\n <i class=\"fa-solid fa-check-circle\"></i>\n {{SuccessMessage}}\n </div>\n }\n @if (ErrorMessage) {\n <div class=\"message error\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n {{ErrorMessage}}\n </div>\n }\n <!-- Applications Grid -->\n <div class=\"applications-grid\">\n @for (appItem of Applications; track appItem) {\n <div class=\"app-card\"\n [class.inactive]=\"!appItem.application.IsActive\"\n [class.expanded]=\"appItem.expanded\">\n <div class=\"app-header\">\n <div class=\"app-header-main\" (click)=\"toggleExpanded(appItem)\">\n <div class=\"app-icon\">\n <i class=\"fa-solid fa-cube\"></i>\n </div>\n <div class=\"app-info\">\n <div class=\"app-name\">\n {{appItem.application.Name}}\n <span class=\"status-badge\" [class.active]=\"appItem.application.IsActive\"\n [class.inactive]=\"!appItem.application.IsActive\">\n {{appItem.application.IsActive ? 'Active' : 'Inactive'}}\n </span>\n </div>\n @if (appItem.application.Description) {\n <div class=\"app-description\">\n {{appItem.application.Description}}\n </div>\n }\n </div>\n <div class=\"app-stats\">\n <div class=\"scope-count\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n {{appItem.scopeCount}} scopes\n </div>\n <i class=\"fa-solid expand-icon\"\n [class.fa-chevron-down]=\"!appItem.expanded\"\n [class.fa-chevron-up]=\"appItem.expanded\"></i>\n </div>\n </div>\n <button class=\"app-edit-btn\" (click)=\"openEditPanel(appItem); $event.stopPropagation()\" title=\"Edit Application\">\n <i class=\"fa-solid fa-pencil\"></i>\n </button>\n </div>\n <!-- Expanded Content -->\n @if (appItem.expanded) {\n <div class=\"app-details\">\n <div class=\"details-section\">\n <h4>Scope Ceiling Rules</h4>\n @if (appItem.scopes.length > 0) {\n <div class=\"scope-rules\">\n @for (scope of appItem.scopes; track scope) {\n <div class=\"scope-rule\">\n <div class=\"rule-icon\" [ngClass]=\"getPatternClass(scope.PatternType, scope.IsDeny)\">\n <i [class]=\"getPatternIcon(scope.PatternType, scope.IsDeny)\"></i>\n </div>\n <div class=\"rule-info\">\n <div class=\"rule-scope\">{{getScopeName(scope.ScopeID)}}</div>\n <div class=\"rule-pattern\">\n <code>{{scope.ResourcePattern}}</code>\n <span class=\"pattern-type\">{{scope.PatternType}}</span>\n @if (scope.Priority > 0) {\n <span class=\"priority\">\n Priority: {{scope.Priority}}\n </span>\n }\n </div>\n </div>\n </div>\n }\n </div>\n }\n @if (appItem.scopes.length === 0) {\n <div class=\"empty-scopes\">\n <i class=\"fa-solid fa-shield-xmark\"></i>\n <span>No scope rules defined - all access denied by default</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (Applications.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>No applications configured</span>\n <p>Create an application to define scope ceilings for API key access</p>\n </div>\n }\n </div>\n }\n\n <!-- Slide-out Backdrop -->\n @if (ShowEditPanel || ShowCreatePanel) {\n <div class=\"slideout-backdrop\" (click)=\"closePanel()\"></div>\n }\n\n <!-- Create Panel (Slide-out) -->\n <div class=\"slideout-panel\" [class.open]=\"ShowCreatePanel\">\n <div class=\"slideout-header\">\n <div class=\"slideout-title\">\n <i class=\"fa-solid fa-plus-circle\"></i>\n <span>New Application</span>\n </div>\n <button class=\"slideout-close\" (click)=\"closePanel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <div class=\"slideout-content\">\n <div class=\"form-section\">\n <div class=\"form-field\">\n <label>Application Name *</label>\n <input class=\"mj-input\" [(ngModel)]=\"EditName\"\n placeholder=\"e.g., MJAPI, MCP Server, Portal\"\n class=\"form-input\" />\n </div>\n\n <div class=\"form-field\">\n <label>Description</label>\n <textarea class=\"mj-textarea\" [(ngModel)]=\"EditDescription\"\n placeholder=\"Describe the application's purpose...\"\n [rows]=\"3\"\n class=\"form-textarea\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" class=\"mj-checkbox\" [(ngModel)]=\"EditIsActive\" />\n <div>\n <span>Active</span>\n <span class=\"checkbox-hint\">Inactive applications cannot be used with API keys</span>\n </div>\n </label>\n </div>\n </div>\n </div>\n\n <div class=\"slideout-footer\">\n <button mjButton variant=\"primary\"\n [disabled]=\"!EditName.trim() || IsSaving\"\n (click)=\"saveApplication()\">\n @if (IsSaving) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n }\n @if (!IsSaving) {\n <span>\n <i class=\"fa-solid fa-save\"></i>\n Create Application\n </span>\n }\n </button>\n <button mjButton (click)=\"closePanel()\">Cancel</button>\n </div>\n </div>\n\n <!-- Edit Panel (Slide-out) -->\n <div class=\"slideout-panel\" [class.open]=\"ShowEditPanel\" [style.width.px]=\"PanelWidth\">\n <!-- Resize Handle -->\n <div class=\"slideout-resize-handle\"\n [class.resizing]=\"IsResizing\"\n (mousedown)=\"startResize($event)\"></div>\n <div class=\"slideout-header\">\n <div class=\"slideout-title\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>{{EditingApplication?.Name}}</span>\n <span class=\"status-pill\" [class.active]=\"EditIsActive\">\n {{EditIsActive ? 'Active' : 'Inactive'}}\n </span>\n </div>\n <button class=\"slideout-close\" (click)=\"closePanel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Tab Bar -->\n <div class=\"slideout-tabs\">\n <button class=\"slideout-tab\" [class.active]=\"EditTab === 'details'\"\n (click)=\"EditTab = 'details'\">\n <i class=\"fa-solid fa-info-circle\"></i>\n Details\n </button>\n <button class=\"slideout-tab\" [class.active]=\"EditTab === 'scopes'\"\n (click)=\"EditTab = 'scopes'\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n Scopes\n <span class=\"tab-badge\">{{getSelectedScopeCount()}}</span>\n </button>\n </div>\n\n <div class=\"slideout-content\">\n <!-- Details Tab -->\n @if (EditTab === 'details') {\n <div class=\"tab-panel\">\n <div class=\"form-section\">\n <div class=\"form-field\">\n <label>Application Name *</label>\n <input class=\"mj-input\" [(ngModel)]=\"EditName\"\n placeholder=\"e.g., MJAPI, MCP Server, Portal\"\n class=\"form-input\" />\n </div>\n <div class=\"form-field\">\n <label>Description</label>\n <textarea class=\"mj-textarea\" [(ngModel)]=\"EditDescription\"\n placeholder=\"Describe the application's purpose...\"\n [rows]=\"4\"\n class=\"form-textarea\"></textarea>\n </div>\n <div class=\"form-field\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" class=\"mj-checkbox\" [(ngModel)]=\"EditIsActive\" />\n <div>\n <span>Active</span>\n <span class=\"checkbox-hint\">Inactive applications cannot be used with API keys</span>\n </div>\n </label>\n </div>\n </div>\n </div>\n }\n\n <!-- Scopes Tab -->\n @if (EditTab === 'scopes') {\n <div class=\"tab-panel scopes-panel\">\n <div class=\"scopes-intro\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <span>Define the maximum permissions this application can grant to API keys.</span>\n </div>\n <div class=\"scope-categories-list\">\n @for (category of ScopeCategories; track category) {\n <div class=\"scope-category-card\">\n <div class=\"category-header\" (click)=\"toggleScopeCategory(category)\">\n <div class=\"category-left\">\n <i [class]=\"category.icon\" [style.color]=\"category.color\"></i>\n <span class=\"category-name\">{{category.name}}</span>\n <span class=\"category-count-badge\">\n {{getCategorySelectedCount(category)}}/{{category.scopes.length}}\n </span>\n </div>\n <div class=\"category-right\">\n <label class=\"category-all-toggle\" (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [checked]=\"category.allSelected\"\n (change)=\"toggleCategoryAll(category)\" />\n <span>All</span>\n </label>\n <i class=\"fa-solid category-chevron\"\n [class.fa-chevron-down]=\"!category.expanded\"\n [class.fa-chevron-up]=\"category.expanded\"></i>\n </div>\n </div>\n @if (category.expanded) {\n <div class=\"category-scopes-list\">\n @for (selection of category.scopes; track selection) {\n <div class=\"scope-row\"\n [class.selected]=\"selection.selected\">\n <label class=\"scope-select\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [(ngModel)]=\"selection.selected\"\n (change)=\"updateCategoryState(category)\" />\n <div class=\"scope-label\">\n <span class=\"scope-path\">{{selection.displayName || '(unnamed scope)'}}</span>\n @if (selection.scope.Description) {\n <span class=\"scope-description\">\n {{selection.scope.Description}}\n </span>\n }\n </div>\n </label>\n <!-- Read-only pattern display -->\n @if (selection.selected) {\n <div class=\"scope-pattern-display\">\n <span class=\"pattern-tag\" [class.include]=\"selection.patternType === 'Include'\"\n [class.exclude]=\"selection.patternType === 'Exclude'\">\n <i class=\"fa-solid\" [class.fa-check]=\"selection.patternType === 'Include'\"\n [class.fa-minus]=\"selection.patternType === 'Exclude'\"></i>\n {{selection.pattern || '*'}}\n </span>\n </div>\n }\n <!-- Editable pattern config - shown on hover -->\n @if (selection.selected) {\n <div class=\"scope-pattern-config\">\n <input class=\"mj-input\" [(ngModel)]=\"selection.pattern\"\n placeholder=\"*\"\n class=\"pattern-field\"\n title=\"Resource Pattern\" />\n <mj-dropdown [(ngModel)]=\"selection.patternType\"\n [Data]=\"['Include', 'Exclude']\"\n [ValuePrimitive]=\"true\"\n class=\"type-field\"\n title=\"Pattern Type\">\n </mj-dropdown>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n @if (ScopeCategories.length === 0) {\n <div class=\"empty-scopes-message\">\n <i class=\"fa-solid fa-shield-xmark\"></i>\n <span>No scopes available</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <div class=\"slideout-footer\">\n <button mjButton variant=\"primary\"\n [disabled]=\"!EditName.trim() || IsSaving\"\n (click)=\"saveAll()\">\n @if (IsSaving) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n }\n @if (!IsSaving) {\n <span>\n <i class=\"fa-solid fa-save\"></i>\n Save Changes\n </span>\n }\n </button>\n <button mjButton (click)=\"closePanel()\">Cancel</button>\n </div>\n </div>\n</div>\n", styles: [".applications-panel {\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n}\n\n.applications-panel.panel-open {\n overflow: hidden;\n}\n\n/* Panel Header */\n.panel-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 24px;\n}\n\n.header-left {\n flex: 1;\n}\n\n.panel-title {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.panel-title i {\n color: var(--mj-brand-primary);\n}\n\n.panel-subtitle {\n font-size: 14px;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.btn-create {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 8px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.btn-create:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n/* Messages */\n.message {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 14px;\n}\n\n.message.success {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.message.error {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n border: 1px solid var(--mj-status-error-border);\n}\n\n/* Applications Grid */\n.applications-grid {\n display: flex;\n flex-direction: column;\n gap: 16px;\n flex: 1;\n overflow-y: auto;\n}\n\n.app-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-md);\n overflow: hidden;\n transition: box-shadow 0.2s ease;\n}\n\n.app-card:hover {\n box-shadow: var(--mj-shadow-lg);\n}\n\n.app-card.inactive {\n opacity: 0.7;\n}\n\n.app-header {\n display: flex;\n align-items: center;\n padding: 16px 20px;\n gap: 12px;\n}\n\n.app-header-main {\n display: flex;\n align-items: center;\n flex: 1;\n cursor: pointer;\n transition: background 0.2s ease;\n border-radius: 8px;\n padding: 4px;\n margin: -4px;\n}\n\n.app-header-main:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.app-edit-btn {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.app-edit-btn:hover {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.app-icon {\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n border-radius: 10px;\n margin-right: 16px;\n flex-shrink: 0;\n}\n\n.app-icon i {\n font-size: 20px;\n color: var(--mj-text-inverse);\n}\n\n.app-info {\n flex: 1;\n min-width: 0;\n}\n\n.app-name {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.status-badge {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.status-badge.active {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.status-badge.inactive {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.app-description {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-stats {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-left: 16px;\n}\n\n.scope-count {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.scope-count i {\n color: var(--mj-brand-primary);\n}\n\n.expand-icon {\n color: var(--mj-text-muted);\n transition: transform 0.2s ease;\n}\n\n/* Expanded Details */\n.app-details {\n padding: 0 20px 20px 20px;\n border-top: 1px solid var(--mj-border-default);\n margin-top: 0;\n}\n\n.details-section {\n margin-top: 16px;\n}\n\n.details-section h4 {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 12px 0;\n}\n\n.scope-rules {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.scope-rule {\n display: flex;\n align-items: flex-start;\n padding: 10px 12px;\n background: var(--mj-bg-page);\n border-radius: 8px;\n}\n\n.rule-icon {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n margin-right: 12px;\n flex-shrink: 0;\n}\n\n.rule-icon.pattern-include {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-600);\n}\n\n.rule-icon.pattern-exclude {\n background: var(--mj-color-brand-100);\n color: var(--mj-brand-primary-hover);\n}\n\n.rule-icon.pattern-deny {\n background: var(--mj-color-error-100);\n color: var(--mj-color-error-600);\n}\n\n.rule-info {\n flex: 1;\n min-width: 0;\n}\n\n.rule-scope {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.rule-pattern {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.rule-pattern code {\n font-family: monospace;\n font-size: 12px;\n background: var(--mj-bg-surface-active);\n padding: 2px 6px;\n border-radius: 4px;\n color: var(--mj-text-primary);\n}\n\n.pattern-type {\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.priority {\n font-size: 11px;\n color: var(--mj-brand-primary);\n}\n\n.empty-scopes {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 24px;\n color: var(--mj-text-secondary);\n text-align: center;\n background: var(--mj-bg-page);\n border-radius: 8px;\n}\n\n.empty-scopes i {\n font-size: 24px;\n margin-bottom: 8px;\n opacity: 0.5;\n}\n\n/* Legacy .details-actions removed - buttons now in header */\n\n/* Empty State */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px;\n color: var(--mj-text-secondary);\n text-align: center;\n}\n\n.empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n.empty-state span {\n font-size: 16px;\n font-weight: 500;\n}\n\n.empty-state p {\n font-size: 14px;\n margin-top: 8px;\n}\n\n/* ========================================\n Slide-out Panel Styles\n ======================================== */\n\n/* Backdrop */\n.slideout-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 100;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Slide-out Panel */\n.slideout-panel {\n position: absolute;\n top: 0;\n right: 0;\n width: 570px;\n height: 100%;\n max-height: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay);\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n transform: translateX(100%);\n}\n\n.slideout-panel.open {\n transform: translateX(0);\n}\n\n/* Resize handle */\n.slideout-resize-handle {\n position: absolute;\n top: 0;\n left: 0;\n width: 6px;\n height: 100%;\n cursor: ew-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s ease;\n}\n\n.slideout-resize-handle:hover,\n.slideout-resize-handle.resizing {\n background: color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.slideout-resize-handle::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 2px;\n height: 40px;\n background: var(--mj-color-neutral-300);\n border-radius: 2px;\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.slideout-resize-handle:hover::after {\n opacity: 1;\n}\n\n/* Panel Header */\n.slideout-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-title {\n display: flex;\n align-items: center;\n gap: 12px;\n font-weight: 600;\n font-size: 17px;\n color: var(--mj-text-primary);\n flex: 1;\n min-width: 0;\n}\n\n.slideout-title i {\n color: var(--mj-brand-primary);\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.slideout-title span:first-of-type {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.status-pill {\n font-size: 11px;\n font-weight: 500;\n padding: 3px 10px;\n border-radius: 12px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n flex-shrink: 0;\n}\n\n.status-pill.active {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.slideout-close {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.slideout-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n/* Panel Tabs */\n.slideout-tabs {\n display: flex;\n gap: 4px;\n padding: 12px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-tab {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 18px;\n background: transparent;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.slideout-tab:hover {\n background: var(--mj-border-default);\n color: var(--mj-text-secondary);\n}\n\n.slideout-tab.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.slideout-tab i {\n font-size: 14px;\n}\n\n.tab-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.slideout-tab:not(.active) .tab-badge {\n background: var(--mj-border-default); /* tab badge neutral bg */\n color: var(--mj-text-secondary);\n}\n\n/* Panel Content */\n.slideout-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n}\n\n.tab-panel {\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.scopes-panel {\n height: 100%;\n}\n\n/* Form Section */\n.form-section {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n/* Form Fields */\n.form-field {\n margin-bottom: 20px;\n}\n\n.form-field:last-of-type {\n margin-bottom: 0;\n}\n\n.form-field label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n letter-spacing: 0.01em;\n}\n\n.form-input {\n width: 100%;\n}\n\n.form-textarea {\n width: 100%;\n resize: vertical;\n min-height: 80px;\n}\n\n:host ::ng-deep .form-input:focus-within,\n:host ::ng-deep .form-textarea:focus-within {\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n/* Slideout Footer */\n.slideout-footer {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default); /* category/slideout separator */\n flex-shrink: 0;\n}\n\n.checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n cursor: pointer;\n padding: 12px 16px;\n background: var(--mj-bg-page);\n border-radius: 10px;\n border: 1px solid var(--mj-border-default);\n transition: all 0.2s ease;\n}\n\n.checkbox-label:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-color-neutral-300);\n}\n\n.checkbox-label input {\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.checkbox-label span:first-of-type {\n font-weight: 600;\n color: var(--mj-text-secondary);\n font-size: 14px;\n}\n\n.checkbox-hint {\n display: block;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n line-height: 1.4;\n}\n\n/* Legacy dialog-actions removed - now using slideout-footer */\n\n/* ========================================\n Scopes Panel - Slide-out Layout\n ======================================== */\n\n.scopes-intro {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-status-info-bg);\n border: 1px solid var(--mj-status-info-border);\n border-radius: 10px;\n font-size: 13px;\n color: var(--mj-status-info-text);\n margin-bottom: 16px;\n line-height: 1.4;\n}\n\n.scopes-intro i {\n color: var(--mj-status-info);\n font-size: 14px;\n flex-shrink: 0;\n margin-top: 1px;\n}\n\n.scope-categories-list {\n flex: 1;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.scope-category-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.scope-category-card .category-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.scope-category-card .category-header:hover {\n background: var(--mj-bg-page);\n}\n\n.scope-category-card .category-left {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.scope-category-card .category-left i {\n font-size: 14px;\n}\n\n.scope-category-card .category-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.category-count-badge {\n font-size: 11px;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-sunken);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.scope-category-card .category-right {\n display: flex;\n align-items: center;\n gap: 14px;\n}\n\n.category-all-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.category-chevron {\n color: var(--mj-text-muted);\n font-size: 11px;\n}\n\n.category-scopes-list {\n border-top: 1px solid var(--mj-border-default); /* category/slideout separator */\n padding: 8px;\n max-height: 280px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.scope-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--mj-bg-page);\n transition: all 0.2s ease;\n}\n\n.scope-row:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.scope-row.selected {\n background: var(--mj-color-violet-100);\n border: 1px solid var(--mj-color-violet-300);\n}\n\n.scope-select {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n flex: 1;\n min-width: 0;\n cursor: pointer;\n}\n\n.scope-select input {\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.scope-label {\n flex: 1;\n min-width: 0;\n}\n\n.scope-path {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-primary);\n font-family: 'SF Mono', 'Consolas', monospace;\n word-break: break-word;\n}\n\n.scope-description {\n display: block;\n font-size: 11px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Pattern config for selected scopes - read-only display */\n.scope-pattern-display {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.pattern-tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 4px;\n font-family: 'SF Mono', 'Consolas', monospace;\n font-size: 11px;\n color: var(--mj-text-secondary);\n}\n\n.pattern-tag.include {\n background: var(--mj-color-success-100);\n color: var(--mj-color-success-800);\n}\n\n.pattern-tag.exclude {\n background: var(--mj-color-error-100);\n color: var(--mj-status-error);\n}\n\n.pattern-tag i {\n font-size: 10px;\n}\n\n/* Editable pattern config - shown on hover/edit */\n.scope-pattern-config {\n display: none;\n gap: 6px;\n margin-left: auto;\n flex-shrink: 0;\n}\n\n.scope-row:hover .scope-pattern-config,\n.scope-row.editing .scope-pattern-config {\n display: flex;\n}\n\n.scope-row:hover .scope-pattern-display,\n.scope-row.editing .scope-pattern-display {\n display: none;\n}\n\n.pattern-field {\n width: 60px;\n}\n\n.type-field {\n width: 75px;\n}\n\n.empty-scopes-message {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 32px;\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n.empty-scopes-message i {\n font-size: 32px;\n margin-bottom: 12px;\n opacity: 0.5;\n}\n\n/* Legacy dialog styles removed - now using slide-out panels */\n"] }]
|
|
1065
1065
|
}], () => [{ type: i0.ChangeDetectorRef }], { ApplicationUpdated: [{
|
|
1066
1066
|
type: Output
|
|
1067
1067
|
}], onMouseMove: [{
|
|
@@ -736,11 +736,11 @@ export class APIKeyCreateDialogComponent extends BaseAngularComponent {
|
|
|
736
736
|
i0.ɵɵconditional(ctx.Step === "scopes" ? 15 : -1);
|
|
737
737
|
i0.ɵɵadvance();
|
|
738
738
|
i0.ɵɵconditional(ctx.Step === "success" ? 16 : -1);
|
|
739
|
-
} }, dependencies: [i1.DefaultValueAccessor, i1.CheckboxControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJButtonDirective, i2.MJDatepickerComponent, i3.LoadingComponent, i4.DatePipe], styles: ["\n\n\n\n\n\n\n.slideout-backdrop[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 100;\n animation: _ngcontent-%COMP%_fadeIn 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n\n\n.slideout-panel[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n right: 0;\n width: 520px;\n height: 100%;\n max-height: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay);\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateX(100%);\n overflow: hidden;\n}\n\n.slideout-panel.open[_ngcontent-%COMP%] {\n transform: translateX(0);\n}\n\n\n\n.slideout-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n font-weight: 600;\n font-size: 17px;\n color: var(--mj-text-primary);\n flex: 1;\n min-width: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.slideout-close[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.slideout-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n\n\n.slideout-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n}\n\n.step-panel[_ngcontent-%COMP%] {\n padding: 24px;\n}\n\n\n\n.slideout-footer[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n[_nghost-%COMP%] .slideout-footer .k-button {\n min-width: 100px;\n padding: 10px 20px;\n font-weight: 600;\n border-radius: 8px;\n transition: all 0.2s ease;\n}\n\n[_nghost-%COMP%] .slideout-footer .k-button-solid-primary {\n background: var(--mj-brand-primary);\n border: none;\n box-shadow: var(--mj-shadow-brand-sm);\n}\n\n[_nghost-%COMP%] .slideout-footer .k-button-solid-primary:hover {\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-brand-md);\n}\n\n[_nghost-%COMP%] .slideout-footer .k-button i {\n margin: 0 5px;\n}\n\n\n\n.step-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px 24px;\n background: linear-gradient(135deg, var(--mj-color-brand-100) 0%, var(--mj-color-brand-200) 100%);\n border-bottom: 1px solid var(--mj-color-accent-300);\n}\n\n.step[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n}\n\n.step-number[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-color-neutral-300);\n border-radius: 50%;\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-secondary);\n transition: all 0.2s ease;\n}\n\n.step.active[_ngcontent-%COMP%] .step-number[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.step.completed[_ngcontent-%COMP%] .step-number[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n color: var(--mj-text-inverse);\n}\n\n.step-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.step.active[_ngcontent-%COMP%] .step-label[_ngcontent-%COMP%] {\n color: var(--mj-color-brand-800);\n font-weight: 600;\n}\n\n.step-connector[_ngcontent-%COMP%] {\n width: 80px;\n height: 2px;\n background: var(--mj-color-neutral-300);\n margin: 0 16px;\n margin-bottom: 20px;\n transition: background 0.2s ease;\n}\n\n.step-connector.active[_ngcontent-%COMP%] {\n background: linear-gradient(90deg, var(--mj-status-success), var(--mj-brand-primary));\n}\n\n\n\n.dialog-content[_ngcontent-%COMP%] {\n padding: 24px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.scopes-content[_ngcontent-%COMP%] {\n max-height: 450px;\n}\n\n\n\n.form-section[_ngcontent-%COMP%] {\n margin-bottom: 24px;\n}\n\n.form-label[_ngcontent-%COMP%] {\n display: block;\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n}\n\n.form-label.required[_ngcontent-%COMP%]::after {\n content: ' *';\n color: var(--mj-status-error);\n}\n\n.form-input[_ngcontent-%COMP%], \n.form-textarea[_ngcontent-%COMP%] {\n width: 100%;\n}\n\n.form-hint[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 6px;\n}\n\n\n\n.expiration-options[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.checkbox-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n.preset-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.preset-btn[_ngcontent-%COMP%] {\n padding: 8px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.preset-btn[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-color-brand-50);\n}\n\n.preset-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.custom-date[_ngcontent-%COMP%] {\n margin-top: 4px;\n}\n\n.expiration-preview[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n background: var(--mj-color-success-50);\n border-radius: 8px;\n font-size: 13px;\n color: var(--mj-color-success-600);\n}\n\n.expiration-preview[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n\n\n.error-message[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n background: var(--mj-color-error-50);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n color: var(--mj-color-error-600);\n font-size: 14px;\n}\n\n\n\n.scopes-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 20px;\n}\n\n.scopes-info[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.scopes-info[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.scopes-count[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-brand-primary);\n border-radius: 20px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-inverse);\n}\n\n.scopes-count[_ngcontent-%COMP%] .count[_ngcontent-%COMP%] {\n font-weight: 700;\n}\n\n\n\n.scope-categories[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.scope-category[_ngcontent-%COMP%] {\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.category-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 14px 16px;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.category-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.category-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.category-left[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n.category-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.category-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.category-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.select-all-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.category-right[_ngcontent-%COMP%] > i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.category-scopes[_ngcontent-%COMP%] {\n padding: 8px 16px 16px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n}\n\n.scope-item[_ngcontent-%COMP%] {\n padding: 10px 0;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.scope-item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.scope-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: pointer;\n}\n\n.scope-info[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.scope-name[_ngcontent-%COMP%] {\n display: block;\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-secondary);\n font-family: monospace;\n}\n\n.scope-desc[_ngcontent-%COMP%] {\n display: block;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n}\n\n.scope-tip[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: var(--mj-status-info-bg);\n border-radius: 8px;\n margin-top: 16px;\n font-size: 13px;\n color: var(--mj-status-info-text);\n}\n\n.scope-tip[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-info);\n}\n\n.scope-tip[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n background: var(--mj-color-info-100);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n}\n\n\n\n.no-scopes-warning[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: linear-gradient(135deg, var(--mj-color-error-50) 0%, var(--mj-color-error-100) 100%);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 10px;\n margin-top: 16px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.no-scopes-warning[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-color-error-600);\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.no-scopes-warning[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n display: block;\n color: var(--mj-status-error);\n font-size: 14px;\n margin-bottom: 4px;\n}\n\n.no-scopes-warning[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n line-height: 1.5;\n}\n\n\n\n.success-content[_ngcontent-%COMP%] {\n text-align: center;\n}\n\n.success-icon[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n}\n\n.success-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 64px;\n color: var(--mj-status-success);\n}\n\n.success-title[_ngcontent-%COMP%] {\n margin: 0 0 24px 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n\n\n.key-display[_ngcontent-%COMP%] {\n background: var(--mj-color-neutral-800);\n border-radius: 12px;\n padding: 20px;\n margin-bottom: 24px;\n}\n\n.key-warning[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-color-accent-300);\n}\n\n.key-value[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n padding: 12px 16px;\n}\n\n.key-value[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n flex: 1;\n font-family: 'Fira Code', 'Consolas', monospace;\n font-size: 14px;\n color: var(--mj-status-success);\n word-break: break-all;\n text-align: left;\n}\n\n.copy-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--mj-color-neutral-600);\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-inverse);\n cursor: pointer;\n transition: all 0.2s ease;\n white-space: nowrap;\n}\n\n.copy-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-text-secondary);\n}\n\n.copy-btn.copied[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n}\n\n\n\n.key-details[_ngcontent-%COMP%] {\n text-align: left;\n background: var(--mj-bg-page);\n border-radius: 10px;\n padding: 16px 20px;\n margin-bottom: 20px;\n}\n\n.detail-row[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.detail-row[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.detail-label[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.detail-value[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n\n\n.security-note[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n text-align: left;\n padding: 16px;\n background: linear-gradient(135deg, var(--mj-color-success-50) 0%, var(--mj-color-success-100) 100%);\n border-radius: 10px;\n font-size: 13px;\n color: var(--mj-color-success-800);\n}\n\n.security-note[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-status-success);\n margin-top: 2px;\n}\n\n.security-note[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n display: block;\n margin-bottom: 4px;\n}\n\n\n\n[_nghost-%COMP%] .form-input .k-input, \n[_nghost-%COMP%] .form-textarea .k-input-inner, \n[_nghost-%COMP%] .k-datepicker .k-input {\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 14px;\n border-color: var(--mj-border-default);\n transition: all 0.2s ease;\n}\n\n[_nghost-%COMP%] .form-input:focus-within, \n[_nghost-%COMP%] .form-textarea:focus-within, \n[_nghost-%COMP%] .k-datepicker:focus-within {\n box-shadow: var(--mj-focus-ring);\n}"] });
|
|
739
|
+
} }, dependencies: [i1.DefaultValueAccessor, i1.CheckboxControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJButtonDirective, i2.MJDatepickerComponent, i3.LoadingComponent, i4.DatePipe], styles: ["\n\n\n\n\n\n\n.slideout-backdrop[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 100;\n animation: _ngcontent-%COMP%_fadeIn 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n\n\n.slideout-panel[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n right: 0;\n width: 520px;\n height: 100%;\n max-height: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay);\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateX(100%);\n overflow: hidden;\n}\n\n.slideout-panel.open[_ngcontent-%COMP%] {\n transform: translateX(0);\n}\n\n\n\n.slideout-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n font-weight: 600;\n font-size: 17px;\n color: var(--mj-text-primary);\n flex: 1;\n min-width: 0;\n}\n\n.slideout-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.slideout-close[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.slideout-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n\n\n.slideout-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n}\n\n.step-panel[_ngcontent-%COMP%] {\n padding: 24px;\n}\n\n\n\n.slideout-footer[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n\n\n.step-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px 24px;\n background: linear-gradient(135deg, var(--mj-color-brand-100) 0%, var(--mj-color-brand-200) 100%);\n border-bottom: 1px solid var(--mj-color-accent-300);\n}\n\n.step[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n}\n\n.step-number[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-color-neutral-300);\n border-radius: 50%;\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-secondary);\n transition: all 0.2s ease;\n}\n\n.step.active[_ngcontent-%COMP%] .step-number[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.step.completed[_ngcontent-%COMP%] .step-number[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n color: var(--mj-text-inverse);\n}\n\n.step-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.step.active[_ngcontent-%COMP%] .step-label[_ngcontent-%COMP%] {\n color: var(--mj-color-brand-800);\n font-weight: 600;\n}\n\n.step-connector[_ngcontent-%COMP%] {\n width: 80px;\n height: 2px;\n background: var(--mj-color-neutral-300);\n margin: 0 16px;\n margin-bottom: 20px;\n transition: background 0.2s ease;\n}\n\n.step-connector.active[_ngcontent-%COMP%] {\n background: linear-gradient(90deg, var(--mj-status-success), var(--mj-brand-primary));\n}\n\n\n\n.dialog-content[_ngcontent-%COMP%] {\n padding: 24px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.scopes-content[_ngcontent-%COMP%] {\n max-height: 450px;\n}\n\n\n\n.form-section[_ngcontent-%COMP%] {\n margin-bottom: 24px;\n}\n\n.form-label[_ngcontent-%COMP%] {\n display: block;\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n}\n\n.form-label.required[_ngcontent-%COMP%]::after {\n content: ' *';\n color: var(--mj-status-error);\n}\n\n.form-input[_ngcontent-%COMP%], \n.form-textarea[_ngcontent-%COMP%] {\n width: 100%;\n}\n\n.form-hint[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 6px;\n}\n\n\n\n.expiration-options[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.checkbox-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n.preset-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.preset-btn[_ngcontent-%COMP%] {\n padding: 8px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.preset-btn[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-color-brand-50);\n}\n\n.preset-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.custom-date[_ngcontent-%COMP%] {\n margin-top: 4px;\n}\n\n.expiration-preview[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n background: var(--mj-color-success-50);\n border-radius: 8px;\n font-size: 13px;\n color: var(--mj-color-success-600);\n}\n\n.expiration-preview[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n}\n\n\n\n.error-message[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n background: var(--mj-color-error-50);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n color: var(--mj-color-error-600);\n font-size: 14px;\n}\n\n\n\n.scopes-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 20px;\n}\n\n.scopes-info[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.scopes-info[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.scopes-count[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-brand-primary);\n border-radius: 20px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-inverse);\n}\n\n.scopes-count[_ngcontent-%COMP%] .count[_ngcontent-%COMP%] {\n font-weight: 700;\n}\n\n\n\n.scope-categories[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.scope-category[_ngcontent-%COMP%] {\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.category-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 14px 16px;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.category-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.category-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.category-left[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n.category-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.category-badge[_ngcontent-%COMP%] {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.category-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.select-all-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.category-right[_ngcontent-%COMP%] > i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.category-scopes[_ngcontent-%COMP%] {\n padding: 8px 16px 16px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n}\n\n.scope-item[_ngcontent-%COMP%] {\n padding: 10px 0;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.scope-item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.scope-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: pointer;\n}\n\n.scope-info[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.scope-name[_ngcontent-%COMP%] {\n display: block;\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-secondary);\n font-family: monospace;\n}\n\n.scope-desc[_ngcontent-%COMP%] {\n display: block;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n}\n\n.scope-tip[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: var(--mj-status-info-bg);\n border-radius: 8px;\n margin-top: 16px;\n font-size: 13px;\n color: var(--mj-status-info-text);\n}\n\n.scope-tip[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-info);\n}\n\n.scope-tip[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n background: var(--mj-color-info-100);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n}\n\n\n\n.no-scopes-warning[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: linear-gradient(135deg, var(--mj-color-error-50) 0%, var(--mj-color-error-100) 100%);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 10px;\n margin-top: 16px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.no-scopes-warning[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-color-error-600);\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.no-scopes-warning[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n display: block;\n color: var(--mj-status-error);\n font-size: 14px;\n margin-bottom: 4px;\n}\n\n.no-scopes-warning[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n line-height: 1.5;\n}\n\n\n\n.success-content[_ngcontent-%COMP%] {\n text-align: center;\n}\n\n.success-icon[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n}\n\n.success-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 64px;\n color: var(--mj-status-success);\n}\n\n.success-title[_ngcontent-%COMP%] {\n margin: 0 0 24px 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n\n\n.key-display[_ngcontent-%COMP%] {\n background: var(--mj-color-neutral-800);\n border-radius: 12px;\n padding: 20px;\n margin-bottom: 24px;\n}\n\n.key-warning[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-color-accent-300);\n}\n\n.key-value[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n padding: 12px 16px;\n}\n\n.key-value[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n flex: 1;\n font-family: 'Fira Code', 'Consolas', monospace;\n font-size: 14px;\n color: var(--mj-status-success);\n word-break: break-all;\n text-align: left;\n}\n\n.copy-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--mj-color-neutral-600);\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-inverse);\n cursor: pointer;\n transition: all 0.2s ease;\n white-space: nowrap;\n}\n\n.copy-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-text-secondary);\n}\n\n.copy-btn.copied[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n}\n\n\n\n.key-details[_ngcontent-%COMP%] {\n text-align: left;\n background: var(--mj-bg-page);\n border-radius: 10px;\n padding: 16px 20px;\n margin-bottom: 20px;\n}\n\n.detail-row[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.detail-row[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.detail-label[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.detail-value[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n\n\n.security-note[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n text-align: left;\n padding: 16px;\n background: linear-gradient(135deg, var(--mj-color-success-50) 0%, var(--mj-color-success-100) 100%);\n border-radius: 10px;\n font-size: 13px;\n color: var(--mj-color-success-800);\n}\n\n.security-note[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-status-success);\n margin-top: 2px;\n}\n\n.security-note[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n display: block;\n margin-bottom: 4px;\n}\n\n\n\n[_nghost-%COMP%] .form-input:focus-within, \n[_nghost-%COMP%] .form-textarea:focus-within {\n box-shadow: var(--mj-focus-ring);\n}"] });
|
|
740
740
|
}
|
|
741
741
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(APIKeyCreateDialogComponent, [{
|
|
742
742
|
type: Component,
|
|
743
|
-
args: [{ standalone: false, selector: 'mj-api-key-create-dialog', template: "<!-- Slide-out Backdrop -->\n@if (Visible) {\n <div class=\"slideout-backdrop\" (click)=\"Step !== 'success' ? close() : null\"></div>\n}\n\n<!-- Slide-out Panel -->\n<div class=\"slideout-panel\" [class.open]=\"Visible\">\n <!-- Header -->\n <div class=\"slideout-header\">\n <div class=\"slideout-title\">\n <i class=\"fa-solid fa-key\"></i>\n <span>Generate New API Key</span>\n </div>\n @if (Step !== 'success') {\n <button class=\"slideout-close\" (click)=\"close()\" title=\"Close (Esc)\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n\n <!-- Step Indicator -->\n @if (Step !== 'success') {\n <div class=\"step-indicator\">\n <div class=\"step\" [class.active]=\"Step === 'configure'\" [class.completed]=\"Step === 'scopes'\">\n <div class=\"step-number\">1</div>\n <div class=\"step-label\">Configure</div>\n </div>\n <div class=\"step-connector\" [class.active]=\"Step === 'scopes'\"></div>\n <div class=\"step\" [class.active]=\"Step === 'scopes'\">\n <div class=\"step-number\">2</div>\n <div class=\"step-label\">Permissions</div>\n </div>\n </div>\n }\n\n <div class=\"slideout-content\">\n <!-- Configure Step -->\n @if (Step === 'configure') {\n <div class=\"step-panel\">\n <div class=\"form-section\">\n <label class=\"form-label required\">Key Label</label>\n <input class=\"mj-input form-input\" [(ngModel)]=\"Label\"\n placeholder=\"e.g., ElevenLabs Integration, CI/CD Pipeline\" />\n <div class=\"form-hint\">A memorable name to identify this key's purpose</div>\n </div>\n <div class=\"form-section\">\n <label class=\"form-label\">Description</label>\n <textarea class=\"mj-textarea form-textarea\" [(ngModel)]=\"Description\"\n placeholder=\"Optional notes about how this key will be used...\"\n [rows]=\"3\"></textarea>\n </div>\n <div class=\"form-section\">\n <label class=\"form-label\">Expiration</label>\n <div class=\"expiration-options\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [(ngModel)]=\"NeverExpires\"\n (change)=\"onNeverExpiresChange()\" />\n <span>Never expires</span>\n </label>\n @if (!NeverExpires) {\n <div class=\"preset-buttons\">\n @for (preset of ExpirationPresets; track preset) {\n <button\n class=\"preset-btn\"\n [class.active]=\"SelectedPreset === preset\"\n (click)=\"onPresetSelect(preset)\">\n {{preset.label}}\n </button>\n }\n </div>\n }\n @if (!NeverExpires && SelectedPreset?.days === -1) {\n <div class=\"custom-date\">\n <mj-datepicker [(ngModel)]=\"ExpiresAt\"\n [Min]=\"getMinDate()\"\n Format=\"MMM d, yyyy\"\n Placeholder=\"Select expiration date\">\n </mj-datepicker>\n </div>\n }\n @if (!NeverExpires && ExpiresAt) {\n <div class=\"expiration-preview\">\n <i class=\"fa-solid fa-calendar-check\"></i>\n <span>Key will expire on {{ExpiresAt | date:'MMMM d, yyyy'}}</span>\n </div>\n }\n </div>\n </div>\n @if (Error) {\n <div class=\"error-message\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n {{Error}}\n </div>\n }\n </div>\n }\n\n <!-- Scopes Step -->\n @if (Step === 'scopes') {\n <div class=\"step-panel scopes-step\">\n <div class=\"scopes-header\">\n <div class=\"scopes-info\">\n <h4>Select Permission Scopes</h4>\n <p>Choose what this API key can access. You can always modify this later.</p>\n </div>\n @if (getSelectedScopeCount() > 0) {\n <div class=\"scopes-count\">\n <span class=\"count\">{{getSelectedScopeCount()}}</span> selected\n </div>\n }\n </div>\n @if (IsLoadingScopes) {\n <mj-loading text=\"Loading scopes...\"></mj-loading>\n }\n @if (!IsLoadingScopes) {\n <div class=\"scope-categories\">\n @for (category of ScopeCategories; track category) {\n <div class=\"scope-category\">\n <div class=\"category-header\" (click)=\"toggleCategory(category)\">\n <div class=\"category-left\">\n <i [class]=\"category.icon\" [style.color]=\"category.color\"></i>\n <span class=\"category-name\">{{category.name}}</span>\n <span class=\"category-badge\">{{category.scopes.length}}</span>\n </div>\n <div class=\"category-right\">\n <label class=\"select-all-label\" (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [checked]=\"category.allSelected\"\n (change)=\"toggleCategoryAll(category)\" />\n <span>All</span>\n </label>\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!category.expanded\"\n [class.fa-chevron-up]=\"category.expanded\"></i>\n </div>\n </div>\n @if (category.expanded) {\n <div class=\"category-scopes\">\n @for (item of category.scopes; track item) {\n <div class=\"scope-item\">\n <label class=\"scope-label\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [(ngModel)]=\"item.selected\"\n (change)=\"updateCategoryState(category)\" />\n <div class=\"scope-info\">\n <span class=\"scope-name\">{{item.scope.Name}}</span>\n @if (item.scope.Description) {\n <span class=\"scope-desc\">\n {{item.scope.Description}}\n </span>\n }\n </div>\n </label>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n <div class=\"scope-tip\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tip: Use wildcards like <code>entities:*</code> for broad access within a category</span>\n </div>\n <!-- No Scopes Warning -->\n @if (!IsLoadingScopes && getSelectedScopeCount() === 0) {\n <div class=\"no-scopes-warning\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <div>\n <strong>Warning: No permissions selected</strong>\n <p>API keys without any assigned scopes will have <strong>no permissions</strong> and cannot perform any operations. This key will be rejected by all API endpoints until scopes are assigned.</p>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Success Step -->\n @if (Step === 'success') {\n <div class=\"step-panel success-step\">\n <div class=\"success-icon\">\n <i class=\"fa-solid fa-check-circle\"></i>\n </div>\n <h3 class=\"success-title\">API Key Created Successfully!</h3>\n <div class=\"key-display\">\n <div class=\"key-warning\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>Copy this key now. It won't be shown again!</span>\n </div>\n <div class=\"key-value\">\n <code>{{RawApiKey}}</code>\n <button class=\"copy-btn\" (click)=\"copyKey()\" [class.copied]=\"KeyCopied\">\n <i class=\"fa-solid\" [class.fa-copy]=\"!KeyCopied\" [class.fa-check]=\"KeyCopied\"></i>\n <span>{{KeyCopied ? 'Copied!' : 'Copy'}}</span>\n </button>\n </div>\n </div>\n <div class=\"key-details\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Label:</span>\n <span class=\"detail-value\">{{Label}}</span>\n </div>\n @if (Description) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Description:</span>\n <span class=\"detail-value\">{{Description}}</span>\n </div>\n }\n <div class=\"detail-row\">\n <span class=\"detail-label\">Expires:</span>\n <span class=\"detail-value\">\n {{NeverExpires ? 'Never' : (ExpiresAt | date:'MMMM d, yyyy')}}\n </span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Scopes:</span>\n <span class=\"detail-value\">{{getSelectedScopeCount()}} permissions</span>\n </div>\n </div>\n <div class=\"security-note\">\n <i class=\"fa-solid fa-shield-check\"></i>\n <div>\n <strong>Security Note:</strong>\n Store this key securely. We only store a hash - the original key cannot be recovered.\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Footer -->\n <div class=\"slideout-footer\">\n @if (Step === 'configure') {\n <button mjButton variant=\"primary\" (click)=\"goToScopes()\">\n Next: Permissions\n <i class=\"fa-solid fa-arrow-right\"></i>\n </button>\n <button mjButton (click)=\"close()\">Cancel</button>\n }\n\n @if (Step === 'scopes') {\n <button mjButton variant=\"primary\"\n [disabled]=\"IsCreating\"\n (click)=\"createKey()\">\n @if (IsCreating) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n }\n @if (!IsCreating) {\n <span>\n <i class=\"fa-solid fa-key\"></i>\n Generate Key\n </span>\n }\n </button>\n <button mjButton (click)=\"goBack()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n Back\n </button>\n }\n\n @if (Step === 'success') {\n <button mjButton variant=\"primary\" (click)=\"close()\">\n <i class=\"fa-solid fa-check\"></i>\n Done\n </button>\n }\n </div>\n</div>\n", styles: ["/* ========================================\n Slide-out Panel Styles\n ======================================== */\n\n/* Backdrop */\n.slideout-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 100;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Slide-out Panel */\n.slideout-panel {\n position: absolute;\n top: 0;\n right: 0;\n width: 520px;\n height: 100%;\n max-height: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay);\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateX(100%);\n overflow: hidden;\n}\n\n.slideout-panel.open {\n transform: translateX(0);\n}\n\n/* Panel Header */\n.slideout-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-title {\n display: flex;\n align-items: center;\n gap: 12px;\n font-weight: 600;\n font-size: 17px;\n color: var(--mj-text-primary);\n flex: 1;\n min-width: 0;\n}\n\n.slideout-title i {\n color: var(--mj-brand-primary);\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.slideout-close {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.slideout-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n/* Panel Content */\n.slideout-content {\n flex: 1;\n overflow-y: auto;\n}\n\n.step-panel {\n padding: 24px;\n}\n\n/* Panel Footer */\n.slideout-footer {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n:host ::ng-deep .slideout-footer .k-button {\n min-width: 100px;\n padding: 10px 20px;\n font-weight: 600;\n border-radius: 8px;\n transition: all 0.2s ease;\n}\n\n:host ::ng-deep .slideout-footer .k-button-solid-primary {\n background: var(--mj-brand-primary);\n border: none;\n box-shadow: var(--mj-shadow-brand-sm);\n}\n\n:host ::ng-deep .slideout-footer .k-button-solid-primary:hover {\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-brand-md);\n}\n\n:host ::ng-deep .slideout-footer .k-button i {\n margin: 0 5px;\n}\n\n/* Step Indicator */\n.step-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px 24px;\n background: linear-gradient(135deg, var(--mj-color-brand-100) 0%, var(--mj-color-brand-200) 100%);\n border-bottom: 1px solid var(--mj-color-accent-300);\n}\n\n.step {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n}\n\n.step-number {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-color-neutral-300);\n border-radius: 50%;\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-secondary);\n transition: all 0.2s ease;\n}\n\n.step.active .step-number {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.step.completed .step-number {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n color: var(--mj-text-inverse);\n}\n\n.step-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.step.active .step-label {\n color: var(--mj-color-brand-800);\n font-weight: 600;\n}\n\n.step-connector {\n width: 80px;\n height: 2px;\n background: var(--mj-color-neutral-300);\n margin: 0 16px;\n margin-bottom: 20px;\n transition: background 0.2s ease;\n}\n\n.step-connector.active {\n background: linear-gradient(90deg, var(--mj-status-success), var(--mj-brand-primary));\n}\n\n/* Dialog Content */\n.dialog-content {\n padding: 24px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.scopes-content {\n max-height: 450px;\n}\n\n/* Form Sections */\n.form-section {\n margin-bottom: 24px;\n}\n\n.form-label {\n display: block;\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n}\n\n.form-label.required::after {\n content: ' *';\n color: var(--mj-status-error);\n}\n\n.form-input,\n.form-textarea {\n width: 100%;\n}\n\n.form-hint {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 6px;\n}\n\n/* Expiration Options */\n.expiration-options {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.checkbox-label {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n.preset-buttons {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.preset-btn {\n padding: 8px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.preset-btn:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-color-brand-50);\n}\n\n.preset-btn.active {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.custom-date {\n margin-top: 4px;\n}\n\n.expiration-preview {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n background: var(--mj-color-success-50);\n border-radius: 8px;\n font-size: 13px;\n color: var(--mj-color-success-600);\n}\n\n.expiration-preview i {\n color: var(--mj-status-success);\n}\n\n/* Error Message */\n.error-message {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n background: var(--mj-color-error-50);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n color: var(--mj-color-error-600);\n font-size: 14px;\n}\n\n/* Scopes */\n.scopes-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 20px;\n}\n\n.scopes-info h4 {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.scopes-info p {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.scopes-count {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-brand-primary);\n border-radius: 20px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-inverse);\n}\n\n.scopes-count .count {\n font-weight: 700;\n}\n\n/* Scope Categories */\n.scope-categories {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.scope-category {\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.category-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 14px 16px;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.category-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.category-left {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.category-left i {\n font-size: 16px;\n}\n\n.category-name {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.category-badge {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.category-right {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.select-all-label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.category-right > i {\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.category-scopes {\n padding: 8px 16px 16px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n}\n\n.scope-item {\n padding: 10px 0;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.scope-item:last-child {\n border-bottom: none;\n}\n\n.scope-label {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: pointer;\n}\n\n.scope-info {\n flex: 1;\n}\n\n.scope-name {\n display: block;\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-secondary);\n font-family: monospace;\n}\n\n.scope-desc {\n display: block;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n}\n\n.scope-tip {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: var(--mj-status-info-bg);\n border-radius: 8px;\n margin-top: 16px;\n font-size: 13px;\n color: var(--mj-status-info-text);\n}\n\n.scope-tip i {\n color: var(--mj-status-info);\n}\n\n.scope-tip code {\n background: var(--mj-color-info-100);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n}\n\n/* No Scopes Warning */\n.no-scopes-warning {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: linear-gradient(135deg, var(--mj-color-error-50) 0%, var(--mj-color-error-100) 100%);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 10px;\n margin-top: 16px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.no-scopes-warning i {\n font-size: 20px;\n color: var(--mj-color-error-600);\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.no-scopes-warning strong {\n display: block;\n color: var(--mj-status-error);\n font-size: 14px;\n margin-bottom: 4px;\n}\n\n.no-scopes-warning p {\n margin: 0;\n line-height: 1.5;\n}\n\n/* Success Content */\n.success-content {\n text-align: center;\n}\n\n.success-icon {\n margin-bottom: 16px;\n}\n\n.success-icon i {\n font-size: 64px;\n color: var(--mj-status-success);\n}\n\n.success-title {\n margin: 0 0 24px 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n/* Key Display */\n.key-display {\n background: var(--mj-color-neutral-800);\n border-radius: 12px;\n padding: 20px;\n margin-bottom: 24px;\n}\n\n.key-warning {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-color-accent-300);\n}\n\n.key-value {\n display: flex;\n align-items: center;\n gap: 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n padding: 12px 16px;\n}\n\n.key-value code {\n flex: 1;\n font-family: 'Fira Code', 'Consolas', monospace;\n font-size: 14px;\n color: var(--mj-status-success);\n word-break: break-all;\n text-align: left;\n}\n\n.copy-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--mj-color-neutral-600);\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-inverse);\n cursor: pointer;\n transition: all 0.2s ease;\n white-space: nowrap;\n}\n\n.copy-btn:hover {\n background: var(--mj-text-secondary);\n}\n\n.copy-btn.copied {\n background: var(--mj-status-success);\n}\n\n/* Key Details */\n.key-details {\n text-align: left;\n background: var(--mj-bg-page);\n border-radius: 10px;\n padding: 16px 20px;\n margin-bottom: 20px;\n}\n\n.detail-row {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.detail-row:last-child {\n border-bottom: none;\n}\n\n.detail-label {\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.detail-value {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n/* Security Note */\n.security-note {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n text-align: left;\n padding: 16px;\n background: linear-gradient(135deg, var(--mj-color-success-50) 0%, var(--mj-color-success-100) 100%);\n border-radius: 10px;\n font-size: 13px;\n color: var(--mj-color-success-800);\n}\n\n.security-note i {\n font-size: 20px;\n color: var(--mj-status-success);\n margin-top: 2px;\n}\n\n.security-note strong {\n display: block;\n margin-bottom: 4px;\n}\n\n/* Form input styling */\n:host ::ng-deep .form-input .k-input,\n:host ::ng-deep .form-textarea .k-input-inner,\n:host ::ng-deep .k-datepicker .k-input {\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 14px;\n border-color: var(--mj-border-default);\n transition: all 0.2s ease;\n}\n\n:host ::ng-deep .form-input:focus-within,\n:host ::ng-deep .form-textarea:focus-within,\n:host ::ng-deep .k-datepicker:focus-within {\n box-shadow: var(--mj-focus-ring);\n}\n"] }]
|
|
743
|
+
args: [{ standalone: false, selector: 'mj-api-key-create-dialog', template: "<!-- Slide-out Backdrop -->\n@if (Visible) {\n <div class=\"slideout-backdrop\" (click)=\"Step !== 'success' ? close() : null\"></div>\n}\n\n<!-- Slide-out Panel -->\n<div class=\"slideout-panel\" [class.open]=\"Visible\">\n <!-- Header -->\n <div class=\"slideout-header\">\n <div class=\"slideout-title\">\n <i class=\"fa-solid fa-key\"></i>\n <span>Generate New API Key</span>\n </div>\n @if (Step !== 'success') {\n <button class=\"slideout-close\" (click)=\"close()\" title=\"Close (Esc)\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n\n <!-- Step Indicator -->\n @if (Step !== 'success') {\n <div class=\"step-indicator\">\n <div class=\"step\" [class.active]=\"Step === 'configure'\" [class.completed]=\"Step === 'scopes'\">\n <div class=\"step-number\">1</div>\n <div class=\"step-label\">Configure</div>\n </div>\n <div class=\"step-connector\" [class.active]=\"Step === 'scopes'\"></div>\n <div class=\"step\" [class.active]=\"Step === 'scopes'\">\n <div class=\"step-number\">2</div>\n <div class=\"step-label\">Permissions</div>\n </div>\n </div>\n }\n\n <div class=\"slideout-content\">\n <!-- Configure Step -->\n @if (Step === 'configure') {\n <div class=\"step-panel\">\n <div class=\"form-section\">\n <label class=\"form-label required\">Key Label</label>\n <input class=\"mj-input form-input\" [(ngModel)]=\"Label\"\n placeholder=\"e.g., ElevenLabs Integration, CI/CD Pipeline\" />\n <div class=\"form-hint\">A memorable name to identify this key's purpose</div>\n </div>\n <div class=\"form-section\">\n <label class=\"form-label\">Description</label>\n <textarea class=\"mj-textarea form-textarea\" [(ngModel)]=\"Description\"\n placeholder=\"Optional notes about how this key will be used...\"\n [rows]=\"3\"></textarea>\n </div>\n <div class=\"form-section\">\n <label class=\"form-label\">Expiration</label>\n <div class=\"expiration-options\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [(ngModel)]=\"NeverExpires\"\n (change)=\"onNeverExpiresChange()\" />\n <span>Never expires</span>\n </label>\n @if (!NeverExpires) {\n <div class=\"preset-buttons\">\n @for (preset of ExpirationPresets; track preset) {\n <button\n class=\"preset-btn\"\n [class.active]=\"SelectedPreset === preset\"\n (click)=\"onPresetSelect(preset)\">\n {{preset.label}}\n </button>\n }\n </div>\n }\n @if (!NeverExpires && SelectedPreset?.days === -1) {\n <div class=\"custom-date\">\n <mj-datepicker [(ngModel)]=\"ExpiresAt\"\n [Min]=\"getMinDate()\"\n Format=\"MMM d, yyyy\"\n Placeholder=\"Select expiration date\">\n </mj-datepicker>\n </div>\n }\n @if (!NeverExpires && ExpiresAt) {\n <div class=\"expiration-preview\">\n <i class=\"fa-solid fa-calendar-check\"></i>\n <span>Key will expire on {{ExpiresAt | date:'MMMM d, yyyy'}}</span>\n </div>\n }\n </div>\n </div>\n @if (Error) {\n <div class=\"error-message\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n {{Error}}\n </div>\n }\n </div>\n }\n\n <!-- Scopes Step -->\n @if (Step === 'scopes') {\n <div class=\"step-panel scopes-step\">\n <div class=\"scopes-header\">\n <div class=\"scopes-info\">\n <h4>Select Permission Scopes</h4>\n <p>Choose what this API key can access. You can always modify this later.</p>\n </div>\n @if (getSelectedScopeCount() > 0) {\n <div class=\"scopes-count\">\n <span class=\"count\">{{getSelectedScopeCount()}}</span> selected\n </div>\n }\n </div>\n @if (IsLoadingScopes) {\n <mj-loading text=\"Loading scopes...\"></mj-loading>\n }\n @if (!IsLoadingScopes) {\n <div class=\"scope-categories\">\n @for (category of ScopeCategories; track category) {\n <div class=\"scope-category\">\n <div class=\"category-header\" (click)=\"toggleCategory(category)\">\n <div class=\"category-left\">\n <i [class]=\"category.icon\" [style.color]=\"category.color\"></i>\n <span class=\"category-name\">{{category.name}}</span>\n <span class=\"category-badge\">{{category.scopes.length}}</span>\n </div>\n <div class=\"category-right\">\n <label class=\"select-all-label\" (click)=\"$event.stopPropagation()\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [checked]=\"category.allSelected\"\n (change)=\"toggleCategoryAll(category)\" />\n <span>All</span>\n </label>\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!category.expanded\"\n [class.fa-chevron-up]=\"category.expanded\"></i>\n </div>\n </div>\n @if (category.expanded) {\n <div class=\"category-scopes\">\n @for (item of category.scopes; track item) {\n <div class=\"scope-item\">\n <label class=\"scope-label\">\n <input type=\"checkbox\" class=\"mj-checkbox\"\n [(ngModel)]=\"item.selected\"\n (change)=\"updateCategoryState(category)\" />\n <div class=\"scope-info\">\n <span class=\"scope-name\">{{item.scope.Name}}</span>\n @if (item.scope.Description) {\n <span class=\"scope-desc\">\n {{item.scope.Description}}\n </span>\n }\n </div>\n </label>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n <div class=\"scope-tip\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <span>Tip: Use wildcards like <code>entities:*</code> for broad access within a category</span>\n </div>\n <!-- No Scopes Warning -->\n @if (!IsLoadingScopes && getSelectedScopeCount() === 0) {\n <div class=\"no-scopes-warning\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <div>\n <strong>Warning: No permissions selected</strong>\n <p>API keys without any assigned scopes will have <strong>no permissions</strong> and cannot perform any operations. This key will be rejected by all API endpoints until scopes are assigned.</p>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Success Step -->\n @if (Step === 'success') {\n <div class=\"step-panel success-step\">\n <div class=\"success-icon\">\n <i class=\"fa-solid fa-check-circle\"></i>\n </div>\n <h3 class=\"success-title\">API Key Created Successfully!</h3>\n <div class=\"key-display\">\n <div class=\"key-warning\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>Copy this key now. It won't be shown again!</span>\n </div>\n <div class=\"key-value\">\n <code>{{RawApiKey}}</code>\n <button class=\"copy-btn\" (click)=\"copyKey()\" [class.copied]=\"KeyCopied\">\n <i class=\"fa-solid\" [class.fa-copy]=\"!KeyCopied\" [class.fa-check]=\"KeyCopied\"></i>\n <span>{{KeyCopied ? 'Copied!' : 'Copy'}}</span>\n </button>\n </div>\n </div>\n <div class=\"key-details\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Label:</span>\n <span class=\"detail-value\">{{Label}}</span>\n </div>\n @if (Description) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Description:</span>\n <span class=\"detail-value\">{{Description}}</span>\n </div>\n }\n <div class=\"detail-row\">\n <span class=\"detail-label\">Expires:</span>\n <span class=\"detail-value\">\n {{NeverExpires ? 'Never' : (ExpiresAt | date:'MMMM d, yyyy')}}\n </span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Scopes:</span>\n <span class=\"detail-value\">{{getSelectedScopeCount()}} permissions</span>\n </div>\n </div>\n <div class=\"security-note\">\n <i class=\"fa-solid fa-shield-check\"></i>\n <div>\n <strong>Security Note:</strong>\n Store this key securely. We only store a hash - the original key cannot be recovered.\n </div>\n </div>\n </div>\n }\n </div>\n\n <!-- Footer -->\n <div class=\"slideout-footer\">\n @if (Step === 'configure') {\n <button mjButton variant=\"primary\" (click)=\"goToScopes()\">\n Next: Permissions\n <i class=\"fa-solid fa-arrow-right\"></i>\n </button>\n <button mjButton (click)=\"close()\">Cancel</button>\n }\n\n @if (Step === 'scopes') {\n <button mjButton variant=\"primary\"\n [disabled]=\"IsCreating\"\n (click)=\"createKey()\">\n @if (IsCreating) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n }\n @if (!IsCreating) {\n <span>\n <i class=\"fa-solid fa-key\"></i>\n Generate Key\n </span>\n }\n </button>\n <button mjButton (click)=\"goBack()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n Back\n </button>\n }\n\n @if (Step === 'success') {\n <button mjButton variant=\"primary\" (click)=\"close()\">\n <i class=\"fa-solid fa-check\"></i>\n Done\n </button>\n }\n </div>\n</div>\n", styles: ["/* ========================================\n Slide-out Panel Styles\n ======================================== */\n\n/* Backdrop */\n.slideout-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 100;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Slide-out Panel */\n.slideout-panel {\n position: absolute;\n top: 0;\n right: 0;\n width: 520px;\n height: 100%;\n max-height: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay);\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateX(100%);\n overflow: hidden;\n}\n\n.slideout-panel.open {\n transform: translateX(0);\n}\n\n/* Panel Header */\n.slideout-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n background: var(--mj-bg-page);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.slideout-title {\n display: flex;\n align-items: center;\n gap: 12px;\n font-weight: 600;\n font-size: 17px;\n color: var(--mj-text-primary);\n flex: 1;\n min-width: 0;\n}\n\n.slideout-title i {\n color: var(--mj-brand-primary);\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.slideout-close {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.slideout-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n/* Panel Content */\n.slideout-content {\n flex: 1;\n overflow-y: auto;\n}\n\n.step-panel {\n padding: 24px;\n}\n\n/* Panel Footer */\n.slideout-footer {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n/* Step Indicator */\n.step-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px 24px;\n background: linear-gradient(135deg, var(--mj-color-brand-100) 0%, var(--mj-color-brand-200) 100%);\n border-bottom: 1px solid var(--mj-color-accent-300);\n}\n\n.step {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n}\n\n.step-number {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-color-neutral-300);\n border-radius: 50%;\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-secondary);\n transition: all 0.2s ease;\n}\n\n.step.active .step-number {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.step.completed .step-number {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n color: var(--mj-text-inverse);\n}\n\n.step-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.step.active .step-label {\n color: var(--mj-color-brand-800);\n font-weight: 600;\n}\n\n.step-connector {\n width: 80px;\n height: 2px;\n background: var(--mj-color-neutral-300);\n margin: 0 16px;\n margin-bottom: 20px;\n transition: background 0.2s ease;\n}\n\n.step-connector.active {\n background: linear-gradient(90deg, var(--mj-status-success), var(--mj-brand-primary));\n}\n\n/* Dialog Content */\n.dialog-content {\n padding: 24px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.scopes-content {\n max-height: 450px;\n}\n\n/* Form Sections */\n.form-section {\n margin-bottom: 24px;\n}\n\n.form-label {\n display: block;\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-secondary);\n margin-bottom: 8px;\n}\n\n.form-label.required::after {\n content: ' *';\n color: var(--mj-status-error);\n}\n\n.form-input,\n.form-textarea {\n width: 100%;\n}\n\n.form-hint {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 6px;\n}\n\n/* Expiration Options */\n.expiration-options {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.checkbox-label {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n.preset-buttons {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.preset-btn {\n padding: 8px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.preset-btn:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-color-brand-50);\n}\n\n.preset-btn.active {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.custom-date {\n margin-top: 4px;\n}\n\n.expiration-preview {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 14px;\n background: var(--mj-color-success-50);\n border-radius: 8px;\n font-size: 13px;\n color: var(--mj-color-success-600);\n}\n\n.expiration-preview i {\n color: var(--mj-status-success);\n}\n\n/* Error Message */\n.error-message {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n background: var(--mj-color-error-50);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 8px;\n color: var(--mj-color-error-600);\n font-size: 14px;\n}\n\n/* Scopes */\n.scopes-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 20px;\n}\n\n.scopes-info h4 {\n margin: 0 0 4px 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.scopes-info p {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.scopes-count {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-brand-primary);\n border-radius: 20px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-inverse);\n}\n\n.scopes-count .count {\n font-weight: 700;\n}\n\n/* Scope Categories */\n.scope-categories {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.scope-category {\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.category-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 14px 16px;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.category-header:hover {\n background: var(--mj-bg-surface-sunken);\n}\n\n.category-left {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.category-left i {\n font-size: 16px;\n}\n\n.category-name {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n}\n\n.category-badge {\n padding: 2px 8px;\n background: var(--mj-border-default);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.category-right {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.select-all-label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.category-right > i {\n color: var(--mj-text-muted);\n font-size: 12px;\n}\n\n.category-scopes {\n padding: 8px 16px 16px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n}\n\n.scope-item {\n padding: 10px 0;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.scope-item:last-child {\n border-bottom: none;\n}\n\n.scope-label {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: pointer;\n}\n\n.scope-info {\n flex: 1;\n}\n\n.scope-name {\n display: block;\n font-weight: 500;\n font-size: 13px;\n color: var(--mj-text-secondary);\n font-family: monospace;\n}\n\n.scope-desc {\n display: block;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 2px;\n}\n\n.scope-tip {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: var(--mj-status-info-bg);\n border-radius: 8px;\n margin-top: 16px;\n font-size: 13px;\n color: var(--mj-status-info-text);\n}\n\n.scope-tip i {\n color: var(--mj-status-info);\n}\n\n.scope-tip code {\n background: var(--mj-color-info-100);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n}\n\n/* No Scopes Warning */\n.no-scopes-warning {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: linear-gradient(135deg, var(--mj-color-error-50) 0%, var(--mj-color-error-100) 100%);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 10px;\n margin-top: 16px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.no-scopes-warning i {\n font-size: 20px;\n color: var(--mj-color-error-600);\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n.no-scopes-warning strong {\n display: block;\n color: var(--mj-status-error);\n font-size: 14px;\n margin-bottom: 4px;\n}\n\n.no-scopes-warning p {\n margin: 0;\n line-height: 1.5;\n}\n\n/* Success Content */\n.success-content {\n text-align: center;\n}\n\n.success-icon {\n margin-bottom: 16px;\n}\n\n.success-icon i {\n font-size: 64px;\n color: var(--mj-status-success);\n}\n\n.success-title {\n margin: 0 0 24px 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n/* Key Display */\n.key-display {\n background: var(--mj-color-neutral-800);\n border-radius: 12px;\n padding: 20px;\n margin-bottom: 24px;\n}\n\n.key-warning {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-color-accent-300);\n}\n\n.key-value {\n display: flex;\n align-items: center;\n gap: 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n padding: 12px 16px;\n}\n\n.key-value code {\n flex: 1;\n font-family: 'Fira Code', 'Consolas', monospace;\n font-size: 14px;\n color: var(--mj-status-success);\n word-break: break-all;\n text-align: left;\n}\n\n.copy-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--mj-color-neutral-600);\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-inverse);\n cursor: pointer;\n transition: all 0.2s ease;\n white-space: nowrap;\n}\n\n.copy-btn:hover {\n background: var(--mj-text-secondary);\n}\n\n.copy-btn.copied {\n background: var(--mj-status-success);\n}\n\n/* Key Details */\n.key-details {\n text-align: left;\n background: var(--mj-bg-page);\n border-radius: 10px;\n padding: 16px 20px;\n margin-bottom: 20px;\n}\n\n.detail-row {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.detail-row:last-child {\n border-bottom: none;\n}\n\n.detail-label {\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.detail-value {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n/* Security Note */\n.security-note {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n text-align: left;\n padding: 16px;\n background: linear-gradient(135deg, var(--mj-color-success-50) 0%, var(--mj-color-success-100) 100%);\n border-radius: 10px;\n font-size: 13px;\n color: var(--mj-color-success-800);\n}\n\n.security-note i {\n font-size: 20px;\n color: var(--mj-status-success);\n margin-top: 2px;\n}\n\n.security-note strong {\n display: block;\n margin-bottom: 4px;\n}\n\n/* Form input styling */\n:host ::ng-deep .form-input:focus-within,\n:host ::ng-deep .form-textarea:focus-within {\n box-shadow: var(--mj-focus-ring);\n}\n"] }]
|
|
744
744
|
}], null, { Visible: [{
|
|
745
745
|
type: Input
|
|
746
746
|
}], VisibleChange: [{
|